diff options
Diffstat (limited to 'zjit/src')
| -rw-r--r-- | zjit/src/codegen.rs | 12 | ||||
| -rw-r--r-- | zjit/src/hir.rs | 40 |
2 files changed, 47 insertions, 5 deletions
diff --git a/zjit/src/codegen.rs b/zjit/src/codegen.rs index 34d34b1c5e..58a5a6d5fa 100644 --- a/zjit/src/codegen.rs +++ b/zjit/src/codegen.rs @@ -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), + Insn::AnyToString { val, str, state } => gen_anytostring(asm, opnd!(val), opnd!(str), &function.frame_state(*state))?, _ => { 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())) } +fn gen_anytostring(asm: &mut Assembler, val: lir::Opnd, str: lir::Opnd, state: &FrameState) -> Option<lir::Opnd> { + // Save PC + gen_save_pc(asm, state); + + asm_comment!(asm, "call rb_obj_as_string_result"); + Some(asm.ccall( + rb_obj_as_string_result as *const u8, + vec![str, val], + )) +} + /// Evaluate if a value is truthy /// Produces a CBool type (0 or 1) /// In Ruby, only nil and false are falsy diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs index 662a523364..f41a39e188 100644 --- a/zjit/src/hir.rs +++ b/zjit/src/hir.rs @@ -498,6 +498,7 @@ pub enum Insn { // Distinct from `SendWithoutBlock` with `mid:to_s` because does not have a patch point for String to_s being redefined ObjToString { val: InsnId, call_info: CallInfo, cd: *const rb_call_data, state: InsnId }, + AnyToString { val: InsnId, str: InsnId, state: InsnId }, /// Side-exit if val doesn't have the expected type. GuardType { val: InsnId, guard_type: Type, state: InsnId }, @@ -699,6 +700,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::AnyToString { val, str, .. } => { write!(f, "AnyToString {val}, str: {str}") }, Insn::SideExit { .. } => write!(f, "SideExit"), Insn::PutSpecialObject { value_type } => { write!(f, "PutSpecialObject {}", value_type) @@ -1023,6 +1025,11 @@ impl Function { cd: *cd, state: *state, }, + AnyToString { val, str, state } => AnyToString { + val: find!(*val), + str: find!(*str), + state: *state, + }, SendWithoutBlock { self_val, call_info, cd, args, state } => SendWithoutBlock { self_val: find!(*self_val), call_info: call_info.clone(), @@ -1154,6 +1161,7 @@ impl Function { Insn::ToNewArray { .. } => types::ArrayExact, Insn::ToArray { .. } => types::ArrayExact, Insn::ObjToString { .. } => types::BasicObject, + Insn::AnyToString { .. } => types::String, } } @@ -1398,7 +1406,7 @@ impl Function { self.make_equal_to(insn_id, replacement); } Insn::ObjToString { val, call_info, cd, state, .. } => { - if self.is_a(val, types::StringExact) { + if self.is_a(val, types::String) { // behaves differently from `SendWithoutBlock` with `mid:to_s` because ObjToString should not have a patch point for String to_s being redefined self.make_equal_to(insn_id, val); } else { @@ -1406,6 +1414,13 @@ impl Function { self.make_equal_to(insn_id, replacement) } } + Insn::AnyToString { str, .. } => { + if self.is_a(str, types::String) { + self.make_equal_to(insn_id, str); + } else { + self.push_insn_id(block, insn_id); + } + } _ => { self.push_insn_id(block, insn_id); } } } @@ -1782,6 +1797,11 @@ impl Function { worklist.push_back(val); worklist.push_back(state); } + Insn::AnyToString { val, str, state, .. } => { + worklist.push_back(val); + worklist.push_back(str); + worklist.push_back(state); + } Insn::GetGlobal { state, .. } | Insn::SideExit { state } => worklist.push_back(state), } @@ -2783,6 +2803,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) } + YARVINSN_anytostring => { + let str = state.stack_pop()?; + let val = state.stack_pop()?; + + let exit_id = fun.push_insn(block, Insn::Snapshot { state: exit_state }); + let anytostring = fun.push_insn(block, Insn::AnyToString { val, str, state: exit_id }); + state.stack_push(anytostring); + } _ => { // Unknown opcode; side-exit into the interpreter let exit_id = fun.push_insn(block, Insn::Snapshot { state: exit_state }); @@ -4452,7 +4480,7 @@ mod tests { } #[test] - fn test_objtostring() { + fn test_objtostring_anytostring() { eval(" def test = \"#{1}\" "); @@ -4462,6 +4490,7 @@ mod tests { v2:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) v3:Fixnum[1] = Const Value(1) v5:BasicObject = ObjToString v3 + v7:String = AnyToString v3, str: v5 SideExit "#]]); } @@ -6010,7 +6039,7 @@ mod opt_tests { } #[test] - fn test_objtostring_string() { + fn test_objtostring_anytostring_string() { eval(r##" def test = "#{('foo')}" "##); @@ -6025,7 +6054,7 @@ mod opt_tests { } #[test] - fn test_objtostring_with_non_string() { + fn test_objtostring_anytostring_with_non_string() { eval(r##" def test = "#{1}" "##); @@ -6034,7 +6063,8 @@ 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 + v10:BasicObject = SendWithoutBlock v3, :to_s + v7:String = AnyToString v3, str: v10 SideExit "#]]); } |
