Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
ec8c701
Add INSTRUCTION_SIZE macro
tomasr8 Oct 14, 2024
09c2f59
Use INSTRUCTION_SIZE in bytecodes.c
tomasr8 Oct 14, 2024
287eb92
Add tests
tomasr8 Oct 14, 2024
c76fce4
Update docs
tomasr8 Oct 14, 2024
e3f3b07
Add news entry
tomasr8 Oct 14, 2024
ed5b375
Fix mypy error
tomasr8 Oct 14, 2024
870341d
Treat INSTRUCTION_SIZE as an identifier
tomasr8 Oct 14, 2024
421ebda
Regenerate executor cases
tomasr8 Oct 14, 2024
5154f7f
Re-add newline
tomasr8 Oct 14, 2024
e7e83dc
Use replaceText()
tomasr8 Oct 14, 2024
101a5fd
Emit size directly
tomasr8 Oct 15, 2024
1338628
Merge branch 'main' into instr-size
tomasr8 Oct 15, 2024
ee461d5
Pass instruction in tier2
tomasr8 Oct 16, 2024
7135103
Fix INSTRUCTION_SIZE in tier2
tomasr8 Oct 16, 2024
715ce31
Make mypy happy
tomasr8 Oct 16, 2024
8235321
Fix tests
tomasr8 Oct 16, 2024
148070e
Remove unused imports
tomasr8 Oct 17, 2024
3e6b592
Ensure all instructions have the same size
tomasr8 Oct 17, 2024
6e78ec0
Add tests
tomasr8 Oct 17, 2024
75e6f6c
Regen files
tomasr8 Oct 17, 2024
a3c0d10
Make mypy happy
tomasr8 Oct 17, 2024
7235d2d
Fix tests
tomasr8 Oct 20, 2024
5cfb423
Raise instead of assert when size is None
tomasr8 Oct 21, 2024
2190784
Add an `instruction_size` field to the Uop class
tomasr8 Oct 21, 2024
33343dd
Remove extra white space
tomasr8 Oct 21, 2024
f9ecb16
Raise analysis_error instead of ValueError
tomasr8 Oct 21, 2024
abfaaff
Fix wording
tomasr8 Oct 21, 2024
3f3ad10
Make mypy happy
tomasr8 Oct 21, 2024
a8f72ef
Simplify test
tomasr8 Oct 28, 2024
8138a06
Replace assert with raise
tomasr8 Oct 28, 2024
772f803
Simplify code
tomasr8 Oct 28, 2024
a718cfb
Remove some code repetition
tomasr8 Oct 28, 2024
a10d4ee
Simplify INSTRUCTION_SIZE check
tomasr8 Oct 29, 2024
3ae7a5d
Simplify code
tomasr8 Oct 29, 2024
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
18 changes: 18 additions & 0 deletions Lib/test/test_generated_cases.py
Original file line number Diff line number Diff line change
Expand Up @@ -1371,6 +1371,24 @@ def test_stack_save_only(self):
with self.assertRaises(SyntaxError):
self.run_cases_test(input, output)

def test_instruction_size_macro(self):
input = """
inst(OP, (--)) {
frame->return_offset = INSTRUCTION_SIZE;
}
"""

output = """
TARGET(OP) {
frame->instr_ptr = next_instr;
next_instr += 1;
INSTRUCTION_STATS(OP);
frame->return_offset = 1;
DISPATCH();
}
"""
self.run_cases_test(input, output)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a test for another instruction that is a different length, but uses OP:

macro(OP2) = unused/1 + OP;

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That test should fail, as the INSTRUCTION_SIZE is inconsistent.
The tier 2 INSTRUCTION_SIZE cannot be both 1 and 2.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, sorry I mistakenly thought it should only fail for tier 2 hence I was only checking the instruction in the tier 2 generator. I extended the check to tier 1 as well and the test now fails as expected.


class TestGeneratedAbstractCases(unittest.TestCase):
def setUp(self) -> None:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Add a new ``INSTRUCTION_SIZE`` macro to the cases generator which returns
the current instruction size.
24 changes: 12 additions & 12 deletions Python/bytecodes.c
Original file line number Diff line number Diff line change
Expand Up @@ -841,7 +841,7 @@ dummy_func(
new_frame->localsplus[0] = container;
new_frame->localsplus[1] = sub;
INPUTS_DEAD();
frame->return_offset = (uint16_t)(1 + INLINE_CACHE_ENTRIES_BINARY_SUBSCR);
frame->return_offset = INSTRUCTION_SIZE;
}

macro(BINARY_SUBSCR_GETITEM) =
Expand Down Expand Up @@ -1101,8 +1101,8 @@ dummy_func(
gen->gi_frame_state = FRAME_EXECUTING;
gen->gi_exc_state.previous_item = tstate->exc_info;
tstate->exc_info = &gen->gi_exc_state;
assert(next_instr - this_instr + oparg <= UINT16_MAX);
frame->return_offset = (uint16_t)(next_instr - this_instr + oparg);
assert(INSTRUCTION_SIZE + oparg <= UINT16_MAX);
frame->return_offset = (uint16_t)(INSTRUCTION_SIZE + oparg);
assert(gen_frame->previous == NULL);
gen_frame->previous = frame;
DISPATCH_INLINED(gen_frame);
Expand Down Expand Up @@ -1147,8 +1147,8 @@ dummy_func(
gen->gi_frame_state = FRAME_EXECUTING;
gen->gi_exc_state.previous_item = tstate->exc_info;
tstate->exc_info = &gen->gi_exc_state;
assert(1 + INLINE_CACHE_ENTRIES_SEND + oparg <= UINT16_MAX);
frame->return_offset = (uint16_t)(1 + INLINE_CACHE_ENTRIES_SEND + oparg);
assert(INSTRUCTION_SIZE + oparg <= UINT16_MAX);
frame->return_offset = (uint16_t)(INSTRUCTION_SIZE + oparg);
gen_frame->previous = frame;
}

Expand Down Expand Up @@ -2255,7 +2255,7 @@ dummy_func(
new_frame->localsplus[0] = owner;
DEAD(owner);
new_frame->localsplus[1] = PyStackRef_FromPyObjectNew(name);
frame->return_offset = (uint16_t)(next_instr - this_instr);
frame->return_offset = INSTRUCTION_SIZE;
DISPATCH_INLINED(new_frame);
}

Expand Down Expand Up @@ -3053,7 +3053,7 @@ dummy_func(
tstate->exc_info = &gen->gi_exc_state;
gen_frame->previous = frame;
// oparg is the return offset from the next instruction.
frame->return_offset = (uint16_t)(1 + INLINE_CACHE_ENTRIES_FOR_ITER + oparg);
frame->return_offset = (uint16_t)(INSTRUCTION_SIZE + oparg);
}

macro(FOR_ITER_GEN) =
Expand Down Expand Up @@ -3332,7 +3332,7 @@ dummy_func(
if (new_frame == NULL) {
ERROR_NO_POP();
}
frame->return_offset = (uint16_t)(next_instr - this_instr);
frame->return_offset = INSTRUCTION_SIZE;
DISPATCH_INLINED(new_frame);
}
/* Callable is not a normal Python function */
Expand Down Expand Up @@ -4194,8 +4194,8 @@ dummy_func(
if (new_frame == NULL) {
ERROR_NO_POP();
}
assert(next_instr - this_instr == 1 + INLINE_CACHE_ENTRIES_CALL_KW);
frame->return_offset = 1 + INLINE_CACHE_ENTRIES_CALL_KW;
assert(INSTRUCTION_SIZE == 1 + INLINE_CACHE_ENTRIES_CALL_KW);
frame->return_offset = INSTRUCTION_SIZE;
DISPATCH_INLINED(new_frame);
}
/* Callable is not a normal Python function */
Expand Down Expand Up @@ -4461,7 +4461,7 @@ dummy_func(
if (new_frame == NULL) {
ERROR_NO_POP();
}
assert(next_instr - this_instr == 1);
assert(INSTRUCTION_SIZE == 1);
frame->return_offset = 1;
DISPATCH_INLINED(new_frame);
}
Expand Down Expand Up @@ -4794,7 +4794,7 @@ dummy_func(

op(_SAVE_RETURN_OFFSET, (--)) {
#if TIER_ONE
frame->return_offset = (uint16_t)(next_instr - this_instr);
frame->return_offset = INSTRUCTION_SIZE;
#endif
#if TIER_TWO
frame->return_offset = oparg;
Expand Down
38 changes: 19 additions & 19 deletions Python/generated_cases.c.h

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

8 changes: 8 additions & 0 deletions Tools/cases_generator/generators_common.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import dataclasses
from pathlib import Path
from typing import TextIO

Expand Down Expand Up @@ -490,12 +491,19 @@ def _emit_block(
if reachable:
reachable = if_reachable
self.out.emit(rbrace)
elif tkn.kind == "INSTRUCTION_SIZE":
self._emit_instruction_size(tkn, inst)
else:
self.out.emit(tkn)
except StackError as ex:
raise analysis_error(ex.args[0], tkn) from None
raise analysis_error("Expecting closing brace. Reached end of file", tkn)

def _emit_instruction_size(self, tkn: Token, inst: Instruction | None) -> None:
"""Replace the INSTRUCTION_SIZE macro with the size of the current instruction."""
assert inst is not None, "INSTRUCTION_SIZE requires instruction to be passed"
params = dataclasses.asdict(tkn) | {"text": str(inst.size)}
self.out.emit(Token(**params))

def emit_tokens(
self,
Expand Down
6 changes: 4 additions & 2 deletions Tools/cases_generator/interpreter_definition.md
Original file line number Diff line number Diff line change
Expand Up @@ -178,15 +178,17 @@ list of annotations and their meanings are as follows:

### Special functions/macros

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

Those functions include:
Those include:

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

Note that the use of `DECREF_INPUTS()` is optional -- manual calls
to `Py_DECREF()` or other approaches are also acceptable
Expand Down
5 changes: 5 additions & 0 deletions Tools/cases_generator/lexer.py
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,9 @@ def choice(*opts: str) -> str:
"tier2",
}

# An instruction size in the DSL
INSTRUCTION_SIZE = "INSTRUCTION_SIZE"

__all__ = []
__all__.extend(kwds)

Expand Down Expand Up @@ -292,6 +295,8 @@ def tokenize(src: str, line: int = 1, filename: str = "") -> Iterator[Token]:
kind = keywords[text]
elif text in annotations:
kind = ANNOTATION
elif text == INSTRUCTION_SIZE:
kind = INSTRUCTION_SIZE
elif letter.match(text):
kind = IDENTIFIER
elif text == "...":
Expand Down
Loading