summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Colson <danieljamescolson@gmail.com>2025-06-10 20:34:41 -0400
committerMax Bernstein <tekknolagi@gmail.com>2025-06-17 08:20:07 +0900
commitb1410c1c75518a54a2a32e0da2555840258ce228 (patch)
treea9748df192025a515db01311dd4ca61ef854e55a
parent2956573b09ec78d7735a07fe3d7b2dcc907879fb (diff)
ZJIT: Add codegen for StringCopy
Prior to this commit we compiled `putstring` and `putchilledstring` to `StringCopy`, but then failed to compile past HIR. This commit adds codegen for `StringCopy` to call `rb_ec_str_ressurrect` as the VM does for these instructions.
Notes
Notes: Merged: https://github.com/ruby/ruby/pull/13625
-rw-r--r--test/ruby/test_zjit.rb14
-rw-r--r--zjit/src/codegen.rs12
-rw-r--r--zjit/src/hir.rs18
3 files changed, 37 insertions, 7 deletions
diff --git a/test/ruby/test_zjit.rb b/test/ruby/test_zjit.rb
index 7b582df2f5..d7249053e5 100644
--- a/test/ruby/test_zjit.rb
+++ b/test/ruby/test_zjit.rb
@@ -31,6 +31,20 @@ class TestZJIT < Test::Unit::TestCase
}
end
+ def test_putstring
+ assert_compiles '""', %q{
+ def test = "#{""}"
+ test
+ }, insns: [:putstring]
+ end
+
+ def test_putchilldedstring
+ assert_compiles '""', %q{
+ def test = ""
+ test
+ }, insns: [:putchilledstring]
+ end
+
def test_leave_param
assert_compiles '5', %q{
def test(n) = n
diff --git a/zjit/src/codegen.rs b/zjit/src/codegen.rs
index 32ea9e1c15..286f3f39b4 100644
--- a/zjit/src/codegen.rs
+++ b/zjit/src/codegen.rs
@@ -252,6 +252,7 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio
Insn::NewArray { elements, state } => gen_new_array(jit, asm, elements, &function.frame_state(*state)),
Insn::NewRange { low, high, flag, state } => gen_new_range(asm, opnd!(low), opnd!(high), *flag, &function.frame_state(*state)),
Insn::ArrayDup { val, state } => gen_array_dup(asm, opnd!(val), &function.frame_state(*state)),
+ Insn::StringCopy { val, chilled } => gen_string_copy(asm, opnd!(val), *chilled),
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
Insn::Jump(branch) => return gen_jump(jit, asm, branch),
@@ -611,6 +612,17 @@ fn gen_send_without_block_direct(
Some(ret)
}
+/// Compile a string resurrection
+fn gen_string_copy(asm: &mut Assembler, recv: Opnd, chilled: bool) -> Opnd {
+ asm_comment!(asm, "call rb_ec_str_resurrect");
+ // TODO: split rb_ec_str_resurrect into separate functions
+ let chilled = if chilled { Opnd::Imm(1) } else { Opnd::Imm(0) };
+ asm.ccall(
+ rb_ec_str_resurrect as *const u8,
+ vec![EC, recv, chilled],
+ )
+}
+
/// Compile an array duplication instruction
fn gen_array_dup(
asm: &mut Assembler,
diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs
index 17b4aad6cf..c67f25451a 100644
--- a/zjit/src/hir.rs
+++ b/zjit/src/hir.rs
@@ -396,7 +396,7 @@ pub enum Insn {
/// SSA block parameter. Also used for function parameters in the function's entry block.
Param { idx: usize },
- StringCopy { val: InsnId },
+ StringCopy { val: InsnId, chilled: bool },
StringIntern { val: InsnId },
/// Put special object (VMCORE, CBASE, etc.) based on value_type
@@ -602,7 +602,7 @@ impl<'a> std::fmt::Display for InsnPrinter<'a> {
Insn::ArraySet { array, idx, val } => { write!(f, "ArraySet {array}, {idx}, {val}") }
Insn::ArrayDup { val, .. } => { write!(f, "ArrayDup {val}") }
Insn::HashDup { val, .. } => { write!(f, "HashDup {val}") }
- Insn::StringCopy { val } => { write!(f, "StringCopy {val}") }
+ Insn::StringCopy { val, .. } => { write!(f, "StringCopy {val}") }
Insn::Test { val } => { write!(f, "Test {val}") }
Insn::IsNil { val } => { write!(f, "IsNil {val}") }
Insn::Jump(target) => { write!(f, "Jump {target}") }
@@ -978,7 +978,7 @@ impl Function {
}
},
Return { val } => Return { val: find!(*val) },
- StringCopy { val } => StringCopy { val: find!(*val) },
+ StringCopy { val, chilled } => StringCopy { val: find!(*val), chilled: *chilled },
StringIntern { val } => StringIntern { val: find!(*val) },
Test { val } => Test { val: find!(*val) },
&IsNil { val } => IsNil { val: find!(val) },
@@ -1623,7 +1623,7 @@ impl Function {
worklist.push_back(high);
worklist.push_back(state);
}
- Insn::StringCopy { val }
+ Insn::StringCopy { val, .. }
| Insn::StringIntern { val }
| Insn::Return { val }
| Insn::Defined { v: val, .. }
@@ -2142,10 +2142,14 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
let value_type = SpecialObjectType::from(get_arg(pc, 0).as_u32());
state.stack_push(fun.push_insn(block, Insn::PutSpecialObject { value_type }));
}
- YARVINSN_putstring | YARVINSN_putchilledstring => {
- // TODO(max): Do something different for chilled string
+ YARVINSN_putstring => {
let val = fun.push_insn(block, Insn::Const { val: Const::Value(get_arg(pc, 0)) });
- let insn_id = fun.push_insn(block, Insn::StringCopy { val });
+ let insn_id = fun.push_insn(block, Insn::StringCopy { val, chilled: false });
+ state.stack_push(insn_id);
+ }
+ YARVINSN_putchilledstring => {
+ let val = fun.push_insn(block, Insn::Const { val: Const::Value(get_arg(pc, 0)) });
+ let insn_id = fun.push_insn(block, Insn::StringCopy { val, chilled: true });
state.stack_push(insn_id);
}
YARVINSN_putself => { state.stack_push(self_param); }