diff options
-rw-r--r-- | zjit/src/codegen.rs | 12 | ||||
-rw-r--r-- | zjit/src/cruby_bindings.inc.rs | 1 | ||||
-rw-r--r-- | zjit/src/hir.rs | 141 | ||||
-rw-r--r-- | zjit/src/hir_type/mod.rs | 2 |
4 files changed, 144 insertions, 12 deletions
@@ -283,6 +283,7 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio Insn::SetIvar { self_val, id, val, state: _ } => return gen_setivar(asm, opnd!(self_val), *id, opnd!(val)), Insn::SideExit { state } => return gen_side_exit(jit, asm, &function.frame_state(*state)), Insn::PutSpecialObject { value_type } => gen_putspecialobject(asm, *value_type), _ => { debug!("ZJIT: gen_function: unexpected insn {:?}", insn); return None; @@ -814,6 +815,17 @@ fn gen_fixnum_ge(asm: &mut Assembler, left: lir::Opnd, right: lir::Opnd) -> Opti Some(asm.csel_ge(Qtrue.into(), Qfalse.into())) } /// Evaluate if a value is truthy /// Produces a CBool type (0 or 1) /// In Ruby, only nil and false are falsy @@ -1001,4 +1001,5 @@ unsafe extern "C" { pub fn rb_assert_iseq_handle(handle: VALUE); pub fn rb_IMEMO_TYPE_P(imemo: VALUE, imemo_type: imemo_type) -> ::std::os::raw::c_int; pub fn rb_assert_cme_handle(handle: VALUE); } @@ -206,6 +206,7 @@ impl<'a> std::fmt::Display for InvariantPrinter<'a> { BOP_FREEZE => write!(f, "BOP_FREEZE")?, BOP_UMINUS => write!(f, "BOP_UMINUS")?, BOP_MAX => write!(f, "BOP_MAX")?, _ => write!(f, "{bop}")?, } write!(f, ")") @@ -498,6 +499,7 @@ pub enum Insn { // Distinct from `SendWithoutBlock` with `mid:to_s` because does not have a point for String to_s being redefined ObjToString { val: InsnId, call_info: CallInfo, cd: *const rb_call_data, state: InsnId }, /// Side-exit if val doesn't have the expected type. GuardType { val: InsnId, guard_type: Type, state: InsnId }, @@ -699,6 +701,7 @@ impl<'a> std::fmt::Display for InsnPrinter<'a> { Insn::ArrayExtend { left, right, .. } => write!(f, "ArrayExtend {left}, {right}"), Insn::ArrayPush { array, val, .. } => write!(f, "ArrayPush {array}, {val}"), Insn::ObjToString { val, .. } => { write!(f, "ObjToString {val}") }, Insn::SideExit { .. } => write!(f, "SideExit"), Insn::PutSpecialObject { value_type } => { write!(f, "PutSpecialObject {}", value_type) @@ -1023,6 +1026,11 @@ impl Function { cd: *cd, state: *state, }, SendWithoutBlock { self_val, call_info, cd, args, state } => SendWithoutBlock { self_val: find!(*self_val), call_info: call_info.clone(), @@ -1154,6 +1162,7 @@ impl Function { Insn::ToNewArray { .. } => types::ArrayExact, Insn::ToArray { .. } => types::ArrayExact, Insn::ObjToString { .. } => types::BasicObject, } } @@ -1309,6 +1318,25 @@ impl Function { } } /// Rewrite SendWithoutBlock opcodes into SendWithoutBlockDirect opcodes if we know the target /// ISEQ statically. This removes run-time method lookups and opens the door for inlining. fn optimize_direct_sends(&mut self) { @@ -1343,6 +1371,8 @@ impl Function { self.try_rewrite_freeze(block, insn_id, self_val), Insn::SendWithoutBlock { self_val, call_info: CallInfo { method_name }, args, .. } if method_name == "-@" && args.len() == 0 => self.try_rewrite_uminus(block, insn_id, self_val), Insn::SendWithoutBlock { mut self_val, call_info, cd, args, state } => { let frame_state = self.frame_state(state); let (klass, guard_equal_to) = if let Some(klass) = self.type_of(self_val).runtime_exact_ruby_class() { @@ -1398,7 +1428,7 @@ impl Function { self.make_equal_to(insn_id, replacement); } Insn::ObjToString { val, call_info, cd, state, .. } => { - if self.is_a(val, types::StringExact) { // behaves differently from `SendWithoutBlock` with `mid:to_s` because ObjToString should not have a point for String to_s being redefined self.make_equal_to(insn_id, val); } else { @@ -1406,6 +1436,13 @@ impl Function { self.make_equal_to(insn_id, replacement) } } _ => { self.push_insn_id(block, insn_id); } } } @@ -1782,6 +1819,11 @@ impl Function { worklist.push_back(val); worklist.push_back(state); } Insn::GetGlobal { state, .. } | Insn::SideExit { state } => worklist.push_back(state), } @@ -2286,7 +2328,12 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> { YARVINSN_putobject => { state.stack_push(fun.push_insn(block, Insn::Const { val: Const::Value(get_arg(pc, 0)) })); }, YARVINSN_putspecialobject => { let value_type = SpecialObjectType::from(get_arg(pc, 0).as_u32()); - state.stack_push(fun.push_insn(block, Insn::PutSpecialObject { value_type })); } YARVINSN_putstring => { let val = fun.push_insn(block, Insn::Const { val: Const::Value(get_arg(pc, 0)) }); @@ -2783,6 +2830,14 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> { let objtostring = fun.push_insn(block, Insn::ObjToString { val: recv, call_info: CallInfo { method_name }, cd, state: exit_id }); state.stack_push(objtostring) } _ => { // Unknown opcode; side-exit into the interpreter let exit_id = fun.push_insn(block, Insn::Snapshot { state: exit_state }); @@ -3872,11 +3927,11 @@ mod tests { assert_method_hir("test", expect![[r#" fn test: bb0(v0:BasicObject, v1:BasicObject): - v3:BasicObject = PutSpecialObject VMCore v5:HashExact = NewHash v7:BasicObject = SendWithoutBlock v3, :core#hash_merge_kwd, v5, v1 - v8:BasicObject = PutSpecialObject VMCore - v9:StaticSymbol[VALUE(0x1000)] = Const Value(VALUE(0x1000)) v10:Fixnum[1] = Const Value(1) v12:BasicObject = SendWithoutBlock v8, :core#hash_merge_ptr, v7, v9, v10 SideExit @@ -4324,10 +4379,10 @@ mod tests { assert_method_hir_with_opcode("test", YARVINSN_putspecialobject, expect![[r#" fn test: bb0(v0:BasicObject): - v2:BasicObject = PutSpecialObject VMCore v3:BasicObject = PutSpecialObject CBase - v4:StaticSymbol[VALUE(0x1000)] = Const Value(VALUE(0x1000)) - v5:StaticSymbol[VALUE(0x1008)] = Const Value(VALUE(0x1008)) v7:BasicObject = SendWithoutBlock v2, :core#set_method_alias, v3, v4, v5 Return v7 "#]]); @@ -4452,7 +4507,7 @@ mod tests { } #[test] - fn test_objtostring() { eval(" def test = \"#{1}\" "); @@ -4462,6 +4517,7 @@ mod tests { v2:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) v3:Fixnum[1] = Const Value(1) v5:BasicObject = ObjToString v3 SideExit "#]]); } @@ -6010,7 +6066,7 @@ mod opt_tests { } #[test] - fn test_objtostring_string() { eval(r##" def test = "#{('foo')}" "##); @@ -6025,7 +6081,7 @@ mod opt_tests { } #[test] - fn test_objtostring_with_non_string() { eval(r##" def test = "#{1}" "##); @@ -6034,8 +6090,69 @@ mod opt_tests { bb0(v0:BasicObject): v2:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) v3:Fixnum[1] = Const Value(1) - v8:BasicObject = SendWithoutBlock v3, :to_s SideExit "#]]); } } @@ -3,6 +3,7 @@ use crate::cruby::{Qfalse, Qnil, Qtrue, VALUE, RUBY_T_ARRAY, RUBY_T_STRING, RUBY use crate::cruby::{rb_cInteger, rb_cFloat, rb_cArray, rb_cHash, rb_cString, rb_cSymbol, rb_cObject, rb_cTrueClass, rb_cFalseClass, rb_cNilClass, rb_cRange}; use crate::cruby::ClassRelationship; use crate::cruby::get_class_name; use crate::hir::PtrPrintMap; #[derive(Copy, Clone, Debug, PartialEq)] @@ -68,6 +69,7 @@ fn write_spec(f: &mut std::fmt::Formatter, printer: &TypePrinter) -> std::fmt::R let ty = printer.inner; match ty.spec { Specialization::Any | Specialization::Empty => { Ok(()) }, Specialization::Object(val) => write!(f, "[{}]", val.print(printer.ptr_map)), Specialization::Type(val) => write!(f, "[class:{}]", get_class_name(val)), Specialization::TypeExact(val) => write!(f, "[class_exact:{}]", get_class_name(val)), |