Changing One RTL SSA Instruction (GNU Compiler Collection (GCC) Internals)

#### 15.21.8.1 Changing One RTL SSA Instruction ¶

Before making a change, passes should first use a statement like the following:

    auto attempt = crtl->ssa->new_change_attempt ();

Here, `attempt` is an RAII object that should remain in scope for the entire change attempt. It automatically frees temporary memory related to the changes when it goes out of scope.

Next, the pass should create an `rtl_ssa::insn_change` object for the instruction that it wants to change. This object specifies several things:

- what the instruction’s new list of uses should be (`new_uses`). By default this is the same as the instruction’s current list of uses.
- what the instruction’s new list of definitions should be (`new_defs`). By default this is the same as the instruction’s current list of definitions.
- where the instruction should be located (`move_range`). This is a range of instructions after which the instruction could be placed, represented as an `rtl_ssa::insn_range`. By default the instruction must remain at its current position.

If a pass was attempting to change all these properties of an instruction `insn`, it might do something like this:

    rtl_ssa::insn_change change (insn);
    change.new_defs = ...;
    change.new_uses = ...;
    change.move_range = ...;

This `rtl_ssa::insn_change` only describes something that the pass might do; at this stage, nothing has actually changed.

As noted above, the default `move_range` requires the instruction to remain where it is. At the other extreme, it is possible to allow the instruction to move anywhere within its extended basic block, provided that all the new uses and definitions can be performed at the new location. The way to do this is:

    change.move_range = insn->ebb ()->insn_range ();

In either case, the next step is to make sure that move range is consistent with the new uses and definitions. The way to do this is:

    if (!rtl_ssa::restrict_movement (change))
      return false;

This function tries to limit `move_range` to a range of instructions at which `new_uses` and `new_defs` can be correctly performed. It returns true on success or false if no suitable location exists.

The pass should also tentatively change the pattern of the instruction to whatever form the pass wants the instruction to have. This should use the facilities provided by `recog.cc`. For example:

    rtl_insn *rtl = insn->rtl ();
    insn_change_watermark watermark;
    validate_change (rtl, &PATTERN (rtl), new_pat, 1);

will tentatively replace `insn`’s pattern with `new_pat`.

These changes and the construction of the `rtl_ssa::insn_change` can happen in either order or be interleaved.

After the tentative changes to the instruction are complete, the pass should check whether the new pattern matches a target instruction or satisfies the requirements of an inline asm:

    if (!rtl_ssa::recog (attempt, change))
      return false;

This step might change the instruction pattern further in order to make it match. It might also add new definitions or restrict the range of the move. For example, if the new pattern did not match in its original form, but could be made to match by adding a clobber of the flags register, `rtl_ssa::recog` will check whether the flags register is free at an appropriate point. If so, it will add a clobber of the flags register to `new_defs` and restrict `move_range` to the locations at which the flags register can be safely clobbered.

Even if the proposed new instruction is valid according to `rtl_ssa::recog`, the change might not be worthwhile. For example, when optimizing for speed, the new instruction might turn out to be slower than the original one. When optimizing for size, the new instruction might turn out to be bigger than the original one.

Passes should check for this case using `change_is_worthwhile`. For example:

    if (!rtl_ssa::change_is_worthwhile (change))
      return false;

If the change passes this test too then the pass can perform the change using:

    confirm_change_group ();
    crtl->ssa->change_insn (change);

Putting all this together, the change has the following form:

    auto attempt = crtl->ssa->new_change_attempt ();

    rtl_ssa::insn_change change (insn);
    change.new_defs = ...;
    change.new_uses = ...;
    change.move_range = ...;

    if (!rtl_ssa::restrict_movement (change))
      return false;

    insn_change_watermark watermark;
    // Use validate_change etc. to change INSN's pattern.
    ...
    if (!rtl_ssa::recog (attempt, change)
        || !rtl_ssa::change_is_worthwhile (change))
      return false;

    confirm_change_group ();
    crtl->ssa->change_insn (change);