8000 gh-119689: generate stack effect metadata for pseudo instructions by iritkatriel · Pull Request #119691 · python/cpython · GitHub
[go: up one dir, main page]

Skip to content

gh-119689: generate stack effect metadata for pseudo instructions #119691

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

Merged
merged 5 commits into from
May 29, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Next Next commit
gh-119689: generate stack effect metadata for pseudo instructions
  • Loading branch information
iritkatriel committed May 28, 2024
commit 1b59d1dd2dc4aa62ed384dec57226a3b1a739210
32 changes: 32 additions & 0 deletions Include/internal/pycore_opcode_metadata.h

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

4 changes: 2 additions & 2 deletions Lib/test/test_generated_cases.py
Original file line number Diff line number Diff line change
Expand Up @@ -485,7 +485,7 @@ def test_unused_caches(self):

def test_pseudo_instruction_no_flags(self):
input = """
pseudo(OP) = {
pseudo(OP, (in -- out1, out2)) = {
OP1,
};

Expand All @@ -504,7 +504,7 @@ def test_pseudo_instruction_no_flags(self):

def test_pseudo_instruction_with_flags(self):
input = """
pseudo(OP, (HAS_ARG, HAS_JUMP)) = {
pseudo(OP, (in1, in2 --), (HAS_ARG, HAS_JUMP)) = {
OP1,
};

Expand Down
24 changes: 16 additions & 8 deletions Python/bytecodes.c
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ dummy_func(
}
}

pseudo(LOAD_CLOSURE) = {
pseudo(LOAD_CLOSURE, (-- unused)) = {
LOAD_FAST,
};

Expand Down Expand Up @@ -259,7 +259,7 @@ dummy_func(
SETLOCAL(oparg, value);
}

pseudo(STORE_FAST_MAYBE_NULL) = {
pseudo(STORE_FAST_MAYBE_NULL, (unused --)) = {
STORE_FAST,
};

Expand Down Expand Up @@ -2391,12 +2391,12 @@ dummy_func(
#endif /* _Py_TIER2 */
}

pseudo(JUMP) = {
pseudo(JUMP, (--)) = {
JUMP_FORWARD,
JUMP_BACKWARD,
};

pseudo(JUMP_NO_INTERRUPT) = {
pseudo(JUMP_NO_INTERRUPT, (--)) = {
JUMP_FORWARD,
JUMP_BACKWARD_NO_INTERRUPT,
};
Expand Down Expand Up @@ -2893,19 +2893,27 @@ dummy_func(
ERROR_IF(res == NULL, error);
}

pseudo(SETUP_FINALLY, (HAS_ARG)) = {
pseudo(SETUP_FINALLY, (-- unused), (HAS_ARG)) = {
/* If an exception is raised, restore the stack position
* and push one value before jumping to the handler.
*/
NOP,
};

pseudo(SETUP_CLEANUP, (HAS_ARG)) = {
pseudo(SETUP_CLEANUP, (-- unused, unused), (HAS_ARG)) = {
/* As SETUP_FINALLY, but push lasti as well */
NOP,
};

pseudo(SETUP_WITH, (HAS_ARG)) = {
pseudo(SETUP_WITH, (-- unused), (HAS_ARG)) = {
/* If an exception is raised, restore the stack position to the
* position before the result of __(a)enter__ and push 2 values
* before jumping to the handler.
*/
NOP,
};

pseudo(POP_BLOCK) = {
pseudo(POP_BLOCK, (--)) = {
NOP,
};

Expand Down
57 changes: 14 additions & 43 deletions Python/compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -703,51 +703,22 @@ compiler_set_qualname(struct compiler *c)
static int
stack_effect(int opcode, int oparg, int jump)
Copy link
Member

Choose a reason for hiding this comment

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

Is jump still used? The deleted SETUP cases use it, but I don't think the generated code uses it. Is it no longer needed? Could you remove the jump argument then? (All the callers are inside this file.)

Copy link
Member Author

Choose a reason for hiding this comment

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

jump is exposed in the c api and used from flowgraph.c.

Copy link
Member

Choose a reason for hiding this comment

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

I see, the key is IS_BLOCK_PUSH_OPCODE(opcode). Sorry I didn't get that at first.

{
if (0 <= opcode && opcode <= MAX_REAL_OPCODE) {
if (_PyOpcode_Deopt[opcode] != opcode) {
// Specialized instructions are not supported.
return PY_INVALID_STACK_EFFECT;
}
int popped = _PyOpcode_num_popped(opcode, oparg);
int pushed = _PyOpcode_num_pushed(opcode, oparg);
if (popped < 0 || pushed < 0) {
return PY_INVALID_STACK_EFFECT;
}
return pushed - popped;
if (opcode < 0) {
return PY_INVALID_STACK_EFFECT;
}

// Pseudo ops
switch (opcode) {
case POP_BLOCK:
case JUMP:
case JUMP_NO_INTERRUPT:
return 0;

/* Exception handling pseudo-instructions */
case SETUP_FINALLY:
/* 0 in the normal flow.
* Restore the stack position and push 1 value before jumping to
* the handler if an exception be raised. */
return jump ? 1 : 0;
case SETUP_CLEANUP:
/* As SETUP_FINALLY, but pushes lasti as well */
return jump ? 2 : 0;
case SETUP_WITH:
/* 0 in the normal flow.
* Restore the stack position to the position before the result
* of __(a)enter__ and push 2 values before jumping to the handler
* if an exception be raised. */
return jump ? 1 : 0;

case STORE_FAST_MAYBE_NULL:
return -1;
case LOAD_CLOSURE:
return 1;
default:
return PY_INVALID_STACK_EFFECT;
if ((opcode <= MAX_REAL_OPCODE) && (_PyOpcode_Deopt[opcode] != opcode)) {
// Specialized instructions are not supported.
return PY_INVALID_STACK_EFFECT;
}

return PY_INVALID_STACK_EFFECT; /* not reachable */
int popped = _PyOpcode_num_popped(opcode, oparg);
int pushed = _PyOpcode_num_pushed(opcode, oparg);
if (popped < 0 || pushed < 0) {
return PY_INVALID_STACK_EFFECT;
}
if (IS_BLOCK_PUSH_OPCODE(opcode) && !jump) {
return 0;
}
return pushed - popped;
}

int
Expand Down
4 changes: 3 additions & 1 deletion Tools/cases_generator/analyzer.py
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,7 @@ def is_super(self) -> bool:
@dataclass
class PseudoInstruction:
name: str
stack: StackEffect
targets: list[Instruction]
flags: list[str]
opcode: int = -1
Expand Down Expand Up @@ -295,7 +296,7 @@ def convert_stack_item(item: parser.StackEffect, replace_op_arg_1: str | None) -
item.name, item.type, cond, (item.size or "1")
)

def analyze_stack(op: parser.InstDef, replace_op_arg_1: str | None = None) -> StackEffect:
def analyze_stack(op: parser.InstDef | parser.Pseudo, replace_op_arg_1: str | None = None) -> StackEffect:
inputs: list[StackItem] = [
convert_stack_item(i, replace_op_arg_1) for i in op.inputs if isinstance(i, parser.StackEffect)
]
Expand Down Expand Up @@ -706,6 +707,7 @@ def add_pseudo(
) -> None:
pseudos[pseudo.name] = PseudoInstruction(
pseudo.name,
analyze_stack(pseudo),
[instructions[target] for target in pseudo.targets],
pseudo.flags,
)
Expand Down
8 changes: 7 additions & 1 deletion Tools/cases_generator/interpreter_definition.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,13 @@ and a piece of C code describing its semantics::
"family" "(" NAME ")" = "{" NAME ("," NAME)+ [","] "}" ";"

pseudo:
"pseudo" "(" NAME ")" = "{" NAME ("," NAME)+ [","] "}" ";"
"pseudo" "(" NAME "," stack_effect ["," "(" flags ")"]")" = "{" NAME ("," NAME)+ [","] "}" ";"

flags:
flag ("|" flag)*

flag:
HAS_ARG | HAS_DEOPT | etc..
```

The following definitions may occur:
Expand Down
9 changes: 8 additions & 1 deletion Tools/cases_generator/opcode_metadata_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from analyzer import (
Analysis,
Instruction,
PseudoInstruction,
analyze_files,
Skip,
Uop,
Expand Down Expand Up @@ -94,12 +95,18 @@ def emit_stack_effect_function(
def generate_stack_effect_functions(analysis: Analysis, out: CWriter) -> None:
popped_data: list[tuple[str, str]] = []
pushed_data: list[tuple[str, str]] = []
for inst in analysis.instructions.values():
def add(inst: Instruction | PseudoInstruction):
stack = get_stack_effect(inst)
popped = (-stack.base_offset).to_c()
pushed = (stack.top_offset - stack.base_offset).to_c()
popped_data.append((inst.name, popped))
pushed_data.append((inst.name, pushed))

for inst in analysis.instructions.values():
add(inst)
for pseudo in analysis.pseudos.values():
add(pseudo)

emit_stack_effect_function(out, "popped", sorted(popped_data))
emit_stack_effect_function(out, "pushed", sorted(pushed_data))

Expand Down
24 changes: 14 additions & 10 deletions Tools/cases_generator/parsing.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,8 @@ class Family(Node):
@dataclass
class Pseudo(Node):
name: str
inputs: list[InputEffect]
outputs: list[OutputEffect]
flags: list[str] # instr flags to set on the pseudo instruction
targets: list[str] # opcodes this can be replaced by

Expand Down Expand Up @@ -409,16 +411,18 @@ def pseudo_def(self) -> Pseudo | None:
if self.expect(lx.LPAREN):
if tkn := self.expect(lx.IDENTIFIER):
if self.expect(lx.COMMA):
flags = self.flags()
else:
flags = []
if self.expect(lx.RPAREN):
if self.expect(lx.EQUALS):
if not self.expect(lx.LBRACE):
raise self.make_syntax_error("Expected {")
if members := self.members():
if self.expect(lx.RBRACE) and self.expect(lx.SEMI):
return Pseudo(tkn.text, flags, members)
inp, outp = self.io_effect()
if self.expect(lx.COMMA):
flags = self.flags()
else:
flags = []
if self.expect(lx.RPAREN):
if self.expect(lx.EQUALS):
if not self.expect(lx.LBRACE):
raise self.make_syntax_error("Expected {")
if members := self.members():
if self.expect(lx.RBRACE) and self.expect(lx.SEMI):
return Pseudo(tkn.text, inp, outp, flags, members)
return None

def members(self) -> list[str] | None:
Expand Down
Loading
0