Skip to content

Commit ac10947

Browse files
authored
GH-112354: _GUARD_IS_TRUE_POP side-exits to target the next instruction, not themselves. (GH-114078)
1 parent 2010d45 commit ac10947

File tree

8 files changed

+40
-26
lines changed

8 files changed

+40
-26
lines changed

Include/internal/pycore_uop_metadata.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = {
169169
[_CHECK_FUNCTION_EXACT_ARGS] = HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_PASSTHROUGH_FLAG,
170170
[_CHECK_STACK_SPACE] = HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_PASSTHROUGH_FLAG,
171171
[_INIT_CALL_PY_EXACT_ARGS] = HAS_ARG_FLAG | HAS_ESCAPES_FLAG | HAS_PURE_FLAG,
172-
[_PUSH_FRAME] = 0,
172+
[_PUSH_FRAME] = HAS_ESCAPES_FLAG,
173173
[_CALL_TYPE_1] = HAS_ARG_FLAG | HAS_DEOPT_FLAG,
174174
[_CALL_STR_1] = HAS_ARG_FLAG | HAS_EVAL_BREAK_FLAG | HAS_DEOPT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG,
175175
[_CALL_TUPLE_1] = HAS_ARG_FLAG | HAS_EVAL_BREAK_FLAG | HAS_DEOPT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG,

Python/bytecodes.c

+14-5
Original file line numberDiff line numberDiff line change
@@ -805,7 +805,8 @@ dummy_func(
805805
#if TIER_ONE
806806
assert(frame != &entry_frame);
807807
#endif
808-
STORE_SP();
808+
SYNC_SP();
809+
_PyFrame_SetStackPointer(frame, stack_pointer);
809810
assert(EMPTY());
810811
_Py_LeaveRecursiveCallPy(tstate);
811812
// GH-99729: We need to unlink the frame *before* clearing it:
@@ -3154,7 +3155,8 @@ dummy_func(
31543155
// Write it out explicitly because it's subtly different.
31553156
// Eventually this should be the only occurrence of this code.
31563157
assert(tstate->interp->eval_frame == NULL);
3157-
STORE_SP();
3158+
SYNC_SP();
3159+
_PyFrame_SetStackPointer(frame, stack_pointer);
31583160
new_frame->previous = frame;
31593161
CALL_STAT_INC(inlined_py_calls);
31603162
frame = tstate->current_frame = new_frame;
@@ -4013,20 +4015,27 @@ dummy_func(
40134015
///////// Tier-2 only opcodes /////////
40144016

40154017
op (_GUARD_IS_TRUE_POP, (flag -- )) {
4016-
DEOPT_IF(Py_IsFalse(flag));
4018+
SYNC_SP();
4019+
DEOPT_IF(!Py_IsTrue(flag));
40174020
assert(Py_IsTrue(flag));
40184021
}
40194022

40204023
op (_GUARD_IS_FALSE_POP, (flag -- )) {
4021-
DEOPT_IF(Py_IsTrue(flag));
4024+
SYNC_SP();
4025+
DEOPT_IF(!Py_IsFalse(flag));
40224026
assert(Py_IsFalse(flag));
40234027
}
40244028

40254029
op (_GUARD_IS_NONE_POP, (val -- )) {
4026-
DEOPT_IF(!Py_IsNone(val));
4030+
SYNC_SP();
4031+
if (!Py_IsNone(val)) {
4032+
Py_DECREF(val);
4033+
DEOPT_IF(1);
4034+
}
40274035
}
40284036

40294037
op (_GUARD_IS_NOT_NONE_POP, (val -- )) {
4038+
SYNC_SP();
40304039
DEOPT_IF(Py_IsNone(val));
40314040
Py_DECREF(val);
40324041
}

Python/executor_cases.c.h

+9-6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Python/optimizer.c

+4-3
Original file line numberDiff line numberDiff line change
@@ -481,18 +481,19 @@ translate_bytecode_to_trace(
481481
goto done;
482482
}
483483
uint32_t uopcode = BRANCH_TO_GUARD[opcode - POP_JUMP_IF_FALSE][jump_likely];
484-
_Py_CODEUNIT *next_instr = instr + 1 + _PyOpcode_Caches[_PyOpcode_Deopt[opcode]];
485484
DPRINTF(2, "%s(%d): counter=%x, bitcount=%d, likely=%d, confidence=%d, uopcode=%s\n",
486485
_PyOpcode_OpName[opcode], oparg,
487486
counter, bitcount, jump_likely, confidence, _PyUOpName(uopcode));
488-
ADD_TO_TRACE(uopcode, max_length, 0, target);
487+
_Py_CODEUNIT *next_instr = instr + 1 + _PyOpcode_Caches[_PyOpcode_Deopt[opcode]];
488+
_Py_CODEUNIT *target_instr = next_instr + oparg;
489489
if (jump_likely) {
490-
_Py_CODEUNIT *target_instr = next_instr + oparg;
491490
DPRINTF(2, "Jump likely (%x = %d bits), continue at byte offset %d\n",
492491
instr[1].cache, bitcount, 2 * INSTR_IP(target_instr, code));
493492
instr = target_instr;
493+
ADD_TO_TRACE(uopcode, max_length, 0, INSTR_IP(next_instr, code));
494494
goto top;
495495
}
496+
ADD_TO_TRACE(uopcode, max_length, 0, INSTR_IP(target_instr, code));
496497
break;
497498
}
498499

Tools/cases_generator/analyzer.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -464,7 +464,7 @@ def compute_properties(op: parser.InstDef) -> Properties:
464464
ends_with_eval_breaker=eval_breaker_at_end(op),
465465
needs_this=variable_used(op, "this_instr"),
466466
always_exits=always_exits(op),
467-
stores_sp=variable_used(op, "STORE_SP"),
467+
stores_sp=variable_used(op, "SYNC_SP"),
468468
tier_one_only=variable_used(op, "TIER_ONE_ONLY"),
469469
uses_co_consts=variable_used(op, "FRAME_CO_CONSTS"),
470470
uses_co_names=variable_used(op, "FRAME_CO_NAMES"),

Tools/cases_generator/generators_common.py

+2-4
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ def replace_decrefs(
124124
out.emit(f"Py_DECREF({var.name});\n")
125125

126126

127-
def replace_store_sp(
127+
def replace_sync_sp(
128128
out: CWriter,
129129
tkn: Token,
130130
tkn_iter: Iterator[Token],
@@ -135,9 +135,7 @@ def replace_store_sp(
135135
next(tkn_iter)
136136
next(tkn_iter)
137137
next(tkn_iter)
138-
out.emit_at("", tkn)
139138
stack.flush(out)
140-
out.emit("_PyFrame_SetStackPointer(frame, stack_pointer);\n")
141139

142140

143141
def replace_check_eval_breaker(
@@ -160,7 +158,7 @@ def replace_check_eval_breaker(
160158
"ERROR_IF": replace_error,
161159
"DECREF_INPUTS": replace_decrefs,
162160
"CHECK_EVAL_BREAKER": replace_check_eval_breaker,
163-
"STORE_SP": replace_store_sp,
161+
"SYNC_SP": replace_sync_sp,
164162
}
165163

166164
ReplacementFunctionType = Callable[

Tools/cases_generator/interpreter_definition.md

+7-6
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ The CPython interpreter is defined in C, meaning that the semantics of the
66
bytecode instructions, the dispatching mechanism, error handling, and
77
tracing and instrumentation are all intermixed.
88

9-
This document proposes defining a custom C-like DSL for defining the
9+
This document proposes defining a custom C-like DSL for defining the
1010
instruction semantics and tools for generating the code deriving from
1111
the instruction definitions.
1212

@@ -46,7 +46,7 @@ passes from the semantic definition, reducing errors.
4646

4747
As we improve the performance of CPython, we need to optimize larger regions
4848
of code, use more complex optimizations and, ultimately, translate to machine
49-
code.
49+
code.
5050

5151
All of these steps introduce the possibility of more bugs, and require more code
5252
to be written. One way to mitigate this is through the use of code generators.
@@ -62,7 +62,7 @@ blocks as the instructions for the tier 1 (PEP 659) interpreter.
6262
Rewriting all the instructions is tedious and error-prone, and changing the
6363
instructions is a maintenance headache as both versions need to be kept in sync.
6464

65-
By using a code generator and using a common source for the instructions, or
65+
By using a code generator and using a common source for the instructions, or
6666
parts of instructions, we can reduce the potential for errors considerably.
6767

6868

@@ -75,7 +75,7 @@ We update it as the need arises.
7575

7676
Each op definition has a kind, a name, a stack and instruction stream effect,
7777
and a piece of C code describing its semantics::
78-
78+
7979
```
8080
file:
8181
(definition | family | pseudo)+
@@ -86,7 +86,7 @@ and a piece of C code describing its semantics::
8686
"op" "(" NAME "," stack_effect ")" "{" C-code "}"
8787
|
8888
"macro" "(" NAME ")" "=" uop ("+" uop)* ";"
89-
89+
9090
stack_effect:
9191
"(" [inputs] "--" [outputs] ")"
9292
@@ -201,6 +201,7 @@ Those functions include:
201201
* `DEOPT_IF(cond, instruction)`. Deoptimize if `cond` is met.
202202
* `ERROR_IF(cond, label)`. Jump to error handler at `label` if `cond` is true.
203203
* `DECREF_INPUTS()`. Generate `Py_DECREF()` calls for the input stack effects.
204+
* `SYNC_SP()`. Synchronizes the physical stack pointer with the stack effects.
204205

205206
Note that the use of `DECREF_INPUTS()` is optional -- manual calls
206207
to `Py_DECREF()` or other approaches are also acceptable
@@ -445,7 +446,7 @@ rather than popping and pushing, such that `LOAD_ATTR_SLOT` would look something
445446
stack_pointer += 1;
446447
}
447448
s1 = res;
448-
}
449+
}
449450
next_instr += (1 + 1 + 2 + 1 + 4);
450451
stack_pointer[-1] = s1;
451452
DISPATCH();

Tools/cases_generator/stack.py

+2
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,7 @@ def push(self, var: StackItem) -> str:
169169
return ""
170170

171171
def flush(self, out: CWriter) -> None:
172+
out.start_line()
172173
for var in self.variables:
173174
if not var.peek:
174175
cast = "(PyObject *)" if var.type else ""
@@ -189,6 +190,7 @@ def flush(self, out: CWriter) -> None:
189190
self.base_offset.clear()
190191
self.top_offset.clear()
191192
self.peek_offset.clear()
193+
out.start_line()
192194

193195
def as_comment(self) -> str:
194196
return f"/* Variables: {[v.name for v in self.variables]}. Base offset: {self.base_offset.to_c()}. Top offset: {self.top_offset.to_c()} */"

0 commit comments

Comments
 (0)