Skip to content

Commit 438fc93

Browse files
authored
pulley: Implement interpreter-to-host calls (#9665)
* pulley: Implement interpreter-to-host calls This commit is an initial stab at implementing interpreter-to-host communication in Pulley. The basic problem is that Pulley needs the ability to call back into Wasmtime to implement tasks such as `memory.grow`, imported functions, etc. For native platforms this is a simple `call_indirect` operation in Cranelift but the story for Pulley must be different because it's effectively switching from interpreted code to native code. The initial idea for this in #9651 is replaced here and looks mostly similar but with a few changes. The overall structure of how this works is: * A new `call_indirect_host` opcode is added to Pulley. * Function signatures that can be called from Pulley bytecode are statically enumerated at build-time. * This enables the implementation of `call_indirect_host` to take an immediate of which signature is being used and cast the function pointer to the right type. * A new pulley-specific relocation is added to Cranelift for this opcode. * `RelocDistance::Far` calls to a name trigger the use of `call_indirect_host`. * The relocation is filled in by Wasmtime after compilation where the signature number is inserted. * A new `NS_*` value for user-function namespaces is reserved in `wasmtime-cranelift` for this new namespace of functions. * Code generation for Pulley in `wasmtime-cranelift` now has Pulley-specific handling of the wasm-to-host transition where all previous `call_indirect` instructions are replaced with a call to a "backend intrinsic" which gets lowered to a `call_indirect_host`. Note that most of this still isn't hooked up everywhere in Wasmtime. That means that the testing here is pretty light at this time. It'll require a fair bit more work to get everything fully integrated from Wasmtime in Pulley. This is expected to be one of the significant remaining chunks of work and should help unblock future testing (or make those diffs smaller ideally). * Review comments
1 parent 2af831a commit 438fc93

File tree

20 files changed

+410
-61
lines changed

20 files changed

+410
-61
lines changed

Cargo.lock

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

cranelift/codegen/src/binemit/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,10 @@ pub enum Reloc {
119119
S390xTlsGd64,
120120
/// s390x TLS GDCall - marker to enable optimization of TLS calls
121121
S390xTlsGdCall,
122+
123+
/// Pulley - call a host function indirectly where the embedder resolving
124+
/// this relocation needs to fill in the expected signature.
125+
PulleyCallIndirectHost,
122126
}
123127

124128
impl fmt::Display for Reloc {
@@ -152,6 +156,7 @@ impl fmt::Display for Reloc {
152156
Self::Aarch64Ld64GotLo12Nc => write!(f, "Aarch64AdrGotLo12Nc"),
153157
Self::S390xTlsGd64 => write!(f, "TlsGd64"),
154158
Self::S390xTlsGdCall => write!(f, "TlsGdCall"),
159+
Self::PulleyCallIndirectHost => write!(f, "PulleyCallIndirectHost"),
155160
}
156161
}
157162
}

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

Lines changed: 22 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -541,36 +541,29 @@ where
541541
insts
542542
}
543543

544-
fn gen_call(dest: &CallDest, tmp: Writable<Reg>, info: CallInfo<()>) -> SmallVec<[Self::I; 2]> {
545-
if info.callee_conv == isa::CallConv::Tail || info.callee_conv == isa::CallConv::Fast {
546-
match &dest {
547-
&CallDest::ExtName(ref name, RelocDistance::Near) => smallvec![Inst::Call {
548-
info: Box::new(info.map(|()| name.clone()))
549-
}
550-
.into()],
551-
&CallDest::ExtName(ref name, RelocDistance::Far) => smallvec![
552-
Inst::LoadExtName {
553-
dst: WritableXReg::try_from(tmp).unwrap(),
554-
name: Box::new(name.clone()),
555-
offset: 0,
556-
}
557-
.into(),
558-
Inst::IndirectCall {
559-
info: Box::new(info.map(|()| XReg::new(tmp.to_reg()).unwrap()))
560-
}
561-
.into(),
562-
],
563-
&CallDest::Reg(reg) => smallvec![Inst::IndirectCall {
564-
info: Box::new(info.map(|()| XReg::new(*reg).unwrap()))
565-
}
566-
.into()],
544+
fn gen_call(
545+
dest: &CallDest,
546+
_tmp: Writable<Reg>,
547+
info: CallInfo<()>,
548+
) -> SmallVec<[Self::I; 2]> {
549+
match dest {
550+
// "near" calls are pulley->pulley calls so they use a normal "call"
551+
// opcode
552+
CallDest::ExtName(name, RelocDistance::Near) => smallvec![Inst::Call {
553+
info: Box::new(info.map(|()| name.clone()))
567554
}
568-
} else {
569-
todo!(
570-
"host calls? callee_conv = {:?}; caller_conv = {:?}",
571-
info.callee_conv,
572-
info.caller_conv,
573-
)
555+
.into()],
556+
// "far" calls are pulley->host calls so they use a different opcode
557+
// which is lowered with a special relocation in the backend.
558+
CallDest::ExtName(name, RelocDistance::Far) => smallvec![Inst::IndirectCallHost {
559+
info: Box::new(info.map(|()| name.clone()))
560+
}
561+
.into()],
562+
// Indirect calls are all assumed to be pulley->pulley calls
563+
CallDest::Reg(reg) => smallvec![Inst::IndirectCall {
564+
info: Box::new(info.map(|()| XReg::new(*reg).unwrap()))
565+
}
566+
.into()],
574567
}
575568
}
576569

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,10 @@
4949
;; An indirect call to an unknown callee.
5050
(IndirectCall (info BoxCallIndInfo))
5151

52+
;; An indirect call out to a host-defined function. The host function
53+
;; pointer is the first "argument" of this function call.
54+
(IndirectCallHost (info BoxCallInfo))
55+
5256
;; Unconditional jumps.
5357
(Jump (label MachLabel))
5458

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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,23 @@ fn pulley_emit<P>(
229229
state.adjust_virtual_sp_offset(-callee_pop_size);
230230
}
231231

232+
Inst::IndirectCallHost { info } => {
233+
// Emit a relocation to fill in the actual immediate argument here
234+
// in `call_indirect_host`.
235+
sink.add_reloc(Reloc::PulleyCallIndirectHost, &info.dest, 0);
236+
enc::call_indirect_host(sink, 0_u8);
237+
238+
if let Some(s) = state.take_stack_map() {
239+
let offset = sink.cur_offset();
240+
sink.push_user_stack_map(state, offset, s);
241+
}
242+
sink.add_call_site();
243+
244+
// If a callee pop is happening here that means that something has
245+
// messed up, these are expected to be "very simple" signatures.
246+
assert!(info.callee_pop_size == 0);
247+
}
248+
232249
Inst::Jump { label } => {
233250
sink.use_label_at_offset(start_offset + 1, *label, LabelUse::Jump(1));
234251
sink.add_uncond_branch(start_offset, start_offset + 5, *label);

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ fn pulley_get_operands(inst: &mut Inst, collector: &mut impl OperandVisitor) {
9191
collector.reg_def(dst);
9292
}
9393

94-
Inst::Call { info } => {
94+
Inst::Call { info } | Inst::IndirectCallHost { info } => {
9595
let CallInfo { uses, defs, .. } = &mut **info;
9696
for CallArgPair { vreg, preg } in uses {
9797
collector.reg_fixed_use(vreg, *preg);
@@ -582,6 +582,10 @@ impl Inst {
582582
format!("indirect_call {callee}, {info:?}")
583583
}
584584

585+
Inst::IndirectCallHost { info } => {
586+
format!("indirect_call_host {info:?}")
587+
}
588+
585589
Inst::Jump { label } => format!("jump {}", label.to_string()),
586590

587591
Inst::BrIf {

cranelift/codegen/src/machinst/buffer.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2037,6 +2037,10 @@ impl<I: VCodeInst> TextSectionBuilder for MachTextSectionBuilder<I> {
20372037
self.force_veneers = ForceVeneers::Yes;
20382038
}
20392039

2040+
fn write(&mut self, offset: u64, data: &[u8]) {
2041+
self.buf.data[offset.try_into().unwrap()..][..data.len()].copy_from_slice(data);
2042+
}
2043+
20402044
fn finish(&mut self, ctrl_plane: &mut ControlPlane) -> Vec<u8> {
20412045
// Double-check all functions were pushed.
20422046
assert_eq!(self.next_func, self.buf.label_offsets.len());

cranelift/codegen/src/machinst/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -554,6 +554,10 @@ pub trait TextSectionBuilder {
554554
/// A debug-only option which is used to for
555555
fn force_veneers(&mut self);
556556

557+
/// Write the `data` provided at `offset`, for example when resolving a
558+
/// relocation.
559+
fn write(&mut self, offset: u64, data: &[u8]);
560+
557561
/// Completes this text section, filling out any final details, and returns
558562
/// the bytes of the text section.
559563
fn finish(&mut self, ctrl_plane: &mut ControlPlane) -> Vec<u8>;
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
test compile precise-output
2+
target pulley64
3+
4+
function %call_indirect_host() {
5+
fn0 = u10:0() system_v
6+
block0:
7+
call fn0()
8+
return
9+
}
10+
11+
; VCode:
12+
; x30 = xconst8 -16
13+
; x27 = xadd32 x27, x30
14+
; store64 sp+8, x28 // flags = notrap aligned
15+
; store64 sp+0, x29 // flags = notrap aligned
16+
; x29 = xmov x27
17+
; block0:
18+
; indirect_call_host CallInfo { dest: User(userextname0), uses: [], defs: [], clobbers: PRegSet { bits: [65535, 65279, 4294967295, 0] }, callee_conv: SystemV, caller_conv: Fast, callee_pop_size: 0 }
19+
; x28 = load64_u sp+8 // flags = notrap aligned
20+
; x29 = load64_u sp+0 // flags = notrap aligned
21+
; x30 = xconst8 16
22+
; x27 = xadd32 x27, x30
23+
; ret
24+
;
25+
; Disassembled:
26+
; xconst8 spilltmp0, -16
27+
; xadd32 sp, sp, spilltmp0
28+
; store64_offset8 sp, 8, lr
29+
; store64 sp, fp
30+
; xmov fp, sp
31+
; call_indirect_host 0
32+
; load64_offset8 lr, sp, 8
33+
; load64 fp, sp
34+
; xconst8 spilltmp0, 16
35+
; xadd32 sp, sp, spilltmp0
36+
; ret
37+

crates/cranelift/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,12 @@ thiserror = { workspace = true }
3232
cfg-if = { workspace = true }
3333
wasmtime-versioned-export-macros = { workspace = true }
3434
itertools = "0.12"
35+
pulley-interpreter = { workspace = true, optional = true }
3536

3637
[features]
3738
all-arch = ["cranelift-codegen/all-arch"]
3839
host-arch = ["cranelift-codegen/host-arch"]
39-
pulley = ["cranelift-codegen/pulley"]
40+
pulley = ["cranelift-codegen/pulley", "dep:pulley-interpreter"]
4041
trace-log = ["cranelift-codegen/trace-log"]
4142
component-model = ["wasmtime-environ/component-model"]
4243
incremental-cache = ["cranelift-codegen/incremental-cache"]

0 commit comments

Comments
 (0)