Skip to content

Commit 2f0570c

Browse files
authored
GH-131798: Narrow types more aggressively in the JIT (GH-134373)
1 parent e1c0c45 commit 2f0570c

File tree

10 files changed

+179
-87
lines changed

10 files changed

+179
-87
lines changed

Include/internal/pycore_uop_ids.h

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

Include/internal/pycore_uop_metadata.h

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

Lib/test/test_capi/test_opt.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2137,6 +2137,25 @@ def testfunc(n):
21372137
self.assertNotIn("_TO_BOOL_BOOL", uops)
21382138
self.assertIn("_GUARD_IS_TRUE_POP", uops)
21392139

2140+
def test_set_type_version_sets_type(self):
2141+
class C:
2142+
A = 1
2143+
2144+
def testfunc(n):
2145+
x = 0
2146+
c = C()
2147+
for _ in range(n):
2148+
x += c.A # Guarded.
2149+
x += type(c).A # Unguarded!
2150+
return x
2151+
2152+
res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD)
2153+
self.assertEqual(res, 2 * TIER2_THRESHOLD)
2154+
self.assertIsNotNone(ex)
2155+
uops = get_opnames(ex)
2156+
self.assertIn("_GUARD_TYPE_VERSION", uops)
2157+
self.assertNotIn("_CHECK_ATTR_CLASS", uops)
2158+
21402159

21412160
def global_identity(x):
21422161
return x
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Improve the JIT's ability to narrow unknown classes to constant values.

Python/bytecodes.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,11 @@ dummy_func(
344344
PyStackRef_CLOSE(value);
345345
}
346346

347+
tier2 op(_POP_TWO, (nos, tos --)) {
348+
PyStackRef_CLOSE(tos);
349+
PyStackRef_CLOSE(nos);
350+
}
351+
347352
pure inst(PUSH_NULL, (-- res)) {
348353
res = PyStackRef_NULL;
349354
}

Python/executor_cases.c.h

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

Python/optimizer_analysis.c

Lines changed: 33 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -523,6 +523,25 @@ optimize_uops(
523523

524524
}
525525

526+
const uint16_t op_without_push[MAX_UOP_ID + 1] = {
527+
[_COPY] = _NOP,
528+
[_LOAD_CONST_INLINE] = _NOP,
529+
[_LOAD_CONST_INLINE_BORROW] = _NOP,
530+
[_LOAD_FAST] = _NOP,
531+
[_LOAD_FAST_BORROW] = _NOP,
532+
[_LOAD_SMALL_INT] = _NOP,
533+
[_POP_TOP_LOAD_CONST_INLINE] = _POP_TOP,
534+
[_POP_TOP_LOAD_CONST_INLINE_BORROW] = _POP_TOP,
535+
[_POP_TWO_LOAD_CONST_INLINE_BORROW] = _POP_TWO,
536+
};
537+
538+
const uint16_t op_without_pop[MAX_UOP_ID + 1] = {
539+
[_POP_TOP] = _NOP,
540+
[_POP_TOP_LOAD_CONST_INLINE] = _LOAD_CONST_INLINE,
541+
[_POP_TOP_LOAD_CONST_INLINE_BORROW] = _LOAD_CONST_INLINE_BORROW,
542+
[_POP_TWO_LOAD_CONST_INLINE_BORROW] = _POP_TOP_LOAD_CONST_INLINE_BORROW,
543+
};
544+
526545

527546
static int
528547
remove_unneeded_uops(_PyUOpInstruction *buffer, int buffer_size)
@@ -551,50 +570,23 @@ remove_unneeded_uops(_PyUOpInstruction *buffer, int buffer_size)
551570
buffer[pc].opcode = _NOP;
552571
}
553572
break;
554-
case _POP_TOP:
555-
case _POP_TOP_LOAD_CONST_INLINE:
556-
case _POP_TOP_LOAD_CONST_INLINE_BORROW:
557-
case _POP_TWO_LOAD_CONST_INLINE_BORROW:
558-
optimize_pop_top_again:
573+
default:
559574
{
560-
_PyUOpInstruction *last = &buffer[pc-1];
561-
while (last->opcode == _NOP) {
562-
last--;
563-
}
564-
switch (last->opcode) {
565-
case _POP_TWO_LOAD_CONST_INLINE_BORROW:
566-
last->opcode = _POP_TOP;
575+
// Cancel out pushes and pops, repeatedly. So:
576+
// _LOAD_FAST + _POP_TWO_LOAD_CONST_INLINE_BORROW + _POP_TOP
577+
// ...becomes:
578+
// _NOP + _POP_TOP + _NOP
579+
while (op_without_pop[opcode]) {
580+
_PyUOpInstruction *last = &buffer[pc - 1];
581+
while (last->opcode == _NOP) {
582+
last--;
583+
}
584+
if (!op_without_push[last->opcode]) {
567585
break;
568-
case _POP_TOP_LOAD_CONST_INLINE:
569-
case _POP_TOP_LOAD_CONST_INLINE_BORROW:
570-
last->opcode = _NOP;
571-
goto optimize_pop_top_again;
572-
case _COPY:
573-
case _LOAD_CONST_INLINE:
574-
case _LOAD_CONST_INLINE_BORROW:
575-
case _LOAD_FAST:
576-
case _LOAD_FAST_BORROW:
577-
case _LOAD_SMALL_INT:
578-
last->opcode = _NOP;
579-
if (opcode == _POP_TOP) {
580-
opcode = buffer[pc].opcode = _NOP;
581-
}
582-
else if (opcode == _POP_TOP_LOAD_CONST_INLINE) {
583-
opcode = buffer[pc].opcode = _LOAD_CONST_INLINE;
584-
}
585-
else if (opcode == _POP_TOP_LOAD_CONST_INLINE_BORROW) {
586-
opcode = buffer[pc].opcode = _LOAD_CONST_INLINE_BORROW;
587-
}
588-
else {
589-
assert(opcode == _POP_TWO_LOAD_CONST_INLINE_BORROW);
590-
opcode = buffer[pc].opcode = _POP_TOP_LOAD_CONST_INLINE_BORROW;
591-
goto optimize_pop_top_again;
592-
}
586+
}
587+
last->opcode = op_without_push[last->opcode];
588+
opcode = buffer[pc].opcode = op_without_pop[opcode];
593589
}
594-
_Py_FALLTHROUGH;
595-
}
596-
default:
597-
{
598590
/* _PUSH_FRAME doesn't escape or error, but it
599591
* does need the IP for the return address */
600592
bool needs_ip = opcode == _PUSH_FRAME;

Python/optimizer_bytecodes.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,18 @@ dummy_func(void) {
118118
sym_set_type(left, &PyLong_Type);
119119
}
120120

121+
op(_CHECK_ATTR_CLASS, (type_version/2, owner -- owner)) {
122+
PyObject *type = (PyObject *)_PyType_LookupByVersion(type_version);
123+
if (type) {
124+
if (type == sym_get_const(ctx, owner)) {
125+
REPLACE_OP(this_instr, _NOP, 0, 0);
126+
}
127+
else {
128+
sym_set_const(owner, type);
129+
}
130+
}
131+
}
132+
121133
op(_GUARD_TYPE_VERSION, (type_version/2, owner -- owner)) {
122134
assert(type_version);
123135
if (sym_matches_type_version(owner, type_version)) {

Python/optimizer_cases.c.h

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

0 commit comments

Comments
 (0)