[codegen] Add a pinned register that's entirely under the control of the user;
This commit is contained in:
@@ -11,6 +11,7 @@ pub struct RegBank {
|
||||
pub names: Vec<&'static str>,
|
||||
pub prefix: &'static str,
|
||||
pub pressure_tracking: bool,
|
||||
pub pinned_reg: Option<u16>,
|
||||
pub toprcs: Vec<RegClassIndex>,
|
||||
pub classes: Vec<RegClassIndex>,
|
||||
}
|
||||
@@ -23,6 +24,7 @@ impl RegBank {
|
||||
names: Vec<&'static str>,
|
||||
prefix: &'static str,
|
||||
pressure_tracking: bool,
|
||||
pinned_reg: Option<u16>,
|
||||
) -> 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<bool>,
|
||||
pub pinned_reg: Option<u16>,
|
||||
}
|
||||
|
||||
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,
|
||||
))
|
||||
}
|
||||
|
||||
|
||||
@@ -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, "};");
|
||||
|
||||
@@ -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]));
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user