14.21.8.2 Changing Multiple RTL SSA Instructions

The process for changing multiple instructions is similar to the process for changing single instructions (see Changing One RTL SSA Instruction). The pass should again start the change attempt with:

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

and keep attempt in scope for the duration of the change attempt. It should then construct an rtl_ssa::insn_change for each change that it wants to make.

After this, it should combine the changes into a sequence of rtl_ssa::insn_change pointers. This sequence must be in reverse postorder; the instructions will remain strictly in the order that the sequence specifies.

For example, if a pass is changing exactly two instructions, it might do:

rtl_ssa::insn_change *changes[] = { &change1, change2 };

where change1’s instruction must come before change2’s. Alternatively, if the pass is changing a variable number of instructions, it might build up the sequence in a vec<rtl_ssa::insn_change *>.

By default, rtl_ssa::restrict_movement assumes that all instructions other than the one passed to it will remain in their current positions and will retain their current uses and definitions. When changing multiple instructions, it is usually more effective to ignore the other instructions that are changing. The sequencing described above ensures that the changing instructions remain in the correct order with respect to each other. The way to do this is:

if (!rtl_ssa::restrict_movement (change, insn_is_changing (changes)))
  return false;

Similarly, when rtl_ssa::restrict_movement is detecting whether a register can be clobbered, it by default assumes that all other instructions will remain in their current positions and retain their current form. It is again more effective to ignore changing instructions (which might, for example, no longer need to clobber the flags register). The way to do this is:

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

When changing multiple instructions, the important question is usually not whether each individual change is worthwhile, but whether the changes as a whole are worthwhile. The way to test this is:

if (!rtl_ssa::changes_are_worthwhile (changes))
  return false;

The process for changing single instructions makes sure that one rtl_ssa::insn_change in isolation is valid. But when changing multiple instructions, it is also necessary to test whether the sequence as a whole is valid. For example, it might be impossible to satisfy all of the move_ranges at once.

Therefore, once the pass has a sequence of changes that are individually correct, it should use:

if (!crtl->ssa->verify_insn_changes (changes))
  return false;

to check whether the sequence as a whole is valid. If all checks pass, the final step is:

confirm_change_group ();
crtl->ssa->change_insns (changes);

Putting all this together, the process for a two-instruction change is:

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

rtl_ssa::insn_change change (insn1);
change1.new_defs = …;
change1.new_uses = …;
change1.move_range = …;

rtl_ssa::insn_change change (insn2);
change2.new_defs = …;
change2.new_uses = …;
change2.move_range = …;

rtl_ssa::insn_change *changes[] = { &change1, change2 };

auto is_changing = insn_is_changing (changes);
if (!rtl_ssa::restrict_movement (change1, is_changing)
    || !rtl_ssa::restrict_movement (change2, is_changing))
  return false;

insn_change_watermark watermark;
// Use validate_change etc. to change INSN1's and INSN2's patterns.
…
if (!rtl_ssa::recog (change1, is_changing)
    || !rtl_ssa::recog (change2, is_changing)
    || !rtl_ssa::changes_are_worthwhile (changes)
    || !crtl->ssa->verify_insn_changes (changes))
  return false;

confirm_change_group ();
crtl->ssa->change_insns (changes);