Skip to content

Commit 987ec70

Browse files
committed
ZJIT: Guard T_* in addition to shape in polymorphic getivar
This is a8f3c34 ("ZJIT: Add missing guard on ivar access on T_{DATA,CLASS,MODULE}") but for the polymorphic implementation in HIR build.
1 parent 1bb1f6c commit 987ec70

7 files changed

Lines changed: 149 additions & 67 deletions

File tree

jit.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ enum jit_bindgen_constants {
4040
// Field offsets for the RString struct
4141
RUBY_OFFSET_RSTRING_LEN = offsetof(struct RString, len),
4242

43+
// Shape constant related to RBasic::flags. (See RBASIC_SET_SHAPE_ID())
44+
RB_SHAPE_FLAG_SHIFT = SHAPE_FLAG_SHIFT,
45+
4346
// Field offsets for rb_execution_context_t
4447
RUBY_OFFSET_EC_CFP = offsetof(rb_execution_context_t, cfp),
4548
RUBY_OFFSET_EC_INTERRUPT_FLAG = offsetof(rb_execution_context_t, interrupt_flag),

zjit/src/codegen.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -595,6 +595,7 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio
595595
&Insn::Const { val: Const::CInt64(val) } => gen_const_long(val),
596596
&Insn::Const { val: Const::CUInt16(val) } => gen_const_uint16(val),
597597
&Insn::Const { val: Const::CUInt32(val) } => gen_const_uint32(val),
598+
&Insn::Const { val: Const::CUInt64(val) } => Opnd::UImm(val),
598599
&Insn::Const { val: Const::CShape(val) } => {
599600
assert_eq!(SHAPE_ID_NUM_BITS, 32);
600601
gen_const_uint32(val.0)

zjit/src/codegen_tests.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3714,6 +3714,31 @@ fn test_getivar_t_class_then_string() {
37143714
assert_snapshot!(assert_compiles("[STR.test, STR.test]"), @"[1000, 1000]");
37153715
}
37163716

3717+
3718+
#[test]
3719+
fn test_getivar_polymorphic_t_class_and_typed_data() {
3720+
set_call_threshold(3);
3721+
eval(r#"
3722+
module Reader
3723+
def test = @a
3724+
end
3725+
3726+
class A
3727+
extend Reader
3728+
@a = 0
3729+
end
3730+
3731+
ARGF.instance_eval do
3732+
extend Reader
3733+
@a = :a
3734+
end
3735+
3736+
A.test
3737+
ARGF.test
3738+
"#);
3739+
assert_snapshot!(assert_compiles("[A.test, ARGF.test]"), @"[0, :a]");
3740+
}
3741+
37173742
#[test]
37183743
fn test_attr_accessor_setivar() {
37193744
eval("

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: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6274,8 +6274,16 @@ impl Function {
62746274
Insn::IntAnd { left, right }
62756275
| Insn::IntOr { left, right } => {
62766276
// TODO: Expand this to other matching C integer sizes when we need them.
6277-
self.assert_subtype(insn_id, left, types::CInt64)?;
6278-
self.assert_subtype(insn_id, right, types::CInt64)
6277+
let left_type = self.type_of(left);
6278+
if left_type.is_subtype(types::CInt64) {
6279+
self.assert_subtype(insn_id, right, types::CInt64)
6280+
} else if left_type.is_subtype(types::CUInt64) {
6281+
self.assert_subtype(insn_id, right, types::CUInt64)
6282+
} else {
6283+
let all_ints = types::CInt64.union(types::CUInt64);
6284+
self.assert_subtype(insn_id, left, all_ints)?;
6285+
self.assert_subtype(insn_id, right, all_ints)
6286+
}
62796287
}
62806288
Insn::BoxBool { val }
62816289
| Insn::IfTrue { val, .. }
@@ -8106,31 +8114,37 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
81068114
}
81078115
if let Some(summary) = fun.polymorphic_summary(&profiles, self_param, exit_state.insn_idx) {
81088116
self_param = fun.push_insn(block, Insn::GuardType { val: self_param, guard_type: types::HeapBasicObject, state: exit_id });
8109-
let shape = fun.load_shape(block, self_param);
8117+
let rbasic_flags = fun.load_rbasic_flags(block, self_param);
81108118
let join_block = insn_idx_to_block.get(&insn_idx).copied().unwrap_or_else(|| fun.new_block(insn_idx));
81118119
let join_param = fun.push_insn(join_block, Insn::Param);
81128120
// Dedup by expected shape so objects with different classes but the same shape can share code
81138121
// TODO(max): De-duplicate further by checking ivar offsets to allow
81148122
// different shapes with the same ivar layout to share code
8115-
let mut seen_shapes = Vec::with_capacity(summary.buckets().len());
8123+
let mut seen_shape_and_flags = Vec::with_capacity(summary.buckets().len());
81168124
for &profiled_type in summary.buckets() {
81178125
// End of the buckets
81188126
if profiled_type.is_empty() { break; }
81198127
// Instance variable lookups on immediate values are always nil; don't bother
81208128
if profiled_type.flags().is_immediate() { continue; }
81218129
let expected_shape = profiled_type.shape();
8130+
let (expected_rbasic_flags, rbasic_flags_mask) = profiled_type.rbasic_flags_and_mask();
81228131
assert!(expected_shape.is_valid());
81238132
// Too-complex shapes use hash tables for ivars;
81248133
// rb_shape_get_iv_index doesn't work for them.
81258134
// Let the fallthrough GetIvar handle these.
81268135
if expected_shape.is_too_complex() { continue; }
8127-
if seen_shapes.contains(&expected_shape) { continue; }
8128-
seen_shapes.push(expected_shape);
8129-
let expected_shape_const = fun.push_insn(block, Insn::Const { val: Const::CShape(expected_shape) });
8130-
let has_shape = fun.push_insn(block, Insn::IsBitEqual { left: shape, right: expected_shape_const });
8136+
if seen_shape_and_flags.contains(&expected_rbasic_flags) { continue; }
8137+
seen_shape_and_flags.push(expected_rbasic_flags);
8138+
let rbasic_flags_mask = fun.push_insn(block, Insn::Const { val: Const::CUInt64(rbasic_flags_mask) });
8139+
// The expected shape can change over run, so we put it
8140+
// as a pointer to keep it stable in snapshot tests.
8141+
let expected_rbasic_flags = fun.push_insn(block, Insn::Const { val: Const::CPtr(ptr::without_provenance(expected_rbasic_flags.to_usize())) });
8142+
let expected_rbasic_flags = fun.push_insn(block, Insn::RefineType { val: expected_rbasic_flags, new_type: types::CUInt64 });
8143+
let masked = fun.push_insn(block, Insn::IntAnd { left: rbasic_flags, right: rbasic_flags_mask});
8144+
let has_shape_and_type = fun.push_insn(block, Insn::IsBitEqual { left: masked, right: expected_rbasic_flags });
81318145
let iftrue_block = fun.new_block(insn_idx);
81328146
let target = BranchEdge { target: iftrue_block, args: vec![] };
8133-
fun.push_insn(block, Insn::IfTrue { val: has_shape, target });
8147+
fun.push_insn(block, Insn::IfTrue { val: has_shape_and_type, target });
81348148
let result = fun.load_ivar(iftrue_block, self_param, profiled_type, id, exit_id);
81358149
fun.push_insn(iftrue_block, Insn::Jump(BranchEdge { target: join_block, args: vec![result] }));
81368150
}

zjit/src/hir/opt_tests.rs

Lines changed: 76 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -7661,29 +7661,35 @@ mod hir_opt_tests {
76617661
bb3(v6:BasicObject):
76627662
PatchPoint SingleRactorMode
76637663
v11:HeapBasicObject = GuardType v6, HeapBasicObject
7664-
v12:CShape = LoadField v11, :_shape_id@0x1000
7665-
v14:CShape[0x1001] = Const CShape(0x1001)
7666-
v15:CBool = IsBitEqual v12, v14
7667-
IfTrue v15, bb5()
7668-
v20:CShape[0x1002] = Const CShape(0x1002)
7669-
v21:CBool = IsBitEqual v12, v20
7670-
IfTrue v21, bb6()
7671-
v25:BasicObject = GetIvar v11, :@foo
7672-
Jump bb4(v25)
7664+
v12:CUInt64 = LoadField v11, :_rbasic_flags@0x1000
7665+
v14:CUInt64[18446744069414584351] = Const CUInt64(18446744069414584351)
7666+
v15:CPtr[CPtr(0x1008)] = Const CPtr(0x1010)
7667+
v16 = RefineType v15, CUInt64
7668+
v17:CInt64 = IntAnd v12, v14
7669+
v18:CBool = IsBitEqual v17, v16
7670+
IfTrue v18, bb5()
7671+
v23:CUInt64[18446744069414584351] = Const CUInt64(18446744069414584351)
7672+
v24:CPtr[CPtr(0x1008)] = Const CPtr(0x1010)
7673+
v25 = RefineType v24, CUInt64
7674+
v26:CInt64 = IntAnd v12, v23
7675+
v27:CBool = IsBitEqual v26, v25
7676+
IfTrue v27, bb6()
7677+
v31:BasicObject = GetIvar v11, :@foo
7678+
Jump bb4(v31)
76737679
bb5():
7674-
v17:CPtr = LoadField v11, :_as_heap@0x1003
7675-
v18:BasicObject = LoadField v17, :@foo@0x1004
7676-
Jump bb4(v18)
7680+
v20:CPtr = LoadField v11, :_as_heap@0x1018
7681+
v21:BasicObject = LoadField v20, :@foo@0x1019
7682+
Jump bb4(v21)
76777683
bb6():
7678-
v23:BasicObject = LoadField v11, :@foo@0x1003
7679-
Jump bb4(v23)
7684+
v29:BasicObject = LoadField v11, :@foo@0x1018
7685+
Jump bb4(v29)
76807686
bb4(v13:BasicObject):
7681-
v28:Fixnum[1] = Const Value(1)
7682-
PatchPoint MethodRedefined(Integer@0x1008, +@0x1010, cme:0x1018)
7683-
v39:Fixnum = GuardType v13, Fixnum
7684-
v40:Fixnum = FixnumAdd v39, v28
7687+
v34:Fixnum[1] = Const Value(1)
7688+
PatchPoint MethodRedefined(Integer@0x1020, +@0x1028, cme:0x1030)
7689+
v45:Fixnum = GuardType v13, Fixnum
7690+
v46:Fixnum = FixnumAdd v45, v34
76857691
CheckInterrupts
7686-
Return v40
7692+
Return v46
76877693
");
76887694
}
76897695

@@ -7731,31 +7737,37 @@ mod hir_opt_tests {
77317737
bb3(v6:BasicObject):
77327738
PatchPoint SingleRactorMode
77337739
v11:HeapBasicObject = GuardType v6, HeapBasicObject
7734-
v12:CShape = LoadField v11, :_shape_id@0x1000
7735-
v14:CShape[0x1001] = Const CShape(0x1001)
7736-
v15:CBool = IsBitEqual v12, v14
7737-
IfTrue v15, bb5()
7738-
v19:CShape[0x1002] = Const CShape(0x1002)
7739-
v20:CBool = IsBitEqual v12, v19
7740-
IfTrue v20, bb6()
7741-
v38:CShape = LoadField v11, :_shape_id@0x1000
7742-
v39:CShape[0x1001] = GuardBitEquals v38, CShape(0x1001)
7743-
v40:BasicObject = LoadField v11, :@foo@0x1003
7744-
Jump bb4(v40)
7740+
v12:CUInt64 = LoadField v11, :_rbasic_flags@0x1000
7741+
v14:CUInt64[18446744069414584351] = Const CUInt64(18446744069414584351)
7742+
v15:CPtr[CPtr(0x1008)] = Const CPtr(0x1010)
7743+
v16 = RefineType v15, CUInt64
7744+
v17:CInt64 = IntAnd v12, v14
7745+
v18:CBool = IsBitEqual v17, v16
7746+
IfTrue v18, bb5()
7747+
v22:CUInt64[18446744069414584351] = Const CUInt64(18446744069414584351)
7748+
v23:CPtr[CPtr(0x1008)] = Const CPtr(0x1010)
7749+
v24 = RefineType v23, CUInt64
7750+
v25:CInt64 = IntAnd v12, v22
7751+
v26:CBool = IsBitEqual v25, v24
7752+
IfTrue v26, bb6()
7753+
v44:CShape = LoadField v11, :_shape_id@0x1018
7754+
v45:CShape[0x1019] = GuardBitEquals v44, CShape(0x1019)
7755+
v46:BasicObject = LoadField v11, :@foo@0x101a
7756+
Jump bb4(v46)
77457757
bb5():
7746-
v17:BasicObject = LoadField v11, :@foo@0x1003
7747-
Jump bb4(v17)
7758+
v20:BasicObject = LoadField v11, :@foo@0x101a
7759+
Jump bb4(v20)
77487760
bb6():
7749-
v22:CPtr = LoadField v11, :_as_heap@0x1003
7750-
v23:BasicObject = LoadField v22, :@foo@0x1004
7751-
Jump bb4(v23)
7761+
v28:CPtr = LoadField v11, :_as_heap@0x101a
7762+
v29:BasicObject = LoadField v28, :@foo@0x101b
7763+
Jump bb4(v29)
77527764
bb4(v13:BasicObject):
7753-
v28:Fixnum[1] = Const Value(1)
7754-
PatchPoint MethodRedefined(Integer@0x1008, +@0x1010, cme:0x1018)
7755-
v43:Fixnum = GuardType v13, Fixnum
7756-
v44:Fixnum = FixnumAdd v43, v28
7765+
v34:Fixnum[1] = Const Value(1)
7766+
PatchPoint MethodRedefined(Integer@0x1020, +@0x1028, cme:0x1030)
7767+
v49:Fixnum = GuardType v13, Fixnum
7768+
v50:Fixnum = FixnumAdd v49, v34
77577769
CheckInterrupts
7758-
Return v44
7770+
Return v50
77597771
");
77607772
}
77617773

@@ -7796,28 +7808,34 @@ mod hir_opt_tests {
77967808
bb3(v6:BasicObject):
77977809
PatchPoint SingleRactorMode
77987810
v11:HeapBasicObject = GuardType v6, HeapBasicObject
7799-
v12:CShape = LoadField v11, :_shape_id@0x1000
7800-
v14:CShape[0x1001] = Const CShape(0x1001)
7801-
v15:CBool = IsBitEqual v12, v14
7802-
IfTrue v15, bb5()
7803-
v19:CShape[0x1002] = Const CShape(0x1002)
7804-
v20:CBool = IsBitEqual v12, v19
7805-
IfTrue v20, bb6()
7806-
v24:BasicObject = GetIvar v11, :@foo
7807-
Jump bb4(v24)
7811+
v12:CUInt64 = LoadField v11, :_rbasic_flags@0x1000
7812+
v14:CUInt64[18446744069414584351] = Const CUInt64(18446744069414584351)
7813+
v15:CPtr[CPtr(0x1008)] = Const CPtr(0x1010)
7814+
v16 = RefineType v15, CUInt64
7815+
v17:CInt64 = IntAnd v12, v14
7816+
v18:CBool = IsBitEqual v17, v16
7817+
IfTrue v18, bb5()
7818+
v22:CUInt64[18446744069414584351] = Const CUInt64(18446744069414584351)
7819+
v23:CPtr[CPtr(0x1008)] = Const CPtr(0x1010)
7820+
v24 = RefineType v23, CUInt64
7821+
v25:CInt64 = IntAnd v12, v22
7822+
v26:CBool = IsBitEqual v25, v24
7823+
IfTrue v26, bb6()
7824+
v30:BasicObject = GetIvar v11, :@foo
7825+
Jump bb4(v30)
78087826
bb5():
7809-
v17:BasicObject = LoadField v11, :@foo@0x1003
7810-
Jump bb4(v17)
7827+
v20:BasicObject = LoadField v11, :@foo@0x1018
7828+
Jump bb4(v20)
78117829
bb6():
7812-
v22:BasicObject = LoadField v11, :@foo@0x1003
7813-
Jump bb4(v22)
7830+
v28:BasicObject = LoadField v11, :@foo@0x1018
7831+
Jump bb4(v28)
78147832
bb4(v13:BasicObject):
7815-
v27:Fixnum[1] = Const Value(1)
7816-
PatchPoint MethodRedefined(Integer@0x1008, +@0x1010, cme:0x1018)
7817-
v38:Fixnum = GuardType v13, Fixnum
7818-
v39:Fixnum = FixnumAdd v38, v27
7833+
v33:Fixnum[1] = Const Value(1)
7834+
PatchPoint MethodRedefined(Integer@0x1020, +@0x1028, cme:0x1030)
7835+
v44:Fixnum = GuardType v13, Fixnum
7836+
v45:Fixnum = FixnumAdd v44, v33
78197837
CheckInterrupts
7820-
Return v39
7838+
Return v45
78217839
");
78227840
}
78237841

zjit/src/profile.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,25 @@ impl ProfiledType {
333333
self.flags
334334
}
335335

336+
/// For ivar access
337+
pub fn rbasic_flags_and_mask(&self) -> (u64, u64) {
338+
let shape_flag_shift = u64::from(RB_SHAPE_FLAG_SHIFT);
339+
let (shape, shape_mask) = (u64::from(self.shape().0) << shape_flag_shift, !0 << shape_flag_shift);
340+
let (builtin_type, type_mask) = if self.flags().is_t_object() {
341+
(RUBY_T_OBJECT, RUBY_T_MASK)
342+
} else if self.class().is_subclass_of(unsafe { rb_cClass }) == ClassRelationship::Subclass {
343+
// Check class first since `Class < Module`
344+
(RUBY_T_CLASS, RUBY_T_MASK)
345+
} else if self.class().is_subclass_of(unsafe { rb_cModule }) == ClassRelationship::Subclass {
346+
(RUBY_T_MODULE, RUBY_T_MASK)
347+
} else if self.flags().is_typed_data() {
348+
(RUBY_T_DATA | RUBY_TYPED_FL_IS_TYPED_DATA, RUBY_T_MASK | RUBY_TYPED_FL_IS_TYPED_DATA)
349+
} else {
350+
(0, 0)
351+
};
352+
(shape | u64::from(builtin_type), shape_mask | u64::from(type_mask))
353+
}
354+
336355
pub fn is_fixnum(&self) -> bool {
337356
self.class == unsafe { rb_cInteger } && self.flags.is_immediate()
338357
}

0 commit comments

Comments
 (0)