diff options
author | Stan Lo <[email protected]> | 2025-06-05 02:51:53 +0100 |
---|---|---|
committer | <[email protected]> | 2025-06-04 18:51:53 -0700 |
commit | 111986f8b0604156e139d96476e06a0d1d645e04 () | |
tree | fb63b11cf974cb2bc9f8e964618c132eba21c628 | |
parent | 0ca80484aca180762f0a17db53981a3e69c0f7f9 (diff) |
ZJIT: Add newrange support (#13505)
* Add newrange support to zjit * Add RangeType enum for Range insn's flag * Address other feedback
Notes: Merged-By: k0kubun <[email protected]>
-rw-r--r-- | test/ruby/test_zjit.rb | 21 | ||||
-rw-r--r-- | zjit/bindgen/src/main.rs | 1 | ||||
-rw-r--r-- | zjit/src/codegen.rs | 25 | ||||
-rw-r--r-- | zjit/src/cruby_bindings.inc.rs | 1 | ||||
-rw-r--r-- | zjit/src/hir.rs | 127 | ||||
-rw-r--r-- | zjit/src/hir_type/gen_hir_type.rb | 1 | ||||
-rw-r--r-- | zjit/src/hir_type/hir_type.inc.rs | 33 | ||||
-rw-r--r-- | zjit/src/hir_type/mod.rs | 11 |
8 files changed, 206 insertions, 14 deletions
@@ -245,6 +245,27 @@ class TestZJIT < Test::Unit::TestCase } end def test_if assert_compiles '[0, nil]', %q{ def test(n) @@ -182,6 +182,7 @@ fn main() { .allowlist_var("rb_cSymbol") .allowlist_var("rb_cFloat") .allowlist_var("rb_cNumeric") .allowlist_var("rb_cString") .allowlist_var("rb_cThread") .allowlist_var("rb_cArray") @@ -7,7 +7,7 @@ use crate::state::ZJITState; use crate::{asm::CodeBlock, cruby::*, options::debug, virtualmem::CodePtr}; use crate::invariants::{iseq_escapes_ep, track_no_ep_escape_assumption}; use crate::backend::lir::{self, asm_comment, Assembler, Opnd, Target, CFP, C_ARG_OPNDS, C_RET_OPND, EC, SP}; -use crate::hir::{iseq_to_hir, Block, BlockId, BranchEdge, CallInfo}; use crate::hir::{Const, FrameState, Function, Insn, InsnId}; use crate::hir_type::{types::Fixnum, Type}; use crate::options::get_option; @@ -251,6 +251,7 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio Insn::PutSelf => gen_putself(), Insn::Const { val: Const::Value(val) } => gen_const(*val), Insn::NewArray { elements, state } => gen_new_array(jit, asm, elements, &function.frame_state(*state)), Insn::ArrayDup { val, state } => gen_array_dup(asm, opnd!(val), &function.frame_state(*state)), Insn::Param { idx } => unreachable!("block.insns should not have Insn::Param({idx})"), Insn::Snapshot { .. } => return Some(()), // we don't need to do anything for this instruction at the moment @@ -552,6 +553,28 @@ fn gen_new_array( new_array } /// Compile code that exits from JIT code with a return value fn gen_return(asm: &mut Assembler, val: lir::Opnd) -> Option<()> { // Pop the current frame (ec->cfp++) @@ -771,6 +771,7 @@ unsafe extern "C" { pub static mut rb_cModule: VALUE; pub static mut rb_cNilClass: VALUE; pub static mut rb_cNumeric: VALUE; pub static mut rb_cString: VALUE; pub static mut rb_cSymbol: VALUE; pub static mut rb_cThread: VALUE; @@ -228,6 +228,50 @@ impl Const { } } /// Print adaptor for [`Const`]. See [`PtrPrintMap`]. struct ConstPrinter<'a> { inner: &'a Const, @@ -330,6 +374,7 @@ pub enum Insn { NewArray { elements: Vec<InsnId>, state: InsnId }, /// NewHash contains a vec of (key, value) pairs NewHash { elements: Vec<(InsnId,InsnId)>, state: InsnId }, ArraySet { array: InsnId, idx: usize, val: InsnId }, ArrayDup { val: InsnId, state: InsnId }, ArrayMax { elements: Vec<InsnId>, state: InsnId }, @@ -439,6 +484,7 @@ impl Insn { Insn::StringCopy { .. } => false, Insn::NewArray { .. } => false, Insn::NewHash { .. } => false, Insn::ArrayDup { .. } => false, Insn::HashDup { .. } => false, Insn::Test { .. } => false, @@ -490,6 +536,9 @@ impl<'a> std::fmt::Display for InsnPrinter<'a> { } Ok(()) } Insn::ArrayMax { elements, .. } => { write!(f, "ArrayMax")?; let mut prefix = " "; @@ -912,6 +961,7 @@ impl Function { } NewHash { elements: found_elements, state: find!(state) } } ArrayMax { elements, state } => ArrayMax { elements: find_vec!(*elements), state: find!(*state) }, &GetIvar { self_val, id, state } => GetIvar { self_val: find!(self_val), id, state }, &SetIvar { self_val, id, val, state } => SetIvar { self_val: find!(self_val), id, val, state }, @@ -972,6 +1022,7 @@ impl Function { Insn::ArrayDup { .. } => types::ArrayExact, Insn::NewHash { .. } => types::HashExact, Insn::HashDup { .. } => types::HashExact, Insn::CCall { return_type, .. } => *return_type, Insn::GuardType { val, guard_type, .. } => self.type_of(*val).intersection(*guard_type), Insn::GuardBitEquals { val, expected, .. } => self.type_of(*val).intersection(Type::from_value(*expected)), @@ -1486,6 +1537,11 @@ impl Function { } worklist.push_back(state); } Insn::StringCopy { val } | Insn::StringIntern { val } | Insn::Return { val } @@ -2342,6 +2398,14 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> { let val = state.stack_pop()?; fun.push_insn(block, Insn::SetIvar { self_val, id, val, state: exit_id }); } _ => { // Unknown opcode; side-exit into the interpreter let exit_id = fun.push_insn(block, Insn::Snapshot { state: exit_state }); @@ -2736,6 +2800,52 @@ mod tests { } #[test] fn test_array_dup() { eval("def test = [1, 2, 3]"); assert_method_hir_with_opcode("test", YARVINSN_duparray, expect![[r#" @@ -4274,6 +4384,23 @@ mod opt_tests { } #[test] fn test_eliminate_new_array_with_elements() { eval(" def test(a) @@ -71,6 +71,7 @@ end base_type "String" base_type "Array" base_type "Hash" (integer, integer_exact) = base_type "Integer" # CRuby partitions Integer into immediate and non-immediate variants. @@ -9,7 +9,7 @@ mod bits { pub const BasicObjectSubclass: u64 = 1u64 << 3; pub const Bignum: u64 = 1u64 << 4; pub const BoolExact: u64 = FalseClassExact | TrueClassExact; - pub const BuiltinExact: u64 = ArrayExact | BasicObjectExact | FalseClassExact | FloatExact | HashExact | IntegerExact | NilClassExact | ObjectExact | StringExact | SymbolExact | TrueClassExact; pub const CBool: u64 = 1u64 << 5; pub const CDouble: u64 = 1u64 << 6; pub const CInt: u64 = CSigned | CUnsigned; @@ -48,23 +48,26 @@ mod bits { pub const NilClass: u64 = NilClassExact | NilClassSubclass; pub const NilClassExact: u64 = 1u64 << 28; pub const NilClassSubclass: u64 = 1u64 << 29; - pub const Object: u64 = Array | FalseClass | Float | Hash | Integer | NilClass | ObjectExact | ObjectSubclass | String | Symbol | TrueClass; pub const ObjectExact: u64 = 1u64 << 30; pub const ObjectSubclass: u64 = 1u64 << 31; pub const RubyValue: u64 = BasicObject | CallableMethodEntry | Undef; - pub const StaticSymbol: u64 = 1u64 << 32; pub const String: u64 = StringExact | StringSubclass; - pub const StringExact: u64 = 1u64 << 33; - pub const StringSubclass: u64 = 1u64 << 34; - pub const Subclass: u64 = ArraySubclass | BasicObjectSubclass | FalseClassSubclass | FloatSubclass | HashSubclass | IntegerSubclass | NilClassSubclass | ObjectSubclass | StringSubclass | SymbolSubclass | TrueClassSubclass; pub const Symbol: u64 = SymbolExact | SymbolSubclass; pub const SymbolExact: u64 = DynamicSymbol | StaticSymbol; - pub const SymbolSubclass: u64 = 1u64 << 35; pub const TrueClass: u64 = TrueClassExact | TrueClassSubclass; - pub const TrueClassExact: u64 = 1u64 << 36; - pub const TrueClassSubclass: u64 = 1u64 << 37; - pub const Undef: u64 = 1u64 << 38; - pub const AllBitPatterns: [(&'static str, u64); 64] = [ ("Any", Any), ("RubyValue", RubyValue), ("Immediate", Immediate), @@ -84,6 +87,9 @@ mod bits { ("StringExact", StringExact), ("SymbolExact", SymbolExact), ("StaticSymbol", StaticSymbol), ("ObjectSubclass", ObjectSubclass), ("ObjectExact", ObjectExact), ("NilClass", NilClass), @@ -130,7 +136,7 @@ mod bits { ("ArrayExact", ArrayExact), ("Empty", Empty), ]; - pub const NumTypeBits: u64 = 39; } pub mod types { use super::*; @@ -185,6 +191,9 @@ pub mod types { pub const Object: Type = Type::from_bits(bits::Object); pub const ObjectExact: Type = Type::from_bits(bits::ObjectExact); pub const ObjectSubclass: Type = Type::from_bits(bits::ObjectSubclass); pub const RubyValue: Type = Type::from_bits(bits::RubyValue); pub const StaticSymbol: Type = Type::from_bits(bits::StaticSymbol); pub const String: Type = Type::from_bits(bits::String); @@ -1,6 +1,6 @@ #![allow(non_upper_case_globals)] use crate::cruby::{Qfalse, Qnil, Qtrue, VALUE, RUBY_T_ARRAY, RUBY_T_STRING, RUBY_T_HASH}; -use crate::cruby::{rb_cInteger, rb_cFloat, rb_cArray, rb_cHash, rb_cString, rb_cSymbol, rb_cObject, rb_cTrueClass, rb_cFalseClass, rb_cNilClass}; use crate::cruby::ClassRelationship; use crate::cruby::get_class_name; use crate::hir::PtrPrintMap; @@ -137,6 +137,10 @@ fn is_hash_exact(val: VALUE) -> bool { val.class_of() == unsafe { rb_cHash } || (val.class_of() == VALUE(0) && val.builtin_type() == RUBY_T_HASH) } impl Type { /// Create a `Type` from the given integer. pub const fn fixnum(val: i64) -> Type { @@ -183,6 +187,9 @@ impl Type { else if is_hash_exact(val) { Type { bits: bits::HashExact, spec: Specialization::Object(val) } } else if is_string_exact(val) { Type { bits: bits::StringExact, spec: Specialization::Object(val) } } @@ -277,6 +284,7 @@ impl Type { if class == unsafe { rb_cInteger } { return true; } if class == unsafe { rb_cNilClass } { return true; } if class == unsafe { rb_cObject } { return true; } if class == unsafe { rb_cString } { return true; } if class == unsafe { rb_cSymbol } { return true; } if class == unsafe { rb_cTrueClass } { return true; } @@ -383,6 +391,7 @@ impl Type { if self.is_subtype(types::IntegerExact) { return Some(unsafe { rb_cInteger }); } if self.is_subtype(types::NilClassExact) { return Some(unsafe { rb_cNilClass }); } if self.is_subtype(types::ObjectExact) { return Some(unsafe { rb_cObject }); } if self.is_subtype(types::StringExact) { return Some(unsafe { rb_cString }); } if self.is_subtype(types::SymbolExact) { return Some(unsafe { rb_cSymbol }); } if self.is_subtype(types::TrueClassExact) { return Some(unsafe { rb_cTrueClass }); } |