Merge pull request #1852 from cfallin/reftypes
Reference type support in MachInst backend and on AArch64
This commit is contained in:
4
Cargo.lock
generated
4
Cargo.lock
generated
@@ -1680,9 +1680,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regalloc"
|
name = "regalloc"
|
||||||
version = "0.0.26"
|
version = "0.0.27"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7c03092d79e0fd610932d89ed53895a38c0dd3bcd317a0046e69940de32f1d95"
|
checksum = "b9ba8aaf5fe7cf307c6dbdaeed85478961d29e25e3bee5169e11b92fa9f027a8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"log",
|
"log",
|
||||||
"rustc-hash",
|
"rustc-hash",
|
||||||
|
|||||||
@@ -26,7 +26,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, version = "0.2.0" }
|
peepmatic-runtime = { path = "../peepmatic/crates/runtime", optional = true, version = "0.2.0" }
|
||||||
regalloc = "0.0.26"
|
regalloc = { version = "0.0.27" }
|
||||||
# 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
|
||||||
|
|||||||
@@ -61,3 +61,10 @@ pub fn is_constant_64bit(func: &Function, inst: Inst) -> Option<u64> {
|
|||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Is the given instruction a safepoint (i.e., potentially causes a GC, depending on the
|
||||||
|
/// embedding, and so requires reftyped values to be enumerated with a stackmap)?
|
||||||
|
pub fn is_safepoint(func: &Function, inst: Inst) -> bool {
|
||||||
|
let op = func.dfg[inst].opcode();
|
||||||
|
op.is_resumable_trap() || op.is_call()
|
||||||
|
}
|
||||||
|
|||||||
@@ -90,12 +90,13 @@
|
|||||||
//! - Return v1 in memory at `[P+8]`.
|
//! - Return v1 in memory at `[P+8]`.
|
||||||
//! - Return v0 in memory at `[P+16]`.
|
//! - Return v0 in memory at `[P+16]`.
|
||||||
|
|
||||||
|
use crate::binemit::Stackmap;
|
||||||
use crate::ir;
|
use crate::ir;
|
||||||
use crate::ir::types;
|
use crate::ir::types;
|
||||||
use crate::ir::types::*;
|
use crate::ir::types::*;
|
||||||
use crate::ir::{ArgumentExtension, StackSlot};
|
use crate::ir::{ArgumentExtension, StackSlot};
|
||||||
use crate::isa;
|
use crate::isa;
|
||||||
use crate::isa::aarch64::{inst::*, lower::ty_bits};
|
use crate::isa::aarch64::{inst::EmitState, inst::*, lower::ty_bits};
|
||||||
use crate::machinst::*;
|
use crate::machinst::*;
|
||||||
use crate::settings;
|
use crate::settings;
|
||||||
use crate::{CodegenError, CodegenResult};
|
use crate::{CodegenError, CodegenResult};
|
||||||
@@ -372,7 +373,10 @@ pub struct AArch64ABIBody {
|
|||||||
clobbered: Set<Writable<RealReg>>,
|
clobbered: Set<Writable<RealReg>>,
|
||||||
/// Total number of spillslots, from regalloc.
|
/// Total number of spillslots, from regalloc.
|
||||||
spillslots: Option<usize>,
|
spillslots: Option<usize>,
|
||||||
/// Total frame size.
|
/// "Total frame size", as defined by "distance between FP and nominal-SP".
|
||||||
|
/// Some items are pushed below nominal SP, so the function may actually use
|
||||||
|
/// more stack than this would otherwise imply. It is simply the initial
|
||||||
|
/// frame/allocation size needed for stackslots and spillslots.
|
||||||
total_frame_size: Option<u32>,
|
total_frame_size: Option<u32>,
|
||||||
/// The register holding the return-area pointer, if needed.
|
/// The register holding the return-area pointer, if needed.
|
||||||
ret_area_ptr: Option<Writable<Reg>>,
|
ret_area_ptr: Option<Writable<Reg>>,
|
||||||
@@ -400,6 +404,8 @@ fn in_int_reg(ty: ir::Type) -> bool {
|
|||||||
match ty {
|
match ty {
|
||||||
types::I8 | types::I16 | types::I32 | types::I64 => true,
|
types::I8 | types::I16 | types::I32 | types::I64 => true,
|
||||||
types::B1 | types::B8 | types::B16 | types::B32 | types::B64 => true,
|
types::B1 | types::B8 | types::B16 | types::B32 | types::B64 => true,
|
||||||
|
types::R64 => true,
|
||||||
|
types::R32 => panic!("Unexpected 32-bit reference on a 64-bit platform!"),
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -653,12 +659,12 @@ fn load_stack(mem: MemArg, into_reg: Writable<Reg>, ty: Type) -> Inst {
|
|||||||
mem,
|
mem,
|
||||||
srcloc: None,
|
srcloc: None,
|
||||||
},
|
},
|
||||||
types::B32 | types::I32 => Inst::ULoad32 {
|
types::B32 | types::I32 | types::R32 => Inst::ULoad32 {
|
||||||
rd: into_reg,
|
rd: into_reg,
|
||||||
mem,
|
mem,
|
||||||
srcloc: None,
|
srcloc: None,
|
||||||
},
|
},
|
||||||
types::B64 | types::I64 => Inst::ULoad64 {
|
types::B64 | types::I64 | types::R64 => Inst::ULoad64 {
|
||||||
rd: into_reg,
|
rd: into_reg,
|
||||||
mem,
|
mem,
|
||||||
srcloc: None,
|
srcloc: None,
|
||||||
@@ -689,12 +695,12 @@ fn store_stack(mem: MemArg, from_reg: Reg, ty: Type) -> Inst {
|
|||||||
mem,
|
mem,
|
||||||
srcloc: None,
|
srcloc: None,
|
||||||
},
|
},
|
||||||
types::B32 | types::I32 => Inst::Store32 {
|
types::B32 | types::I32 | types::R32 => Inst::Store32 {
|
||||||
rd: from_reg,
|
rd: from_reg,
|
||||||
mem,
|
mem,
|
||||||
srcloc: None,
|
srcloc: None,
|
||||||
},
|
},
|
||||||
types::B64 | types::I64 => Inst::Store64 {
|
types::B64 | types::I64 | types::R64 => Inst::Store64 {
|
||||||
rd: from_reg,
|
rd: from_reg,
|
||||||
mem,
|
mem,
|
||||||
srcloc: None,
|
srcloc: None,
|
||||||
@@ -810,6 +816,35 @@ fn get_caller_saves(call_conv: isa::CallConv) -> Vec<Writable<Reg>> {
|
|||||||
caller_saved
|
caller_saved
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn gen_sp_adjust_insts<F: FnMut(Inst)>(adj: u64, is_sub: bool, mut f: F) {
|
||||||
|
let alu_op = if is_sub { ALUOp::Sub64 } else { ALUOp::Add64 };
|
||||||
|
|
||||||
|
if let Some(imm12) = Imm12::maybe_from_u64(adj) {
|
||||||
|
let adj_inst = Inst::AluRRImm12 {
|
||||||
|
alu_op,
|
||||||
|
rd: writable_stack_reg(),
|
||||||
|
rn: stack_reg(),
|
||||||
|
imm12,
|
||||||
|
};
|
||||||
|
f(adj_inst);
|
||||||
|
} else {
|
||||||
|
let tmp = writable_spilltmp_reg();
|
||||||
|
let const_inst = Inst::LoadConst64 {
|
||||||
|
rd: tmp,
|
||||||
|
const_data: adj,
|
||||||
|
};
|
||||||
|
let adj_inst = Inst::AluRRRExtend {
|
||||||
|
alu_op,
|
||||||
|
rd: writable_stack_reg(),
|
||||||
|
rn: stack_reg(),
|
||||||
|
rm: tmp.to_reg(),
|
||||||
|
extendop: ExtendOp::UXTX,
|
||||||
|
};
|
||||||
|
f(const_inst);
|
||||||
|
f(adj_inst);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl ABIBody for AArch64ABIBody {
|
impl ABIBody for AArch64ABIBody {
|
||||||
type I = Inst;
|
type I = Inst;
|
||||||
|
|
||||||
@@ -1024,6 +1059,29 @@ impl ABIBody for AArch64ABIBody {
|
|||||||
store_stack(MemArg::NominalSPOffset(sp_off, ty), from_reg, ty)
|
store_stack(MemArg::NominalSPOffset(sp_off, ty), from_reg, ty)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn spillslots_to_stackmap(&self, slots: &[SpillSlot], state: &EmitState) -> Stackmap {
|
||||||
|
assert!(state.virtual_sp_offset >= 0);
|
||||||
|
trace!(
|
||||||
|
"spillslots_to_stackmap: slots = {:?}, state = {:?}",
|
||||||
|
slots,
|
||||||
|
state
|
||||||
|
);
|
||||||
|
let map_size = (state.virtual_sp_offset + state.nominal_sp_to_fp) as u32;
|
||||||
|
let map_words = (map_size + 7) / 8;
|
||||||
|
let mut bits = std::iter::repeat(false)
|
||||||
|
.take(map_words as usize)
|
||||||
|
.collect::<Vec<bool>>();
|
||||||
|
|
||||||
|
let first_spillslot_word =
|
||||||
|
((self.stackslots_size + state.virtual_sp_offset as u32) / 8) as usize;
|
||||||
|
for &slot in slots {
|
||||||
|
let slot = slot.get() as usize;
|
||||||
|
bits[first_spillslot_word + slot] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Stackmap::from_slice(&bits[..])
|
||||||
|
}
|
||||||
|
|
||||||
fn gen_prologue(&mut self) -> Vec<Inst> {
|
fn gen_prologue(&mut self) -> Vec<Inst> {
|
||||||
let mut insts = vec![];
|
let mut insts = vec![];
|
||||||
if !self.call_conv.extends_baldrdash() {
|
if !self.call_conv.extends_baldrdash() {
|
||||||
@@ -1059,6 +1117,9 @@ impl ABIBody for AArch64ABIBody {
|
|||||||
}
|
}
|
||||||
let total_stacksize = (total_stacksize + 15) & !15; // 16-align the stack.
|
let total_stacksize = (total_stacksize + 15) & !15; // 16-align the stack.
|
||||||
|
|
||||||
|
let mut total_sp_adjust = 0;
|
||||||
|
let mut nominal_sp_to_real_sp = 0;
|
||||||
|
|
||||||
if !self.call_conv.extends_baldrdash() {
|
if !self.call_conv.extends_baldrdash() {
|
||||||
// Leaf functions with zero stack don't need a stack check if one's
|
// Leaf functions with zero stack don't need a stack check if one's
|
||||||
// specified, otherwise always insert the stack check.
|
// specified, otherwise always insert the stack check.
|
||||||
@@ -1069,42 +1130,29 @@ impl ABIBody for AArch64ABIBody {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if total_stacksize > 0 {
|
if total_stacksize > 0 {
|
||||||
// sub sp, sp, #total_stacksize
|
total_sp_adjust += total_stacksize as u64;
|
||||||
if let Some(imm12) = Imm12::maybe_from_u64(total_stacksize as u64) {
|
|
||||||
let sub_inst = Inst::AluRRImm12 {
|
|
||||||
alu_op: ALUOp::Sub64,
|
|
||||||
rd: writable_stack_reg(),
|
|
||||||
rn: stack_reg(),
|
|
||||||
imm12,
|
|
||||||
};
|
|
||||||
insts.push(sub_inst);
|
|
||||||
} else {
|
|
||||||
let tmp = writable_spilltmp_reg();
|
|
||||||
let const_inst = Inst::LoadConst64 {
|
|
||||||
rd: tmp,
|
|
||||||
const_data: total_stacksize as u64,
|
|
||||||
};
|
|
||||||
let sub_inst = Inst::AluRRRExtend {
|
|
||||||
alu_op: ALUOp::Sub64,
|
|
||||||
rd: writable_stack_reg(),
|
|
||||||
rn: stack_reg(),
|
|
||||||
rm: tmp.to_reg(),
|
|
||||||
extendop: ExtendOp::UXTX,
|
|
||||||
};
|
|
||||||
insts.push(const_inst);
|
|
||||||
insts.push(sub_inst);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// N.B.: "nominal SP", which we use to refer to stackslots
|
// N.B.: "nominal SP", which we use to refer to stackslots and
|
||||||
// and spillslots, is *here* (the value of SP at this program point).
|
// spillslots, is defined to be equal to the stack pointer at this point
|
||||||
|
// in the prologue.
|
||||||
|
//
|
||||||
// If we push any clobbers below, we emit a virtual-SP adjustment
|
// If we push any clobbers below, we emit a virtual-SP adjustment
|
||||||
// meta-instruction so that the nominal-SP references behave as if SP
|
// meta-instruction so that the nominal-SP references behave as if SP
|
||||||
// were still at this point. See documentation for
|
// were still at this point. See documentation for
|
||||||
// [crate::isa::aarch64::abi](this module) for more details on
|
// [crate::isa::aarch64::abi](this module) for more details on
|
||||||
// stackframe layout and nominal-SP maintenance.
|
// stackframe layout and nominal-SP maintenance.
|
||||||
|
|
||||||
|
if total_sp_adjust > 0 {
|
||||||
|
// sub sp, sp, #total_stacksize
|
||||||
|
gen_sp_adjust_insts(
|
||||||
|
total_sp_adjust,
|
||||||
|
/* is_sub = */ true,
|
||||||
|
|inst| insts.push(inst),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Save clobbered registers.
|
// Save clobbered registers.
|
||||||
let (clobbered_int, clobbered_vec) =
|
let (clobbered_int, clobbered_vec) =
|
||||||
get_callee_saves(self.call_conv, self.clobbered.to_vec());
|
get_callee_saves(self.call_conv, self.clobbered.to_vec());
|
||||||
@@ -1148,10 +1196,11 @@ impl ABIBody for AArch64ABIBody {
|
|||||||
srcloc: None,
|
srcloc: None,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
nominal_sp_to_real_sp += clobber_size as i64;
|
||||||
|
|
||||||
if clobber_size > 0 {
|
if clobber_size > 0 {
|
||||||
insts.push(Inst::VirtualSPOffsetAdj {
|
insts.push(Inst::VirtualSPOffsetAdj {
|
||||||
offset: clobber_size as i64,
|
offset: nominal_sp_to_real_sp,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1245,6 +1294,10 @@ impl ABIBody for AArch64ABIBody {
|
|||||||
.expect("frame size not computed before prologue generation")
|
.expect("frame size not computed before prologue generation")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn stack_args_size(&self) -> u32 {
|
||||||
|
self.sig.stack_arg_space as u32
|
||||||
|
}
|
||||||
|
|
||||||
fn get_spillslot_size(&self, rc: RegClass, ty: Type) -> u32 {
|
fn get_spillslot_size(&self, rc: RegClass, ty: Type) -> u32 {
|
||||||
// We allocate in terms of 8-byte slots.
|
// We allocate in terms of 8-byte slots.
|
||||||
match (rc, ty) {
|
match (rc, ty) {
|
||||||
@@ -1255,15 +1308,42 @@ impl ABIBody for AArch64ABIBody {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn gen_spill(&self, to_slot: SpillSlot, from_reg: RealReg, ty: Type) -> Inst {
|
fn gen_spill(&self, to_slot: SpillSlot, from_reg: RealReg, ty: Option<Type>) -> Inst {
|
||||||
|
let ty = ty_from_ty_hint_or_reg_class(from_reg.to_reg(), ty);
|
||||||
self.store_spillslot(to_slot, ty, from_reg.to_reg())
|
self.store_spillslot(to_slot, ty, from_reg.to_reg())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn gen_reload(&self, to_reg: Writable<RealReg>, from_slot: SpillSlot, ty: Type) -> Inst {
|
fn gen_reload(
|
||||||
|
&self,
|
||||||
|
to_reg: Writable<RealReg>,
|
||||||
|
from_slot: SpillSlot,
|
||||||
|
ty: Option<Type>,
|
||||||
|
) -> Inst {
|
||||||
|
let ty = ty_from_ty_hint_or_reg_class(to_reg.to_reg().to_reg(), ty);
|
||||||
self.load_spillslot(from_slot, ty, to_reg.map(|r| r.to_reg()))
|
self.load_spillslot(from_slot, ty, to_reg.map(|r| r.to_reg()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return a type either from an optional type hint, or if not, from the default
|
||||||
|
/// type associated with the given register's class. This is used to generate
|
||||||
|
/// loads/spills appropriately given the type of value loaded/stored (which may
|
||||||
|
/// be narrower than the spillslot). We usually have the type because the
|
||||||
|
/// regalloc usually provides the vreg being spilled/reloaded, and we know every
|
||||||
|
/// vreg's type. However, the regalloc *can* request a spill/reload without an
|
||||||
|
/// associated vreg when needed to satisfy a safepoint (which requires all
|
||||||
|
/// ref-typed values, even those in real registers in the original vcode, to be
|
||||||
|
/// in spillslots).
|
||||||
|
fn ty_from_ty_hint_or_reg_class(r: Reg, ty: Option<Type>) -> Type {
|
||||||
|
match (ty, r.get_class()) {
|
||||||
|
// If the type is provided
|
||||||
|
(Some(t), _) => t,
|
||||||
|
// If no type is provided, this should be a register spill for a
|
||||||
|
// safepoint, so we only expect I64 (integer) registers.
|
||||||
|
(None, RegClass::I64) => I64,
|
||||||
|
_ => panic!("Unexpected register class!"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
enum CallDest {
|
enum CallDest {
|
||||||
ExtName(ir::ExternalName, RelocDistance),
|
ExtName(ir::ExternalName, RelocDistance),
|
||||||
Reg(Reg),
|
Reg(Reg),
|
||||||
@@ -1342,7 +1422,7 @@ impl AArch64ABICall {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn adjust_stack<C: LowerCtx<I = Inst>>(ctx: &mut C, amount: u64, is_sub: bool) {
|
fn adjust_stack_and_nominal_sp<C: LowerCtx<I = Inst>>(ctx: &mut C, amount: u64, is_sub: bool) {
|
||||||
if amount == 0 {
|
if amount == 0 {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1356,27 +1436,9 @@ fn adjust_stack<C: LowerCtx<I = Inst>>(ctx: &mut C, amount: u64, is_sub: bool) {
|
|||||||
offset: sp_adjustment,
|
offset: sp_adjustment,
|
||||||
});
|
});
|
||||||
|
|
||||||
let alu_op = if is_sub { ALUOp::Sub64 } else { ALUOp::Add64 };
|
gen_sp_adjust_insts(amount, is_sub, |inst| {
|
||||||
if let Some(imm12) = Imm12::maybe_from_u64(amount) {
|
ctx.emit(inst);
|
||||||
ctx.emit(Inst::AluRRImm12 {
|
|
||||||
alu_op,
|
|
||||||
rd: writable_stack_reg(),
|
|
||||||
rn: stack_reg(),
|
|
||||||
imm12,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
ctx.emit(Inst::LoadConst64 {
|
|
||||||
rd: writable_spilltmp_reg(),
|
|
||||||
const_data: amount,
|
|
||||||
});
|
});
|
||||||
ctx.emit(Inst::AluRRRExtend {
|
|
||||||
alu_op,
|
|
||||||
rd: writable_stack_reg(),
|
|
||||||
rn: stack_reg(),
|
|
||||||
rm: spilltmp_reg(),
|
|
||||||
extendop: ExtendOp::UXTX,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ABICall for AArch64ABICall {
|
impl ABICall for AArch64ABICall {
|
||||||
@@ -1392,12 +1454,12 @@ impl ABICall for AArch64ABICall {
|
|||||||
|
|
||||||
fn emit_stack_pre_adjust<C: LowerCtx<I = Self::I>>(&self, ctx: &mut C) {
|
fn emit_stack_pre_adjust<C: LowerCtx<I = Self::I>>(&self, ctx: &mut C) {
|
||||||
let off = self.sig.stack_arg_space + self.sig.stack_ret_space;
|
let off = self.sig.stack_arg_space + self.sig.stack_ret_space;
|
||||||
adjust_stack(ctx, off as u64, /* is_sub = */ true)
|
adjust_stack_and_nominal_sp(ctx, off as u64, /* is_sub = */ true)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn emit_stack_post_adjust<C: LowerCtx<I = Self::I>>(&self, ctx: &mut C) {
|
fn emit_stack_post_adjust<C: LowerCtx<I = Self::I>>(&self, ctx: &mut C) {
|
||||||
let off = self.sig.stack_arg_space + self.sig.stack_ret_space;
|
let off = self.sig.stack_arg_space + self.sig.stack_ret_space;
|
||||||
adjust_stack(ctx, off as u64, /* is_sub = */ false)
|
adjust_stack_and_nominal_sp(ctx, off as u64, /* is_sub = */ false)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn emit_copy_reg_to_arg<C: LowerCtx<I = Self::I>>(
|
fn emit_copy_reg_to_arg<C: LowerCtx<I = Self::I>>(
|
||||||
@@ -1452,7 +1514,7 @@ impl ABICall for AArch64ABICall {
|
|||||||
self.emit_copy_reg_to_arg(ctx, i, rd.to_reg());
|
self.emit_copy_reg_to_arg(ctx, i, rd.to_reg());
|
||||||
}
|
}
|
||||||
match &self.dest {
|
match &self.dest {
|
||||||
&CallDest::ExtName(ref name, RelocDistance::Near) => ctx.emit(Inst::Call {
|
&CallDest::ExtName(ref name, RelocDistance::Near) => ctx.emit_safepoint(Inst::Call {
|
||||||
info: Box::new(CallInfo {
|
info: Box::new(CallInfo {
|
||||||
dest: name.clone(),
|
dest: name.clone(),
|
||||||
uses,
|
uses,
|
||||||
@@ -1468,7 +1530,7 @@ impl ABICall for AArch64ABICall {
|
|||||||
offset: 0,
|
offset: 0,
|
||||||
srcloc: self.loc,
|
srcloc: self.loc,
|
||||||
});
|
});
|
||||||
ctx.emit(Inst::CallInd {
|
ctx.emit_safepoint(Inst::CallInd {
|
||||||
info: Box::new(CallIndInfo {
|
info: Box::new(CallIndInfo {
|
||||||
rn: spilltmp_reg(),
|
rn: spilltmp_reg(),
|
||||||
uses,
|
uses,
|
||||||
@@ -1478,7 +1540,7 @@ impl ABICall for AArch64ABICall {
|
|||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
&CallDest::Reg(reg) => ctx.emit(Inst::CallInd {
|
&CallDest::Reg(reg) => ctx.emit_safepoint(Inst::CallInd {
|
||||||
info: Box::new(CallIndInfo {
|
info: Box::new(CallIndInfo {
|
||||||
rn: reg,
|
rn: reg,
|
||||||
uses,
|
uses,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
//! AArch64 ISA: binary code emission.
|
//! AArch64 ISA: binary code emission.
|
||||||
|
|
||||||
use crate::binemit::{CodeOffset, Reloc};
|
use crate::binemit::{CodeOffset, Reloc, Stackmap};
|
||||||
use crate::ir::constant::ConstantData;
|
use crate::ir::constant::ConstantData;
|
||||||
use crate::ir::types::*;
|
use crate::ir::types::*;
|
||||||
use crate::ir::TrapCode;
|
use crate::ir::TrapCode;
|
||||||
@@ -376,7 +376,37 @@ fn enc_vec_lanes(q: u32, u: u32, size: u32, opcode: u32, rd: Writable<Reg>, rn:
|
|||||||
/// State carried between emissions of a sequence of instructions.
|
/// State carried between emissions of a sequence of instructions.
|
||||||
#[derive(Default, Clone, Debug)]
|
#[derive(Default, Clone, Debug)]
|
||||||
pub struct EmitState {
|
pub struct EmitState {
|
||||||
virtual_sp_offset: i64,
|
/// Addend to convert nominal-SP offsets to real-SP offsets at the current
|
||||||
|
/// program point.
|
||||||
|
pub(crate) virtual_sp_offset: i64,
|
||||||
|
/// Offset of FP from nominal-SP.
|
||||||
|
pub(crate) nominal_sp_to_fp: i64,
|
||||||
|
/// Safepoint stackmap for upcoming instruction, as provided to `pre_safepoint()`.
|
||||||
|
stackmap: Option<Stackmap>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MachInstEmitState<Inst> for EmitState {
|
||||||
|
fn new(abi: &dyn ABIBody<I = Inst>) -> Self {
|
||||||
|
EmitState {
|
||||||
|
virtual_sp_offset: 0,
|
||||||
|
nominal_sp_to_fp: abi.frame_size() as i64,
|
||||||
|
stackmap: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pre_safepoint(&mut self, stackmap: Stackmap) {
|
||||||
|
self.stackmap = Some(stackmap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EmitState {
|
||||||
|
fn take_stackmap(&mut self) -> Option<Stackmap> {
|
||||||
|
self.stackmap.take()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clear_post_insn(&mut self) {
|
||||||
|
self.stackmap = None;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MachInstEmit for Inst {
|
impl MachInstEmit for Inst {
|
||||||
@@ -1463,6 +1493,9 @@ impl MachInstEmit for Inst {
|
|||||||
// Noop; this is just a placeholder for epilogues.
|
// Noop; this is just a placeholder for epilogues.
|
||||||
}
|
}
|
||||||
&Inst::Call { ref info } => {
|
&Inst::Call { ref info } => {
|
||||||
|
if let Some(s) = state.take_stackmap() {
|
||||||
|
sink.add_stackmap(4, s);
|
||||||
|
}
|
||||||
sink.add_reloc(info.loc, Reloc::Arm64Call, &info.dest, 0);
|
sink.add_reloc(info.loc, Reloc::Arm64Call, &info.dest, 0);
|
||||||
sink.put4(enc_jump26(0b100101, 0));
|
sink.put4(enc_jump26(0b100101, 0));
|
||||||
if info.opcode.is_call() {
|
if info.opcode.is_call() {
|
||||||
@@ -1470,6 +1503,9 @@ impl MachInstEmit for Inst {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
&Inst::CallInd { ref info } => {
|
&Inst::CallInd { ref info } => {
|
||||||
|
if let Some(s) = state.take_stackmap() {
|
||||||
|
sink.add_stackmap(4, s);
|
||||||
|
}
|
||||||
sink.put4(0b1101011_0001_11111_000000_00000_00000 | (machreg_to_gpr(info.rn) << 5));
|
sink.put4(0b1101011_0001_11111_000000_00000_00000 | (machreg_to_gpr(info.rn) << 5));
|
||||||
if info.opcode.is_call() {
|
if info.opcode.is_call() {
|
||||||
sink.add_call_site(info.loc, info.opcode);
|
sink.add_call_site(info.loc, info.opcode);
|
||||||
@@ -1525,6 +1561,9 @@ impl MachInstEmit for Inst {
|
|||||||
&Inst::Udf { trap_info } => {
|
&Inst::Udf { trap_info } => {
|
||||||
let (srcloc, code) = trap_info;
|
let (srcloc, code) = trap_info;
|
||||||
sink.add_trap(srcloc, code);
|
sink.add_trap(srcloc, code);
|
||||||
|
if let Some(s) = state.take_stackmap() {
|
||||||
|
sink.add_stackmap(4, s);
|
||||||
|
}
|
||||||
sink.put4(0xd4a00000);
|
sink.put4(0xd4a00000);
|
||||||
}
|
}
|
||||||
&Inst::Adr { rd, off } => {
|
&Inst::Adr { rd, off } => {
|
||||||
@@ -1709,7 +1748,7 @@ impl MachInstEmit for Inst {
|
|||||||
debug!(
|
debug!(
|
||||||
"virtual sp offset adjusted by {} -> {}",
|
"virtual sp offset adjusted by {} -> {}",
|
||||||
offset,
|
offset,
|
||||||
state.virtual_sp_offset + offset
|
state.virtual_sp_offset + offset,
|
||||||
);
|
);
|
||||||
state.virtual_sp_offset += offset;
|
state.virtual_sp_offset += offset;
|
||||||
}
|
}
|
||||||
@@ -1728,5 +1767,11 @@ impl MachInstEmit for Inst {
|
|||||||
|
|
||||||
let end_off = sink.cur_offset();
|
let end_off = sink.cur_offset();
|
||||||
debug_assert!((end_off - start_off) <= Inst::worst_case_size());
|
debug_assert!((end_off - start_off) <= Inst::worst_case_size());
|
||||||
|
|
||||||
|
state.clear_post_insn();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pretty_print(&self, mb_rru: Option<&RealRegUniverse>, state: &mut EmitState) -> String {
|
||||||
|
self.print_with_state(mb_rru, state)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
use crate::binemit::CodeOffset;
|
use crate::binemit::CodeOffset;
|
||||||
use crate::ir::types::{
|
use crate::ir::types::{
|
||||||
B1, B16, B16X8, B32, B32X4, B64, B64X2, B8, B8X16, F32, F32X2, F32X4, F64, F64X2, FFLAGS, I16,
|
B1, B16, B16X8, B32, B32X4, B64, B64X2, B8, B8X16, F32, F32X2, F32X4, F64, F64X2, FFLAGS, I16,
|
||||||
I16X4, I16X8, I32, I32X2, I32X4, I64, I64X2, I8, I8X16, I8X8, IFLAGS,
|
I16X4, I16X8, I32, I32X2, I32X4, I64, I64X2, I8, I8X16, I8X8, IFLAGS, R32, R64,
|
||||||
};
|
};
|
||||||
use crate::ir::{ExternalName, Opcode, SourceLoc, TrapCode, Type};
|
use crate::ir::{ExternalName, Opcode, SourceLoc, TrapCode, Type};
|
||||||
use crate::machinst::*;
|
use crate::machinst::*;
|
||||||
@@ -1346,11 +1346,11 @@ fn aarch64_get_regs(inst: &Inst, collector: &mut RegUsageCollector) {
|
|||||||
collector.add_use(rn);
|
collector.add_use(rn);
|
||||||
}
|
}
|
||||||
&Inst::Jump { .. } | &Inst::Ret | &Inst::EpiloguePlaceholder => {}
|
&Inst::Jump { .. } | &Inst::Ret | &Inst::EpiloguePlaceholder => {}
|
||||||
&Inst::Call { ref info } => {
|
&Inst::Call { ref info, .. } => {
|
||||||
collector.add_uses(&*info.uses);
|
collector.add_uses(&*info.uses);
|
||||||
collector.add_defs(&*info.defs);
|
collector.add_defs(&*info.defs);
|
||||||
}
|
}
|
||||||
&Inst::CallInd { ref info } => {
|
&Inst::CallInd { ref info, .. } => {
|
||||||
collector.add_uses(&*info.uses);
|
collector.add_uses(&*info.uses);
|
||||||
collector.add_defs(&*info.defs);
|
collector.add_defs(&*info.defs);
|
||||||
collector.add_use(info.rn);
|
collector.add_use(info.rn);
|
||||||
@@ -2081,6 +2081,8 @@ impl MachInst for Inst {
|
|||||||
|| ty == B32
|
|| ty == B32
|
||||||
|| ty == I64
|
|| ty == I64
|
||||||
|| ty == B64
|
|| ty == B64
|
||||||
|
|| ty == R32
|
||||||
|
|| ty == R64
|
||||||
);
|
);
|
||||||
Inst::load_constant(to_reg, value)
|
Inst::load_constant(to_reg, value)
|
||||||
}
|
}
|
||||||
@@ -2102,7 +2104,7 @@ impl MachInst for Inst {
|
|||||||
|
|
||||||
fn rc_for_type(ty: Type) -> CodegenResult<RegClass> {
|
fn rc_for_type(ty: Type) -> CodegenResult<RegClass> {
|
||||||
match ty {
|
match ty {
|
||||||
I8 | I16 | I32 | I64 | B1 | B8 | B16 | B32 | B64 => Ok(RegClass::I64),
|
I8 | I16 | I32 | I64 | B1 | B8 | B16 | B32 | B64 | R32 | R64 => Ok(RegClass::I64),
|
||||||
F32 | F64 => Ok(RegClass::V128),
|
F32 | F64 => Ok(RegClass::V128),
|
||||||
IFLAGS | FFLAGS => Ok(RegClass::I64),
|
IFLAGS | FFLAGS => Ok(RegClass::I64),
|
||||||
B8X16 | I8X16 | B16X8 | I16X8 | B32X4 | I32X4 | B64X2 | I64X2 | F32X4 | F64X2 => {
|
B8X16 | I8X16 | B16X8 | I16X8 | B32X4 | I32X4 | B64X2 | I64X2 | F32X4 | F64X2 => {
|
||||||
@@ -2135,13 +2137,21 @@ impl MachInst for Inst {
|
|||||||
// feasible for other reasons).
|
// feasible for other reasons).
|
||||||
44
|
44
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn ref_type_regclass(_: &settings::Flags) -> RegClass {
|
||||||
|
RegClass::I64
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//=============================================================================
|
//=============================================================================
|
||||||
// Pretty-printing of instructions.
|
// Pretty-printing of instructions.
|
||||||
|
|
||||||
fn mem_finalize_for_show(mem: &MemArg, mb_rru: Option<&RealRegUniverse>) -> (String, MemArg) {
|
fn mem_finalize_for_show(
|
||||||
let (mem_insts, mem) = mem_finalize(0, mem, &mut Default::default());
|
mem: &MemArg,
|
||||||
|
mb_rru: Option<&RealRegUniverse>,
|
||||||
|
state: &EmitState,
|
||||||
|
) -> (String, MemArg) {
|
||||||
|
let (mem_insts, mem) = mem_finalize(0, mem, state);
|
||||||
let mut mem_str = mem_insts
|
let mut mem_str = mem_insts
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|inst| inst.show_rru(mb_rru))
|
.map(|inst| inst.show_rru(mb_rru))
|
||||||
@@ -2156,6 +2166,12 @@ fn mem_finalize_for_show(mem: &MemArg, mb_rru: Option<&RealRegUniverse>) -> (Str
|
|||||||
|
|
||||||
impl ShowWithRRU for Inst {
|
impl ShowWithRRU for Inst {
|
||||||
fn show_rru(&self, mb_rru: Option<&RealRegUniverse>) -> String {
|
fn show_rru(&self, mb_rru: Option<&RealRegUniverse>) -> String {
|
||||||
|
self.pretty_print(mb_rru, &mut EmitState::default())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Inst {
|
||||||
|
fn print_with_state(&self, mb_rru: Option<&RealRegUniverse>, state: &mut EmitState) -> String {
|
||||||
fn op_name_size(alu_op: ALUOp) -> (&'static str, OperandSize) {
|
fn op_name_size(alu_op: ALUOp) -> (&'static str, OperandSize) {
|
||||||
match alu_op {
|
match alu_op {
|
||||||
ALUOp::Add32 => ("add", OperandSize::Size32),
|
ALUOp::Add32 => ("add", OperandSize::Size32),
|
||||||
@@ -2342,7 +2358,7 @@ impl ShowWithRRU for Inst {
|
|||||||
srcloc: _srcloc,
|
srcloc: _srcloc,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
let (mem_str, mem) = mem_finalize_for_show(mem, mb_rru);
|
let (mem_str, mem) = mem_finalize_for_show(mem, mb_rru, state);
|
||||||
|
|
||||||
let is_unscaled = match &mem {
|
let is_unscaled = match &mem {
|
||||||
&MemArg::Unscaled(..) => true,
|
&MemArg::Unscaled(..) => true,
|
||||||
@@ -2390,7 +2406,7 @@ impl ShowWithRRU for Inst {
|
|||||||
srcloc: _srcloc,
|
srcloc: _srcloc,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
let (mem_str, mem) = mem_finalize_for_show(mem, mb_rru);
|
let (mem_str, mem) = mem_finalize_for_show(mem, mb_rru, state);
|
||||||
|
|
||||||
let is_unscaled = match &mem {
|
let is_unscaled = match &mem {
|
||||||
&MemArg::Unscaled(..) => true,
|
&MemArg::Unscaled(..) => true,
|
||||||
@@ -2574,39 +2590,39 @@ impl ShowWithRRU for Inst {
|
|||||||
}
|
}
|
||||||
&Inst::FpuLoad32 { rd, ref mem, .. } => {
|
&Inst::FpuLoad32 { rd, ref mem, .. } => {
|
||||||
let rd = show_freg_sized(rd.to_reg(), mb_rru, ScalarSize::Size32);
|
let rd = show_freg_sized(rd.to_reg(), mb_rru, ScalarSize::Size32);
|
||||||
let (mem_str, mem) = mem_finalize_for_show(mem, mb_rru);
|
let (mem_str, mem) = mem_finalize_for_show(mem, mb_rru, state);
|
||||||
let mem = mem.show_rru(mb_rru);
|
let mem = mem.show_rru(mb_rru);
|
||||||
format!("{}ldr {}, {}", mem_str, rd, mem)
|
format!("{}ldr {}, {}", mem_str, rd, mem)
|
||||||
}
|
}
|
||||||
&Inst::FpuLoad64 { rd, ref mem, .. } => {
|
&Inst::FpuLoad64 { rd, ref mem, .. } => {
|
||||||
let rd = show_freg_sized(rd.to_reg(), mb_rru, ScalarSize::Size64);
|
let rd = show_freg_sized(rd.to_reg(), mb_rru, ScalarSize::Size64);
|
||||||
let (mem_str, mem) = mem_finalize_for_show(mem, mb_rru);
|
let (mem_str, mem) = mem_finalize_for_show(mem, mb_rru, state);
|
||||||
let mem = mem.show_rru(mb_rru);
|
let mem = mem.show_rru(mb_rru);
|
||||||
format!("{}ldr {}, {}", mem_str, rd, mem)
|
format!("{}ldr {}, {}", mem_str, rd, mem)
|
||||||
}
|
}
|
||||||
&Inst::FpuLoad128 { rd, ref mem, .. } => {
|
&Inst::FpuLoad128 { rd, ref mem, .. } => {
|
||||||
let rd = rd.to_reg().show_rru(mb_rru);
|
let rd = rd.to_reg().show_rru(mb_rru);
|
||||||
let rd = "q".to_string() + &rd[1..];
|
let rd = "q".to_string() + &rd[1..];
|
||||||
let (mem_str, mem) = mem_finalize_for_show(mem, mb_rru);
|
let (mem_str, mem) = mem_finalize_for_show(mem, mb_rru, state);
|
||||||
let mem = mem.show_rru(mb_rru);
|
let mem = mem.show_rru(mb_rru);
|
||||||
format!("{}ldr {}, {}", mem_str, rd, mem)
|
format!("{}ldr {}, {}", mem_str, rd, mem)
|
||||||
}
|
}
|
||||||
&Inst::FpuStore32 { rd, ref mem, .. } => {
|
&Inst::FpuStore32 { rd, ref mem, .. } => {
|
||||||
let rd = show_freg_sized(rd, mb_rru, ScalarSize::Size32);
|
let rd = show_freg_sized(rd, mb_rru, ScalarSize::Size32);
|
||||||
let (mem_str, mem) = mem_finalize_for_show(mem, mb_rru);
|
let (mem_str, mem) = mem_finalize_for_show(mem, mb_rru, state);
|
||||||
let mem = mem.show_rru(mb_rru);
|
let mem = mem.show_rru(mb_rru);
|
||||||
format!("{}str {}, {}", mem_str, rd, mem)
|
format!("{}str {}, {}", mem_str, rd, mem)
|
||||||
}
|
}
|
||||||
&Inst::FpuStore64 { rd, ref mem, .. } => {
|
&Inst::FpuStore64 { rd, ref mem, .. } => {
|
||||||
let rd = show_freg_sized(rd, mb_rru, ScalarSize::Size64);
|
let rd = show_freg_sized(rd, mb_rru, ScalarSize::Size64);
|
||||||
let (mem_str, mem) = mem_finalize_for_show(mem, mb_rru);
|
let (mem_str, mem) = mem_finalize_for_show(mem, mb_rru, state);
|
||||||
let mem = mem.show_rru(mb_rru);
|
let mem = mem.show_rru(mb_rru);
|
||||||
format!("{}str {}, {}", mem_str, rd, mem)
|
format!("{}str {}, {}", mem_str, rd, mem)
|
||||||
}
|
}
|
||||||
&Inst::FpuStore128 { rd, ref mem, .. } => {
|
&Inst::FpuStore128 { rd, ref mem, .. } => {
|
||||||
let rd = rd.show_rru(mb_rru);
|
let rd = rd.show_rru(mb_rru);
|
||||||
let rd = "q".to_string() + &rd[1..];
|
let rd = "q".to_string() + &rd[1..];
|
||||||
let (mem_str, mem) = mem_finalize_for_show(mem, mb_rru);
|
let (mem_str, mem) = mem_finalize_for_show(mem, mb_rru, state);
|
||||||
let mem = mem.show_rru(mb_rru);
|
let mem = mem.show_rru(mb_rru);
|
||||||
format!("{}str {}, {}", mem_str, rd, mem)
|
format!("{}str {}, {}", mem_str, rd, mem)
|
||||||
}
|
}
|
||||||
@@ -2976,7 +2992,7 @@ impl ShowWithRRU for Inst {
|
|||||||
// this logic between `emit()` and `show_rru()` -- a separate 1-to-N
|
// this logic between `emit()` and `show_rru()` -- a separate 1-to-N
|
||||||
// expansion stage (i.e., legalization, but without the slow edit-in-place
|
// expansion stage (i.e., legalization, but without the slow edit-in-place
|
||||||
// of the existing legalization framework).
|
// of the existing legalization framework).
|
||||||
let (mem_insts, mem) = mem_finalize(0, mem, &EmitState::default());
|
let (mem_insts, mem) = mem_finalize(0, mem, state);
|
||||||
let mut ret = String::new();
|
let mut ret = String::new();
|
||||||
for inst in mem_insts.into_iter() {
|
for inst in mem_insts.into_iter() {
|
||||||
ret.push_str(&inst.show_rru(mb_rru));
|
ret.push_str(&inst.show_rru(mb_rru));
|
||||||
@@ -3023,7 +3039,10 @@ impl ShowWithRRU for Inst {
|
|||||||
}
|
}
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
&Inst::VirtualSPOffsetAdj { offset } => format!("virtual_sp_offset_adjust {}", offset),
|
&Inst::VirtualSPOffsetAdj { offset } => {
|
||||||
|
state.virtual_sp_offset += offset;
|
||||||
|
format!("virtual_sp_offset_adjust {}", offset)
|
||||||
|
}
|
||||||
&Inst::EmitIsland { needed_space } => format!("emit_island {}", needed_space),
|
&Inst::EmitIsland { needed_space } => format!("emit_island {}", needed_space),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -829,8 +829,8 @@ pub fn ty_bits(ty: Type) -> usize {
|
|||||||
B1 => 1,
|
B1 => 1,
|
||||||
B8 | I8 => 8,
|
B8 | I8 => 8,
|
||||||
B16 | I16 => 16,
|
B16 | I16 => 16,
|
||||||
B32 | I32 | F32 => 32,
|
B32 | I32 | F32 | R32 => 32,
|
||||||
B64 | I64 | F64 => 64,
|
B64 | I64 | F64 | R64 => 64,
|
||||||
B128 | I128 => 128,
|
B128 | I128 => 128,
|
||||||
IFLAGS | FFLAGS => 32,
|
IFLAGS | FFLAGS => 32,
|
||||||
B8X8 | I8X8 | B16X4 | I16X4 | B32X2 | I32X2 => 64,
|
B8X8 | I8X8 | B16X4 | I16X4 | B32X2 | I32X2 => 64,
|
||||||
@@ -842,7 +842,7 @@ pub fn ty_bits(ty: Type) -> usize {
|
|||||||
|
|
||||||
pub(crate) fn ty_is_int(ty: Type) -> bool {
|
pub(crate) fn ty_is_int(ty: Type) -> bool {
|
||||||
match ty {
|
match ty {
|
||||||
B1 | B8 | I8 | B16 | I16 | B32 | I32 | B64 | I64 => true,
|
B1 | B8 | I8 | B16 | I16 | B32 | I32 | B64 | I64 | R32 | R64 => true,
|
||||||
F32 | F64 | B128 | I128 | I8X8 | I8X16 | I16X4 | I16X8 | I32X2 | I32X4 | I64X2 => false,
|
F32 | F64 | B128 | I128 | I8X8 | I8X16 | I16X4 | I16X8 | I32X2 | I32X4 | I64X2 => false,
|
||||||
IFLAGS | FFLAGS => panic!("Unexpected flags type"),
|
IFLAGS | FFLAGS => panic!("Unexpected flags type"),
|
||||||
_ => panic!("ty_is_int() on unknown type: {:?}", ty),
|
_ => panic!("ty_is_int() on unknown type: {:?}", ty),
|
||||||
|
|||||||
@@ -1204,7 +1204,26 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
Opcode::IsNull | Opcode::IsInvalid => {
|
Opcode::IsNull | Opcode::IsInvalid => {
|
||||||
panic!("Reference types not supported");
|
// Null references are represented by the constant value 0; invalid references are
|
||||||
|
// represented by the constant value -1. See `define_reftypes()` in
|
||||||
|
// `meta/src/isa/x86/encodings.rs` to confirm.
|
||||||
|
let rd = get_output_reg(ctx, outputs[0]);
|
||||||
|
let rn = put_input_in_reg(ctx, inputs[0], NarrowValueMode::None);
|
||||||
|
let ty = ctx.input_ty(insn, 0);
|
||||||
|
let (alu_op, const_value) = match op {
|
||||||
|
Opcode::IsNull => {
|
||||||
|
// cmp rn, #0
|
||||||
|
(choose_32_64(ty, ALUOp::SubS32, ALUOp::SubS64), 0)
|
||||||
|
}
|
||||||
|
Opcode::IsInvalid => {
|
||||||
|
// cmn rn, #1
|
||||||
|
(choose_32_64(ty, ALUOp::AddS32, ALUOp::AddS64), 1)
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
let const_value = ResultRSEImm12::Imm12(Imm12::maybe_from_u64(const_value).unwrap());
|
||||||
|
ctx.emit(alu_inst_imm12(alu_op, writable_zero_reg(), rn, const_value));
|
||||||
|
ctx.emit(Inst::CSet { rd, cond: Cond::Eq });
|
||||||
}
|
}
|
||||||
|
|
||||||
Opcode::Copy => {
|
Opcode::Copy => {
|
||||||
@@ -1215,6 +1234,21 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
Opcode::Bint | Opcode::Breduce | Opcode::Bextend | Opcode::Ireduce => {
|
Opcode::Bint | Opcode::Breduce | Opcode::Bextend | Opcode::Ireduce => {
|
||||||
|
// If this is a Bint from a Trueif/Trueff/IsNull/IsInvalid, then the result is already
|
||||||
|
// 64-bit-zero-extended, even if the CLIF type doesn't say so, because it was produced
|
||||||
|
// by a CSet. In this case, we do not need to do any zero-extension.
|
||||||
|
let input_info = ctx.get_input(insn, 0);
|
||||||
|
let src_op = input_info
|
||||||
|
.inst
|
||||||
|
.map(|(src_inst, _)| ctx.data(src_inst).opcode());
|
||||||
|
let narrow_mode = match (src_op, op) {
|
||||||
|
(Some(Opcode::Trueif), Opcode::Bint)
|
||||||
|
| (Some(Opcode::Trueff), Opcode::Bint)
|
||||||
|
| (Some(Opcode::IsNull), Opcode::Bint)
|
||||||
|
| (Some(Opcode::IsInvalid), Opcode::Bint) => NarrowValueMode::None,
|
||||||
|
_ => NarrowValueMode::ZeroExtend64,
|
||||||
|
};
|
||||||
|
|
||||||
// All of these ops are simply a move from a zero-extended source.
|
// All of these ops are simply a move from a zero-extended source.
|
||||||
// Here is why this works, in each case:
|
// Here is why this works, in each case:
|
||||||
//
|
//
|
||||||
@@ -1227,7 +1261,7 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
|
|||||||
// - Ireduce: changing width of an integer. Smaller ints are stored
|
// - Ireduce: changing width of an integer. Smaller ints are stored
|
||||||
// with undefined high-order bits, so we can simply do a copy.
|
// with undefined high-order bits, so we can simply do a copy.
|
||||||
|
|
||||||
let rn = put_input_in_reg(ctx, inputs[0], NarrowValueMode::ZeroExtend64);
|
let rn = put_input_in_reg(ctx, inputs[0], narrow_mode);
|
||||||
let rd = get_output_reg(ctx, outputs[0]);
|
let rd = get_output_reg(ctx, outputs[0]);
|
||||||
let ty = ctx.input_ty(insn, 0);
|
let ty = ctx.input_ty(insn, 0);
|
||||||
ctx.emit(Inst::gen_move(rd, rn, ty));
|
ctx.emit(Inst::gen_move(rd, rn, ty));
|
||||||
@@ -1360,7 +1394,7 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
|
|||||||
|
|
||||||
Opcode::Trap | Opcode::ResumableTrap => {
|
Opcode::Trap | Opcode::ResumableTrap => {
|
||||||
let trap_info = (ctx.srcloc(insn), inst_trapcode(ctx.data(insn)).unwrap());
|
let trap_info = (ctx.srcloc(insn), inst_trapcode(ctx.data(insn)).unwrap());
|
||||||
ctx.emit(Inst::Udf { trap_info })
|
ctx.emit_safepoint(Inst::Udf { trap_info });
|
||||||
}
|
}
|
||||||
|
|
||||||
Opcode::Trapif | Opcode::Trapff => {
|
Opcode::Trapif | Opcode::Trapff => {
|
||||||
@@ -1398,10 +1432,11 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
|
|||||||
trap_info,
|
trap_info,
|
||||||
kind: CondBrKind::Cond(cond),
|
kind: CondBrKind::Cond(cond),
|
||||||
});
|
});
|
||||||
|
ctx.emit_safepoint(Inst::Udf { trap_info })
|
||||||
}
|
}
|
||||||
|
|
||||||
Opcode::Safepoint => {
|
Opcode::Safepoint => {
|
||||||
panic!("safepoint support not implemented!");
|
panic!("safepoint instructions not used by new backend's safepoints!");
|
||||||
}
|
}
|
||||||
|
|
||||||
Opcode::Trapz | Opcode::Trapnz | Opcode::ResumableTrapnz => {
|
Opcode::Trapz | Opcode::Trapnz | Opcode::ResumableTrapnz => {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ use log::trace;
|
|||||||
use regalloc::{RealReg, Reg, RegClass, Set, SpillSlot, Writable};
|
use regalloc::{RealReg, Reg, RegClass, Set, SpillSlot, Writable};
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
||||||
|
use crate::binemit::Stackmap;
|
||||||
use crate::ir::{self, types, types::*, ArgumentExtension, StackSlot, Type};
|
use crate::ir::{self, types, types::*, ArgumentExtension, StackSlot, Type};
|
||||||
use crate::isa::{self, x64::inst::*};
|
use crate::isa::{self, x64::inst::*};
|
||||||
use crate::machinst::*;
|
use crate::machinst::*;
|
||||||
@@ -415,6 +416,10 @@ impl ABIBody for X64ABIBody {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn spillslots_to_stackmap(&self, _slots: &[SpillSlot], _state: &EmitState) -> Stackmap {
|
||||||
|
unimplemented!("spillslots_to_stackmap")
|
||||||
|
}
|
||||||
|
|
||||||
fn gen_prologue(&mut self) -> Vec<Inst> {
|
fn gen_prologue(&mut self) -> Vec<Inst> {
|
||||||
let r_rsp = regs::rsp();
|
let r_rsp = regs::rsp();
|
||||||
|
|
||||||
@@ -553,6 +558,10 @@ impl ABIBody for X64ABIBody {
|
|||||||
.expect("frame size not computed before prologue generation") as u32
|
.expect("frame size not computed before prologue generation") as u32
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn stack_args_size(&self) -> u32 {
|
||||||
|
unimplemented!("I need to be computed!")
|
||||||
|
}
|
||||||
|
|
||||||
fn get_spillslot_size(&self, rc: RegClass, ty: Type) -> u32 {
|
fn get_spillslot_size(&self, rc: RegClass, ty: Type) -> u32 {
|
||||||
// We allocate in terms of 8-byte slots.
|
// We allocate in terms of 8-byte slots.
|
||||||
match (rc, ty) {
|
match (rc, ty) {
|
||||||
@@ -563,15 +572,42 @@ impl ABIBody for X64ABIBody {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn gen_spill(&self, to_slot: SpillSlot, from_reg: RealReg, ty: Type) -> Inst {
|
fn gen_spill(&self, to_slot: SpillSlot, from_reg: RealReg, ty: Option<Type>) -> Inst {
|
||||||
|
let ty = ty_from_ty_hint_or_reg_class(from_reg.to_reg(), ty);
|
||||||
self.store_spillslot(to_slot, ty, from_reg.to_reg())
|
self.store_spillslot(to_slot, ty, from_reg.to_reg())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn gen_reload(&self, to_reg: Writable<RealReg>, from_slot: SpillSlot, ty: Type) -> Inst {
|
fn gen_reload(
|
||||||
|
&self,
|
||||||
|
to_reg: Writable<RealReg>,
|
||||||
|
from_slot: SpillSlot,
|
||||||
|
ty: Option<Type>,
|
||||||
|
) -> Inst {
|
||||||
|
let ty = ty_from_ty_hint_or_reg_class(to_reg.to_reg().to_reg(), ty);
|
||||||
self.load_spillslot(from_slot, ty, to_reg.map(|r| r.to_reg()))
|
self.load_spillslot(from_slot, ty, to_reg.map(|r| r.to_reg()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return a type either from an optional type hint, or if not, from the default
|
||||||
|
/// type associated with the given register's class. This is used to generate
|
||||||
|
/// loads/spills appropriately given the type of value loaded/stored (which may
|
||||||
|
/// be narrower than the spillslot). We usually have the type because the
|
||||||
|
/// regalloc usually provides the vreg being spilled/reloaded, and we know every
|
||||||
|
/// vreg's type. However, the regalloc *can* request a spill/reload without an
|
||||||
|
/// associated vreg when needed to satisfy a safepoint (which requires all
|
||||||
|
/// ref-typed values, even those in real registers in the original vcode, to be
|
||||||
|
/// in spillslots).
|
||||||
|
fn ty_from_ty_hint_or_reg_class(r: Reg, ty: Option<Type>) -> Type {
|
||||||
|
match (ty, r.get_class()) {
|
||||||
|
// If the type is provided
|
||||||
|
(Some(t), _) => t,
|
||||||
|
// If no type is provided, this should be a register spill for a
|
||||||
|
// safepoint, so we only expect I64 (integer) registers.
|
||||||
|
(None, RegClass::I64) => I64,
|
||||||
|
_ => panic!("Unexpected register class!"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn get_caller_saves(call_conv: isa::CallConv) -> Vec<Writable<Reg>> {
|
fn get_caller_saves(call_conv: isa::CallConv) -> Vec<Writable<Reg>> {
|
||||||
let mut caller_saved = Vec::new();
|
let mut caller_saved = Vec::new();
|
||||||
|
|
||||||
|
|||||||
@@ -1258,6 +1258,10 @@ impl MachInst for Inst {
|
|||||||
15
|
15
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn ref_type_regclass(_: &settings::Flags) -> RegClass {
|
||||||
|
RegClass::I64
|
||||||
|
}
|
||||||
|
|
||||||
type LabelUse = LabelUse;
|
type LabelUse = LabelUse;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1273,6 +1277,18 @@ impl MachInstEmit for Inst {
|
|||||||
fn emit(&self, sink: &mut MachBuffer<Inst>, flags: &settings::Flags, state: &mut Self::State) {
|
fn emit(&self, sink: &mut MachBuffer<Inst>, flags: &settings::Flags, state: &mut Self::State) {
|
||||||
emit::emit(self, sink, flags, state);
|
emit::emit(self, sink, flags, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn pretty_print(&self, mb_rru: Option<&RealRegUniverse>, _: &mut Self::State) -> String {
|
||||||
|
self.show_rru(mb_rru)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MachInstEmitState<Inst> for EmitState {
|
||||||
|
fn new(_: &dyn ABIBody<I = Inst>) -> Self {
|
||||||
|
EmitState {
|
||||||
|
virtual_sp_offset: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A label-use (internal relocation) in generated code.
|
/// A label-use (internal relocation) in generated code.
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
//! ABI definitions.
|
//! ABI definitions.
|
||||||
|
|
||||||
|
use crate::binemit::Stackmap;
|
||||||
use crate::ir::{ArgumentExtension, StackSlot};
|
use crate::ir::{ArgumentExtension, StackSlot};
|
||||||
use crate::machinst::*;
|
use crate::machinst::*;
|
||||||
use crate::settings;
|
use crate::settings;
|
||||||
@@ -100,6 +101,15 @@ pub trait ABIBody {
|
|||||||
/// Store to a spillslot.
|
/// Store to a spillslot.
|
||||||
fn store_spillslot(&self, slot: SpillSlot, ty: Type, from_reg: Reg) -> Self::I;
|
fn store_spillslot(&self, slot: SpillSlot, ty: Type, from_reg: Reg) -> Self::I;
|
||||||
|
|
||||||
|
/// Generate a stackmap, given a list of spillslots and the emission state
|
||||||
|
/// at a given program point (prior to emission fo the safepointing
|
||||||
|
/// instruction).
|
||||||
|
fn spillslots_to_stackmap(
|
||||||
|
&self,
|
||||||
|
slots: &[SpillSlot],
|
||||||
|
state: &<Self::I as MachInstEmit>::State,
|
||||||
|
) -> Stackmap;
|
||||||
|
|
||||||
/// Generate a prologue, post-regalloc. This should include any stack
|
/// Generate a prologue, post-regalloc. This should include any stack
|
||||||
/// frame or other setup necessary to use the other methods (`load_arg`,
|
/// frame or other setup necessary to use the other methods (`load_arg`,
|
||||||
/// `store_retval`, and spillslot accesses.) `self` is mutable so that we
|
/// `store_retval`, and spillslot accesses.) `self` is mutable so that we
|
||||||
@@ -113,21 +123,34 @@ pub trait ABIBody {
|
|||||||
/// likely closely related.
|
/// likely closely related.
|
||||||
fn gen_epilogue(&self) -> Vec<Self::I>;
|
fn gen_epilogue(&self) -> Vec<Self::I>;
|
||||||
|
|
||||||
/// Returns the full frame size for the given function, after prologue emission has run. This
|
/// Returns the full frame size for the given function, after prologue
|
||||||
/// comprises the spill slots and stack-storage slots (but not storage for clobbered callee-save
|
/// emission has run. This comprises the spill slots and stack-storage slots
|
||||||
/// registers, arguments pushed at callsites within this function, or other ephemeral pushes).
|
/// (but not storage for clobbered callee-save registers, arguments pushed
|
||||||
/// This is used for ABI variants where the client generates prologue/epilogue code, as in
|
/// at callsites within this function, or other ephemeral pushes). This is
|
||||||
/// Baldrdash (SpiderMonkey integration).
|
/// used for ABI variants where the client generates prologue/epilogue code,
|
||||||
|
/// as in Baldrdash (SpiderMonkey integration).
|
||||||
fn frame_size(&self) -> u32;
|
fn frame_size(&self) -> u32;
|
||||||
|
|
||||||
|
/// Returns the size of arguments expected on the stack.
|
||||||
|
fn stack_args_size(&self) -> u32;
|
||||||
|
|
||||||
/// Get the spill-slot size.
|
/// Get the spill-slot size.
|
||||||
fn get_spillslot_size(&self, rc: RegClass, ty: Type) -> u32;
|
fn get_spillslot_size(&self, rc: RegClass, ty: Type) -> u32;
|
||||||
|
|
||||||
/// Generate a spill.
|
/// Generate a spill. The type, if known, is given; this can be used to
|
||||||
fn gen_spill(&self, to_slot: SpillSlot, from_reg: RealReg, ty: Type) -> Self::I;
|
/// generate a store instruction optimized for the particular type rather
|
||||||
|
/// than the RegClass (e.g., only F64 that resides in a V128 register). If
|
||||||
|
/// no type is given, the implementation should spill the whole register.
|
||||||
|
fn gen_spill(&self, to_slot: SpillSlot, from_reg: RealReg, ty: Option<Type>) -> Self::I;
|
||||||
|
|
||||||
/// Generate a reload (fill).
|
/// Generate a reload (fill). As for spills, the type may be given to allow
|
||||||
fn gen_reload(&self, to_reg: Writable<RealReg>, from_slot: SpillSlot, ty: Type) -> Self::I;
|
/// a more optimized load instruction to be generated.
|
||||||
|
fn gen_reload(
|
||||||
|
&self,
|
||||||
|
to_reg: Writable<RealReg>,
|
||||||
|
from_slot: SpillSlot,
|
||||||
|
ty: Option<Type>,
|
||||||
|
) -> Self::I;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Trait implemented by an object that tracks ABI-related state and can
|
/// Trait implemented by an object that tracks ABI-related state and can
|
||||||
|
|||||||
@@ -140,7 +140,7 @@
|
|||||||
//! Given these invariants, we argue why each optimization preserves execution
|
//! Given these invariants, we argue why each optimization preserves execution
|
||||||
//! semantics below (grep for "Preserves execution semantics").
|
//! semantics below (grep for "Preserves execution semantics").
|
||||||
|
|
||||||
use crate::binemit::{Addend, CodeOffset, CodeSink, Reloc};
|
use crate::binemit::{Addend, CodeOffset, CodeSink, Reloc, Stackmap};
|
||||||
use crate::ir::{ExternalName, Opcode, SourceLoc, TrapCode};
|
use crate::ir::{ExternalName, Opcode, SourceLoc, TrapCode};
|
||||||
use crate::machinst::{BlockIndex, MachInstLabelUse, VCodeInst};
|
use crate::machinst::{BlockIndex, MachInstLabelUse, VCodeInst};
|
||||||
|
|
||||||
@@ -168,6 +168,8 @@ pub struct MachBuffer<I: VCodeInst> {
|
|||||||
call_sites: SmallVec<[MachCallSite; 16]>,
|
call_sites: SmallVec<[MachCallSite; 16]>,
|
||||||
/// Any source location mappings referring to this code.
|
/// Any source location mappings referring to this code.
|
||||||
srclocs: SmallVec<[MachSrcLoc; 64]>,
|
srclocs: SmallVec<[MachSrcLoc; 64]>,
|
||||||
|
/// Any stackmaps referring to this code.
|
||||||
|
stackmaps: SmallVec<[MachStackMap; 8]>,
|
||||||
/// The current source location in progress (after `start_srcloc()` and
|
/// The current source location in progress (after `start_srcloc()` and
|
||||||
/// before `end_srcloc()`). This is a (start_offset, src_loc) tuple.
|
/// before `end_srcloc()`). This is a (start_offset, src_loc) tuple.
|
||||||
cur_srcloc: Option<(CodeOffset, SourceLoc)>,
|
cur_srcloc: Option<(CodeOffset, SourceLoc)>,
|
||||||
@@ -228,6 +230,8 @@ pub struct MachBufferFinalized {
|
|||||||
call_sites: SmallVec<[MachCallSite; 16]>,
|
call_sites: SmallVec<[MachCallSite; 16]>,
|
||||||
/// Any source location mappings referring to this code.
|
/// Any source location mappings referring to this code.
|
||||||
srclocs: SmallVec<[MachSrcLoc; 64]>,
|
srclocs: SmallVec<[MachSrcLoc; 64]>,
|
||||||
|
/// Any stackmaps referring to this code.
|
||||||
|
stackmaps: SmallVec<[MachStackMap; 8]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
static UNKNOWN_LABEL_OFFSET: CodeOffset = 0xffff_ffff;
|
static UNKNOWN_LABEL_OFFSET: CodeOffset = 0xffff_ffff;
|
||||||
@@ -262,6 +266,7 @@ impl<I: VCodeInst> MachBuffer<I> {
|
|||||||
traps: SmallVec::new(),
|
traps: SmallVec::new(),
|
||||||
call_sites: SmallVec::new(),
|
call_sites: SmallVec::new(),
|
||||||
srclocs: SmallVec::new(),
|
srclocs: SmallVec::new(),
|
||||||
|
stackmaps: SmallVec::new(),
|
||||||
cur_srcloc: None,
|
cur_srcloc: None,
|
||||||
label_offsets: SmallVec::new(),
|
label_offsets: SmallVec::new(),
|
||||||
label_aliases: SmallVec::new(),
|
label_aliases: SmallVec::new(),
|
||||||
@@ -1090,6 +1095,7 @@ impl<I: VCodeInst> MachBuffer<I> {
|
|||||||
traps: self.traps,
|
traps: self.traps,
|
||||||
call_sites: self.call_sites,
|
call_sites: self.call_sites,
|
||||||
srclocs: self.srclocs,
|
srclocs: self.srclocs,
|
||||||
|
stackmaps: self.stackmaps,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1149,6 +1155,22 @@ impl<I: VCodeInst> MachBuffer<I> {
|
|||||||
self.srclocs.push(MachSrcLoc { start, end, loc });
|
self.srclocs.push(MachSrcLoc { start, end, loc });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Add stackmap metadata for this program point: a set of stack offsets
|
||||||
|
/// (from SP upward) that contain live references.
|
||||||
|
///
|
||||||
|
/// The `offset_to_fp` value is the offset from the nominal SP (at which the
|
||||||
|
/// `stack_offsets` are based) and the FP value. By subtracting
|
||||||
|
/// `offset_to_fp` from each `stack_offsets` element, one can obtain
|
||||||
|
/// live-reference offsets from FP instead.
|
||||||
|
pub fn add_stackmap(&mut self, insn_len: CodeOffset, stackmap: Stackmap) {
|
||||||
|
let offset = self.cur_offset();
|
||||||
|
self.stackmaps.push(MachStackMap {
|
||||||
|
offset,
|
||||||
|
offset_end: offset + insn_len,
|
||||||
|
stackmap,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MachBufferFinalized {
|
impl MachBufferFinalized {
|
||||||
@@ -1207,6 +1229,11 @@ impl MachBufferFinalized {
|
|||||||
sink.begin_rodata();
|
sink.begin_rodata();
|
||||||
sink.end_codegen();
|
sink.end_codegen();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the stackmap metadata for this code.
|
||||||
|
pub fn stackmaps(&self) -> &[MachStackMap] {
|
||||||
|
&self.stackmaps[..]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A constant that is deferred to the next constant-pool opportunity.
|
/// A constant that is deferred to the next constant-pool opportunity.
|
||||||
@@ -1286,6 +1313,19 @@ pub struct MachSrcLoc {
|
|||||||
pub loc: SourceLoc,
|
pub loc: SourceLoc,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Record of stackmap metadata: stack offsets containing references.
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct MachStackMap {
|
||||||
|
/// The code offset at which this stackmap applies.
|
||||||
|
pub offset: CodeOffset,
|
||||||
|
/// The code offset just past the "end" of the instruction: that is, the
|
||||||
|
/// offset of the first byte of the following instruction, or equivalently,
|
||||||
|
/// the start offset plus the instruction length.
|
||||||
|
pub offset_end: CodeOffset,
|
||||||
|
/// The Stackmap itself.
|
||||||
|
pub stackmap: Stackmap,
|
||||||
|
}
|
||||||
|
|
||||||
/// Record of branch instruction in the buffer, to facilitate editing.
|
/// Record of branch instruction in the buffer, to facilitate editing.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
struct MachBranch {
|
struct MachBranch {
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ where
|
|||||||
// Build the lowering context.
|
// Build the lowering context.
|
||||||
let lower = Lower::new(f, abi, block_order)?;
|
let lower = Lower::new(f, abi, block_order)?;
|
||||||
// Lower the IR.
|
// Lower the IR.
|
||||||
let mut vcode = lower.lower(b)?;
|
let (mut vcode, stackmap_request_info) = lower.lower(b)?;
|
||||||
|
|
||||||
debug!(
|
debug!(
|
||||||
"vcode from lowering: \n{}",
|
"vcode from lowering: \n{}",
|
||||||
@@ -57,11 +57,23 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If either there are no reference-typed values, or else there are
|
||||||
|
// but there are no safepoints at which we need to know about them,
|
||||||
|
// then we don't need stackmaps.
|
||||||
|
let sri = if stackmap_request_info.reftyped_vregs.len() > 0
|
||||||
|
&& stackmap_request_info.safepoint_insns.len() > 0
|
||||||
|
{
|
||||||
|
Some(&stackmap_request_info)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
let result = {
|
let result = {
|
||||||
let _tt = timing::regalloc();
|
let _tt = timing::regalloc();
|
||||||
allocate_registers_with_opts(
|
allocate_registers_with_opts(
|
||||||
&mut vcode,
|
&mut vcode,
|
||||||
b.reg_universe(),
|
b.reg_universe(),
|
||||||
|
sri,
|
||||||
Options {
|
Options {
|
||||||
run_checker,
|
run_checker,
|
||||||
algorithm,
|
algorithm,
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ use crate::machinst::{
|
|||||||
};
|
};
|
||||||
use crate::CodegenResult;
|
use crate::CodegenResult;
|
||||||
|
|
||||||
use regalloc::{Reg, RegClass, VirtualReg, Writable};
|
use regalloc::{Reg, RegClass, StackmapRequestInfo, VirtualReg, Writable};
|
||||||
|
|
||||||
use alloc::boxed::Box;
|
use alloc::boxed::Box;
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
@@ -146,6 +146,8 @@ pub trait LowerCtx {
|
|||||||
fn alloc_tmp(&mut self, rc: RegClass, ty: Type) -> Writable<Reg>;
|
fn alloc_tmp(&mut self, rc: RegClass, ty: Type) -> Writable<Reg>;
|
||||||
/// Emit a machine instruction.
|
/// Emit a machine instruction.
|
||||||
fn emit(&mut self, mach_inst: Self::I);
|
fn emit(&mut self, mach_inst: Self::I);
|
||||||
|
/// Emit a machine instruction that is a safepoint.
|
||||||
|
fn emit_safepoint(&mut self, mach_inst: Self::I);
|
||||||
/// Indicate that the given input uses the register returned by
|
/// Indicate that the given input uses the register returned by
|
||||||
/// `get_input()`. Codegen may not happen otherwise for the producing
|
/// `get_input()`. Codegen may not happen otherwise for the producing
|
||||||
/// instruction if it has no side effects and no uses.
|
/// instruction if it has no side effects and no uses.
|
||||||
@@ -206,6 +208,14 @@ pub trait LowerBackend {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A pending instruction to insert and auxiliary information about it: its source location and
|
||||||
|
/// whether it is a safepoint.
|
||||||
|
struct InstTuple<I: VCodeInst> {
|
||||||
|
loc: SourceLoc,
|
||||||
|
is_safepoint: bool,
|
||||||
|
inst: I,
|
||||||
|
}
|
||||||
|
|
||||||
/// Machine-independent lowering driver / machine-instruction container. Maintains a correspondence
|
/// Machine-independent lowering driver / machine-instruction container. Maintains a correspondence
|
||||||
/// from original Inst to MachInsts.
|
/// from original Inst to MachInsts.
|
||||||
pub struct Lower<'func, I: VCodeInst> {
|
pub struct Lower<'func, I: VCodeInst> {
|
||||||
@@ -237,17 +247,17 @@ pub struct Lower<'func, I: VCodeInst> {
|
|||||||
next_vreg: u32,
|
next_vreg: u32,
|
||||||
|
|
||||||
/// Insts in reverse block order, before final copy to vcode.
|
/// Insts in reverse block order, before final copy to vcode.
|
||||||
block_insts: Vec<(SourceLoc, I)>,
|
block_insts: Vec<InstTuple<I>>,
|
||||||
|
|
||||||
/// Ranges in `block_insts` constituting BBs.
|
/// Ranges in `block_insts` constituting BBs.
|
||||||
block_ranges: Vec<(usize, usize)>,
|
block_ranges: Vec<(usize, usize)>,
|
||||||
|
|
||||||
/// Instructions collected for the BB in progress, in reverse order, with
|
/// Instructions collected for the BB in progress, in reverse order, with
|
||||||
/// source-locs attached.
|
/// source-locs attached.
|
||||||
bb_insts: Vec<(SourceLoc, I)>,
|
bb_insts: Vec<InstTuple<I>>,
|
||||||
|
|
||||||
/// Instructions collected for the CLIF inst in progress, in forward order.
|
/// Instructions collected for the CLIF inst in progress, in forward order.
|
||||||
ir_insts: Vec<I>,
|
ir_insts: Vec<InstTuple<I>>,
|
||||||
|
|
||||||
/// The register to use for GetPinnedReg, if any, on this architecture.
|
/// The register to use for GetPinnedReg, if any, on this architecture.
|
||||||
pinned_reg: Option<Reg>,
|
pinned_reg: Option<Reg>,
|
||||||
@@ -276,6 +286,7 @@ fn alloc_vreg(
|
|||||||
let v = *next_vreg;
|
let v = *next_vreg;
|
||||||
*next_vreg += 1;
|
*next_vreg += 1;
|
||||||
value_regs[value] = Reg::new_virtual(regclass, v);
|
value_regs[value] = Reg::new_virtual(regclass, v);
|
||||||
|
debug!("value {} gets vreg {:?}", value, v);
|
||||||
}
|
}
|
||||||
value_regs[value].as_virtual_reg().unwrap()
|
value_regs[value].as_virtual_reg().unwrap()
|
||||||
}
|
}
|
||||||
@@ -579,15 +590,18 @@ impl<'func, I: VCodeInst> Lower<'func, I> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn finish_ir_inst(&mut self, loc: SourceLoc) {
|
fn finish_ir_inst(&mut self, loc: SourceLoc) {
|
||||||
for inst in self.ir_insts.drain(..).rev() {
|
// `bb_insts` is kept in reverse order, so emit the instructions in
|
||||||
self.bb_insts.push((loc, inst));
|
// reverse order.
|
||||||
|
for mut tuple in self.ir_insts.drain(..).rev() {
|
||||||
|
tuple.loc = loc;
|
||||||
|
self.bb_insts.push(tuple);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finish_bb(&mut self) {
|
fn finish_bb(&mut self) {
|
||||||
let start = self.block_insts.len();
|
let start = self.block_insts.len();
|
||||||
for pair in self.bb_insts.drain(..).rev() {
|
for tuple in self.bb_insts.drain(..).rev() {
|
||||||
self.block_insts.push(pair);
|
self.block_insts.push(tuple);
|
||||||
}
|
}
|
||||||
let end = self.block_insts.len();
|
let end = self.block_insts.len();
|
||||||
self.block_ranges.push((start, end));
|
self.block_ranges.push((start, end));
|
||||||
@@ -595,9 +609,14 @@ impl<'func, I: VCodeInst> Lower<'func, I> {
|
|||||||
|
|
||||||
fn copy_bbs_to_vcode(&mut self) {
|
fn copy_bbs_to_vcode(&mut self) {
|
||||||
for &(start, end) in self.block_ranges.iter().rev() {
|
for &(start, end) in self.block_ranges.iter().rev() {
|
||||||
for &(loc, ref inst) in &self.block_insts[start..end] {
|
for &InstTuple {
|
||||||
|
loc,
|
||||||
|
is_safepoint,
|
||||||
|
ref inst,
|
||||||
|
} in &self.block_insts[start..end]
|
||||||
|
{
|
||||||
self.vcode.set_srcloc(loc);
|
self.vcode.set_srcloc(loc);
|
||||||
self.vcode.push(inst.clone());
|
self.vcode.push(inst.clone(), is_safepoint);
|
||||||
}
|
}
|
||||||
self.vcode.end_bb();
|
self.vcode.end_bb();
|
||||||
}
|
}
|
||||||
@@ -645,7 +664,10 @@ impl<'func, I: VCodeInst> Lower<'func, I> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Lower the function.
|
/// Lower the function.
|
||||||
pub fn lower<B: LowerBackend<MInst = I>>(mut self, backend: &B) -> CodegenResult<VCode<I>> {
|
pub fn lower<B: LowerBackend<MInst = I>>(
|
||||||
|
mut self,
|
||||||
|
backend: &B,
|
||||||
|
) -> CodegenResult<(VCode<I>, StackmapRequestInfo)> {
|
||||||
debug!("about to lower function: {:?}", self.f);
|
debug!("about to lower function: {:?}", self.f);
|
||||||
|
|
||||||
// Initialize the ABI object, giving it a temp if requested.
|
// Initialize the ABI object, giving it a temp if requested.
|
||||||
@@ -730,10 +752,10 @@ impl<'func, I: VCodeInst> Lower<'func, I> {
|
|||||||
self.copy_bbs_to_vcode();
|
self.copy_bbs_to_vcode();
|
||||||
|
|
||||||
// Now that we've emitted all instructions into the VCodeBuilder, let's build the VCode.
|
// Now that we've emitted all instructions into the VCodeBuilder, let's build the VCode.
|
||||||
let vcode = self.vcode.build();
|
let (vcode, stackmap_info) = self.vcode.build();
|
||||||
debug!("built vcode: {:?}", vcode);
|
debug!("built vcode: {:?}", vcode);
|
||||||
|
|
||||||
Ok(vcode)
|
Ok((vcode, stackmap_info))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the actual inputs for a value. This is the implementation for
|
/// Get the actual inputs for a value. This is the implementation for
|
||||||
@@ -916,7 +938,19 @@ impl<'func, I: VCodeInst> LowerCtx for Lower<'func, I> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn emit(&mut self, mach_inst: I) {
|
fn emit(&mut self, mach_inst: I) {
|
||||||
self.ir_insts.push(mach_inst);
|
self.ir_insts.push(InstTuple {
|
||||||
|
loc: SourceLoc::default(),
|
||||||
|
is_safepoint: false,
|
||||||
|
inst: mach_inst,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn emit_safepoint(&mut self, mach_inst: I) {
|
||||||
|
self.ir_insts.push(InstTuple {
|
||||||
|
loc: SourceLoc::default(),
|
||||||
|
is_safepoint: true,
|
||||||
|
inst: mach_inst,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn use_input_reg(&mut self, input: LowerInput) {
|
fn use_input_reg(&mut self, input: LowerInput) {
|
||||||
|
|||||||
@@ -96,7 +96,7 @@
|
|||||||
//!
|
//!
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
use crate::binemit::{CodeInfo, CodeOffset};
|
use crate::binemit::{CodeInfo, CodeOffset, Stackmap};
|
||||||
use crate::ir::condcodes::IntCC;
|
use crate::ir::condcodes::IntCC;
|
||||||
use crate::ir::{Function, Type};
|
use crate::ir::{Function, Type};
|
||||||
use crate::result::CodegenResult;
|
use crate::result::CodegenResult;
|
||||||
@@ -191,6 +191,10 @@ pub trait MachInst: Clone + Debug {
|
|||||||
/// What is the worst-case instruction size emitted by this instruction type?
|
/// What is the worst-case instruction size emitted by this instruction type?
|
||||||
fn worst_case_size() -> CodeOffset;
|
fn worst_case_size() -> CodeOffset;
|
||||||
|
|
||||||
|
/// What is the register class used for reference types (GC-observable pointers)? Can
|
||||||
|
/// be dependent on compilation flags.
|
||||||
|
fn ref_type_regclass(_flags: &Flags) -> RegClass;
|
||||||
|
|
||||||
/// A label-use kind: a type that describes the types of label references that
|
/// A label-use kind: a type that describes the types of label references that
|
||||||
/// can occur in an instruction.
|
/// can occur in an instruction.
|
||||||
type LabelUse: MachInstLabelUse;
|
type LabelUse: MachInstLabelUse;
|
||||||
@@ -256,9 +260,21 @@ pub enum MachTerminator<'a> {
|
|||||||
/// A trait describing the ability to encode a MachInst into binary machine code.
|
/// A trait describing the ability to encode a MachInst into binary machine code.
|
||||||
pub trait MachInstEmit: MachInst {
|
pub trait MachInstEmit: MachInst {
|
||||||
/// Persistent state carried across `emit` invocations.
|
/// Persistent state carried across `emit` invocations.
|
||||||
type State: Default + Clone + Debug;
|
type State: MachInstEmitState<Self>;
|
||||||
/// Emit the instruction.
|
/// Emit the instruction.
|
||||||
fn emit(&self, code: &mut MachBuffer<Self>, flags: &Flags, state: &mut Self::State);
|
fn emit(&self, code: &mut MachBuffer<Self>, flags: &Flags, state: &mut Self::State);
|
||||||
|
/// Pretty-print the instruction.
|
||||||
|
fn pretty_print(&self, mb_rru: Option<&RealRegUniverse>, state: &mut Self::State) -> String;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A trait describing the emission state carried between MachInsts when
|
||||||
|
/// emitting a function body.
|
||||||
|
pub trait MachInstEmitState<I: MachInst>: Default + Clone + Debug {
|
||||||
|
/// Create a new emission state given the ABI object.
|
||||||
|
fn new(abi: &dyn ABIBody<I = I>) -> Self;
|
||||||
|
/// Update the emission state before emitting an instruction that is a
|
||||||
|
/// safepoint.
|
||||||
|
fn pre_safepoint(&mut self, _stackmap: Stackmap) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The result of a `MachBackend::compile_function()` call. Contains machine
|
/// The result of a `MachBackend::compile_function()` call. Contains machine
|
||||||
|
|||||||
@@ -17,14 +17,15 @@
|
|||||||
//! See the main module comment in `mod.rs` for more details on the VCode-based
|
//! See the main module comment in `mod.rs` for more details on the VCode-based
|
||||||
//! backend pipeline.
|
//! backend pipeline.
|
||||||
|
|
||||||
use crate::ir::{self, SourceLoc};
|
use crate::ir::{self, types, SourceLoc};
|
||||||
use crate::machinst::*;
|
use crate::machinst::*;
|
||||||
use crate::settings;
|
use crate::settings;
|
||||||
|
|
||||||
use regalloc::Function as RegallocFunction;
|
use regalloc::Function as RegallocFunction;
|
||||||
use regalloc::Set as RegallocSet;
|
use regalloc::Set as RegallocSet;
|
||||||
use regalloc::{
|
use regalloc::{
|
||||||
BlockIx, InstIx, Range, RegAllocResult, RegClass, RegUsageCollector, RegUsageMapper,
|
BlockIx, InstIx, Range, RegAllocResult, RegClass, RegUsageCollector, RegUsageMapper, SpillSlot,
|
||||||
|
StackmapRequestInfo,
|
||||||
};
|
};
|
||||||
|
|
||||||
use alloc::boxed::Box;
|
use alloc::boxed::Box;
|
||||||
@@ -56,6 +57,9 @@ pub struct VCode<I: VCodeInst> {
|
|||||||
/// VReg IR-level types.
|
/// VReg IR-level types.
|
||||||
vreg_types: Vec<Type>,
|
vreg_types: Vec<Type>,
|
||||||
|
|
||||||
|
/// Do we have any ref values among our vregs?
|
||||||
|
have_ref_values: bool,
|
||||||
|
|
||||||
/// Lowered machine instructions in order corresponding to the original IR.
|
/// Lowered machine instructions in order corresponding to the original IR.
|
||||||
insts: Vec<I>,
|
insts: Vec<I>,
|
||||||
|
|
||||||
@@ -82,6 +86,16 @@ pub struct VCode<I: VCodeInst> {
|
|||||||
|
|
||||||
/// ABI object.
|
/// ABI object.
|
||||||
abi: Box<dyn ABIBody<I = I>>,
|
abi: Box<dyn ABIBody<I = I>>,
|
||||||
|
|
||||||
|
/// Safepoint instruction indices. Filled in post-regalloc. (Prior to
|
||||||
|
/// regalloc, the safepoint instructions are listed in the separate
|
||||||
|
/// `StackmapRequestInfo` held separate from the `VCode`.)
|
||||||
|
safepoint_insns: Vec<InsnIndex>,
|
||||||
|
|
||||||
|
/// For each safepoint entry in `safepoint_insns`, a list of `SpillSlot`s.
|
||||||
|
/// These are used to generate actual stackmaps at emission. Filled in
|
||||||
|
/// post-regalloc.
|
||||||
|
safepoint_slots: Vec<Vec<SpillSlot>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A builder for a VCode function body. This builder is designed for the
|
/// A builder for a VCode function body. This builder is designed for the
|
||||||
@@ -102,6 +116,9 @@ pub struct VCodeBuilder<I: VCodeInst> {
|
|||||||
/// In-progress VCode.
|
/// In-progress VCode.
|
||||||
vcode: VCode<I>,
|
vcode: VCode<I>,
|
||||||
|
|
||||||
|
/// In-progress stackmap-request info.
|
||||||
|
stackmap_info: StackmapRequestInfo,
|
||||||
|
|
||||||
/// Index of the last block-start in the vcode.
|
/// Index of the last block-start in the vcode.
|
||||||
block_start: InsnIndex,
|
block_start: InsnIndex,
|
||||||
|
|
||||||
@@ -115,9 +132,17 @@ pub struct VCodeBuilder<I: VCodeInst> {
|
|||||||
impl<I: VCodeInst> VCodeBuilder<I> {
|
impl<I: VCodeInst> VCodeBuilder<I> {
|
||||||
/// Create a new VCodeBuilder.
|
/// Create a new VCodeBuilder.
|
||||||
pub fn new(abi: Box<dyn ABIBody<I = I>>, block_order: BlockLoweringOrder) -> VCodeBuilder<I> {
|
pub fn new(abi: Box<dyn ABIBody<I = I>>, block_order: BlockLoweringOrder) -> VCodeBuilder<I> {
|
||||||
|
let reftype_class = I::ref_type_regclass(abi.flags());
|
||||||
let vcode = VCode::new(abi, block_order);
|
let vcode = VCode::new(abi, block_order);
|
||||||
|
let stackmap_info = StackmapRequestInfo {
|
||||||
|
reftype_class,
|
||||||
|
reftyped_vregs: vec![],
|
||||||
|
safepoint_insns: vec![],
|
||||||
|
};
|
||||||
|
|
||||||
VCodeBuilder {
|
VCodeBuilder {
|
||||||
vcode,
|
vcode,
|
||||||
|
stackmap_info,
|
||||||
block_start: 0,
|
block_start: 0,
|
||||||
succ_start: 0,
|
succ_start: 0,
|
||||||
cur_srcloc: SourceLoc::default(),
|
cur_srcloc: SourceLoc::default(),
|
||||||
@@ -142,6 +167,15 @@ impl<I: VCodeInst> VCodeBuilder<I> {
|
|||||||
.resize(vreg.get_index() + 1, ir::types::I8);
|
.resize(vreg.get_index() + 1, ir::types::I8);
|
||||||
}
|
}
|
||||||
self.vcode.vreg_types[vreg.get_index()] = ty;
|
self.vcode.vreg_types[vreg.get_index()] = ty;
|
||||||
|
if is_reftype(ty) {
|
||||||
|
self.stackmap_info.reftyped_vregs.push(vreg);
|
||||||
|
self.vcode.have_ref_values = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Are there any reference-typed values at all among the vregs?
|
||||||
|
pub fn have_ref_values(&self) -> bool {
|
||||||
|
self.vcode.have_ref_values()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the current block as the entry block.
|
/// Set the current block as the entry block.
|
||||||
@@ -166,7 +200,7 @@ impl<I: VCodeInst> VCodeBuilder<I> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Push an instruction for the current BB and current IR inst within the BB.
|
/// Push an instruction for the current BB and current IR inst within the BB.
|
||||||
pub fn push(&mut self, insn: I) {
|
pub fn push(&mut self, insn: I, is_safepoint: bool) {
|
||||||
match insn.is_term() {
|
match insn.is_term() {
|
||||||
MachTerminator::None | MachTerminator::Ret => {}
|
MachTerminator::None | MachTerminator::Ret => {}
|
||||||
MachTerminator::Uncond(target) => {
|
MachTerminator::Uncond(target) => {
|
||||||
@@ -186,6 +220,11 @@ impl<I: VCodeInst> VCodeBuilder<I> {
|
|||||||
}
|
}
|
||||||
self.vcode.insts.push(insn);
|
self.vcode.insts.push(insn);
|
||||||
self.vcode.srclocs.push(self.cur_srcloc);
|
self.vcode.srclocs.push(self.cur_srcloc);
|
||||||
|
if is_safepoint {
|
||||||
|
self.stackmap_info
|
||||||
|
.safepoint_insns
|
||||||
|
.push(InstIx::new((self.vcode.insts.len() - 1) as u32));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the current source location.
|
/// Get the current source location.
|
||||||
@@ -198,21 +237,16 @@ impl<I: VCodeInst> VCodeBuilder<I> {
|
|||||||
self.cur_srcloc = srcloc;
|
self.cur_srcloc = srcloc;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Build the final VCode.
|
/// Build the final VCode, returning the vcode itself as well as auxiliary
|
||||||
pub fn build(self) -> VCode<I> {
|
/// information, such as the stackmap request information.
|
||||||
self.vcode
|
pub fn build(self) -> (VCode<I>, StackmapRequestInfo) {
|
||||||
|
// TODO: come up with an abstraction for "vcode and auxiliary data". The
|
||||||
|
// auxiliary data needs to be separate from the vcode so that it can be
|
||||||
|
// referenced as the vcode is mutated (e.g. by the register allocator).
|
||||||
|
(self.vcode, self.stackmap_info)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn block_ranges(indices: &[InstIx], len: usize) -> Vec<(usize, usize)> {
|
|
||||||
let v = indices
|
|
||||||
.iter()
|
|
||||||
.map(|iix| iix.get() as usize)
|
|
||||||
.chain(iter::once(len))
|
|
||||||
.collect::<Vec<usize>>();
|
|
||||||
v.windows(2).map(|p| (p[0], p[1])).collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_redundant_move<I: VCodeInst>(insn: &I) -> bool {
|
fn is_redundant_move<I: VCodeInst>(insn: &I) -> bool {
|
||||||
if let Some((to, from)) = insn.is_move() {
|
if let Some((to, from)) = insn.is_move() {
|
||||||
to.to_reg() == from
|
to.to_reg() == from
|
||||||
@@ -221,6 +255,11 @@ fn is_redundant_move<I: VCodeInst>(insn: &I) -> bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Is this type a reference type?
|
||||||
|
fn is_reftype(ty: Type) -> bool {
|
||||||
|
ty == types::R64 || ty == types::R32
|
||||||
|
}
|
||||||
|
|
||||||
impl<I: VCodeInst> VCode<I> {
|
impl<I: VCodeInst> VCode<I> {
|
||||||
/// New empty VCode.
|
/// New empty VCode.
|
||||||
fn new(abi: Box<dyn ABIBody<I = I>>, block_order: BlockLoweringOrder) -> VCode<I> {
|
fn new(abi: Box<dyn ABIBody<I = I>>, block_order: BlockLoweringOrder) -> VCode<I> {
|
||||||
@@ -228,6 +267,7 @@ impl<I: VCodeInst> VCode<I> {
|
|||||||
liveins: abi.liveins(),
|
liveins: abi.liveins(),
|
||||||
liveouts: abi.liveouts(),
|
liveouts: abi.liveouts(),
|
||||||
vreg_types: vec![],
|
vreg_types: vec![],
|
||||||
|
have_ref_values: false,
|
||||||
insts: vec![],
|
insts: vec![],
|
||||||
srclocs: vec![],
|
srclocs: vec![],
|
||||||
entry: 0,
|
entry: 0,
|
||||||
@@ -236,6 +276,8 @@ impl<I: VCodeInst> VCode<I> {
|
|||||||
block_succs: vec![],
|
block_succs: vec![],
|
||||||
block_order,
|
block_order,
|
||||||
abi,
|
abi,
|
||||||
|
safepoint_insns: vec![],
|
||||||
|
safepoint_slots: vec![],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -249,6 +291,11 @@ impl<I: VCodeInst> VCode<I> {
|
|||||||
self.vreg_types[vreg.get_index()]
|
self.vreg_types[vreg.get_index()]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Are there any reference-typed values at all among the vregs?
|
||||||
|
pub fn have_ref_values(&self) -> bool {
|
||||||
|
self.have_ref_values
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the entry block.
|
/// Get the entry block.
|
||||||
pub fn entry(&self) -> BlockIndex {
|
pub fn entry(&self) -> BlockIndex {
|
||||||
self.entry
|
self.entry
|
||||||
@@ -265,6 +312,11 @@ impl<I: VCodeInst> VCode<I> {
|
|||||||
self.abi.frame_size()
|
self.abi.frame_size()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Inbound stack-args size.
|
||||||
|
pub fn stack_args_size(&self) -> u32 {
|
||||||
|
self.abi.stack_args_size()
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the successors for a block.
|
/// Get the successors for a block.
|
||||||
pub fn succs(&self, block: BlockIndex) -> &[BlockIx] {
|
pub fn succs(&self, block: BlockIndex) -> &[BlockIx] {
|
||||||
let (start, end) = self.block_succ_range[block as usize];
|
let (start, end) = self.block_succ_range[block as usize];
|
||||||
@@ -281,17 +333,21 @@ impl<I: VCodeInst> VCode<I> {
|
|||||||
self.abi
|
self.abi
|
||||||
.set_clobbered(result.clobbered_registers.map(|r| Writable::from_reg(*r)));
|
.set_clobbered(result.clobbered_registers.map(|r| Writable::from_reg(*r)));
|
||||||
|
|
||||||
// We want to move instructions over in final block order, using the new
|
|
||||||
// block-start map given by the regalloc.
|
|
||||||
let block_ranges: Vec<(usize, usize)> =
|
|
||||||
block_ranges(result.target_map.elems(), result.insns.len());
|
|
||||||
let mut final_insns = vec![];
|
let mut final_insns = vec![];
|
||||||
let mut final_block_ranges = vec![(0, 0); self.num_blocks()];
|
let mut final_block_ranges = vec![(0, 0); self.num_blocks()];
|
||||||
let mut final_srclocs = vec![];
|
let mut final_srclocs = vec![];
|
||||||
|
let mut final_safepoint_insns = vec![];
|
||||||
|
let mut safept_idx = 0;
|
||||||
|
|
||||||
|
assert!(result.target_map.elems().len() == self.num_blocks());
|
||||||
for block in 0..self.num_blocks() {
|
for block in 0..self.num_blocks() {
|
||||||
|
let start = result.target_map.elems()[block].get() as usize;
|
||||||
|
let end = if block == self.num_blocks() - 1 {
|
||||||
|
result.insns.len()
|
||||||
|
} else {
|
||||||
|
result.target_map.elems()[block + 1].get() as usize
|
||||||
|
};
|
||||||
let block = block as BlockIndex;
|
let block = block as BlockIndex;
|
||||||
let (start, end) = block_ranges[block as usize];
|
|
||||||
let final_start = final_insns.len() as InsnIndex;
|
let final_start = final_insns.len() as InsnIndex;
|
||||||
|
|
||||||
if block == self.entry {
|
if block == self.entry {
|
||||||
@@ -333,6 +389,16 @@ impl<I: VCodeInst> VCode<I> {
|
|||||||
final_insns.push(insn.clone());
|
final_insns.push(insn.clone());
|
||||||
final_srclocs.push(srcloc);
|
final_srclocs.push(srcloc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Was this instruction a safepoint instruction? Add its final
|
||||||
|
// index to the safepoint insn-index list if so.
|
||||||
|
if safept_idx < result.new_safepoint_insns.len()
|
||||||
|
&& (result.new_safepoint_insns[safept_idx].get() as usize) == i
|
||||||
|
{
|
||||||
|
let idx = final_insns.len() - 1;
|
||||||
|
final_safepoint_insns.push(idx as InsnIndex);
|
||||||
|
safept_idx += 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let final_end = final_insns.len() as InsnIndex;
|
let final_end = final_insns.len() as InsnIndex;
|
||||||
@@ -344,6 +410,12 @@ impl<I: VCodeInst> VCode<I> {
|
|||||||
self.insts = final_insns;
|
self.insts = final_insns;
|
||||||
self.srclocs = final_srclocs;
|
self.srclocs = final_srclocs;
|
||||||
self.block_ranges = final_block_ranges;
|
self.block_ranges = final_block_ranges;
|
||||||
|
self.safepoint_insns = final_safepoint_insns;
|
||||||
|
|
||||||
|
// Save safepoint slot-lists. These will be passed to the `EmitState`
|
||||||
|
// for the machine backend during emission so that it can do
|
||||||
|
// target-specific translations of slot numbers to stack offsets.
|
||||||
|
self.safepoint_slots = result.stackmaps;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Emit the instructions to a `MachBuffer`, containing fixed-up code and external
|
/// Emit the instructions to a `MachBuffer`, containing fixed-up code and external
|
||||||
@@ -353,11 +425,12 @@ impl<I: VCodeInst> VCode<I> {
|
|||||||
I: MachInstEmit,
|
I: MachInstEmit,
|
||||||
{
|
{
|
||||||
let mut buffer = MachBuffer::new();
|
let mut buffer = MachBuffer::new();
|
||||||
let mut state = Default::default();
|
let mut state = I::State::new(&*self.abi);
|
||||||
|
|
||||||
buffer.reserve_labels_for_blocks(self.num_blocks() as BlockIndex); // first N MachLabels are simply block indices.
|
buffer.reserve_labels_for_blocks(self.num_blocks() as BlockIndex); // first N MachLabels are simply block indices.
|
||||||
|
|
||||||
let flags = self.abi.flags();
|
let flags = self.abi.flags();
|
||||||
|
let mut safepoint_idx = 0;
|
||||||
let mut cur_srcloc = None;
|
let mut cur_srcloc = None;
|
||||||
for block in 0..self.num_blocks() {
|
for block in 0..self.num_blocks() {
|
||||||
let block = block as BlockIndex;
|
let block = block as BlockIndex;
|
||||||
@@ -381,6 +454,19 @@ impl<I: VCodeInst> VCode<I> {
|
|||||||
cur_srcloc = Some(srcloc);
|
cur_srcloc = Some(srcloc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if safepoint_idx < self.safepoint_insns.len()
|
||||||
|
&& self.safepoint_insns[safepoint_idx] == iix
|
||||||
|
{
|
||||||
|
if self.safepoint_slots[safepoint_idx].len() > 0 {
|
||||||
|
let stackmap = self.abi.spillslots_to_stackmap(
|
||||||
|
&self.safepoint_slots[safepoint_idx][..],
|
||||||
|
&state,
|
||||||
|
);
|
||||||
|
state.pre_safepoint(stackmap);
|
||||||
|
}
|
||||||
|
safepoint_idx += 1;
|
||||||
|
}
|
||||||
|
|
||||||
self.insts[iix as usize].emit(&mut buffer, flags, &mut state);
|
self.insts[iix as usize].emit(&mut buffer, flags, &mut state);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -476,13 +562,18 @@ impl<I: VCodeInst> RegallocFunction for VCode<I> {
|
|||||||
self.abi.get_spillslot_size(regclass, ty)
|
self.abi.get_spillslot_size(regclass, ty)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn gen_spill(&self, to_slot: SpillSlot, from_reg: RealReg, vreg: VirtualReg) -> I {
|
fn gen_spill(&self, to_slot: SpillSlot, from_reg: RealReg, vreg: Option<VirtualReg>) -> I {
|
||||||
let ty = self.vreg_type(vreg);
|
let ty = vreg.map(|v| self.vreg_type(v));
|
||||||
self.abi.gen_spill(to_slot, from_reg, ty)
|
self.abi.gen_spill(to_slot, from_reg, ty)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn gen_reload(&self, to_reg: Writable<RealReg>, from_slot: SpillSlot, vreg: VirtualReg) -> I {
|
fn gen_reload(
|
||||||
let ty = self.vreg_type(vreg);
|
&self,
|
||||||
|
to_reg: Writable<RealReg>,
|
||||||
|
from_slot: SpillSlot,
|
||||||
|
vreg: Option<VirtualReg>,
|
||||||
|
) -> I {
|
||||||
|
let ty = vreg.map(|v| self.vreg_type(v));
|
||||||
self.abi.gen_reload(to_reg, from_slot, ty)
|
self.abi.gen_reload(to_reg, from_slot, ty)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -531,7 +622,7 @@ impl<I: VCodeInst> fmt::Debug for VCode<I> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Pretty-printing with `RealRegUniverse` context.
|
/// Pretty-printing with `RealRegUniverse` context.
|
||||||
impl<I: VCodeInst + ShowWithRRU> ShowWithRRU for VCode<I> {
|
impl<I: VCodeInst> ShowWithRRU for VCode<I> {
|
||||||
fn show_rru(&self, mb_rru: Option<&RealRegUniverse>) -> String {
|
fn show_rru(&self, mb_rru: Option<&RealRegUniverse>) -> String {
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
|
|
||||||
@@ -539,6 +630,8 @@ impl<I: VCodeInst + ShowWithRRU> ShowWithRRU for VCode<I> {
|
|||||||
write!(&mut s, "VCode_ShowWithRRU {{{{\n").unwrap();
|
write!(&mut s, "VCode_ShowWithRRU {{{{\n").unwrap();
|
||||||
write!(&mut s, " Entry block: {}\n", self.entry).unwrap();
|
write!(&mut s, " Entry block: {}\n", self.entry).unwrap();
|
||||||
|
|
||||||
|
let mut state = Default::default();
|
||||||
|
let mut safepoint_idx = 0;
|
||||||
for i in 0..self.num_blocks() {
|
for i in 0..self.num_blocks() {
|
||||||
let block = i as BlockIndex;
|
let block = i as BlockIndex;
|
||||||
|
|
||||||
@@ -552,11 +645,22 @@ impl<I: VCodeInst + ShowWithRRU> ShowWithRRU for VCode<I> {
|
|||||||
let (start, end) = self.block_ranges[block as usize];
|
let (start, end) = self.block_ranges[block as usize];
|
||||||
write!(&mut s, " (instruction range: {} .. {})\n", start, end).unwrap();
|
write!(&mut s, " (instruction range: {} .. {})\n", start, end).unwrap();
|
||||||
for inst in start..end {
|
for inst in start..end {
|
||||||
|
if safepoint_idx < self.safepoint_insns.len()
|
||||||
|
&& self.safepoint_insns[safepoint_idx] == inst
|
||||||
|
{
|
||||||
|
write!(
|
||||||
|
&mut s,
|
||||||
|
" (safepoint: slots {:?} with EmitState {:?})\n",
|
||||||
|
self.safepoint_slots[safepoint_idx], state,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
safepoint_idx += 1;
|
||||||
|
}
|
||||||
write!(
|
write!(
|
||||||
&mut s,
|
&mut s,
|
||||||
" Inst {}: {}\n",
|
" Inst {}: {}\n",
|
||||||
inst,
|
inst,
|
||||||
self.insts[inst as usize].show_rru(mb_rru)
|
self.insts[inst as usize].pretty_print(mb_rru, &mut state)
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
use crate::cursor::{Cursor, FuncCursor};
|
use crate::cursor::{Cursor, FuncCursor};
|
||||||
use crate::dominator_tree::DominatorTree;
|
use crate::dominator_tree::DominatorTree;
|
||||||
use crate::ir::{Function, InstBuilder, Opcode};
|
use crate::inst_predicates::is_safepoint;
|
||||||
|
use crate::ir::{Function, InstBuilder};
|
||||||
use crate::isa::TargetIsa;
|
use crate::isa::TargetIsa;
|
||||||
use crate::regalloc::live_value_tracker::LiveValueTracker;
|
use crate::regalloc::live_value_tracker::LiveValueTracker;
|
||||||
use crate::regalloc::liveness::Liveness;
|
use crate::regalloc::liveness::Liveness;
|
||||||
@@ -51,12 +52,8 @@ pub fn emit_stackmaps(
|
|||||||
pos.goto_top(block);
|
pos.goto_top(block);
|
||||||
|
|
||||||
while let Some(inst) = pos.next_inst() {
|
while let Some(inst) = pos.next_inst() {
|
||||||
if pos.func.dfg[inst].opcode().is_resumable_trap() {
|
if is_safepoint(&pos.func, inst) {
|
||||||
insert_and_encode_safepoint(&mut pos, tracker, isa);
|
insert_and_encode_safepoint(&mut pos, tracker, isa);
|
||||||
} else if pos.func.dfg[inst].opcode().is_call() {
|
|
||||||
insert_and_encode_safepoint(&mut pos, tracker, isa);
|
|
||||||
} else if pos.func.dfg[inst].opcode() == Opcode::Safepoint {
|
|
||||||
panic!("safepoint instruction can only be used by the compiler!");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process the instruction and get rid of dead values.
|
// Process the instruction and get rid of dead values.
|
||||||
|
|||||||
116
cranelift/filetests/filetests/vcode/aarch64/reftypes.clif
Normal file
116
cranelift/filetests/filetests/vcode/aarch64/reftypes.clif
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
test compile
|
||||||
|
target aarch64
|
||||||
|
|
||||||
|
function %f0(r64) -> r64 {
|
||||||
|
block0(v0: r64):
|
||||||
|
return v0
|
||||||
|
}
|
||||||
|
|
||||||
|
; check: stp fp, lr, [sp, #-16]!
|
||||||
|
; nextln: mov fp, sp
|
||||||
|
; nextln: mov sp, fp
|
||||||
|
; nextln: ldp fp, lr, [sp], #16
|
||||||
|
; nextln: ret
|
||||||
|
|
||||||
|
function %f1(r64) -> b1 {
|
||||||
|
block0(v0: r64):
|
||||||
|
v1 = is_null v0
|
||||||
|
return v1
|
||||||
|
}
|
||||||
|
|
||||||
|
; check: stp fp, lr, [sp, #-16]!
|
||||||
|
; nextln: mov fp, sp
|
||||||
|
; nextln: subs xzr, x0, #0
|
||||||
|
; nextln: cset x0, eq
|
||||||
|
; nextln: mov sp, fp
|
||||||
|
; nextln: ldp fp, lr, [sp], #16
|
||||||
|
; nextln: ret
|
||||||
|
|
||||||
|
function %f2(r64) -> b1 {
|
||||||
|
block0(v0: r64):
|
||||||
|
v1 = is_invalid v0
|
||||||
|
return v1
|
||||||
|
}
|
||||||
|
|
||||||
|
; check: stp fp, lr, [sp, #-16]!
|
||||||
|
; nextln: mov fp, sp
|
||||||
|
; nextln: adds xzr, x0, #1
|
||||||
|
; nextln: cset x0, eq
|
||||||
|
; nextln: mov sp, fp
|
||||||
|
; nextln: ldp fp, lr, [sp], #16
|
||||||
|
; nextln: ret
|
||||||
|
|
||||||
|
function %f3() -> r64 {
|
||||||
|
block0:
|
||||||
|
v0 = null.r64
|
||||||
|
return v0
|
||||||
|
}
|
||||||
|
|
||||||
|
; check: stp fp, lr, [sp, #-16]!
|
||||||
|
; nextln: mov fp, sp
|
||||||
|
; nextln: movz x0, #0
|
||||||
|
; nextln: mov sp, fp
|
||||||
|
; nextln: ldp fp, lr, [sp], #16
|
||||||
|
; nextln: ret
|
||||||
|
|
||||||
|
function %f4(r64, r64) -> r64, r64, r64 {
|
||||||
|
fn0 = %f(r64) -> b1
|
||||||
|
ss0 = explicit_slot 8
|
||||||
|
|
||||||
|
block0(v0: r64, v1: r64):
|
||||||
|
v2 = call fn0(v0)
|
||||||
|
stack_store.r64 v0, ss0
|
||||||
|
brz v2, block1(v1, v0)
|
||||||
|
jump block2(v0, v1)
|
||||||
|
|
||||||
|
block1(v3: r64, v4: r64):
|
||||||
|
jump block3(v3, v4)
|
||||||
|
|
||||||
|
block2(v5: r64, v6: r64):
|
||||||
|
jump block3(v5, v6)
|
||||||
|
|
||||||
|
block3(v7: r64, v8: r64):
|
||||||
|
v9 = stack_load.r64 ss0
|
||||||
|
return v7, v8, v9
|
||||||
|
}
|
||||||
|
|
||||||
|
; check: Block 0:
|
||||||
|
; check: stp fp, lr, [sp, #-16]!
|
||||||
|
; nextln: mov fp, sp
|
||||||
|
; nextln: sub sp, sp, #32
|
||||||
|
; nextln: stp x19, x20, [sp, #-16]!
|
||||||
|
; nextln: virtual_sp_offset_adjust 16
|
||||||
|
; nextln: mov x19, x0
|
||||||
|
; nextln: mov x20, x1
|
||||||
|
; nextln: mov x0, x19
|
||||||
|
; nextln: ldr x16, 8 ; b 12 ; data
|
||||||
|
; nextln: stur x19, [sp, #24]
|
||||||
|
; nextln: stur x20, [sp, #32]
|
||||||
|
; nextln: (safepoint: slots [S0, S1]
|
||||||
|
; nextln: blr x16
|
||||||
|
; nextln: ldur x19, [sp, #24]
|
||||||
|
; nextln: ldur x20, [sp, #32]
|
||||||
|
; nextln: add x1, sp, #16
|
||||||
|
; nextln: stur x19, [x1]
|
||||||
|
; nextln: and w0, w0, #1
|
||||||
|
; nextln: cbz x0, label1 ; b label3
|
||||||
|
; check: Block 1:
|
||||||
|
; check: b label2
|
||||||
|
; check: Block 2:
|
||||||
|
; check: mov x0, x20
|
||||||
|
; nextln: b label5
|
||||||
|
; check: Block 3:
|
||||||
|
; check: b label4
|
||||||
|
; check: Block 4:
|
||||||
|
; check: mov x0, x19
|
||||||
|
; nextln: mov x19, x20
|
||||||
|
; nextln: b label5
|
||||||
|
; check: Block 5:
|
||||||
|
; check: add x1, sp, #16
|
||||||
|
; nextln: ldur x1, [x1]
|
||||||
|
; nextln: mov x2, x1
|
||||||
|
; nextln: mov x1, x19
|
||||||
|
; nextln: ldp x19, x20, [sp], #16
|
||||||
|
; nextln: mov sp, fp
|
||||||
|
; nextln: ldp fp, lr, [sp], #16
|
||||||
|
; nextln: ret
|
||||||
@@ -30,6 +30,7 @@ fn cranelift_to_wasmparser_type(ty: Type) -> WasmResult<wasmparser::Type> {
|
|||||||
types::I64 => wasmparser::Type::I64,
|
types::I64 => wasmparser::Type::I64,
|
||||||
types::F32 => wasmparser::Type::F32,
|
types::F32 => wasmparser::Type::F32,
|
||||||
types::F64 => wasmparser::Type::F64,
|
types::F64 => wasmparser::Type::F64,
|
||||||
|
types::R32 | types::R64 => wasmparser::Type::ExternRef,
|
||||||
_ => {
|
_ => {
|
||||||
return Err(WasmError::Unsupported(format!(
|
return Err(WasmError::Unsupported(format!(
|
||||||
"Cannot convert Cranelift type to Wasm signature: {:?}",
|
"Cannot convert Cranelift type to Wasm signature: {:?}",
|
||||||
|
|||||||
Reference in New Issue
Block a user