diff options
author | Daniel Colson <[email protected]> | 2025-06-18 18:58:34 -0400 |
---|---|---|
committer | GitHub <[email protected]> | 2025-06-18 15:58:34 -0700 |
commit | 3290d3d7f004fdf19311c3f4e40b01e45f14c23c (patch) | |
tree | 13413ac6c8983b65d80bf0fb9ee967cbef24e38e /zjit/src/codegen.rs | |
parent | 521b2fcba4e96898bfd237c79f17f19530b7a030 (diff) |
ZJIT: Support invokebuiltin opcodes (#13632)
* `invokebuiltin`
* `invokebuiltin_delegate`
* `invokebuiltin_delegate_leave`
These instructions all call out to a C function, passing EC, self, and
some number of arguments. `invokebuiltin` gets the arguments from the
stack, whereas the `_delegate` instructions use a subset of the locals.
`opt_invokebuiltin_delegate_leave` has a fast path for `leave`, but I'm
not sure we need to do anything special for that here (FWIW YJIT appears
to treat the two delegate instructions the same).
Notes
Notes:
Merged-By: k0kubun <[email protected]>
Diffstat (limited to 'zjit/src/codegen.rs')
-rw-r--r-- | zjit/src/codegen.rs | 21 |
1 files changed, 21 insertions, 0 deletions
diff --git a/zjit/src/codegen.rs b/zjit/src/codegen.rs index b1869f71c0..90c3ce640e 100644 --- a/zjit/src/codegen.rs +++ b/zjit/src/codegen.rs @@ -260,6 +260,7 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio Insn::IfFalse { val, target } => return gen_if_false(jit, asm, opnd!(val), target), Insn::SendWithoutBlock { call_info, cd, state, self_val, args, .. } => gen_send_without_block(jit, asm, call_info, *cd, &function.frame_state(*state), self_val, args)?, Insn::SendWithoutBlockDirect { cme, iseq, self_val, args, state, .. } => gen_send_without_block_direct(cb, jit, asm, *cme, *iseq, opnd!(self_val), args, &function.frame_state(*state))?, + Insn::InvokeBuiltin { bf, args, state } => gen_invokebuiltin(jit, asm, &function.frame_state(*state), bf, args)?, Insn::Return { val } => return Some(gen_return(asm, opnd!(val))?), Insn::FixnumAdd { left, right, state } => gen_fixnum_add(jit, asm, opnd!(left), opnd!(right), &function.frame_state(*state))?, Insn::FixnumSub { left, right, state } => gen_fixnum_sub(jit, asm, opnd!(left), opnd!(right), &function.frame_state(*state))?, @@ -311,6 +312,26 @@ fn gen_get_constant_path(asm: &mut Assembler, ic: *const iseq_inline_constant_ca val } +fn gen_invokebuiltin(jit: &mut JITState, asm: &mut Assembler, state: &FrameState, bf: &rb_builtin_function, args: &Vec<InsnId>) -> Option<lir::Opnd> { + // Ensure we have enough room fit ec, self, and arguments + // TODO remove this check when we have stack args (we can use Time.new to test it) + if bf.argc + 2 > (C_ARG_OPNDS.len() as i32) { + return None; + } + + gen_save_pc(asm, state); + + let mut cargs = vec![EC]; + for &arg in args.iter() { + let opnd = jit.get_opnd(arg)?; + cargs.push(opnd); + } + + let val = asm.ccall(bf.func_ptr as *const u8, cargs); + + Some(val) +} + /// Lowering for [`Insn::CCall`]. This is a low-level raw call that doesn't know /// anything about the callee, so handling for e.g. GC safety is dealt with elsewhere. fn gen_ccall(jit: &mut JITState, asm: &mut Assembler, cfun: *const u8, args: &[InsnId]) -> Option<lir::Opnd> { |