Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit aab58a9

Browse files
authored
gh-118423: Add INSTRUCTION_SIZE macro to code generator (GH-125467)
1 parent b2eaa75 commit aab58a9

File tree

9 files changed

+115
-34
lines changed

9 files changed

+115
-34
lines changed

Lib/test/test_generated_cases.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1398,6 +1398,37 @@ def test_stack_save_only(self):
13981398
with self.assertRaises(SyntaxError):
13991399
self.run_cases_test(input, output)
14001400

1401+
def test_instruction_size_macro(self):
1402+
input = """
1403+
inst(OP, (--)) {
1404+
frame->return_offset = INSTRUCTION_SIZE;
1405+
}
1406+
"""
1407+
1408+
output = """
1409+
TARGET(OP) {
1410+
frame->instr_ptr = next_instr;
1411+
next_instr += 1;
1412+
INSTRUCTION_STATS(OP);
1413+
frame->return_offset = 1 ;
1414+
DISPATCH();
1415+
}
1416+
"""
1417+
self.run_cases_test(input, output)
1418+
1419+
# Two instructions of different sizes referencing the same
1420+
# uop containing the `INSTRUCTION_SIZE` macro is not allowed.
1421+
input = """
1422+
inst(OP, (--)) {
1423+
frame->return_offset = INSTRUCTION_SIZE;
1424+
}
1425+
macro(OP2) = unused/1 + OP;
1426+
"""
1427+
1428+
output = "" # No output needed as this should raise an error.
1429+
with self.assertRaisesRegex(SyntaxError, "All instructions containing a uop"):
1430+
self.run_cases_test(input, output)
1431+
14011432

14021433
class TestGeneratedAbstractCases(unittest.TestCase):
14031434
def setUp(self) -> None:
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Add a new ``INSTRUCTION_SIZE`` macro to the cases generator which returns
2+
the current instruction size.

Python/bytecodes.c

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -859,7 +859,7 @@ dummy_func(
859859
new_frame->localsplus[0] = container;
860860
new_frame->localsplus[1] = sub;
861861
INPUTS_DEAD();
862-
frame->return_offset = (uint16_t)(1 + INLINE_CACHE_ENTRIES_BINARY_SUBSCR);
862+
frame->return_offset = INSTRUCTION_SIZE;
863863
}
864864

865865
macro(BINARY_SUBSCR_GETITEM) =
@@ -1111,8 +1111,8 @@ dummy_func(
11111111
gen->gi_frame_state = FRAME_EXECUTING;
11121112
gen->gi_exc_state.previous_item = tstate->exc_info;
11131113
tstate->exc_info = &gen->gi_exc_state;
1114-
assert(next_instr - this_instr + oparg <= UINT16_MAX);
1115-
frame->return_offset = (uint16_t)(next_instr - this_instr + oparg);
1114+
assert(INSTRUCTION_SIZE + oparg <= UINT16_MAX);
1115+
frame->return_offset = (uint16_t)(INSTRUCTION_SIZE + oparg);
11161116
assert(gen_frame->previous == NULL);
11171117
gen_frame->previous = frame;
11181118
DISPATCH_INLINED(gen_frame);
@@ -1157,8 +1157,8 @@ dummy_func(
11571157
gen->gi_frame_state = FRAME_EXECUTING;
11581158
gen->gi_exc_state.previous_item = tstate->exc_info;
11591159
tstate->exc_info = &gen->gi_exc_state;
1160-
assert(1 + INLINE_CACHE_ENTRIES_SEND + oparg <= UINT16_MAX);
1161-
frame->return_offset = (uint16_t)(1 + INLINE_CACHE_ENTRIES_SEND + oparg);
1160+
assert(INSTRUCTION_SIZE + oparg <= UINT16_MAX);
1161+
frame->return_offset = (uint16_t)(INSTRUCTION_SIZE + oparg);
11621162
gen_frame->previous = frame;
11631163
}
11641164

@@ -2265,7 +2265,7 @@ dummy_func(
22652265
new_frame->localsplus[0] = owner;
22662266
DEAD(owner);
22672267
new_frame->localsplus[1] = PyStackRef_FromPyObjectNew(name);
2268-
frame->return_offset = (uint16_t)(next_instr - this_instr);
2268+
frame->return_offset = INSTRUCTION_SIZE;
22692269
DISPATCH_INLINED(new_frame);
22702270
}
22712271

@@ -3062,7 +3062,7 @@ dummy_func(
30623062
tstate->exc_info = &gen->gi_exc_state;
30633063
gen_frame->previous = frame;
30643064
// oparg is the return offset from the next instruction.
3065-
frame->return_offset = (uint16_t)(1 + INLINE_CACHE_ENTRIES_FOR_ITER + oparg);
3065+
frame->return_offset = (uint16_t)(INSTRUCTION_SIZE + oparg);
30663066
}
30673067

30683068
macro(FOR_ITER_GEN) =
@@ -3341,7 +3341,7 @@ dummy_func(
33413341
if (new_frame == NULL) {
33423342
ERROR_NO_POP();
33433343
}
3344-
frame->return_offset = (uint16_t)(next_instr - this_instr);
3344+
frame->return_offset = INSTRUCTION_SIZE;
33453345
DISPATCH_INLINED(new_frame);
33463346
}
33473347
/* Callable is not a normal Python function */
@@ -4205,8 +4205,8 @@ dummy_func(
42054205
if (new_frame == NULL) {
42064206
ERROR_NO_POP();
42074207
}
4208-
assert(next_instr - this_instr == 1 + INLINE_CACHE_ENTRIES_CALL_KW);
4209-
frame->return_offset = 1 + INLINE_CACHE_ENTRIES_CALL_KW;
4208+
assert(INSTRUCTION_SIZE == 1 + INLINE_CACHE_ENTRIES_CALL_KW);
4209+
frame->return_offset = INSTRUCTION_SIZE;
42104210
DISPATCH_INLINED(new_frame);
42114211
}
42124212
/* Callable is not a normal Python function */
@@ -4472,7 +4472,7 @@ dummy_func(
44724472
if (new_frame == NULL) {
44734473
ERROR_NO_POP();
44744474
}
4475-
assert(next_instr - this_instr == 1);
4475+
assert(INSTRUCTION_SIZE == 1);
44764476
frame->return_offset = 1;
44774477
DISPATCH_INLINED(new_frame);
44784478
}

Python/executor_cases.c.h

Lines changed: 5 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Python/generated_cases.c.h

Lines changed: 12 additions & 12 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Tools/cases_generator/analyzer.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,8 @@ class Uop:
173173
implicitly_created: bool = False
174174
replicated = 0
175175
replicates: "Uop | None" = None
176+
# Size of the instruction(s), only set for uops containing the INSTRUCTION_SIZE macro
177+
instruction_size: int | None = None
176178

177179
def dump(self, indent: str) -> None:
178180
print(
@@ -1079,6 +1081,35 @@ def add_instruction(name: str) -> None:
10791081
return instmap, len(no_arg), min_instrumented
10801082

10811083

1084+
def get_instruction_size_for_uop(instructions: dict[str, Instruction], uop: Uop) -> int | None:
1085+
"""Return the size of the instruction that contains the given uop or
1086+
`None` if the uop does not contains the `INSTRUCTION_SIZE` macro.
1087+
1088+
If there is more than one instruction that contains the uop,
1089+
ensure that they all have the same size.
1090+
"""
1091+
for tkn in uop.body:
1092+
if tkn.text == "INSTRUCTION_SIZE":
1093+
break
1094+
else:
1095+
return None
1096+
1097+
size = None
1098+
for inst in instructions.values():
1099+
if uop in inst.parts:
1100+
if size is None:
1101+
size = inst.size
1102+
if size != inst.size:
1103+
raise analysis_error(
1104+
"All instructions containing a uop with the `INSTRUCTION_SIZE` macro "
1105+
f"must have the same size: {size} != {inst.size}",
1106+
tkn
1107+
)
1108+
if size is None:
1109+
raise analysis_error(f"No instruction containing the uop '{uop.name}' was found", tkn)
1110+
return size
1111+
1112+
10821113
def analyze_forest(forest: list[parser.AstNode]) -> Analysis:
10831114
instructions: dict[str, Instruction] = {}
10841115
uops: dict[str, Uop] = {}
@@ -1122,6 +1153,8 @@ def analyze_forest(forest: list[parser.AstNode]) -> Analysis:
11221153
continue
11231154
if target.text in instructions:
11241155
instructions[target.text].is_target = True
1156+
for uop in uops.values():
1157+
uop.instruction_size = get_instruction_size_for_uop(instructions, uop)
11251158
# Special case BINARY_OP_INPLACE_ADD_UNICODE
11261159
# BINARY_OP_INPLACE_ADD_UNICODE is not a normal family member,
11271160
# as it is the wrong size, but we need it to maintain an

Tools/cases_generator/generators_common.py

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@
99
analysis_error,
1010
)
1111
from cwriter import CWriter
12-
from typing import Callable, Mapping, TextIO, Iterator, Iterable
12+
from typing import Callable, TextIO, Iterator, Iterable
1313
from lexer import Token
14-
from stack import Stack, Local, Storage, StackError
14+
from stack import Storage, StackError
1515

1616
# Set this to true for voluminous output showing state of stack and locals
1717
PRINT_STACKS = False
@@ -118,7 +118,8 @@ def __init__(self, out: CWriter):
118118
"PyStackRef_CLOSE": self.stackref_close,
119119
"PyStackRef_CLOSE_SPECIALIZED": self.stackref_close,
120120
"PyStackRef_AsPyObjectSteal": self.stackref_steal,
121-
"DISPATCH": self.dispatch
121+
"DISPATCH": self.dispatch,
122+
"INSTRUCTION_SIZE": self.instruction_size,
122123
}
123124
self.out = out
124125

@@ -365,6 +366,19 @@ def reload_stack(
365366
self.emit_reload(storage)
366367
return True
367368

369+
def instruction_size(self,
370+
tkn: Token,
371+
tkn_iter: TokenIterator,
372+
uop: Uop,
373+
storage: Storage,
374+
inst: Instruction | None,
375+
) -> bool:
376+
"""Replace the INSTRUCTION_SIZE macro with the size of the current instruction."""
377+
if uop.instruction_size is None:
378+
raise analysis_error("The INSTRUCTION_SIZE macro requires uop.instruction_size to be set", tkn)
379+
self.out.emit(f" {uop.instruction_size} ")
380+
return True
381+
368382
def _print_storage(self, storage: Storage) -> None:
369383
if PRINT_STACKS:
370384
self.out.start_line()

Tools/cases_generator/interpreter_definition.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -178,15 +178,17 @@ list of annotations and their meanings are as follows:
178178

179179
### Special functions/macros
180180

181-
The C code may include special functions that are understood by the tools as
181+
The C code may include special functions and macros that are understood by the tools as
182182
part of the DSL.
183183

184-
Those functions include:
184+
Those include:
185185

186186
* `DEOPT_IF(cond, instruction)`. Deoptimize if `cond` is met.
187187
* `ERROR_IF(cond, label)`. Jump to error handler at `label` if `cond` is true.
188188
* `DECREF_INPUTS()`. Generate `Py_DECREF()` calls for the input stack effects.
189189
* `SYNC_SP()`. Synchronizes the physical stack pointer with the stack effects.
190+
* `INSTRUCTION_SIZE`. Replaced with the size of the instruction which is equal
191+
to `1 + INLINE_CACHE_ENTRIES`.
190192

191193
Note that the use of `DECREF_INPUTS()` is optional -- manual calls
192194
to `Py_DECREF()` or other approaches are also acceptable

Tools/cases_generator/tier1_generator.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
write_header,
2323
type_and_null,
2424
Emitter,
25-
TokenIterator,
2625
)
2726
from cwriter import CWriter
2827
from typing import TextIO

0 commit comments

Comments
 (0)