diff --git a/cranelift/filetests/regalloc/basic.cton b/cranelift/filetests/regalloc/basic.cton index dd6cab11cf..89e160d75a 100644 --- a/cranelift/filetests/regalloc/basic.cton +++ b/cranelift/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;