Support for SpiderMonkey's "Wasm ABI 2020".
As part of a Wasm JIT update, SpiderMonkey is changing its internal WebAssembly function ABI. The new ABI's frame format includes "caller TLS" and "callee TLS" slots. The details of where these come from are not important; from Cranelift's point of view, the only relevant requirement is that we have two on-stack args that are always present (offsetting other on-stack args), and that we define special argument purposes so that we can supply values for these slots. Note that this adds a *new* ABI (a variant of the Baldrdash ABI) because we do not want to tightly couple the landing of this PR to the landing of the changes in SpiderMonkey; it's better if both the old and new behavior remain available in Cranelift, so SpiderMonkey can continue to vendor Cranelift even if it does not land (or backs out) the ABI change. Furthermore, note that this needs to be a Cranelift-level change (i.e. cannot be done purely from the translator environment implementation) because the special TLS arguments must always go on the stack, which would not otherwise happen with the usual argument-placement logic; and there is no primitive to push a value directly in CLIF code (the notion of a stack frame is a lower-level concept).
This commit is contained in:
@@ -127,9 +127,24 @@ use std::mem;
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum ABIArg {
|
||||
/// In a real register.
|
||||
Reg(RealReg, ir::Type, ir::ArgumentExtension),
|
||||
Reg(
|
||||
RealReg,
|
||||
ir::Type,
|
||||
ir::ArgumentExtension,
|
||||
ir::ArgumentPurpose,
|
||||
),
|
||||
/// Arguments only: on stack, at given offset from SP at entry.
|
||||
Stack(i64, ir::Type, ir::ArgumentExtension),
|
||||
Stack(i64, ir::Type, ir::ArgumentExtension, ir::ArgumentPurpose),
|
||||
}
|
||||
|
||||
impl ABIArg {
|
||||
/// Get the purpose of this arg.
|
||||
fn get_purpose(self) -> ir::ArgumentPurpose {
|
||||
match self {
|
||||
ABIArg::Reg(_, _, _, purpose) => purpose,
|
||||
ABIArg::Stack(_, _, _, purpose) => purpose,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Are we computing information about arguments or return values? Much of the
|
||||
@@ -308,6 +323,7 @@ pub trait ABIMachineSpec {
|
||||
/// nominal SP offset; caller will do that.
|
||||
fn gen_clobber_save(
|
||||
call_conv: isa::CallConv,
|
||||
flags: &settings::Flags,
|
||||
clobbers: &Set<Writable<RealReg>>,
|
||||
) -> (u64, SmallVec<[Self::I; 16]>);
|
||||
|
||||
@@ -317,6 +333,7 @@ pub trait ABIMachineSpec {
|
||||
/// clobber-save sequence finished.
|
||||
fn gen_clobber_restore(
|
||||
call_conv: isa::CallConv,
|
||||
flags: &settings::Flags,
|
||||
clobbers: &Set<Writable<RealReg>>,
|
||||
) -> SmallVec<[Self::I; 16]>;
|
||||
|
||||
@@ -700,8 +717,8 @@ impl<M: ABIMachineSpec> ABICallee for ABICalleeImpl<M> {
|
||||
match &self.sig.args[idx] {
|
||||
// Extension mode doesn't matter (we're copying out, not in; we
|
||||
// ignore high bits by convention).
|
||||
&ABIArg::Reg(r, ty, _) => M::gen_move(into_reg, r.to_reg(), ty),
|
||||
&ABIArg::Stack(off, ty, _) => M::gen_load_stack(
|
||||
&ABIArg::Reg(r, ty, ..) => M::gen_move(into_reg, r.to_reg(), ty),
|
||||
&ABIArg::Stack(off, ty, ..) => M::gen_load_stack(
|
||||
StackAMode::FPOffset(M::fp_to_arg_offset(self.call_conv, &self.flags) + off, ty),
|
||||
into_reg,
|
||||
ty,
|
||||
@@ -709,11 +726,21 @@ impl<M: ABIMachineSpec> ABICallee for ABICalleeImpl<M> {
|
||||
}
|
||||
}
|
||||
|
||||
fn arg_is_needed_in_body(&self, idx: usize) -> bool {
|
||||
match self.sig.args[idx].get_purpose() {
|
||||
// Special Baldrdash-specific pseudo-args that are present only to
|
||||
// fill stack slots. Won't ever be used as ordinary values in the
|
||||
// body.
|
||||
ir::ArgumentPurpose::CalleeTLS | ir::ArgumentPurpose::CallerTLS => false,
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
|
||||
fn gen_copy_reg_to_retval(&self, idx: usize, from_reg: Writable<Reg>) -> Vec<Self::I> {
|
||||
let mut ret = Vec::new();
|
||||
let word_bits = M::word_bits() as u8;
|
||||
match &self.sig.rets[idx] {
|
||||
&ABIArg::Reg(r, ty, ext) => {
|
||||
&ABIArg::Reg(r, ty, ext, ..) => {
|
||||
let from_bits = ty_bits(ty) as u8;
|
||||
let dest_reg = Writable::from_reg(r.to_reg());
|
||||
match (ext, from_bits) {
|
||||
@@ -732,7 +759,7 @@ impl<M: ABIMachineSpec> ABICallee for ABICalleeImpl<M> {
|
||||
_ => ret.push(M::gen_move(dest_reg, from_reg.to_reg(), ty)),
|
||||
};
|
||||
}
|
||||
&ABIArg::Stack(off, mut ty, ext) => {
|
||||
&ABIArg::Stack(off, mut ty, ext, ..) => {
|
||||
let from_bits = ty_bits(ty) as u8;
|
||||
// A machine ABI implementation should ensure that stack frames
|
||||
// have "reasonable" size. All current ABIs for machinst
|
||||
@@ -937,7 +964,8 @@ impl<M: ABIMachineSpec> ABICallee for ABICalleeImpl<M> {
|
||||
}
|
||||
|
||||
// Save clobbered registers.
|
||||
let (clobber_size, clobber_insts) = M::gen_clobber_save(self.call_conv, &self.clobbered);
|
||||
let (clobber_size, clobber_insts) =
|
||||
M::gen_clobber_save(self.call_conv, &self.flags, &self.clobbered);
|
||||
insts.extend(clobber_insts);
|
||||
|
||||
if clobber_size > 0 {
|
||||
@@ -952,7 +980,11 @@ impl<M: ABIMachineSpec> ABICallee for ABICalleeImpl<M> {
|
||||
let mut insts = vec![];
|
||||
|
||||
// Restore clobbered registers.
|
||||
insts.extend(M::gen_clobber_restore(self.call_conv, &self.clobbered));
|
||||
insts.extend(M::gen_clobber_restore(
|
||||
self.call_conv,
|
||||
&self.flags,
|
||||
&self.clobbered,
|
||||
));
|
||||
|
||||
// N.B.: we do *not* emit a nominal SP adjustment here, because (i) there will be no
|
||||
// references to nominal SP offsets before the return below, and (ii) the instruction
|
||||
@@ -1135,7 +1167,7 @@ impl<M: ABIMachineSpec> ABICaller for ABICallerImpl<M> {
|
||||
let word_rc = M::word_reg_class();
|
||||
let word_bits = M::word_bits() as usize;
|
||||
match &self.sig.args[idx] {
|
||||
&ABIArg::Reg(reg, ty, ext)
|
||||
&ABIArg::Reg(reg, ty, ext, _)
|
||||
if ext != ir::ArgumentExtension::None && ty_bits(ty) < word_bits =>
|
||||
{
|
||||
assert_eq!(word_rc, reg.get_class());
|
||||
@@ -1152,10 +1184,10 @@ impl<M: ABIMachineSpec> ABICaller for ABICallerImpl<M> {
|
||||
word_bits as u8,
|
||||
));
|
||||
}
|
||||
&ABIArg::Reg(reg, ty, _) => {
|
||||
&ABIArg::Reg(reg, ty, _, _) => {
|
||||
ctx.emit(M::gen_move(Writable::from_reg(reg.to_reg()), from_reg, ty));
|
||||
}
|
||||
&ABIArg::Stack(off, mut ty, ext) => {
|
||||
&ABIArg::Stack(off, mut ty, ext, _) => {
|
||||
if ext != ir::ArgumentExtension::None && ty_bits(ty) < word_bits {
|
||||
assert_eq!(word_rc, from_reg.get_class());
|
||||
let signed = match ext {
|
||||
@@ -1194,8 +1226,8 @@ impl<M: ABIMachineSpec> ABICaller for ABICallerImpl<M> {
|
||||
match &self.sig.rets[idx] {
|
||||
// Extension mode doesn't matter because we're copying out, not in,
|
||||
// and we ignore high bits in our own registers by convention.
|
||||
&ABIArg::Reg(reg, ty, _) => ctx.emit(M::gen_move(into_reg, reg.to_reg(), ty)),
|
||||
&ABIArg::Stack(off, ty, _) => {
|
||||
&ABIArg::Reg(reg, ty, _, _) => ctx.emit(M::gen_move(into_reg, reg.to_reg(), ty)),
|
||||
&ABIArg::Stack(off, ty, _, _) => {
|
||||
let ret_area_base = self.sig.stack_arg_space;
|
||||
ctx.emit(M::gen_load_stack(
|
||||
StackAMode::SPOffset(off + ret_area_base, ty),
|
||||
|
||||
Reference in New Issue
Block a user