diff --git a/cranelift/codegen/meta/src/cdsl/regs.rs b/cranelift/codegen/meta/src/cdsl/regs.rs index e7a3464d10..920b1f5426 100644 --- a/cranelift/codegen/meta/src/cdsl/regs.rs +++ b/cranelift/codegen/meta/src/cdsl/regs.rs @@ -11,6 +11,7 @@ pub struct RegBank { pub names: Vec<&'static str>, pub prefix: &'static str, pub pressure_tracking: bool, + pub pinned_reg: Option, pub toprcs: Vec, pub classes: Vec, } @@ -23,6 +24,7 @@ impl RegBank { names: Vec<&'static str>, prefix: &'static str, pressure_tracking: bool, + pinned_reg: Option, ) -> Self { RegBank { name, @@ -31,6 +33,7 @@ impl RegBank { names, prefix, pressure_tracking, + pinned_reg, toprcs: Vec::new(), classes: Vec::new(), } @@ -183,6 +186,7 @@ pub struct RegBankBuilder { pub names: Vec<&'static str>, pub prefix: &'static str, pub pressure_tracking: Option, + pub pinned_reg: Option, } impl RegBankBuilder { @@ -193,6 +197,7 @@ impl RegBankBuilder { names: vec![], prefix, pressure_tracking: None, + pinned_reg: None, } } pub fn units(mut self, units: u8) -> Self { @@ -207,6 +212,11 @@ impl RegBankBuilder { self.pressure_tracking = Some(track); self } + pub fn pinned_reg(mut self, unit: u16) -> Self { + assert!(unit < (self.units as u16)); + self.pinned_reg = Some(unit); + self + } } pub struct IsaRegsBuilder { @@ -246,6 +256,7 @@ impl IsaRegsBuilder { builder .pressure_tracking .expect("Pressure tracking must be explicitly set"), + builder.pinned_reg, )) } diff --git a/cranelift/codegen/meta/src/gen_registers.rs b/cranelift/codegen/meta/src/gen_registers.rs index 5e0fdac57b..08edfa9e21 100644 --- a/cranelift/codegen/meta/src/gen_registers.rs +++ b/cranelift/codegen/meta/src/gen_registers.rs @@ -56,6 +56,7 @@ fn gen_regclass(isa: &TargetIsa, reg_class: &RegClass, fmt: &mut Formatter) { fmtln!(fmt, "first: {},", reg_bank.first_unit + reg_class.start); fmtln!(fmt, "subclasses: {:#x},", reg_class.subclass_mask()); fmtln!(fmt, "mask: [{}],", mask); + fmtln!(fmt, "pinned_reg: {:?},", reg_bank.pinned_reg); fmtln!(fmt, "info: &INFO,"); }); fmtln!(fmt, "};"); diff --git a/cranelift/codegen/meta/src/isa/x86/encodings.rs b/cranelift/codegen/meta/src/isa/x86/encodings.rs index ecdff33740..71f1042100 100644 --- a/cranelift/codegen/meta/src/isa/x86/encodings.rs +++ b/cranelift/codegen/meta/src/isa/x86/encodings.rs @@ -372,6 +372,7 @@ pub(crate) fn define( let fpromote = shared.by_name("fpromote"); let fsub = shared.by_name("fsub"); let func_addr = shared.by_name("func_addr"); + let get_pinned_reg = shared.by_name("get_pinned_reg"); let iadd = shared.by_name("iadd"); let iadd_cout = shared.by_name("iadd_cout"); let iadd_cin = shared.by_name("iadd_cin"); @@ -421,6 +422,7 @@ pub(crate) fn define( let scalar_to_vector = shared.by_name("scalar_to_vector"); let selectif = shared.by_name("selectif"); let sextend = shared.by_name("sextend"); + let set_pinned_reg = shared.by_name("set_pinned_reg"); let sload16 = shared.by_name("sload16"); let sload16_complex = shared.by_name("sload16_complex"); let sload32 = shared.by_name("sload32"); @@ -516,6 +518,7 @@ pub(crate) fn define( let rec_furm = r.template("furm"); let rec_furm_reg_to_ssa = r.template("furm_reg_to_ssa"); let rec_furmi_rnd = r.template("furmi_rnd"); + let rec_get_pinned_reg = r.recipe("get_pinned_reg"); let rec_got_fnaddr8 = r.template("got_fnaddr8"); let rec_got_gvaddr8 = r.template("got_gvaddr8"); let rec_gvaddr4 = r.template("gvaddr4"); @@ -569,6 +572,7 @@ pub(crate) fn define( let rec_safepoint = r.recipe("safepoint"); let rec_setf_abcd = r.template("setf_abcd"); let rec_seti_abcd = r.template("seti_abcd"); + let rec_set_pinned_reg = r.template("set_pinned_reg"); let rec_spaddr4_id = r.template("spaddr4_id"); let rec_spaddr8_id = r.template("spaddr8_id"); let rec_spillSib32 = r.template("spillSib32"); @@ -619,6 +623,13 @@ pub(crate) fn define( // Definitions. let mut e = PerCpuModeEncodings::new(); + // The pinned reg is fixed to a certain value entirely user-controlled, so it generates nothing! + e.enc64_rec(get_pinned_reg.bind(I64), rec_get_pinned_reg, 0); + e.enc_x86_64( + set_pinned_reg.bind(I64), + rec_set_pinned_reg.opcodes(vec![0x89]).rex().w(), + ); + e.enc_i32_i64(iadd, rec_rr.opcodes(vec![0x01])); e.enc_i32_i64(iadd_cout, rec_rr.opcodes(vec![0x01])); e.enc_i32_i64(iadd_cin, rec_rin.opcodes(vec![0x11])); diff --git a/cranelift/codegen/meta/src/isa/x86/recipes.rs b/cranelift/codegen/meta/src/isa/x86/recipes.rs index 6a369f34c6..a5db0c1b97 100644 --- a/cranelift/codegen/meta/src/isa/x86/recipes.rs +++ b/cranelift/codegen/meta/src/isa/x86/recipes.rs @@ -351,6 +351,7 @@ pub(crate) fn define<'shared>( let reg_rax = Register::new(gpr, regs.regunit_by_name(gpr, "rax")); let reg_rcx = Register::new(gpr, regs.regunit_by_name(gpr, "rcx")); let reg_rdx = Register::new(gpr, regs.regunit_by_name(gpr, "rdx")); + let reg_r15 = Register::new(gpr, regs.regunit_by_name(gpr, "r15")); // Stack operand with a 32-bit signed displacement from either RBP or RSP. let stack_gpr32 = Stack::new(gpr); @@ -428,6 +429,25 @@ pub(crate) fn define<'shared>( .emit(""), ); + recipes.add_recipe( + EncodingRecipeBuilder::new("get_pinned_reg", f_nullary, 0) + .operands_out(vec![reg_r15]) + .emit(""), + ); + // umr with a fixed register output that's r15. + recipes.add_template_recipe( + EncodingRecipeBuilder::new("set_pinned_reg", f_unary, 1) + .operands_in(vec![gpr]) + .clobbers_flags(false) + .emit( + r#" + let r15 = RU::r15.into(); + {{PUT_OP}}(bits, rex2(r15, in_reg0), sink); + modrm_rr(r15, in_reg0, sink); + "#, + ), + ); + // No-op fills, created by late-stage redundant-fill removal. recipes.add_recipe( EncodingRecipeBuilder::new("fillnull", f_unary, 0) diff --git a/cranelift/codegen/meta/src/isa/x86/registers.rs b/cranelift/codegen/meta/src/isa/x86/registers.rs index 3039bafba2..4157084c15 100644 --- a/cranelift/codegen/meta/src/isa/x86/registers.rs +++ b/cranelift/codegen/meta/src/isa/x86/registers.rs @@ -6,7 +6,8 @@ pub fn define() -> IsaRegs { let builder = RegBankBuilder::new("IntRegs", "r") .units(16) .names(vec!["rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi"]) - .track_pressure(true); + .track_pressure(true) + .pinned_reg(15); let int_regs = regs.add_bank(builder); let builder = RegBankBuilder::new("FloatRegs", "xmm") diff --git a/cranelift/codegen/meta/src/shared/instructions.rs b/cranelift/codegen/meta/src/shared/instructions.rs index f636141a37..67e2891068 100644 --- a/cranelift/codegen/meta/src/shared/instructions.rs +++ b/cranelift/codegen/meta/src/shared/instructions.rs @@ -964,6 +964,34 @@ pub(crate) fn define( .operands_out(vec![addr]), ); + // Note this instruction is marked as having other side-effects, so GVN won't try to hoist it, + // which would result in it being subject to spilling. While not hoisting would generally hurt + // performance, since a computed value used many times may need to be regenerated before each + // use, it is not the case here: this instruction doesn't generate any code. That's because, + // by definition the pinned register is never used by the register allocator, but is written to + // and read explicitly and exclusively by set_pinned_reg and get_pinned_reg. + ig.push( + Inst::new( + "get_pinned_reg", + r#" + Gets the content of the pinned register, when it's enabled. + "#, + ) + .operands_out(vec![addr]) + .other_side_effects(true), + ); + + ig.push( + Inst::new( + "set_pinned_reg", + r#" + Sets the content of the pinned register, when it's enabled. + "#, + ) + .operands_in(vec![addr]) + .other_side_effects(true), + ); + let TableOffset = &TypeVar::new( "TableOffset", "An unsigned table offset", diff --git a/cranelift/codegen/meta/src/shared/settings.rs b/cranelift/codegen/meta/src/shared/settings.rs index 6561ee9856..e7c567c345 100644 --- a/cranelift/codegen/meta/src/shared/settings.rs +++ b/cranelift/codegen/meta/src/shared/settings.rs @@ -84,6 +84,17 @@ pub fn define() -> SettingGroup { false, ); + settings.add_bool( + "enable_pinned_reg", + r#"Enable the use of the pinned register. + + This register is excluded from register allocation, and is completely under the control of + the end-user. It is possible to read it via the get_pinned_reg instruction, and to set it + with the set_pinned_reg instruction. + "#, + false, + ); + settings.add_bool("enable_simd", "Enable the use of SIMD instructions.", false); settings.add_bool( diff --git a/cranelift/codegen/src/isa/registers.rs b/cranelift/codegen/src/isa/registers.rs index 3c1b4262c1..f7fcdcac2e 100644 --- a/cranelift/codegen/src/isa/registers.rs +++ b/cranelift/codegen/src/isa/registers.rs @@ -154,6 +154,12 @@ pub struct RegClassData { /// The global `RegInfo` instance containing this register class. pub info: &'static RegInfo, + + /// The "pinned" register of the associated register bank. + /// + /// This register must be non-volatile (callee-preserved) and must not be the fixed + /// output register of any instruction. + pub pinned_reg: Option, } impl RegClassData { @@ -201,6 +207,15 @@ impl RegClassData { pub fn contains(&self, regunit: RegUnit) -> bool { self.mask[(regunit / 32) as usize] & (1u32 << (regunit % 32)) != 0 } + + /// If the pinned register is used, is the given regunit the pinned register of this class? + #[inline] + pub fn is_pinned_reg(&self, enabled: bool, regunit: RegUnit) -> bool { + enabled + && self + .pinned_reg + .map_or(false, |pinned_reg| pinned_reg == regunit) + } } impl fmt::Display for RegClassData { diff --git a/cranelift/codegen/src/isa/x86/abi.rs b/cranelift/codegen/src/isa/x86/abi.rs index 0072935229..6252a3fb77 100644 --- a/cranelift/codegen/src/isa/x86/abi.rs +++ b/cranelift/codegen/src/isa/x86/abi.rs @@ -89,9 +89,8 @@ impl ArgAssigner for Args { let reg = FPR.unit(self.fpr_used); self.fpr_used += 1; return ArgumentLoc::Reg(reg).into(); - } else { - return ValueConversion::VectorSplit.into(); } + return ValueConversion::VectorSplit.into(); } // Large integers and booleans are broken down to fit in a register. @@ -229,7 +228,7 @@ pub fn regclass_for_abi_type(ty: ir::Type) -> RegClass { } /// Get the set of allocatable registers for `func`. -pub fn allocatable_registers(_func: &ir::Function, triple: &Triple) -> RegisterSet { +pub fn allocatable_registers(triple: &Triple, flags: &shared_settings::Flags) -> RegisterSet { let mut regs = RegisterSet::new(); regs.take(GPR, RU::rsp as RegUnit); regs.take(GPR, RU::rbp as RegUnit); @@ -240,6 +239,15 @@ pub fn allocatable_registers(_func: &ir::Function, triple: &Triple) -> RegisterS regs.take(GPR, GPR.unit(i)); regs.take(FPR, FPR.unit(i)); } + if flags.enable_pinned_reg() { + unimplemented!("Pinned register not implemented on x86-32."); + } + } else { + // Choose r15 as the pinned register on 64-bits: it is non-volatile on native ABIs and + // isn't the fixed output register of any instruction. + if flags.enable_pinned_reg() { + regs.take(GPR, RU::r15 as RegUnit); + } } regs diff --git a/cranelift/codegen/src/isa/x86/mod.rs b/cranelift/codegen/src/isa/x86/mod.rs index 9244791ea8..52ab055440 100644 --- a/cranelift/codegen/src/isa/x86/mod.rs +++ b/cranelift/codegen/src/isa/x86/mod.rs @@ -119,8 +119,8 @@ impl TargetIsa for Isa { abi::regclass_for_abi_type(ty) } - fn allocatable_registers(&self, func: &ir::Function) -> regalloc::RegisterSet { - abi::allocatable_registers(func, &self.triple) + fn allocatable_registers(&self, _func: &ir::Function) -> regalloc::RegisterSet { + abi::allocatable_registers(&self.triple, &self.shared_flags) } #[cfg(feature = "testing_hooks")] diff --git a/cranelift/codegen/src/regalloc/coloring.rs b/cranelift/codegen/src/regalloc/coloring.rs index f23c738583..1e69c342a9 100644 --- a/cranelift/codegen/src/regalloc/coloring.rs +++ b/cranelift/codegen/src/regalloc/coloring.rs @@ -106,6 +106,8 @@ struct Context<'a> { // Pristine set of registers that the allocator can use. // This set remains immutable, we make clones. usable_regs: RegisterSet, + + uses_pinned_reg: bool, } impl Coloring { @@ -137,6 +139,7 @@ impl Coloring { debug!("Coloring for:\n{}", func.display(isa)); let mut ctx = Context { usable_regs: isa.allocatable_registers(func), + uses_pinned_reg: isa.flags().enable_pinned_reg(), cur: EncCursor::new(func, isa), reginfo: isa.register_info(), encinfo: isa.encoding_info(), @@ -151,6 +154,12 @@ impl Coloring { } impl<'a> Context<'a> { + /// Is the pinned register usage enabled, and is this register the pinned register? + #[inline] + fn is_pinned_reg(&self, rc: RegClass, reg: RegUnit) -> bool { + rc.is_pinned_reg(self.uses_pinned_reg, reg) + } + /// Run the coloring algorithm. fn run(&mut self, tracker: &mut LiveValueTracker) { self.cur @@ -425,9 +434,11 @@ impl<'a> Context<'a> { // Program the solver with register constraints for the input side. self.solver.reset(®s.input); + if let Some(constraints) = constraints { self.program_input_constraints(inst, constraints.ins); } + let call_sig = self.cur.func.dfg.call_signature(inst); if let Some(sig) = call_sig { self.program_input_abi(inst, AbiParams::Parameters(sig)); @@ -457,6 +468,7 @@ impl<'a> Context<'a> { if self.solver.has_fixed_input_conflicts() { self.divert_fixed_input_conflicts(tracker.live()); } + self.solver.inputs_done(); // Update the live value tracker with this instruction. @@ -467,6 +479,13 @@ impl<'a> Context<'a> { if let Affinity::Reg(rci) = lv.affinity { let rc = self.reginfo.rc(rci); let reg = self.divert.reg(lv.value, &self.cur.func.locations); + + if self.is_pinned_reg(rc, reg) { + // Don't kill the pinned reg, either in the local or global register sets. + debug_assert!(lv.is_local, "pinned register SSA value can't be global"); + continue; + } + debug!( " kill {} in {} ({} {})", lv.value, @@ -506,6 +525,7 @@ impl<'a> Context<'a> { ); } } + if let Some(sig) = call_sig { self.program_output_abi( sig, @@ -515,6 +535,7 @@ impl<'a> Context<'a> { ®s.global, ); } + if let Some(constraints) = constraints { self.program_output_constraints( inst, @@ -596,16 +617,28 @@ impl<'a> Context<'a> { if let Affinity::Reg(rci) = lv.affinity { let rc = self.reginfo.rc(rci); + let reg = loc.unwrap_reg(); + + debug_assert!( + !self.is_pinned_reg(rc, reg) + || self.cur.func.dfg[inst].opcode() == Opcode::GetPinnedReg, + "pinned register may not be part of outputs for '{}'.", + self.cur.func.dfg[inst].opcode() + ); + + if self.is_pinned_reg(rc, reg) { + continue; + } // Remove the dead defs. if lv.endpoint == inst { - regs.input.free(rc, loc.unwrap_reg()); + regs.input.free(rc, reg); debug_assert!(lv.is_local); } // Track globals in their undiverted locations. if !lv.is_local && !replace_global_defines { - regs.global.take(rc, loc.unwrap_reg()); + regs.global.take(rc, reg); } } } @@ -626,14 +659,35 @@ impl<'a> Context<'a> { // already in a register. let cur_reg = self.divert.reg(value, &self.cur.func.locations); match op.kind { - ConstraintKind::FixedReg(regunit) | ConstraintKind::FixedTied(regunit) => { + ConstraintKind::FixedReg(regunit) => { // Add the fixed constraint even if `cur_reg == regunit`. // It is possible that we will want to convert the value to a variable later, // and this identity assignment prevents that from happening. self.solver .reassign_in(value, op.regclass, cur_reg, regunit); } - ConstraintKind::Reg | ConstraintKind::Tied(_) => { + ConstraintKind::FixedTied(regunit) => { + // The pinned register may not be part of a fixed tied requirement. If this + // becomes the case, then it must be changed to a different register. + debug_assert!( + !self.is_pinned_reg(op.regclass, regunit), + "see comment above" + ); + // See comment right above. + self.solver + .reassign_in(value, op.regclass, cur_reg, regunit); + } + ConstraintKind::Tied(_) => { + if self.is_pinned_reg(op.regclass, cur_reg) { + // Divert the pinned register; it shouldn't be reused for a tied input. + if self.solver.can_add_var(op.regclass, cur_reg) { + self.solver.add_var(value, op.regclass, cur_reg); + } + } else if !op.regclass.contains(cur_reg) { + self.solver.add_var(value, op.regclass, cur_reg); + } + } + ConstraintKind::Reg => { if !op.regclass.contains(cur_reg) { self.solver.add_var(value, op.regclass, cur_reg); } @@ -664,10 +718,13 @@ impl<'a> Context<'a> { match op.kind { ConstraintKind::Reg | ConstraintKind::Tied(_) => { let cur_reg = self.divert.reg(value, &self.cur.func.locations); - // This is the opposite condition of `program_input_constraints()`. - if op.regclass.contains(cur_reg) { + + // This is the opposite condition of `program_input_constraints()`. The pinned + // register mustn't be added back as a variable. + if op.regclass.contains(cur_reg) && !self.is_pinned_reg(op.regclass, cur_reg) { // This code runs after calling `solver.inputs_done()` so we must identify - // the new variable as killed or live-through. + // the new variable as killed or live-through. Always special-case the + // pinned register as a through variable. let ctx = self.liveness.context(&self.cur.func.layout); if self.liveness[value].killed_at(inst, ctx.order.pp_ebb(inst), ctx) { self.solver.add_killed_var(value, op.regclass, cur_reg); @@ -778,7 +835,7 @@ impl<'a> Context<'a> { if pred(lr, self.liveness.context(&self.cur.func.layout)) { if let Affinity::Reg(rci) = lr.affinity { let rc = self.reginfo.rc(rci); - // Stack diversions should not be possible here. The only live transiently + // Stack diversions should not be possible here. They only live transiently // during `shuffle_inputs()`. self.solver.reassign_in( value, @@ -797,8 +854,8 @@ impl<'a> Context<'a> { } } - // Find existing live values that conflict with the fixed input register constraints programmed - // into the constraint solver. Convert them to solver variables so they can be diverted. + /// Find existing live values that conflict with the fixed input register constraints programmed + /// into the constraint solver. Convert them to solver variables so they can be diverted. fn divert_fixed_input_conflicts(&mut self, live: &[LiveValue]) { for lv in live { if let Affinity::Reg(rci) = lv.affinity { @@ -886,7 +943,9 @@ impl<'a> Context<'a> { reg: RegUnit, throughs: &[LiveValue], ) { - if !self.solver.add_fixed_output(rc, reg) { + // Pinned register is already unavailable in the solver, since it is copied in the + // available registers on entry. + if !self.is_pinned_reg(rc, reg) && !self.solver.add_fixed_output(rc, reg) { // The fixed output conflicts with some of the live-through registers. for lv in throughs { if let Affinity::Reg(rci) = lv.affinity { @@ -929,12 +988,12 @@ impl<'a> Context<'a> { // Find the input operand we're tied to. // The solver doesn't care about the output value. let arg = self.cur.func.dfg.inst_args(inst)[num as usize]; - if let Some(reg) = self.solver.add_tied_input( - arg, - op.regclass, - self.divert.reg(arg, &self.cur.func.locations), - !lv.is_local, - ) { + let reg = self.divert.reg(arg, &self.cur.func.locations); + + if let Some(reg) = + self.solver + .add_tied_input(arg, op.regclass, reg, !lv.is_local) + { // The value we're tied to has been assigned to a fixed register. // We need to make sure that fixed output register is compatible with the // global register set. @@ -1000,7 +1059,7 @@ impl<'a> Context<'a> { let toprc2 = self.reginfo.toprc(rci); let reg2 = self.divert.reg(lv.value, &self.cur.func.locations); if rc.contains(reg2) - && self.solver.can_add_var(lv.value, toprc2, reg2) + && self.solver.can_add_var(toprc2, reg2) && !self.is_live_on_outgoing_edge(lv.value) { self.solver.add_through_var(lv.value, toprc2, reg2); @@ -1061,8 +1120,15 @@ impl<'a> Context<'a> { for m in self.solver.moves() { match *m { Reg { - value, from, to, .. + value, + from, + to, + rc, } => { + debug_assert!( + !self.is_pinned_reg(rc, to), + "pinned register used in a regmove" + ); self.divert.regmove(value, from, to); self.cur.ins().regmove(value, from, to); } @@ -1086,8 +1152,12 @@ impl<'a> Context<'a> { value, from_slot, to, - .. + rc, } => { + debug_assert!( + !self.is_pinned_reg(rc, to), + "pinned register used in a regfill" + ); // These slots are single use, so mark `ss` as available again. let ss = slot[from_slot].take().expect("Using unallocated slot"); self.divert.regfill(value, ss, to); diff --git a/cranelift/codegen/src/regalloc/register_set.rs b/cranelift/codegen/src/regalloc/register_set.rs index fb7f208c59..e5edaa96d6 100644 --- a/cranelift/codegen/src/regalloc/register_set.rs +++ b/cranelift/codegen/src/regalloc/register_set.rs @@ -268,6 +268,7 @@ mod tests { subclasses: 0, mask: [0xf0000000, 0x0000000f, 0], info: &INFO, + pinned_reg: None, }; const DPR: RegClass = &RegClassData { @@ -280,6 +281,7 @@ mod tests { subclasses: 0, mask: [0x50000000, 0x0000000a, 0], info: &INFO, + pinned_reg: None, }; const INFO: RegInfo = RegInfo { diff --git a/cranelift/codegen/src/regalloc/solver.rs b/cranelift/codegen/src/regalloc/solver.rs index ec517028cc..35e17b050d 100644 --- a/cranelift/codegen/src/regalloc/solver.rs +++ b/cranelift/codegen/src/regalloc/solver.rs @@ -877,6 +877,7 @@ impl Solver { let d = v.iter(&self.regs_in, &self.regs_out, global_regs).len(); v.domain = cmp::min(d, u16::MAX as usize) as u16; } + // Solve for vars with small domains first to increase the chance of finding a solution. // // Also consider this case: @@ -949,9 +950,8 @@ impl Solver { // live registers be diverted. We need to make it a non-global value. if v.is_global && gregs.iter(rc).next().is_none() { return Err(SolverError::Global(v.value)); - } else { - return Err(SolverError::Divert(rc)); } + return Err(SolverError::Divert(rc)); } }; @@ -976,7 +976,7 @@ impl Solver { } /// Check if `value` can be added as a variable to help find a solution. - pub fn can_add_var(&mut self, _value: Value, constraint: RegClass, from: RegUnit) -> bool { + pub fn can_add_var(&mut self, constraint: RegClass, from: RegUnit) -> bool { !self.regs_in.is_avail(constraint, from) } } @@ -1030,7 +1030,7 @@ impl Solver { let mut avail = regs.clone(); let mut i = 0; while i < self.moves.len() + self.fills.len() { - // Don't even look at the fills until we've spent all the moves. Deferring these let's + // Don't even look at the fills until we've spent all the moves. Deferring these lets // us potentially reuse the claimed registers to resolve multiple cycles. if i >= self.moves.len() { self.moves.append(&mut self.fills); diff --git a/cranelift/codegen/src/settings.rs b/cranelift/codegen/src/settings.rs index f9bc9f6d1a..c22eb3f9a4 100644 --- a/cranelift/codegen/src/settings.rs +++ b/cranelift/codegen/src/settings.rs @@ -388,6 +388,7 @@ mod tests { avoid_div_traps = false\n\ enable_float = true\n\ enable_nan_canonicalization = false\n\ + enable_pinned_reg = false\n\ enable_simd = false\n\ enable_atomics = true\n\ enable_safepoints = false\n\ diff --git a/cranelift/codegen/src/verifier/mod.rs b/cranelift/codegen/src/verifier/mod.rs index 72fa1f7e17..5bc10d9d92 100644 --- a/cranelift/codegen/src/verifier/mod.rs +++ b/cranelift/codegen/src/verifier/mod.rs @@ -676,6 +676,26 @@ impl<'a> Verifier<'a> { self.verify_value_list(inst, args, errors)?; } + NullAry { + opcode: Opcode::GetPinnedReg, + } + | Unary { + opcode: Opcode::SetPinnedReg, + .. + } => { + if let Some(isa) = &self.isa { + if !isa.flags().enable_pinned_reg() { + return fatal!( + errors, + inst, + "GetPinnedReg/SetPinnedReg cannot be used without enable_pinned_reg" + ); + } + } else { + return fatal!(errors, inst, "GetPinnedReg/SetPinnedReg need an ISA!"); + } + } + // Exhaustive list so we can't forget to add new formats Unary { .. } | UnaryImm { .. }