Skip to content

Commit 7968af6

Browse files
authored
Compile wasm builtins with the tail convention, not host (#9640)
* Compile wasm builtins with the `tail` convention, not host This commit updates how Cranelift compiles the shims that go from WebAssembly to various libcall builtins (e.g. `memory.grow`). Previously the small trampolines in Cranelift were compiled with the native host calling convention and this commit instead changes them to the wasm calling convention (e.g. "tail"). This helps keep caller/callee matched with calling conventions in wasm code itself and shifts the boundary of the wasm->host transition to the indirect call of the host function pointer itself. The general hope is that this makes Pulley a bit easier since it'll want to minimize handling of wasm->host transitions. * Fix wasm calling convention on Winch * Handle different calling conventions in Winch too
1 parent 2e6b18f commit 7968af6

Some content is hidden

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

46 files changed

+111
-83
lines changed

crates/cranelift/src/compiler.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -516,10 +516,12 @@ impl wasmtime_environ::Compiler for Compiler {
516516
let isa = &*self.isa;
517517
let ptr_size = isa.pointer_bytes();
518518
let pointer_type = isa.pointer_type();
519-
let sig = BuiltinFunctionSignatures::new(isa).signature(index);
519+
let sigs = BuiltinFunctionSignatures::new(isa, &self.tunables);
520+
let wasm_sig = sigs.wasm_signature(index);
521+
let host_sig = sigs.host_signature(index);
520522

521523
let mut compiler = self.function_compiler();
522-
let func = ir::Function::with_name_signature(Default::default(), sig.clone());
524+
let func = ir::Function::with_name_signature(Default::default(), wasm_sig.clone());
523525
let (mut builder, block0) = compiler.builder(func);
524526
let vmctx = builder.block_params(block0)[0];
525527

@@ -554,8 +556,10 @@ impl wasmtime_environ::Compiler for Compiler {
554556
// Forward all our own arguments to the libcall itself, and then return
555557
// all the same results as the libcall.
556558
let block_params = builder.block_params(block0).to_vec();
557-
let sig = builder.func.import_signature(sig);
558-
let call = builder.ins().call_indirect(sig, func_addr, &block_params);
559+
let host_sig = builder.func.import_signature(host_sig);
560+
let call = builder
561+
.ins()
562+
.call_indirect(host_sig, func_addr, &block_params);
559563
let results = builder.func.dfg.inst_results(call).to_vec();
560564
builder.ins().return_(&results);
561565
builder.finalize();

crates/cranelift/src/func_environ.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,9 @@ pub(crate) struct BuiltinFunctions {
3737
}
3838

3939
impl BuiltinFunctions {
40-
fn new(isa: &dyn TargetIsa) -> Self {
40+
fn new(isa: &dyn TargetIsa, tunables: &Tunables) -> Self {
4141
Self {
42-
types: BuiltinFunctionSignatures::new(isa),
42+
types: BuiltinFunctionSignatures::new(isa, tunables),
4343
builtins: [None; BuiltinFunctionIndex::builtin_functions_total_number() as usize],
4444
}
4545
}
@@ -49,7 +49,7 @@ impl BuiltinFunctions {
4949
if let Some(f) = cache {
5050
return *f;
5151
}
52-
let signature = func.import_signature(self.types.signature(index));
52+
let signature = func.import_signature(self.types.wasm_signature(index));
5353
let name =
5454
ir::ExternalName::User(func.declare_imported_user_function(ir::UserExternalName {
5555
namespace: crate::NS_WASMTIME_BUILTIN,
@@ -169,7 +169,7 @@ impl<'module_environment> FuncEnvironment<'module_environment> {
169169
wmemcheck: bool,
170170
wasm_func_ty: &'module_environment WasmFuncType,
171171
) -> Self {
172-
let builtin_functions = BuiltinFunctions::new(isa);
172+
let builtin_functions = BuiltinFunctions::new(isa, tunables);
173173

174174
// Avoid unused warning in default build.
175175
#[cfg(not(feature = "wmemcheck"))]

crates/cranelift/src/lib.rs

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -154,12 +154,8 @@ fn array_call_signature(isa: &dyn TargetIsa) -> ir::Signature {
154154
sig
155155
}
156156

157-
/// Get the internal Wasm calling convention signature for the given type.
158-
fn wasm_call_signature(
159-
isa: &dyn TargetIsa,
160-
wasm_func_ty: &WasmFuncType,
161-
tunables: &Tunables,
162-
) -> ir::Signature {
157+
/// Get the internal Wasm calling convention for the target/tunables combo
158+
fn wasm_call_conv(isa: &dyn TargetIsa, tunables: &Tunables) -> CallConv {
163159
// The default calling convention is `CallConv::Tail` to enable the use of
164160
// tail calls in modules when needed. Note that this is used even if the
165161
// tail call proposal is disabled in wasm. This is not interacted with on
@@ -169,7 +165,7 @@ fn wasm_call_signature(
169165
// which call Winch-generated functions. The winch calling convention is
170166
// only implemented for x64 and aarch64, so assert that here and panic on
171167
// other architectures.
172-
let call_conv = if tunables.winch_callable {
168+
if tunables.winch_callable {
173169
assert!(
174170
matches!(
175171
isa.triple().architecture,
@@ -180,7 +176,16 @@ fn wasm_call_signature(
180176
CallConv::Winch
181177
} else {
182178
CallConv::Tail
183-
};
179+
}
180+
}
181+
182+
/// Get the internal Wasm calling convention signature for the given type.
183+
fn wasm_call_signature(
184+
isa: &dyn TargetIsa,
185+
wasm_func_ty: &WasmFuncType,
186+
tunables: &Tunables,
187+
) -> ir::Signature {
188+
let call_conv = wasm_call_conv(isa, tunables);
184189
let mut sig = blank_sig(isa, call_conv);
185190
let cvt = |ty: &WasmValType| ir::AbiParam::new(value_type(isa, *ty));
186191
sig.params.extend(wasm_func_ty.params().iter().map(&cvt));
@@ -327,14 +332,16 @@ struct BuiltinFunctionSignatures {
327332
#[cfg(feature = "gc")]
328333
reference_type: ir::Type,
329334

330-
call_conv: CallConv,
335+
host_call_conv: CallConv,
336+
wasm_call_conv: CallConv,
331337
}
332338

333339
impl BuiltinFunctionSignatures {
334-
fn new(isa: &dyn TargetIsa) -> Self {
340+
fn new(isa: &dyn TargetIsa, tunables: &Tunables) -> Self {
335341
Self {
336342
pointer_type: isa.pointer_type(),
337-
call_conv: CallConv::triple_default(isa.triple()),
343+
host_call_conv: CallConv::triple_default(isa.triple()),
344+
wasm_call_conv: wasm_call_conv(isa, tunables),
338345

339346
#[cfg(feature = "gc")]
340347
reference_type: ir::types::I32,
@@ -379,7 +386,7 @@ impl BuiltinFunctionSignatures {
379386
AbiParam::new(ir::types::I8)
380387
}
381388

382-
fn signature(&self, builtin: BuiltinFunctionIndex) -> Signature {
389+
fn wasm_signature(&self, builtin: BuiltinFunctionIndex) -> Signature {
383390
let mut _cur = 0;
384391
macro_rules! iter {
385392
(
@@ -394,7 +401,7 @@ impl BuiltinFunctionSignatures {
394401
return Signature {
395402
params: vec![ $( self.$param() ),* ],
396403
returns: vec![ $( self.$result() )? ],
397-
call_conv: self.call_conv,
404+
call_conv: self.wasm_call_conv,
398405
};
399406
}
400407
_cur += 1;
@@ -406,6 +413,12 @@ impl BuiltinFunctionSignatures {
406413

407414
unreachable!();
408415
}
416+
417+
fn host_signature(&self, builtin: BuiltinFunctionIndex) -> Signature {
418+
let mut sig = self.wasm_signature(builtin);
419+
sig.call_conv = self.host_call_conv;
420+
sig
421+
}
409422
}
410423

411424
/// If this bit is set on a GC reference, then the GC reference is actually an

crates/winch/src/compiler.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use wasmtime_environ::{
1111
FunctionLoc, ModuleTranslation, ModuleTypesBuilder, PrimaryMap, RelocationTarget,
1212
StaticModuleIndex, TrapEncodingBuilder, Tunables, VMOffsets, WasmFunctionInfo,
1313
};
14-
use winch_codegen::{BuiltinFunctions, TargetIsa};
14+
use winch_codegen::{BuiltinFunctions, CallingConvention, TargetIsa};
1515

1616
/// Function compilation context.
1717
/// This struct holds information that can be shared globally across
@@ -51,7 +51,11 @@ impl Compiler {
5151
let vmoffsets = VMOffsets::new(pointer_size, &translation.module);
5252
CompilationContext {
5353
allocations: Default::default(),
54-
builtins: BuiltinFunctions::new(&vmoffsets, self.isa.wasmtime_call_conv()),
54+
builtins: BuiltinFunctions::new(
55+
&vmoffsets,
56+
self.isa.wasmtime_call_conv(),
57+
CallingConvention::Default,
58+
),
5559
}
5660
})
5761
}

tests/disas/epoch-interruption.wat

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
;; gv1 = load.i64 notrap aligned readonly gv0+8
1010
;; gv2 = load.i64 notrap aligned gv1
1111
;; gv3 = vmctx
12-
;; sig0 = (i64 vmctx) -> i64 system_v
12+
;; sig0 = (i64 vmctx) -> i64 tail
1313
;; fn0 = colocated u1:16 sig0
1414
;; stack_limit = gv2
1515
;;

tests/disas/gc/drc/array-new-fixed.wat

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
;; gv1 = load.i64 notrap aligned readonly gv0+8
1515
;; gv2 = load.i64 notrap aligned gv1
1616
;; gv3 = vmctx
17-
;; sig0 = (i64 vmctx, i32 uext, i32 uext, i32 uext, i32 uext) -> i32 system_v
17+
;; sig0 = (i64 vmctx, i32 uext, i32 uext, i32 uext, i32 uext) -> i32 tail
1818
;; fn0 = colocated u1:27 sig0
1919
;; stack_limit = gv2
2020
;;

tests/disas/gc/drc/array-new.wat

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
;; gv1 = load.i64 notrap aligned readonly gv0+8
1515
;; gv2 = load.i64 notrap aligned gv1
1616
;; gv3 = vmctx
17-
;; sig0 = (i64 vmctx, i32 uext, i32 uext, i32 uext, i32 uext) -> i32 system_v
17+
;; sig0 = (i64 vmctx, i32 uext, i32 uext, i32 uext, i32 uext) -> i32 tail
1818
;; fn0 = colocated u1:27 sig0
1919
;; stack_limit = gv2
2020
;;

tests/disas/gc/drc/br-on-cast-fail.wat

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
;; gv1 = load.i64 notrap aligned readonly gv0+8
2323
;; gv2 = load.i64 notrap aligned gv1
2424
;; gv3 = vmctx
25-
;; sig0 = (i64 vmctx, i32 uext, i32 uext) -> i32 uext system_v
25+
;; sig0 = (i64 vmctx, i32 uext, i32 uext) -> i32 uext tail
2626
;; sig1 = (i64 vmctx, i64) tail
2727
;; sig2 = (i64 vmctx, i64) tail
2828
;; fn0 = colocated u1:35 sig0

tests/disas/gc/drc/br-on-cast.wat

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
;; gv1 = load.i64 notrap aligned readonly gv0+8
2323
;; gv2 = load.i64 notrap aligned gv1
2424
;; gv3 = vmctx
25-
;; sig0 = (i64 vmctx, i32 uext, i32 uext) -> i32 uext system_v
25+
;; sig0 = (i64 vmctx, i32 uext, i32 uext) -> i32 uext tail
2626
;; sig1 = (i64 vmctx, i64) tail
2727
;; sig2 = (i64 vmctx, i64) tail
2828
;; fn0 = colocated u1:35 sig0

tests/disas/gc/drc/call-indirect-and-subtyping.wat

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@
2323
;; gv3 = vmctx
2424
;; gv4 = load.i64 notrap aligned readonly gv3+136
2525
;; sig0 = (i64 vmctx, i64) tail
26-
;; sig1 = (i64 vmctx, i32 uext, i64) -> i64 system_v
27-
;; sig2 = (i64 vmctx, i32 uext, i32 uext) -> i32 uext system_v
26+
;; sig1 = (i64 vmctx, i32 uext, i64) -> i64 tail
27+
;; sig2 = (i64 vmctx, i32 uext, i32 uext) -> i32 uext tail
2828
;; fn0 = colocated u1:9 sig1
2929
;; fn1 = colocated u1:35 sig2
3030
;; stack_limit = gv2

0 commit comments

Comments
 (0)