ZJIT: Allow DCE to remove some CCalls (#13363)

Allow DCE to remove some CCalls

Add `elidable` field that signals that there would be no discernible
effect if the call to the method were removed. The default is false.
This commit is contained in:
Max Bernstein 2025-05-20 18:41:37 -04:00 committed by GitHub
parent b043abc048
commit b08e20d34a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
Notes: git 2025-05-20 22:41:49 +00:00
Merged-By: k0kubun <takashikkbn@gmail.com>
3 changed files with 50 additions and 9 deletions

View File

@ -273,7 +273,7 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio
Insn::GuardType { val, guard_type, state } => gen_guard_type(asm, opnd!(val), *guard_type, &function.frame_state(*state))?,
Insn::GuardBitEquals { val, expected, state } => gen_guard_bit_equals(asm, opnd!(val), *expected, &function.frame_state(*state))?,
Insn::PatchPoint(_) => return Some(()), // For now, rb_zjit_bop_redefined() panics. TODO: leave a patch point and fix rb_zjit_bop_redefined()
Insn::CCall { cfun, args, name: _, return_type: _ } => gen_ccall(jit, asm, *cfun, args)?,
Insn::CCall { cfun, args, name: _, return_type: _, elidable: _ } => gen_ccall(jit, asm, *cfun, args)?,
_ => {
debug!("ZJIT: gen_function: unexpected insn {:?}", insn);
return None;

View File

@ -26,6 +26,8 @@ pub struct FnProperties {
pub leaf: bool,
/// What Type the C function returns
pub return_type: Type,
/// Whether it's legal to remove the call if the result is unused
pub elidable: bool,
}
impl Annotations {
@ -64,7 +66,7 @@ pub fn init() -> Annotations {
macro_rules! annotate {
($module:ident, $method_name:literal, $return_type:expr, $($properties:ident),+) => {
let mut props = FnProperties { no_gc: false, leaf: false, return_type: $return_type };
let mut props = FnProperties { no_gc: false, leaf: false, elidable: false, return_type: $return_type };
$(
props.$properties = true;
)+
@ -72,9 +74,9 @@ pub fn init() -> Annotations {
}
}
annotate!(rb_mKernel, "itself", types::BasicObject, no_gc, leaf);
annotate!(rb_mKernel, "itself", types::BasicObject, no_gc, leaf, elidable);
annotate!(rb_cString, "bytesize", types::Fixnum, no_gc, leaf);
annotate!(rb_cModule, "name", types::StringExact.union(types::NilClassExact), no_gc, leaf);
annotate!(rb_cModule, "name", types::StringExact.union(types::NilClassExact), no_gc, leaf, elidable);
annotate!(rb_cModule, "===", types::BoolExact, no_gc, leaf);
Annotations {

View File

@ -348,7 +348,7 @@ pub enum Insn {
// Call a C function
// `name` is for printing purposes only
CCall { cfun: *const u8, args: Vec<InsnId>, name: ID, return_type: Type },
CCall { cfun: *const u8, args: Vec<InsnId>, name: ID, return_type: Type, elidable: bool },
// Send without block with dynamic dispatch
// Ignoring keyword arguments etc for now
@ -429,6 +429,7 @@ impl Insn {
Insn::FixnumLe { .. } => false,
Insn::FixnumGt { .. } => false,
Insn::FixnumGe { .. } => false,
Insn::CCall { elidable, .. } => !elidable,
_ => true,
}
}
@ -510,7 +511,7 @@ impl<'a> std::fmt::Display for InsnPrinter<'a> {
Insn::GuardBitEquals { val, expected, .. } => { write!(f, "GuardBitEquals {val}, {}", expected.print(self.ptr_map)) },
Insn::PatchPoint(invariant) => { write!(f, "PatchPoint {}", invariant.print(self.ptr_map)) },
Insn::GetConstantPath { ic } => { write!(f, "GetConstantPath {:p}", self.ptr_map.map_ptr(ic)) },
Insn::CCall { cfun, args, name, return_type: _ } => {
Insn::CCall { cfun, args, name, return_type: _, elidable: _ } => {
write!(f, "CCall {}@{:p}", name.contents_lossy(), self.ptr_map.map_ptr(cfun))?;
for arg in args {
write!(f, ", {arg}")?;
@ -850,7 +851,7 @@ impl Function {
},
ArraySet { array, idx, val } => ArraySet { array: find!(*array), idx: *idx, val: find!(*val) },
ArrayDup { val , state } => ArrayDup { val: find!(*val), state: *state },
CCall { cfun, args, name, return_type } => CCall { cfun: *cfun, args: args.iter().map(|arg| find!(*arg)).collect(), name: *name, return_type: *return_type },
&CCall { cfun, ref args, name, return_type, elidable } => CCall { cfun: cfun, args: args.iter().map(|arg| find!(*arg)).collect(), name: name, return_type: return_type, elidable },
Defined { .. } => todo!("find(Defined)"),
NewArray { elements, state } => NewArray { elements: find_vec!(*elements), state: find!(*state) },
ArrayMax { elements, state } => ArrayMax { elements: find_vec!(*elements), state: find!(*state) },
@ -1194,7 +1195,7 @@ impl Function {
// Filter for a leaf and GC free function
use crate::cruby_methods::FnProperties;
let Some(FnProperties { leaf: true, no_gc: true, return_type }) =
let Some(FnProperties { leaf: true, no_gc: true, return_type, elidable }) =
ZJITState::get_method_annotations().get_cfunc_properties(method)
else {
return Err(());
@ -1212,7 +1213,7 @@ impl Function {
let cfun = unsafe { get_mct_func(cfunc) }.cast();
let mut cfunc_args = vec![self_val];
cfunc_args.append(&mut args);
let ccall = fun.push_insn(block, Insn::CCall { cfun, args: cfunc_args, name: method_id, return_type });
let ccall = fun.push_insn(block, Insn::CCall { cfun, args: cfunc_args, name: method_id, return_type, elidable });
fun.make_equal_to(send_insn_id, ccall);
return Ok(());
}
@ -3832,6 +3833,44 @@ mod opt_tests {
"#]]);
}
#[test]
fn eliminate_kernel_itself() {
eval("
def test
x = [].itself
1
end
");
assert_optimized_method_hir("test", expect![[r#"
fn test:
bb0():
PatchPoint MethodRedefined(Array@0x1000, itself@0x1008)
v6:Fixnum[1] = Const Value(1)
Return v6
"#]]);
}
#[test]
fn eliminate_module_name() {
eval("
module M; end
def test
x = M.name
1
end
test
");
assert_optimized_method_hir("test", expect![[r#"
fn test:
bb0():
PatchPoint SingleRactorMode
PatchPoint StableConstantNames(0x1000, M)
PatchPoint MethodRedefined(Module@0x1008, name@0x1010)
v5:Fixnum[1] = Const Value(1)
Return v5
"#]]);
}
#[test]
fn kernel_itself_argc_mismatch() {
eval("