mirror of https://github.com/python/cpython
gh-104584: Allow unspecialized instructions in superblocks (#106497)
This adds several of unspecialized opcodes to superblocks: TO_BOOL, BINARY_SUBSCR, STORE_SUBSCR, UNPACK_SEQUENCE, LOAD_GLOBAL, LOAD_ATTR, COMPARE_OP, BINARY_OP. While we may not want that eventually, for now this helps finding bugs. There is a rudimentary test checking for UNPACK_SEQUENCE. Once we're ready to undo this, that would be simple: just replace the call to variable_used_unspecialized with a call to variable_used (as shown in a comment). Or add individual opcdes to FORBIDDEN_NAMES_IN_UOPS.
This commit is contained in:
parent
11038c56ad
commit
b3648f036e
|
@ -2499,6 +2499,34 @@ class TestUops(unittest.TestCase):
|
|||
ex = get_first_executor(many_vars.__code__)
|
||||
self.assertIn(("LOAD_FAST", 259), list(ex))
|
||||
|
||||
def test_unspecialized_unpack(self):
|
||||
# An example of an unspecialized opcode
|
||||
def testfunc(x):
|
||||
i = 0
|
||||
while i < x:
|
||||
i += 1
|
||||
a, b = {1: 2, 3: 3}
|
||||
assert a == 1 and b == 3
|
||||
i = 0
|
||||
while i < x:
|
||||
i += 1
|
||||
|
||||
opt = _testinternalcapi.get_uop_optimizer()
|
||||
|
||||
with temporary_optimizer(opt):
|
||||
testfunc(10)
|
||||
|
||||
ex = None
|
||||
for offset in range(0, len(testfunc.__code__.co_code), 2):
|
||||
try:
|
||||
ex = _testinternalcapi.get_executor(testfunc.__code__, offset)
|
||||
break
|
||||
except ValueError:
|
||||
pass
|
||||
self.assertIsNotNone(ex)
|
||||
uops = {opname for opname, _ in ex}
|
||||
self.assertIn("UNPACK_SEQUENCE", uops)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1182,6 +1182,7 @@ const struct opcode_macro_expansion _PyOpcode_macro_expansion[256] = {
|
|||
[END_SEND] = { .nuops = 1, .uops = { { END_SEND, 0, 0 } } },
|
||||
[UNARY_NEGATIVE] = { .nuops = 1, .uops = { { UNARY_NEGATIVE, 0, 0 } } },
|
||||
[UNARY_NOT] = { .nuops = 1, .uops = { { UNARY_NOT, 0, 0 } } },
|
||||
[TO_BOOL] = { .nuops = 1, .uops = { { TO_BOOL, 0, 0 } } },
|
||||
[TO_BOOL_BOOL] = { .nuops = 1, .uops = { { TO_BOOL_BOOL, 0, 0 } } },
|
||||
[TO_BOOL_INT] = { .nuops = 1, .uops = { { TO_BOOL_INT, 0, 0 } } },
|
||||
[TO_BOOL_LIST] = { .nuops = 1, .uops = { { TO_BOOL_LIST, 0, 0 } } },
|
||||
|
@ -1196,6 +1197,7 @@ const struct opcode_macro_expansion _PyOpcode_macro_expansion[256] = {
|
|||
[BINARY_OP_ADD_FLOAT] = { .nuops = 2, .uops = { { _GUARD_BOTH_FLOAT, 0, 0 }, { _BINARY_OP_ADD_FLOAT, 0, 0 } } },
|
||||
[BINARY_OP_SUBTRACT_FLOAT] = { .nuops = 2, .uops = { { _GUARD_BOTH_FLOAT, 0, 0 }, { _BINARY_OP_SUBTRACT_FLOAT, 0, 0 } } },
|
||||
[BINARY_OP_ADD_UNICODE] = { .nuops = 2, .uops = { { _GUARD_BOTH_UNICODE, 0, 0 }, { _BINARY_OP_ADD_UNICODE, 0, 0 } } },
|
||||
[BINARY_SUBSCR] = { .nuops = 1, .uops = { { BINARY_SUBSCR, 0, 0 } } },
|
||||
[BINARY_SLICE] = { .nuops = 1, .uops = { { BINARY_SLICE, 0, 0 } } },
|
||||
[STORE_SLICE] = { .nuops = 1, .uops = { { STORE_SLICE, 0, 0 } } },
|
||||
[BINARY_SUBSCR_LIST_INT] = { .nuops = 1, .uops = { { BINARY_SUBSCR_LIST_INT, 0, 0 } } },
|
||||
|
@ -1203,6 +1205,7 @@ const struct opcode_macro_expansion _PyOpcode_macro_expansion[256] = {
|
|||
[BINARY_SUBSCR_DICT] = { .nuops = 1, .uops = { { BINARY_SUBSCR_DICT, 0, 0 } } },
|
||||
[LIST_APPEND] = { .nuops = 1, .uops = { { LIST_APPEND, 0, 0 } } },
|
||||
[SET_ADD] = { .nuops = 1, .uops = { { SET_ADD, 0, 0 } } },
|
||||
[STORE_SUBSCR] = { .nuops = 1, .uops = { { STORE_SUBSCR, 1, 0 } } },
|
||||
[STORE_SUBSCR_LIST_INT] = { .nuops = 1, .uops = { { STORE_SUBSCR_LIST_INT, 0, 0 } } },
|
||||
[STORE_SUBSCR_DICT] = { .nuops = 1, .uops = { { STORE_SUBSCR_DICT, 0, 0 } } },
|
||||
[DELETE_SUBSCR] = { .nuops = 1, .uops = { { DELETE_SUBSCR, 0, 0 } } },
|
||||
|
@ -1216,6 +1219,7 @@ const struct opcode_macro_expansion _PyOpcode_macro_expansion[256] = {
|
|||
[LOAD_BUILD_CLASS] = { .nuops = 1, .uops = { { LOAD_BUILD_CLASS, 0, 0 } } },
|
||||
[STORE_NAME] = { .nuops = 1, .uops = { { STORE_NAME, 0, 0 } } },
|
||||
[DELETE_NAME] = { .nuops = 1, .uops = { { DELETE_NAME, 0, 0 } } },
|
||||
[UNPACK_SEQUENCE] = { .nuops = 1, .uops = { { UNPACK_SEQUENCE, 0, 0 } } },
|
||||
[UNPACK_SEQUENCE_TWO_TUPLE] = { .nuops = 1, .uops = { { UNPACK_SEQUENCE_TWO_TUPLE, 0, 0 } } },
|
||||
[UNPACK_SEQUENCE_TUPLE] = { .nuops = 1, .uops = { { UNPACK_SEQUENCE_TUPLE, 0, 0 } } },
|
||||
[UNPACK_SEQUENCE_LIST] = { .nuops = 1, .uops = { { UNPACK_SEQUENCE_LIST, 0, 0 } } },
|
||||
|
@ -1226,6 +1230,7 @@ const struct opcode_macro_expansion _PyOpcode_macro_expansion[256] = {
|
|||
[LOAD_LOCALS] = { .nuops = 1, .uops = { { _LOAD_LOCALS, 0, 0 } } },
|
||||
[LOAD_NAME] = { .nuops = 2, .uops = { { _LOAD_LOCALS, 0, 0 }, { _LOAD_FROM_DICT_OR_GLOBALS, 0, 0 } } },
|
||||
[LOAD_FROM_DICT_OR_GLOBALS] = { .nuops = 1, .uops = { { _LOAD_FROM_DICT_OR_GLOBALS, 0, 0 } } },
|
||||
[LOAD_GLOBAL] = { .nuops = 1, .uops = { { LOAD_GLOBAL, 0, 0 } } },
|
||||
[DELETE_FAST] = { .nuops = 1, .uops = { { DELETE_FAST, 0, 0 } } },
|
||||
[DELETE_DEREF] = { .nuops = 1, .uops = { { DELETE_DEREF, 0, 0 } } },
|
||||
[LOAD_FROM_DICT_OR_DEREF] = { .nuops = 1, .uops = { { LOAD_FROM_DICT_OR_DEREF, 0, 0 } } },
|
||||
|
@ -1246,6 +1251,8 @@ const struct opcode_macro_expansion _PyOpcode_macro_expansion[256] = {
|
|||
[MAP_ADD] = { .nuops = 1, .uops = { { MAP_ADD, 0, 0 } } },
|
||||
[LOAD_SUPER_ATTR_ATTR] = { .nuops = 1, .uops = { { LOAD_SUPER_ATTR_ATTR, 0, 0 } } },
|
||||
[LOAD_SUPER_ATTR_METHOD] = { .nuops = 1, .uops = { { LOAD_SUPER_ATTR_METHOD, 0, 0 } } },
|
||||
[LOAD_ATTR] = { .nuops = 1, .uops = { { LOAD_ATTR, 0, 0 } } },
|
||||
[COMPARE_OP] = { .nuops = 1, .uops = { { COMPARE_OP, 0, 0 } } },
|
||||
[COMPARE_OP_FLOAT] = { .nuops = 1, .uops = { { COMPARE_OP_FLOAT, 0, 0 } } },
|
||||
[COMPARE_OP_INT] = { .nuops = 1, .uops = { { COMPARE_OP_INT, 0, 0 } } },
|
||||
[COMPARE_OP_STR] = { .nuops = 1, .uops = { { COMPARE_OP_STR, 0, 0 } } },
|
||||
|
@ -1270,6 +1277,7 @@ const struct opcode_macro_expansion _PyOpcode_macro_expansion[256] = {
|
|||
[FORMAT_SIMPLE] = { .nuops = 1, .uops = { { FORMAT_SIMPLE, 0, 0 } } },
|
||||
[FORMAT_WITH_SPEC] = { .nuops = 1, .uops = { { FORMAT_WITH_SPEC, 0, 0 } } },
|
||||
[COPY] = { .nuops = 1, .uops = { { COPY, 0, 0 } } },
|
||||
[BINARY_OP] = { .nuops = 1, .uops = { { BINARY_OP, 0, 0 } } },
|
||||
[SWAP] = { .nuops = 1, .uops = { { SWAP, 0, 0 } } },
|
||||
};
|
||||
#ifdef NEED_OPCODE_METADATA
|
||||
|
|
|
@ -425,8 +425,9 @@ class Instruction:
|
|||
return False
|
||||
res = True
|
||||
for forbidden in FORBIDDEN_NAMES_IN_UOPS:
|
||||
# TODO: Don't check in '#ifdef ENABLE_SPECIALIZATION' regions
|
||||
if variable_used(self.inst, forbidden):
|
||||
# NOTE: To disallow unspecialized uops, use
|
||||
# if variable_used(self.inst, forbidden):
|
||||
if variable_used_unspecialized(self.inst, forbidden):
|
||||
# print(f"Skipping {self.name} because it uses {forbidden}")
|
||||
res = False
|
||||
return res
|
||||
|
@ -1644,6 +1645,27 @@ def variable_used(node: parser.Node, name: str) -> bool:
|
|||
)
|
||||
|
||||
|
||||
def variable_used_unspecialized(node: parser.Node, name: str) -> bool:
|
||||
"""Like variable_used(), but skips #if ENABLE_SPECIALIZATION blocks."""
|
||||
tokens: list[lx.Token] = []
|
||||
skipping = False
|
||||
for i, token in enumerate(node.tokens):
|
||||
if token.kind == "MACRO":
|
||||
text = "".join(token.text.split())
|
||||
# TODO: Handle nested #if
|
||||
if text == "#if":
|
||||
if (
|
||||
i + 1 < len(node.tokens)
|
||||
and node.tokens[i + 1].text == "ENABLE_SPECIALIZATION"
|
||||
):
|
||||
skipping = True
|
||||
elif text in ("#else", "#endif"):
|
||||
skipping = False
|
||||
if not skipping:
|
||||
tokens.append(token)
|
||||
return any(token.kind == "IDENTIFIER" and token.text == name for token in tokens)
|
||||
|
||||
|
||||
def main():
|
||||
"""Parse command line, parse input, analyze, write output."""
|
||||
args = arg_parser.parse_args() # Prints message and sys.exit(2) on error
|
||||
|
|
Loading…
Reference in New Issue