mirror of
https://github.com/python/cpython
synced 2024-10-14 15:09:02 +00:00
GH-111485: Allow arbitrary annotations on instructions and micro-ops. (GH-111697)
This commit is contained in:
parent
13405ecffd
commit
931f4438c9
10
Include/internal/pycore_opcode_metadata.h
generated
10
Include/internal/pycore_opcode_metadata.h
generated
|
@ -1771,6 +1771,7 @@ const struct opcode_macro_expansion _PyOpcode_macro_expansion[OPCODE_MACRO_EXPAN
|
||||||
[END_SEND] = { .nuops = 1, .uops = { { END_SEND, 0, 0 } } },
|
[END_SEND] = { .nuops = 1, .uops = { { END_SEND, 0, 0 } } },
|
||||||
[UNARY_NEGATIVE] = { .nuops = 1, .uops = { { UNARY_NEGATIVE, 0, 0 } } },
|
[UNARY_NEGATIVE] = { .nuops = 1, .uops = { { UNARY_NEGATIVE, 0, 0 } } },
|
||||||
[UNARY_NOT] = { .nuops = 1, .uops = { { UNARY_NOT, 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_BOOL] = { .nuops = 1, .uops = { { TO_BOOL_BOOL, 0, 0 } } },
|
||||||
[TO_BOOL_INT] = { .nuops = 1, .uops = { { TO_BOOL_INT, 0, 0 } } },
|
[TO_BOOL_INT] = { .nuops = 1, .uops = { { TO_BOOL_INT, 0, 0 } } },
|
||||||
[TO_BOOL_LIST] = { .nuops = 1, .uops = { { TO_BOOL_LIST, 0, 0 } } },
|
[TO_BOOL_LIST] = { .nuops = 1, .uops = { { TO_BOOL_LIST, 0, 0 } } },
|
||||||
|
@ -1785,6 +1786,7 @@ const struct opcode_macro_expansion _PyOpcode_macro_expansion[OPCODE_MACRO_EXPAN
|
||||||
[BINARY_OP_ADD_FLOAT] = { .nuops = 2, .uops = { { _GUARD_BOTH_FLOAT, 0, 0 }, { _BINARY_OP_ADD_FLOAT, 0, 0 } } },
|
[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_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_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 } } },
|
[BINARY_SLICE] = { .nuops = 1, .uops = { { BINARY_SLICE, 0, 0 } } },
|
||||||
[STORE_SLICE] = { .nuops = 1, .uops = { { STORE_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 } } },
|
[BINARY_SUBSCR_LIST_INT] = { .nuops = 1, .uops = { { BINARY_SUBSCR_LIST_INT, 0, 0 } } },
|
||||||
|
@ -1793,6 +1795,7 @@ const struct opcode_macro_expansion _PyOpcode_macro_expansion[OPCODE_MACRO_EXPAN
|
||||||
[BINARY_SUBSCR_DICT] = { .nuops = 1, .uops = { { BINARY_SUBSCR_DICT, 0, 0 } } },
|
[BINARY_SUBSCR_DICT] = { .nuops = 1, .uops = { { BINARY_SUBSCR_DICT, 0, 0 } } },
|
||||||
[LIST_APPEND] = { .nuops = 1, .uops = { { LIST_APPEND, 0, 0 } } },
|
[LIST_APPEND] = { .nuops = 1, .uops = { { LIST_APPEND, 0, 0 } } },
|
||||||
[SET_ADD] = { .nuops = 1, .uops = { { SET_ADD, 0, 0 } } },
|
[SET_ADD] = { .nuops = 1, .uops = { { SET_ADD, 0, 0 } } },
|
||||||
|
[STORE_SUBSCR] = { .nuops = 1, .uops = { { _STORE_SUBSCR, 0, 0 } } },
|
||||||
[STORE_SUBSCR_LIST_INT] = { .nuops = 1, .uops = { { STORE_SUBSCR_LIST_INT, 0, 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 } } },
|
[STORE_SUBSCR_DICT] = { .nuops = 1, .uops = { { STORE_SUBSCR_DICT, 0, 0 } } },
|
||||||
[DELETE_SUBSCR] = { .nuops = 1, .uops = { { DELETE_SUBSCR, 0, 0 } } },
|
[DELETE_SUBSCR] = { .nuops = 1, .uops = { { DELETE_SUBSCR, 0, 0 } } },
|
||||||
|
@ -1808,17 +1811,19 @@ const struct opcode_macro_expansion _PyOpcode_macro_expansion[OPCODE_MACRO_EXPAN
|
||||||
[LOAD_BUILD_CLASS] = { .nuops = 1, .uops = { { LOAD_BUILD_CLASS, 0, 0 } } },
|
[LOAD_BUILD_CLASS] = { .nuops = 1, .uops = { { LOAD_BUILD_CLASS, 0, 0 } } },
|
||||||
[STORE_NAME] = { .nuops = 1, .uops = { { STORE_NAME, 0, 0 } } },
|
[STORE_NAME] = { .nuops = 1, .uops = { { STORE_NAME, 0, 0 } } },
|
||||||
[DELETE_NAME] = { .nuops = 1, .uops = { { DELETE_NAME, 0, 0 } } },
|
[DELETE_NAME] = { .nuops = 1, .uops = { { DELETE_NAME, 0, 0 } } },
|
||||||
[UNPACK_SEQUENCE] = { .nuops = 2, .uops = { { _SPECIALIZE_UNPACK_SEQUENCE, 1, 0 }, { _UNPACK_SEQUENCE, 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_TWO_TUPLE] = { .nuops = 1, .uops = { { UNPACK_SEQUENCE_TWO_TUPLE, 0, 0 } } },
|
||||||
[UNPACK_SEQUENCE_TUPLE] = { .nuops = 1, .uops = { { UNPACK_SEQUENCE_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 } } },
|
[UNPACK_SEQUENCE_LIST] = { .nuops = 1, .uops = { { UNPACK_SEQUENCE_LIST, 0, 0 } } },
|
||||||
[UNPACK_EX] = { .nuops = 1, .uops = { { UNPACK_EX, 0, 0 } } },
|
[UNPACK_EX] = { .nuops = 1, .uops = { { UNPACK_EX, 0, 0 } } },
|
||||||
|
[STORE_ATTR] = { .nuops = 1, .uops = { { _STORE_ATTR, 0, 0 } } },
|
||||||
[DELETE_ATTR] = { .nuops = 1, .uops = { { DELETE_ATTR, 0, 0 } } },
|
[DELETE_ATTR] = { .nuops = 1, .uops = { { DELETE_ATTR, 0, 0 } } },
|
||||||
[STORE_GLOBAL] = { .nuops = 1, .uops = { { STORE_GLOBAL, 0, 0 } } },
|
[STORE_GLOBAL] = { .nuops = 1, .uops = { { STORE_GLOBAL, 0, 0 } } },
|
||||||
[DELETE_GLOBAL] = { .nuops = 1, .uops = { { DELETE_GLOBAL, 0, 0 } } },
|
[DELETE_GLOBAL] = { .nuops = 1, .uops = { { DELETE_GLOBAL, 0, 0 } } },
|
||||||
[LOAD_LOCALS] = { .nuops = 1, .uops = { { LOAD_LOCALS, 0, 0 } } },
|
[LOAD_LOCALS] = { .nuops = 1, .uops = { { LOAD_LOCALS, 0, 0 } } },
|
||||||
[LOAD_FROM_DICT_OR_GLOBALS] = { .nuops = 1, .uops = { { LOAD_FROM_DICT_OR_GLOBALS, 0, 0 } } },
|
[LOAD_FROM_DICT_OR_GLOBALS] = { .nuops = 1, .uops = { { LOAD_FROM_DICT_OR_GLOBALS, 0, 0 } } },
|
||||||
[LOAD_NAME] = { .nuops = 1, .uops = { { LOAD_NAME, 0, 0 } } },
|
[LOAD_NAME] = { .nuops = 1, .uops = { { LOAD_NAME, 0, 0 } } },
|
||||||
|
[LOAD_GLOBAL] = { .nuops = 1, .uops = { { _LOAD_GLOBAL, 0, 0 } } },
|
||||||
[LOAD_GLOBAL_MODULE] = { .nuops = 2, .uops = { { _GUARD_GLOBALS_VERSION, 1, 1 }, { _LOAD_GLOBAL_MODULE, 1, 3 } } },
|
[LOAD_GLOBAL_MODULE] = { .nuops = 2, .uops = { { _GUARD_GLOBALS_VERSION, 1, 1 }, { _LOAD_GLOBAL_MODULE, 1, 3 } } },
|
||||||
[LOAD_GLOBAL_BUILTIN] = { .nuops = 3, .uops = { { _GUARD_GLOBALS_VERSION, 1, 1 }, { _GUARD_BUILTINS_VERSION, 1, 2 }, { _LOAD_GLOBAL_BUILTINS, 1, 3 } } },
|
[LOAD_GLOBAL_BUILTIN] = { .nuops = 3, .uops = { { _GUARD_GLOBALS_VERSION, 1, 1 }, { _GUARD_BUILTINS_VERSION, 1, 2 }, { _LOAD_GLOBAL_BUILTINS, 1, 3 } } },
|
||||||
[DELETE_FAST] = { .nuops = 1, .uops = { { DELETE_FAST, 0, 0 } } },
|
[DELETE_FAST] = { .nuops = 1, .uops = { { DELETE_FAST, 0, 0 } } },
|
||||||
|
@ -1842,6 +1847,7 @@ const struct opcode_macro_expansion _PyOpcode_macro_expansion[OPCODE_MACRO_EXPAN
|
||||||
[MAP_ADD] = { .nuops = 1, .uops = { { MAP_ADD, 0, 0 } } },
|
[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_ATTR] = { .nuops = 1, .uops = { { LOAD_SUPER_ATTR_ATTR, 0, 0 } } },
|
||||||
[LOAD_SUPER_ATTR_METHOD] = { .nuops = 1, .uops = { { LOAD_SUPER_ATTR_METHOD, 0, 0 } } },
|
[LOAD_SUPER_ATTR_METHOD] = { .nuops = 1, .uops = { { LOAD_SUPER_ATTR_METHOD, 0, 0 } } },
|
||||||
|
[LOAD_ATTR] = { .nuops = 1, .uops = { { _LOAD_ATTR, 0, 0 } } },
|
||||||
[LOAD_ATTR_INSTANCE_VALUE] = { .nuops = 3, .uops = { { _GUARD_TYPE_VERSION, 2, 1 }, { _CHECK_MANAGED_OBJECT_HAS_VALUES, 0, 0 }, { _LOAD_ATTR_INSTANCE_VALUE, 1, 3 } } },
|
[LOAD_ATTR_INSTANCE_VALUE] = { .nuops = 3, .uops = { { _GUARD_TYPE_VERSION, 2, 1 }, { _CHECK_MANAGED_OBJECT_HAS_VALUES, 0, 0 }, { _LOAD_ATTR_INSTANCE_VALUE, 1, 3 } } },
|
||||||
[LOAD_ATTR_MODULE] = { .nuops = 2, .uops = { { _CHECK_ATTR_MODULE, 2, 1 }, { _LOAD_ATTR_MODULE, 1, 3 } } },
|
[LOAD_ATTR_MODULE] = { .nuops = 2, .uops = { { _CHECK_ATTR_MODULE, 2, 1 }, { _LOAD_ATTR_MODULE, 1, 3 } } },
|
||||||
[LOAD_ATTR_WITH_HINT] = { .nuops = 3, .uops = { { _GUARD_TYPE_VERSION, 2, 1 }, { _CHECK_ATTR_WITH_HINT, 0, 0 }, { _LOAD_ATTR_WITH_HINT, 1, 3 } } },
|
[LOAD_ATTR_WITH_HINT] = { .nuops = 3, .uops = { { _GUARD_TYPE_VERSION, 2, 1 }, { _CHECK_ATTR_WITH_HINT, 0, 0 }, { _LOAD_ATTR_WITH_HINT, 1, 3 } } },
|
||||||
|
@ -1849,6 +1855,7 @@ const struct opcode_macro_expansion _PyOpcode_macro_expansion[OPCODE_MACRO_EXPAN
|
||||||
[LOAD_ATTR_CLASS] = { .nuops = 2, .uops = { { _CHECK_ATTR_CLASS, 2, 1 }, { _LOAD_ATTR_CLASS, 4, 5 } } },
|
[LOAD_ATTR_CLASS] = { .nuops = 2, .uops = { { _CHECK_ATTR_CLASS, 2, 1 }, { _LOAD_ATTR_CLASS, 4, 5 } } },
|
||||||
[STORE_ATTR_INSTANCE_VALUE] = { .nuops = 3, .uops = { { _GUARD_TYPE_VERSION, 2, 1 }, { _GUARD_DORV_VALUES, 0, 0 }, { _STORE_ATTR_INSTANCE_VALUE, 1, 3 } } },
|
[STORE_ATTR_INSTANCE_VALUE] = { .nuops = 3, .uops = { { _GUARD_TYPE_VERSION, 2, 1 }, { _GUARD_DORV_VALUES, 0, 0 }, { _STORE_ATTR_INSTANCE_VALUE, 1, 3 } } },
|
||||||
[STORE_ATTR_SLOT] = { .nuops = 2, .uops = { { _GUARD_TYPE_VERSION, 2, 1 }, { _STORE_ATTR_SLOT, 1, 3 } } },
|
[STORE_ATTR_SLOT] = { .nuops = 2, .uops = { { _GUARD_TYPE_VERSION, 2, 1 }, { _STORE_ATTR_SLOT, 1, 3 } } },
|
||||||
|
[COMPARE_OP] = { .nuops = 1, .uops = { { _COMPARE_OP, 0, 0 } } },
|
||||||
[COMPARE_OP_FLOAT] = { .nuops = 1, .uops = { { COMPARE_OP_FLOAT, 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_INT] = { .nuops = 1, .uops = { { COMPARE_OP_INT, 0, 0 } } },
|
||||||
[COMPARE_OP_STR] = { .nuops = 1, .uops = { { COMPARE_OP_STR, 0, 0 } } },
|
[COMPARE_OP_STR] = { .nuops = 1, .uops = { { COMPARE_OP_STR, 0, 0 } } },
|
||||||
|
@ -1895,6 +1902,7 @@ const struct opcode_macro_expansion _PyOpcode_macro_expansion[OPCODE_MACRO_EXPAN
|
||||||
[FORMAT_SIMPLE] = { .nuops = 1, .uops = { { FORMAT_SIMPLE, 0, 0 } } },
|
[FORMAT_SIMPLE] = { .nuops = 1, .uops = { { FORMAT_SIMPLE, 0, 0 } } },
|
||||||
[FORMAT_WITH_SPEC] = { .nuops = 1, .uops = { { FORMAT_WITH_SPEC, 0, 0 } } },
|
[FORMAT_WITH_SPEC] = { .nuops = 1, .uops = { { FORMAT_WITH_SPEC, 0, 0 } } },
|
||||||
[COPY] = { .nuops = 1, .uops = { { COPY, 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 } } },
|
[SWAP] = { .nuops = 1, .uops = { { SWAP, 0, 0 } } },
|
||||||
};
|
};
|
||||||
#endif // NEED_OPCODE_METADATA
|
#endif // NEED_OPCODE_METADATA
|
||||||
|
|
|
@ -707,6 +707,49 @@ def test_override_op(self):
|
||||||
"""
|
"""
|
||||||
self.run_cases_test(input, output)
|
self.run_cases_test(input, output)
|
||||||
|
|
||||||
|
def test_annotated_inst(self):
|
||||||
|
input = """
|
||||||
|
guard inst(OP, (--)) {
|
||||||
|
ham();
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
output = """
|
||||||
|
TARGET(OP) {
|
||||||
|
frame->instr_ptr = next_instr;
|
||||||
|
next_instr += 1;
|
||||||
|
INSTRUCTION_STATS(OP);
|
||||||
|
ham();
|
||||||
|
DISPATCH();
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
self.run_cases_test(input, output)
|
||||||
|
|
||||||
|
def test_annotated_op(self):
|
||||||
|
input = """
|
||||||
|
guard op(OP, (--)) {
|
||||||
|
spam();
|
||||||
|
}
|
||||||
|
macro(M) = OP;
|
||||||
|
"""
|
||||||
|
output = """
|
||||||
|
TARGET(M) {
|
||||||
|
frame->instr_ptr = next_instr;
|
||||||
|
next_instr += 1;
|
||||||
|
INSTRUCTION_STATS(M);
|
||||||
|
spam();
|
||||||
|
DISPATCH();
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
self.run_cases_test(input, output)
|
||||||
|
|
||||||
|
input = """
|
||||||
|
guard register specializing op(OP, (--)) {
|
||||||
|
spam();
|
||||||
|
}
|
||||||
|
macro(M) = OP;
|
||||||
|
"""
|
||||||
|
self.run_cases_test(input, output)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
4
Python/abstract_interp_cases.c.h
generated
4
Python/abstract_interp_cases.c.h
generated
|
@ -242,10 +242,6 @@
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case _SPECIALIZE_UNPACK_SEQUENCE: {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case _UNPACK_SEQUENCE: {
|
case _UNPACK_SEQUENCE: {
|
||||||
STACK_SHRINK(1);
|
STACK_SHRINK(1);
|
||||||
STACK_GROW(oparg);
|
STACK_GROW(oparg);
|
||||||
|
|
|
@ -50,6 +50,11 @@
|
||||||
#define family(name, ...) static int family_##name
|
#define family(name, ...) static int family_##name
|
||||||
#define pseudo(name) static int pseudo_##name
|
#define pseudo(name) static int pseudo_##name
|
||||||
|
|
||||||
|
/* Annotations */
|
||||||
|
#define guard
|
||||||
|
#define override
|
||||||
|
#define specializing
|
||||||
|
|
||||||
// Dummy variables for stack effects.
|
// Dummy variables for stack effects.
|
||||||
static PyObject *value, *value1, *value2, *left, *right, *res, *sum, *prod, *sub;
|
static PyObject *value, *value1, *value2, *left, *right, *res, *sum, *prod, *sub;
|
||||||
static PyObject *container, *start, *stop, *v, *lhs, *rhs, *res2;
|
static PyObject *container, *start, *stop, *v, *lhs, *rhs, *res2;
|
||||||
|
@ -312,7 +317,7 @@ dummy_func(
|
||||||
TO_BOOL_STR,
|
TO_BOOL_STR,
|
||||||
};
|
};
|
||||||
|
|
||||||
op(_SPECIALIZE_TO_BOOL, (counter/1, value -- value)) {
|
specializing op(_SPECIALIZE_TO_BOOL, (counter/1, value -- value)) {
|
||||||
TIER_ONE_ONLY
|
TIER_ONE_ONLY
|
||||||
#if ENABLE_SPECIALIZATION
|
#if ENABLE_SPECIALIZATION
|
||||||
if (ADAPTIVE_COUNTER_IS_ZERO(counter)) {
|
if (ADAPTIVE_COUNTER_IS_ZERO(counter)) {
|
||||||
|
@ -537,7 +542,7 @@ dummy_func(
|
||||||
BINARY_SUBSCR_TUPLE_INT,
|
BINARY_SUBSCR_TUPLE_INT,
|
||||||
};
|
};
|
||||||
|
|
||||||
op(_SPECIALIZE_BINARY_SUBSCR, (counter/1, container, sub -- container, sub)) {
|
specializing op(_SPECIALIZE_BINARY_SUBSCR, (counter/1, container, sub -- container, sub)) {
|
||||||
TIER_ONE_ONLY
|
TIER_ONE_ONLY
|
||||||
#if ENABLE_SPECIALIZATION
|
#if ENABLE_SPECIALIZATION
|
||||||
if (ADAPTIVE_COUNTER_IS_ZERO(counter)) {
|
if (ADAPTIVE_COUNTER_IS_ZERO(counter)) {
|
||||||
|
@ -689,7 +694,7 @@ dummy_func(
|
||||||
STORE_SUBSCR_LIST_INT,
|
STORE_SUBSCR_LIST_INT,
|
||||||
};
|
};
|
||||||
|
|
||||||
op(_SPECIALIZE_STORE_SUBSCR, (counter/1, container, sub -- container, sub)) {
|
specializing op(_SPECIALIZE_STORE_SUBSCR, (counter/1, container, sub -- container, sub)) {
|
||||||
TIER_ONE_ONLY
|
TIER_ONE_ONLY
|
||||||
#if ENABLE_SPECIALIZATION
|
#if ENABLE_SPECIALIZATION
|
||||||
if (ADAPTIVE_COUNTER_IS_ZERO(counter)) {
|
if (ADAPTIVE_COUNTER_IS_ZERO(counter)) {
|
||||||
|
@ -974,7 +979,7 @@ dummy_func(
|
||||||
SEND_GEN,
|
SEND_GEN,
|
||||||
};
|
};
|
||||||
|
|
||||||
op(_SPECIALIZE_SEND, (counter/1, receiver, unused -- receiver, unused)) {
|
specializing op(_SPECIALIZE_SEND, (counter/1, receiver, unused -- receiver, unused)) {
|
||||||
TIER_ONE_ONLY
|
TIER_ONE_ONLY
|
||||||
#if ENABLE_SPECIALIZATION
|
#if ENABLE_SPECIALIZATION
|
||||||
if (ADAPTIVE_COUNTER_IS_ZERO(counter)) {
|
if (ADAPTIVE_COUNTER_IS_ZERO(counter)) {
|
||||||
|
@ -1208,7 +1213,7 @@ dummy_func(
|
||||||
UNPACK_SEQUENCE_LIST,
|
UNPACK_SEQUENCE_LIST,
|
||||||
};
|
};
|
||||||
|
|
||||||
op(_SPECIALIZE_UNPACK_SEQUENCE, (counter/1, seq -- seq)) {
|
specializing op(_SPECIALIZE_UNPACK_SEQUENCE, (counter/1, seq -- seq)) {
|
||||||
#if ENABLE_SPECIALIZATION
|
#if ENABLE_SPECIALIZATION
|
||||||
if (ADAPTIVE_COUNTER_IS_ZERO(counter)) {
|
if (ADAPTIVE_COUNTER_IS_ZERO(counter)) {
|
||||||
next_instr = this_instr;
|
next_instr = this_instr;
|
||||||
|
@ -1277,7 +1282,7 @@ dummy_func(
|
||||||
STORE_ATTR_WITH_HINT,
|
STORE_ATTR_WITH_HINT,
|
||||||
};
|
};
|
||||||
|
|
||||||
op(_SPECIALIZE_STORE_ATTR, (counter/1, owner -- owner)) {
|
specializing op(_SPECIALIZE_STORE_ATTR, (counter/1, owner -- owner)) {
|
||||||
TIER_ONE_ONLY
|
TIER_ONE_ONLY
|
||||||
#if ENABLE_SPECIALIZATION
|
#if ENABLE_SPECIALIZATION
|
||||||
if (ADAPTIVE_COUNTER_IS_ZERO(counter)) {
|
if (ADAPTIVE_COUNTER_IS_ZERO(counter)) {
|
||||||
|
@ -1404,7 +1409,7 @@ dummy_func(
|
||||||
LOAD_GLOBAL_BUILTIN,
|
LOAD_GLOBAL_BUILTIN,
|
||||||
};
|
};
|
||||||
|
|
||||||
op(_SPECIALIZE_LOAD_GLOBAL, (counter/1 -- )) {
|
specializing op(_SPECIALIZE_LOAD_GLOBAL, (counter/1 -- )) {
|
||||||
TIER_ONE_ONLY
|
TIER_ONE_ONLY
|
||||||
#if ENABLE_SPECIALIZATION
|
#if ENABLE_SPECIALIZATION
|
||||||
if (ADAPTIVE_COUNTER_IS_ZERO(counter)) {
|
if (ADAPTIVE_COUNTER_IS_ZERO(counter)) {
|
||||||
|
@ -1744,7 +1749,7 @@ dummy_func(
|
||||||
LOAD_SUPER_ATTR_METHOD,
|
LOAD_SUPER_ATTR_METHOD,
|
||||||
};
|
};
|
||||||
|
|
||||||
op(_SPECIALIZE_LOAD_SUPER_ATTR, (counter/1, global_super, class, unused -- global_super, class, unused)) {
|
specializing op(_SPECIALIZE_LOAD_SUPER_ATTR, (counter/1, global_super, class, unused -- global_super, class, unused)) {
|
||||||
TIER_ONE_ONLY
|
TIER_ONE_ONLY
|
||||||
#if ENABLE_SPECIALIZATION
|
#if ENABLE_SPECIALIZATION
|
||||||
int load_method = oparg & 1;
|
int load_method = oparg & 1;
|
||||||
|
@ -1860,7 +1865,7 @@ dummy_func(
|
||||||
LOAD_ATTR_NONDESCRIPTOR_NO_DICT,
|
LOAD_ATTR_NONDESCRIPTOR_NO_DICT,
|
||||||
};
|
};
|
||||||
|
|
||||||
op(_SPECIALIZE_LOAD_ATTR, (counter/1, owner -- owner)) {
|
specializing op(_SPECIALIZE_LOAD_ATTR, (counter/1, owner -- owner)) {
|
||||||
TIER_ONE_ONLY
|
TIER_ONE_ONLY
|
||||||
#if ENABLE_SPECIALIZATION
|
#if ENABLE_SPECIALIZATION
|
||||||
if (ADAPTIVE_COUNTER_IS_ZERO(counter)) {
|
if (ADAPTIVE_COUNTER_IS_ZERO(counter)) {
|
||||||
|
@ -2182,7 +2187,7 @@ dummy_func(
|
||||||
COMPARE_OP_STR,
|
COMPARE_OP_STR,
|
||||||
};
|
};
|
||||||
|
|
||||||
op(_SPECIALIZE_COMPARE_OP, (counter/1, left, right -- left, right)) {
|
specializing op(_SPECIALIZE_COMPARE_OP, (counter/1, left, right -- left, right)) {
|
||||||
TIER_ONE_ONLY
|
TIER_ONE_ONLY
|
||||||
#if ENABLE_SPECIALIZATION
|
#if ENABLE_SPECIALIZATION
|
||||||
if (ADAPTIVE_COUNTER_IS_ZERO(counter)) {
|
if (ADAPTIVE_COUNTER_IS_ZERO(counter)) {
|
||||||
|
@ -2506,7 +2511,7 @@ dummy_func(
|
||||||
FOR_ITER_GEN,
|
FOR_ITER_GEN,
|
||||||
};
|
};
|
||||||
|
|
||||||
op(_SPECIALIZE_FOR_ITER, (counter/1, iter -- iter)) {
|
specializing op(_SPECIALIZE_FOR_ITER, (counter/1, iter -- iter)) {
|
||||||
TIER_ONE_ONLY
|
TIER_ONE_ONLY
|
||||||
#if ENABLE_SPECIALIZATION
|
#if ENABLE_SPECIALIZATION
|
||||||
if (ADAPTIVE_COUNTER_IS_ZERO(counter)) {
|
if (ADAPTIVE_COUNTER_IS_ZERO(counter)) {
|
||||||
|
@ -3000,7 +3005,7 @@ dummy_func(
|
||||||
CALL_ALLOC_AND_ENTER_INIT,
|
CALL_ALLOC_AND_ENTER_INIT,
|
||||||
};
|
};
|
||||||
|
|
||||||
op(_SPECIALIZE_CALL, (counter/1, callable, self_or_null, args[oparg] -- callable, self_or_null, args[oparg])) {
|
specializing op(_SPECIALIZE_CALL, (counter/1, callable, self_or_null, args[oparg] -- callable, self_or_null, args[oparg])) {
|
||||||
TIER_ONE_ONLY
|
TIER_ONE_ONLY
|
||||||
#if ENABLE_SPECIALIZATION
|
#if ENABLE_SPECIALIZATION
|
||||||
if (ADAPTIVE_COUNTER_IS_ZERO(counter)) {
|
if (ADAPTIVE_COUNTER_IS_ZERO(counter)) {
|
||||||
|
@ -3865,7 +3870,7 @@ dummy_func(
|
||||||
top = Py_NewRef(bottom);
|
top = Py_NewRef(bottom);
|
||||||
}
|
}
|
||||||
|
|
||||||
op(_SPECIALIZE_BINARY_OP, (counter/1, lhs, rhs -- lhs, rhs)) {
|
specializing op(_SPECIALIZE_BINARY_OP, (counter/1, lhs, rhs -- lhs, rhs)) {
|
||||||
TIER_ONE_ONLY
|
TIER_ONE_ONLY
|
||||||
#if ENABLE_SPECIALIZATION
|
#if ENABLE_SPECIALIZATION
|
||||||
if (ADAPTIVE_COUNTER_IS_ZERO(counter)) {
|
if (ADAPTIVE_COUNTER_IS_ZERO(counter)) {
|
||||||
|
|
18
Python/executor_cases.c.h
generated
18
Python/executor_cases.c.h
generated
|
@ -871,24 +871,6 @@
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case _SPECIALIZE_UNPACK_SEQUENCE: {
|
|
||||||
PyObject *seq;
|
|
||||||
seq = stack_pointer[-1];
|
|
||||||
uint16_t counter = (uint16_t)next_uop[-1].operand;
|
|
||||||
#if ENABLE_SPECIALIZATION
|
|
||||||
if (ADAPTIVE_COUNTER_IS_ZERO(counter)) {
|
|
||||||
next_instr = this_instr;
|
|
||||||
_Py_Specialize_UnpackSequence(seq, next_instr, oparg);
|
|
||||||
DISPATCH_SAME_OPARG();
|
|
||||||
}
|
|
||||||
STAT_INC(UNPACK_SEQUENCE, deferred);
|
|
||||||
DECREMENT_ADAPTIVE_COUNTER(this_instr[1].cache);
|
|
||||||
#endif /* ENABLE_SPECIALIZATION */
|
|
||||||
(void)seq;
|
|
||||||
(void)counter;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case _UNPACK_SEQUENCE: {
|
case _UNPACK_SEQUENCE: {
|
||||||
PyObject *seq;
|
PyObject *seq;
|
||||||
seq = stack_pointer[-1];
|
seq = stack_pointer[-1];
|
||||||
|
|
|
@ -139,17 +139,17 @@ def parse_file(self, filename: str, instrs_idx: dict[str, int]) -> None:
|
||||||
match thing:
|
match thing:
|
||||||
case parsing.InstDef(name=name):
|
case parsing.InstDef(name=name):
|
||||||
macro: parsing.Macro | None = None
|
macro: parsing.Macro | None = None
|
||||||
if thing.kind == "inst" and not thing.override:
|
if thing.kind == "inst" and "override" not in thing.annotations:
|
||||||
macro = parsing.Macro(name, [parsing.OpName(name)])
|
macro = parsing.Macro(name, [parsing.OpName(name)])
|
||||||
if name in self.instrs:
|
if name in self.instrs:
|
||||||
if not thing.override:
|
if "override" not in thing.annotations:
|
||||||
raise psr.make_syntax_error(
|
raise psr.make_syntax_error(
|
||||||
f"Duplicate definition of '{name}' @ {thing.context} "
|
f"Duplicate definition of '{name}' @ {thing.context} "
|
||||||
f"previous definition @ {self.instrs[name].inst.context}",
|
f"previous definition @ {self.instrs[name].inst.context}",
|
||||||
thing_first_token,
|
thing_first_token,
|
||||||
)
|
)
|
||||||
self.everything[instrs_idx[name]] = thing
|
self.everything[instrs_idx[name]] = thing
|
||||||
if name not in self.instrs and thing.override:
|
if name not in self.instrs and "override" in thing.annotations:
|
||||||
raise psr.make_syntax_error(
|
raise psr.make_syntax_error(
|
||||||
f"Definition of '{name}' @ {thing.context} is supposed to be "
|
f"Definition of '{name}' @ {thing.context} is supposed to be "
|
||||||
"an override but no previous definition exists.",
|
"an override but no previous definition exists.",
|
||||||
|
|
|
@ -651,7 +651,10 @@ def write_macro_expansions(
|
||||||
expansions: list[tuple[str, int, int]] = [] # [(name, size, offset), ...]
|
expansions: list[tuple[str, int, int]] = [] # [(name, size, offset), ...]
|
||||||
for part in parts:
|
for part in parts:
|
||||||
if isinstance(part, Component):
|
if isinstance(part, Component):
|
||||||
# All component instructions must be viable uops
|
# Skip specializations
|
||||||
|
if "specializing" in part.instr.annotations:
|
||||||
|
continue
|
||||||
|
# All other component instructions must be viable uops
|
||||||
if not part.instr.is_viable_uop():
|
if not part.instr.is_viable_uop():
|
||||||
# This note just reminds us about macros that cannot
|
# This note just reminds us about macros that cannot
|
||||||
# be expanded to Tier 2 uops. It is not an error.
|
# be expanded to Tier 2 uops. It is not an error.
|
||||||
|
|
|
@ -46,6 +46,7 @@ class Instruction:
|
||||||
# Parts of the underlying instruction definition
|
# Parts of the underlying instruction definition
|
||||||
inst: parsing.InstDef
|
inst: parsing.InstDef
|
||||||
name: str
|
name: str
|
||||||
|
annotations: list[str]
|
||||||
block: parsing.Block
|
block: parsing.Block
|
||||||
block_text: list[str] # Block.text, less curlies, less PREDICT() calls
|
block_text: list[str] # Block.text, less curlies, less PREDICT() calls
|
||||||
block_line: int # First line of block in original code
|
block_line: int # First line of block in original code
|
||||||
|
@ -70,6 +71,7 @@ class Instruction:
|
||||||
def __init__(self, inst: parsing.InstDef):
|
def __init__(self, inst: parsing.InstDef):
|
||||||
self.inst = inst
|
self.inst = inst
|
||||||
self.name = inst.name
|
self.name = inst.name
|
||||||
|
self.annotations = inst.annotations
|
||||||
self.block = inst.block
|
self.block = inst.block
|
||||||
self.block_text, self.check_eval_breaker, self.block_line = extract_block_text(
|
self.block_text, self.check_eval_breaker, self.block_line = extract_block_text(
|
||||||
self.block
|
self.block
|
||||||
|
@ -118,6 +120,8 @@ def is_viable_uop(self) -> bool:
|
||||||
|
|
||||||
if self.name == "_EXIT_TRACE":
|
if self.name == "_EXIT_TRACE":
|
||||||
return True # This has 'return frame' but it's okay
|
return True # This has 'return frame' but it's okay
|
||||||
|
if self.name == "_SAVE_RETURN_OFFSET":
|
||||||
|
return True # Adjusts next_instr, but only in tier 1 code
|
||||||
if self.always_exits:
|
if self.always_exits:
|
||||||
dprint(f"Skipping {self.name} because it always exits: {self.always_exits}")
|
dprint(f"Skipping {self.name} because it always exits: {self.always_exits}")
|
||||||
return False
|
return False
|
||||||
|
|
|
@ -80,11 +80,12 @@ def choice(*opts: str) -> str:
|
||||||
|
|
||||||
# Macros
|
# Macros
|
||||||
macro = r"# *(ifdef|ifndef|undef|define|error|endif|if|else|include|#)"
|
macro = r"# *(ifdef|ifndef|undef|define|error|endif|if|else|include|#)"
|
||||||
MACRO = "MACRO"
|
CMACRO = "CMACRO"
|
||||||
|
|
||||||
id_re = r"[a-zA-Z_][0-9a-zA-Z_]*"
|
id_re = r"[a-zA-Z_][0-9a-zA-Z_]*"
|
||||||
IDENTIFIER = "IDENTIFIER"
|
IDENTIFIER = "IDENTIFIER"
|
||||||
|
|
||||||
|
|
||||||
suffix = r"([uU]?[lL]?[lL]?)"
|
suffix = r"([uU]?[lL]?[lL]?)"
|
||||||
octal = r"0[0-7]+" + suffix
|
octal = r"0[0-7]+" + suffix
|
||||||
hex = r"0[xX][0-9a-fA-F]+"
|
hex = r"0[xX][0-9a-fA-F]+"
|
||||||
|
@ -173,10 +174,6 @@ def choice(*opts: str) -> str:
|
||||||
kwds.append(INT)
|
kwds.append(INT)
|
||||||
LONG = "LONG"
|
LONG = "LONG"
|
||||||
kwds.append(LONG)
|
kwds.append(LONG)
|
||||||
OVERRIDE = "OVERRIDE"
|
|
||||||
kwds.append(OVERRIDE)
|
|
||||||
REGISTER = "REGISTER"
|
|
||||||
kwds.append(REGISTER)
|
|
||||||
OFFSETOF = "OFFSETOF"
|
OFFSETOF = "OFFSETOF"
|
||||||
kwds.append(OFFSETOF)
|
kwds.append(OFFSETOF)
|
||||||
RESTRICT = "RESTRICT"
|
RESTRICT = "RESTRICT"
|
||||||
|
@ -207,8 +204,20 @@ def choice(*opts: str) -> str:
|
||||||
kwds.append(VOLATILE)
|
kwds.append(VOLATILE)
|
||||||
WHILE = "WHILE"
|
WHILE = "WHILE"
|
||||||
kwds.append(WHILE)
|
kwds.append(WHILE)
|
||||||
|
# An instruction in the DSL
|
||||||
|
INST = "INST"
|
||||||
|
kwds.append(INST)
|
||||||
|
# A micro-op in the DSL
|
||||||
|
OP = "OP"
|
||||||
|
kwds.append(OP)
|
||||||
|
# A macro in the DSL
|
||||||
|
MACRO = "MACRO"
|
||||||
|
kwds.append(MACRO)
|
||||||
keywords = {name.lower(): name for name in kwds}
|
keywords = {name.lower(): name for name in kwds}
|
||||||
|
|
||||||
|
ANNOTATION = "ANNOTATION"
|
||||||
|
annotations = {"specializing", "guard", "override", "register"}
|
||||||
|
|
||||||
__all__ = []
|
__all__ = []
|
||||||
__all__.extend(kwds)
|
__all__.extend(kwds)
|
||||||
|
|
||||||
|
@ -270,6 +279,8 @@ def tokenize(src: str, line: int = 1, filename: str | None = None) -> Iterator[T
|
||||||
text = m.group(0)
|
text = m.group(0)
|
||||||
if text in keywords:
|
if text in keywords:
|
||||||
kind = keywords[text]
|
kind = keywords[text]
|
||||||
|
elif text in annotations:
|
||||||
|
kind = ANNOTATION
|
||||||
elif letter.match(text):
|
elif letter.match(text):
|
||||||
kind = IDENTIFIER
|
kind = IDENTIFIER
|
||||||
elif text == "...":
|
elif text == "...":
|
||||||
|
@ -289,7 +300,7 @@ def tokenize(src: str, line: int = 1, filename: str | None = None) -> Iterator[T
|
||||||
elif text[0] == "'":
|
elif text[0] == "'":
|
||||||
kind = CHARACTER
|
kind = CHARACTER
|
||||||
elif text[0] == "#":
|
elif text[0] == "#":
|
||||||
kind = MACRO
|
kind = CMACRO
|
||||||
elif text[0] == "/" and text[1] in "/*":
|
elif text[0] == "/" and text[1] in "/*":
|
||||||
kind = COMMENT
|
kind = COMMENT
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -105,8 +105,7 @@ class OpName(Node):
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class InstHeader(Node):
|
class InstHeader(Node):
|
||||||
override: bool
|
annotations : list[str]
|
||||||
register: bool
|
|
||||||
kind: Literal["inst", "op"]
|
kind: Literal["inst", "op"]
|
||||||
name: str
|
name: str
|
||||||
inputs: list[InputEffect]
|
inputs: list[InputEffect]
|
||||||
|
@ -115,8 +114,7 @@ class InstHeader(Node):
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class InstDef(Node):
|
class InstDef(Node):
|
||||||
override: bool
|
annotations : list[str]
|
||||||
register: bool
|
|
||||||
kind: Literal["inst", "op"]
|
kind: Literal["inst", "op"]
|
||||||
name: str
|
name: str
|
||||||
inputs: list[InputEffect]
|
inputs: list[InputEffect]
|
||||||
|
@ -146,14 +144,14 @@ class Pseudo(Node):
|
||||||
class Parser(PLexer):
|
class Parser(PLexer):
|
||||||
@contextual
|
@contextual
|
||||||
def definition(self) -> InstDef | Macro | Pseudo | Family | None:
|
def definition(self) -> InstDef | Macro | Pseudo | Family | None:
|
||||||
if inst := self.inst_def():
|
|
||||||
return inst
|
|
||||||
if macro := self.macro_def():
|
if macro := self.macro_def():
|
||||||
return macro
|
return macro
|
||||||
if family := self.family_def():
|
if family := self.family_def():
|
||||||
return family
|
return family
|
||||||
if pseudo := self.pseudo_def():
|
if pseudo := self.pseudo_def():
|
||||||
return pseudo
|
return pseudo
|
||||||
|
if inst := self.inst_def():
|
||||||
|
return inst
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@contextual
|
@contextual
|
||||||
|
@ -161,8 +159,7 @@ def inst_def(self) -> InstDef | None:
|
||||||
if hdr := self.inst_header():
|
if hdr := self.inst_header():
|
||||||
if block := self.block():
|
if block := self.block():
|
||||||
return InstDef(
|
return InstDef(
|
||||||
hdr.override,
|
hdr.annotations,
|
||||||
hdr.register,
|
|
||||||
hdr.kind,
|
hdr.kind,
|
||||||
hdr.name,
|
hdr.name,
|
||||||
hdr.inputs,
|
hdr.inputs,
|
||||||
|
@ -174,13 +171,15 @@ def inst_def(self) -> InstDef | None:
|
||||||
|
|
||||||
@contextual
|
@contextual
|
||||||
def inst_header(self) -> InstHeader | None:
|
def inst_header(self) -> InstHeader | None:
|
||||||
# [override] inst(NAME)
|
# annotation* inst(NAME, (inputs -- outputs))
|
||||||
# | [override] [register] inst(NAME, (inputs -- outputs))
|
# | annotation* op(NAME, (inputs -- outputs))
|
||||||
# | [override] [register] op(NAME, (inputs -- outputs))
|
annotations = []
|
||||||
# TODO: Make INST a keyword in the lexer.
|
while anno := self.expect(lx.ANNOTATION):
|
||||||
override = bool(self.expect(lx.OVERRIDE))
|
annotations.append(anno.text)
|
||||||
register = bool(self.expect(lx.REGISTER))
|
tkn = self.expect(lx.INST)
|
||||||
if (tkn := self.expect(lx.IDENTIFIER)) and tkn.text in ("inst", "op"):
|
if not tkn:
|
||||||
|
tkn = self.expect(lx.OP)
|
||||||
|
if tkn:
|
||||||
kind = cast(Literal["inst", "op"], tkn.text)
|
kind = cast(Literal["inst", "op"], tkn.text)
|
||||||
if self.expect(lx.LPAREN) and (tkn := self.expect(lx.IDENTIFIER)):
|
if self.expect(lx.LPAREN) and (tkn := self.expect(lx.IDENTIFIER)):
|
||||||
name = tkn.text
|
name = tkn.text
|
||||||
|
@ -188,7 +187,7 @@ def inst_header(self) -> InstHeader | None:
|
||||||
inp, outp = self.io_effect()
|
inp, outp = self.io_effect()
|
||||||
if self.expect(lx.RPAREN):
|
if self.expect(lx.RPAREN):
|
||||||
if (tkn := self.peek()) and tkn.kind == lx.LBRACE:
|
if (tkn := self.peek()) and tkn.kind == lx.LBRACE:
|
||||||
return InstHeader(override, register, kind, name, inp, outp)
|
return InstHeader(annotations, kind, name, inp, outp)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def io_effect(self) -> tuple[list[InputEffect], list[OutputEffect]]:
|
def io_effect(self) -> tuple[list[InputEffect], list[OutputEffect]]:
|
||||||
|
@ -312,7 +311,7 @@ def op(self) -> OpName | None:
|
||||||
|
|
||||||
@contextual
|
@contextual
|
||||||
def macro_def(self) -> Macro | None:
|
def macro_def(self) -> Macro | None:
|
||||||
if (tkn := self.expect(lx.IDENTIFIER)) and tkn.text == "macro":
|
if tkn := self.expect(lx.MACRO):
|
||||||
if self.expect(lx.LPAREN):
|
if self.expect(lx.LPAREN):
|
||||||
if tkn := self.expect(lx.IDENTIFIER):
|
if tkn := self.expect(lx.IDENTIFIER):
|
||||||
if self.expect(lx.RPAREN):
|
if self.expect(lx.RPAREN):
|
||||||
|
|
Loading…
Reference in a new issue