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:
4
Cargo.lock
generated
4
Cargo.lock
generated
@@ -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",
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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,
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -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,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -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, .. } => {
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user