Skip to content

Commit a9c244a

Browse files
JIT: Support custom binary op and property frames
1 parent 054a565 commit a9c244a

3 files changed

Lines changed: 83 additions & 11 deletions

File tree

Lib/test/test_capi/test_opt.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3487,6 +3487,44 @@ def testfunc(n):
34873487
# _POP_TOP_NOP is a sign the optimizer ran and didn't hit bottom.
34883488
self.assertGreaterEqual(count_ops(ex, "_POP_TOP_NOP"), 1)
34893489

3490+
def test_binary_op_subscr_init_frame(self):
3491+
class B:
3492+
def __getitem__(self, other):
3493+
return other + 1
3494+
def testfunc(*args):
3495+
n, b = args[0]
3496+
for _ in range(n):
3497+
y = b[2]
3498+
3499+
res, ex = self._run_with_optimizer(testfunc, (TIER2_THRESHOLD, B()))
3500+
self.assertIsNotNone(ex)
3501+
uops = get_opnames(ex)
3502+
3503+
self.assertIn("_BINARY_OP_SUBSCR_INIT_CALL", uops)
3504+
# _POP_TOP_NOP is a sign the optimizer ran and didn't hit bottom.
3505+
self.assertGreaterEqual(count_ops(ex, "_POP_TOP_NOP"), 1)
3506+
3507+
def test_load_attr_property_frame(self):
3508+
class B:
3509+
@property
3510+
def prop(self):
3511+
return 3
3512+
def testfunc(*args):
3513+
n, b = args[0]
3514+
for _ in range(n):
3515+
y = b.prop + b.prop
3516+
3517+
testfunc((3, B()))
3518+
import dis
3519+
dis.dis(testfunc, adaptive=True)
3520+
res, ex = self._run_with_optimizer(testfunc, (TIER2_THRESHOLD, B()))
3521+
self.assertIsNotNone(ex)
3522+
uops = get_opnames(ex)
3523+
3524+
self.assertIn("_LOAD_ATTR_PROPERTY_FRAME", uops)
3525+
# This is a sign the optimizer ran and didn't hit bottom.
3526+
self.assertIn("_INSERT_2_LOAD_CONST_INLINE_BORROW", uops)
3527+
34903528
def test_unary_negative(self):
34913529
def testfunc(n):
34923530
a = 3

Python/optimizer_bytecodes.c

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -327,9 +327,17 @@ dummy_func(void) {
327327
GETLOCAL(this_instr->operand0) = sym_new_null(ctx);
328328
}
329329

330-
op(_BINARY_OP_SUBSCR_INIT_CALL, (container, sub, getitem -- new_frame)) {
331-
new_frame = PyJitRef_NULL;
332-
ctx->done = true;
330+
op(_BINARY_OP_SUBSCR_INIT_CALL, (container, sub, getitem -- new_frame)) {
331+
assert((this_instr + 1)->opcode == _PUSH_FRAME);
332+
PyCodeObject *co = get_code_with_logging(this_instr + 1);
333+
if (co == NULL) {
334+
ctx->done = true;
335+
break;
336+
}
337+
_Py_UOpsAbstractFrame *f = frame_new(ctx, co, 0, NULL, 0);
338+
f->locals[0] = container;
339+
f->locals[1] = sub;
340+
new_frame = PyJitRef_Wrap((JitOptSymbol *)f);
333341
}
334342

335343
op(_BINARY_OP_SUBSCR_STR_INT, (str_st, sub_st -- res, s, i)) {
@@ -753,9 +761,15 @@ dummy_func(void) {
753761
}
754762

755763
op(_LOAD_ATTR_PROPERTY_FRAME, (fget/4, owner -- new_frame)) {
756-
(void)fget;
757-
new_frame = PyJitRef_NULL;
758-
ctx->done = true;
764+
assert((this_instr + 2)->opcode == _PUSH_FRAME);
765+
PyCodeObject *co = get_code_with_logging(this_instr + 2);
766+
if (co == NULL) {
767+
ctx->done = true;
768+
break;
769+
}
770+
_Py_UOpsAbstractFrame *f = frame_new(ctx, co, 0, NULL, 0);
771+
f->locals[0] = owner;
772+
new_frame = PyJitRef_Wrap((JitOptSymbol *)f);
759773
}
760774

761775
op(_INIT_CALL_BOUND_METHOD_EXACT_ARGS, (callable, self_or_null, unused[oparg] -- callable, self_or_null, unused[oparg])) {

Python/optimizer_cases.c.h

Lines changed: 25 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)