Skip to content

Commit fad2aff

Browse files
authored
Merge pull request #1852 from cfallin/reftypes
Reference type support in MachInst backend and on AArch64
2 parents 1d09c8e + 2652900 commit fad2aff

File tree

19 files changed

+717
-154
lines changed

19 files changed

+717
-154
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: 125 additions & 63 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>>,
@@ -400,6 +404,8 @@ fn in_int_reg(ty: ir::Type) -> bool {
400404
match ty {
401405
types::I8 | types::I16 | types::I32 | types::I64 => true,
402406
types::B1 | types::B8 | types::B16 | types::B32 | types::B64 => true,
407+
types::R64 => true,
408+
types::R32 => panic!("Unexpected 32-bit reference on a 64-bit platform!"),
403409
_ => false,
404410
}
405411
}
@@ -653,12 +659,12 @@ fn load_stack(mem: MemArg, into_reg: Writable<Reg>, ty: Type) -> Inst {
653659
mem,
654660
srcloc: None,
655661
},
656-
types::B32 | types::I32 => Inst::ULoad32 {
662+
types::B32 | types::I32 | types::R32 => Inst::ULoad32 {
657663
rd: into_reg,
658664
mem,
659665
srcloc: None,
660666
},
661-
types::B64 | types::I64 => Inst::ULoad64 {
667+
types::B64 | types::I64 | types::R64 => Inst::ULoad64 {
662668
rd: into_reg,
663669
mem,
664670
srcloc: None,
@@ -689,12 +695,12 @@ fn store_stack(mem: MemArg, from_reg: Reg, ty: Type) -> Inst {
689695
mem,
690696
srcloc: None,
691697
},
692-
types::B32 | types::I32 => Inst::Store32 {
698+
types::B32 | types::I32 | types::R32 => Inst::Store32 {
693699
rd: from_reg,
694700
mem,
695701
srcloc: None,
696702
},
697-
types::B64 | types::I64 => Inst::Store64 {
703+
types::B64 | types::I64 | types::R64 => Inst::Store64 {
698704
rd: from_reg,
699705
mem,
700706
srcloc: None,
@@ -810,6 +816,35 @@ fn get_caller_saves(call_conv: isa::CallConv) -> Vec<Writable<Reg>> {
810816
caller_saved
811817
}
812818

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

@@ -1024,6 +1059,29 @@ impl ABIBody for AArch64ABIBody {
10241059
store_stack(MemArg::NominalSPOffset(sp_off, ty), from_reg, ty)
10251060
}
10261061

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

1120+
let mut total_sp_adjust = 0;
1121+
let mut nominal_sp_to_real_sp = 0;
1122+
10621123
if !self.call_conv.extends_baldrdash() {
10631124
// Leaf functions with zero stack don't need a stack check if one's
10641125
// specified, otherwise always insert the stack check.
@@ -1069,42 +1130,29 @@ impl ABIBody for AArch64ABIBody {
10691130
}
10701131
}
10711132
if total_stacksize > 0 {
1072-
// sub sp, sp, #total_stacksize
1073-
if let Some(imm12) = Imm12::maybe_from_u64(total_stacksize as u64) {
1074-
let sub_inst = Inst::AluRRImm12 {
1075-
alu_op: ALUOp::Sub64,
1076-
rd: writable_stack_reg(),
1077-
rn: stack_reg(),
1078-
imm12,
1079-
};
1080-
insts.push(sub_inst);
1081-
} else {
1082-
let tmp = writable_spilltmp_reg();
1083-
let const_inst = Inst::LoadConst64 {
1084-
rd: tmp,
1085-
const_data: total_stacksize as u64,
1086-
};
1087-
let sub_inst = Inst::AluRRRExtend {
1088-
alu_op: ALUOp::Sub64,
1089-
rd: writable_stack_reg(),
1090-
rn: stack_reg(),
1091-
rm: tmp.to_reg(),
1092-
extendop: ExtendOp::UXTX,
1093-
};
1094-
insts.push(const_inst);
1095-
insts.push(sub_inst);
1096-
}
1133+
total_sp_adjust += total_stacksize as u64;
10971134
}
10981135
}
10991136

1100-
// N.B.: "nominal SP", which we use to refer to stackslots
1101-
// and spillslots, is *here* (the value of SP at this program point).
1137+
// N.B.: "nominal SP", which we use to refer to stackslots and
1138+
// spillslots, is defined to be equal to the stack pointer at this point
1139+
// in the prologue.
1140+
//
11021141
// If we push any clobbers below, we emit a virtual-SP adjustment
11031142
// meta-instruction so that the nominal-SP references behave as if SP
11041143
// were still at this point. See documentation for
11051144
// [crate::isa::aarch64::abi](this module) for more details on
11061145
// stackframe layout and nominal-SP maintenance.
11071146

1147+
if total_sp_adjust > 0 {
1148+
// sub sp, sp, #total_stacksize
1149+
gen_sp_adjust_insts(
1150+
total_sp_adjust,
1151+
/* is_sub = */ true,
1152+
|inst| insts.push(inst),
1153+
);
1154+
}
1155+
11081156
// Save clobbered registers.
11091157
let (clobbered_int, clobbered_vec) =
11101158
get_callee_saves(self.call_conv, self.clobbered.to_vec());
@@ -1148,10 +1196,11 @@ impl ABIBody for AArch64ABIBody {
11481196
srcloc: None,
11491197
});
11501198
}
1199+
nominal_sp_to_real_sp += clobber_size as i64;
11511200

11521201
if clobber_size > 0 {
11531202
insts.push(Inst::VirtualSPOffsetAdj {
1154-
offset: clobber_size as i64,
1203+
offset: nominal_sp_to_real_sp,
11551204
});
11561205
}
11571206

@@ -1245,6 +1294,10 @@ impl ABIBody for AArch64ABIBody {
12451294
.expect("frame size not computed before prologue generation")
12461295
}
12471296

1297+
fn stack_args_size(&self) -> u32 {
1298+
self.sig.stack_arg_space as u32
1299+
}
1300+
12481301
fn get_spillslot_size(&self, rc: RegClass, ty: Type) -> u32 {
12491302
// We allocate in terms of 8-byte slots.
12501303
match (rc, ty) {
@@ -1255,15 +1308,42 @@ impl ABIBody for AArch64ABIBody {
12551308
}
12561309
}
12571310

1258-
fn gen_spill(&self, to_slot: SpillSlot, from_reg: RealReg, ty: Type) -> Inst {
1311+
fn gen_spill(&self, to_slot: SpillSlot, from_reg: RealReg, ty: Option<Type>) -> Inst {
1312+
let ty = ty_from_ty_hint_or_reg_class(from_reg.to_reg(), ty);
12591313
self.store_spillslot(to_slot, ty, from_reg.to_reg())
12601314
}
12611315

1262-
fn gen_reload(&self, to_reg: Writable<RealReg>, from_slot: SpillSlot, ty: Type) -> Inst {
1316+
fn gen_reload(
1317+
&self,
1318+
to_reg: Writable<RealReg>,
1319+
from_slot: SpillSlot,
1320+
ty: Option<Type>,
1321+
) -> Inst {
1322+
let ty = ty_from_ty_hint_or_reg_class(to_reg.to_reg().to_reg(), ty);
12631323
self.load_spillslot(from_slot, ty, to_reg.map(|r| r.to_reg()))
12641324
}
12651325
}
12661326

1327+
/// Return a type either from an optional type hint, or if not, from the default
1328+
/// type associated with the given register's class. This is used to generate
1329+
/// loads/spills appropriately given the type of value loaded/stored (which may
1330+
/// be narrower than the spillslot). We usually have the type because the
1331+
/// regalloc usually provides the vreg being spilled/reloaded, and we know every
1332+
/// vreg's type. However, the regalloc *can* request a spill/reload without an
1333+
/// associated vreg when needed to satisfy a safepoint (which requires all
1334+
/// ref-typed values, even those in real registers in the original vcode, to be
1335+
/// in spillslots).
1336+
fn ty_from_ty_hint_or_reg_class(r: Reg, ty: Option<Type>) -> Type {
1337+
match (ty, r.get_class()) {
1338+
// If the type is provided
1339+
(Some(t), _) => t,
1340+
// If no type is provided, this should be a register spill for a
1341+
// safepoint, so we only expect I64 (integer) registers.
1342+
(None, RegClass::I64) => I64,
1343+
_ => panic!("Unexpected register class!"),
1344+
}
1345+
}
1346+
12671347
enum CallDest {
12681348
ExtName(ir::ExternalName, RelocDistance),
12691349
Reg(Reg),
@@ -1342,7 +1422,7 @@ impl AArch64ABICall {
13421422
}
13431423
}
13441424

1345-
fn adjust_stack<C: LowerCtx<I = Inst>>(ctx: &mut C, amount: u64, is_sub: bool) {
1425+
fn adjust_stack_and_nominal_sp<C: LowerCtx<I = Inst>>(ctx: &mut C, amount: u64, is_sub: bool) {
13461426
if amount == 0 {
13471427
return;
13481428
}
@@ -1356,27 +1436,9 @@ fn adjust_stack<C: LowerCtx<I = Inst>>(ctx: &mut C, amount: u64, is_sub: bool) {
13561436
offset: sp_adjustment,
13571437
});
13581438

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

13821444
impl ABICall for AArch64ABICall {
@@ -1392,12 +1454,12 @@ impl ABICall for AArch64ABICall {
13921454

13931455
fn emit_stack_pre_adjust<C: LowerCtx<I = Self::I>>(&self, ctx: &mut C) {
13941456
let off = self.sig.stack_arg_space + self.sig.stack_ret_space;
1395-
adjust_stack(ctx, off as u64, /* is_sub = */ true)
1457+
adjust_stack_and_nominal_sp(ctx, off as u64, /* is_sub = */ true)
13961458
}
13971459

13981460
fn emit_stack_post_adjust<C: LowerCtx<I = Self::I>>(&self, ctx: &mut C) {
13991461
let off = self.sig.stack_arg_space + self.sig.stack_ret_space;
1400-
adjust_stack(ctx, off as u64, /* is_sub = */ false)
1462+
adjust_stack_and_nominal_sp(ctx, off as u64, /* is_sub = */ false)
14011463
}
14021464

14031465
fn emit_copy_reg_to_arg<C: LowerCtx<I = Self::I>>(
@@ -1452,7 +1514,7 @@ impl ABICall for AArch64ABICall {
14521514
self.emit_copy_reg_to_arg(ctx, i, rd.to_reg());
14531515
}
14541516
match &self.dest {
1455-
&CallDest::ExtName(ref name, RelocDistance::Near) => ctx.emit(Inst::Call {
1517+
&CallDest::ExtName(ref name, RelocDistance::Near) => ctx.emit_safepoint(Inst::Call {
14561518
info: Box::new(CallInfo {
14571519
dest: name.clone(),
14581520
uses,
@@ -1468,7 +1530,7 @@ impl ABICall for AArch64ABICall {
14681530
offset: 0,
14691531
srcloc: self.loc,
14701532
});
1471-
ctx.emit(Inst::CallInd {
1533+
ctx.emit_safepoint(Inst::CallInd {
14721534
info: Box::new(CallIndInfo {
14731535
rn: spilltmp_reg(),
14741536
uses,
@@ -1478,7 +1540,7 @@ impl ABICall for AArch64ABICall {
14781540
}),
14791541
});
14801542
}
1481-
&CallDest::Reg(reg) => ctx.emit(Inst::CallInd {
1543+
&CallDest::Reg(reg) => ctx.emit_safepoint(Inst::CallInd {
14821544
info: Box::new(CallIndInfo {
14831545
rn: reg,
14841546
uses,

0 commit comments

Comments
 (0)