Skip to content

Commit 15cf72d

Browse files
authored
ZJIT: Check if BOP is redefined before rewriting (ruby#13916)
Fix Shopify#592
1 parent 616df50 commit 15cf72d

1 file changed

Lines changed: 94 additions & 0 deletions

File tree

zjit/src/hir.rs

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1382,6 +1382,11 @@ impl Function {
13821382
}
13831383

13841384
fn try_rewrite_fixnum_op(&mut self, block: BlockId, orig_insn_id: InsnId, f: &dyn Fn(InsnId, InsnId) -> Insn, bop: u32, left: InsnId, right: InsnId, state: InsnId) {
1385+
if !unsafe { rb_BASIC_OP_UNREDEFINED_P(bop, INTEGER_REDEFINED_OP_FLAG) } {
1386+
// If the basic operation is already redefined, we cannot optimize it.
1387+
self.push_insn_id(block, orig_insn_id);
1388+
return;
1389+
}
13851390
if self.arguments_likely_fixnums(left, right, state) {
13861391
if bop == BOP_NEQ {
13871392
// For opt_neq, the interpreter checks that both neq and eq are unchanged.
@@ -1399,6 +1404,11 @@ impl Function {
13991404
}
14001405

14011406
fn rewrite_if_frozen(&mut self, block: BlockId, orig_insn_id: InsnId, self_val: InsnId, klass: u32, bop: u32, state: InsnId) {
1407+
if !unsafe { rb_BASIC_OP_UNREDEFINED_P(bop, klass) } {
1408+
// If the basic operation is already redefined, we cannot optimize it.
1409+
self.push_insn_id(block, orig_insn_id);
1410+
return;
1411+
}
14021412
let self_type = self.type_of(self_val);
14031413
if let Some(obj) = self_type.ruby_object() {
14041414
if obj.is_frozen() {
@@ -1431,6 +1441,11 @@ impl Function {
14311441
}
14321442

14331443
fn try_rewrite_aref(&mut self, block: BlockId, orig_insn_id: InsnId, self_val: InsnId, idx_val: InsnId, state: InsnId) {
1444+
if !unsafe { rb_BASIC_OP_UNREDEFINED_P(BOP_AREF, ARRAY_REDEFINED_OP_FLAG) } {
1445+
// If the basic operation is already redefined, we cannot optimize it.
1446+
self.push_insn_id(block, orig_insn_id);
1447+
return;
1448+
}
14341449
let self_type = self.type_of(self_val);
14351450
let idx_type = self.type_of(idx_val);
14361451
if self_type.is_subtype(types::ArrayExact) {
@@ -2667,6 +2682,11 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
26672682
break; // End the block
26682683
},
26692684
};
2685+
if !unsafe { rb_BASIC_OP_UNREDEFINED_P(bop, ARRAY_REDEFINED_OP_FLAG) } {
2686+
// If the basic operation is already redefined, we cannot optimize it.
2687+
fun.push_insn(block, Insn::SideExit { state: exit_id, reason: SideExitReason::PatchPoint(Invariant::BOPRedefined { klass: ARRAY_REDEFINED_OP_FLAG, bop }) });
2688+
break; // End the block
2689+
}
26702690
fun.push_insn(block, Insn::PatchPoint { invariant: Invariant::BOPRedefined { klass: ARRAY_REDEFINED_OP_FLAG, bop }, state: exit_id });
26712691
state.stack_push(fun.push_insn(block, insn));
26722692
}
@@ -5590,6 +5610,25 @@ mod opt_tests {
55905610
"#]]);
55915611
}
55925612

5613+
#[test]
5614+
fn test_dont_optimize_fixnum_add_if_redefined() {
5615+
eval("
5616+
class Integer
5617+
def +(other)
5618+
100
5619+
end
5620+
end
5621+
def test(a, b) = a + b
5622+
test(1,2); test(3,4)
5623+
");
5624+
assert_optimized_method_hir("test", expect![[r#"
5625+
fn test@<compiled>:7:
5626+
bb0(v0:BasicObject, v1:BasicObject, v2:BasicObject):
5627+
v5:BasicObject = SendWithoutBlock v1, :+, v2
5628+
Return v5
5629+
"#]]);
5630+
}
5631+
55935632
#[test]
55945633
fn test_optimize_send_into_fixnum_add_both_profiled() {
55955634
eval("
@@ -6628,6 +6667,23 @@ mod opt_tests {
66286667
"#]]);
66296668
}
66306669

6670+
#[test]
6671+
fn test_dont_optimize_hash_freeze_if_redefined() {
6672+
eval("
6673+
class Hash
6674+
def freeze; end
6675+
end
6676+
def test = {}.freeze
6677+
");
6678+
assert_optimized_method_hir("test", expect![[r#"
6679+
fn test@<compiled>:5:
6680+
bb0(v0:BasicObject):
6681+
v3:HashExact[VALUE(0x1000)] = Const Value(VALUE(0x1000))
6682+
v4:BasicObject = SendWithoutBlock v3, :freeze
6683+
Return v4
6684+
"#]]);
6685+
}
6686+
66316687
#[test]
66326688
fn test_elide_freeze_with_refrozen_hash() {
66336689
eval("
@@ -6973,6 +7029,44 @@ mod opt_tests {
69737029
"#]]);
69747030
}
69757031

7032+
#[test]
7033+
fn test_dont_optimize_array_aref_if_redefined() {
7034+
eval(r##"
7035+
class Array
7036+
def [](index); end
7037+
end
7038+
def test = [4,5,6].freeze[10]
7039+
"##);
7040+
assert_optimized_method_hir("test", expect![[r#"
7041+
fn test@<compiled>:5:
7042+
bb0(v0:BasicObject):
7043+
v3:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000))
7044+
PatchPoint BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_FREEZE)
7045+
v5:Fixnum[10] = Const Value(10)
7046+
v7:BasicObject = SendWithoutBlock v3, :[], v5
7047+
Return v7
7048+
"#]]);
7049+
}
7050+
7051+
#[test]
7052+
fn test_dont_optimize_array_max_if_redefined() {
7053+
eval(r##"
7054+
class Array
7055+
def max = 10
7056+
end
7057+
def test = [4,5,6].max
7058+
"##);
7059+
assert_optimized_method_hir("test", expect![[r#"
7060+
fn test@<compiled>:5:
7061+
bb0(v0:BasicObject):
7062+
v2:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000))
7063+
v4:ArrayExact = ArrayDup v2
7064+
PatchPoint MethodRedefined(Array@0x1008, max@0x1010)
7065+
v9:BasicObject = SendWithoutBlockDirect v4, :max (0x1018)
7066+
Return v9
7067+
"#]]);
7068+
}
7069+
69767070
#[test]
69777071
fn test_set_type_from_constant() {
69787072
eval("

0 commit comments

Comments
 (0)