From dcc4577bed2123150475fd0fba1f33a45b602de7 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 21 Nov 2024 11:23:43 -0800 Subject: [PATCH] pulley: Initial support for indirect calls Not thoroughly tested at runtime yet but this should be enough to get the Cranelift backend to not panic and start testing more and more wasm instructions and modules. --- .../src/isa/pulley_shared/inst/emit.rs | 14 +++++++- .../filetests/isa/pulley32/call.clif | 35 +++++++++++++++++++ .../filetests/isa/pulley64/call.clif | 35 +++++++++++++++++++ pulley/fuzz/src/interp.rs | 1 + pulley/src/interp.rs | 12 +++++++ pulley/src/lib.rs | 4 +++ 6 files changed, 100 insertions(+), 1 deletion(-) diff --git a/cranelift/codegen/src/isa/pulley_shared/inst/emit.rs b/cranelift/codegen/src/isa/pulley_shared/inst/emit.rs index 3772c127955b..5bb7fcecc2d1 100644 --- a/cranelift/codegen/src/isa/pulley_shared/inst/emit.rs +++ b/cranelift/codegen/src/isa/pulley_shared/inst/emit.rs @@ -215,7 +215,19 @@ fn pulley_emit

( state.adjust_virtual_sp_offset(-callee_pop_size); } - Inst::IndirectCall { .. } => todo!(), + Inst::IndirectCall { info } => { + enc::call_indirect(sink, info.dest); + + if let Some(s) = state.take_stack_map() { + let offset = sink.cur_offset(); + sink.push_user_stack_map(state, offset, s); + } + + sink.add_call_site(); + + let callee_pop_size = i64::from(info.callee_pop_size); + state.adjust_virtual_sp_offset(-callee_pop_size); + } Inst::Jump { label } => { sink.use_label_at_offset(start_offset + 1, *label, LabelUse::Jump(1)); diff --git a/cranelift/filetests/filetests/isa/pulley32/call.clif b/cranelift/filetests/filetests/isa/pulley32/call.clif index 2a1c1d07f29b..4dd26b42c745 100644 --- a/cranelift/filetests/filetests/isa/pulley32/call.clif +++ b/cranelift/filetests/filetests/isa/pulley32/call.clif @@ -408,3 +408,38 @@ block0: ; xadd32 sp, sp, spilltmp0 ; ret +function %call_indirect(i32) -> i64 { + sig0 = () -> i64 tail + +block0(v0: i32): + v1 = call_indirect sig0, v0() + return v1 +} + +; VCode: +; x30 = xconst8 -16 +; x27 = xadd32 x27, x30 +; store64 sp+8, x28 // flags = notrap aligned +; store64 sp+0, x29 // flags = notrap aligned +; x29 = xmov x27 +; block0: +; indirect_call x0, CallInfo { dest: XReg(p0i), uses: [], defs: [CallRetPair { vreg: Writable { reg: p0i }, preg: p0i }], clobbers: PRegSet { bits: [65534, 65279, 4294967295, 0] }, callee_conv: Tail, caller_conv: Fast, callee_pop_size: 0 } +; x28 = load64_u sp+8 // flags = notrap aligned +; x29 = load64_u sp+0 // flags = notrap aligned +; x30 = xconst8 16 +; x27 = xadd32 x27, x30 +; ret +; +; Disassembled: +; xconst8 spilltmp0, -16 +; xadd32 sp, sp, spilltmp0 +; store64_offset8 sp, 8, lr +; store64 sp, fp +; xmov fp, sp +; call_indirect x0 +; load64_offset8 lr, sp, 8 +; load64 fp, sp +; xconst8 spilltmp0, 16 +; xadd32 sp, sp, spilltmp0 +; ret + diff --git a/cranelift/filetests/filetests/isa/pulley64/call.clif b/cranelift/filetests/filetests/isa/pulley64/call.clif index 559598418621..99a31a5f29ab 100644 --- a/cranelift/filetests/filetests/isa/pulley64/call.clif +++ b/cranelift/filetests/filetests/isa/pulley64/call.clif @@ -408,3 +408,38 @@ block0: ; xadd32 sp, sp, spilltmp0 ; ret +function %call_indirect(i64) -> i64 { + sig0 = () -> i64 tail + +block0(v0: i64): + v1 = call_indirect sig0, v0() + return v1 +} + +; VCode: +; x30 = xconst8 -16 +; x27 = xadd32 x27, x30 +; store64 sp+8, x28 // flags = notrap aligned +; store64 sp+0, x29 // flags = notrap aligned +; x29 = xmov x27 +; block0: +; indirect_call x0, CallInfo { dest: XReg(p0i), uses: [], defs: [CallRetPair { vreg: Writable { reg: p0i }, preg: p0i }], clobbers: PRegSet { bits: [65534, 65279, 4294967295, 0] }, callee_conv: Tail, caller_conv: Fast, callee_pop_size: 0 } +; x28 = load64_u sp+8 // flags = notrap aligned +; x29 = load64_u sp+0 // flags = notrap aligned +; x30 = xconst8 16 +; x27 = xadd32 x27, x30 +; ret +; +; Disassembled: +; xconst8 spilltmp0, -16 +; xadd32 sp, sp, spilltmp0 +; store64_offset8 sp, 8, lr +; store64 sp, fp +; xmov fp, sp +; call_indirect x0 +; load64_offset8 lr, sp, 8 +; load64 fp, sp +; xconst8 spilltmp0, 16 +; xadd32 sp, sp, spilltmp0 +; ret + diff --git a/pulley/fuzz/src/interp.rs b/pulley/fuzz/src/interp.rs index 5fd70b082c16..c7a659ede488 100644 --- a/pulley/fuzz/src/interp.rs +++ b/pulley/fuzz/src/interp.rs @@ -97,6 +97,7 @@ fn op_is_safe_for_fuzzing(op: &Op) -> bool { Op::BitcastFloatFromInt64(_) => true, Op::ExtendedOp(op) => extended_op_is_safe_for_fuzzing(op), Op::Call(_) => false, + Op::CallIndirect(_) => false, Op::Xadd32(Xadd32 { operands, .. }) | Op::Xadd64(Xadd64 { operands, .. }) | Op::Xeq64(Xeq64 { operands, .. }) diff --git a/pulley/src/interp.rs b/pulley/src/interp.rs index c0817f454144..470ea0dce0a5 100644 --- a/pulley/src/interp.rs +++ b/pulley/src/interp.rs @@ -642,6 +642,18 @@ impl OpVisitor for Interpreter<'_> { ControlFlow::Continue(()) } + fn call_indirect(&mut self, dst: XReg) -> ControlFlow { + let return_addr = self.pc.as_ptr(); + self.state[XReg::lr].set_ptr(return_addr.as_ptr()); + // SAFETY: part of the unsafe contract of the interpreter is only valid + // bytecode is interpreted, so the jump destination is part of the validity + // of the bytecode itself. + unsafe { + self.pc = UnsafeBytecodeStream::new(NonNull::new_unchecked(self.state[dst].get_ptr())); + } + ControlFlow::Continue(()) + } + fn jump(&mut self, offset: PcRelOffset) -> ControlFlow { self.pc_rel_jump(offset, 5); ControlFlow::Continue(()) diff --git a/pulley/src/lib.rs b/pulley/src/lib.rs index b80a5646ec9b..688ffe533f88 100644 --- a/pulley/src/lib.rs +++ b/pulley/src/lib.rs @@ -24,6 +24,10 @@ macro_rules! for_each_op { /// register to the PC just after this instruction. call = Call { offset: PcRelOffset }; + /// Transfer control to the PC in `reg` and set `lr` to the PC just + /// after this instruction. + call_indirect = CallIndirect { reg: XReg }; + /// Unconditionally transfer control to the PC at the given offset. jump = Jump { offset: PcRelOffset };