Skip to content

Commit 7354cfe

Browse files
authored
Remove support for program moves (#118)
1 parent 54f074e commit 7354cfe

File tree

9 files changed

+4
-415
lines changed

9 files changed

+4
-415
lines changed

README.md

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,7 @@
33
This is a register allocator that started life as, and is about 50%
44
still, a port of IonMonkey's backtracking register allocator to
55
Rust. In many regards, it has been generalized, optimized, and
6-
improved since the initial port, and now supports both SSA and non-SSA
7-
use-cases. (However, non-SSA should be considered deprecated; we want to
8-
move to SSA-only in the future, to enable some performance improvements.
9-
See #4.)
6+
improved since the initial port.
107

118
In addition, it contains substantial amounts of testing infrastructure
129
(fuzzing harnesses and checkers) that does not exist in the original

src/checker.rs

Lines changed: 1 addition & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -269,10 +269,6 @@ fn visit_all_vregs<F: Function, V: FnMut(VReg)>(f: &F, mut v: V) {
269269
for op in f.inst_operands(inst) {
270270
v(op.vreg());
271271
}
272-
if let Some((src, dst)) = f.is_move(inst) {
273-
v(src.vreg());
274-
v(dst.vreg());
275-
}
276272
if f.is_branch(inst) {
277273
for succ_idx in 0..f.block_succs(block).len() {
278274
for &param in f.branch_blockparams(block, inst, succ_idx) {
@@ -565,25 +561,6 @@ impl CheckerState {
565561
// according to the move semantics in the step
566562
// function below.
567563
}
568-
&CheckerInst::ProgramMove { inst, src, dst: _ } => {
569-
// Validate that the fixed-reg constraint, if any, on
570-
// `src` is satisfied.
571-
if let OperandConstraint::FixedReg(preg) = src.constraint() {
572-
let alloc = Allocation::reg(preg);
573-
let val = self.get_value(&alloc).unwrap_or(&default_val);
574-
trace!(
575-
"checker: checkinst {:?}: cheker value in {:?} is {:?}",
576-
checkinst,
577-
alloc,
578-
val
579-
);
580-
self.check_val(inst, src, alloc, val, &[alloc], checker)?;
581-
}
582-
// Note that we don't do anything with `dst`
583-
// here. That is implicitly checked whenever `dst` is
584-
// used; the `update()` step below adds the symbolic
585-
// vreg for `dst` into wherever `src` may be stored.
586-
}
587564
}
588565
Ok(())
589566
}
@@ -686,15 +663,6 @@ impl CheckerState {
686663
}
687664
}
688665
}
689-
&CheckerInst::ProgramMove { inst: _, src, dst } => {
690-
// Remove all earlier instances of `dst`: this vreg is
691-
// now stale (it is being overwritten).
692-
self.remove_vreg(dst.vreg());
693-
// Define `dst` wherever `src` occurs.
694-
for (_, value) in self.get_mappings_mut() {
695-
value.copy_vreg(src.vreg(), dst.vreg());
696-
}
697-
}
698666
}
699667
}
700668

@@ -786,23 +754,6 @@ pub(crate) enum CheckerInst {
786754
/// A safepoint, with the given Allocations specified as containing
787755
/// reftyped values. All other reftyped values become invalid.
788756
Safepoint { inst: Inst, allocs: Vec<Allocation> },
789-
790-
/// An op with one source operand, and one dest operand, that
791-
/// copies any symbolic values from the source to the dest, in
792-
/// addition to adding the symbolic value of the dest vreg to the
793-
/// set. This "program move" is distinguished from the above
794-
/// `Move` by being semantically relevant in the original
795-
/// (pre-regalloc) program.
796-
///
797-
/// We transform checker values as follows: for any vreg-set that
798-
/// contains `dst`'s vreg, we first delete that vreg (because it
799-
/// is being redefined). Then, for any vreg-set with `src`
800-
/// present, we add `dst`.
801-
ProgramMove {
802-
inst: Inst,
803-
src: Operand,
804-
dst: Operand,
805-
},
806757
}
807758

808759
#[derive(Debug)]
@@ -903,35 +854,10 @@ impl<'a, F: Function> Checker<'a, F> {
903854
self.bb_insts.get_mut(&block).unwrap().push(checkinst);
904855
}
905856

906-
// If this is a move, handle specially. Note that the
907-
// regalloc2-inserted moves are not semantically present in
908-
// the original program and so do not modify the sets of
909-
// symbolic values at all, but rather just move them around;
910-
// but "program moves" *are* present, and have the following
911-
// semantics: they define the destination vreg, but also
912-
// retain any symbolic values in the source.
913-
//
914-
// regalloc2 reifies all moves into edits in its unified
915-
// move/edit framework, so we don't get allocs for these moves
916-
// in the post-regalloc output, and the embedder is not
917-
// supposed to emit the moves. But we *do* want to check the
918-
// semantic implications, namely definition of new vregs. So
919-
// we emit `ProgramMove` ops that do just this.
920-
if let Some((src, dst)) = self.f.is_move(inst) {
921-
let src_op = Operand::any_use(src.vreg());
922-
let dst_op = Operand::any_def(dst.vreg());
923-
let checkinst = CheckerInst::ProgramMove {
924-
inst,
925-
src: src_op,
926-
dst: dst_op,
927-
};
928-
trace!("checker: adding inst {:?}", checkinst);
929-
self.bb_insts.get_mut(&block).unwrap().push(checkinst);
930-
}
931857
// Skip normal checks if this is a branch: the blockparams do
932858
// not exist in post-regalloc code, and the edge-moves have to
933859
// be inserted before the branch rather than after.
934-
else if !self.f.is_branch(inst) {
860+
if !self.f.is_branch(inst) {
935861
let operands: Vec<_> = self.f.inst_operands(inst).iter().cloned().collect();
936862
let allocs: Vec<_> = out.inst_allocs(inst).iter().cloned().collect();
937863
let clobbers: Vec<_> = self.f.inst_clobbers(inst).into_iter().collect();
@@ -1128,9 +1054,6 @@ impl<'a, F: Function> Checker<'a, F> {
11281054
}
11291055
trace!(" safepoint: {}", slotargs.join(", "));
11301056
}
1131-
&CheckerInst::ProgramMove { inst, src, dst } => {
1132-
trace!(" inst{}: prog_move {} -> {}", inst.index(), src, dst);
1133-
}
11341057
&CheckerInst::ParallelMove { .. } => {
11351058
panic!("unexpected parallel_move in body (non-edge)")
11361059
}

src/fuzzing/func.rs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -124,10 +124,6 @@ impl Function for Func {
124124
&self.debug_value_labels[..]
125125
}
126126

127-
fn is_move(&self, _: Inst) -> Option<(Operand, Operand)> {
128-
None
129-
}
130-
131127
fn inst_operands(&self, insn: Inst) -> &[Operand] {
132128
&self.insts[insn.index()].operands[..]
133129
}

src/ion/data_structures.rs

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -405,21 +405,6 @@ pub struct Env<'a, F: Function> {
405405
pub extra_spillslots_by_class: [SmallVec<[Allocation; 2]>; 2],
406406
pub preferred_victim_by_class: [PReg; 2],
407407

408-
// Program moves: these are moves in the provided program that we
409-
// handle with our internal machinery, in order to avoid the
410-
// overhead of ordinary operand processing. We expect the client
411-
// to not generate any code for instructions that return
412-
// `Some(..)` for `.is_move()`, and instead use the edits that we
413-
// provide to implement those moves (or some simplified version of
414-
// them) post-regalloc.
415-
//
416-
// (from-vreg, inst, from-alloc), sorted by (from-vreg, inst)
417-
pub prog_move_srcs: Vec<((VRegIndex, Inst), Allocation)>,
418-
// (to-vreg, inst, to-alloc), sorted by (to-vreg, inst)
419-
pub prog_move_dsts: Vec<((VRegIndex, Inst), Allocation)>,
420-
// (from-vreg, to-vreg) for bundle-merging.
421-
pub prog_move_merges: Vec<(LiveRangeIndex, LiveRangeIndex)>,
422-
423408
// When multiple fixed-register constraints are present on a
424409
// single VReg at a single program point (this can happen for,
425410
// e.g., call args that use the same value multiple times), we
@@ -628,9 +613,7 @@ pub struct InsertedMove {
628613
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
629614
pub enum InsertMovePrio {
630615
InEdgeMoves,
631-
BlockParam,
632616
Regular,
633-
PostRegular,
634617
MultiFixedRegInitial,
635618
MultiFixedRegSecondary,
636619
ReusedInput,
@@ -660,10 +643,6 @@ pub struct Stats {
660643
pub livein_iterations: usize,
661644
pub initial_liverange_count: usize,
662645
pub merged_bundle_count: usize,
663-
pub prog_moves: usize,
664-
pub prog_moves_dead_src: usize,
665-
pub prog_move_merge_attempt: usize,
666-
pub prog_move_merge_success: usize,
667646
pub process_bundle_count: usize,
668647
pub process_bundle_reg_probes_fixed: usize,
669648
pub process_bundle_reg_success_fixed: usize,

src/ion/liveranges.rs

Lines changed: 0 additions & 154 deletions
Original file line numberDiff line numberDiff line change
@@ -356,13 +356,6 @@ impl<'a, F: Function> Env<'a, F> {
356356
}
357357

358358
for inst in insns.rev().iter() {
359-
if let Some((src, dst)) = self.func.is_move(inst) {
360-
live.set(dst.vreg().vreg(), false);
361-
live.set(src.vreg().vreg(), true);
362-
self.observe_vreg_class(src.vreg());
363-
self.observe_vreg_class(dst.vreg());
364-
}
365-
366359
for pos in &[OperandPos::Late, OperandPos::Early] {
367360
for op in self.func.inst_operands(inst) {
368361
if op.as_fixed_nonallocatable().is_some() {
@@ -519,148 +512,6 @@ impl<'a, F: Function> Env<'a, F> {
519512
}
520513
}
521514

522-
// If this is a move, handle specially.
523-
if let Some((src, dst)) = self.func.is_move(inst) {
524-
assert!(
525-
src.vreg() != dst.vreg(),
526-
"Invalid move: overwriting an SSA value"
527-
);
528-
529-
trace!(" -> move inst{}: src {} -> dst {}", inst.index(), src, dst);
530-
531-
debug_assert_eq!(src.class(), dst.class());
532-
debug_assert_eq!(src.kind(), OperandKind::Use);
533-
debug_assert_eq!(src.pos(), OperandPos::Early);
534-
debug_assert_eq!(dst.kind(), OperandKind::Def);
535-
debug_assert_eq!(dst.pos(), OperandPos::Late);
536-
537-
// Redefine src and dst operands to have
538-
// positions of After and Before respectively
539-
// (see note below), and to have Any
540-
// constraints if they were originally Reg.
541-
let src_constraint = match src.constraint() {
542-
OperandConstraint::Reg => OperandConstraint::Any,
543-
x => x,
544-
};
545-
let dst_constraint = match dst.constraint() {
546-
OperandConstraint::Reg => OperandConstraint::Any,
547-
x => x,
548-
};
549-
let src = Operand::new(
550-
src.vreg(),
551-
src_constraint,
552-
OperandKind::Use,
553-
OperandPos::Late,
554-
);
555-
let dst = Operand::new(
556-
dst.vreg(),
557-
dst_constraint,
558-
OperandKind::Def,
559-
OperandPos::Early,
560-
);
561-
562-
if self.annotations_enabled {
563-
self.annotate(
564-
ProgPoint::after(inst),
565-
format!(
566-
" prog-move v{} ({:?}) -> v{} ({:?})",
567-
src.vreg().vreg(),
568-
src_constraint,
569-
dst.vreg().vreg(),
570-
dst_constraint,
571-
),
572-
);
573-
}
574-
575-
// N.B.: in order to integrate with the move
576-
// resolution that joins LRs in general, we
577-
// conceptually treat the move as happening
578-
// between the move inst's After and the next
579-
// inst's Before. Thus the src LR goes up to
580-
// (exclusive) next-inst-pre, and the dst LR
581-
// starts at next-inst-pre. We have to take
582-
// care in our move insertion to handle this
583-
// like other inter-inst moves, i.e., at
584-
// `Regular` priority, so it properly happens
585-
// in parallel with other inter-LR moves.
586-
//
587-
// Why the progpoint between move and next
588-
// inst, and not the progpoint between prev
589-
// inst and move? Because a move can be the
590-
// first inst in a block, but cannot be the
591-
// last; so the following progpoint is always
592-
// within the same block, while the previous
593-
// one may be an inter-block point (and the
594-
// After of the prev inst in a different
595-
// block).
596-
597-
// Handle the def w.r.t. liveranges: trim the
598-
// start of the range and mark it dead at this
599-
// point in our backward scan.
600-
let pos = ProgPoint::before(inst.next());
601-
let mut dst_lr = vreg_ranges[dst.vreg().vreg()];
602-
if !live.get(dst.vreg().vreg()) {
603-
let from = pos;
604-
let to = pos.next();
605-
dst_lr = self.add_liverange_to_vreg(
606-
VRegIndex::new(dst.vreg().vreg()),
607-
CodeRange { from, to },
608-
);
609-
trace!(" -> invalid LR for def; created {:?}", dst_lr);
610-
}
611-
trace!(" -> has existing LR {:?}", dst_lr);
612-
// Trim the LR to start here.
613-
if self.ranges[dst_lr.index()].range.from
614-
== self.cfginfo.block_entry[block.index()]
615-
{
616-
trace!(" -> started at block start; trimming to {:?}", pos);
617-
self.ranges[dst_lr.index()].range.from = pos;
618-
}
619-
self.ranges[dst_lr.index()].set_flag(LiveRangeFlag::StartsAtDef);
620-
live.set(dst.vreg().vreg(), false);
621-
vreg_ranges[dst.vreg().vreg()] = LiveRangeIndex::invalid();
622-
623-
// Handle the use w.r.t. liveranges: make it live
624-
// and create an initial LR back to the start of
625-
// the block.
626-
let pos = ProgPoint::after(inst);
627-
let src_lr = if !live.get(src.vreg().vreg()) {
628-
let range = CodeRange {
629-
from: self.cfginfo.block_entry[block.index()],
630-
to: pos.next(),
631-
};
632-
let src_lr =
633-
self.add_liverange_to_vreg(VRegIndex::new(src.vreg().vreg()), range);
634-
vreg_ranges[src.vreg().vreg()] = src_lr;
635-
src_lr
636-
} else {
637-
vreg_ranges[src.vreg().vreg()]
638-
};
639-
640-
trace!(" -> src LR {:?}", src_lr);
641-
642-
// Add to live-set.
643-
let src_is_dead_after_move = !live.get(src.vreg().vreg());
644-
live.set(src.vreg().vreg(), true);
645-
646-
// Add to program-moves lists.
647-
self.prog_move_srcs.push((
648-
(VRegIndex::new(src.vreg().vreg()), inst),
649-
Allocation::none(),
650-
));
651-
self.prog_move_dsts.push((
652-
(VRegIndex::new(dst.vreg().vreg()), inst.next()),
653-
Allocation::none(),
654-
));
655-
self.stats.prog_moves += 1;
656-
if src_is_dead_after_move {
657-
self.stats.prog_moves_dead_src += 1;
658-
self.prog_move_merges.push((src_lr, dst_lr));
659-
}
660-
661-
continue;
662-
}
663-
664515
// Preprocess defs and uses. Specifically, if there
665516
// are any fixed-reg-constrained defs at Late position
666517
// and fixed-reg-constrained uses at Early position
@@ -1016,11 +867,6 @@ impl<'a, F: Function> Env<'a, F> {
1016867

1017868
self.blockparam_ins.sort_unstable_by_key(|x| x.key());
1018869
self.blockparam_outs.sort_unstable_by_key(|x| x.key());
1019-
self.prog_move_srcs.sort_unstable_by_key(|(pos, _)| *pos);
1020-
self.prog_move_dsts.sort_unstable_by_key(|(pos, _)| *pos);
1021-
1022-
trace!("prog_move_srcs = {:?}", self.prog_move_srcs);
1023-
trace!("prog_move_dsts = {:?}", self.prog_move_dsts);
1024870

1025871
self.stats.initial_liverange_count = self.ranges.len();
1026872
self.stats.blockparam_ins_count = self.blockparam_ins.len();

src/ion/merge.rs

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -351,28 +351,6 @@ impl<'a, F: Function> Env<'a, F> {
351351
self.merge_bundles(from_bundle, to_bundle);
352352
}
353353

354-
// Attempt to merge move srcs/dsts.
355-
for i in 0..self.prog_move_merges.len() {
356-
let (src, dst) = self.prog_move_merges[i];
357-
trace!("trying to merge move src LR {:?} to dst LR {:?}", src, dst);
358-
let src = self.resolve_merged_lr(src);
359-
let dst = self.resolve_merged_lr(dst);
360-
trace!(
361-
"resolved LR-construction merging chains: move-merge is now src LR {:?} to dst LR {:?}",
362-
src,
363-
dst
364-
);
365-
366-
let src_bundle = self.ranges[src.index()].bundle;
367-
debug_assert!(src_bundle.is_valid());
368-
let dest_bundle = self.ranges[dst.index()].bundle;
369-
debug_assert!(dest_bundle.is_valid());
370-
self.stats.prog_move_merge_attempt += 1;
371-
if self.merge_bundles(/* from */ dest_bundle, /* to */ src_bundle) {
372-
self.stats.prog_move_merge_success += 1;
373-
}
374-
}
375-
376354
trace!("done merging bundles");
377355
}
378356

0 commit comments

Comments
 (0)