mach backend: use vectors instead of sets to remember set of uses/defs for calls;

This avoids the set uniqueness (hashing) test, reduces memory
churn when re-mapping virtual register onto real registers, and is
generally more memory-efficient.
This commit is contained in:
Benjamin Bouvier
2020-06-01 15:09:00 +02:00
parent cfa0527794
commit e227608510
6 changed files with 47 additions and 58 deletions

4
Cargo.lock generated
View File

@@ -1640,9 +1640,9 @@ dependencies = [
[[package]] [[package]]
name = "regalloc" name = "regalloc"
version = "0.0.24" version = "0.0.25"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5842bece8a4b1690ffa6d9d959081c1d5d851ee4337a36c0a121fafe8c16add2" checksum = "cca5b48c9db66c5ba084e4660b4c0cfe8b551a96074bc04b7c11de86ad0bf1f9"
dependencies = [ dependencies = [
"log", "log",
"rustc-hash", "rustc-hash",

View File

@@ -25,7 +25,7 @@ smallvec = { version = "1.0.0" }
thiserror = "1.0.4" thiserror = "1.0.4"
byteorder = { version = "1.3.2", default-features = false } byteorder = { version = "1.3.2", default-features = false }
peepmatic-runtime = { path = "../peepmatic/crates/runtime", optional = true } peepmatic-runtime = { path = "../peepmatic/crates/runtime", optional = true }
regalloc = "0.0.24" regalloc = "0.0.25"
# It is a goal of the cranelift-codegen crate to have minimal external dependencies. # It is a goal of the cranelift-codegen crate to have minimal external dependencies.
# Please don't add any unless they are essential to the task of creating binary # Please don't add any unless they are essential to the task of creating binary
# machine code. Integration tests that need external dependencies can be # machine code. Integration tests that need external dependencies can be

View File

@@ -655,21 +655,21 @@ fn is_caller_save(call_conv: isa::CallConv, r: RealReg) -> bool {
} }
} }
fn get_caller_saves_set(call_conv: isa::CallConv) -> Set<Writable<Reg>> { fn get_caller_saves(call_conv: isa::CallConv) -> Vec<Writable<Reg>> {
let mut set = Set::empty(); let mut caller_saved = Vec::new();
for i in 0..29 { for i in 0..29 {
let x = writable_xreg(i); let x = writable_xreg(i);
if is_caller_save(call_conv, x.to_reg().to_real_reg()) { if is_caller_save(call_conv, x.to_reg().to_real_reg()) {
set.insert(x); caller_saved.push(x);
} }
} }
for i in 0..32 { for i in 0..32 {
let v = writable_vreg(i); let v = writable_vreg(i);
if is_caller_save(call_conv, v.to_reg().to_real_reg()) { if is_caller_save(call_conv, v.to_reg().to_real_reg()) {
set.insert(v); caller_saved.push(v);
} }
} }
set caller_saved
} }
impl ABIBody for AArch64ABIBody { impl ABIBody for AArch64ABIBody {
@@ -1111,28 +1111,28 @@ enum CallDest {
/// AArch64 ABI object for a function call. /// AArch64 ABI object for a function call.
pub struct AArch64ABICall { pub struct AArch64ABICall {
sig: ABISig, sig: ABISig,
uses: Set<Reg>, uses: Vec<Reg>,
defs: Set<Writable<Reg>>, defs: Vec<Writable<Reg>>,
dest: CallDest, dest: CallDest,
loc: ir::SourceLoc, loc: ir::SourceLoc,
opcode: ir::Opcode, opcode: ir::Opcode,
} }
fn abisig_to_uses_and_defs(sig: &ABISig) -> (Set<Reg>, Set<Writable<Reg>>) { fn abisig_to_uses_and_defs(sig: &ABISig) -> (Vec<Reg>, Vec<Writable<Reg>>) {
// Compute uses: all arg regs. // Compute uses: all arg regs.
let mut uses = Set::empty(); let mut uses = Vec::new();
for arg in &sig.args { for arg in &sig.args {
match arg { match arg {
&ABIArg::Reg(reg, _) => uses.insert(reg.to_reg()), &ABIArg::Reg(reg, _) => uses.push(reg.to_reg()),
_ => {} _ => {}
} }
} }
// Compute defs: all retval regs, and all caller-save (clobbered) regs. // Compute defs: all retval regs, and all caller-save (clobbered) regs.
let mut defs = get_caller_saves_set(sig.call_conv); let mut defs = get_caller_saves(sig.call_conv);
for ret in &sig.rets { for ret in &sig.rets {
match ret { match ret {
&ABIArg::Reg(reg, _) => defs.insert(Writable::from_reg(reg.to_reg())), &ABIArg::Reg(reg, _) => defs.push(Writable::from_reg(reg.to_reg())),
_ => {} _ => {}
} }
} }
@@ -1271,14 +1271,14 @@ impl ABICall for AArch64ABICall {
fn emit_call<C: LowerCtx<I = Self::I>>(&mut self, ctx: &mut C) { fn emit_call<C: LowerCtx<I = Self::I>>(&mut self, ctx: &mut C) {
let (uses, defs) = ( let (uses, defs) = (
mem::replace(&mut self.uses, Set::empty()), mem::replace(&mut self.uses, Default::default()),
mem::replace(&mut self.defs, Set::empty()), mem::replace(&mut self.defs, Default::default()),
); );
match &self.dest { match &self.dest {
&CallDest::ExtName(ref name, RelocDistance::Near) => ctx.emit(Inst::Call { &CallDest::ExtName(ref name, RelocDistance::Near) => ctx.emit(Inst::Call {
dest: Box::new(name.clone()), dest: Box::new(name.clone()),
uses: Box::new(uses), uses: uses.into_boxed_slice(),
defs: Box::new(defs), defs: defs.into_boxed_slice(),
loc: self.loc, loc: self.loc,
opcode: self.opcode, opcode: self.opcode,
}), }),
@@ -1291,16 +1291,16 @@ impl ABICall for AArch64ABICall {
}); });
ctx.emit(Inst::CallInd { ctx.emit(Inst::CallInd {
rn: spilltmp_reg(), rn: spilltmp_reg(),
uses: Box::new(uses), uses: uses.into_boxed_slice(),
defs: Box::new(defs), defs: defs.into_boxed_slice(),
loc: self.loc, loc: self.loc,
opcode: self.opcode, opcode: self.opcode,
}); });
} }
&CallDest::Reg(reg) => ctx.emit(Inst::CallInd { &CallDest::Reg(reg) => ctx.emit(Inst::CallInd {
rn: reg, rn: reg,
uses: Box::new(uses), uses: uses.into_boxed_slice(),
defs: Box::new(defs), defs: defs.into_boxed_slice(),
loc: self.loc, loc: self.loc,
opcode: self.opcode, opcode: self.opcode,
}), }),

View File

@@ -2114,8 +2114,8 @@ fn test_aarch64_binemit() {
insns.push(( insns.push((
Inst::Call { Inst::Call {
dest: Box::new(ExternalName::testcase("test0")), dest: Box::new(ExternalName::testcase("test0")),
uses: Box::new(Set::empty()), uses: Vec::new().into_boxed_slice(),
defs: Box::new(Set::empty()), defs: Vec::new().into_boxed_slice(),
loc: SourceLoc::default(), loc: SourceLoc::default(),
opcode: Opcode::Call, opcode: Opcode::Call,
}, },
@@ -2126,8 +2126,8 @@ fn test_aarch64_binemit() {
insns.push(( insns.push((
Inst::CallInd { Inst::CallInd {
rn: xreg(10), rn: xreg(10),
uses: Box::new(Set::empty()), uses: Vec::new().into_boxed_slice(),
defs: Box::new(Set::empty()), defs: Vec::new().into_boxed_slice(),
loc: SourceLoc::default(), loc: SourceLoc::default(),
opcode: Opcode::CallIndirect, opcode: Opcode::CallIndirect,
}, },

View File

@@ -12,7 +12,7 @@ use crate::machinst::*;
use crate::{settings, CodegenError, CodegenResult}; use crate::{settings, CodegenError, CodegenResult};
use regalloc::{RealRegUniverse, Reg, RegClass, SpillSlot, VirtualReg, Writable}; use regalloc::{RealRegUniverse, Reg, RegClass, SpillSlot, VirtualReg, Writable};
use regalloc::{RegUsageCollector, RegUsageMapper, Set}; use regalloc::{RegUsageCollector, RegUsageMapper};
use alloc::boxed::Box; use alloc::boxed::Box;
use alloc::vec::Vec; use alloc::vec::Vec;
@@ -650,16 +650,16 @@ pub enum Inst {
/// target. /// target.
Call { Call {
dest: Box<ExternalName>, dest: Box<ExternalName>,
uses: Box<Set<Reg>>, uses: Box<[Reg]>,
defs: Box<Set<Writable<Reg>>>, defs: Box<[Writable<Reg>]>,
loc: SourceLoc, loc: SourceLoc,
opcode: Opcode, opcode: Opcode,
}, },
/// A machine indirect-call instruction. /// A machine indirect-call instruction.
CallInd { CallInd {
rn: Reg, rn: Reg,
uses: Box<Set<Reg>>, uses: Box<[Reg]>,
defs: Box<Set<Writable<Reg>>>, defs: Box<[Writable<Reg>]>,
loc: SourceLoc, loc: SourceLoc,
opcode: Opcode, opcode: Opcode,
}, },
@@ -1729,19 +1729,12 @@ fn aarch64_map_regs<RUM: RegUsageMapper>(inst: &mut Inst, mapper: &RUM) {
ref mut defs, ref mut defs,
.. ..
} => { } => {
// TODO: add `map_mut()` to regalloc.rs's Set. for r in uses.iter_mut() {
let new_uses = uses.map(|r| { map_use(mapper, r);
let mut r = *r; }
map_use(mapper, &mut r); for r in defs.iter_mut() {
r map_def(mapper, r);
}); }
let new_defs = defs.map(|r| {
let mut r = *r;
map_def(mapper, &mut r);
r
});
*uses = Box::new(new_uses);
*defs = Box::new(new_defs);
} }
&mut Inst::Ret | &mut Inst::EpiloguePlaceholder => {} &mut Inst::Ret | &mut Inst::EpiloguePlaceholder => {}
&mut Inst::CallInd { &mut Inst::CallInd {
@@ -1750,19 +1743,12 @@ fn aarch64_map_regs<RUM: RegUsageMapper>(inst: &mut Inst, mapper: &RUM) {
ref mut rn, ref mut rn,
.. ..
} => { } => {
// TODO: add `map_mut()` to regalloc.rs's Set. for r in uses.iter_mut() {
let new_uses = uses.map(|r| { map_use(mapper, r);
let mut r = *r; }
map_use(mapper, &mut r); for r in defs.iter_mut() {
r map_def(mapper, r);
}); }
let new_defs = defs.map(|r| {
let mut r = *r;
map_def(mapper, &mut r);
r
});
*uses = Box::new(new_uses);
*defs = Box::new(new_defs);
map_use(mapper, rn); map_use(mapper, rn);
} }
&mut Inst::CondBr { ref mut kind, .. } | &mut Inst::OneWayCondBr { ref mut kind, .. } => { &mut Inst::CondBr { ref mut kind, .. } | &mut Inst::OneWayCondBr { ref mut kind, .. } => {

View File

@@ -167,5 +167,8 @@ pub trait ABICall {
/// registers are also logically defs, but should never be read; their /// registers are also logically defs, but should never be read; their
/// values are "defined" (to the regalloc) but "undefined" in every other /// values are "defined" (to the regalloc) but "undefined" in every other
/// sense.) /// sense.)
///
/// This function should only be called once, as it is allowed to re-use
/// parts of the ABICall object in emitting instructions.
fn emit_call<C: LowerCtx<I = Self::I>>(&mut self, ctx: &mut C); fn emit_call<C: LowerCtx<I = Self::I>>(&mut self, ctx: &mut C);
} }