Skip to content

Commit 48dd4b1

Browse files
committed
ZJIT: Deduplicate redundant GuardType instructions in fold_constants
When two GuardType instructions in the same basic block guard the same value with the same (or narrower) type, the second guard is redundant. For example, `(n - 1) + (n - 2)` generates two `GuardType n, Fixnum` — one for each subtraction — but after the first guard succeeds, the second is guaranteed to succeed as well. The new logic in fold_constants tracks every GuardType emitted so far in the current block. If a later GuardType's (val, guard_type) pair is already covered (i.e. guard_type is a subtype of the earlier guard's type), the later guard is replaced with the earlier result via make_equal_to.
1 parent 892991b commit 48dd4b1

2 files changed

Lines changed: 66 additions & 16 deletions

File tree

zjit/src/hir.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5239,13 +5239,36 @@ impl Function {
52395239
for block in self.rpo() {
52405240
let old_insns = std::mem::take(&mut self.blocks[block.0].insns);
52415241
let mut new_insns = vec![];
5242+
// Track guards seen so far in this block: (val, guard_type, result_insn_id).
5243+
// When we encounter a GuardType whose (val, guard_type) pair is already covered
5244+
// by a previous guard, we can eliminate it by reusing the earlier result.
5245+
let mut seen_guards: Vec<(InsnId, Type, InsnId)> = vec![];
52425246
for insn_id in old_insns {
52435247
let replacement_id = match self.find(insn_id) {
52445248
Insn::GuardType { val, guard_type, .. } if self.is_a(val, guard_type) => {
52455249
self.make_equal_to(insn_id, val);
52465250
// Don't bother re-inferring the type of val; we already know it.
52475251
continue;
52485252
}
5253+
// Deduplicate GuardType: if we already guarded the same val with a
5254+
// type that is the same or narrower, the new guard is redundant.
5255+
// e.g. if we already proved val is Fixnum, a later Fixnum or
5256+
// BasicObject guard on the same val is guaranteed to pass.
5257+
Insn::GuardType { val, guard_type, .. } => {
5258+
let mut found = None;
5259+
for &(prev_val, prev_type, prev_result) in &seen_guards {
5260+
if prev_val == val && prev_type.is_subtype(guard_type) {
5261+
found = Some(prev_result);
5262+
break;
5263+
}
5264+
}
5265+
if let Some(prev_result) = found {
5266+
self.make_equal_to(insn_id, prev_result);
5267+
continue;
5268+
}
5269+
seen_guards.push((val, guard_type, insn_id));
5270+
insn_id
5271+
}
52495272
Insn::LoadField { recv, offset, return_type, .. } if return_type.is_subtype(types::BasicObject) &&
52505273
u32::try_from(offset).is_ok() => {
52515274
let offset = (offset as u32).to_usize();

zjit/src/hir/opt_tests.rs

Lines changed: 43 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -224,8 +224,6 @@ mod hir_opt_tests {
224224
PatchPoint MethodRedefined(Integer@0x1008, *@0x1010, cme:0x1018)
225225
v35:Fixnum = GuardType v10, Fixnum
226226
v44:Fixnum[0] = Const Value(0)
227-
v21:Fixnum[0] = Const Value(0)
228-
v39:Fixnum = GuardType v10, Fixnum
229227
v45:Fixnum[0] = Const Value(0)
230228
PatchPoint MethodRedefined(Integer@0x1008, +@0x1040, cme:0x1048)
231229
v46:Fixnum[0] = Const Value(0)
@@ -1633,8 +1631,7 @@ mod hir_opt_tests {
16331631
v23:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)]
16341632
v24:BasicObject = SendDirect v23, 0x1038, :foo (0x1048)
16351633
PatchPoint MethodRedefined(Object@0x1000, bar@0x1050, cme:0x1058)
1636-
v26:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)]
1637-
v27:BasicObject = SendDirect v26, 0x1038, :bar (0x1048)
1634+
v27:BasicObject = SendDirect v23, 0x1038, :bar (0x1048)
16381635
CheckInterrupts
16391636
Return v27
16401637
");
@@ -1747,8 +1744,7 @@ mod hir_opt_tests {
17471744
v16:Fixnum[20] = Const Value(20)
17481745
v18:Fixnum[30] = Const Value(30)
17491746
PatchPoint MethodRedefined(Object@0x1000, target@0x1008, cme:0x1010)
1750-
v47:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)]
1751-
v48:BasicObject = SendDirect v47, 0x1038, :target (0x1048), v14, v16, v18
1747+
v48:BasicObject = SendDirect v44, 0x1038, :target (0x1048), v14, v16, v18
17521748
v24:Fixnum[10] = Const Value(10)
17531749
v26:Fixnum[20] = Const Value(20)
17541750
v28:Fixnum[30] = Const Value(30)
@@ -3901,8 +3897,7 @@ mod hir_opt_tests {
39013897
v24:Fixnum[4] = Const Value(4)
39023898
v26:Fixnum[3] = Const Value(3)
39033899
PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010)
3904-
v41:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)]
3905-
v42:BasicObject = SendDirect v41, 0x1038, :foo (0x1048), v20, v22, v26, v24
3900+
v42:BasicObject = SendDirect v37, 0x1038, :foo (0x1048), v20, v22, v26, v24
39063901
v30:ArrayExact = NewArray v38, v42
39073902
CheckInterrupts
39083903
Return v30
@@ -3939,8 +3934,7 @@ mod hir_opt_tests {
39393934
v22:Fixnum[40] = Const Value(40)
39403935
v24:Fixnum[30] = Const Value(30)
39413936
PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010)
3942-
v41:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)]
3943-
v42:BasicObject = SendDirect v41, 0x1038, :foo (0x1048), v18, v20, v24, v22
3937+
v42:BasicObject = SendDirect v37, 0x1038, :foo (0x1048), v18, v20, v24, v22
39443938
v28:ArrayExact = NewArray v38, v42
39453939
CheckInterrupts
39463940
Return v28
@@ -3975,8 +3969,7 @@ mod hir_opt_tests {
39753969
v20:Fixnum[30] = Const Value(30)
39763970
v22:Fixnum[6] = Const Value(6)
39773971
PatchPoint MethodRedefined(Object@0x1000, target@0x1008, cme:0x1010)
3978-
v51:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)]
3979-
v52:BasicObject = SendDirect v51, 0x1038, :target (0x1048), v16, v18, v20, v22
3972+
v52:BasicObject = SendDirect v48, 0x1038, :target (0x1048), v16, v18, v20, v22
39803973
v27:Fixnum[10] = Const Value(10)
39813974
v29:Fixnum[20] = Const Value(20)
39823975
v31:Fixnum[30] = Const Value(30)
@@ -6527,7 +6520,6 @@ mod hir_opt_tests {
65276520
v22:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)]
65286521
v28:StaticSymbol[:b] = Const Value(VALUE(0x1038))
65296522
PatchPoint MethodRedefined(Object@0x1000, one@0x1040, cme:0x1048)
6530-
v26:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)]
65316523
CheckInterrupts
65326524
Return v28
65336525
");
@@ -11812,7 +11804,6 @@ mod hir_opt_tests {
1181211804
PatchPoint NoSingletonClass(String@0x1008)
1181311805
PatchPoint MethodRedefined(String@0x1008, ==@0x1010, cme:0x1018)
1181411806
v26:StringExact = GuardType v10, StringExact
11815-
v27:String = GuardType v10, String
1181611807
v29:TrueClass = Const Value(true)
1181711808
CheckInterrupts
1181811809
Return v29
@@ -11842,7 +11833,6 @@ mod hir_opt_tests {
1184211833
PatchPoint NoSingletonClass(String@0x1008)
1184311834
PatchPoint MethodRedefined(String@0x1008, ===@0x1010, cme:0x1018)
1184411835
v25:StringExact = GuardType v10, StringExact
11845-
v26:String = GuardType v10, String
1184611836
v28:TrueClass = Const Value(true)
1184711837
CheckInterrupts
1184811838
Return v28
@@ -12254,7 +12244,6 @@ mod hir_opt_tests {
1225412244
PatchPoint MethodRedefined(String@0x1008, !=@0x1010, cme:0x1018)
1225512245
v26:StringExact = GuardType v10, StringExact
1225612246
PatchPoint MethodRedefined(String@0x1008, ==@0x1040, cme:0x1048)
12257-
v30:String = GuardType v10, String
1225812247
v35:TrueClass = Const Value(true)
1225912248
v32:TrueClass = Const Value(true)
1226012249
v33:CBool = IsBitNotEqual v35, v32
@@ -15295,4 +15284,42 @@ mod hir_opt_tests {
1529515284
Return v35
1529615285
");
1529715286
}
15287+
15288+
#[test]
15289+
fn test_dedup_guard_type() {
15290+
// Two subtractions on the same Fixnum operand `n` each require a
15291+
// GuardType n, Fixnum. The second guard is redundant and should be
15292+
// eliminated by fold_constants.
15293+
eval("
15294+
def test(n)
15295+
(n - 1) + (n - 2)
15296+
end
15297+
test 1; test 2
15298+
");
15299+
assert_snapshot!(hir_string("test"), @"
15300+
fn test@<compiled>:3:
15301+
bb1():
15302+
EntryPoint interpreter
15303+
v1:BasicObject = LoadSelf
15304+
v2:CPtr = LoadSP
15305+
v3:BasicObject = LoadField v2, :n@0x1000
15306+
Jump bb3(v1, v3)
15307+
bb2():
15308+
EntryPoint JIT(0)
15309+
v6:BasicObject = LoadArg :self@0
15310+
v7:BasicObject = LoadArg :n@1
15311+
Jump bb3(v6, v7)
15312+
bb3(v9:BasicObject, v10:BasicObject):
15313+
v15:Fixnum[1] = Const Value(1)
15314+
PatchPoint MethodRedefined(Integer@0x1008, -@0x1010, cme:0x1018)
15315+
v35:Fixnum = GuardType v10, Fixnum
15316+
v36:Fixnum = FixnumSub v35, v15
15317+
v21:Fixnum[2] = Const Value(2)
15318+
v40:Fixnum = FixnumSub v35, v21
15319+
PatchPoint MethodRedefined(Integer@0x1008, +@0x1040, cme:0x1048)
15320+
v43:Fixnum = FixnumAdd v36, v40
15321+
CheckInterrupts
15322+
Return v43
15323+
");
15324+
}
1529815325
}

0 commit comments

Comments
 (0)