Skip to content

Commit 23e72f3

Browse files
committed
WIP
1 parent 0edded4 commit 23e72f3

2 files changed

Lines changed: 100 additions & 2 deletions

File tree

‎zjit/src/codegen.rs‎

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,9 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio
395395
Insn::GuardBitEquals { val, expected, state } => gen_guard_bit_equals(jit, asm, opnd!(val), *expected, &function.frame_state(*state)),
396396
Insn::PatchPoint { invariant, state } => no_output!(gen_patch_point(jit, asm, invariant, &function.frame_state(*state))),
397397
Insn::CCall { cfun, args, name: _, return_type: _, elidable: _ } => gen_ccall(asm, *cfun, opnds!(args)),
398+
Insn::CCallVariadic { cfun, recv, args, name: _, state } => {
399+
gen_ccall_variadic(jit, asm, *cfun, opnd!(recv), opnds!(args), &function.frame_state(*state))
400+
}
398401
Insn::GetIvar { self_val, id, state: _ } => gen_getivar(asm, opnd!(self_val), *id),
399402
Insn::SetGlobal { id, val, state } => no_output!(gen_setglobal(jit, asm, *id, opnd!(val), &function.frame_state(*state))),
400403
Insn::GetGlobal { id, state } => gen_getglobal(jit, asm, *id, &function.frame_state(*state)),
@@ -640,6 +643,36 @@ fn gen_ccall(asm: &mut Assembler, cfun: *const u8, args: Vec<Opnd>) -> lir::Opnd
640643
asm.ccall(cfun, args)
641644
}
642645

646+
/// Generate code for a variadic C function call
647+
/// func(int argc, VALUE *argv, VALUE recv)
648+
fn gen_ccall_variadic(
649+
jit: &mut JITState,
650+
asm: &mut Assembler,
651+
cfun: *const u8,
652+
recv: Opnd,
653+
args: Vec<Opnd>,
654+
state: &FrameState,
655+
) -> lir::Opnd {
656+
gen_prepare_non_leaf_call(jit, asm, state);
657+
658+
let argc = args.len();
659+
660+
asm_comment!(asm, "call variadic cfunc: argc={}", argc);
661+
662+
if args.is_empty() {
663+
let result = asm.ccall(cfun, vec![0.into(), 0.into(), recv]);
664+
result
665+
} else {
666+
let argv_ptr = gen_push_opnds(jit, asm, &args);
667+
668+
let result = asm.ccall(cfun, vec![argc.into(), argv_ptr, recv]);
669+
670+
gen_pop_opnds(asm, &args);
671+
672+
result
673+
}
674+
}
675+
643676
/// Emit an uncached instance variable lookup
644677
fn gen_getivar(asm: &mut Assembler, recv: Opnd, id: ID) -> Opnd {
645678
asm_ccall!(asm, rb_ivar_get, recv, id.0.into())

‎zjit/src/hir.rs‎

Lines changed: 67 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -617,6 +617,16 @@ pub enum Insn {
617617
/// `name` is for printing purposes only
618618
CCall { cfun: *const u8, args: Vec<InsnId>, name: ID, return_type: Type, elidable: bool },
619619

620+
/// Call a variadic C function with signature: func(int argc, VALUE *argv, VALUE recv)
621+
/// This handles frame setup, argv creation, and frame teardown all in one
622+
CCallVariadic {
623+
cfun: *const u8,
624+
recv: InsnId,
625+
args: Vec<InsnId>,
626+
name: ID,
627+
state: InsnId,
628+
},
629+
620630
/// Un-optimized fallback implementation (dynamic dispatch) for send-ish instructions
621631
/// Ignoring keyword arguments etc for now
622632
SendWithoutBlock {
@@ -755,6 +765,7 @@ impl Insn {
755765
Insn::LoadIvarEmbedded { .. } => false,
756766
Insn::LoadIvarExtended { .. } => false,
757767
Insn::CCall { elidable, .. } => !elidable,
768+
Insn::CCallVariadic { .. } => true,
758769
// TODO: NewRange is effects free if we can prove the two ends to be Fixnum,
759770
// but we don't have type information here in `impl Insn`. See rb_range_new().
760771
Insn::NewRange { .. } => true,
@@ -930,6 +941,13 @@ impl<'a> std::fmt::Display for InsnPrinter<'a> {
930941
}
931942
Ok(())
932943
},
944+
Insn::CCallVariadic { cfun, recv, args, name, state: _ } => {
945+
write!(f, "CCallVariadic {}@{:p}, {recv}", name.contents_lossy(), self.ptr_map.map_ptr(cfun))?;
946+
for arg in args {
947+
write!(f, ", {arg}")?;
948+
}
949+
Ok(())
950+
},
933951
Insn::Snapshot { state } => write!(f, "Snapshot {}", state.print(self.ptr_map)),
934952
Insn::Defined { op_type, v, .. } => {
935953
// op_type (enum defined_type) printing logic from iseq.c.
@@ -1411,6 +1429,9 @@ impl Function {
14111429
&HashDup { val, state } => HashDup { val: find!(val), state },
14121430
&ObjectAlloc { val, state } => ObjectAlloc { val: find!(val), state },
14131431
&CCall { cfun, ref args, name, return_type, elidable } => CCall { cfun, args: find_vec!(args), name, return_type, elidable },
1432+
&CCallVariadic { cfun, recv, ref args, name, state } => CCallVariadic {
1433+
cfun, recv: find!(recv), args: find_vec!(args), name, state
1434+
},
14141435
&Defined { op_type, obj, pushval, v, state } => Defined { op_type, obj, pushval, v: find!(v), state: find!(state) },
14151436
&DefinedIvar { self_val, pushval, id, state } => DefinedIvar { self_val: find!(self_val), pushval, id, state },
14161437
&NewArray { ref elements, state } => NewArray { elements: find_vec!(elements), state: find!(state) },
@@ -1498,6 +1519,7 @@ impl Function {
14981519
Insn::NewRangeFixnum { .. } => types::RangeExact,
14991520
Insn::ObjectAlloc { .. } => types::HeapObject,
15001521
Insn::CCall { return_type, .. } => *return_type,
1522+
Insn::CCallVariadic { .. } => types::BasicObject,
15011523
Insn::GuardType { val, guard_type, .. } => self.type_of(*val).intersection(*guard_type),
15021524
Insn::GuardTypeNot { .. } => types::BasicObject,
15031525
Insn::GuardBitEquals { val, expected, .. } => self.type_of(*val).intersection(Type::from_value(*expected)),
@@ -2030,9 +2052,47 @@ impl Function {
20302052
return Ok(());
20312053
}
20322054
}
2055+
// Variadic method
20332056
-1 => {
2034-
// (argc, argv, self) parameter form
2035-
// Falling through for now
2057+
// The method gets a pointer to the first argument
2058+
// rb_f_puts(int argc, VALUE *argv, VALUE recv)
2059+
let ci_flags = unsafe { vm_ci_flag(call_info) };
2060+
if ci_flags & VM_CALL_ARGS_SIMPLE != 0 {
2061+
// We need profiled type information for variadic calls
2062+
// to ensure we're calling the right method
2063+
if profiled_type.is_none() {
2064+
return Err(());
2065+
}
2066+
2067+
// Add method redefinition guard
2068+
fun.push_insn(block, Insn::PatchPoint {
2069+
invariant: Invariant::MethodRedefined {
2070+
klass: recv_class,
2071+
method: method_id,
2072+
cme: method
2073+
},
2074+
state
2075+
});
2076+
2077+
fun.push_insn(block, Insn::GuardType {
2078+
val: recv,
2079+
guard_type: Type::from_profiled_type(profiled_type.unwrap()),
2080+
state
2081+
});
2082+
2083+
let cfun = unsafe { get_mct_func(cfunc) }.cast();
2084+
let ccall = fun.push_insn(block, Insn::CCallVariadic {
2085+
cfun,
2086+
recv,
2087+
args,
2088+
name: method_id,
2089+
state,
2090+
});
2091+
2092+
fun.make_equal_to(send_insn_id, ccall);
2093+
return Ok(());
2094+
}
2095+
// Fall through for complex cases (splat, kwargs, etc.)
20362096
}
20372097
-2 => {
20382098
// (self, args_ruby_array) parameter form
@@ -2345,6 +2405,11 @@ impl Function {
23452405
worklist.push_back(state)
23462406
}
23472407
Insn::CCall { args, .. } => worklist.extend(args),
2408+
Insn::CCallVariadic { recv, args, state, .. } => {
2409+
worklist.push_back(*recv);
2410+
worklist.extend(args);
2411+
worklist.push_back(*state);
2412+
}
23482413
&Insn::GetIvar { self_val, state, .. } | &Insn::DefinedIvar { self_val, state, .. } => {
23492414
worklist.push_back(self_val);
23502415
worklist.push_back(state);

0 commit comments

Comments
 (0)