CT_00_055 -- Zicond with if-conversion improvements (GCC)

CT_00_055 -- Zicond with if-conversion improvements (GCC)

About

The ZiCondops extension provides a conditional zero primitive upon which subsets of conditional move and conditional arithmetic/logical operations can be implemented.   Transforming control flow into conditional operations can improve code performance by eliminating branch mispredict costs as well as reducing the load on the branch predictors.  The earlier in the optimizer pipeline these transformations are performed the more likely they are to expose secondary optimization opportunities as well since the transformations result in larger basic blocks (a fundamental unit of code most compiler optimizations work on).

 

This item is meant to track additional opportunities to optimize code using the zicond extension

 

  1. Improve gimple if-conversion and  gimple→rtl expansion

    1. Conditional moves can be expressed in multiple forms in gimple.  The multiplication by a boolean variant could have initial RTL generation step improved to first try conditional move expanders, then fall back to other approaches

      1. Proof of concept code exists

        1. Recognize multiply variant and rewrite into COND_EXPR

        2. Adjust gimple→rtl expansion to try and avoid regressions on targets without conditional moves

        3. But still needs work as it's regressing, even on risc-v targets

    2. Adjustment of gimple if-converter to handle sign bit test selecting across various constant scenarios can pick up more cases.  For example selecting between 0 and a constant with a run of on bits in the LSB or MSB.

      1. Upstream agreement on form (use COND_EXPR form rather than shifts)

      2. Implement in match.pd framework (initial POC was done in phi-opt)

      3. Expander work to handle the various constants we can select across

  2. Improvement of if-conversion pass in GCC

    1. Resolve implementation details for condzero_arith code to use more efficient sequences than generalized conditional moves when possible

    2. Handle SUBREG and zero/sign extended objects

    3. Requires adjustments to the conditional execution pass in RTL

    4. extensions probably aren't hard to support, subregs will require deeper thought

    5. Biggest issues here may all be in the code to handle #8 below If-convert the conditional in the move_one_fast loop of deepsjeng

      • Two approaches

        • Improve min/max discovery in gimple which should simplify the conditional code to optimizable form in the RTL if-converter code

        • Improve the RTL if-converter code to better handle multiple if-convertable instrutions

        • Add backend pattern to recognize an if-then-else as a min/max

        • Robin has submitted some code for this, but it needs to be adjusted for reviewer feedback

  3. Cost model adjustments

    1. Costing the generated conditional move sequence needs adjustments.  There are often insns that appear in the sequence that have a high probability of being removed by later passes (particularly register allocation and const/copy propagation).  These can throw off the initial costing and need adjustments

    2. As touched on in upstream bug 112462, when we have a condition other than (reg) eq/ne (const_int 0) we need to bump the rtx_cost of using zicond as the condition will need canonicalization.

    3. Similarly we may need to bump the rtx_cost depending on the true/false arms

    4. May want to do some refactoring so that we can share code across costing & expansion.

  4. Relax predicate on conditional move expander to allow more constants.  The general idea is to be able to select more efficient sequences in special cases.   For example given two constants C1 and C2.  If the sum of the costs of computing C1 and C2 is greater than the cost of computing C1 by 2 insns or more, then it may be profitable to compute C1 and conditionally add (C2 - C1) to the result.

  5. When one arm of a conditional move can be trivially derived from the other, say by adding a small constant, we can emit a single zicond + adjustment rather than a fully generalized conditional move via 2 zicond instructions.   Conceptually this is similar to how we handle something like x = cond ? C1 : C2, we just need to detect it earlier.  See these examples on godbolt.

    Matching this style would be one approach and probably generally profitable for the first case: (set (reg:DI 135 [ <retval> ]) (plus:DI (if_then_else:DI (reg:DI 145) (const_int 0 [0]) (reg:DI 143)) (reg:DI 147))) Obviously we could replace the PLUS with a variety of operators. Another approach would likely be to match (which falls into the sub-word cases) (set (reg:DI 147) (if_then_else:DI (reg:DI 145) (sign_extend:DI (plus:SI (subreg:SI (reg:DI 138) 0) (const_int 5 [0x5]))) (const_int 0 [0])))
  6. Selecting between constants based on output of  a sign bit test. 

    1. -1, 2^n is srai+bset –  best handled in rtl ifcvt

    2. ~(2^n), 0 is srai+bclr – best handled in rtl ifcvt

    3. -1, simm12 is srai+ori – best handled in rtl ifcvt

    4. simm12, 0 is srai+andi – best handled in rtl ifcvt

    5. high bits set, 0 is srai+sll – must be handled in gimple

    6.  low bits set, 0 is srai+srl – must be handled in gimple

Stakeholders/Partners

RISE:

Ventana: Raphael Zinsly, Jeff Law, Robin Dapp ESWIN: Fei Gao

External:

 

Dependencies

 

 

Status

Development

IN PROGRESS

 

Development Timeline

1H2025

 

Upstreaming

IN PROGRESS

 

 

Upstream Version

gcc-16 (target)

(Spring 2026)

 

 

 

 

Contacts

Raphael Zinsly (Ventana)

Jeff Law (Ventana)

 

Dependencies

None

 

 

Updates

 

Jul 16, 2025  

  • Remaining items from 1H2025 rolled into new task