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_range
s 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);