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 };