Skip to content

Commit cccf3a4

Browse files
gvanrossumadorilson
authored andcommitted
pythongh-115859: Re-enable T2 optimizer pass by default (python#116062)
This undoes the *temporary* default disabling of the T2 optimizer pass in pythongh-115860. - Add a new test that reproduces Brandt's example from pythongh-115859; it indeed crashes before pythongh-116028 with PYTHONUOPSOPTIMIZE=1 - Re-enable the optimizer pass in T2, stop checking PYTHONUOPSOPTIMIZE - Rename the env var to disable T2 entirely to PYTHON_UOPS_OPTIMIZE (must be explicitly set to 0 to disable) - Fix skipIf conditions on tests in test_opt.py accordingly - Export sym_is_bottom() (for debugging) - Fix various things in the `_BINARY_OP_` specializations in the abstract interpreter: - DECREF(temp) - out-of-space check after sym_new_const() - add sym_matches_type() checks, so even if we somehow reach a binary op with symbolic constants of the wrong type on the stack we won't trigger the type assert
1 parent adeccdc commit cccf3a4

File tree

7 files changed

+96
-28
lines changed

7 files changed

+96
-28
lines changed

Include/internal/pycore_optimizer.h

+2
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@ extern void _Py_uop_sym_set_null(_Py_UopsSymbol *sym);
9595
extern void _Py_uop_sym_set_non_null(_Py_UopsSymbol *sym);
9696
extern void _Py_uop_sym_set_type(_Py_UopsSymbol *sym, PyTypeObject *typ);
9797
extern void _Py_uop_sym_set_const(_Py_UopsSymbol *sym, PyObject *const_val);
98+
extern bool _Py_uop_sym_is_bottom(_Py_UopsSymbol *sym);
99+
98100

99101
extern int _Py_uop_abstractcontext_init(_Py_UOpsContext *ctx);
100102
extern void _Py_uop_abstractcontext_fini(_Py_UOpsContext *ctx);

Lib/test/test_capi/test_opt.py

+20-1
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,8 @@ def f():
210210
exe = get_first_executor(f)
211211
self.assertIsNone(exe)
212212

213+
214+
@unittest.skipIf(os.getenv("PYTHON_UOPS_OPTIMIZE") == "0", "Needs uop optimizer to run.")
213215
class TestUops(unittest.TestCase):
214216

215217
def test_basic_loop(self):
@@ -570,7 +572,7 @@ def testfunc(n):
570572
self.assertLessEqual(count, 2)
571573

572574

573-
@unittest.skipIf(os.getenv("PYTHONUOPSOPTIMIZE", default=0) == 0, "Needs uop optimizer to run.")
575+
@unittest.skipIf(os.getenv("PYTHON_UOPS_OPTIMIZE") == "0", "Needs uop optimizer to run.")
574576
class TestUopsOptimization(unittest.TestCase):
575577

576578
def _run_with_optimizer(self, testfunc, arg):
@@ -890,5 +892,22 @@ def testfunc(n):
890892
self.assertLessEqual(len(guard_both_float_count), 1)
891893
self.assertIn("_COMPARE_OP_STR", uops)
892894

895+
def test_type_inconsistency(self):
896+
def testfunc(n):
897+
for i in range(n):
898+
x = _test_global + _test_global
899+
# Must be a real global else it won't be optimized to _LOAD_CONST_INLINE
900+
global _test_global
901+
_test_global = 0
902+
_, ex = self._run_with_optimizer(testfunc, 16)
903+
self.assertIsNone(ex)
904+
_test_global = 1.2
905+
_, ex = self._run_with_optimizer(testfunc, 16)
906+
self.assertIsNotNone(ex)
907+
uops = get_opnames(ex)
908+
self.assertIn("_GUARD_BOTH_INT", uops)
909+
self.assertIn("_BINARY_OP_ADD_INT", uops)
910+
911+
893912
if __name__ == "__main__":
894913
unittest.main()

Python/optimizer.c

+2-2
Original file line numberDiff line numberDiff line change
@@ -1008,8 +1008,8 @@ uop_optimize(
10081008
return err;
10091009
}
10101010
OPT_STAT_INC(traces_created);
1011-
char *uop_optimize = Py_GETENV("PYTHONUOPSOPTIMIZE");
1012-
if (uop_optimize == NULL || *uop_optimize > '0') {
1011+
char *env_var = Py_GETENV("PYTHON_UOPS_OPTIMIZE");
1012+
if (env_var == NULL || *env_var == '\0' || *env_var > '0') {
10131013
err = _Py_uop_analyze_and_optimize(frame, buffer,
10141014
UOP_MAX_TRACE_LENGTH,
10151015
curr_stackentries, &dependencies);

Python/optimizer_analysis.c

+4-6
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,7 @@ remove_globals(_PyInterpreterFrame *frame, _PyUOpInstruction *buffer,
297297
#define sym_set_non_null _Py_uop_sym_set_non_null
298298
#define sym_set_type _Py_uop_sym_set_type
299299
#define sym_set_const _Py_uop_sym_set_const
300+
#define sym_is_bottom _Py_uop_sym_is_bottom
300301
#define frame_new _Py_uop_frame_new
301302
#define frame_pop _Py_uop_frame_pop
302303

@@ -510,12 +511,9 @@ _Py_uop_analyze_and_optimize(
510511

511512
peephole_opt(frame, buffer, buffer_size);
512513

513-
char *uop_optimize = Py_GETENV("PYTHONUOPSOPTIMIZE");
514-
if (uop_optimize != NULL && *uop_optimize > '0') {
515-
err = optimize_uops(
516-
(PyCodeObject *)frame->f_executable, buffer,
517-
buffer_size, curr_stacklen, dependencies);
518-
}
514+
err = optimize_uops(
515+
(PyCodeObject *)frame->f_executable, buffer,
516+
buffer_size, curr_stacklen, dependencies);
519517

520518
if (err == 0) {
521519
goto not_ready;

Python/optimizer_bytecodes.c

+34-9
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ typedef struct _Py_UOpsAbstractFrame _Py_UOpsAbstractFrame;
2525
#define sym_set_non_null _Py_uop_sym_set_non_null
2626
#define sym_set_type _Py_uop_sym_set_type
2727
#define sym_set_const _Py_uop_sym_set_const
28+
#define sym_is_bottom _Py_uop_sym_is_bottom
2829
#define frame_new _Py_uop_frame_new
2930
#define frame_pop _Py_uop_frame_pop
3031

@@ -107,15 +108,19 @@ dummy_func(void) {
107108
}
108109

109110
op(_BINARY_OP_ADD_INT, (left, right -- res)) {
110-
if (sym_is_const(left) && sym_is_const(right)) {
111+
if (sym_is_const(left) && sym_is_const(right) &&
112+
sym_matches_type(left, &PyLong_Type) && sym_matches_type(right, &PyLong_Type))
113+
{
111114
assert(PyLong_CheckExact(sym_get_const(left)));
112115
assert(PyLong_CheckExact(sym_get_const(right)));
113116
PyObject *temp = _PyLong_Add((PyLongObject *)sym_get_const(left),
114117
(PyLongObject *)sym_get_const(right));
115118
if (temp == NULL) {
116119
goto error;
117120
}
118-
OUT_OF_SPACE_IF_NULL(res = sym_new_const(ctx, temp));
121+
res = sym_new_const(ctx, temp);
122+
Py_DECREF(temp);
123+
OUT_OF_SPACE_IF_NULL(res);
119124
// TODO gh-115506:
120125
// replace opcode with constant propagated one and add tests!
121126
}
@@ -125,15 +130,19 @@ dummy_func(void) {
125130
}
126131

127132
op(_BINARY_OP_SUBTRACT_INT, (left, right -- res)) {
128-
if (sym_is_const(left) && sym_is_const(right)) {
133+
if (sym_is_const(left) && sym_is_const(right) &&
134+
sym_matches_type(left, &PyLong_Type) && sym_matches_type(right, &PyLong_Type))
135+
{
129136
assert(PyLong_CheckExact(sym_get_const(left)));
130137
assert(PyLong_CheckExact(sym_get_const(right)));
131138
PyObject *temp = _PyLong_Subtract((PyLongObject *)sym_get_const(left),
132139
(PyLongObject *)sym_get_const(right));
133140
if (temp == NULL) {
134141
goto error;
135142
}
136-
OUT_OF_SPACE_IF_NULL(res = sym_new_const(ctx, temp));
143+
res = sym_new_const(ctx, temp);
144+
Py_DECREF(temp);
145+
OUT_OF_SPACE_IF_NULL(res);
137146
// TODO gh-115506:
138147
// replace opcode with constant propagated one and add tests!
139148
}
@@ -143,15 +152,19 @@ dummy_func(void) {
143152
}
144153

145154
op(_BINARY_OP_MULTIPLY_INT, (left, right -- res)) {
146-
if (sym_is_const(left) && sym_is_const(right)) {
155+
if (sym_is_const(left) && sym_is_const(right) &&
156+
sym_matches_type(left, &PyLong_Type) && sym_matches_type(right, &PyLong_Type))
157+
{
147158
assert(PyLong_CheckExact(sym_get_const(left)));
148159
assert(PyLong_CheckExact(sym_get_const(right)));
149160
PyObject *temp = _PyLong_Multiply((PyLongObject *)sym_get_const(left),
150161
(PyLongObject *)sym_get_const(right));
151162
if (temp == NULL) {
152163
goto error;
153164
}
154-
OUT_OF_SPACE_IF_NULL(res = sym_new_const(ctx, temp));
165+
res = sym_new_const(ctx, temp);
166+
Py_DECREF(temp);
167+
OUT_OF_SPACE_IF_NULL(res);
155168
// TODO gh-115506:
156169
// replace opcode with constant propagated one and add tests!
157170
}
@@ -161,7 +174,9 @@ dummy_func(void) {
161174
}
162175

163176
op(_BINARY_OP_ADD_FLOAT, (left, right -- res)) {
164-
if (sym_is_const(left) && sym_is_const(right)) {
177+
if (sym_is_const(left) && sym_is_const(right) &&
178+
sym_matches_type(left, &PyFloat_Type) && sym_matches_type(right, &PyFloat_Type))
179+
{
165180
assert(PyFloat_CheckExact(sym_get_const(left)));
166181
assert(PyFloat_CheckExact(sym_get_const(right)));
167182
PyObject *temp = PyFloat_FromDouble(
@@ -171,6 +186,8 @@ dummy_func(void) {
171186
goto error;
172187
}
173188
res = sym_new_const(ctx, temp);
189+
Py_DECREF(temp);
190+
OUT_OF_SPACE_IF_NULL(res);
174191
// TODO gh-115506:
175192
// replace opcode with constant propagated one and update tests!
176193
}
@@ -180,7 +197,9 @@ dummy_func(void) {
180197
}
181198

182199
op(_BINARY_OP_SUBTRACT_FLOAT, (left, right -- res)) {
183-
if (sym_is_const(left) && sym_is_const(right)) {
200+
if (sym_is_const(left) && sym_is_const(right) &&
201+
sym_matches_type(left, &PyFloat_Type) && sym_matches_type(right, &PyFloat_Type))
202+
{
184203
assert(PyFloat_CheckExact(sym_get_const(left)));
185204
assert(PyFloat_CheckExact(sym_get_const(right)));
186205
PyObject *temp = PyFloat_FromDouble(
@@ -190,6 +209,8 @@ dummy_func(void) {
190209
goto error;
191210
}
192211
res = sym_new_const(ctx, temp);
212+
Py_DECREF(temp);
213+
OUT_OF_SPACE_IF_NULL(res);
193214
// TODO gh-115506:
194215
// replace opcode with constant propagated one and update tests!
195216
}
@@ -199,7 +220,9 @@ dummy_func(void) {
199220
}
200221

201222
op(_BINARY_OP_MULTIPLY_FLOAT, (left, right -- res)) {
202-
if (sym_is_const(left) && sym_is_const(right)) {
223+
if (sym_is_const(left) && sym_is_const(right) &&
224+
sym_matches_type(left, &PyFloat_Type) && sym_matches_type(right, &PyFloat_Type))
225+
{
203226
assert(PyFloat_CheckExact(sym_get_const(left)));
204227
assert(PyFloat_CheckExact(sym_get_const(right)));
205228
PyObject *temp = PyFloat_FromDouble(
@@ -209,6 +232,8 @@ dummy_func(void) {
209232
goto error;
210233
}
211234
res = sym_new_const(ctx, temp);
235+
Py_DECREF(temp);
236+
OUT_OF_SPACE_IF_NULL(res);
212237
// TODO gh-115506:
213238
// replace opcode with constant propagated one and update tests!
214239
}

Python/optimizer_cases.c.h

+33-9
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Python/optimizer_symbols.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ sym_set_bottom(_Py_UopsSymbol *sym)
7777
Py_CLEAR(sym->const_val);
7878
}
7979

80-
static inline bool
80+
bool
8181
_Py_uop_sym_is_bottom(_Py_UopsSymbol *sym)
8282
{
8383
if ((sym->flags & IS_NULL) && (sym->flags & NOT_NULL)) {

0 commit comments

Comments
 (0)