Skip to content

Commit 2d7f5f3

Browse files
authored
Merge branch 'master' into zjit-more-opt-send-specializeds
2 parents 082d56d + 94ba62c commit 2d7f5f3

3 files changed

Lines changed: 70 additions & 7 deletions

File tree

zjit/bindgen/src/main.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,7 @@ fn main() {
368368
.allowlist_function("rb_iseqw_to_iseq")
369369
.allowlist_function("rb_iseq_label")
370370
.allowlist_function("rb_iseq_line_no")
371+
.allowlist_function("rb_iseq_defined_string")
371372
.allowlist_type("defined_type")
372373

373374
// From builtin.h

zjit/src/cruby_bindings.inc.rs

Lines changed: 1 addition & 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: 68 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,9 @@ pub enum Insn {
397397
GetIvar { self_val: InsnId, id: ID, state: InsnId },
398398
/// Set `self_val`'s instance variable `id` to `val`
399399
SetIvar { self_val: InsnId, id: ID, val: InsnId, state: InsnId },
400+
/// Check whether an instance variable exists on `self_val`
401+
DefinedIvar { self_val: InsnId, id: ID, pushval: VALUE, state: InsnId },
402+
400403

401404
/// Own a FrameState so that instructions can look up their dominating FrameState when
402405
/// generating deopt side-exits and frame reconstruction metadata. Does not directly generate
@@ -603,6 +606,23 @@ impl<'a> std::fmt::Display for InsnPrinter<'a> {
603606
Ok(())
604607
},
605608
Insn::Snapshot { state } => write!(f, "Snapshot {}", state),
609+
Insn::Defined { op_type, v, .. } => {
610+
// op_type (enum defined_type) printing logic from iseq.c.
611+
// Not sure why rb_iseq_defined_string() isn't exhaustive.
612+
use std::borrow::Cow;
613+
let op_type = *op_type as u32;
614+
let op_type = if op_type == DEFINED_FUNC {
615+
Cow::Borrowed("func")
616+
} else if op_type == DEFINED_REF {
617+
Cow::Borrowed("ref")
618+
} else if op_type == DEFINED_CONST_FROM {
619+
Cow::Borrowed("constant-from")
620+
} else {
621+
String::from_utf8_lossy(unsafe { rb_iseq_defined_string(op_type).as_rstring_byte_slice().unwrap() })
622+
};
623+
write!(f, "Defined {op_type}, {v}")
624+
}
625+
Insn::DefinedIvar { self_val, id, .. } => write!(f, "DefinedIvar {self_val}, :{}", id.contents_lossy().into_owned()),
606626
Insn::GetIvar { self_val, id, .. } => write!(f, "GetIvar {self_val}, :{}", id.contents_lossy().into_owned()),
607627
Insn::SetIvar { self_val, id, val, .. } => write!(f, "SetIvar {self_val}, :{}, {val}", id.contents_lossy().into_owned()),
608628
Insn::ToArray { val, .. } => write!(f, "ToArray {val}"),
@@ -951,6 +971,7 @@ impl Function {
951971
&HashDup { val , state } => HashDup { val: find!(val), state },
952972
&CCall { cfun, ref args, name, return_type, elidable } => CCall { cfun: cfun, args: args.iter().map(|arg| find!(*arg)).collect(), name: name, return_type: return_type, elidable },
953973
&Defined { op_type, obj, pushval, v } => Defined { op_type, obj, pushval, v: find!(v) },
974+
&DefinedIvar { self_val, pushval, id, state } => DefinedIvar { self_val: find!(self_val), pushval, id, state },
954975
NewArray { elements, state } => NewArray { elements: find_vec!(*elements), state: find!(*state) },
955976
&NewHash { ref elements, state } => {
956977
let mut found_elements = vec![];
@@ -1039,6 +1060,7 @@ impl Function {
10391060
Insn::SendWithoutBlockDirect { .. } => types::BasicObject,
10401061
Insn::Send { .. } => types::BasicObject,
10411062
Insn::Defined { .. } => types::BasicObject,
1063+
Insn::DefinedIvar { .. } => types::BasicObject,
10421064
Insn::GetConstantPath { .. } => types::BasicObject,
10431065
Insn::ArrayMax { .. } => types::BasicObject,
10441066
Insn::GetIvar { .. } => types::BasicObject,
@@ -1599,7 +1621,7 @@ impl Function {
15991621
worklist.push_back(state);
16001622
}
16011623
Insn::CCall { args, .. } => worklist.extend(args),
1602-
Insn::GetIvar { self_val, state, .. } => {
1624+
Insn::GetIvar { self_val, state, .. } | Insn::DefinedIvar { self_val, state, .. } => {
16031625
worklist.push_back(self_val);
16041626
worklist.push_back(state);
16051627
}
@@ -2159,12 +2181,20 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
21592181
state.stack_push(fun.push_insn(block, Insn::Const { val: Const::Value(VALUE::fixnum_from_usize(1)) }));
21602182
}
21612183
YARVINSN_defined => {
2184+
// (rb_num_t op_type, VALUE obj, VALUE pushval)
21622185
let op_type = get_arg(pc, 0).as_usize();
2163-
let obj = get_arg(pc, 0);
2164-
let pushval = get_arg(pc, 0);
2186+
let obj = get_arg(pc, 1);
2187+
let pushval = get_arg(pc, 2);
21652188
let v = state.stack_pop()?;
21662189
state.stack_push(fun.push_insn(block, Insn::Defined { op_type, obj, pushval, v }));
21672190
}
2191+
YARVINSN_definedivar => {
2192+
// (ID id, IVC ic, VALUE pushval)
2193+
let id = ID(get_arg(pc, 0).as_u64());
2194+
let pushval = get_arg(pc, 2);
2195+
let exit_id = fun.push_insn(block, Insn::Snapshot { state: exit_state });
2196+
state.stack_push(fun.push_insn(block, Insn::DefinedIvar { self_val: self_param, id, pushval, state: exit_id }));
2197+
}
21682198
YARVINSN_opt_getconstant_path => {
21692199
let ic = get_arg(pc, 0).as_ptr();
21702200
state.stack_push(fun.push_insn(block, Insn::GetConstantPath { ic }));
@@ -2746,9 +2776,9 @@ mod tests {
27462776
}
27472777

27482778
#[track_caller]
2749-
fn assert_method_hir_with_opcodes(method: &str, opcodes: Vec<u32>, hir: Expect) {
2779+
fn assert_method_hir_with_opcodes(method: &str, opcodes: &[u32], hir: Expect) {
27502780
let iseq = crate::cruby::with_rubyvm(|| get_method_iseq(method));
2751-
for opcode in opcodes {
2781+
for &opcode in opcodes {
27522782
assert!(iseq_contains_opcode(iseq, opcode), "iseq {method} does not contain {}", insn_name(opcode as usize));
27532783
}
27542784
unsafe { crate::cruby::rb_zjit_profile_disable(iseq) };
@@ -2758,7 +2788,7 @@ mod tests {
27582788

27592789
#[track_caller]
27602790
fn assert_method_hir_with_opcode(method: &str, opcode: u32, hir: Expect) {
2761-
assert_method_hir_with_opcodes(method, vec![opcode], hir)
2791+
assert_method_hir_with_opcodes(method, &[opcode], hir)
27622792
}
27632793

27642794
#[track_caller]
@@ -2992,7 +3022,7 @@ mod tests {
29923022
a
29933023
end
29943024
");
2995-
assert_method_hir_with_opcodes("test", vec![YARVINSN_getlocal_WC_0, YARVINSN_setlocal_WC_0], expect![[r#"
3025+
assert_method_hir_with_opcodes("test", &[YARVINSN_getlocal_WC_0, YARVINSN_setlocal_WC_0], expect![[r#"
29963026
fn test:
29973027
bb0(v0:BasicObject):
29983028
v1:NilClassExact = Const Value(nil)
@@ -3001,6 +3031,37 @@ mod tests {
30013031
"#]]);
30023032
}
30033033

3034+
#[test]
3035+
fn defined_ivar() {
3036+
eval("
3037+
def test = defined?(@foo)
3038+
");
3039+
assert_method_hir_with_opcode("test", YARVINSN_definedivar, expect![[r#"
3040+
fn test:
3041+
bb0(v0:BasicObject):
3042+
v3:BasicObject = DefinedIvar v0, :@foo
3043+
Return v3
3044+
"#]]);
3045+
}
3046+
3047+
#[test]
3048+
fn defined() {
3049+
eval("
3050+
def test = return defined?(SeaChange), defined?(favourite), defined?($ruby)
3051+
");
3052+
assert_method_hir_with_opcode("test", YARVINSN_defined, expect![[r#"
3053+
fn test:
3054+
bb0(v0:BasicObject):
3055+
v2:NilClassExact = Const Value(nil)
3056+
v3:BasicObject = Defined constant, v2
3057+
v4:BasicObject = Defined func, v0
3058+
v5:NilClassExact = Const Value(nil)
3059+
v6:BasicObject = Defined global-variable, v5
3060+
v8:ArrayExact = NewArray v3, v4, v6
3061+
Return v8
3062+
"#]]);
3063+
}
3064+
30043065
#[test]
30053066
fn test_return_const() {
30063067
eval("

0 commit comments

Comments
 (0)