Skip to content

Commit 87ed3b6

Browse files
authored
Cranelift: make all non-tail, non-indirect calls patchable, and rename patchable ABI to preserve_all. (#12160)
* Cranelift: make all non-tail, non-indirect calls patchable, and rename patchable ABI to `preserve_all`. As discussed in this week's Cranelift meeting, we've discovered a need to generalize the `patchable_call` mechanism and corresponding `patchable` ABI slightly. In particular, we will need patchable `try_call` callsites as well in order to allow breakpoint handlers to throw exceptions (desirable functionality eventually) and have this work in the presence of inlining. Also, it's just a nice generalization to say that patchability is an orthogonal dimension to the call ABI and the other restrictions we initially imposed, and works as long as the basic requirement (no return values) is met. This also renames the `patchable` ABI to `preserve_all`, to make it clear that its purpose is actually orthogonal, and it can be used independently of patchable callsites. It also deletes the `cold` ABI, which never actually did anything and is misleading in the presence of an actual cold-ish (subzero temperature, actually) ABI like `preserve_all`. * Review feedback.
1 parent ab03078 commit 87ed3b6

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

76 files changed

+1581
-639
lines changed

cranelift/codegen/meta/src/shared/instructions.rs

Lines changed: 0 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -3918,31 +3918,4 @@ pub(crate) fn define(
39183918
)
39193919
.other_side_effects(),
39203920
);
3921-
3922-
ig.push(
3923-
Inst::new(
3924-
"patchable_call",
3925-
r#"
3926-
A call instruction that can be turned on/off by patching machine code.
3927-
3928-
This call is different than an ordinary call in a few ways:
3929-
- It can only call functions with the `patchable` ABI.
3930-
- As a result of that, the called function cannot have any
3931-
return values.
3932-
- The call emits metadata into the MachBuffer that allows the consumer
3933-
of the machine code to edit the code to either make the call or not.
3934-
3935-
Note that the lack of return values is necessary: because the call
3936-
may or may not occur, dynamically, we cannot rely on any values
3937-
actually being defined.
3938-
"#,
3939-
&formats.call,
3940-
)
3941-
.operands_in(vec![
3942-
Operand::new("FN", &entities.func_ref)
3943-
.with_doc("function to call, declared by `function`"),
3944-
Operand::new("args", &entities.varargs).with_doc("call arguments"),
3945-
])
3946-
.call(),
3947-
);
39483921
}

cranelift/codegen/meta/src/shared/settings.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,11 +169,11 @@ pub(crate) fn define() -> SettingGroup {
169169
vec![
170170
"isa_default",
171171
"fast",
172-
"cold",
173172
"system_v",
174173
"windows_fastcall",
175174
"apple_aarch64",
176175
"probestack",
176+
"preserve_all",
177177
],
178178
);
179179

cranelift/codegen/src/inline.rs

Lines changed: 11 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,14 @@ pub(crate) fn do_inlining(
139139
debug_assert_eq!(Some(block), cursor.func.layout.inst_block(inst));
140140

141141
match cursor.func.dfg.insts[inst] {
142+
ir::InstructionData::Call { func_ref, .. }
143+
if cursor.func.dfg.ext_funcs[func_ref].patchable =>
144+
{
145+
// Can't inline patchable calls; they need to
146+
// remain patchable and inlining the whole body is
147+
// decidedly *not* patchable!
148+
}
149+
142150
ir::InstructionData::Call {
143151
opcode: opcode @ ir::Opcode::Call | opcode @ ir::Opcode::ReturnCall,
144152
args: _,
@@ -227,14 +235,6 @@ pub(crate) fn do_inlining(
227235
// Can't inline indirect calls; need to have some earlier
228236
// pass rewrite them into direct calls first, when possible.
229237
}
230-
ir::InstructionData::Call {
231-
opcode: ir::Opcode::PatchableCall,
232-
..
233-
} => {
234-
// Can't inline patchable calls; they need to
235-
// remain patchable and inlining the whole body is
236-
// decidedly *not* patchable!
237-
}
238238
_ => {
239239
debug_assert!(
240240
!cursor.func.dfg.insts[inst].opcode().is_call(),
@@ -505,19 +505,7 @@ fn inline_one(
505505
call_opcode == ir::Opcode::TryCall,
506506
call_exception_table.is_some()
507507
);
508-
// Note that we do not fix up patchable calls
509-
// inlined at a try-call to a try-call, because
510-
// the "patchable ABI" does not support catching
511-
// exceptions. This does mean that we cannot have
512-
// an exception-throw propagate out of a
513-
// breakpoint when we use patchable calls to set
514-
// up breakpoints, but we don't expect that to
515-
// occur.
516-
//
517-
// FIXME: consider making patchability an aspect
518-
// of any call; then we can remove this special
519-
// case.
520-
if call_opcode == ir::Opcode::TryCall && opcode != ir::Opcode::PatchableCall {
508+
if call_opcode == ir::Opcode::TryCall {
521509
allocs
522510
.calls_needing_exception_table_fixup
523511
.push(inlined_inst);
@@ -1491,6 +1479,7 @@ fn create_func_refs(
14911479
name,
14921480
signature,
14931481
colocated,
1482+
patchable,
14941483
} in callee.dfg.ext_funcs.values()
14951484
{
14961485
func.dfg.ext_funcs.push(ir::ExtFuncData {
@@ -1507,6 +1496,7 @@ fn create_func_refs(
15071496
},
15081497
signature: entity_map.inlined_sig_ref(*signature),
15091498
colocated: *colocated,
1499+
patchable: *patchable,
15101500
});
15111501
}
15121502

cranelift/codegen/src/inst_predicates.rs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -149,11 +149,7 @@ pub fn has_memory_fence_semantics(op: Opcode) -> bool {
149149
| Opcode::Fence
150150
| Opcode::Debugtrap
151151
| Opcode::SequencePoint => true,
152-
Opcode::Call
153-
| Opcode::CallIndirect
154-
| Opcode::TryCall
155-
| Opcode::TryCallIndirect
156-
| Opcode::PatchableCall => true,
152+
Opcode::Call | Opcode::CallIndirect | Opcode::TryCall | Opcode::TryCallIndirect => true,
157153
op if op.can_trap() => true,
158154
_ => false,
159155
}

cranelift/codegen/src/ir/extfunc.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,14 @@ pub struct ExtFuncData {
313313
/// See the documentation for `RelocDistance` for more details. A `colocated` flag value of
314314
/// `true` implies `RelocDistance::Near`.
315315
pub colocated: bool,
316+
/// Is this function "patchable"? If so, calls to this function will
317+
/// emit additional metadata indicating how to patch them in or out.
318+
///
319+
/// Calls to functions of any calling convention may be patchable,
320+
/// *but* only calls with no return values are patchable. This is
321+
/// because every SSA value must always be defined, and return
322+
/// values would not be if the call were patched out.
323+
pub patchable: bool,
316324
}
317325

318326
impl ExtFuncData {
@@ -340,6 +348,9 @@ impl<'a> fmt::Display for DisplayableExtFuncData<'a> {
340348
if self.ext_func.colocated {
341349
write!(f, "colocated ")?;
342350
}
351+
if self.ext_func.patchable {
352+
write!(f, "patchable ")?;
353+
}
343354
write!(
344355
f,
345356
"{} {}",
@@ -384,7 +395,8 @@ mod tests {
384395
fn call_conv() {
385396
for &cc in &[
386397
CallConv::Fast,
387-
CallConv::Cold,
398+
CallConv::PreserveAll,
399+
CallConv::Tail,
388400
CallConv::SystemV,
389401
CallConv::WindowsFastcall,
390402
] {

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

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1067,6 +1067,7 @@ impl ABIMachineSpec for AArch64MachineDeps {
10671067
callee_conv: call_conv,
10681068
callee_pop_size: 0,
10691069
try_call_info: None,
1070+
patchable: false,
10701071
}),
10711072
});
10721073
insts
@@ -1101,8 +1102,14 @@ impl ABIMachineSpec for AArch64MachineDeps {
11011102
(isa::CallConv::Tail, true) => ALL_CLOBBERS,
11021103
(isa::CallConv::Winch, true) => ALL_CLOBBERS,
11031104
(isa::CallConv::Winch, false) => WINCH_CLOBBERS,
1105+
// Note that "PreserveAll" actually preserves nothing at
1106+
// the callsite if used for a `try_call`, because the
1107+
// unwinder ABI for `try_call`s is still "no clobbered
1108+
// register restores" for this ABI (so as to work with
1109+
// Wasmtime).
1110+
(isa::CallConv::PreserveAll, true) => ALL_CLOBBERS,
11041111
(isa::CallConv::SystemV, _) => DEFAULT_AAPCS_CLOBBERS,
1105-
(isa::CallConv::Patchable, _) => NO_CLOBBERS,
1112+
(isa::CallConv::PreserveAll, _) => NO_CLOBBERS,
11061113
(_, false) => DEFAULT_AAPCS_CLOBBERS,
11071114
(_, true) => panic!("unimplemented clobbers for exn abi of {call_conv:?}"),
11081115
}
@@ -1184,7 +1191,9 @@ impl ABIMachineSpec for AArch64MachineDeps {
11841191
fn exception_payload_regs(call_conv: isa::CallConv) -> &'static [Reg] {
11851192
const PAYLOAD_REGS: &'static [Reg] = &[regs::xreg(0), regs::xreg(1)];
11861193
match call_conv {
1187-
isa::CallConv::SystemV | isa::CallConv::Tail => PAYLOAD_REGS,
1194+
isa::CallConv::SystemV | isa::CallConv::Tail | isa::CallConv::PreserveAll => {
1195+
PAYLOAD_REGS
1196+
}
11881197
_ => &[],
11891198
}
11901199
}
@@ -1271,7 +1280,7 @@ fn is_reg_saved_in_prologue(
12711280
sig: &Signature,
12721281
r: RealReg,
12731282
) -> bool {
1274-
if call_conv == isa::CallConv::Patchable {
1283+
if call_conv == isa::CallConv::PreserveAll {
12751284
return true;
12761285
}
12771286

cranelift/codegen/src/isa/aarch64/inst.isle

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -817,9 +817,6 @@
817817
;; An indirect return-call macro instruction.
818818
(ReturnCallInd (info BoxReturnCallIndInfo))
819819

820-
;; A patchable call instruction.
821-
(PatchableCall (info BoxCallInfo))
822-
823820
;; A pseudo-instruction that captures register arguments in vregs.
824821
(Args
825822
(args VecArgPair))
@@ -4481,7 +4478,7 @@
44814478

44824479
;;;; Helpers for Emitting Calls ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
44834480

4484-
(decl gen_call_info (Sig ExternalName CallArgList CallRetList OptionTryCallInfo) BoxCallInfo)
4481+
(decl gen_call_info (Sig ExternalName CallArgList CallRetList OptionTryCallInfo bool) BoxCallInfo)
44854482
(extern constructor gen_call_info gen_call_info)
44864483

44874484
(decl gen_call_ind_info (Sig Reg CallArgList CallRetList OptionTryCallInfo) BoxCallIndInfo)
@@ -4513,11 +4510,6 @@
45134510
(rule (return_call_ind_impl info)
45144511
(SideEffectNoResult.Inst (MInst.ReturnCallInd info)))
45154512

4516-
;; Helper for creating `MInst.PatchableCall` instructions.
4517-
(decl patchable_call_impl (BoxCallInfo) SideEffectNoResult)
4518-
(rule (patchable_call_impl info)
4519-
(SideEffectNoResult.Inst (MInst.PatchableCall info)))
4520-
45214513
;; Helpers for pinned register manipulation.
45224514

45234515
(decl write_pinned_reg (Reg) SideEffectNoResult)

cranelift/codegen/src/isa/aarch64/inst/emit.rs

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2941,8 +2941,8 @@ impl MachInstEmit for Inst {
29412941
sink.put4(0xd65f0bff | (op2 << 9)); // reta{key}
29422942
}
29432943
}
2944-
&Inst::Call { ref info } | &Inst::PatchableCall { ref info } => {
2945-
let is_patchable = matches!(self, Inst::PatchableCall { .. });
2944+
&Inst::Call { ref info } => {
2945+
let start = sink.cur_offset();
29462946
let user_stack_map = state.take_stack_map();
29472947
sink.add_reloc(Reloc::Arm64Call, &info.dest, 0);
29482948
sink.put4(enc_jump26(0b100101, 0));
@@ -2956,8 +2956,6 @@ impl MachInstEmit for Inst {
29562956
Some(state.frame_layout.sp_to_fp()),
29572957
try_call.exception_handlers(&state.frame_layout),
29582958
);
2959-
} else if is_patchable {
2960-
sink.add_patchable_call_site(4);
29612959
} else {
29622960
sink.add_call_site();
29632961
}
@@ -2970,12 +2968,16 @@ impl MachInstEmit for Inst {
29702968
}
29712969
}
29722970

2973-
// Load any stack-carried return values.
2974-
info.emit_retval_loads::<AArch64MachineDeps, _, _>(
2975-
state.frame_layout().stackslots_size,
2976-
|inst| inst.emit(sink, emit_info, state),
2977-
|needed_space| Some(Inst::EmitIsland { needed_space }),
2978-
);
2971+
if info.patchable {
2972+
sink.add_patchable_call_site(sink.cur_offset() - start);
2973+
} else {
2974+
// Load any stack-carried return values.
2975+
info.emit_retval_loads::<AArch64MachineDeps, _, _>(
2976+
state.frame_layout().stackslots_size,
2977+
|inst| inst.emit(sink, emit_info, state),
2978+
|needed_space| Some(Inst::EmitIsland { needed_space }),
2979+
);
2980+
}
29792981

29802982
// If this is a try-call, jump to the continuation
29812983
// (normal-return) block.

cranelift/codegen/src/isa/aarch64/inst/mod.rs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -801,7 +801,7 @@ fn aarch64_get_operands(inst: &mut Inst, collector: &mut impl OperandVisitor) {
801801
}
802802
Inst::Ret { .. } | Inst::AuthenticatedRet { .. } => {}
803803
Inst::Jump { .. } => {}
804-
Inst::Call { info, .. } | Inst::PatchableCall { info, .. } => {
804+
Inst::Call { info, .. } => {
805805
let CallInfo { uses, defs, .. } = &mut **info;
806806
for CallArgPair { vreg, preg } in uses {
807807
collector.reg_fixed_use(vreg, *preg);
@@ -1008,7 +1008,6 @@ impl MachInst for Inst {
10081008
match self {
10091009
Inst::Call { .. }
10101010
| Inst::CallInd { .. }
1011-
| Inst::PatchableCall { .. }
10121011
| Inst::ElfTlsGetAddr { .. }
10131012
| Inst::MachOTlsGetAddr { .. } => CallType::Regular,
10141013

@@ -1093,7 +1092,7 @@ impl MachInst for Inst {
10931092

10941093
fn is_safepoint(&self) -> bool {
10951094
match self {
1096-
Inst::Call { .. } | Inst::CallInd { .. } | Inst::PatchableCall { .. } => true,
1095+
Inst::Call { .. } | Inst::CallInd { .. } => true,
10971096
_ => false,
10981097
}
10991098
}
@@ -2599,9 +2598,6 @@ impl Inst {
25992598
.unwrap_or_default();
26002599
format!("blr {rn}{try_call}")
26012600
}
2602-
&Inst::PatchableCall { .. } => {
2603-
format!("bl 0 ; patchable")
2604-
}
26052601
&Inst::ReturnCall { ref info } => {
26062602
let mut s = format!(
26072603
"return_call {:?} new_stack_arg_size:{}",

cranelift/codegen/src/isa/aarch64/lower.isle

Lines changed: 9 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2520,7 +2520,7 @@
25202520

25212521
;;;; Rules for `func_addr` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
25222522

2523-
(rule (lower (func_addr (func_ref_data _ extname dist)))
2523+
(rule (lower (func_addr (func_ref_data _ extname dist _)))
25242524
(load_ext_name (box_external_name extname) 0 dist))
25252525

25262526
;;;; Rules for `symbol_value` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -2542,17 +2542,17 @@
25422542
;; Rules for `call` and `call_indirect` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
25432543

25442544
;; Direct call to an in-range function.
2545-
(rule 1 (lower (call (func_ref_data sig_ref name (RelocDistance.Near)) args))
2545+
(rule 1 (lower (call (func_ref_data sig_ref name (RelocDistance.Near) patchable) args))
25462546
(let ((output ValueRegsVec (gen_call_output sig_ref))
25472547
(abi Sig (abi_sig sig_ref))
25482548
(uses CallArgList (gen_call_args abi args))
25492549
(defs CallRetList (gen_call_rets abi output))
2550-
(info BoxCallInfo (gen_call_info abi name uses defs (try_call_none)))
2550+
(info BoxCallInfo (gen_call_info abi name uses defs (try_call_none) patchable))
25512551
(_ Unit (emit_side_effect (call_impl info))))
25522552
output))
25532553

25542554
;; Direct call to an out-of-range function (implicitly via pointer).
2555-
(rule (lower (call (func_ref_data sig_ref name dist) args))
2555+
(rule (lower (call (func_ref_data sig_ref name dist false) args))
25562556
(let ((output ValueRegsVec (gen_call_output sig_ref))
25572557
(abi Sig (abi_sig sig_ref))
25582558
(uses CallArgList (gen_call_args abi args))
@@ -2573,30 +2573,19 @@
25732573
(_ Unit (emit_side_effect (call_ind_impl info))))
25742574
output))
25752575

2576-
;;;; Rules for `patchable_call` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2577-
2578-
;; Direct call to an in-range function.
2579-
(rule (lower (patchable_call (func_ref_data sig_ref name (RelocDistance.Near)) args))
2580-
(let ((abi Sig (abi_sig sig_ref))
2581-
(uses CallArgList (gen_call_args abi args))
2582-
(defs CallRetList (gen_patchable_call_rets))
2583-
(info BoxCallInfo (gen_call_info abi name uses defs (try_call_none)))
2584-
(_ Unit (emit_side_effect (patchable_call_impl info))))
2585-
(output_none)))
2586-
25872576
;;;; Rules for `try_call` and `try_call_indirect` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
25882577

25892578
;; Direct call to an in-range function.
2590-
(rule 1 (lower_branch (try_call (func_ref_data sig_ref name (RelocDistance.Near)) args et) targets)
2579+
(rule 1 (lower_branch (try_call (func_ref_data sig_ref name (RelocDistance.Near) patchable) args et) targets)
25912580
(let ((abi Sig (abi_sig sig_ref))
25922581
(trycall OptionTryCallInfo (try_call_info et targets))
25932582
(uses CallArgList (gen_call_args abi args))
25942583
(defs CallRetList (gen_try_call_rets abi))
2595-
(info BoxCallInfo (gen_call_info abi name uses defs trycall)))
2584+
(info BoxCallInfo (gen_call_info abi name uses defs trycall patchable)))
25962585
(emit_side_effect (call_impl info))))
25972586

25982587
;; Direct call to an out-of-range function (implicitly via pointer).
2599-
(rule (lower_branch (try_call (func_ref_data sig_ref name dist) args et) targets)
2588+
(rule (lower_branch (try_call (func_ref_data sig_ref name dist false) args et) targets)
26002589
(let ((abi Sig (abi_sig sig_ref))
26012590
(trycall OptionTryCallInfo (try_call_info et targets))
26022591
(uses CallArgList (gen_call_args abi args))
@@ -2625,14 +2614,14 @@
26252614
;;;; Rules for `return_call` and `return_call_indirect` ;;;;;;;;;;;;;;;;;;;;;;;;
26262615

26272616
;; Direct call to an in-range function.
2628-
(rule 1 (lower (return_call (func_ref_data sig_ref name (RelocDistance.Near)) args))
2617+
(rule 1 (lower (return_call (func_ref_data sig_ref name (RelocDistance.Near) false) args))
26292618
(let ((abi Sig (abi_sig sig_ref))
26302619
(uses CallArgList (gen_return_call_args abi args))
26312620
(info BoxReturnCallInfo (gen_return_call_info abi name uses)))
26322621
(side_effect (return_call_impl info))))
26332622

26342623
;; Direct call to an out-of-range function (implicitly via pointer).
2635-
(rule (lower (return_call (func_ref_data sig_ref name dist) args))
2624+
(rule (lower (return_call (func_ref_data sig_ref name dist false) args))
26362625
(let ((abi Sig (abi_sig sig_ref))
26372626
(uses CallArgList (gen_return_call_args abi args))
26382627
(target Reg (load_ext_name name 0 dist))

0 commit comments

Comments
 (0)