diff options
| author | Jeff Zhang <jeff.j.zhang@shopify.com> | 2026-01-20 10:50:43 -0500 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-01-20 10:50:43 -0500 |
| commit | d225bb8b464e4e03d2eb6c09ef15adf727af9e2b (patch) | |
| tree | 62b212032cf2e84188b4e491daa2faf3e3391a24 /zjit/src/codegen.rs | |
| parent | c27ae8d91aadca0660070ee1eeae9598b1fe47ee (diff) | |
ZJIT: Compile IsA into load + compare for String/Array/Hash (#15878)
Resolves https://github.com/Shopify/ruby/issues/880
Implemented this by using the code generation for `GuardType` as a reference.
Not sure if this is the best way to go about it, but it seems to work.
Diffstat (limited to 'zjit/src/codegen.rs')
| -rw-r--r-- | zjit/src/codegen.rs | 41 |
1 files changed, 40 insertions, 1 deletions
diff --git a/zjit/src/codegen.rs b/zjit/src/codegen.rs index 7afcff5863..0ae85c24a2 100644 --- a/zjit/src/codegen.rs +++ b/zjit/src/codegen.rs @@ -1743,7 +1743,46 @@ fn gen_dup_array_include( } fn gen_is_a(asm: &mut Assembler, obj: Opnd, class: Opnd) -> lir::Opnd { - asm_ccall!(asm, rb_obj_is_kind_of, obj, class) + let builtin_type = match class { + Opnd::Value(value) if value == unsafe { rb_cString } => Some(RUBY_T_STRING), + Opnd::Value(value) if value == unsafe { rb_cArray } => Some(RUBY_T_ARRAY), + Opnd::Value(value) if value == unsafe { rb_cHash } => Some(RUBY_T_HASH), + _ => None + }; + + if let Some(builtin_type) = builtin_type { + asm_comment!(asm, "IsA by matching builtin type"); + let ret_label = asm.new_label("is_a_ret"); + let false_label = asm.new_label("is_a_false"); + + let val = match obj { + Opnd::Reg(_) | Opnd::VReg { .. } => obj, + _ => asm.load(obj), + }; + + // Check special constant + asm.test(val, Opnd::UImm(RUBY_IMMEDIATE_MASK as u64)); + asm.jnz(ret_label.clone()); + + // Check false + asm.cmp(val, Qfalse.into()); + asm.je(false_label.clone()); + + let flags = asm.load(Opnd::mem(VALUE_BITS, val, RUBY_OFFSET_RBASIC_FLAGS)); + let obj_builtin_type = asm.and(flags, Opnd::UImm(RUBY_T_MASK as u64)); + asm.cmp(obj_builtin_type, Opnd::UImm(builtin_type as u64)); + asm.jmp(ret_label.clone()); + + // If we get here then the value was false, unset the Z flag + // so that csel_e will select false instead of true + asm.write_label(false_label); + asm.test(Opnd::UImm(1), Opnd::UImm(1)); + + asm.write_label(ret_label); + asm.csel_e(Qtrue.into(), Qfalse.into()) + } else { + asm_ccall!(asm, rb_obj_is_kind_of, obj, class) + } } /// Compile a new hash instruction |
