Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GH-111485: Allow arbitrary annotations on instructions and micro-ops. #111697

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion Include/internal/pycore_opcode_metadata.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

43 changes: 43 additions & 0 deletions Lib/test/test_generated_cases.py
Original file line number Diff line number Diff line change
Expand Up @@ -707,6 +707,49 @@ def test_override_op(self):
"""
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__":
unittest.main()
4 changes: 0 additions & 4 deletions Python/abstract_interp_cases.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

31 changes: 18 additions & 13 deletions Python/bytecodes.c
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@
#define family(name, ...) static int family_##name
#define pseudo(name) static int pseudo_##name

/* Annotations */
#define guard
#define override
#define specializing

// Dummy variables for stack effects.
static PyObject *value, *value1, *value2, *left, *right, *res, *sum, *prod, *sub;
static PyObject *container, *start, *stop, *v, *lhs, *rhs, *res2;
Expand Down Expand Up @@ -312,7 +317,7 @@ dummy_func(
TO_BOOL_STR,
};

op(_SPECIALIZE_TO_BOOL, (counter/1, value -- value)) {
specializing op(_SPECIALIZE_TO_BOOL, (counter/1, value -- value)) {
TIER_ONE_ONLY
#if ENABLE_SPECIALIZATION
if (ADAPTIVE_COUNTER_IS_ZERO(counter)) {
Expand Down Expand Up @@ -537,7 +542,7 @@ dummy_func(
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
#if ENABLE_SPECIALIZATION
if (ADAPTIVE_COUNTER_IS_ZERO(counter)) {
Expand Down Expand Up @@ -689,7 +694,7 @@ dummy_func(
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
#if ENABLE_SPECIALIZATION
if (ADAPTIVE_COUNTER_IS_ZERO(counter)) {
Expand Down Expand Up @@ -974,7 +979,7 @@ dummy_func(
SEND_GEN,
};

op(_SPECIALIZE_SEND, (counter/1, receiver, unused -- receiver, unused)) {
specializing op(_SPECIALIZE_SEND, (counter/1, receiver, unused -- receiver, unused)) {
TIER_ONE_ONLY
#if ENABLE_SPECIALIZATION
if (ADAPTIVE_COUNTER_IS_ZERO(counter)) {
Expand Down Expand Up @@ -1208,7 +1213,7 @@ dummy_func(
UNPACK_SEQUENCE_LIST,
};

op(_SPECIALIZE_UNPACK_SEQUENCE, (counter/1, seq -- seq)) {
specializing op(_SPECIALIZE_UNPACK_SEQUENCE, (counter/1, seq -- seq)) {
#if ENABLE_SPECIALIZATION
if (ADAPTIVE_COUNTER_IS_ZERO(counter)) {
next_instr = this_instr;
Expand Down Expand Up @@ -1277,7 +1282,7 @@ dummy_func(
STORE_ATTR_WITH_HINT,
};

op(_SPECIALIZE_STORE_ATTR, (counter/1, owner -- owner)) {
specializing op(_SPECIALIZE_STORE_ATTR, (counter/1, owner -- owner)) {
TIER_ONE_ONLY
#if ENABLE_SPECIALIZATION
if (ADAPTIVE_COUNTER_IS_ZERO(counter)) {
Expand Down Expand Up @@ -1404,7 +1409,7 @@ dummy_func(
LOAD_GLOBAL_BUILTIN,
};

op(_SPECIALIZE_LOAD_GLOBAL, (counter/1 -- )) {
specializing op(_SPECIALIZE_LOAD_GLOBAL, (counter/1 -- )) {
TIER_ONE_ONLY
#if ENABLE_SPECIALIZATION
if (ADAPTIVE_COUNTER_IS_ZERO(counter)) {
Expand Down Expand Up @@ -1744,7 +1749,7 @@ dummy_func(
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
#if ENABLE_SPECIALIZATION
int load_method = oparg & 1;
Expand Down Expand Up @@ -1860,7 +1865,7 @@ dummy_func(
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
#if ENABLE_SPECIALIZATION
if (ADAPTIVE_COUNTER_IS_ZERO(counter)) {
Expand Down Expand Up @@ -2182,7 +2187,7 @@ dummy_func(
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
#if ENABLE_SPECIALIZATION
if (ADAPTIVE_COUNTER_IS_ZERO(counter)) {
Expand Down Expand Up @@ -2506,7 +2511,7 @@ dummy_func(
FOR_ITER_GEN,
};

op(_SPECIALIZE_FOR_ITER, (counter/1, iter -- iter)) {
specializing op(_SPECIALIZE_FOR_ITER, (counter/1, iter -- iter)) {
TIER_ONE_ONLY
#if ENABLE_SPECIALIZATION
if (ADAPTIVE_COUNTER_IS_ZERO(counter)) {
Expand Down Expand Up @@ -3001,7 +3006,7 @@ dummy_func(
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
#if ENABLE_SPECIALIZATION
if (ADAPTIVE_COUNTER_IS_ZERO(counter)) {
Expand Down Expand Up @@ -3866,7 +3871,7 @@ dummy_func(
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
#if ENABLE_SPECIALIZATION
if (ADAPTIVE_COUNTER_IS_ZERO(counter)) {
Expand Down
18 changes: 0 additions & 18 deletions Python/executor_cases.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions Tools/cases_generator/analysis.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,17 +139,17 @@ def parse_file(self, filename: str, instrs_idx: dict[str, int]) -> None:
match thing:
case parsing.InstDef(name=name):
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)])
if name in self.instrs:
if not thing.override:
if "override" not in thing.annotations:
raise psr.make_syntax_error(
f"Duplicate definition of '{name}' @ {thing.context} "
f"previous definition @ {self.instrs[name].inst.context}",
thing_first_token,
)
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(
f"Definition of '{name}' @ {thing.context} is supposed to be "
"an override but no previous definition exists.",
Expand Down
5 changes: 4 additions & 1 deletion Tools/cases_generator/generate_cases.py
Original file line number Diff line number Diff line change
Expand Up @@ -651,7 +651,10 @@ def write_macro_expansions(
expansions: list[tuple[str, int, int]] = [] # [(name, size, offset), ...]
for part in parts:
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():
# This note just reminds us about macros that cannot
# be expanded to Tier 2 uops. It is not an error.
Expand Down
4 changes: 4 additions & 0 deletions Tools/cases_generator/instructions.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ class Instruction:
# Parts of the underlying instruction definition
inst: parsing.InstDef
name: str
annotations: list[str]
block: parsing.Block
block_text: list[str] # Block.text, less curlies, less PREDICT() calls
block_line: int # First line of block in original code
Expand All @@ -70,6 +71,7 @@ class Instruction:
def __init__(self, inst: parsing.InstDef):
self.inst = inst
self.name = inst.name
self.annotations = inst.annotations
self.block = inst.block
self.block_text, self.check_eval_breaker, self.block_line = extract_block_text(
self.block
Expand Down Expand Up @@ -118,6 +120,8 @@ def is_viable_uop(self) -> bool:

if self.name == "_EXIT_TRACE":
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:
dprint(f"Skipping {self.name} because it always exits: {self.always_exits}")
return False
Expand Down
Loading
Loading