Skip to content

Commit a093af0

Browse files
committed
Add newrange support to zjit
1 parent 5f24741 commit a093af0

7 files changed

Lines changed: 167 additions & 14 deletions

File tree

test/ruby/test_zjit.rb

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,27 @@ def test = [1,2,3]
227227
}
228228
end
229229

230+
def test_new_range_inclusive
231+
assert_compiles '1..5', %q{
232+
def test(a, b) = a..b
233+
test(1, 5)
234+
}
235+
end
236+
237+
def test_new_range_exclusive
238+
assert_compiles '1...5', %q{
239+
def test(a, b) = a...b
240+
test(1, 5)
241+
}
242+
end
243+
244+
def test_new_range_with_literal
245+
assert_compiles '3..10', %q{
246+
def test(n) = n..10
247+
test(3)
248+
}
249+
end
250+
230251
def test_if
231252
assert_compiles '[0, nil]', %q{
232253
def test(n)

zjit/src/codegen.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,7 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio
251251
Insn::PutSelf => gen_putself(),
252252
Insn::Const { val: Const::Value(val) } => gen_const(*val),
253253
Insn::NewArray { elements, state } => gen_new_array(jit, asm, elements, &function.frame_state(*state)),
254+
Insn::NewRange { low, high, flag, state } => gen_new_range(asm, opnd!(low), opnd!(high), *flag, &function.frame_state(*state)),
254255
Insn::ArrayDup { val, state } => gen_array_dup(asm, opnd!(val), &function.frame_state(*state)),
255256
Insn::Param { idx } => unreachable!("block.insns should not have Insn::Param({idx})"),
256257
Insn::Snapshot { .. } => return Some(()), // we don't need to do anything for this instruction at the moment
@@ -550,6 +551,28 @@ fn gen_new_array(
550551
new_array
551552
}
552553

554+
/// Compile a new range instruction
555+
fn gen_new_range(
556+
asm: &mut Assembler,
557+
low: lir::Opnd,
558+
high: lir::Opnd,
559+
flag: u32,
560+
state: &FrameState,
561+
) -> lir::Opnd {
562+
asm_comment!(asm, "call rb_range_new");
563+
564+
// Save PC
565+
gen_save_pc(asm, state);
566+
567+
// Call rb_range_new(low, high, flag)
568+
let new_range = asm.ccall(
569+
rb_range_new as *const u8,
570+
vec![low, high, lir::Opnd::Imm(flag as i64)],
571+
);
572+
573+
new_range
574+
}
575+
553576
/// Compile code that exits from JIT code with a return value
554577
fn gen_return(asm: &mut Assembler, val: lir::Opnd) -> Option<()> {
555578
// Pop the current frame (ec->cfp++)

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.

zjit/src/hir.rs

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,7 @@ pub enum Insn {
334334
NewArray { elements: Vec<InsnId>, state: InsnId },
335335
/// NewHash contains a vec of (key, value) pairs
336336
NewHash { elements: Vec<(InsnId,InsnId)>, state: InsnId },
337+
NewRange { low: InsnId, high: InsnId, flag: u32, state: InsnId },
337338
ArraySet { array: InsnId, idx: usize, val: InsnId },
338339
ArrayDup { val: InsnId, state: InsnId },
339340
ArrayMax { elements: Vec<InsnId>, state: InsnId },
@@ -443,6 +444,7 @@ impl Insn {
443444
Insn::StringCopy { .. } => false,
444445
Insn::NewArray { .. } => false,
445446
Insn::NewHash { .. } => false,
447+
Insn::NewRange { .. } => false,
446448
Insn::ArrayDup { .. } => false,
447449
Insn::HashDup { .. } => false,
448450
Insn::Test { .. } => false,
@@ -494,6 +496,14 @@ impl<'a> std::fmt::Display for InsnPrinter<'a> {
494496
}
495497
Ok(())
496498
}
499+
Insn::NewRange { low, high, flag, .. } => {
500+
let flag_str = match *flag {
501+
0 => "..",
502+
1 => "...",
503+
_ => &format!("flag({})", flag),
504+
};
505+
write!(f, "NewRange {low} {flag_str} {high}")
506+
}
497507
Insn::ArrayMax { elements, .. } => {
498508
write!(f, "ArrayMax")?;
499509
let mut prefix = " ";
@@ -916,6 +926,7 @@ impl Function {
916926
}
917927
NewHash { elements: found_elements, state: find!(state) }
918928
}
929+
&NewRange { low, high, flag, state } => NewRange { low: find!(low), high: find!(high), flag, state },
919930
ArrayMax { elements, state } => ArrayMax { elements: find_vec!(*elements), state: find!(*state) },
920931
&GetIvar { self_val, id, state } => GetIvar { self_val: find!(self_val), id, state },
921932
&SetIvar { self_val, id, val, state } => SetIvar { self_val: find!(self_val), id, val, state },
@@ -976,6 +987,7 @@ impl Function {
976987
Insn::ArrayDup { .. } => types::ArrayExact,
977988
Insn::NewHash { .. } => types::HashExact,
978989
Insn::HashDup { .. } => types::HashExact,
990+
Insn::NewRange { .. } => types::RangeExact,
979991
Insn::CCall { return_type, .. } => *return_type,
980992
Insn::GuardType { val, guard_type, .. } => self.type_of(*val).intersection(*guard_type),
981993
Insn::GuardBitEquals { val, expected, .. } => self.type_of(*val).intersection(Type::from_value(*expected)),
@@ -1490,6 +1502,11 @@ impl Function {
14901502
}
14911503
worklist.push_back(state);
14921504
}
1505+
Insn::NewRange { low, high, state, .. } => {
1506+
worklist.push_back(low);
1507+
worklist.push_back(high);
1508+
worklist.push_back(state);
1509+
}
14931510
Insn::StringCopy { val }
14941511
| Insn::StringIntern { val }
14951512
| Insn::Return { val }
@@ -2316,6 +2333,14 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
23162333
let val = state.stack_pop()?;
23172334
fun.push_insn(block, Insn::SetIvar { self_val, id, val, state: exit_id });
23182335
}
2336+
YARVINSN_newrange => {
2337+
let flag = get_arg(pc, 0).as_u32();
2338+
let high = state.stack_pop()?;
2339+
let low = state.stack_pop()?;
2340+
let exit_id = fun.push_insn(block, Insn::Snapshot { state: exit_state });
2341+
let insn_id = fun.push_insn(block, Insn::NewRange { low, high, flag, state: exit_id });
2342+
state.stack_push(insn_id);
2343+
}
23192344
_ => {
23202345
// Unknown opcode; side-exit into the interpreter
23212346
let exit_id = fun.push_insn(block, Insn::Snapshot { state: exit_state });
@@ -2709,6 +2734,52 @@ mod tests {
27092734
"#]]);
27102735
}
27112736

2737+
#[test]
2738+
fn test_new_range_with_one_element() {
2739+
eval("def test(a) = (a..10)");
2740+
assert_method_hir_with_opcode("test", YARVINSN_newrange, expect![[r#"
2741+
fn test:
2742+
bb0(v0:BasicObject):
2743+
v2:Fixnum[10] = Const Value(10)
2744+
v4:RangeExact = NewRange v0 .. v2
2745+
Return v4
2746+
"#]]);
2747+
}
2748+
2749+
#[test]
2750+
fn test_new_range_with_two_elements() {
2751+
eval("def test(a, b) = (a..b)");
2752+
assert_method_hir_with_opcode("test", YARVINSN_newrange, expect![[r#"
2753+
fn test:
2754+
bb0(v0:BasicObject, v1:BasicObject):
2755+
v4:RangeExact = NewRange v0 .. v1
2756+
Return v4
2757+
"#]]);
2758+
}
2759+
2760+
#[test]
2761+
fn test_new_range_exclude_with_one_element() {
2762+
eval("def test(a) = (a...10)");
2763+
assert_method_hir_with_opcode("test", YARVINSN_newrange, expect![[r#"
2764+
fn test:
2765+
bb0(v0:BasicObject):
2766+
v2:Fixnum[10] = Const Value(10)
2767+
v4:RangeExact = NewRange v0 ... v2
2768+
Return v4
2769+
"#]]);
2770+
}
2771+
2772+
#[test]
2773+
fn test_new_range_exclude_with_two_elements() {
2774+
eval("def test(a, b) = (a...b)");
2775+
assert_method_hir_with_opcode("test", YARVINSN_newrange, expect![[r#"
2776+
fn test:
2777+
bb0(v0:BasicObject, v1:BasicObject):
2778+
v4:RangeExact = NewRange v0 ... v1
2779+
Return v4
2780+
"#]]);
2781+
}
2782+
27122783
#[test]
27132784
fn test_array_dup() {
27142785
eval("def test = [1, 2, 3]");
@@ -4233,6 +4304,23 @@ mod opt_tests {
42334304
"#]]);
42344305
}
42354306

4307+
#[test]
4308+
fn test_eliminate_new_range() {
4309+
eval("
4310+
def test()
4311+
c = (1..2)
4312+
5
4313+
end
4314+
test; test
4315+
");
4316+
assert_optimized_method_hir("test", expect![[r#"
4317+
fn test:
4318+
bb0():
4319+
v3:Fixnum[5] = Const Value(5)
4320+
Return v3
4321+
"#]]);
4322+
}
4323+
42364324
#[test]
42374325
fn test_eliminate_new_array_with_elements() {
42384326
eval("

zjit/src/hir_type/gen_hir_type.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ def base_type name
7171
base_type "String"
7272
base_type "Array"
7373
base_type "Hash"
74+
base_type "Range"
7475

7576
(integer, integer_exact) = base_type "Integer"
7677
# CRuby partitions Integer into immediate and non-immediate variants.

zjit/src/hir_type/hir_type.inc.rs

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

zjit/src/hir_type/mod.rs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#![allow(non_upper_case_globals)]
2-
use crate::cruby::{Qfalse, Qnil, Qtrue, VALUE, RUBY_T_ARRAY, RUBY_T_STRING, RUBY_T_HASH};
3-
use crate::cruby::{rb_cInteger, rb_cFloat, rb_cArray, rb_cHash, rb_cString, rb_cSymbol, rb_cObject, rb_cTrueClass, rb_cFalseClass, rb_cNilClass};
2+
use crate::cruby::{Qfalse, Qnil, Qtrue, VALUE, RUBY_T_ARRAY, RUBY_T_STRING, RUBY_T_HASH, RUBY_T_RANGE};
3+
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};
44
use crate::cruby::ClassRelationship;
55
use crate::cruby::get_class_name;
66
use crate::hir::PtrPrintMap;
@@ -137,6 +137,10 @@ fn is_hash_exact(val: VALUE) -> bool {
137137
val.class_of() == unsafe { rb_cHash } || (val.class_of() == VALUE(0) && val.builtin_type() == RUBY_T_HASH)
138138
}
139139

140+
fn is_range_exact(val: VALUE) -> bool {
141+
val.class_of() == unsafe { rb_cRange } || (val.class_of() == VALUE(0) && val.builtin_type() == RUBY_T_RANGE)
142+
}
143+
140144
impl Type {
141145
/// Create a `Type` from the given integer.
142146
pub const fn fixnum(val: i64) -> Type {
@@ -183,6 +187,9 @@ impl Type {
183187
else if is_hash_exact(val) {
184188
Type { bits: bits::HashExact, spec: Specialization::Object(val) }
185189
}
190+
else if is_range_exact(val) {
191+
Type { bits: bits::RangeExact, spec: Specialization::Object(val) }
192+
}
186193
else if is_string_exact(val) {
187194
Type { bits: bits::StringExact, spec: Specialization::Object(val) }
188195
}
@@ -277,6 +284,7 @@ impl Type {
277284
if class == unsafe { rb_cInteger } { return true; }
278285
if class == unsafe { rb_cNilClass } { return true; }
279286
if class == unsafe { rb_cObject } { return true; }
287+
if class == unsafe { rb_cRange } { return true; }
280288
if class == unsafe { rb_cString } { return true; }
281289
if class == unsafe { rb_cSymbol } { return true; }
282290
if class == unsafe { rb_cTrueClass } { return true; }
@@ -383,6 +391,7 @@ impl Type {
383391
if self.is_subtype(types::IntegerExact) { return Some(unsafe { rb_cInteger }); }
384392
if self.is_subtype(types::NilClassExact) { return Some(unsafe { rb_cNilClass }); }
385393
if self.is_subtype(types::ObjectExact) { return Some(unsafe { rb_cObject }); }
394+
if self.is_subtype(types::RangeExact) { return Some(unsafe { rb_cRange }); }
386395
if self.is_subtype(types::StringExact) { return Some(unsafe { rb_cString }); }
387396
if self.is_subtype(types::SymbolExact) { return Some(unsafe { rb_cSymbol }); }
388397
if self.is_subtype(types::TrueClassExact) { return Some(unsafe { rb_cTrueClass }); }

0 commit comments

Comments
 (0)