Skip to content

Commit 2873c31

Browse files
gh-131798: JIT optimizer: Support custom binary op and property frames (GH-143735)
1 parent dfc66e5 commit 2873c31

File tree

3 files changed

+94
-11
lines changed

3 files changed

+94
-11
lines changed

Lib/test/test_capi/test_opt.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3575,6 +3575,42 @@ def testfunc(n):
35753575
# _POP_TOP_NOP is a sign the optimizer ran and didn't hit bottom.
35763576
self.assertGreaterEqual(count_ops(ex, "_POP_TOP_NOP"), 1)
35773577

3578+
def test_binary_op_subscr_init_frame(self):
3579+
class B:
3580+
def __getitem__(self, other):
3581+
return other + 1
3582+
def testfunc(*args):
3583+
n, b = args[0]
3584+
for _ in range(n):
3585+
y = b[2]
3586+
3587+
res, ex = self._run_with_optimizer(testfunc, (TIER2_THRESHOLD, B()))
3588+
self.assertIsNotNone(ex)
3589+
uops = get_opnames(ex)
3590+
3591+
self.assertIn("_BINARY_OP_SUBSCR_INIT_CALL", uops)
3592+
# _POP_TOP_NOP is a sign the optimizer ran and didn't hit contradiction.
3593+
self.assertGreaterEqual(count_ops(ex, "_POP_TOP_NOP"), 1)
3594+
3595+
def test_load_attr_property_frame(self):
3596+
class B:
3597+
@property
3598+
def prop(self):
3599+
return 3
3600+
def testfunc(*args):
3601+
n, b = args[0]
3602+
for _ in range(n):
3603+
y = b.prop + b.prop
3604+
3605+
testfunc((3, B()))
3606+
res, ex = self._run_with_optimizer(testfunc, (TIER2_THRESHOLD, B()))
3607+
self.assertIsNotNone(ex)
3608+
uops = get_opnames(ex)
3609+
3610+
self.assertIn("_LOAD_ATTR_PROPERTY_FRAME", uops)
3611+
# This is a sign the optimizer ran and didn't hit contradiction.
3612+
self.assertIn("_INSERT_2_LOAD_CONST_INLINE_BORROW", uops)
3613+
35783614
def test_unary_negative(self):
35793615
def testfunc(n):
35803616
a = 3

Python/optimizer_bytecodes.c

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -327,9 +327,20 @@ 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+
if (f == NULL) {
339+
break;
340+
}
341+
f->locals[0] = container;
342+
f->locals[1] = sub;
343+
new_frame = PyJitRef_Wrap((JitOptSymbol *)f);
333344
}
334345

335346
op(_BINARY_OP_SUBSCR_STR_INT, (str_st, sub_st -- res, s, i)) {
@@ -761,9 +772,19 @@ dummy_func(void) {
761772
}
762773

763774
op(_LOAD_ATTR_PROPERTY_FRAME, (fget/4, owner -- new_frame)) {
764-
(void)fget;
765-
new_frame = PyJitRef_NULL;
766-
ctx->done = true;
775+
// + 1 for _SAVE_RETURN_OFFSET
776+
assert((this_instr + 2)->opcode == _PUSH_FRAME);
777+
PyCodeObject *co = get_code_with_logging(this_instr + 2);
778+
if (co == NULL) {
779+
ctx->done = true;
780+
break;
781+
}
782+
_Py_UOpsAbstractFrame *f = frame_new(ctx, co, 0, NULL, 0);
783+
if (f == NULL) {
784+
break;
785+
}
786+
f->locals[0] = owner;
787+
new_frame = PyJitRef_Wrap((JitOptSymbol *)f);
767788
}
768789

769790
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: 31 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)