Skip to content

Commit cb7a69b

Browse files
committed
Add fast-path unary operations and optional heapcache-skipping record helpers
1 parent 1766fd2 commit cb7a69b

File tree

2 files changed

+163
-76
lines changed

2 files changed

+163
-76
lines changed

rpython/jit/codewriter/genextension.py

Lines changed: 89 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1031,6 +1031,64 @@ def emit_unspecialized_float_gt(self):
10311031
def emit_unspecialized_float_ge(self):
10321032
return self._emit_unspecialized_float_comparison_fast("FLOAT_GE", ">=")
10331033

1034+
def _emit_unspecialized_int_unary_fast(self, rop_name, py_op):
1035+
arg0, result = self._get_args_and_res()
1036+
lines = []
1037+
self._emit_sync_registers(lines)
1038+
box0 = self._get_as_box_after_sync(arg0)
1039+
lines.append("_v0 = %s" % self._get_as_unboxed_after_sync(arg0))
1040+
lines.append("_res = %s_v0" % py_op)
1041+
lines.append("_op = self.metainterp.history.record1_int(rop.%s, %s, _res)" % (
1042+
rop_name, box0))
1043+
lines.append("self.registers_i[%d] = _op" % result.index)
1044+
lines.append("i%d = _res" % result.index)
1045+
next_consts = self.constant_registers - {result}
1046+
self._emit_jump(lines, constant_registers=next_consts)
1047+
return lines
1048+
1049+
def _emit_unspecialized_float_unary_fast(self, rop_name, py_op):
1050+
arg0, result = self._get_args_and_res()
1051+
lines = []
1052+
self._emit_sync_registers(lines)
1053+
box0 = self._get_as_box_after_sync(arg0)
1054+
lines.append("_v0 = %s" % self._get_as_unboxed_after_sync(arg0))
1055+
lines.append("_res = %s_v0" % py_op)
1056+
lines.append("_op = self.metainterp.history.record1_float(rop.%s, %s, _res)" % (
1057+
rop_name, box0))
1058+
lines.append("self.registers_f[%d] = _op" % result.index)
1059+
lines.append("f%d = _res" % result.index)
1060+
next_consts = self.constant_registers - {result}
1061+
self._emit_jump(lines, constant_registers=next_consts)
1062+
return lines
1063+
1064+
def _emit_unspecialized_int_is_true_fast(self):
1065+
arg0, result = self._get_args_and_res()
1066+
lines = []
1067+
self._emit_sync_registers(lines)
1068+
box0 = self._get_as_box_after_sync(arg0)
1069+
lines.append("_v0 = %s" % self._get_as_unboxed_after_sync(arg0))
1070+
lines.append("_res = int(bool(_v0))")
1071+
lines.append("_op = self.metainterp.history.record1_int(rop.INT_IS_TRUE, %s, _res)" % box0)
1072+
lines.append("self.registers_i[%d] = _op" % result.index)
1073+
lines.append("i%d = _res" % result.index)
1074+
next_consts = self.constant_registers - {result}
1075+
self._emit_jump(lines, constant_registers=next_consts)
1076+
return lines
1077+
1078+
def _emit_unspecialized_int_is_zero_fast(self):
1079+
arg0, result = self._get_args_and_res()
1080+
lines = []
1081+
self._emit_sync_registers(lines)
1082+
box0 = self._get_as_box_after_sync(arg0)
1083+
lines.append("_v0 = %s" % self._get_as_unboxed_after_sync(arg0))
1084+
lines.append("_res = int(_v0 == 0)")
1085+
lines.append("_op = self.metainterp.history.record1_int(rop.INT_IS_ZERO, %s, _res)" % box0)
1086+
lines.append("self.registers_i[%d] = _op" % result.index)
1087+
lines.append("i%d = _res" % result.index)
1088+
next_consts = self.constant_registers - {result}
1089+
self._emit_jump(lines, constant_registers=next_consts)
1090+
return lines
1091+
10341092
def _emit_jump(self, lines, target_pc=-1, constant_registers=None, indent=''):
10351093
if target_pc == -1:
10361094
target_pc = self.work_list.pc_to_nextpc[self.orig_pc]
@@ -1562,68 +1620,46 @@ def _emit_unspecialized_float_binary(self):
15621620
emit_unspecialized_float_ge = _emit_unspecialized_float_binary
15631621

15641622
def emit_unspecialized_int_neg(self):
1565-
lines = []
1566-
arg0, result = self._get_args_and_res()
1567-
self._emit_n_ary_if([arg0], lines)
1568-
self._emit_jump(lines, constant_registers=self.constant_registers.union({arg0}),
1569-
indent=' ', target_pc=self.orig_pc)
1570-
lines.append("else:")
1571-
lines.append(" self.registers_i[%d] = self.%s(%s)" % (
1572-
result.index, self.methodname,
1573-
self._get_as_box(arg0)))
1574-
self._emit_jump(lines, indent=' ')
1575-
return lines
1623+
return self._emit_unspecialized_int_unary_fast("INT_NEG", "-")
15761624

1577-
def emit_unspecialized_int_abs(self):
1578-
lines = []
1579-
arg0, result = self._get_args_and_res()
1580-
self._emit_n_ary_if([arg0], lines)
1581-
self._emit_jump(lines, constant_registers=self.constant_registers.union({arg0}),
1582-
indent=' ', target_pc=self.orig_pc)
1583-
lines.append("else:")
1584-
lines.append(" self.registers_i[%d] = self.%s(%s)" % (
1585-
result.index, self.methodname,
1586-
self._get_as_box(arg0)))
1587-
self._emit_jump(lines, indent=' ')
1588-
return lines
1625+
def emit_unspecialized_int_invert(self):
1626+
return self._emit_unspecialized_int_unary_fast("INT_INVERT", "~")
1627+
1628+
def emit_unspecialized_int_is_true(self):
1629+
return self._emit_unspecialized_int_is_true_fast()
1630+
1631+
def emit_unspecialized_int_is_zero(self):
1632+
return self._emit_unspecialized_int_is_zero_fast()
15891633

15901634
def emit_unspecialized_float_neg(self):
1591-
lines = []
1592-
arg0, result = self._get_args_and_res()
1593-
self._emit_n_ary_if([arg0], lines)
1594-
self._emit_jump(lines, constant_registers=self.constant_registers.union({arg0}),
1595-
indent=' ', target_pc=self.orig_pc)
1596-
lines.append("else:")
1597-
lines.append(" self.registers_f[%d] = self.%s(%s)" % (
1598-
result.index, self.methodname,
1599-
self._get_as_box(arg0)))
1600-
self._emit_jump(lines)
1601-
return lines
1635+
return self._emit_unspecialized_float_unary_fast("FLOAT_NEG", "-")
16021636

16031637
def emit_unspecialized_float_abs(self):
1604-
lines = []
16051638
arg0, result = self._get_args_and_res()
1606-
self._emit_n_ary_if([arg0], lines)
1607-
self._emit_jump(lines, constant_registers=self.constant_registers.union({arg0}),
1608-
indent=' ', target_pc=self.orig_pc)
1609-
lines.append("else:")
1610-
lines.append(" self.registers_f[%d] = self.%s(%s)" % (
1611-
result.index, self.methodname,
1612-
self._get_as_box(arg0)))
1613-
self._emit_jump(lines)
1639+
lines = []
1640+
self._emit_sync_registers(lines)
1641+
box0 = self._get_as_box_after_sync(arg0)
1642+
lines.append("_v0 = %s" % self._get_as_unboxed_after_sync(arg0))
1643+
lines.append("_res = abs(_v0)")
1644+
lines.append("_op = self.metainterp.history.record1_float(rop.FLOAT_ABS, %s, _res)" % box0)
1645+
lines.append("self.registers_f[%d] = _op" % result.index)
1646+
lines.append("f%d = _res" % result.index)
1647+
next_consts = self.constant_registers - {result}
1648+
self._emit_jump(lines, constant_registers=next_consts)
16141649
return lines
16151650

1616-
def emit_unspecialized_int_invert(self):
1617-
lines = []
1651+
def emit_unspecialized_int_force_ge_zero(self):
16181652
arg0, result = self._get_args_and_res()
1619-
self._emit_n_ary_if([arg0], lines)
1620-
self._emit_jump(lines, constant_registers=self.constant_registers.union({arg0}),
1621-
indent=' ', target_pc=self.orig_pc)
1622-
lines.append("else:")
1623-
lines.append(" self.registers_i[%d] = self.%s(%s)" % (
1624-
result.index, self.methodname,
1625-
self._get_as_box(arg0)))
1626-
self._emit_jump(lines)
1653+
lines = []
1654+
self._emit_sync_registers(lines)
1655+
box0 = self._get_as_box_after_sync(arg0)
1656+
lines.append("_v0 = %s" % self._get_as_unboxed_after_sync(arg0))
1657+
lines.append("_res = _v0 if _v0 >= 0 else 0")
1658+
lines.append("_op = self.metainterp.history.record1_int(rop.INT_FORCE_GE_ZERO, %s, _res)" % box0)
1659+
lines.append("self.registers_i[%d] = _op" % result.index)
1660+
lines.append("i%d = _res" % result.index)
1661+
next_consts = self.constant_registers - {result}
1662+
self._emit_jump(lines, constant_registers=next_consts)
16271663
return lines
16281664

16291665
def emit_unspecialized_strgetitem(self):
@@ -1664,21 +1700,6 @@ def emit_unspecialized_guard_value(self):
16641700
emit_unspecialized_int_guard_value = emit_unspecialized_guard_value
16651701
emit_unspecialized_ref_guard_value = emit_unspecialized_guard_value
16661702

1667-
def emit_unspecialized_int_is_true(self):
1668-
args = self._get_args()
1669-
res = self.insn[self.resindex]
1670-
arg, = self._get_args()
1671-
result = self.insn[self.resindex]
1672-
lines = []
1673-
self._emit_n_ary_if([arg], lines)
1674-
self._emit_jump(lines, constant_registers=self.constant_registers.union({arg}),
1675-
indent=' ', target_pc=self.orig_pc)
1676-
lines.append("self.registers_%s[%s] = self.opimpl_%s(%s)" % (
1677-
result.kind[0], result.index,
1678-
self.insn[0], self._get_as_box(arg)))
1679-
self._emit_jump(lines)
1680-
return lines
1681-
16821703
def emit_unspecialized_guard_class(self):
16831704
arg0 = self.insn[1]
16841705
res = self.insn[self.resindex]

rpython/jit/metainterp/pyjitpl.py

Lines changed: 74 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,37 @@
2727
from rpython.rlib.unroll import unrolling_iterable
2828
from rpython.rtyper.lltypesystem import lltype, rffi, llmemory
2929
from rpython.rtyper import rclass
30+
import os
3031

3132
SIZE_LIVE_OP = OFFSET_SIZE + 1
3233

34+
SKIP_HEAPCACHE_PURE_INT = os.environ.get('PYPY_SKIP_HEAPCACHE_PURE_INT', '') == '1'
35+
FAST_INT_RECORD = os.environ.get('PYPY_FAST_INT_RECORD', '') == '1'
36+
37+
class PyjitplCounters(object):
38+
_record_helper_calls = 0
39+
_record_int_binop_calls = 0
40+
_heapcache_skipped = 0
41+
_heapcache_called = 0
42+
43+
@staticmethod
44+
def reset():
45+
PyjitplCounters._record_helper_calls = 0
46+
PyjitplCounters._record_int_binop_calls = 0
47+
PyjitplCounters._heapcache_skipped = 0
48+
PyjitplCounters._heapcache_called = 0
49+
50+
@staticmethod
51+
def report():
52+
if not we_are_translated():
53+
total = PyjitplCounters._record_helper_calls + PyjitplCounters._record_int_binop_calls
54+
if total > 0:
55+
debug_print("PYJITPL_COUNTERS: record_helper=%d record_int_binop=%d heapcache_skipped=%d heapcache_called=%d" % (
56+
PyjitplCounters._record_helper_calls,
57+
PyjitplCounters._record_int_binop_calls,
58+
PyjitplCounters._heapcache_skipped,
59+
PyjitplCounters._heapcache_called))
60+
3361
CONST_0 = ConstInt(0)
3462
CONST_1 = ConstInt(1)
3563

@@ -297,11 +325,6 @@ def opimpl_%s(self, b1, b2):
297325
def special_int_add(self, position):
298326
from rpython.jit.metainterp.blackhole import signedord
299327
from rpython.jit.metainterp.history import IntFrontendOp
300-
# bit of a micro-optimization: int_add with a constant argument is one
301-
# of the most common opcodes in PyPy, and this way we
302-
# - allocate one ConstInt fewer in the (common) non-recorded case
303-
# - don't wrap and unwrap
304-
# - only check one of the arguments for constness
305328
assert position >= 0
306329
code = self.bytecode
307330
position += 1
@@ -321,7 +344,10 @@ def special_int_add(self, position):
321344
else:
322345
assert isinstance(b1, IntFrontendOp)
323346
resvalue = b1.getint() + c
324-
resbox = self.metainterp._record_helper(rop.INT_ADD, resvalue, None, b1, ConstInt(c))
347+
if FAST_INT_RECORD:
348+
resbox = self.metainterp._record_int_binop(rop.INT_ADD, resvalue, b1, ConstInt(c))
349+
else:
350+
resbox = self.metainterp._record_helper(rop.INT_ADD, resvalue, None, b1, ConstInt(c))
325351
regs[ord(code[position])] = resbox
326352
self.pc = position + 1
327353

@@ -2785,10 +2811,17 @@ def _record_helper_varargs(self, opnum, resvalue, descr, argboxes):
27852811

27862812
@specialize.arg(1)
27872813
def _record_helper(self, opnum, resvalue, descr, *argboxes):
2788-
# record the operation
2814+
if not we_are_translated():
2815+
PyjitplCounters._record_helper_calls += 1
27892816
profiler = self.staticdata.profiler
27902817
profiler.count_ops(opnum, Counters.RECORDED_OPS)
2791-
self.heapcache.invalidate_caches(opnum, descr, *argboxes)
2818+
if SKIP_HEAPCACHE_PURE_INT and self.heapcache._is_pure_int_op(opnum):
2819+
if not we_are_translated():
2820+
PyjitplCounters._heapcache_skipped += 1
2821+
else:
2822+
if not we_are_translated():
2823+
PyjitplCounters._heapcache_called += 1
2824+
self.heapcache.invalidate_caches(opnum, descr, *argboxes)
27922825
if self.framestack:
27932826
self.framestack[-1].jitcode.traced_operations += 1
27942827

@@ -2811,6 +2844,39 @@ def _record_helper(self, opnum, resvalue, descr, *argboxes):
28112844
if op.type != 'v':
28122845
return op
28132846

2847+
@always_inline
2848+
def _record_int_binop(self, opnum, resvalue, b1, b2):
2849+
if not we_are_translated():
2850+
PyjitplCounters._record_int_binop_calls += 1
2851+
profiler = self.staticdata.profiler
2852+
profiler.count_ops(opnum, Counters.RECORDED_OPS)
2853+
if self.framestack:
2854+
self.framestack[-1].jitcode.traced_operations += 1
2855+
op = self.history.record2_int(opnum, b1, b2, resvalue)
2856+
return op
2857+
2858+
@always_inline
2859+
def _record_int_unop(self, opnum, resvalue, b1):
2860+
if not we_are_translated():
2861+
PyjitplCounters._record_int_binop_calls += 1
2862+
profiler = self.staticdata.profiler
2863+
profiler.count_ops(opnum, Counters.RECORDED_OPS)
2864+
if self.framestack:
2865+
self.framestack[-1].jitcode.traced_operations += 1
2866+
op = self.history.record1_int(opnum, b1, resvalue)
2867+
return op
2868+
2869+
@always_inline
2870+
def _record_float_binop(self, opnum, resvalue, b1, b2):
2871+
if not we_are_translated():
2872+
PyjitplCounters._record_int_binop_calls += 1
2873+
profiler = self.staticdata.profiler
2874+
profiler.count_ops(opnum, Counters.RECORDED_OPS)
2875+
if self.framestack:
2876+
self.framestack[-1].jitcode.traced_operations += 1
2877+
op = self.history.record2_float(opnum, b1, b2, resvalue)
2878+
return op
2879+
28142880
def execute_new_with_vtable(self, descr):
28152881
resbox = self.execute_and_record(rop.NEW_WITH_VTABLE, descr)
28162882
self.heapcache.new(resbox)

0 commit comments

Comments
 (0)