gh-106701: Move the hand-written Tier 2 uops to bytecodes.c (#106702)

This moves EXIT_TRACE, SAVE_IP, JUMP_TO_TOP, and
_POP_JUMP_IF_{FALSE,TRUE} from ceval.c to bytecodes.c.

They are no less special than before, but this way
they are discoverable o the copy-and-patch tooling.
This commit is contained in:
Guido van Rossum 2023-07-13 12:14:51 -07:00 committed by GitHub
parent 2f3ee02c22
commit e6e0ea0113
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 124 additions and 94 deletions

View file

@ -21,30 +21,30 @@
#define EXIT_TRACE 300 #define EXIT_TRACE 300
#define SAVE_IP 301 #define SAVE_IP 301
#define _POP_JUMP_IF_FALSE 302 #define _GUARD_BOTH_INT 302
#define _POP_JUMP_IF_TRUE 303 #define _BINARY_OP_MULTIPLY_INT 303
#define JUMP_TO_TOP 304 #define _BINARY_OP_ADD_INT 304
#define _GUARD_BOTH_INT 305 #define _BINARY_OP_SUBTRACT_INT 305
#define _BINARY_OP_MULTIPLY_INT 306 #define _GUARD_BOTH_FLOAT 306
#define _BINARY_OP_ADD_INT 307 #define _BINARY_OP_MULTIPLY_FLOAT 307
#define _BINARY_OP_SUBTRACT_INT 308 #define _BINARY_OP_ADD_FLOAT 308
#define _GUARD_BOTH_FLOAT 309 #define _BINARY_OP_SUBTRACT_FLOAT 309
#define _BINARY_OP_MULTIPLY_FLOAT 310 #define _GUARD_BOTH_UNICODE 310
#define _BINARY_OP_ADD_FLOAT 311 #define _BINARY_OP_ADD_UNICODE 311
#define _BINARY_OP_SUBTRACT_FLOAT 312 #define _LOAD_LOCALS 312
#define _GUARD_BOTH_UNICODE 313 #define _LOAD_FROM_DICT_OR_GLOBALS 313
#define _BINARY_OP_ADD_UNICODE 314 #define _SKIP_CACHE 314
#define _LOAD_LOCALS 315 #define _GUARD_GLOBALS_VERSION 315
#define _LOAD_FROM_DICT_OR_GLOBALS 316 #define _GUARD_BUILTINS_VERSION 316
#define _SKIP_CACHE 317 #define _GUARD_TYPE_VERSION 317
#define _GUARD_GLOBALS_VERSION 318 #define _CHECK_MANAGED_OBJECT_HAS_VALUES 318
#define _GUARD_BUILTINS_VERSION 319 #define IS_NONE 319
#define _GUARD_TYPE_VERSION 320 #define _ITER_CHECK_RANGE 320
#define _CHECK_MANAGED_OBJECT_HAS_VALUES 321 #define _ITER_EXHAUSTED_RANGE 321
#define IS_NONE 322 #define _ITER_NEXT_RANGE 322
#define _ITER_CHECK_RANGE 323 #define _POP_JUMP_IF_FALSE 323
#define _ITER_EXHAUSTED_RANGE 324 #define _POP_JUMP_IF_TRUE 324
#define _ITER_NEXT_RANGE 325 #define JUMP_TO_TOP 325
#ifndef NEED_OPCODE_METADATA #ifndef NEED_OPCODE_METADATA
extern int _PyOpcode_num_popped(int opcode, int oparg, bool jump); extern int _PyOpcode_num_popped(int opcode, int oparg, bool jump);
@ -1303,31 +1303,31 @@ const struct opcode_macro_expansion _PyOpcode_macro_expansion[256] = {
[SWAP] = { .nuops = 1, .uops = { { SWAP, 0, 0 } } }, [SWAP] = { .nuops = 1, .uops = { { SWAP, 0, 0 } } },
}; };
const char * const _PyOpcode_uop_name[512] = { const char * const _PyOpcode_uop_name[512] = {
[300] = "EXIT_TRACE", [EXIT_TRACE] = "EXIT_TRACE",
[301] = "SAVE_IP", [SAVE_IP] = "SAVE_IP",
[302] = "_POP_JUMP_IF_FALSE", [_GUARD_BOTH_INT] = "_GUARD_BOTH_INT",
[303] = "_POP_JUMP_IF_TRUE", [_BINARY_OP_MULTIPLY_INT] = "_BINARY_OP_MULTIPLY_INT",
[304] = "JUMP_TO_TOP", [_BINARY_OP_ADD_INT] = "_BINARY_OP_ADD_INT",
[305] = "_GUARD_BOTH_INT", [_BINARY_OP_SUBTRACT_INT] = "_BINARY_OP_SUBTRACT_INT",
[306] = "_BINARY_OP_MULTIPLY_INT", [_GUARD_BOTH_FLOAT] = "_GUARD_BOTH_FLOAT",
[307] = "_BINARY_OP_ADD_INT", [_BINARY_OP_MULTIPLY_FLOAT] = "_BINARY_OP_MULTIPLY_FLOAT",
[308] = "_BINARY_OP_SUBTRACT_INT", [_BINARY_OP_ADD_FLOAT] = "_BINARY_OP_ADD_FLOAT",
[309] = "_GUARD_BOTH_FLOAT", [_BINARY_OP_SUBTRACT_FLOAT] = "_BINARY_OP_SUBTRACT_FLOAT",
[310] = "_BINARY_OP_MULTIPLY_FLOAT", [_GUARD_BOTH_UNICODE] = "_GUARD_BOTH_UNICODE",
[311] = "_BINARY_OP_ADD_FLOAT", [_BINARY_OP_ADD_UNICODE] = "_BINARY_OP_ADD_UNICODE",
[312] = "_BINARY_OP_SUBTRACT_FLOAT", [_LOAD_LOCALS] = "_LOAD_LOCALS",
[313] = "_GUARD_BOTH_UNICODE", [_LOAD_FROM_DICT_OR_GLOBALS] = "_LOAD_FROM_DICT_OR_GLOBALS",
[314] = "_BINARY_OP_ADD_UNICODE", [_SKIP_CACHE] = "_SKIP_CACHE",
[315] = "_LOAD_LOCALS", [_GUARD_GLOBALS_VERSION] = "_GUARD_GLOBALS_VERSION",
[316] = "_LOAD_FROM_DICT_OR_GLOBALS", [_GUARD_BUILTINS_VERSION] = "_GUARD_BUILTINS_VERSION",
[317] = "_SKIP_CACHE", [_GUARD_TYPE_VERSION] = "_GUARD_TYPE_VERSION",
[318] = "_GUARD_GLOBALS_VERSION", [_CHECK_MANAGED_OBJECT_HAS_VALUES] = "_CHECK_MANAGED_OBJECT_HAS_VALUES",
[319] = "_GUARD_BUILTINS_VERSION", [IS_NONE] = "IS_NONE",
[320] = "_GUARD_TYPE_VERSION", [_ITER_CHECK_RANGE] = "_ITER_CHECK_RANGE",
[321] = "_CHECK_MANAGED_OBJECT_HAS_VALUES", [_ITER_EXHAUSTED_RANGE] = "_ITER_EXHAUSTED_RANGE",
[322] = "IS_NONE", [_ITER_NEXT_RANGE] = "_ITER_NEXT_RANGE",
[323] = "_ITER_CHECK_RANGE", [_POP_JUMP_IF_FALSE] = "_POP_JUMP_IF_FALSE",
[324] = "_ITER_EXHAUSTED_RANGE", [_POP_JUMP_IF_TRUE] = "_POP_JUMP_IF_TRUE",
[325] = "_ITER_NEXT_RANGE", [JUMP_TO_TOP] = "JUMP_TO_TOP",
}; };
#endif // NEED_OPCODE_METADATA #endif // NEED_OPCODE_METADATA

View file

@ -3654,6 +3654,36 @@ dummy_func(
Py_UNREACHABLE(); Py_UNREACHABLE();
} }
///////// Tier-2 only opcodes /////////
op(_POP_JUMP_IF_FALSE, (flag -- )) {
if (Py_IsFalse(flag)) {
pc = oparg;
}
}
op(_POP_JUMP_IF_TRUE, (flag -- )) {
if (Py_IsTrue(flag)) {
pc = oparg;
}
}
op(JUMP_TO_TOP, (--)) {
pc = 0;
CHECK_EVAL_BREAKER();
}
op(SAVE_IP, (--)) {
frame->prev_instr = ip_offset + oparg;
}
op(EXIT_TRACE, (--)) {
frame->prev_instr--; // Back up to just before destination
_PyFrame_SetStackPointer(frame, stack_pointer);
Py_DECREF(self);
return frame;
}
// END BYTECODES // // END BYTECODES //

View file

@ -2764,46 +2764,6 @@ _PyUopExecute(_PyExecutorObject *executor, _PyInterpreterFrame *frame, PyObject
#define ENABLE_SPECIALIZATION 0 #define ENABLE_SPECIALIZATION 0
#include "executor_cases.c.h" #include "executor_cases.c.h"
// NOTE: These pop-jumps move the uop pc, not the bytecode ip
case _POP_JUMP_IF_FALSE:
{
if (Py_IsFalse(stack_pointer[-1])) {
pc = oparg;
}
stack_pointer--;
break;
}
case _POP_JUMP_IF_TRUE:
{
if (Py_IsTrue(stack_pointer[-1])) {
pc = oparg;
}
stack_pointer--;
break;
}
case JUMP_TO_TOP:
{
pc = 0;
CHECK_EVAL_BREAKER();
break;
}
case SAVE_IP:
{
frame->prev_instr = ip_offset + oparg;
break;
}
case EXIT_TRACE:
{
frame->prev_instr--; // Back up to just before destination
_PyFrame_SetStackPointer(frame, stack_pointer);
Py_DECREF(self);
return frame;
}
default: default:
{ {
fprintf(stderr, "Unknown uop %d, operand %" PRIu64 "\n", opcode, operand); fprintf(stderr, "Unknown uop %d, operand %" PRIu64 "\n", opcode, operand);

View file

@ -1987,3 +1987,39 @@
stack_pointer[-(2 + (oparg-2))] = top; stack_pointer[-(2 + (oparg-2))] = top;
break; break;
} }
case _POP_JUMP_IF_FALSE: {
PyObject *flag = stack_pointer[-1];
if (Py_IsFalse(flag)) {
pc = oparg;
}
STACK_SHRINK(1);
break;
}
case _POP_JUMP_IF_TRUE: {
PyObject *flag = stack_pointer[-1];
if (Py_IsTrue(flag)) {
pc = oparg;
}
STACK_SHRINK(1);
break;
}
case JUMP_TO_TOP: {
pc = 0;
break;
}
case SAVE_IP: {
frame->prev_instr = ip_offset + oparg;
break;
}
case EXIT_TRACE: {
frame->prev_instr--; // Back up to just before destination
_PyFrame_SetStackPointer(frame, stack_pointer);
Py_DECREF(self);
return frame;
break;
}

View file

@ -410,6 +410,8 @@ class Instruction:
def is_viable_uop(self) -> bool: def is_viable_uop(self) -> bool:
"""Whether this instruction is viable as a uop.""" """Whether this instruction is viable as a uop."""
if self.name == "EXIT_TRACE":
return True # This has 'return frame' but it's okay
if self.always_exits: if self.always_exits:
# print(f"Skipping {self.name} because it always exits") # print(f"Skipping {self.name} because it always exits")
return False return False
@ -1278,7 +1280,7 @@ class Analyzer:
typing.assert_never(thing) typing.assert_never(thing)
with self.out.block("const char * const _PyOpcode_uop_name[512] =", ";"): with self.out.block("const char * const _PyOpcode_uop_name[512] =", ";"):
self.write_uop_items(lambda name, counter: f"[{counter}] = \"{name}\",") self.write_uop_items(lambda name, counter: f"[{name}] = \"{name}\",")
self.out.emit("#endif // NEED_OPCODE_METADATA") self.out.emit("#endif // NEED_OPCODE_METADATA")
@ -1324,17 +1326,19 @@ class Analyzer:
def write_uop_items(self, make_text: typing.Callable[[str, int], str]) -> None: def write_uop_items(self, make_text: typing.Callable[[str, int], str]) -> None:
"""Write '#define XXX NNN' for each uop""" """Write '#define XXX NNN' for each uop"""
counter = 300 # TODO: Avoid collision with pseudo instructions counter = 300 # TODO: Avoid collision with pseudo instructions
seen = set()
def add(name: str) -> None: def add(name: str) -> None:
if name in seen:
return
nonlocal counter nonlocal counter
self.out.emit(make_text(name, counter)) self.out.emit(make_text(name, counter))
counter += 1 counter += 1
seen.add(name)
# These two are first by convention
add("EXIT_TRACE") add("EXIT_TRACE")
add("SAVE_IP") add("SAVE_IP")
add("_POP_JUMP_IF_FALSE")
add("_POP_JUMP_IF_TRUE")
add("JUMP_TO_TOP")
for instr in self.instrs.values(): for instr in self.instrs.values():
if instr.kind == "op" and instr.is_viable_uop(): if instr.kind == "op" and instr.is_viable_uop():