From 6d34476cd695fc9f041f90fdecd9f1090e0fae0e Mon Sep 17 00:00:00 2001 From: Jakob Stoklund Olesen Date: Thu, 29 Jun 2017 10:30:26 -0700 Subject: [PATCH] Propagate affinities for EBB arguments. A priory, an EBB argument value only gets an affinity if it is used directly by a non-ghost instruction. A use by a branch passing arguments to an EBB doesn't count. When an EBB argument value does have an affinity, the values passed by all the predecessors must also have affinities. This can cause EBB argument values to get affinities recursively. - Add a second pass to the liveness computation for propagating EBB argument affinities, possibly recursively. - Verify EBB argument affinities correctly: A value passed to a branch must have an affinity only if the corresponding EBB argument value in the destination has an affinity. --- filetests/regalloc/basic.cton | 15 ++++++++++ lib/cretonne/src/regalloc/liveness.rs | 41 +++++++++++++++++++++++++++ lib/cretonne/src/verifier/liveness.rs | 33 +++++++++++++++++++-- 3 files changed, 87 insertions(+), 2 deletions(-) diff --git a/filetests/regalloc/basic.cton b/filetests/regalloc/basic.cton index dd6cab11cf..89e160d75a 100644 --- a/filetests/regalloc/basic.cton +++ b/filetests/regalloc/basic.cton @@ -63,3 +63,18 @@ ebb1(v10: i32): v11 = call fn0(v10) return v11 } + +; Pass an EBB argument as a jump argument. +function %jumpebb(i32, i32) -> i32 { + fn0 = function %foo(i32) -> i32 + +ebb0(v1: i32, v2: i32): + brnz v1, ebb1(v1, v2) + jump ebb1(v2, v1) + +ebb1(v10: i32, v11: i32): + jump ebb2(v10, v11) + +ebb2(v20: i32, v21: i32): + return v21 +} diff --git a/lib/cretonne/src/regalloc/liveness.rs b/lib/cretonne/src/regalloc/liveness.rs index 71fa4961e7..2093b15299 100644 --- a/lib/cretonne/src/regalloc/liveness.rs +++ b/lib/cretonne/src/regalloc/liveness.rs @@ -279,6 +279,9 @@ pub struct Liveness { /// This vector is always empty, except for inside that function. /// It lives here to avoid repeated allocation of scratch memory. worklist: Vec, + + /// Working space for the `propagate_ebb_arguments` algorithm. + ebb_args: Vec, } impl Liveness { @@ -290,6 +293,7 @@ impl Liveness { Liveness { ranges: LiveRangeSet::new(), worklist: Vec::new(), + ebb_args: Vec::new(), } } @@ -408,5 +412,42 @@ impl Liveness { } } } + + self.propagate_ebb_arguments(func, cfg); + } + + /// Propagate affinities for EBB arguments. + /// + /// If an EBB argument value has an affinity, all predecessors must pass a value with an + /// affinity. + pub fn propagate_ebb_arguments(&mut self, func: &Function, cfg: &ControlFlowGraph) { + assert!(self.ebb_args.is_empty()); + + for ebb in func.layout.ebbs() { + for &arg in func.dfg.ebb_args(ebb).iter() { + let affinity = self.ranges.get(arg).unwrap().affinity; + if affinity.is_none() { + continue; + } + self.ebb_args.push(arg); + + // Now apply the affinity to all predecessors recursively. + while let Some(succ_arg) = self.ebb_args.pop() { + let (succ_ebb, num) = match func.dfg.value_def(succ_arg) { + ValueDef::Arg(e, n) => (e, n), + _ => continue, + }; + + for &(_, pred_branch) in cfg.get_predecessors(succ_ebb) { + let pred_arg = func.dfg.inst_variable_args(pred_branch)[num]; + let pred_affinity = &mut self.ranges.get_mut(pred_arg).unwrap().affinity; + if pred_affinity.is_none() { + *pred_affinity = affinity; + self.ebb_args.push(pred_arg); + } + } + } + } + } } } diff --git a/lib/cretonne/src/verifier/liveness.rs b/lib/cretonne/src/verifier/liveness.rs index 51911b3f38..2dff4a6e3e 100644 --- a/lib/cretonne/src/verifier/liveness.rs +++ b/lib/cretonne/src/verifier/liveness.rs @@ -93,7 +93,7 @@ impl<'a> LivenessVerifier<'a> { } // Check the uses. - for &val in self.func.dfg.inst_args(inst) { + for (idx, &val) in self.func.dfg.inst_args(inst).iter().enumerate() { let lr = match self.liveness.get(val) { Some(lr) => lr, None => return err!(inst, "{} has no live range", val), @@ -104,7 +104,10 @@ impl<'a> LivenessVerifier<'a> { if encoding.is_legal() { // A legal instruction is not allowed to depend on ghost values. - if lr.affinity.is_none() { + // + // A branch argument can be a ghost value if the corresponding destination + // EBB argument is a ghost value. + if lr.affinity.is_none() && !self.is_ghost_branch_argument(inst, idx) { return err!(inst, "{} is a ghost value used by a real [{}] instruction", val, @@ -134,6 +137,32 @@ impl<'a> LivenessVerifier<'a> { } } + /// Is argument `argnum` on `inst` a branch argument that leads to a ghost EBB argument? + fn is_ghost_branch_argument(&self, inst: Inst, argnum: usize) -> bool { + let dest = match self.func.dfg[inst].branch_destination() { + Some(d) => d, + None => return false, + }; + + let fixed_args = self.func.dfg[inst] + .opcode() + .constraints() + .fixed_value_arguments(); + if argnum < fixed_args { + return false; + } + + // If the EBB argument value in the destination is a ghost value, we'll allow a ghost + // branch argument. + self.func + .dfg + .ebb_args(dest) + .get(argnum - fixed_args) + .and_then(|&v| self.liveness.get(v)) + .map(|lr| lr.affinity.is_none()) + .unwrap_or(false) + } + /// Check the integrity of the live range `lr`. fn check_lr(&self, def: ProgramPoint, val: Value, lr: &LiveRange) -> Result { let l = &self.func.layout;