Skip to content

Commit 4ebb947

Browse files
committed
ZJIT: Implement ToArray and ArrayExtend
- ToArray is used in both `concattoarray` and `splatarray` instructions to convert a value to an array OR put the value in an array - ArrayExtend is used in `concattoarray` to extend `ary1` with `ary2` With this change, ZJIT can actually execute `concattoarray` and `splatarray` instructions.
1 parent 75f25e5 commit 4ebb947

4 files changed

Lines changed: 43 additions & 2 deletions

File tree

test/ruby/test_zjit.rb

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -489,6 +489,21 @@ def test = [1,2,3]
489489
}
490490
end
491491

492+
def test_array_splat
493+
assert_compiles '"foo"', %q{
494+
def foo(b) = b
495+
def test(a) = foo(*a)
496+
test("foo")
497+
}, insns: [:splatarray]
498+
end
499+
500+
def test_array_concat_implicit
501+
assert_compiles '[1, 2, 3]', %q{
502+
def test(a) = [1, *a]
503+
test([2, 3])
504+
}, insns: [:concattoarray]
505+
end
506+
492507
def test_new_range_inclusive
493508
assert_compiles '1..5', %q{
494509
def test(a, b) = a..b

zjit/bindgen/src/main.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ fn main() {
118118
.allowlist_function("rb_obj_is_kind_of")
119119
.allowlist_function("rb_obj_frozen_p")
120120
.allowlist_function("rb_class_inherited_p")
121+
.allowlist_function("rb_Array")
121122

122123
// From ruby/internal/encoding/encoding.h
123124
.allowlist_type("ruby_encoding_consts")
@@ -149,6 +150,7 @@ fn main() {
149150
.allowlist_function("rb_ary_dup")
150151
.allowlist_function("rb_ary_push")
151152
.allowlist_function("rb_ary_unshift_m")
153+
.allowlist_function("rb_ary_concat")
152154

153155
// From internal/array.h
154156
.allowlist_function("rb_ec_ary_new_from_values")

zjit/src/codegen.rs

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -367,7 +367,8 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio
367367
Insn::AnyToString { val, str, state } => gen_anytostring(asm, opnd!(val), opnd!(str), &function.frame_state(*state))?,
368368
Insn::Defined { op_type, obj, pushval, v } => gen_defined(jit, asm, *op_type, *obj, *pushval, opnd!(v))?,
369369
&Insn::IncrCounter(counter) => return Some(gen_incr_counter(asm, counter)),
370-
Insn::ArrayExtend { .. }
370+
Insn::ToArray { val, state } => gen_to_array(asm, opnd!(val), &function.frame_state(*state))?,
371+
Insn::ArrayExtend { left, right, state } => return gen_array_extend(asm, opnd!(left), opnd!(right), &function.frame_state(*state)),
371372
| Insn::ArrayMax { .. }
372373
| Insn::ArrayPush { .. }
373374
| Insn::DefinedIvar { .. }
@@ -379,7 +380,6 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio
379380
| Insn::Send { .. }
380381
| Insn::StringIntern { .. }
381382
| Insn::Throw { .. }
382-
| Insn::ToArray { .. }
383383
| Insn::ToNewArray { .. }
384384
| Insn::Const { .. }
385385
=> {
@@ -1042,6 +1042,28 @@ fn gen_anytostring(asm: &mut Assembler, val: lir::Opnd, str: lir::Opnd, state: &
10421042
Some(asm_ccall!(asm, rb_obj_as_string_result, str, val))
10431043
}
10441044

1045+
fn gen_to_array(asm: &mut Assembler, val: lir::Opnd, state: &FrameState) -> Option<lir::Opnd> {
1046+
// Save PC
1047+
gen_save_pc(asm, state);
1048+
1049+
// ToArray is used in both concattoarray and splatarray instructions
1050+
// It needs to satisfy the following conditions:
1051+
// - Check if the value is already an array. If it is, return it
1052+
// - Check if the value can be converted into an array. If it is, return it
1053+
// - Return an array with just the value in it
1054+
//
1055+
// rb_Array satisfies the above conditions, so we can use it here
1056+
Some(asm_ccall!(asm, rb_Array, val))
1057+
}
1058+
1059+
fn gen_array_extend(asm: &mut Assembler, left: lir::Opnd, right: lir::Opnd, state: &FrameState) -> Option<()> {
1060+
// Save PC
1061+
gen_save_pc(asm, state);
1062+
1063+
asm_ccall!(asm, rb_ary_concat, left, right);
1064+
Some(())
1065+
}
1066+
10451067
/// Evaluate if a value is truthy
10461068
/// Produces a CBool type (0 or 1)
10471069
/// In Ruby, only nil and false are falsy

zjit/src/cruby_bindings.inc.rs

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)