diff --git a/cranelift/filetests/regalloc/multiple-returns.clif b/cranelift/filetests/regalloc/multiple-returns.clif index 6e36c9b9b8..6514e3e030 100644 --- a/cranelift/filetests/regalloc/multiple-returns.clif +++ b/cranelift/filetests/regalloc/multiple-returns.clif @@ -11,3 +11,13 @@ ebb0: ; check: v2 = iconst.i64 0 ; check: v3 = copy v2 ; check: return v2, v3 + +; Same thing, now with a fallthrough_return. +function %multiple_returns() -> i64, i64 { +ebb0: + v2 = iconst.i64 0 + fallthrough_return v2, v2 +} +; check: v2 = iconst.i64 0 +; check: v3 = copy v2 +; check: fallthrough_return v2, v3 diff --git a/lib/codegen/src/regalloc/spilling.rs b/lib/codegen/src/regalloc/spilling.rs index e4b1d52e48..168956703c 100644 --- a/lib/codegen/src/regalloc/spilling.rs +++ b/lib/codegen/src/regalloc/spilling.rs @@ -134,11 +134,8 @@ impl<'a> Context<'a> { self.process_spills(tracker); while let Some(inst) = self.cur.next_inst() { - if let Some(constraints) = self - .encinfo - .operand_constraints(self.cur.func.encodings[inst]) - { - self.visit_inst(inst, ebb, constraints, tracker); + if !self.cur.func.dfg[inst].opcode().is_ghost() { + self.visit_inst(inst, ebb, tracker); } else { let (_throughs, kills) = tracker.process_ghost(inst); self.free_regs(kills); @@ -237,17 +234,15 @@ impl<'a> Context<'a> { self.free_dead_regs(params); } - fn visit_inst( - &mut self, - inst: Inst, - ebb: Ebb, - constraints: &RecipeConstraints, - tracker: &mut LiveValueTracker, - ) { + fn visit_inst(&mut self, inst: Inst, ebb: Ebb, tracker: &mut LiveValueTracker) { debug!("Inst {}, {}", self.cur.display_inst(inst), self.pressure); debug_assert_eq!(self.cur.current_inst(), Some(inst)); debug_assert_eq!(self.cur.current_ebb(), Some(ebb)); + let constraints = self + .encinfo + .operand_constraints(self.cur.func.encodings[inst]); + // We may need to resolve register constraints if there are any noteworthy uses. debug_assert!(self.reg_uses.is_empty()); self.collect_reg_uses(inst, ebb, constraints); @@ -282,23 +277,25 @@ impl<'a> Context<'a> { // Make sure we have enough registers for the register defs. // Dead defs are included here. They need a register too. // No need to process call return values, they are in fixed registers. - for op in constraints.outs { - if op.kind != ConstraintKind::Stack { - // Add register def to pressure, spill if needed. - while let Err(mask) = self.pressure.take_transient(op.regclass) { - debug!("Need {} reg from {} throughs", op.regclass, throughs.len()); - match self.spill_candidate(mask, throughs) { - Some(cand) => self.spill_reg(cand), - None => panic!( - "Ran out of {} registers for {}", - op.regclass, - self.cur.display_inst(inst) - ), + if let Some(constraints) = constraints { + for op in constraints.outs { + if op.kind != ConstraintKind::Stack { + // Add register def to pressure, spill if needed. + while let Err(mask) = self.pressure.take_transient(op.regclass) { + debug!("Need {} reg from {} throughs", op.regclass, throughs.len()); + match self.spill_candidate(mask, throughs) { + Some(cand) => self.spill_reg(cand), + None => panic!( + "Ran out of {} registers for {}", + op.regclass, + self.cur.display_inst(inst) + ), + } } } } + self.pressure.reset_transient(); } - self.pressure.reset_transient(); // Restore pressure state, compute pressure with affinities from `defs`. // Exclude dead defs. Includes call return values. @@ -315,35 +312,42 @@ impl<'a> Context<'a> { // We are assuming here that if a value is used both by a fixed register operand and a register // class operand, they two are compatible. We are also assuming that two register class // operands are always compatible. - fn collect_reg_uses(&mut self, inst: Inst, ebb: Ebb, constraints: &RecipeConstraints) { + fn collect_reg_uses(&mut self, inst: Inst, ebb: Ebb, constraints: Option<&RecipeConstraints>) { let args = self.cur.func.dfg.inst_args(inst); - for (idx, (op, &arg)) in constraints.ins.iter().zip(args).enumerate() { - let mut reguse = RegUse::new(arg, idx, op.regclass.into()); - let lr = &self.liveness[arg]; - let ctx = self.liveness.context(&self.cur.func.layout); - match op.kind { - ConstraintKind::Stack => continue, - ConstraintKind::FixedReg(_) => reguse.fixed = true, - ConstraintKind::Tied(_) => { - // A tied operand must kill the used value. - reguse.tied = !lr.killed_at(inst, ebb, ctx); + let num_fixed_ins = if let Some(constraints) = constraints { + for (idx, (op, &arg)) in constraints.ins.iter().zip(args).enumerate() { + let mut reguse = RegUse::new(arg, idx, op.regclass.into()); + let lr = &self.liveness[arg]; + let ctx = self.liveness.context(&self.cur.func.layout); + match op.kind { + ConstraintKind::Stack => continue, + ConstraintKind::FixedReg(_) => reguse.fixed = true, + ConstraintKind::Tied(_) => { + // A tied operand must kill the used value. + reguse.tied = !lr.killed_at(inst, ebb, ctx); + } + ConstraintKind::FixedTied(_) => { + reguse.fixed = true; + reguse.tied = !lr.killed_at(inst, ebb, ctx); + } + ConstraintKind::Reg => {} } - ConstraintKind::FixedTied(_) => { - reguse.fixed = true; - reguse.tied = !lr.killed_at(inst, ebb, ctx); + if lr.affinity.is_stack() { + reguse.spilled = true; } - ConstraintKind::Reg => {} - } - if lr.affinity.is_stack() { - reguse.spilled = true; - } - // Only collect the interesting register uses. - if reguse.fixed || reguse.tied || reguse.spilled { - debug!(" reguse: {}", reguse); - self.reg_uses.push(reguse); + // Only collect the interesting register uses. + if reguse.fixed || reguse.tied || reguse.spilled { + debug!(" reguse: {}", reguse); + self.reg_uses.push(reguse); + } } - } + constraints.ins.len() + } else { + // A non-ghost instruction with no constraints can't have any + // fixed operands. + 0 + }; // Similarly, for return instructions, collect uses of ABI-defined // return values. @@ -356,7 +360,7 @@ impl<'a> Context<'a> { for (ret_idx, (ret, &arg)) in self.cur.func.signature.returns.iter().zip(args).enumerate() { - let idx = constraints.ins.len() + ret_idx; + let idx = num_fixed_ins + ret_idx; let unit = match ret.location { ArgumentLoc::Unassigned => { panic!("function return signature should be legalized")