From 0400f562c48109633f56e252fd0c12299a530022 Mon Sep 17 00:00:00 2001 From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com> Date: Sun, 10 Mar 2024 22:14:46 +0800 Subject: [PATCH 1/6] Eliminate guard for constant promoted function --- Lib/test/test_capi/test_opt.py | 47 +++++ Python/optimizer_bytecodes.c | 25 ++- Python/optimizer_cases.c.h | 196 ++++++++++--------- Tools/cases_generator/optimizer_generator.py | 4 +- 4 files changed, 178 insertions(+), 94 deletions(-) diff --git a/Lib/test/test_capi/test_opt.py b/Lib/test/test_capi/test_opt.py index b0859a382de523..c71ac8a2e194a6 100644 --- a/Lib/test/test_capi/test_opt.py +++ b/Lib/test/test_capi/test_opt.py @@ -793,6 +793,53 @@ def testfunc(n): """)) self.assertEqual(result[0].rc, 0, result) + def test_const_eliminate_function_guards(self): + """Note: this function must be executed in a subprocess, because when other tests are run, + the globals of this module might get affected, causing globals to constant promotion + to fail. + """ + + result = script_helper.run_python_until_end('-c', textwrap.dedent(""" + import _testinternalcapi + import opcode + + def get_first_executor(func): + code = func.__code__ + co_code = code.co_code + JUMP_BACKWARD = opcode.opmap["JUMP_BACKWARD"] + for i in range(0, len(co_code), 2): + if co_code[i] == JUMP_BACKWARD: + try: + return _testinternalcapi.get_executor(code, i) + except ValueError: + pass + return None + + def get_opnames(ex): + return {item[0] for item in ex} + + def func(n): + return n + + def testfunc(n): + for i in range(n): + x = func(i) + return x + + opt = _testinternalcapi.new_uop_optimizer() + _testinternalcapi.set_optimizer(opt) + testfunc(64) + + ex = get_first_executor(testfunc) + assert ex is not None + uops = get_opnames(ex) + print(uops) + assert "_LOAD_GLOBAL_MODULE" not in uops + assert "_LOAD_CONST_INLINE_WITH_NULL" in uops + assert "_CHECK_FUNCTION_EXACT_ARGS" not in uops + """)) + self.assertEqual(result[0].rc, 0, result) + def test_float_add_constant_propagation(self): def testfunc(n): a = 1.0 diff --git a/Python/optimizer_bytecodes.c b/Python/optimizer_bytecodes.c index e3f7c9822103e6..9e7a855c684e8a 100644 --- a/Python/optimizer_bytecodes.c +++ b/Python/optimizer_bytecodes.c @@ -449,6 +449,14 @@ dummy_func(void) { } } + op(_LOAD_ATTR, (owner -- attr, self_or_null if (oparg & 1))) { + (void)owner; + OUT_OF_SPACE_IF_NULL(attr = sym_new_not_null(ctx)); + if (oparg & 1) { + OUT_OF_SPACE_IF_NULL(self_or_null = sym_new_unknown(ctx)); + } + } + op(_LOAD_ATTR_MODULE, (index/1, owner -- attr, null if (oparg & 1))) { (void)index; OUT_OF_SPACE_IF_NULL(null = sym_new_null(ctx)); @@ -515,11 +523,24 @@ dummy_func(void) { op(_CHECK_FUNCTION_EXACT_ARGS, (func_version/2, callable, self_or_null, unused[oparg] -- callable, self_or_null, unused[oparg])) { + if (sym_is_const(callable) && + sym_matches_type(callable, &PyFunction_Type) && + (sym_is_null(self_or_null) || sym_is_not_null(self_or_null))) { + assert(PyFunction_Check(sym_get_const(callable))); + PyFunctionObject *func = sym_get_const(callable); + if (func->func_version != func_version) { + goto hit_bottom; + } + PyCodeObject *code = (PyCodeObject *)func->func_code; + int argcount = oparg + sym_is_not_null(self_or_null); + if (code->co_argcount != argcount) { + goto hit_bottom; + } + REPLACE_OP(this_instr, _NOP, 0, 0); + } if (!sym_set_type(callable, &PyFunction_Type)) { goto hit_bottom; } - (void)self_or_null; - (void)func_version; } op(_CHECK_CALL_BOUND_METHOD_EXACT_ARGS, (callable, null, unused[oparg] -- callable, null, unused[oparg])) { diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h index c88b25132c0e90..8635a1097c4231 100644 --- a/Python/optimizer_cases.c.h +++ b/Python/optimizer_cases.c.h @@ -80,7 +80,7 @@ case _END_SEND: { _Py_UopsSymbol *value; - value = sym_new_unknown(ctx); + value = sym_new_not_null(ctx); if (value == NULL) goto out_of_space; stack_pointer[-2] = value; stack_pointer += -1; @@ -89,7 +89,7 @@ case _UNARY_NEGATIVE: { _Py_UopsSymbol *res; - res = sym_new_unknown(ctx); + res = sym_new_not_null(ctx); if (res == NULL) goto out_of_space; stack_pointer[-1] = res; break; @@ -97,7 +97,7 @@ case _UNARY_NOT: { _Py_UopsSymbol *res; - res = sym_new_unknown(ctx); + res = sym_new_not_null(ctx); if (res == NULL) goto out_of_space; stack_pointer[-1] = res; break; @@ -205,7 +205,7 @@ case _REPLACE_WITH_TRUE: { _Py_UopsSymbol *res; - res = sym_new_unknown(ctx); + res = sym_new_not_null(ctx); if (res == NULL) goto out_of_space; stack_pointer[-1] = res; break; @@ -213,7 +213,7 @@ case _UNARY_INVERT: { _Py_UopsSymbol *res; - res = sym_new_unknown(ctx); + res = sym_new_not_null(ctx); if (res == NULL) goto out_of_space; stack_pointer[-1] = res; break; @@ -482,7 +482,7 @@ case _BINARY_SUBSCR: { _Py_UopsSymbol *res; - res = sym_new_unknown(ctx); + res = sym_new_not_null(ctx); if (res == NULL) goto out_of_space; stack_pointer[-2] = res; stack_pointer += -1; @@ -491,7 +491,7 @@ case _BINARY_SLICE: { _Py_UopsSymbol *res; - res = sym_new_unknown(ctx); + res = sym_new_not_null(ctx); if (res == NULL) goto out_of_space; stack_pointer[-3] = res; stack_pointer += -2; @@ -505,7 +505,7 @@ case _BINARY_SUBSCR_LIST_INT: { _Py_UopsSymbol *res; - res = sym_new_unknown(ctx); + res = sym_new_not_null(ctx); if (res == NULL) goto out_of_space; stack_pointer[-2] = res; stack_pointer += -1; @@ -514,7 +514,7 @@ case _BINARY_SUBSCR_STR_INT: { _Py_UopsSymbol *res; - res = sym_new_unknown(ctx); + res = sym_new_not_null(ctx); if (res == NULL) goto out_of_space; stack_pointer[-2] = res; stack_pointer += -1; @@ -523,7 +523,7 @@ case _BINARY_SUBSCR_TUPLE_INT: { _Py_UopsSymbol *res; - res = sym_new_unknown(ctx); + res = sym_new_not_null(ctx); if (res == NULL) goto out_of_space; stack_pointer[-2] = res; stack_pointer += -1; @@ -532,7 +532,7 @@ case _BINARY_SUBSCR_DICT: { _Py_UopsSymbol *res; - res = sym_new_unknown(ctx); + res = sym_new_not_null(ctx); if (res == NULL) goto out_of_space; stack_pointer[-2] = res; stack_pointer += -1; @@ -573,7 +573,7 @@ case _CALL_INTRINSIC_1: { _Py_UopsSymbol *res; - res = sym_new_unknown(ctx); + res = sym_new_not_null(ctx); if (res == NULL) goto out_of_space; stack_pointer[-1] = res; break; @@ -581,7 +581,7 @@ case _CALL_INTRINSIC_2: { _Py_UopsSymbol *res; - res = sym_new_unknown(ctx); + res = sym_new_not_null(ctx); if (res == NULL) goto out_of_space; stack_pointer[-2] = res; stack_pointer += -1; @@ -608,7 +608,7 @@ case _GET_AITER: { _Py_UopsSymbol *iter; - iter = sym_new_unknown(ctx); + iter = sym_new_not_null(ctx); if (iter == NULL) goto out_of_space; stack_pointer[-1] = iter; break; @@ -616,7 +616,7 @@ case _GET_ANEXT: { _Py_UopsSymbol *awaitable; - awaitable = sym_new_unknown(ctx); + awaitable = sym_new_not_null(ctx); if (awaitable == NULL) goto out_of_space; stack_pointer[0] = awaitable; stack_pointer += 1; @@ -625,7 +625,7 @@ case _GET_AWAITABLE: { _Py_UopsSymbol *iter; - iter = sym_new_unknown(ctx); + iter = sym_new_not_null(ctx); if (iter == NULL) goto out_of_space; stack_pointer[-1] = iter; break; @@ -644,7 +644,7 @@ case _LOAD_ASSERTION_ERROR: { _Py_UopsSymbol *value; - value = sym_new_unknown(ctx); + value = sym_new_not_null(ctx); if (value == NULL) goto out_of_space; stack_pointer[0] = value; stack_pointer += 1; @@ -653,7 +653,7 @@ case _LOAD_BUILD_CLASS: { _Py_UopsSymbol *bc; - bc = sym_new_unknown(ctx); + bc = sym_new_not_null(ctx); if (bc == NULL) goto out_of_space; stack_pointer[0] = bc; stack_pointer += 1; @@ -687,7 +687,7 @@ _Py_UopsSymbol **values; values = &stack_pointer[-1]; for (int _i = oparg; --_i >= 0;) { - values[_i] = sym_new_unknown(ctx); + values[_i] = sym_new_not_null(ctx); if (values[_i] == NULL) goto out_of_space; } stack_pointer += -1 + oparg; @@ -698,7 +698,7 @@ _Py_UopsSymbol **values; values = &stack_pointer[-1]; for (int _i = oparg; --_i >= 0;) { - values[_i] = sym_new_unknown(ctx); + values[_i] = sym_new_not_null(ctx); if (values[_i] == NULL) goto out_of_space; } stack_pointer += -1 + oparg; @@ -709,7 +709,7 @@ _Py_UopsSymbol **values; values = &stack_pointer[-1]; for (int _i = oparg; --_i >= 0;) { - values[_i] = sym_new_unknown(ctx); + values[_i] = sym_new_not_null(ctx); if (values[_i] == NULL) goto out_of_space; } stack_pointer += -1 + oparg; @@ -752,7 +752,7 @@ case _LOAD_LOCALS: { _Py_UopsSymbol *locals; - locals = sym_new_unknown(ctx); + locals = sym_new_not_null(ctx); if (locals == NULL) goto out_of_space; stack_pointer[0] = locals; stack_pointer += 1; @@ -761,7 +761,7 @@ case _LOAD_FROM_DICT_OR_GLOBALS: { _Py_UopsSymbol *v; - v = sym_new_unknown(ctx); + v = sym_new_not_null(ctx); if (v == NULL) goto out_of_space; stack_pointer[-1] = v; break; @@ -769,7 +769,7 @@ case _LOAD_NAME: { _Py_UopsSymbol *v; - v = sym_new_unknown(ctx); + v = sym_new_not_null(ctx); if (v == NULL) goto out_of_space; stack_pointer[0] = v; stack_pointer += 1; @@ -779,7 +779,7 @@ case _LOAD_GLOBAL: { _Py_UopsSymbol *res; _Py_UopsSymbol *null = NULL; - res = sym_new_unknown(ctx); + res = sym_new_not_null(ctx); if (res == NULL) goto out_of_space; null = sym_new_null(ctx); if (null == NULL) goto out_of_space; @@ -800,7 +800,7 @@ case _LOAD_GLOBAL_MODULE: { _Py_UopsSymbol *res; _Py_UopsSymbol *null = NULL; - res = sym_new_unknown(ctx); + res = sym_new_not_null(ctx); if (res == NULL) goto out_of_space; null = sym_new_null(ctx); if (null == NULL) goto out_of_space; @@ -813,7 +813,7 @@ case _LOAD_GLOBAL_BUILTINS: { _Py_UopsSymbol *res; _Py_UopsSymbol *null = NULL; - res = sym_new_unknown(ctx); + res = sym_new_not_null(ctx); if (res == NULL) goto out_of_space; null = sym_new_null(ctx); if (null == NULL) goto out_of_space; @@ -837,7 +837,7 @@ case _LOAD_FROM_DICT_OR_DEREF: { _Py_UopsSymbol *value; - value = sym_new_unknown(ctx); + value = sym_new_not_null(ctx); if (value == NULL) goto out_of_space; stack_pointer[-1] = value; break; @@ -845,7 +845,7 @@ case _LOAD_DEREF: { _Py_UopsSymbol *value; - value = sym_new_unknown(ctx); + value = sym_new_not_null(ctx); if (value == NULL) goto out_of_space; stack_pointer[0] = value; stack_pointer += 1; @@ -863,7 +863,7 @@ case _BUILD_STRING: { _Py_UopsSymbol *str; - str = sym_new_unknown(ctx); + str = sym_new_not_null(ctx); if (str == NULL) goto out_of_space; stack_pointer[-oparg] = str; stack_pointer += 1 - oparg; @@ -872,7 +872,7 @@ case _BUILD_TUPLE: { _Py_UopsSymbol *tup; - tup = sym_new_unknown(ctx); + tup = sym_new_not_null(ctx); if (tup == NULL) goto out_of_space; stack_pointer[-oparg] = tup; stack_pointer += 1 - oparg; @@ -881,7 +881,7 @@ case _BUILD_LIST: { _Py_UopsSymbol *list; - list = sym_new_unknown(ctx); + list = sym_new_not_null(ctx); if (list == NULL) goto out_of_space; stack_pointer[-oparg] = list; stack_pointer += 1 - oparg; @@ -900,7 +900,7 @@ case _BUILD_SET: { _Py_UopsSymbol *set; - set = sym_new_unknown(ctx); + set = sym_new_not_null(ctx); if (set == NULL) goto out_of_space; stack_pointer[-oparg] = set; stack_pointer += 1 - oparg; @@ -909,7 +909,7 @@ case _BUILD_MAP: { _Py_UopsSymbol *map; - map = sym_new_unknown(ctx); + map = sym_new_not_null(ctx); if (map == NULL) goto out_of_space; stack_pointer[-oparg*2] = map; stack_pointer += 1 - oparg*2; @@ -922,7 +922,7 @@ case _BUILD_CONST_KEY_MAP: { _Py_UopsSymbol *map; - map = sym_new_unknown(ctx); + map = sym_new_not_null(ctx); if (map == NULL) goto out_of_space; stack_pointer[-1 - oparg] = map; stack_pointer += -oparg; @@ -948,7 +948,7 @@ case _LOAD_SUPER_ATTR_ATTR: { _Py_UopsSymbol *attr; - attr = sym_new_unknown(ctx); + attr = sym_new_not_null(ctx); if (attr == NULL) goto out_of_space; stack_pointer[-3] = attr; stack_pointer += -2; @@ -958,9 +958,9 @@ case _LOAD_SUPER_ATTR_METHOD: { _Py_UopsSymbol *attr; _Py_UopsSymbol *self_or_null; - attr = sym_new_unknown(ctx); + attr = sym_new_not_null(ctx); if (attr == NULL) goto out_of_space; - self_or_null = sym_new_unknown(ctx); + self_or_null = sym_new_not_null(ctx); if (self_or_null == NULL) goto out_of_space; stack_pointer[-3] = attr; stack_pointer[-2] = self_or_null; @@ -969,12 +969,15 @@ } case _LOAD_ATTR: { + _Py_UopsSymbol *owner; _Py_UopsSymbol *attr; _Py_UopsSymbol *self_or_null = NULL; - attr = sym_new_unknown(ctx); - if (attr == NULL) goto out_of_space; - self_or_null = sym_new_unknown(ctx); - if (self_or_null == NULL) goto out_of_space; + owner = stack_pointer[-1]; + (void)owner; + OUT_OF_SPACE_IF_NULL(attr = sym_new_not_null(ctx)); + if (oparg & 1) { + OUT_OF_SPACE_IF_NULL(self_or_null = sym_new_unknown(ctx)); + } stack_pointer[-1] = attr; if (oparg & 1) stack_pointer[0] = self_or_null; stack_pointer += (oparg & 1); @@ -1220,7 +1223,7 @@ case _CONTAINS_OP_SET: { _Py_UopsSymbol *b; - b = sym_new_unknown(ctx); + b = sym_new_not_null(ctx); if (b == NULL) goto out_of_space; stack_pointer[-2] = b; stack_pointer += -1; @@ -1229,7 +1232,7 @@ case _CONTAINS_OP_DICT: { _Py_UopsSymbol *b; - b = sym_new_unknown(ctx); + b = sym_new_not_null(ctx); if (b == NULL) goto out_of_space; stack_pointer[-2] = b; stack_pointer += -1; @@ -1239,9 +1242,9 @@ case _CHECK_EG_MATCH: { _Py_UopsSymbol *rest; _Py_UopsSymbol *match; - rest = sym_new_unknown(ctx); + rest = sym_new_not_null(ctx); if (rest == NULL) goto out_of_space; - match = sym_new_unknown(ctx); + match = sym_new_not_null(ctx); if (match == NULL) goto out_of_space; stack_pointer[-2] = rest; stack_pointer[-1] = match; @@ -1250,7 +1253,7 @@ case _CHECK_EXC_MATCH: { _Py_UopsSymbol *b; - b = sym_new_unknown(ctx); + b = sym_new_not_null(ctx); if (b == NULL) goto out_of_space; stack_pointer[-1] = b; break; @@ -1262,7 +1265,7 @@ case _IS_NONE: { _Py_UopsSymbol *b; - b = sym_new_unknown(ctx); + b = sym_new_not_null(ctx); if (b == NULL) goto out_of_space; stack_pointer[-1] = b; break; @@ -1270,7 +1273,7 @@ case _GET_LEN: { _Py_UopsSymbol *len_o; - len_o = sym_new_unknown(ctx); + len_o = sym_new_not_null(ctx); if (len_o == NULL) goto out_of_space; stack_pointer[0] = len_o; stack_pointer += 1; @@ -1279,7 +1282,7 @@ case _MATCH_CLASS: { _Py_UopsSymbol *attrs; - attrs = sym_new_unknown(ctx); + attrs = sym_new_not_null(ctx); if (attrs == NULL) goto out_of_space; stack_pointer[-3] = attrs; stack_pointer += -2; @@ -1288,7 +1291,7 @@ case _MATCH_MAPPING: { _Py_UopsSymbol *res; - res = sym_new_unknown(ctx); + res = sym_new_not_null(ctx); if (res == NULL) goto out_of_space; stack_pointer[0] = res; stack_pointer += 1; @@ -1297,7 +1300,7 @@ case _MATCH_SEQUENCE: { _Py_UopsSymbol *res; - res = sym_new_unknown(ctx); + res = sym_new_not_null(ctx); if (res == NULL) goto out_of_space; stack_pointer[0] = res; stack_pointer += 1; @@ -1306,7 +1309,7 @@ case _MATCH_KEYS: { _Py_UopsSymbol *values_or_none; - values_or_none = sym_new_unknown(ctx); + values_or_none = sym_new_not_null(ctx); if (values_or_none == NULL) goto out_of_space; stack_pointer[0] = values_or_none; stack_pointer += 1; @@ -1315,7 +1318,7 @@ case _GET_ITER: { _Py_UopsSymbol *iter; - iter = sym_new_unknown(ctx); + iter = sym_new_not_null(ctx); if (iter == NULL) goto out_of_space; stack_pointer[-1] = iter; break; @@ -1323,7 +1326,7 @@ case _GET_YIELD_FROM_ITER: { _Py_UopsSymbol *iter; - iter = sym_new_unknown(ctx); + iter = sym_new_not_null(ctx); if (iter == NULL) goto out_of_space; stack_pointer[-1] = iter; break; @@ -1333,7 +1336,7 @@ case _FOR_ITER_TIER_TWO: { _Py_UopsSymbol *next; - next = sym_new_unknown(ctx); + next = sym_new_not_null(ctx); if (next == NULL) goto out_of_space; stack_pointer[0] = next; stack_pointer += 1; @@ -1354,7 +1357,7 @@ case _ITER_NEXT_LIST: { _Py_UopsSymbol *next; - next = sym_new_unknown(ctx); + next = sym_new_not_null(ctx); if (next == NULL) goto out_of_space; stack_pointer[0] = next; stack_pointer += 1; @@ -1373,7 +1376,7 @@ case _ITER_NEXT_TUPLE: { _Py_UopsSymbol *next; - next = sym_new_unknown(ctx); + next = sym_new_not_null(ctx); if (next == NULL) goto out_of_space; stack_pointer[0] = next; stack_pointer += 1; @@ -1406,9 +1409,9 @@ case _BEFORE_ASYNC_WITH: { _Py_UopsSymbol *exit; _Py_UopsSymbol *res; - exit = sym_new_unknown(ctx); + exit = sym_new_not_null(ctx); if (exit == NULL) goto out_of_space; - res = sym_new_unknown(ctx); + res = sym_new_not_null(ctx); if (res == NULL) goto out_of_space; stack_pointer[-1] = exit; stack_pointer[0] = res; @@ -1419,9 +1422,9 @@ case _BEFORE_WITH: { _Py_UopsSymbol *exit; _Py_UopsSymbol *res; - exit = sym_new_unknown(ctx); + exit = sym_new_not_null(ctx); if (exit == NULL) goto out_of_space; - res = sym_new_unknown(ctx); + res = sym_new_not_null(ctx); if (res == NULL) goto out_of_space; stack_pointer[-1] = exit; stack_pointer[0] = res; @@ -1431,7 +1434,7 @@ case _WITH_EXCEPT_START: { _Py_UopsSymbol *res; - res = sym_new_unknown(ctx); + res = sym_new_not_null(ctx); if (res == NULL) goto out_of_space; stack_pointer[0] = res; stack_pointer += 1; @@ -1441,9 +1444,9 @@ case _PUSH_EXC_INFO: { _Py_UopsSymbol *prev_exc; _Py_UopsSymbol *new_exc; - prev_exc = sym_new_unknown(ctx); + prev_exc = sym_new_not_null(ctx); if (prev_exc == NULL) goto out_of_space; - new_exc = sym_new_unknown(ctx); + new_exc = sym_new_not_null(ctx); if (new_exc == NULL) goto out_of_space; stack_pointer[-1] = prev_exc; stack_pointer[0] = new_exc; @@ -1491,7 +1494,7 @@ case _LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES: { _Py_UopsSymbol *attr; - attr = sym_new_unknown(ctx); + attr = sym_new_not_null(ctx); if (attr == NULL) goto out_of_space; stack_pointer[-1] = attr; break; @@ -1499,7 +1502,7 @@ case _LOAD_ATTR_NONDESCRIPTOR_NO_DICT: { _Py_UopsSymbol *attr; - attr = sym_new_unknown(ctx); + attr = sym_new_not_null(ctx); if (attr == NULL) goto out_of_space; stack_pointer[-1] = attr; break; @@ -1565,11 +1568,24 @@ self_or_null = stack_pointer[-1 - oparg]; callable = stack_pointer[-2 - oparg]; uint32_t func_version = (uint32_t)this_instr->operand; + if (sym_is_const(callable) && + sym_matches_type(callable, &PyFunction_Type) && + (sym_is_null(self_or_null) || sym_is_not_null(self_or_null))) { + assert(PyFunction_Check(sym_get_const(callable))); + PyFunctionObject *func = sym_get_const(callable); + if (func->func_version != func_version) { + goto hit_bottom; + } + PyCodeObject *code = (PyCodeObject *)func->func_code; + int argcount = oparg + sym_is_not_null(self_or_null); + if (code->co_argcount != argcount) { + goto hit_bottom; + } + REPLACE_OP(this_instr, _NOP, 0, 0); + } if (!sym_set_type(callable, &PyFunction_Type)) { goto hit_bottom; } - (void)self_or_null; - (void)func_version; break; } @@ -1630,7 +1646,7 @@ case _CALL_TYPE_1: { _Py_UopsSymbol *res; - res = sym_new_unknown(ctx); + res = sym_new_not_null(ctx); if (res == NULL) goto out_of_space; stack_pointer[-2 - oparg] = res; stack_pointer += -1 - oparg; @@ -1639,7 +1655,7 @@ case _CALL_STR_1: { _Py_UopsSymbol *res; - res = sym_new_unknown(ctx); + res = sym_new_not_null(ctx); if (res == NULL) goto out_of_space; stack_pointer[-2 - oparg] = res; stack_pointer += -1 - oparg; @@ -1648,7 +1664,7 @@ case _CALL_TUPLE_1: { _Py_UopsSymbol *res; - res = sym_new_unknown(ctx); + res = sym_new_not_null(ctx); if (res == NULL) goto out_of_space; stack_pointer[-2 - oparg] = res; stack_pointer += -1 - oparg; @@ -1664,7 +1680,7 @@ case _CALL_BUILTIN_CLASS: { _Py_UopsSymbol *res; - res = sym_new_unknown(ctx); + res = sym_new_not_null(ctx); if (res == NULL) goto out_of_space; stack_pointer[-2 - oparg] = res; stack_pointer += -1 - oparg; @@ -1673,7 +1689,7 @@ case _CALL_BUILTIN_O: { _Py_UopsSymbol *res; - res = sym_new_unknown(ctx); + res = sym_new_not_null(ctx); if (res == NULL) goto out_of_space; stack_pointer[-2 - oparg] = res; stack_pointer += -1 - oparg; @@ -1682,7 +1698,7 @@ case _CALL_BUILTIN_FAST: { _Py_UopsSymbol *res; - res = sym_new_unknown(ctx); + res = sym_new_not_null(ctx); if (res == NULL) goto out_of_space; stack_pointer[-2 - oparg] = res; stack_pointer += -1 - oparg; @@ -1691,7 +1707,7 @@ case _CALL_BUILTIN_FAST_WITH_KEYWORDS: { _Py_UopsSymbol *res; - res = sym_new_unknown(ctx); + res = sym_new_not_null(ctx); if (res == NULL) goto out_of_space; stack_pointer[-2 - oparg] = res; stack_pointer += -1 - oparg; @@ -1700,7 +1716,7 @@ case _CALL_LEN: { _Py_UopsSymbol *res; - res = sym_new_unknown(ctx); + res = sym_new_not_null(ctx); if (res == NULL) goto out_of_space; stack_pointer[-2 - oparg] = res; stack_pointer += -1 - oparg; @@ -1709,7 +1725,7 @@ case _CALL_ISINSTANCE: { _Py_UopsSymbol *res; - res = sym_new_unknown(ctx); + res = sym_new_not_null(ctx); if (res == NULL) goto out_of_space; stack_pointer[-2 - oparg] = res; stack_pointer += -1 - oparg; @@ -1718,7 +1734,7 @@ case _CALL_METHOD_DESCRIPTOR_O: { _Py_UopsSymbol *res; - res = sym_new_unknown(ctx); + res = sym_new_not_null(ctx); if (res == NULL) goto out_of_space; stack_pointer[-2 - oparg] = res; stack_pointer += -1 - oparg; @@ -1727,7 +1743,7 @@ case _CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS: { _Py_UopsSymbol *res; - res = sym_new_unknown(ctx); + res = sym_new_not_null(ctx); if (res == NULL) goto out_of_space; stack_pointer[-2 - oparg] = res; stack_pointer += -1 - oparg; @@ -1736,7 +1752,7 @@ case _CALL_METHOD_DESCRIPTOR_NOARGS: { _Py_UopsSymbol *res; - res = sym_new_unknown(ctx); + res = sym_new_not_null(ctx); if (res == NULL) goto out_of_space; stack_pointer[-2 - oparg] = res; stack_pointer += -1 - oparg; @@ -1745,7 +1761,7 @@ case _CALL_METHOD_DESCRIPTOR_FAST: { _Py_UopsSymbol *res; - res = sym_new_unknown(ctx); + res = sym_new_not_null(ctx); if (res == NULL) goto out_of_space; stack_pointer[-2 - oparg] = res; stack_pointer += -1 - oparg; @@ -1762,7 +1778,7 @@ case _MAKE_FUNCTION: { _Py_UopsSymbol *func; - func = sym_new_unknown(ctx); + func = sym_new_not_null(ctx); if (func == NULL) goto out_of_space; stack_pointer[-1] = func; break; @@ -1770,7 +1786,7 @@ case _SET_FUNCTION_ATTRIBUTE: { _Py_UopsSymbol *func; - func = sym_new_unknown(ctx); + func = sym_new_not_null(ctx); if (func == NULL) goto out_of_space; stack_pointer[-2] = func; stack_pointer += -1; @@ -1779,7 +1795,7 @@ case _BUILD_SLICE: { _Py_UopsSymbol *slice; - slice = sym_new_unknown(ctx); + slice = sym_new_not_null(ctx); if (slice == NULL) goto out_of_space; stack_pointer[-2 - ((oparg == 3) ? 1 : 0)] = slice; stack_pointer += -1 - ((oparg == 3) ? 1 : 0); @@ -1788,7 +1804,7 @@ case _CONVERT_VALUE: { _Py_UopsSymbol *result; - result = sym_new_unknown(ctx); + result = sym_new_not_null(ctx); if (result == NULL) goto out_of_space; stack_pointer[-1] = result; break; @@ -1796,7 +1812,7 @@ case _FORMAT_SIMPLE: { _Py_UopsSymbol *res; - res = sym_new_unknown(ctx); + res = sym_new_not_null(ctx); if (res == NULL) goto out_of_space; stack_pointer[-1] = res; break; @@ -1804,7 +1820,7 @@ case _FORMAT_WITH_SPEC: { _Py_UopsSymbol *res; - res = sym_new_unknown(ctx); + res = sym_new_not_null(ctx); if (res == NULL) goto out_of_space; stack_pointer[-2] = res; stack_pointer += -1; @@ -1824,7 +1840,7 @@ case _BINARY_OP: { _Py_UopsSymbol *res; - res = sym_new_unknown(ctx); + res = sym_new_not_null(ctx); if (res == NULL) goto out_of_space; stack_pointer[-2] = res; stack_pointer += -1; @@ -1951,7 +1967,7 @@ case _POP_TOP_LOAD_CONST_INLINE_BORROW: { _Py_UopsSymbol *value; - value = sym_new_unknown(ctx); + value = sym_new_not_null(ctx); if (value == NULL) goto out_of_space; stack_pointer[-1] = value; break; diff --git a/Tools/cases_generator/optimizer_generator.py b/Tools/cases_generator/optimizer_generator.py index a0a2f10aa760b7..1c6b708e82321a 100644 --- a/Tools/cases_generator/optimizer_generator.py +++ b/Tools/cases_generator/optimizer_generator.py @@ -83,14 +83,14 @@ def emit_default(out: CWriter, uop: Uop) -> None: if var.name != "unused" and not var.peek: if var.is_array(): out.emit(f"for (int _i = {var.size}; --_i >= 0;) {{\n") - out.emit(f"{var.name}[_i] = sym_new_unknown(ctx);\n") + out.emit(f"{var.name}[_i] = sym_new_not_null(ctx);\n") out.emit(f"if ({var.name}[_i] == NULL) goto out_of_space;\n") out.emit("}\n") elif var.name == "null": out.emit(f"{var.name} = sym_new_null(ctx);\n") out.emit(f"if ({var.name} == NULL) goto out_of_space;\n") else: - out.emit(f"{var.name} = sym_new_unknown(ctx);\n") + out.emit(f"{var.name} = sym_new_not_null(ctx);\n") out.emit(f"if ({var.name} == NULL) goto out_of_space;\n") From d71527b893655d97295f638058452ac604dbd461 Mon Sep 17 00:00:00 2001 From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com> Date: Sun, 10 Mar 2024 22:22:50 +0800 Subject: [PATCH 2/6] Fix test_generated_cases --- Lib/test/test_generated_cases.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_generated_cases.py b/Lib/test/test_generated_cases.py index 32c2c2fca05c4e..7b9dd36f85454f 100644 --- a/Lib/test/test_generated_cases.py +++ b/Lib/test/test_generated_cases.py @@ -908,7 +908,7 @@ def test_overridden_abstract_args(self): case OP2: { _Py_UopsSymbol *out; - out = sym_new_unknown(ctx); + out = sym_new_not_null(ctx); if (out == NULL) goto out_of_space; stack_pointer[-1] = out; break; @@ -933,7 +933,7 @@ def test_no_overridden_case(self): output = """ case OP: { _Py_UopsSymbol *out; - out = sym_new_unknown(ctx); + out = sym_new_not_null(ctx); if (out == NULL) goto out_of_space; stack_pointer[-1] = out; break; From ae588839c7454be6c09e41e5b35d04ebab16ae80 Mon Sep 17 00:00:00 2001 From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com> Date: Sun, 10 Mar 2024 22:31:37 +0800 Subject: [PATCH 3/6] fix gH warning --- Python/optimizer_bytecodes.c | 2 +- Python/optimizer_cases.c.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Python/optimizer_bytecodes.c b/Python/optimizer_bytecodes.c index 9e7a855c684e8a..b8eaf0a1ce9121 100644 --- a/Python/optimizer_bytecodes.c +++ b/Python/optimizer_bytecodes.c @@ -527,7 +527,7 @@ dummy_func(void) { sym_matches_type(callable, &PyFunction_Type) && (sym_is_null(self_or_null) || sym_is_not_null(self_or_null))) { assert(PyFunction_Check(sym_get_const(callable))); - PyFunctionObject *func = sym_get_const(callable); + PyFunctionObject *func = (PyFunctionObject *)sym_get_const(callable); if (func->func_version != func_version) { goto hit_bottom; } diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h index 8635a1097c4231..45a760801832de 100644 --- a/Python/optimizer_cases.c.h +++ b/Python/optimizer_cases.c.h @@ -1572,7 +1572,7 @@ sym_matches_type(callable, &PyFunction_Type) && (sym_is_null(self_or_null) || sym_is_not_null(self_or_null))) { assert(PyFunction_Check(sym_get_const(callable))); - PyFunctionObject *func = sym_get_const(callable); + PyFunctionObject *func = (PyFunctionObject *)sym_get_const(callable); if (func->func_version != func_version) { goto hit_bottom; } From be261d9419f7d355457a154df7a4e86189ebee00 Mon Sep 17 00:00:00 2001 From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com> Date: Mon, 11 Mar 2024 14:33:20 +0800 Subject: [PATCH 4/6] simplify test --- Lib/test/test_capi/test_opt.py | 47 ++++++++-------------------------- 1 file changed, 10 insertions(+), 37 deletions(-) diff --git a/Lib/test/test_capi/test_opt.py b/Lib/test/test_capi/test_opt.py index c71ac8a2e194a6..28fbabe037333c 100644 --- a/Lib/test/test_capi/test_opt.py +++ b/Lib/test/test_capi/test_opt.py @@ -794,30 +794,8 @@ def testfunc(n): self.assertEqual(result[0].rc, 0, result) def test_const_eliminate_function_guards(self): - """Note: this function must be executed in a subprocess, because when other tests are run, - the globals of this module might get affected, causing globals to constant promotion - to fail. - """ - - result = script_helper.run_python_until_end('-c', textwrap.dedent(""" - import _testinternalcapi - import opcode - - def get_first_executor(func): - code = func.__code__ - co_code = code.co_code - JUMP_BACKWARD = opcode.opmap["JUMP_BACKWARD"] - for i in range(0, len(co_code), 2): - if co_code[i] == JUMP_BACKWARD: - try: - return _testinternalcapi.get_executor(code, i) - except ValueError: - pass - return None - - def get_opnames(ex): - return {item[0] for item in ex} - + ns = {} + src = textwrap.dedent(""" def func(n): return n @@ -825,20 +803,15 @@ def testfunc(n): for i in range(n): x = func(i) return x - - opt = _testinternalcapi.new_uop_optimizer() - _testinternalcapi.set_optimizer(opt) - testfunc(64) - - ex = get_first_executor(testfunc) - assert ex is not None + """) + exec(src, ns, ns) + testfunc = ns['testfunc'] + _, ex = self._run_with_optimizer(testfunc, 20) + self.assertIsNotNone(ex) uops = get_opnames(ex) - print(uops) - assert "_LOAD_GLOBAL_MODULE" not in uops - assert "_LOAD_CONST_INLINE_WITH_NULL" in uops - assert "_CHECK_FUNCTION_EXACT_ARGS" not in uops - """)) - self.assertEqual(result[0].rc, 0, result) + self.assertNotIn("_LOAD_GLOBAL_MODULE", uops) + self.assertIn("_LOAD_CONST_INLINE_WITH_NULL", uops) + self.assertNotIn("_CHECK_FUNCTION_EXACT_ARGS", uops) def test_float_add_constant_propagation(self): def testfunc(n): From 4ae1f9fb3d6c2fec5778f63e33dad13d9ed56aa5 Mon Sep 17 00:00:00 2001 From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com> Date: Mon, 11 Mar 2024 20:33:25 +0800 Subject: [PATCH 5/6] address review --- Lib/test/test_capi/test_opt.py | 20 -------------------- Python/optimizer_bytecodes.c | 22 ---------------------- 2 files changed, 42 deletions(-) diff --git a/Lib/test/test_capi/test_opt.py b/Lib/test/test_capi/test_opt.py index 28fbabe037333c..b0859a382de523 100644 --- a/Lib/test/test_capi/test_opt.py +++ b/Lib/test/test_capi/test_opt.py @@ -793,26 +793,6 @@ def testfunc(n): """)) self.assertEqual(result[0].rc, 0, result) - def test_const_eliminate_function_guards(self): - ns = {} - src = textwrap.dedent(""" - def func(n): - return n - - def testfunc(n): - for i in range(n): - x = func(i) - return x - """) - exec(src, ns, ns) - testfunc = ns['testfunc'] - _, ex = self._run_with_optimizer(testfunc, 20) - self.assertIsNotNone(ex) - uops = get_opnames(ex) - self.assertNotIn("_LOAD_GLOBAL_MODULE", uops) - self.assertIn("_LOAD_CONST_INLINE_WITH_NULL", uops) - self.assertNotIn("_CHECK_FUNCTION_EXACT_ARGS", uops) - def test_float_add_constant_propagation(self): def testfunc(n): a = 1.0 diff --git a/Python/optimizer_bytecodes.c b/Python/optimizer_bytecodes.c index b8eaf0a1ce9121..b2c5b8ca4b23da 100644 --- a/Python/optimizer_bytecodes.c +++ b/Python/optimizer_bytecodes.c @@ -521,28 +521,6 @@ dummy_func(void) { OUT_OF_SPACE_IF_NULL(self = sym_new_not_null(ctx)); } - - op(_CHECK_FUNCTION_EXACT_ARGS, (func_version/2, callable, self_or_null, unused[oparg] -- callable, self_or_null, unused[oparg])) { - if (sym_is_const(callable) && - sym_matches_type(callable, &PyFunction_Type) && - (sym_is_null(self_or_null) || sym_is_not_null(self_or_null))) { - assert(PyFunction_Check(sym_get_const(callable))); - PyFunctionObject *func = (PyFunctionObject *)sym_get_const(callable); - if (func->func_version != func_version) { - goto hit_bottom; - } - PyCodeObject *code = (PyCodeObject *)func->func_code; - int argcount = oparg + sym_is_not_null(self_or_null); - if (code->co_argcount != argcount) { - goto hit_bottom; - } - REPLACE_OP(this_instr, _NOP, 0, 0); - } - if (!sym_set_type(callable, &PyFunction_Type)) { - goto hit_bottom; - } - } - op(_CHECK_CALL_BOUND_METHOD_EXACT_ARGS, (callable, null, unused[oparg] -- callable, null, unused[oparg])) { if (!sym_set_null(null)) { goto hit_bottom; From 3d28b37c90d7c55773263330ee981db993db3c42 Mon Sep 17 00:00:00 2001 From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com> Date: Mon, 11 Mar 2024 20:35:45 +0800 Subject: [PATCH 6/6] add back missing thing --- Python/optimizer_bytecodes.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Python/optimizer_bytecodes.c b/Python/optimizer_bytecodes.c index b2c5b8ca4b23da..54abbcd74d7934 100644 --- a/Python/optimizer_bytecodes.c +++ b/Python/optimizer_bytecodes.c @@ -521,6 +521,14 @@ dummy_func(void) { OUT_OF_SPACE_IF_NULL(self = sym_new_not_null(ctx)); } + op(_CHECK_FUNCTION_EXACT_ARGS, (func_version/2, callable, self_or_null, unused[oparg] -- callable, self_or_null, unused[oparg])) { + if (!sym_set_type(callable, &PyFunction_Type)) { + goto hit_bottom; + } + (void)self_or_null; + (void)func_version; + } + op(_CHECK_CALL_BOUND_METHOD_EXACT_ARGS, (callable, null, unused[oparg] -- callable, null, unused[oparg])) { if (!sym_set_null(null)) { goto hit_bottom;