Skip to content

Commit 08353fc

Browse files
committed
Reftypes part two: add support for stackmaps.
This commit adds support for generating stackmaps at safepoints to the new backend framework and to the AArch64 backend in particular. It has been tested to work with SpiderMonkey.
1 parent b93e8c2 commit 08353fc

File tree

17 files changed

+597
-143
lines changed

17 files changed

+597
-143
lines changed

Cargo.lock

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cranelift/codegen/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ smallvec = { version = "1.0.0" }
2626
thiserror = "1.0.4"
2727
byteorder = { version = "1.3.2", default-features = false }
2828
peepmatic-runtime = { path = "../peepmatic/crates/runtime", optional = true, version = "0.2.0" }
29-
regalloc = "0.0.26"
29+
regalloc = { version = "0.0.27" }
3030
# It is a goal of the cranelift-codegen crate to have minimal external dependencies.
3131
# Please don't add any unless they are essential to the task of creating binary
3232
# machine code. Integration tests that need external dependencies can be

cranelift/codegen/src/inst_predicates.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,3 +61,10 @@ pub fn is_constant_64bit(func: &Function, inst: Inst) -> Option<u64> {
6161
_ => None,
6262
}
6363
}
64+
65+
/// Is the given instruction a safepoint (i.e., potentially causes a GC, depending on the
66+
/// embedding, and so requires reftyped values to be enumerated with a stackmap)?
67+
pub fn is_safepoint(func: &Function, inst: Inst) -> bool {
68+
let op = func.dfg[inst].opcode();
69+
op.is_resumable_trap() || op.is_call()
70+
}

cranelift/codegen/src/isa/aarch64/abi.rs

Lines changed: 106 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -90,12 +90,13 @@
9090
//! - Return v1 in memory at `[P+8]`.
9191
//! - Return v0 in memory at `[P+16]`.
9292
93+
use crate::binemit::Stackmap;
9394
use crate::ir;
9495
use crate::ir::types;
9596
use crate::ir::types::*;
9697
use crate::ir::{ArgumentExtension, StackSlot};
9798
use crate::isa;
98-
use crate::isa::aarch64::{inst::*, lower::ty_bits};
99+
use crate::isa::aarch64::{inst::EmitState, inst::*, lower::ty_bits};
99100
use crate::machinst::*;
100101
use crate::settings;
101102
use crate::{CodegenError, CodegenResult};
@@ -372,7 +373,10 @@ pub struct AArch64ABIBody {
372373
clobbered: Set<Writable<RealReg>>,
373374
/// Total number of spillslots, from regalloc.
374375
spillslots: Option<usize>,
375-
/// Total frame size.
376+
/// "Total frame size", as defined by "distance between FP and nominal-SP".
377+
/// Some items are pushed below nominal SP, so the function may actually use
378+
/// more stack than this would otherwise imply. It is simply the initial
379+
/// frame/allocation size needed for stackslots and spillslots.
376380
total_frame_size: Option<u32>,
377381
/// The register holding the return-area pointer, if needed.
378382
ret_area_ptr: Option<Writable<Reg>>,
@@ -811,6 +815,35 @@ fn get_caller_saves(call_conv: isa::CallConv) -> Vec<Writable<Reg>> {
811815
caller_saved
812816
}
813817

818+
fn gen_sp_adjust_insts<F: FnMut(Inst)>(adj: u64, is_sub: bool, mut f: F) {
819+
let alu_op = if is_sub { ALUOp::Sub64 } else { ALUOp::Add64 };
820+
821+
if let Some(imm12) = Imm12::maybe_from_u64(adj) {
822+
let adj_inst = Inst::AluRRImm12 {
823+
alu_op,
824+
rd: writable_stack_reg(),
825+
rn: stack_reg(),
826+
imm12,
827+
};
828+
f(adj_inst);
829+
} else {
830+
let tmp = writable_spilltmp_reg();
831+
let const_inst = Inst::LoadConst64 {
832+
rd: tmp,
833+
const_data: adj,
834+
};
835+
let adj_inst = Inst::AluRRRExtend {
836+
alu_op,
837+
rd: writable_stack_reg(),
838+
rn: stack_reg(),
839+
rm: tmp.to_reg(),
840+
extendop: ExtendOp::UXTX,
841+
};
842+
f(const_inst);
843+
f(adj_inst);
844+
}
845+
}
846+
814847
impl ABIBody for AArch64ABIBody {
815848
type I = Inst;
816849

@@ -1025,6 +1058,29 @@ impl ABIBody for AArch64ABIBody {
10251058
store_stack(MemArg::NominalSPOffset(sp_off, ty), from_reg, ty)
10261059
}
10271060

1061+
fn spillslots_to_stackmap(&self, slots: &[SpillSlot], state: &EmitState) -> Stackmap {
1062+
assert!(state.virtual_sp_offset >= 0);
1063+
trace!(
1064+
"spillslots_to_stackmap: slots = {:?}, state = {:?}",
1065+
slots,
1066+
state
1067+
);
1068+
let map_size = (state.virtual_sp_offset + state.nominal_sp_to_fp) as u32;
1069+
let map_words = (map_size + 7) / 8;
1070+
let mut bits = std::iter::repeat(false)
1071+
.take(map_words as usize)
1072+
.collect::<Vec<bool>>();
1073+
1074+
let first_spillslot_word =
1075+
((self.stackslots_size + state.virtual_sp_offset as u32) / 8) as usize;
1076+
for &slot in slots {
1077+
let slot = slot.get() as usize;
1078+
bits[first_spillslot_word + slot] = true;
1079+
}
1080+
1081+
Stackmap::from_slice(&bits[..])
1082+
}
1083+
10281084
fn gen_prologue(&mut self) -> Vec<Inst> {
10291085
let mut insts = vec![];
10301086
if !self.call_conv.extends_baldrdash() {
@@ -1060,6 +1116,9 @@ impl ABIBody for AArch64ABIBody {
10601116
}
10611117
let total_stacksize = (total_stacksize + 15) & !15; // 16-align the stack.
10621118

1119+
let mut total_sp_adjust = 0;
1120+
let mut nominal_sp_to_real_sp = 0;
1121+
10631122
if !self.call_conv.extends_baldrdash() {
10641123
// Leaf functions with zero stack don't need a stack check if one's
10651124
// specified, otherwise always insert the stack check.
@@ -1070,42 +1129,28 @@ impl ABIBody for AArch64ABIBody {
10701129
}
10711130
}
10721131
if total_stacksize > 0 {
1073-
// sub sp, sp, #total_stacksize
1074-
if let Some(imm12) = Imm12::maybe_from_u64(total_stacksize as u64) {
1075-
let sub_inst = Inst::AluRRImm12 {
1076-
alu_op: ALUOp::Sub64,
1077-
rd: writable_stack_reg(),
1078-
rn: stack_reg(),
1079-
imm12,
1080-
};
1081-
insts.push(sub_inst);
1082-
} else {
1083-
let tmp = writable_spilltmp_reg();
1084-
let const_inst = Inst::LoadConst64 {
1085-
rd: tmp,
1086-
const_data: total_stacksize as u64,
1087-
};
1088-
let sub_inst = Inst::AluRRRExtend {
1089-
alu_op: ALUOp::Sub64,
1090-
rd: writable_stack_reg(),
1091-
rn: stack_reg(),
1092-
rm: tmp.to_reg(),
1093-
extendop: ExtendOp::UXTX,
1094-
};
1095-
insts.push(const_inst);
1096-
insts.push(sub_inst);
1097-
}
1132+
total_sp_adjust += total_stacksize as u64;
10981133
}
10991134
}
11001135

1101-
// N.B.: "nominal SP", which we use to refer to stackslots
1102-
// and spillslots, is *here* (the value of SP at this program point).
1136+
// N.B.: "nominal SP", which we use to refer to stackslots and
1137+
// spillslots, is right here.
1138+
//
11031139
// If we push any clobbers below, we emit a virtual-SP adjustment
11041140
// meta-instruction so that the nominal-SP references behave as if SP
11051141
// were still at this point. See documentation for
11061142
// [crate::isa::aarch64::abi](this module) for more details on
11071143
// stackframe layout and nominal-SP maintenance.
11081144

1145+
if total_sp_adjust > 0 {
1146+
// sub sp, sp, #total_stacksize
1147+
gen_sp_adjust_insts(
1148+
total_sp_adjust,
1149+
/* is_sub = */ true,
1150+
|inst| insts.push(inst),
1151+
);
1152+
}
1153+
11091154
// Save clobbered registers.
11101155
let (clobbered_int, clobbered_vec) =
11111156
get_callee_saves(self.call_conv, self.clobbered.to_vec());
@@ -1149,10 +1194,11 @@ impl ABIBody for AArch64ABIBody {
11491194
srcloc: None,
11501195
});
11511196
}
1197+
nominal_sp_to_real_sp += clobber_size as i64;
11521198

11531199
if clobber_size > 0 {
11541200
insts.push(Inst::VirtualSPOffsetAdj {
1155-
offset: clobber_size as i64,
1201+
offset: nominal_sp_to_real_sp,
11561202
});
11571203
}
11581204

@@ -1246,6 +1292,10 @@ impl ABIBody for AArch64ABIBody {
12461292
.expect("frame size not computed before prologue generation")
12471293
}
12481294

1295+
fn stack_args_size(&self) -> u32 {
1296+
self.sig.stack_arg_space as u32
1297+
}
1298+
12491299
fn get_spillslot_size(&self, rc: RegClass, ty: Type) -> u32 {
12501300
// We allocate in terms of 8-byte slots.
12511301
match (rc, ty) {
@@ -1256,15 +1306,30 @@ impl ABIBody for AArch64ABIBody {
12561306
}
12571307
}
12581308

1259-
fn gen_spill(&self, to_slot: SpillSlot, from_reg: RealReg, ty: Type) -> Inst {
1309+
fn gen_spill(&self, to_slot: SpillSlot, from_reg: RealReg, ty: Option<Type>) -> Inst {
1310+
let ty = ty_from_ty_hint_or_reg_class(from_reg.to_reg(), ty);
12601311
self.store_spillslot(to_slot, ty, from_reg.to_reg())
12611312
}
12621313

1263-
fn gen_reload(&self, to_reg: Writable<RealReg>, from_slot: SpillSlot, ty: Type) -> Inst {
1314+
fn gen_reload(
1315+
&self,
1316+
to_reg: Writable<RealReg>,
1317+
from_slot: SpillSlot,
1318+
ty: Option<Type>,
1319+
) -> Inst {
1320+
let ty = ty_from_ty_hint_or_reg_class(to_reg.to_reg().to_reg(), ty);
12641321
self.load_spillslot(from_slot, ty, to_reg.map(|r| r.to_reg()))
12651322
}
12661323
}
12671324

1325+
fn ty_from_ty_hint_or_reg_class(r: Reg, ty: Option<Type>) -> Type {
1326+
match (ty, r.get_class()) {
1327+
(Some(t), _) => t,
1328+
(None, RegClass::I64) => I64,
1329+
_ => panic!("Unexpected register class!"),
1330+
}
1331+
}
1332+
12681333
enum CallDest {
12691334
ExtName(ir::ExternalName, RelocDistance),
12701335
Reg(Reg),
@@ -1343,7 +1408,7 @@ impl AArch64ABICall {
13431408
}
13441409
}
13451410

1346-
fn adjust_stack<C: LowerCtx<I = Inst>>(ctx: &mut C, amount: u64, is_sub: bool) {
1411+
fn adjust_stack_and_nominal_sp<C: LowerCtx<I = Inst>>(ctx: &mut C, amount: u64, is_sub: bool) {
13471412
if amount == 0 {
13481413
return;
13491414
}
@@ -1357,27 +1422,9 @@ fn adjust_stack<C: LowerCtx<I = Inst>>(ctx: &mut C, amount: u64, is_sub: bool) {
13571422
offset: sp_adjustment,
13581423
});
13591424

1360-
let alu_op = if is_sub { ALUOp::Sub64 } else { ALUOp::Add64 };
1361-
if let Some(imm12) = Imm12::maybe_from_u64(amount) {
1362-
ctx.emit(Inst::AluRRImm12 {
1363-
alu_op,
1364-
rd: writable_stack_reg(),
1365-
rn: stack_reg(),
1366-
imm12,
1367-
})
1368-
} else {
1369-
ctx.emit(Inst::LoadConst64 {
1370-
rd: writable_spilltmp_reg(),
1371-
const_data: amount,
1372-
});
1373-
ctx.emit(Inst::AluRRRExtend {
1374-
alu_op,
1375-
rd: writable_stack_reg(),
1376-
rn: stack_reg(),
1377-
rm: spilltmp_reg(),
1378-
extendop: ExtendOp::UXTX,
1379-
});
1380-
}
1425+
gen_sp_adjust_insts(amount, is_sub, |inst| {
1426+
ctx.emit(inst);
1427+
});
13811428
}
13821429

13831430
impl ABICall for AArch64ABICall {
@@ -1393,12 +1440,12 @@ impl ABICall for AArch64ABICall {
13931440

13941441
fn emit_stack_pre_adjust<C: LowerCtx<I = Self::I>>(&self, ctx: &mut C) {
13951442
let off = self.sig.stack_arg_space + self.sig.stack_ret_space;
1396-
adjust_stack(ctx, off as u64, /* is_sub = */ true)
1443+
adjust_stack_and_nominal_sp(ctx, off as u64, /* is_sub = */ true)
13971444
}
13981445

13991446
fn emit_stack_post_adjust<C: LowerCtx<I = Self::I>>(&self, ctx: &mut C) {
14001447
let off = self.sig.stack_arg_space + self.sig.stack_ret_space;
1401-
adjust_stack(ctx, off as u64, /* is_sub = */ false)
1448+
adjust_stack_and_nominal_sp(ctx, off as u64, /* is_sub = */ false)
14021449
}
14031450

14041451
fn emit_copy_reg_to_arg<C: LowerCtx<I = Self::I>>(
@@ -1453,7 +1500,7 @@ impl ABICall for AArch64ABICall {
14531500
self.emit_copy_reg_to_arg(ctx, i, rd.to_reg());
14541501
}
14551502
match &self.dest {
1456-
&CallDest::ExtName(ref name, RelocDistance::Near) => ctx.emit(Inst::Call {
1503+
&CallDest::ExtName(ref name, RelocDistance::Near) => ctx.emit_safepoint(Inst::Call {
14571504
info: Box::new(CallInfo {
14581505
dest: name.clone(),
14591506
uses,
@@ -1469,7 +1516,7 @@ impl ABICall for AArch64ABICall {
14691516
offset: 0,
14701517
srcloc: self.loc,
14711518
});
1472-
ctx.emit(Inst::CallInd {
1519+
ctx.emit_safepoint(Inst::CallInd {
14731520
info: Box::new(CallIndInfo {
14741521
rn: spilltmp_reg(),
14751522
uses,
@@ -1479,7 +1526,7 @@ impl ABICall for AArch64ABICall {
14791526
}),
14801527
});
14811528
}
1482-
&CallDest::Reg(reg) => ctx.emit(Inst::CallInd {
1529+
&CallDest::Reg(reg) => ctx.emit_safepoint(Inst::CallInd {
14831530
info: Box::new(CallIndInfo {
14841531
rn: reg,
14851532
uses,

0 commit comments

Comments
 (0)