CC0 transition

There are two ways of representing the condition codes in the back-end for an architecture: the old-fashioned, deprecated way called CC0 representation and the modern, mandatory for new back-ends, way called MODE_CC representation. As of now, most back-ends have been converted to the MODE_CC representation, but a few of them still use the CC0 representation, as summarized in Status of supported architectures.

CC0 vs MODE_CC representation

The following table summarizes the main differences between the 2 models:

CC0 representation

MODE_CC representation

Condition Code object

single special cc0_rtx

one or several hard registers

Implicit clobbering of Condition Code

Yes

No

Separation of CC setters and users

No

Yes

Condition Code vs Reload issue

No

Yes

Optimization

NOTICE_UPDATE_CC
final pass

SELECT_CC_MODE
combine or post-reload compare elimination pass

Condition Code object

In the new model, condition codes are represented by one of several usual hard registers, which can be fixed or subject to register allocation, call-used or call-preserved, etc. Values are represented in one or several machine modes of class MODE_CC, the default one being CCmode.

Implicit clobbering of Condition Code

In the old model, any RTL instruction that doesn't set cc0_rtx implicitly clobbers it, which means that patterns in the MD files don't need to have explicit CLOBBERs of cc0_rtx. On the contrary, there is no implicit clobbering in the new model so patterns in the MD files for instructions that change the condition codes must have an explicit SET or CLOBBER of a CC register, usually by means of a PARALLEL.

Separation of CC setters and users

The RTL subsystem guarantees that instructions setting cc0_rtx and instructions using it are never separated once they are emitted in the RTL stream, i.e. they are always consecutive (modulo inactive RTL instructions like e.g. NOTEs). This guarantee falls in the new model, which means that RTL passes are free to reorder instructions setting or using condition codes, as long as the usual data dependencies expressed in the patterns are preserved.

Condition Code vs Reload issue

The above guarantee provided by the old model is universal and the Reload pass of the register allocator abides by it. Things are fundamentally different in the new model: since there can be any number of instructions between a set and a use of the condition codes, e.g. a compare and a branch, the Reload pass may need to emit a move or a load or a store or even an instruction computing an address between them. This creates the following alternative: either the architecture contains move and load and store and simple integer arithmetical instructions that do not clobber the condition codes and Reload will be well-behaved (case #1, e.g. SPARC) or it lacks such instructions and Reload will be problematic (case #2, e.g. Visium).

Optimization

The compiler can eliminate redundant compare instructions initially emitted in the RTL stream. In the old model, that's an ad-hoc optimization executed during the final RTL pass and parameterized by the NOTICE_UPDATE_CC macro. In the new model, it's either done by the combine pass (case #1 above) or by the post-reload compare elimination pass (case #2 above), both parameterized by the SELECT_CC_MODE macro.

Conversion from CC0 to MODE_CC representation

Preparation

Some amount of planning ahead of the actual conversion is probably required to avoid putting oneself in a corner. Here's a list of the main choices to be made:

Implementation

case #1:

The Reload pass is well-behaved so the conversion is relatively straightforward, but a prerequisite is to verify and adjust, if need be, the instructions emitted by the default integer move and addition/subtraction patterns: they must not clobber the condition codes.

          (define_insn "*comparesi"
            [(set (reg:CC CC_REG)
                  (compare:CC (match_operand:SI 0 "register_operand" "r")
                              (match_operand:SI 1 "register_operand" "r")))]
            ""
            "cmp %0, %1")

          (define_insn "*addsi_clobber_flags"
            [(set (match_operand:SI 0 "register_operand" "=r")
                  (plus:SI (match_operand:SI 1 "register_operand" "%r")
                           (match_operand:SI 2 "arith_operand" "rI")))
             (clobber (reg:CC CC_REG))]
            ""
            "addcc %0, %1, %2")

          (define_insn "*addsi_set_flags"
            [(set (reg:CCNZ CC_REG)
                  (compare:CCNZ (plus:SI (match_operand:SI 1 "register_operand" "%r")
                                         (match_operand:SI 2 "arith_operand" "rI"))
                                (const_int 0)))
             (set (match_operand:SI 0 "register_operand" "=r")
                  (plus:SI (match_dup 1) (match_dup 2)))]
            ""
            "addcc %0, %1, %2")

The MODE_CC mode of the register in the CLOBBER doesn't really matter so CCmode is used. On the contrary, the mode of the register in the SET is significant and encodes the implicit comparison of the result of the addition with zero done by the instruction; but since the instruction is not a fully-fledged compare instruction, it doesn't set the CC register as a fully-fledged compare instruction would, so it cannot use CCmode but needs another MODE_CC mode (for example, CCNZmode here means that only the 'N' and 'Z' flags of the CC register are valid). Note that the process can be automated by means of the define_subst construct of the MD language.

case #2:

The Reload pass is problematic because it can emit instructions clobbering the condition codes anywhere. That's why, until after it is completed, there must not be any instruction setting or using the CC register(s) in the RTL stream; only CLOBBERs are permitted at this point.

          (define_expand "cbranchsi4"
            [(set (pc)
                  (if_then_else (match_operator 0 "ordered_comparison_operator"
                                  [(match_operand:SI 1 "register_operand")
                                   (match_operand:SI 2 "register_operand")])
                                (label_ref (match_operand 3 ""))
                                (pc)))]
            "")

          (define_insn_and_split "*cbranchsi4_insn"
            [(set (pc)
                  (if_then_else (match_operator 0 "ordered_comparison_operator"
                                  [(match_operand:SI 1 "register_operand" "r")
                                   (match_operand:SI 2 "register_operand" "r")])
                                (label_ref (match_operand 3 ""))
                                (pc)))]
            ""
            "#"
            "reload_completed"
            [(set (reg:CC CC_REG)
                  (compare:CC (match_dup 1) (match_dup 2)))
             (set (pc)
                  (if_then_else (match_op_dup 0)
                                  [(reg:CC CC_REG) (const_int 0)])
                                (label_ref (match_dup 3))
                                (pc)))]
            "")

          (define_insn "*comparesi"
            [(set (reg:CC CC_REG)
                  (compare:CC (match_operand:SI 0 "register_operand" "r")
                              (match_operand:SI 1 "register_operand" "r")))]
            "reload_completed"
            "cmp %0, %1")

          (define_insn "*addsi_clobber_flags"
            [(set (match_operand:SI 0 "register_operand" "=r")
                  (plus:SI (match_operand:SI 1 "register_operand" "%r")
                           (match_operand:SI 2 "arith_operand" "rI")))
             (clobber (reg:CC CC_REG))]
            "reload_completed"
            "addcc %0, %1, %2")

          (define_insn "*addsi_set_flags"
            [(set (match_operand:SI 0 "register_operand" "=r")
                  (plus:SI (match_operand:SI 1 "register_operand" "%r")
                           (match_operand:SI 2 "arith_operand" "rI")))
             (set (reg:CCNZ CC_REG)
                  (compare:CCNZ (plus:SI (match_dup 1) (match_dup 2))
                                (const_int 0)))]
            "reload_completed"
            "addcc %0, %1, %2")

The MODE_CC mode of the register in the CLOBBER doesn't really matter so CCmode is used. On the contrary, the mode of the register in the SET is significant and encodes the implicit comparison of the result of the addition with zero done by the instruction; but since the instruction is not a fully-fledged compare instruction, it doesn't set the CC register as a fully-fledged compare instruction would, so it cannot use CCmode but needs another MODE_CC mode (for example, CCNZmode here means that only the 'N' and 'Z' flags of the CC register are valid). Note that the process can be automated by means of the define_subst construct of the MD language.

Finalization

If you have any questions, please contact me: Eric Botcazou ebotcazou@adacore.com

None: CC0Transition (last edited 2016-10-11 07:08:30 by EricBotcazou)