From c4e235f42d5017440f7d1e28e0cdb24ce0d9a800 Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Wed, 26 Aug 2026 09:25:50 -0700 Subject: [PATCH] ZJIT: Pass overflow cfunc call args on the stack (#18428) --- zjit/src/codegen.rs | 3 --- zjit/src/hir.rs | 22 +--------------------- zjit/src/hir/opt_tests.rs | 7 +++++-- zjit/src/stats.rs | 4 ---- 4 files changed, 6 insertions(+), 30 deletions(-) diff --git a/zjit/src/codegen.rs b/zjit/src/codegen.rs index 0166ea35950a4f..6e860241be08a9 100644 --- a/zjit/src/codegen.rs +++ b/zjit/src/codegen.rs @@ -1053,9 +1053,6 @@ fn gen_ccall_with_frame( gen_stack_overflow_check(jit, asm, function, state, state.stack_size()); let args_with_recv_len = args.len() + 1; - if args_with_recv_len > C_ARG_OPNDS.len() { - unimplemented!("Passing C call arguments on the stack"); - } let caller_stack_size = state.stack().len() - args_with_recv_len; // Can't use gen_prepare_non_leaf_call() because we need to adjust the SP diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs index 9fc2915df28339..0e92bdceab02d7 100644 --- a/zjit/src/hir.rs +++ b/zjit/src/hir.rs @@ -6,7 +6,7 @@ #![allow(clippy::if_same_then_else)] #![allow(clippy::match_like_matches_macro)] use crate::{ - backend::lir::C_ARG_OPNDS, cast::IntoUsize, codegen::max_iseq_versions, cruby::*, invariants::{self, iseq_seen_ep_escape}, json::Json, options::{DumpHIR, InlineDepth, debug, get_option}, payload::get_or_create_iseq_payload, profile::reset_profiles_remaining, state::{self, ZJITState}, + cast::IntoUsize, codegen::max_iseq_versions, cruby::*, invariants::{self, iseq_seen_ep_escape}, json::Json, options::{DumpHIR, InlineDepth, debug, get_option}, payload::get_or_create_iseq_payload, profile::reset_profiles_remaining, state::{self, ZJITState}, }; use std::{ cell::RefCell, collections::{HashMap, HashSet, VecDeque}, ffi::{c_void, c_uint, c_int, CStr}, fmt::Display, ptr, slice::Iter, @@ -797,10 +797,7 @@ pub enum SendFallbackReason { SendNotOptimizedNeedPermission, /// The block argument is not nil, so we can't optimize to SendWithoutBlockDirect SendBlockArgNotNil, - CCallWithFrameTooManyArgs, ObjToStringNotString, - /// Too many arguments in a C call to fit in C ABI registers. - TooManyArgsForLir, /// An operand doesn't fit in the integer type that encodes it, /// e.g. an argument count that overflows IseqCall's u16. OperandTooLarge, @@ -871,9 +868,7 @@ impl Display for SendFallbackReason { SendCfuncArrayVariadic => write!(f, "Send: C function expects array variadic"), SendNotOptimizedMethodType(method_type) => write!(f, "Send: unsupported method type {:?}", method_type), SendBlockArgNotNil => write!(f, "Send: block argument is not nil"), - CCallWithFrameTooManyArgs => write!(f, "CCallWithFrame: too many arguments"), ObjToStringNotString => write!(f, "ObjToString: result is not a string"), - TooManyArgsForLir => write!(f, "Too many arguments for LIR"), OperandTooLarge => write!(f, "Operand doesn't fit in its encoding"), BmethodNonIseqProc => write!(f, "Bmethod: Proc object is not defined by an ISEQ"), ArgcParamMismatch => write!(f, "Argument count does not match parameter count"), @@ -4882,13 +4877,6 @@ impl Function { return Err(()); } - // TODO: Support passing arguments on the stack in C calls - // +1 for self - if (argc as usize)+1 > C_ARG_OPNDS.len() { - fun.set_dynamic_send_reason(send_insn_id, TooManyArgsForLir); - return Err(()); - } - // Check singleton class assumption first, before emitting other patchpoints if !fun.assume_no_singleton_classes(block, recv_class, state) { fun.set_dynamic_send_reason(send_insn_id, SingletonClassSeen); @@ -5243,14 +5231,6 @@ impl Function { self.set_dynamic_send_reason(insn_id, ArgcParamMismatch); continue; } - // TODO: Support passing arguments on the stack in C calls - // +1 for self - if args.len()+1 > C_ARG_OPNDS.len() { - self.push_insn_id(block, insn_id); - self.set_dynamic_send_reason(insn_id, TooManyArgsForLir); - continue; - } - emit_super_call_guards(self, block, super_cme, current_cme, mid, state, frame_state_iseq); // Try inlining the cfunc into HIR diff --git a/zjit/src/hir/opt_tests.rs b/zjit/src/hir/opt_tests.rs index 134f57a5d6d44b..f1ceb59f514752 100644 --- a/zjit/src/hir/opt_tests.rs +++ b/zjit/src/hir/opt_tests.rs @@ -21831,7 +21831,10 @@ mod hir_opt_tests { v31:Fixnum[6] = Const Value(6) v33:Fixnum[7] = Const Value(7) v35:Fixnum[8] = Const Value(8) - v37:BasicObject = Send v14, :eight, v21, v23, v25, v27, v29, v31, v33, v35 # SendFallbackReason: Too many arguments for LIR + PatchPoint NoSingletonClass(ZJITEightArgs@0x1008) + PatchPoint MethodRedefined(ZJITEightArgs@0x1008, eight@0x1010, cme:0x1018) + v70:ObjectSubclass[class_exact:ZJITEightArgs] = GuardType v14, ObjectSubclass[class_exact:ZJITEightArgs] recompile + v71:BasicObject = CCallWithFrame v70, :ZJITEightArgs#eight@0x1040, v21, v23, v25, v27, v29, v31, v33, v35 PatchPoint NoEPEscape(test) v44:CBool = Test v15 v45:Falsy = RefineType v15, Falsy @@ -21839,7 +21842,7 @@ mod hir_opt_tests { bb5(): v47:Truthy = RefineType v15, Truthy CheckInterrupts - Return v37 + Return v71 bb4(): v61:NilClass = Const Value(nil) CheckInterrupts diff --git a/zjit/src/stats.rs b/zjit/src/stats.rs index 57774aa4d7db21..aae92a2e84bed2 100644 --- a/zjit/src/stats.rs +++ b/zjit/src/stats.rs @@ -257,7 +257,6 @@ make_counters! { // send_fallback_: Fallback reasons for send-ish instructions send_fallback_send_cfunc_not_variadic, send_fallback_send_not_optimized_method_type_optimized, - send_fallback_too_many_args_for_lir, send_fallback_operand_too_large, send_fallback_send_bop_redefined, send_fallback_send_operands_not_fixnum, @@ -272,7 +271,6 @@ make_counters! { send_fallback_send_not_optimized_method_type, send_fallback_send_not_optimized_need_permission, send_fallback_send_block_arg_not_nil, - send_fallback_ccall_with_frame_too_many_args, send_fallback_argc_param_mismatch, // The call has at least one feature on the caller or callee side // that the optimizer does not support. @@ -682,7 +680,6 @@ pub fn send_fallback_counter(reason: crate::hir::SendFallbackReason) -> Counter SendCfuncNotVariadic => send_fallback_send_cfunc_not_variadic, SendNotOptimizedMethodTypeOptimized(_) => send_fallback_send_not_optimized_method_type_optimized, - TooManyArgsForLir => send_fallback_too_many_args_for_lir, OperandTooLarge => send_fallback_operand_too_large, SendBopRedefined => send_fallback_send_bop_redefined, SendOperandsNotFixnum => send_fallback_send_operands_not_fixnum, @@ -704,7 +701,6 @@ pub fn send_fallback_counter(reason: crate::hir::SendFallbackReason) -> Counter SendNotOptimizedMethodType(_) => send_fallback_send_not_optimized_method_type, SendNotOptimizedNeedPermission => send_fallback_send_not_optimized_need_permission, SendBlockArgNotNil => send_fallback_send_block_arg_not_nil, - CCallWithFrameTooManyArgs => send_fallback_ccall_with_frame_too_many_args, ObjToStringNotString => send_fallback_obj_to_string_not_string, SuperCallWithBlock => send_fallback_super_call_with_block, SuperFromBlock => send_fallback_super_from_block,