Skip to content

Commit 69ab1ef

Browse files
committed
Add fast path for goto_if
1 parent 2397d7c commit 69ab1ef

File tree

2 files changed

+122
-29
lines changed

2 files changed

+122
-29
lines changed

rpython/jit/codewriter/genextension.py

Lines changed: 86 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1127,6 +1127,22 @@ def emit_specialized_int_is_true(self):
11271127
self._emit_jump(lines)
11281128
return lines
11291129

1130+
def emit_specialized_int_is_zero(self):
1131+
arg, = self._get_args()
1132+
result = self.insn[self.resindex]
1133+
# Constant folding: if arg is a constant, evaluate at compile time
1134+
val = self._get_constant_int_value(arg)
1135+
if val is not None:
1136+
lines = ["i%s = %d" % (result.index, int(val == 0))]
1137+
self._emit_jump(lines)
1138+
return lines
1139+
lines = ["i%s = int(%s == 0)" % (
1140+
result.index,
1141+
self._get_as_unboxed(arg)
1142+
)]
1143+
self._emit_jump(lines)
1144+
return lines
1145+
11301146
def emit_specialized_guard_class(self):
11311147
lines = ['# guard_class, argument is already constant']
11321148
arg, = self._get_args()
@@ -1146,6 +1162,8 @@ def emit_specialized_getfield_raw_i(self):
11461162
self._emit_jump(lines, constant_registers=self.constant_registers.union({res}))
11471163
return lines
11481164
raise Unsupported
1165+
emit_specialized_getfield_raw_r = emit_specialized_getfield_raw_i
1166+
emit_specialized_getfield_raw_f = emit_specialized_getfield_raw_i
11491167

11501168
def emit_specialized_getfield_gc_i_pure(self):
11511169
if self.insn[2].is_always_pure():
@@ -1175,6 +1193,19 @@ def emit_specialized_getarrayitem_gc_i_pure(self):
11751193
emit_specialized_getarrayitem_gc_r_pure = emit_specialized_getarrayitem_gc_i_pure
11761194
emit_specialized_getarrayitem_gc_f_pure = emit_specialized_getarrayitem_gc_i_pure
11771195

1196+
def emit_specialized_getarrayitem_raw_i_pure(self):
1197+
lines = []
1198+
arg, index, descr, res = self._get_args_and_res()
1199+
TYPE, ITEM = _get_ptrtype_itemtype_from_arraydescr(descr)
1200+
resultcast = _find_result_cast(ITEM)
1201+
lines.append('%s = %sllmemory.cast_adr_to_ptr(support.int2adr(i%s), %s)[%s])' % (
1202+
self._get_as_unboxed(res), resultcast, arg.index, self._add_global(TYPE),
1203+
self._get_as_unboxed(index)))
1204+
self._emit_jump(lines, constant_registers=self.constant_registers.union({res}))
1205+
return lines
1206+
emit_specialized_getarrayitem_raw_r_pure = emit_specialized_getarrayitem_raw_i_pure
1207+
emit_specialized_getarrayitem_raw_f_pure = emit_specialized_getarrayitem_raw_i_pure
1208+
11781209
def emit_specialized_getfield_gc_i(self):
11791210
arg, descr = self._get_args()
11801211
res = self.insn[self.resindex]
@@ -1430,6 +1461,14 @@ def emit_specialized_unicodelen(self):
14301461
self._emit_jump(lines)
14311462
return lines
14321463

1464+
def emit_specialized_unicodegetitem(self):
1465+
arg0, arg1 = self._get_args()
1466+
result = self.insn[self.resindex]
1467+
lines = ["i%s = ord(lltype.cast_opaque_ptr(lltype.Ptr(rstr.UNICODE), r%d).chars[%s])" % (
1468+
result.index, arg0.index, self._get_as_unboxed(arg1))]
1469+
self._emit_jump(lines)
1470+
return lines
1471+
14331472
def _get_type_prefix(self, arg):
14341473
if isinstance(arg, Constant) or isinstance(arg, Register):
14351474
# TODO: this logic also works for the 'else' case. probably.
@@ -2039,23 +2078,64 @@ def emit_unspecialized_goto_if_not_comparison(self, name, symbol):
20392078
lines.append("continue")
20402079
return lines
20412080

2081+
def _emit_goto_if_not_int_comparison_fast(self, rop_name, py_op):
2082+
"""Generate fast-path code for goto_if_not_int_* that skips heapcache."""
2083+
lines = []
2084+
_, arg0, arg1, arg2 = self.insn # left, right, label
2085+
2086+
target_pc = self.get_target_pc(arg2)
2087+
2088+
# Handle constant folding case
2089+
self._emit_n_ary_if([arg0, arg1], lines)
2090+
specializer = self.work_list.specialize_insn(
2091+
self.insn, self.constant_registers.union({arg0, arg1}), self.orig_pc)
2092+
lines.append(" pc = %d" % (specializer.get_pc(),))
2093+
lines.append(" continue")
2094+
2095+
# Fast-path: compute comparison and record directly, skip heapcache
2096+
self._emit_sync_registers(lines)
2097+
box0 = self._get_as_box_after_sync(arg0)
2098+
box1 = self._get_as_box_after_sync(arg1)
2099+
lines.append("_v0 = %s" % self._get_as_unboxed_after_sync(arg0))
2100+
lines.append("_v1 = %s" % self._get_as_unboxed_after_sync(arg1))
2101+
lines.append("_cond = int(_v0 %s _v1)" % py_op)
2102+
lines.append("# fast-path: record comparison directly, skip heapcache")
2103+
lines.append("condbox = self.metainterp.history.record2_int(rop.%s, %s, %s, _cond)" % (
2104+
rop_name, box0, box1))
2105+
2106+
# Call opimpl_goto_if_not for guard generation
2107+
lines.append("self.opimpl_goto_if_not(condbox, %d, %d, replace=False)" % (target_pc, self.orig_pc))
2108+
lines.append("pc = self.pc")
2109+
lines.append("if pc == %s:" % (target_pc,))
2110+
specializer = self.work_list.specialize_pc(
2111+
self.constant_registers, target_pc)
2112+
lines.append(" pc = %s" % (specializer.spec_pc,))
2113+
lines.append("else:")
2114+
next_pc = self.work_list.pc_to_nextpc[self.orig_pc]
2115+
specializer = self.work_list.specialize_pc(
2116+
self.constant_registers, next_pc)
2117+
lines.append(" assert self.pc == %s" % (specializer.orig_pc,))
2118+
lines.append(" pc = %s" % (specializer.spec_pc,))
2119+
lines.append("continue")
2120+
return lines
2121+
20422122
def emit_unspecialized_goto_if_not_int_lt(self):
2043-
return self.emit_unspecialized_goto_if_not_comparison("int_lt", "<")
2123+
return self._emit_goto_if_not_int_comparison_fast("INT_LT", "<")
20442124

20452125
def emit_unspecialized_goto_if_not_int_gt(self):
2046-
return self.emit_unspecialized_goto_if_not_comparison("int_gt", ">")
2126+
return self._emit_goto_if_not_int_comparison_fast("INT_GT", ">")
20472127

20482128
def emit_unspecialized_goto_if_not_int_le(self):
2049-
return self.emit_unspecialized_goto_if_not_comparison("int_le", "<=")
2129+
return self._emit_goto_if_not_int_comparison_fast("INT_LE", "<=")
20502130

20512131
def emit_unspecialized_goto_if_not_int_ge(self):
2052-
return self.emit_unspecialized_goto_if_not_comparison("int_ge", ">=")
2132+
return self._emit_goto_if_not_int_comparison_fast("INT_GE", ">=")
20532133

20542134
def emit_unspecialized_goto_if_not_int_ne(self):
2055-
return self.emit_unspecialized_goto_if_not_comparison("int_ne", "!=")
2135+
return self._emit_goto_if_not_int_comparison_fast("INT_NE", "!=")
20562136

20572137
def emit_unspecialized_goto_if_not_int_eq(self):
2058-
return self.emit_unspecialized_goto_if_not_comparison("int_eq", "==")
2138+
return self._emit_goto_if_not_int_comparison_fast("INT_EQ", "==")
20592139

20602140
def emit_unspecialized_switch(self):
20612141
lines = []

rpython/jit/codewriter/test/test_genextension.py

Lines changed: 36 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,12 @@ def jit_shortcut(self): # test
5656
i22 = ri22.getint()
5757
pc = 116
5858
continue
59-
condbox = self.opimpl_int_gt(ri22, ConstInt(4))
60-
self.opimpl_goto_if_not(condbox, 16, 0)
59+
_v0 = self.registers_i[22].getint()
60+
_v1 = 4
61+
_cond = int(_v0 > _v1)
62+
# fast-path: record comparison directly, skip heapcache
63+
condbox = self.metainterp.history.record2_int(rop.INT_GT, self.registers_i[22], ConstInt(4), _cond)
64+
self.opimpl_goto_if_not(condbox, 16, 0, replace=False)
6165
pc = self.pc
6266
if pc == 16:
6367
pc = 16
@@ -627,16 +631,15 @@ def test_int_invert():
627631
newpc = insn_specializer.get_pc()
628632
assert newpc == 5
629633
s = insn_specializer.make_code()
634+
# fast-path: directly compute and record, skip heapcache
630635
assert s == """\
631-
ri0 = self.registers_i[0]
632-
if isinstance(ri0, ConstInt):
633-
i0 = ri0.getint()
634-
pc = %d
635-
continue
636-
else:
637-
self.registers_i[1] = self.opimpl_int_invert(ri0)
636+
_v0 = self.registers_i[0].getint()
637+
_res = ~_v0
638+
_op = self.metainterp.history.record1_int(rop.INT_INVERT, self.registers_i[0], _res)
639+
self.registers_i[1] = _op
640+
i1 = _res
638641
pc = 7
639-
continue""" % (work_list.OFFSET + 7)
642+
continue"""
640643
next_constant_registers = insn_specializer.get_next_constant_registers()
641644
assert next_constant_registers == set()
642645

@@ -684,6 +687,7 @@ def test_goto_if_not_int_lt():
684687
newpc = insn_specializer.get_pc()
685688
assert newpc == 5
686689
s = insn_specializer.make_code()
690+
# fast-path: directly compute and record comparison, skip heapcache
687691
assert s == """\
688692
ri0 = self.registers_i[0]
689693
ri1 = self.registers_i[1]
@@ -692,8 +696,12 @@ def test_goto_if_not_int_lt():
692696
i1 = ri1.getint()
693697
pc = 117
694698
continue
695-
condbox = self.opimpl_int_lt(ri0, ri1)
696-
self.opimpl_goto_if_not(condbox, 17, 5)
699+
_v0 = self.registers_i[0].getint()
700+
_v1 = self.registers_i[1].getint()
701+
_cond = int(_v0 < _v1)
702+
# fast-path: record comparison directly, skip heapcache
703+
condbox = self.metainterp.history.record2_int(rop.INT_LT, self.registers_i[0], self.registers_i[1], _cond)
704+
self.opimpl_goto_if_not(condbox, 17, 5, replace=False)
697705
pc = self.pc
698706
if pc == 17:
699707
pc = 17
@@ -702,9 +710,10 @@ def test_goto_if_not_int_lt():
702710
pc = 6
703711
continue"""
704712

705-
# unspecialized case
713+
# unspecialized case with constant register
706714
insn_specializer = work_list.specialize_pc({i2}, 5)
707715
s = insn_specializer.make_code()
716+
# fast-path with register sync
708717
assert s == """\
709718
ri0 = self.registers_i[0]
710719
ri1 = self.registers_i[1]
@@ -713,9 +722,13 @@ def test_goto_if_not_int_lt():
713722
i1 = ri1.getint()
714723
pc = 119
715724
continue
716-
condbox = self.opimpl_int_lt(ri0, ri1)
717725
glob0(self, i2) # jit_sync_regs_i2
718-
self.opimpl_goto_if_not(condbox, 17, 5)
726+
_v0 = self.registers_i[0].getint()
727+
_v1 = self.registers_i[1].getint()
728+
_cond = int(_v0 < _v1)
729+
# fast-path: record comparison directly, skip heapcache
730+
condbox = self.metainterp.history.record2_int(rop.INT_LT, self.registers_i[0], self.registers_i[1], _cond)
731+
self.opimpl_goto_if_not(condbox, 17, 5, replace=False)
719732
pc = self.pc
720733
if pc == 17:
721734
pc = 120
@@ -1443,15 +1456,15 @@ def test_int_is_true():
14431456
newpc = insn_specializer.get_pc()
14441457
assert newpc == 5
14451458
s = insn_specializer.make_code()
1459+
# fast-path: directly compute and record, skip heapcache
14461460
assert s == """\
1447-
ri0 = self.registers_i[0]
1448-
if isinstance(ri0, ConstInt):
1449-
i0 = ri0.getint()
1450-
pc = %d
1451-
continue
1452-
self.registers_i[1] = self.opimpl_int_is_true(ri0)
1461+
_v0 = self.registers_i[0].getint()
1462+
_res = int(bool(_v0))
1463+
_op = self.metainterp.history.record1_int(rop.INT_IS_TRUE, self.registers_i[0], _res)
1464+
self.registers_i[1] = _op
1465+
i1 = _res
14531466
pc = 7
1454-
continue""" % (work_list.OFFSET + 7)
1467+
continue"""
14551468
next_constant_registers = insn_specializer.get_next_constant_registers()
14561469
assert next_constant_registers == set()
14571470

@@ -1552,7 +1565,7 @@ def get_extra_info(self):
15521565
continue
15531566
else:
15541567
ri0 = self.registers_i[0]
1555-
v0 = self.do_residual_call(ConstPtr(lltype.cast_opaque_ptr(llmemory.GCREF, glob3)), [ri0], glob2, 5)
1568+
v0 = self.do_residual_call(ConstPtr(lltype.cast_opaque_ptr(llmemory.GCREF, glob3)), [self.registers_i[0]], glob2, 5)
15561569
i1 = v0.getint()
15571570
self.registers_i[1] = v0
15581571
pc = 7

0 commit comments

Comments
 (0)