From 4b7125f331c91c3da37fc0586d2a74ca618f4c83 Mon Sep 17 00:00:00 2001 From: Brandt Bucher Date: Wed, 27 Oct 2021 13:16:01 -0700 Subject: [PATCH 01/17] Merge BINARY_*/INPLACE_* into BINARY_OP/INPLACE_OP --- Include/abstract.h | 4 + Include/opcode.h | 104 ++++++------ Lib/dis.py | 13 ++ Lib/importlib/_bootstrap_external.py | 3 +- Lib/opcode.py | 38 ++--- Lib/test/test_compile.py | 24 +-- Lib/test/test_dis.py | 6 +- Objects/abstract.c | 39 +++++ Python/ceval.c | 242 +++------------------------ Python/compile.c | 158 +++++++---------- Python/opcode_targets.h | 100 +++++------ Tools/scripts/generate_opcode_h.py | 4 + 12 files changed, 285 insertions(+), 450 deletions(-) diff --git a/Include/abstract.h b/Include/abstract.h index 9e06fbbb749138..d697f68a52fd50 100644 --- a/Include/abstract.h +++ b/Include/abstract.h @@ -633,6 +633,10 @@ PyAPI_FUNC(PyObject *) PyNumber_InPlaceOr(PyObject *o1, PyObject *o2); If n is not an int object, it is converted with PyNumber_Index first. */ PyAPI_FUNC(PyObject *) PyNumber_ToBase(PyObject *n, int base); +PyAPI_FUNC(PyObject *) _PyNumber_Op(PyObject *o1, PyObject *o2, int op); + +PyAPI_FUNC(PyObject *) _PyNumber_InPlaceOp(PyObject *o1, PyObject *o2, int op); + /* === Sequence protocol ================================================ */ diff --git a/Include/opcode.h b/Include/opcode.h index 87ed32c019909a..1c7ffd0d34a777 100644 --- a/Include/opcode.h +++ b/Include/opcode.h @@ -18,18 +18,11 @@ extern "C" { #define UNARY_NEGATIVE 11 #define UNARY_NOT 12 #define UNARY_INVERT 15 -#define BINARY_MATRIX_MULTIPLY 16 -#define INPLACE_MATRIX_MULTIPLY 17 #define BINARY_POWER 19 #define BINARY_MULTIPLY 20 #define BINARY_MODULO 22 #define BINARY_ADD 23 -#define BINARY_SUBTRACT 24 #define BINARY_SUBSCR 25 -#define BINARY_FLOOR_DIVIDE 26 -#define BINARY_TRUE_DIVIDE 27 -#define INPLACE_FLOOR_DIVIDE 28 -#define INPLACE_TRUE_DIVIDE 29 #define GET_LEN 30 #define MATCH_MAPPING 31 #define MATCH_SEQUENCE 32 @@ -43,16 +36,10 @@ extern "C" { #define BEFORE_WITH 53 #define END_ASYNC_FOR 54 #define INPLACE_ADD 55 -#define INPLACE_SUBTRACT 56 #define INPLACE_MULTIPLY 57 #define INPLACE_MODULO 59 #define STORE_SUBSCR 60 #define DELETE_SUBSCR 61 -#define BINARY_LSHIFT 62 -#define BINARY_RSHIFT 63 -#define BINARY_AND 64 -#define BINARY_XOR 65 -#define BINARY_OR 66 #define INPLACE_POWER 67 #define GET_ITER 68 #define GET_YIELD_FROM_ITER 69 @@ -61,11 +48,6 @@ extern "C" { #define YIELD_FROM 72 #define GET_AWAITABLE 73 #define LOAD_ASSERTION_ERROR 74 -#define INPLACE_LSHIFT 75 -#define INPLACE_RSHIFT 76 -#define INPLACE_AND 77 -#define INPLACE_XOR 78 -#define INPLACE_OR 79 #define LIST_TO_TUPLE 82 #define RETURN_VALUE 83 #define IMPORT_STAR 84 @@ -105,6 +87,8 @@ extern "C" { #define RERAISE 119 #define COPY 120 #define JUMP_IF_NOT_EXC_MATCH 121 +#define BINARY_OP 122 +#define INPLACE_OP 123 #define LOAD_FAST 124 #define STORE_FAST 125 #define DELETE_FAST 126 @@ -140,43 +124,43 @@ extern "C" { #define BINARY_ADD_INT 8 #define BINARY_ADD_FLOAT 13 #define BINARY_ADD_UNICODE 14 -#define BINARY_ADD_UNICODE_INPLACE_FAST 18 -#define BINARY_MULTIPLY_ADAPTIVE 21 -#define BINARY_MULTIPLY_INT 34 -#define BINARY_MULTIPLY_FLOAT 36 -#define BINARY_SUBSCR_ADAPTIVE 38 -#define BINARY_SUBSCR_LIST_INT 39 -#define BINARY_SUBSCR_TUPLE_INT 40 -#define BINARY_SUBSCR_DICT 41 -#define CALL_FUNCTION_ADAPTIVE 42 -#define CALL_FUNCTION_BUILTIN_O 43 -#define CALL_FUNCTION_BUILTIN_FAST 44 -#define CALL_FUNCTION_LEN 45 -#define CALL_FUNCTION_ISINSTANCE 46 -#define CALL_FUNCTION_PY_SIMPLE 47 -#define JUMP_ABSOLUTE_QUICK 48 -#define LOAD_ATTR_ADAPTIVE 58 -#define LOAD_ATTR_INSTANCE_VALUE 80 -#define LOAD_ATTR_WITH_HINT 81 -#define LOAD_ATTR_SLOT 87 -#define LOAD_ATTR_MODULE 88 -#define LOAD_GLOBAL_ADAPTIVE 122 -#define LOAD_GLOBAL_MODULE 123 -#define LOAD_GLOBAL_BUILTIN 127 -#define LOAD_METHOD_ADAPTIVE 128 -#define LOAD_METHOD_CACHED 134 -#define LOAD_METHOD_CLASS 140 -#define LOAD_METHOD_MODULE 143 -#define LOAD_METHOD_NO_DICT 149 -#define STORE_ATTR_ADAPTIVE 150 -#define STORE_ATTR_INSTANCE_VALUE 151 -#define STORE_ATTR_SLOT 153 -#define STORE_ATTR_WITH_HINT 154 -#define LOAD_FAST__LOAD_FAST 158 -#define STORE_FAST__LOAD_FAST 159 -#define LOAD_FAST__LOAD_CONST 167 -#define LOAD_CONST__LOAD_FAST 168 -#define STORE_FAST__STORE_FAST 169 +#define BINARY_ADD_UNICODE_INPLACE_FAST 16 +#define BINARY_MULTIPLY_ADAPTIVE 17 +#define BINARY_MULTIPLY_INT 18 +#define BINARY_MULTIPLY_FLOAT 21 +#define BINARY_SUBSCR_ADAPTIVE 24 +#define BINARY_SUBSCR_LIST_INT 26 +#define BINARY_SUBSCR_TUPLE_INT 27 +#define BINARY_SUBSCR_DICT 28 +#define CALL_FUNCTION_ADAPTIVE 29 +#define CALL_FUNCTION_BUILTIN_O 34 +#define CALL_FUNCTION_BUILTIN_FAST 36 +#define CALL_FUNCTION_LEN 38 +#define CALL_FUNCTION_ISINSTANCE 39 +#define CALL_FUNCTION_PY_SIMPLE 40 +#define JUMP_ABSOLUTE_QUICK 41 +#define LOAD_ATTR_ADAPTIVE 42 +#define LOAD_ATTR_INSTANCE_VALUE 43 +#define LOAD_ATTR_WITH_HINT 44 +#define LOAD_ATTR_SLOT 45 +#define LOAD_ATTR_MODULE 46 +#define LOAD_GLOBAL_ADAPTIVE 47 +#define LOAD_GLOBAL_MODULE 48 +#define LOAD_GLOBAL_BUILTIN 56 +#define LOAD_METHOD_ADAPTIVE 58 +#define LOAD_METHOD_CACHED 62 +#define LOAD_METHOD_CLASS 63 +#define LOAD_METHOD_MODULE 64 +#define LOAD_METHOD_NO_DICT 65 +#define STORE_ATTR_ADAPTIVE 66 +#define STORE_ATTR_INSTANCE_VALUE 75 +#define STORE_ATTR_SLOT 76 +#define STORE_ATTR_WITH_HINT 77 +#define LOAD_FAST__LOAD_FAST 78 +#define STORE_FAST__LOAD_FAST 79 +#define LOAD_FAST__LOAD_CONST 80 +#define LOAD_CONST__LOAD_FAST 81 +#define STORE_FAST__STORE_FAST 87 #define DO_TRACING 255 #ifdef NEED_OPCODE_JUMP_TABLES static uint32_t _PyOpcode_RelativeJump[8] = { @@ -205,6 +189,16 @@ static uint32_t _PyOpcode_Jump[8] = { || ((op) == 100) \ ) +#define NB_AND 0 +#define NB_FLOOR_DIVIDE 1 +#define NB_LSHIFT 2 +#define NB_MATRIX_MULTIPLY 3 +#define NB_OR 4 +#define NB_RSHIFT 5 +#define NB_SUBTRACT 6 +#define NB_TRUE_DIVIDE 7 +#define NB_XOR 8 + #define HAS_ARG(op) ((op) >= HAVE_ARGUMENT) /* Reserve some bytecodes for internal use in the compiler. diff --git a/Lib/dis.py b/Lib/dis.py index 54275648fbb4b6..49446541c8fca5 100644 --- a/Lib/dis.py +++ b/Lib/dis.py @@ -7,6 +7,7 @@ from opcode import * from opcode import __all__ as _opcodes_all +from opcode import _nb_ops __all__ = ["code_info", "dis", "disassemble", "distb", "disco", "findlinestarts", "findlabels", "show_code", @@ -28,6 +29,14 @@ LOAD_CONST = opmap['LOAD_CONST'] +BINARY_OP = opmap['BINARY_OP'] +BINARY_OPS = {i: name for i, (_, name) in enumerate(_nb_ops)} + +INPLACE_OP = opmap['INPLACE_OP'] +INPLACE_OPS = {i: f"{name}=" for i, (_, name) in enumerate(_nb_ops)} + +del _nb_ops + def _try_compile(source, name): """Attempts to compile the given source, first as an expression and then as a statement if the first approach fails. @@ -446,6 +455,10 @@ def _get_instructions_bytes(code, varname_from_oparg=None, elif op == MAKE_FUNCTION: argrepr = ', '.join(s for i, s in enumerate(MAKE_FUNCTION_FLAGS) if arg & (1<>"), + ("NB_SUBTRACT", "-"), + ("NB_TRUE_DIVIDE", "/"), + ("NB_XOR", "^"), +] + _specialized_instructions = [ "BINARY_ADD_ADAPTIVE", "BINARY_ADD_INT", diff --git a/Lib/test/test_compile.py b/Lib/test/test_compile.py index 495a223fc3da74..7819e9a3d56956 100644 --- a/Lib/test/test_compile.py +++ b/Lib/test/test_compile.py @@ -1048,15 +1048,17 @@ def generic_visit(self, node): return code, ast_tree def assertOpcodeSourcePositionIs(self, code, opcode, - line, end_line, column, end_column): + line, end_line, column, end_column, occurrence=1): for instr, position in zip(dis.Bytecode(code), code.co_positions()): if instr.opname == opcode: - self.assertEqual(position[0], line) - self.assertEqual(position[1], end_line) - self.assertEqual(position[2], column) - self.assertEqual(position[3], end_column) - return + occurrence -= 1 + if occurrence == 0: + self.assertEqual(position[0], line) + self.assertEqual(position[1], end_line) + self.assertEqual(position[2], column) + self.assertEqual(position[3], end_column) + return self.fail(f"Opcode {opcode} not found in code") @@ -1077,7 +1079,7 @@ def test_compiles_to_extended_op_arg(self): compiled_code, _ = self.check_positions_against_ast(snippet) - self.assertOpcodeSourcePositionIs(compiled_code, 'INPLACE_SUBTRACT', + self.assertOpcodeSourcePositionIs(compiled_code, 'INPLACE_OP', line=10_000 + 2, end_line=10_000 + 2, column=2, end_column=8) self.assertOpcodeSourcePositionIs(compiled_code, 'INPLACE_ADD', @@ -1114,10 +1116,10 @@ def test_complex_single_line_expression(self): line=1, end_line=1, column=9, end_column=21) self.assertOpcodeSourcePositionIs(compiled_code, 'BINARY_ADD', line=1, end_line=1, column=9, end_column=26) - self.assertOpcodeSourcePositionIs(compiled_code, 'BINARY_MATRIX_MULTIPLY', - line=1, end_line=1, column=4, end_column=27) - self.assertOpcodeSourcePositionIs(compiled_code, 'BINARY_SUBTRACT', - line=1, end_line=1, column=0, end_column=27) + self.assertOpcodeSourcePositionIs(compiled_code, 'BINARY_OP', + line=1, end_line=1, column=4, end_column=27, occurrence=1) + self.assertOpcodeSourcePositionIs(compiled_code, 'BINARY_OP', + line=1, end_line=1, column=0, end_column=27, occurrence=2) class TestExpressionStackSize(unittest.TestCase): diff --git a/Lib/test/test_dis.py b/Lib/test/test_dis.py index 4755e3f3a17247..c743851ed97c57 100644 --- a/Lib/test/test_dis.py +++ b/Lib/test/test_dis.py @@ -285,7 +285,7 @@ def bug42562(): %3d 2 LOAD_CONST 1 (1) 4 LOAD_CONST 2 (0) - --> 6 BINARY_TRUE_DIVIDE + --> 6 BINARY_OP 7 (/) 8 POP_TOP %3d 10 LOAD_FAST 1 (tb) @@ -1060,7 +1060,7 @@ def _prepare_test_cases(): Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=62, starts_line=None, is_jump_target=False, positions=None), Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='i', argrepr='i', offset=64, starts_line=13, is_jump_target=False, positions=None), Instruction(opname='LOAD_CONST', opcode=100, arg=5, argval=1, argrepr='1', offset=66, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='INPLACE_SUBTRACT', opcode=56, arg=None, argval=None, argrepr='', offset=68, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='INPLACE_OP', opcode=123, arg=6, argval=6, argrepr='-=', offset=68, starts_line=None, is_jump_target=False, positions=None), Instruction(opname='STORE_FAST', opcode=125, arg=0, argval='i', argrepr='i', offset=70, starts_line=None, is_jump_target=False, positions=None), Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='i', argrepr='i', offset=72, starts_line=14, is_jump_target=False, positions=None), Instruction(opname='LOAD_CONST', opcode=100, arg=3, argval=6, argrepr='6', offset=74, starts_line=None, is_jump_target=False, positions=None), @@ -1081,7 +1081,7 @@ def _prepare_test_cases(): Instruction(opname='NOP', opcode=9, arg=None, argval=None, argrepr='', offset=104, starts_line=20, is_jump_target=True, positions=None), Instruction(opname='LOAD_CONST', opcode=100, arg=5, argval=1, argrepr='1', offset=106, starts_line=21, is_jump_target=False, positions=None), Instruction(opname='LOAD_CONST', opcode=100, arg=7, argval=0, argrepr='0', offset=108, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='BINARY_TRUE_DIVIDE', opcode=27, arg=None, argval=None, argrepr='', offset=110, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='BINARY_OP', opcode=122, arg=7, argval=7, argrepr='/', offset=110, starts_line=None, is_jump_target=False, positions=None), Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=112, starts_line=None, is_jump_target=False, positions=None), Instruction(opname='JUMP_FORWARD', opcode=110, arg=15, argval=146, argrepr='to 146', offset=114, starts_line=None, is_jump_target=False, positions=None), Instruction(opname='PUSH_EXC_INFO', opcode=35, arg=None, argval=None, argrepr='', offset=116, starts_line=None, is_jump_target=False, positions=None), diff --git a/Objects/abstract.c b/Objects/abstract.c index 6227ad5a18bb95..cb08adb272bfe1 100644 --- a/Objects/abstract.c +++ b/Objects/abstract.c @@ -1,6 +1,7 @@ /* Abstract Object Interface (many thanks to Jim Fulton) */ #include "Python.h" +#include "opcode.h" #include "pycore_abstract.h" // _PyIndex_Check() #include "pycore_call.h" // _PyObject_CallNoArgs() #include "pycore_ceval.h" // _Py_EnterRecursiveCall() @@ -1714,6 +1715,44 @@ PyNumber_ToBase(PyObject *n, int base) return res; } +typedef struct { + const char *iname; + const char *name; + size_t islot; + size_t slot; +} nb_info; + +#define NB_INFO(name, slot) \ + {name "=", name, NB_SLOT(nb_inplace_##slot), NB_SLOT(nb_##slot)} + +static nb_info nb_infos[] = { + [NB_AND] = NB_INFO("&", and), + [NB_FLOOR_DIVIDE] = NB_INFO("//", floor_divide), + [NB_LSHIFT] = NB_INFO("<<", lshift), + [NB_MATRIX_MULTIPLY] = NB_INFO("@", matrix_multiply), + [NB_OR] = NB_INFO("|", or), + [NB_RSHIFT] = NB_INFO(">>", rshift), + [NB_SUBTRACT] = NB_INFO("-", subtract), + [NB_TRUE_DIVIDE] = NB_INFO("/", true_divide), + [NB_XOR] = NB_INFO("^", xor), +}; + +#undef NB_INFO + +PyObject * +_PyNumber_Op(PyObject *o1, PyObject *o2, int op) +{ + nb_info *ni = &nb_infos[op]; + return binary_op(o1, o2, ni->slot, ni->name); +} + +PyObject * +_PyNumber_InPlaceOp(PyObject *o1, PyObject *o2, int op) +{ + nb_info *ni = &nb_infos[op]; + return binary_iop(o1, o2, ni->islot, ni->slot, ni->iname); +} + /* Operations on sequences */ diff --git a/Python/ceval.c b/Python/ceval.c index bd01e8159c8e4c..c0795b0d963a68 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -2000,42 +2000,6 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr DISPATCH(); } - TARGET(BINARY_MATRIX_MULTIPLY) { - PyObject *right = POP(); - PyObject *left = TOP(); - PyObject *res = PyNumber_MatrixMultiply(left, right); - Py_DECREF(left); - Py_DECREF(right); - SET_TOP(res); - if (res == NULL) - goto error; - DISPATCH(); - } - - TARGET(BINARY_TRUE_DIVIDE) { - PyObject *divisor = POP(); - PyObject *dividend = TOP(); - PyObject *quotient = PyNumber_TrueDivide(dividend, divisor); - Py_DECREF(dividend); - Py_DECREF(divisor); - SET_TOP(quotient); - if (quotient == NULL) - goto error; - DISPATCH(); - } - - TARGET(BINARY_FLOOR_DIVIDE) { - PyObject *divisor = POP(); - PyObject *dividend = TOP(); - PyObject *quotient = PyNumber_FloorDivide(dividend, divisor); - Py_DECREF(dividend); - Py_DECREF(divisor); - SET_TOP(quotient); - if (quotient == NULL) - goto error; - DISPATCH(); - } - TARGET(BINARY_MODULO) { PyObject *divisor = POP(); PyObject *dividend = TOP(); @@ -2170,18 +2134,6 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr DISPATCH(); } - TARGET(BINARY_SUBTRACT) { - PyObject *right = POP(); - PyObject *left = TOP(); - PyObject *diff = PyNumber_Subtract(left, right); - Py_DECREF(right); - Py_DECREF(left); - SET_TOP(diff); - if (diff == NULL) - goto error; - DISPATCH(); - } - TARGET(BINARY_SUBSCR) { PREDICTED(BINARY_SUBSCR); STAT_INC(BINARY_SUBSCR, unquickened); @@ -2280,66 +2232,6 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr DISPATCH(); } - TARGET(BINARY_LSHIFT) { - PyObject *right = POP(); - PyObject *left = TOP(); - PyObject *res = PyNumber_Lshift(left, right); - Py_DECREF(left); - Py_DECREF(right); - SET_TOP(res); - if (res == NULL) - goto error; - DISPATCH(); - } - - TARGET(BINARY_RSHIFT) { - PyObject *right = POP(); - PyObject *left = TOP(); - PyObject *res = PyNumber_Rshift(left, right); - Py_DECREF(left); - Py_DECREF(right); - SET_TOP(res); - if (res == NULL) - goto error; - DISPATCH(); - } - - TARGET(BINARY_AND) { - PyObject *right = POP(); - PyObject *left = TOP(); - PyObject *res = PyNumber_And(left, right); - Py_DECREF(left); - Py_DECREF(right); - SET_TOP(res); - if (res == NULL) - goto error; - DISPATCH(); - } - - TARGET(BINARY_XOR) { - PyObject *right = POP(); - PyObject *left = TOP(); - PyObject *res = PyNumber_Xor(left, right); - Py_DECREF(left); - Py_DECREF(right); - SET_TOP(res); - if (res == NULL) - goto error; - DISPATCH(); - } - - TARGET(BINARY_OR) { - PyObject *right = POP(); - PyObject *left = TOP(); - PyObject *res = PyNumber_Or(left, right); - Py_DECREF(left); - Py_DECREF(right); - SET_TOP(res); - if (res == NULL) - goto error; - DISPATCH(); - } - TARGET(LIST_APPEND) { PyObject *v = POP(); PyObject *list = PEEK(oparg); @@ -2388,42 +2280,6 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr DISPATCH(); } - TARGET(INPLACE_MATRIX_MULTIPLY) { - PyObject *right = POP(); - PyObject *left = TOP(); - PyObject *res = PyNumber_InPlaceMatrixMultiply(left, right); - Py_DECREF(left); - Py_DECREF(right); - SET_TOP(res); - if (res == NULL) - goto error; - DISPATCH(); - } - - TARGET(INPLACE_TRUE_DIVIDE) { - PyObject *divisor = POP(); - PyObject *dividend = TOP(); - PyObject *quotient = PyNumber_InPlaceTrueDivide(dividend, divisor); - Py_DECREF(dividend); - Py_DECREF(divisor); - SET_TOP(quotient); - if (quotient == NULL) - goto error; - DISPATCH(); - } - - TARGET(INPLACE_FLOOR_DIVIDE) { - PyObject *divisor = POP(); - PyObject *dividend = TOP(); - PyObject *quotient = PyNumber_InPlaceFloorDivide(dividend, divisor); - Py_DECREF(dividend); - Py_DECREF(divisor); - SET_TOP(quotient); - if (quotient == NULL) - goto error; - DISPATCH(); - } - TARGET(INPLACE_MODULO) { PyObject *right = POP(); PyObject *left = TOP(); @@ -2455,78 +2311,6 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr DISPATCH(); } - TARGET(INPLACE_SUBTRACT) { - PyObject *right = POP(); - PyObject *left = TOP(); - PyObject *diff = PyNumber_InPlaceSubtract(left, right); - Py_DECREF(left); - Py_DECREF(right); - SET_TOP(diff); - if (diff == NULL) - goto error; - DISPATCH(); - } - - TARGET(INPLACE_LSHIFT) { - PyObject *right = POP(); - PyObject *left = TOP(); - PyObject *res = PyNumber_InPlaceLshift(left, right); - Py_DECREF(left); - Py_DECREF(right); - SET_TOP(res); - if (res == NULL) - goto error; - DISPATCH(); - } - - TARGET(INPLACE_RSHIFT) { - PyObject *right = POP(); - PyObject *left = TOP(); - PyObject *res = PyNumber_InPlaceRshift(left, right); - Py_DECREF(left); - Py_DECREF(right); - SET_TOP(res); - if (res == NULL) - goto error; - DISPATCH(); - } - - TARGET(INPLACE_AND) { - PyObject *right = POP(); - PyObject *left = TOP(); - PyObject *res = PyNumber_InPlaceAnd(left, right); - Py_DECREF(left); - Py_DECREF(right); - SET_TOP(res); - if (res == NULL) - goto error; - DISPATCH(); - } - - TARGET(INPLACE_XOR) { - PyObject *right = POP(); - PyObject *left = TOP(); - PyObject *res = PyNumber_InPlaceXor(left, right); - Py_DECREF(left); - Py_DECREF(right); - SET_TOP(res); - if (res == NULL) - goto error; - DISPATCH(); - } - - TARGET(INPLACE_OR) { - PyObject *right = POP(); - PyObject *left = TOP(); - PyObject *res = PyNumber_InPlaceOr(left, right); - Py_DECREF(left); - Py_DECREF(right); - SET_TOP(res); - if (res == NULL) - goto error; - DISPATCH(); - } - TARGET(STORE_SUBSCR) { PyObject *sub = TOP(); PyObject *container = SECOND(); @@ -5020,6 +4804,32 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr DISPATCH(); } + TARGET(BINARY_OP) { + PyObject *right = POP(); + PyObject *left = TOP(); + PyObject *res = _PyNumber_Op(left, right, oparg); + Py_DECREF(left); + Py_DECREF(right); + SET_TOP(res); + if (res == NULL) { + goto error; + } + DISPATCH(); + } + + TARGET(INPLACE_OP) { + PyObject *right = POP(); + PyObject *left = TOP(); + PyObject *res = _PyNumber_InPlaceOp(left, right, oparg); + Py_DECREF(left); + Py_DECREF(right); + SET_TOP(res); + if (res == NULL) { + goto error; + } + DISPATCH(); + } + TARGET(EXTENDED_ARG) { int oldoparg = oparg; NEXTOPARG(); diff --git a/Python/compile.c b/Python/compile.c index 28b5c076d50aa1..1f1b0126cf3ec7 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -309,7 +309,6 @@ static int compiler_annassign(struct compiler *, stmt_ty); static int compiler_subscript(struct compiler *, expr_ty); static int compiler_slice(struct compiler *, expr_ty); -static int inplace_binop(operator_ty); static int are_all_items_const(asdl_expr_seq *, Py_ssize_t, Py_ssize_t); @@ -1025,22 +1024,13 @@ stack_effect(int opcode, int oparg, int jump) /* Binary operators */ case BINARY_POWER: case BINARY_MULTIPLY: - case BINARY_MATRIX_MULTIPLY: case BINARY_MODULO: case BINARY_ADD: - case BINARY_SUBTRACT: case BINARY_SUBSCR: - case BINARY_FLOOR_DIVIDE: - case BINARY_TRUE_DIVIDE: - return -1; - case INPLACE_FLOOR_DIVIDE: - case INPLACE_TRUE_DIVIDE: return -1; case INPLACE_ADD: - case INPLACE_SUBTRACT: case INPLACE_MULTIPLY: - case INPLACE_MATRIX_MULTIPLY: case INPLACE_MODULO: return -1; case STORE_SUBSCR: @@ -1048,12 +1038,6 @@ stack_effect(int opcode, int oparg, int jump) case DELETE_SUBSCR: return -2; - case BINARY_LSHIFT: - case BINARY_RSHIFT: - case BINARY_AND: - case BINARY_XOR: - case BINARY_OR: - return -1; case INPLACE_POWER: return -1; case GET_ITER: @@ -1063,12 +1047,6 @@ stack_effect(int opcode, int oparg, int jump) return -1; case LOAD_BUILD_CLASS: return 1; - case INPLACE_LSHIFT: - case INPLACE_RSHIFT: - case INPLACE_AND: - case INPLACE_XOR: - case INPLACE_OR: - return -1; case RETURN_VALUE: return -1; @@ -1259,6 +1237,9 @@ stack_effect(int opcode, int oparg, int jump) return 0; case COPY: return 1; + case BINARY_OP: + case INPLACE_OP: + return -1; default: return PY_INVALID_STACK_EFFECT; } @@ -1685,6 +1666,12 @@ compiler_addop_j_noline(struct compiler *c, int opcode, basicblock *b) return 0; \ } +#define ADDOP_BINARY(C, BINOP) \ + RETURN_IF_FALSE(addop_binary((C), (BINOP), false)) + +#define ADDOP_INPLACE(C, BINOP) \ + RETURN_IF_FALSE(addop_binary((C), (BINOP), true)) + /* VISIT and VISIT_SEQ takes an ASDL type as their second argument. They use the ASDL name to synthesize the name of the C type and the visit function. */ @@ -3695,77 +3682,60 @@ unaryop(unaryop_ty op) } static int -binop(operator_ty op) -{ - switch (op) { - case Add: - return BINARY_ADD; - case Sub: - return BINARY_SUBTRACT; - case Mult: - return BINARY_MULTIPLY; - case MatMult: - return BINARY_MATRIX_MULTIPLY; - case Div: - return BINARY_TRUE_DIVIDE; - case Mod: - return BINARY_MODULO; - case Pow: - return BINARY_POWER; - case LShift: - return BINARY_LSHIFT; - case RShift: - return BINARY_RSHIFT; - case BitOr: - return BINARY_OR; - case BitXor: - return BINARY_XOR; - case BitAnd: - return BINARY_AND; - case FloorDiv: - return BINARY_FLOOR_DIVIDE; - default: - PyErr_Format(PyExc_SystemError, - "binary op %d should not be possible", op); - return 0; - } -} - -static int -inplace_binop(operator_ty op) +addop_binary(struct compiler *c, operator_ty binop, bool inplace) { - switch (op) { - case Add: - return INPLACE_ADD; - case Sub: - return INPLACE_SUBTRACT; - case Mult: - return INPLACE_MULTIPLY; - case MatMult: - return INPLACE_MATRIX_MULTIPLY; - case Div: - return INPLACE_TRUE_DIVIDE; - case Mod: - return INPLACE_MODULO; - case Pow: - return INPLACE_POWER; - case LShift: - return INPLACE_LSHIFT; - case RShift: - return INPLACE_RSHIFT; - case BitOr: - return INPLACE_OR; - case BitXor: - return INPLACE_XOR; - case BitAnd: - return INPLACE_AND; - case FloorDiv: - return INPLACE_FLOOR_DIVIDE; - default: - PyErr_Format(PyExc_SystemError, - "inplace binary op %d should not be possible", op); - return 0; + int oparg; + switch (binop) { + case Add: + // Addition interacts with sq_concat: + ADDOP(c, inplace ? INPLACE_ADD : BINARY_ADD); + return 1; + case Sub: + oparg = NB_SUBTRACT; + break; + case Mult: + // Multiplication interacts with sq_repeat: + ADDOP(c, inplace ? INPLACE_MULTIPLY : BINARY_MULTIPLY); + return 1; + case MatMult: + oparg = NB_MATRIX_MULTIPLY; + break; + case Div: + oparg = NB_TRUE_DIVIDE; + break; + case Mod: + // Modulation contains a fast path for strings: + ADDOP(c, inplace ? INPLACE_MODULO : BINARY_MODULO); + return 1; + case Pow: + // Exponentiation is techncally ternary: + ADDOP(c, inplace ? INPLACE_POWER : BINARY_POWER); + return 1; + case LShift: + oparg = NB_LSHIFT; + break; + case RShift: + oparg = NB_RSHIFT; + break; + case BitOr: + oparg = NB_OR; + break; + case BitXor: + oparg = NB_XOR; + break; + case BitAnd: + oparg = NB_AND; + break; + case FloorDiv: + oparg = NB_FLOOR_DIVIDE; + break; + default: + PyErr_Format(PyExc_SystemError, "%s op %d should not be possible", + inplace ? "inplace" : "binary", binop); + return 0; } + ADDOP_I(c, inplace ? INPLACE_OP : BINARY_OP, oparg); + return 1; } static int @@ -5356,7 +5326,7 @@ compiler_visit_expr1(struct compiler *c, expr_ty e) case BinOp_kind: VISIT(c, expr, e->v.BinOp.left); VISIT(c, expr, e->v.BinOp.right); - ADDOP(c, binop(e->v.BinOp.op)); + ADDOP_BINARY(c, e->v.BinOp.op); break; case UnaryOp_kind: VISIT(c, expr, e->v.UnaryOp.operand); @@ -5542,7 +5512,7 @@ compiler_augassign(struct compiler *c, stmt_ty s) c->u->u_end_col_offset = old_end_col_offset; VISIT(c, expr, s->v.AugAssign.value); - ADDOP(c, inplace_binop(s->v.AugAssign.op)); + ADDOP_INPLACE(c, s->v.AugAssign.op); SET_LOC(c, e); @@ -6002,7 +5972,7 @@ pattern_helper_sequence_subscr(struct compiler *c, asdl_pattern_seq *patterns, // nonnegative index: ADDOP(c, GET_LEN); ADDOP_LOAD_CONST_NEW(c, PyLong_FromSsize_t(size - i)); - ADDOP(c, BINARY_SUBTRACT); + ADDOP_BINARY(c, Sub); } ADDOP(c, BINARY_SUBSCR); RETURN_IF_FALSE(compiler_pattern_subpattern(c, pattern, pc)); diff --git a/Python/opcode_targets.h b/Python/opcode_targets.h index 93a9e4ab42cfbc..a9096417896115 100644 --- a/Python/opcode_targets.h +++ b/Python/opcode_targets.h @@ -15,39 +15,39 @@ static void *opcode_targets[256] = { &&TARGET_BINARY_ADD_FLOAT, &&TARGET_BINARY_ADD_UNICODE, &&TARGET_UNARY_INVERT, - &&TARGET_BINARY_MATRIX_MULTIPLY, - &&TARGET_INPLACE_MATRIX_MULTIPLY, &&TARGET_BINARY_ADD_UNICODE_INPLACE_FAST, + &&TARGET_BINARY_MULTIPLY_ADAPTIVE, + &&TARGET_BINARY_MULTIPLY_INT, &&TARGET_BINARY_POWER, &&TARGET_BINARY_MULTIPLY, - &&TARGET_BINARY_MULTIPLY_ADAPTIVE, + &&TARGET_BINARY_MULTIPLY_FLOAT, &&TARGET_BINARY_MODULO, &&TARGET_BINARY_ADD, - &&TARGET_BINARY_SUBTRACT, - &&TARGET_BINARY_SUBSCR, - &&TARGET_BINARY_FLOOR_DIVIDE, - &&TARGET_BINARY_TRUE_DIVIDE, - &&TARGET_INPLACE_FLOOR_DIVIDE, - &&TARGET_INPLACE_TRUE_DIVIDE, - &&TARGET_GET_LEN, - &&TARGET_MATCH_MAPPING, - &&TARGET_MATCH_SEQUENCE, - &&TARGET_MATCH_KEYS, - &&TARGET_BINARY_MULTIPLY_INT, - &&TARGET_PUSH_EXC_INFO, - &&TARGET_BINARY_MULTIPLY_FLOAT, - &&TARGET_POP_EXCEPT_AND_RERAISE, &&TARGET_BINARY_SUBSCR_ADAPTIVE, + &&TARGET_BINARY_SUBSCR, &&TARGET_BINARY_SUBSCR_LIST_INT, &&TARGET_BINARY_SUBSCR_TUPLE_INT, &&TARGET_BINARY_SUBSCR_DICT, &&TARGET_CALL_FUNCTION_ADAPTIVE, + &&TARGET_GET_LEN, + &&TARGET_MATCH_MAPPING, + &&TARGET_MATCH_SEQUENCE, + &&TARGET_MATCH_KEYS, &&TARGET_CALL_FUNCTION_BUILTIN_O, + &&TARGET_PUSH_EXC_INFO, &&TARGET_CALL_FUNCTION_BUILTIN_FAST, + &&TARGET_POP_EXCEPT_AND_RERAISE, &&TARGET_CALL_FUNCTION_LEN, &&TARGET_CALL_FUNCTION_ISINSTANCE, &&TARGET_CALL_FUNCTION_PY_SIMPLE, &&TARGET_JUMP_ABSOLUTE_QUICK, + &&TARGET_LOAD_ATTR_ADAPTIVE, + &&TARGET_LOAD_ATTR_INSTANCE_VALUE, + &&TARGET_LOAD_ATTR_WITH_HINT, + &&TARGET_LOAD_ATTR_SLOT, + &&TARGET_LOAD_ATTR_MODULE, + &&TARGET_LOAD_GLOBAL_ADAPTIVE, + &&TARGET_LOAD_GLOBAL_MODULE, &&TARGET_WITH_EXCEPT_START, &&TARGET_GET_AITER, &&TARGET_GET_ANEXT, @@ -55,17 +55,17 @@ static void *opcode_targets[256] = { &&TARGET_BEFORE_WITH, &&TARGET_END_ASYNC_FOR, &&TARGET_INPLACE_ADD, - &&TARGET_INPLACE_SUBTRACT, + &&TARGET_LOAD_GLOBAL_BUILTIN, &&TARGET_INPLACE_MULTIPLY, - &&TARGET_LOAD_ATTR_ADAPTIVE, + &&TARGET_LOAD_METHOD_ADAPTIVE, &&TARGET_INPLACE_MODULO, &&TARGET_STORE_SUBSCR, &&TARGET_DELETE_SUBSCR, - &&TARGET_BINARY_LSHIFT, - &&TARGET_BINARY_RSHIFT, - &&TARGET_BINARY_AND, - &&TARGET_BINARY_XOR, - &&TARGET_BINARY_OR, + &&TARGET_LOAD_METHOD_CACHED, + &&TARGET_LOAD_METHOD_CLASS, + &&TARGET_LOAD_METHOD_MODULE, + &&TARGET_LOAD_METHOD_NO_DICT, + &&TARGET_STORE_ATTR_ADAPTIVE, &&TARGET_INPLACE_POWER, &&TARGET_GET_ITER, &&TARGET_GET_YIELD_FROM_ITER, @@ -74,20 +74,20 @@ static void *opcode_targets[256] = { &&TARGET_YIELD_FROM, &&TARGET_GET_AWAITABLE, &&TARGET_LOAD_ASSERTION_ERROR, - &&TARGET_INPLACE_LSHIFT, - &&TARGET_INPLACE_RSHIFT, - &&TARGET_INPLACE_AND, - &&TARGET_INPLACE_XOR, - &&TARGET_INPLACE_OR, - &&TARGET_LOAD_ATTR_INSTANCE_VALUE, - &&TARGET_LOAD_ATTR_WITH_HINT, + &&TARGET_STORE_ATTR_INSTANCE_VALUE, + &&TARGET_STORE_ATTR_SLOT, + &&TARGET_STORE_ATTR_WITH_HINT, + &&TARGET_LOAD_FAST__LOAD_FAST, + &&TARGET_STORE_FAST__LOAD_FAST, + &&TARGET_LOAD_FAST__LOAD_CONST, + &&TARGET_LOAD_CONST__LOAD_FAST, &&TARGET_LIST_TO_TUPLE, &&TARGET_RETURN_VALUE, &&TARGET_IMPORT_STAR, &&TARGET_SETUP_ANNOTATIONS, &&TARGET_YIELD_VALUE, - &&TARGET_LOAD_ATTR_SLOT, - &&TARGET_LOAD_ATTR_MODULE, + &&TARGET_STORE_FAST__STORE_FAST, + &&_unknown_opcode, &&TARGET_POP_EXCEPT, &&TARGET_STORE_NAME, &&TARGET_DELETE_NAME, @@ -121,44 +121,44 @@ static void *opcode_targets[256] = { &&TARGET_RERAISE, &&TARGET_COPY, &&TARGET_JUMP_IF_NOT_EXC_MATCH, - &&TARGET_LOAD_GLOBAL_ADAPTIVE, - &&TARGET_LOAD_GLOBAL_MODULE, + &&TARGET_BINARY_OP, + &&TARGET_INPLACE_OP, &&TARGET_LOAD_FAST, &&TARGET_STORE_FAST, &&TARGET_DELETE_FAST, - &&TARGET_LOAD_GLOBAL_BUILTIN, - &&TARGET_LOAD_METHOD_ADAPTIVE, + &&_unknown_opcode, + &&_unknown_opcode, &&TARGET_GEN_START, &&TARGET_RAISE_VARARGS, &&TARGET_CALL_FUNCTION, &&TARGET_MAKE_FUNCTION, &&TARGET_BUILD_SLICE, - &&TARGET_LOAD_METHOD_CACHED, + &&_unknown_opcode, &&TARGET_MAKE_CELL, &&TARGET_LOAD_CLOSURE, &&TARGET_LOAD_DEREF, &&TARGET_STORE_DEREF, &&TARGET_DELETE_DEREF, - &&TARGET_LOAD_METHOD_CLASS, + &&_unknown_opcode, &&TARGET_CALL_FUNCTION_KW, &&TARGET_CALL_FUNCTION_EX, - &&TARGET_LOAD_METHOD_MODULE, + &&_unknown_opcode, &&TARGET_EXTENDED_ARG, &&TARGET_LIST_APPEND, &&TARGET_SET_ADD, &&TARGET_MAP_ADD, &&TARGET_LOAD_CLASSDEREF, - &&TARGET_LOAD_METHOD_NO_DICT, - &&TARGET_STORE_ATTR_ADAPTIVE, - &&TARGET_STORE_ATTR_INSTANCE_VALUE, + &&_unknown_opcode, + &&_unknown_opcode, + &&_unknown_opcode, &&TARGET_MATCH_CLASS, - &&TARGET_STORE_ATTR_SLOT, - &&TARGET_STORE_ATTR_WITH_HINT, + &&_unknown_opcode, + &&_unknown_opcode, &&TARGET_FORMAT_VALUE, &&TARGET_BUILD_CONST_KEY_MAP, &&TARGET_BUILD_STRING, - &&TARGET_LOAD_FAST__LOAD_FAST, - &&TARGET_STORE_FAST__LOAD_FAST, + &&_unknown_opcode, + &&_unknown_opcode, &&TARGET_LOAD_METHOD, &&TARGET_CALL_METHOD, &&TARGET_LIST_EXTEND, @@ -166,9 +166,9 @@ static void *opcode_targets[256] = { &&TARGET_DICT_MERGE, &&TARGET_DICT_UPDATE, &&TARGET_CALL_METHOD_KW, - &&TARGET_LOAD_FAST__LOAD_CONST, - &&TARGET_LOAD_CONST__LOAD_FAST, - &&TARGET_STORE_FAST__STORE_FAST, + &&_unknown_opcode, + &&_unknown_opcode, + &&_unknown_opcode, &&_unknown_opcode, &&_unknown_opcode, &&_unknown_opcode, diff --git a/Tools/scripts/generate_opcode_h.py b/Tools/scripts/generate_opcode_h.py index dca9bbcfcbc839..18c39213a4ec43 100644 --- a/Tools/scripts/generate_opcode_h.py +++ b/Tools/scripts/generate_opcode_h.py @@ -84,6 +84,10 @@ def main(opcode_py, outfile='Include/opcode.h'): fobj.write(f"\n || ((op) == {op}) \\") fobj.write("\n )\n") + fobj.write("\n") + for i, (op, _) in enumerate(opcode["_nb_ops"]): + fobj.write(f"#define {op} {i}\n") + fobj.write(footer) From 3a870838f396f8d4b5b3a79e44ed1c4bd9f0ad76 Mon Sep 17 00:00:00 2001 From: Brandt Bucher Date: Wed, 27 Oct 2021 13:45:54 -0700 Subject: [PATCH 02/17] Clean things up a bit --- Include/opcode.h | 328 ++++++++++++++--------------- Lib/dis.py | 4 +- Tools/scripts/generate_opcode_h.py | 17 +- 3 files changed, 175 insertions(+), 174 deletions(-) diff --git a/Include/opcode.h b/Include/opcode.h index 1c7ffd0d34a777..d8393c80cbad46 100644 --- a/Include/opcode.h +++ b/Include/opcode.h @@ -6,162 +6,162 @@ extern "C" { #endif - /* Instruction opcodes for compiled code */ -#define POP_TOP 1 -#define ROT_TWO 2 -#define ROT_THREE 3 -#define DUP_TOP 4 -#define DUP_TOP_TWO 5 -#define ROT_FOUR 6 -#define NOP 9 -#define UNARY_POSITIVE 10 -#define UNARY_NEGATIVE 11 -#define UNARY_NOT 12 -#define UNARY_INVERT 15 -#define BINARY_POWER 19 -#define BINARY_MULTIPLY 20 -#define BINARY_MODULO 22 -#define BINARY_ADD 23 -#define BINARY_SUBSCR 25 -#define GET_LEN 30 -#define MATCH_MAPPING 31 -#define MATCH_SEQUENCE 32 -#define MATCH_KEYS 33 -#define PUSH_EXC_INFO 35 -#define POP_EXCEPT_AND_RERAISE 37 -#define WITH_EXCEPT_START 49 -#define GET_AITER 50 -#define GET_ANEXT 51 -#define BEFORE_ASYNC_WITH 52 -#define BEFORE_WITH 53 -#define END_ASYNC_FOR 54 -#define INPLACE_ADD 55 -#define INPLACE_MULTIPLY 57 -#define INPLACE_MODULO 59 -#define STORE_SUBSCR 60 -#define DELETE_SUBSCR 61 -#define INPLACE_POWER 67 -#define GET_ITER 68 -#define GET_YIELD_FROM_ITER 69 -#define PRINT_EXPR 70 -#define LOAD_BUILD_CLASS 71 -#define YIELD_FROM 72 -#define GET_AWAITABLE 73 -#define LOAD_ASSERTION_ERROR 74 -#define LIST_TO_TUPLE 82 -#define RETURN_VALUE 83 -#define IMPORT_STAR 84 -#define SETUP_ANNOTATIONS 85 -#define YIELD_VALUE 86 -#define POP_EXCEPT 89 -#define HAVE_ARGUMENT 90 -#define STORE_NAME 90 -#define DELETE_NAME 91 -#define UNPACK_SEQUENCE 92 -#define FOR_ITER 93 -#define UNPACK_EX 94 -#define STORE_ATTR 95 -#define DELETE_ATTR 96 -#define STORE_GLOBAL 97 -#define DELETE_GLOBAL 98 -#define ROT_N 99 -#define LOAD_CONST 100 -#define LOAD_NAME 101 -#define BUILD_TUPLE 102 -#define BUILD_LIST 103 -#define BUILD_SET 104 -#define BUILD_MAP 105 -#define LOAD_ATTR 106 -#define COMPARE_OP 107 -#define IMPORT_NAME 108 -#define IMPORT_FROM 109 -#define JUMP_FORWARD 110 -#define JUMP_IF_FALSE_OR_POP 111 -#define JUMP_IF_TRUE_OR_POP 112 -#define JUMP_ABSOLUTE 113 -#define POP_JUMP_IF_FALSE 114 -#define POP_JUMP_IF_TRUE 115 -#define LOAD_GLOBAL 116 -#define IS_OP 117 -#define CONTAINS_OP 118 -#define RERAISE 119 -#define COPY 120 -#define JUMP_IF_NOT_EXC_MATCH 121 -#define BINARY_OP 122 -#define INPLACE_OP 123 -#define LOAD_FAST 124 -#define STORE_FAST 125 -#define DELETE_FAST 126 -#define GEN_START 129 -#define RAISE_VARARGS 130 -#define CALL_FUNCTION 131 -#define MAKE_FUNCTION 132 -#define BUILD_SLICE 133 -#define MAKE_CELL 135 -#define LOAD_CLOSURE 136 -#define LOAD_DEREF 137 -#define STORE_DEREF 138 -#define DELETE_DEREF 139 -#define CALL_FUNCTION_KW 141 -#define CALL_FUNCTION_EX 142 -#define EXTENDED_ARG 144 -#define LIST_APPEND 145 -#define SET_ADD 146 -#define MAP_ADD 147 -#define LOAD_CLASSDEREF 148 -#define MATCH_CLASS 152 -#define FORMAT_VALUE 155 -#define BUILD_CONST_KEY_MAP 156 -#define BUILD_STRING 157 -#define LOAD_METHOD 160 -#define CALL_METHOD 161 -#define LIST_EXTEND 162 -#define SET_UPDATE 163 -#define DICT_MERGE 164 -#define DICT_UPDATE 165 -#define CALL_METHOD_KW 166 -#define BINARY_ADD_ADAPTIVE 7 -#define BINARY_ADD_INT 8 -#define BINARY_ADD_FLOAT 13 -#define BINARY_ADD_UNICODE 14 +/* Instruction opcodes for compiled code */ +#define POP_TOP 1 +#define ROT_TWO 2 +#define ROT_THREE 3 +#define DUP_TOP 4 +#define DUP_TOP_TWO 5 +#define ROT_FOUR 6 +#define NOP 9 +#define UNARY_POSITIVE 10 +#define UNARY_NEGATIVE 11 +#define UNARY_NOT 12 +#define UNARY_INVERT 15 +#define BINARY_POWER 19 +#define BINARY_MULTIPLY 20 +#define BINARY_MODULO 22 +#define BINARY_ADD 23 +#define BINARY_SUBSCR 25 +#define GET_LEN 30 +#define MATCH_MAPPING 31 +#define MATCH_SEQUENCE 32 +#define MATCH_KEYS 33 +#define PUSH_EXC_INFO 35 +#define POP_EXCEPT_AND_RERAISE 37 +#define WITH_EXCEPT_START 49 +#define GET_AITER 50 +#define GET_ANEXT 51 +#define BEFORE_ASYNC_WITH 52 +#define BEFORE_WITH 53 +#define END_ASYNC_FOR 54 +#define INPLACE_ADD 55 +#define INPLACE_MULTIPLY 57 +#define INPLACE_MODULO 59 +#define STORE_SUBSCR 60 +#define DELETE_SUBSCR 61 +#define INPLACE_POWER 67 +#define GET_ITER 68 +#define GET_YIELD_FROM_ITER 69 +#define PRINT_EXPR 70 +#define LOAD_BUILD_CLASS 71 +#define YIELD_FROM 72 +#define GET_AWAITABLE 73 +#define LOAD_ASSERTION_ERROR 74 +#define LIST_TO_TUPLE 82 +#define RETURN_VALUE 83 +#define IMPORT_STAR 84 +#define SETUP_ANNOTATIONS 85 +#define YIELD_VALUE 86 +#define POP_EXCEPT 89 +#define HAVE_ARGUMENT 90 +#define STORE_NAME 90 +#define DELETE_NAME 91 +#define UNPACK_SEQUENCE 92 +#define FOR_ITER 93 +#define UNPACK_EX 94 +#define STORE_ATTR 95 +#define DELETE_ATTR 96 +#define STORE_GLOBAL 97 +#define DELETE_GLOBAL 98 +#define ROT_N 99 +#define LOAD_CONST 100 +#define LOAD_NAME 101 +#define BUILD_TUPLE 102 +#define BUILD_LIST 103 +#define BUILD_SET 104 +#define BUILD_MAP 105 +#define LOAD_ATTR 106 +#define COMPARE_OP 107 +#define IMPORT_NAME 108 +#define IMPORT_FROM 109 +#define JUMP_FORWARD 110 +#define JUMP_IF_FALSE_OR_POP 111 +#define JUMP_IF_TRUE_OR_POP 112 +#define JUMP_ABSOLUTE 113 +#define POP_JUMP_IF_FALSE 114 +#define POP_JUMP_IF_TRUE 115 +#define LOAD_GLOBAL 116 +#define IS_OP 117 +#define CONTAINS_OP 118 +#define RERAISE 119 +#define COPY 120 +#define JUMP_IF_NOT_EXC_MATCH 121 +#define BINARY_OP 122 +#define INPLACE_OP 123 +#define LOAD_FAST 124 +#define STORE_FAST 125 +#define DELETE_FAST 126 +#define GEN_START 129 +#define RAISE_VARARGS 130 +#define CALL_FUNCTION 131 +#define MAKE_FUNCTION 132 +#define BUILD_SLICE 133 +#define MAKE_CELL 135 +#define LOAD_CLOSURE 136 +#define LOAD_DEREF 137 +#define STORE_DEREF 138 +#define DELETE_DEREF 139 +#define CALL_FUNCTION_KW 141 +#define CALL_FUNCTION_EX 142 +#define EXTENDED_ARG 144 +#define LIST_APPEND 145 +#define SET_ADD 146 +#define MAP_ADD 147 +#define LOAD_CLASSDEREF 148 +#define MATCH_CLASS 152 +#define FORMAT_VALUE 155 +#define BUILD_CONST_KEY_MAP 156 +#define BUILD_STRING 157 +#define LOAD_METHOD 160 +#define CALL_METHOD 161 +#define LIST_EXTEND 162 +#define SET_UPDATE 163 +#define DICT_MERGE 164 +#define DICT_UPDATE 165 +#define CALL_METHOD_KW 166 +#define BINARY_ADD_ADAPTIVE 7 +#define BINARY_ADD_INT 8 +#define BINARY_ADD_FLOAT 13 +#define BINARY_ADD_UNICODE 14 #define BINARY_ADD_UNICODE_INPLACE_FAST 16 -#define BINARY_MULTIPLY_ADAPTIVE 17 -#define BINARY_MULTIPLY_INT 18 -#define BINARY_MULTIPLY_FLOAT 21 -#define BINARY_SUBSCR_ADAPTIVE 24 -#define BINARY_SUBSCR_LIST_INT 26 -#define BINARY_SUBSCR_TUPLE_INT 27 -#define BINARY_SUBSCR_DICT 28 -#define CALL_FUNCTION_ADAPTIVE 29 -#define CALL_FUNCTION_BUILTIN_O 34 -#define CALL_FUNCTION_BUILTIN_FAST 36 -#define CALL_FUNCTION_LEN 38 -#define CALL_FUNCTION_ISINSTANCE 39 -#define CALL_FUNCTION_PY_SIMPLE 40 -#define JUMP_ABSOLUTE_QUICK 41 -#define LOAD_ATTR_ADAPTIVE 42 -#define LOAD_ATTR_INSTANCE_VALUE 43 -#define LOAD_ATTR_WITH_HINT 44 -#define LOAD_ATTR_SLOT 45 -#define LOAD_ATTR_MODULE 46 -#define LOAD_GLOBAL_ADAPTIVE 47 -#define LOAD_GLOBAL_MODULE 48 -#define LOAD_GLOBAL_BUILTIN 56 -#define LOAD_METHOD_ADAPTIVE 58 -#define LOAD_METHOD_CACHED 62 -#define LOAD_METHOD_CLASS 63 -#define LOAD_METHOD_MODULE 64 -#define LOAD_METHOD_NO_DICT 65 -#define STORE_ATTR_ADAPTIVE 66 -#define STORE_ATTR_INSTANCE_VALUE 75 -#define STORE_ATTR_SLOT 76 -#define STORE_ATTR_WITH_HINT 77 -#define LOAD_FAST__LOAD_FAST 78 -#define STORE_FAST__LOAD_FAST 79 -#define LOAD_FAST__LOAD_CONST 80 -#define LOAD_CONST__LOAD_FAST 81 -#define STORE_FAST__STORE_FAST 87 -#define DO_TRACING 255 +#define BINARY_MULTIPLY_ADAPTIVE 17 +#define BINARY_MULTIPLY_INT 18 +#define BINARY_MULTIPLY_FLOAT 21 +#define BINARY_SUBSCR_ADAPTIVE 24 +#define BINARY_SUBSCR_LIST_INT 26 +#define BINARY_SUBSCR_TUPLE_INT 27 +#define BINARY_SUBSCR_DICT 28 +#define CALL_FUNCTION_ADAPTIVE 29 +#define CALL_FUNCTION_BUILTIN_O 34 +#define CALL_FUNCTION_BUILTIN_FAST 36 +#define CALL_FUNCTION_LEN 38 +#define CALL_FUNCTION_ISINSTANCE 39 +#define CALL_FUNCTION_PY_SIMPLE 40 +#define JUMP_ABSOLUTE_QUICK 41 +#define LOAD_ATTR_ADAPTIVE 42 +#define LOAD_ATTR_INSTANCE_VALUE 43 +#define LOAD_ATTR_WITH_HINT 44 +#define LOAD_ATTR_SLOT 45 +#define LOAD_ATTR_MODULE 46 +#define LOAD_GLOBAL_ADAPTIVE 47 +#define LOAD_GLOBAL_MODULE 48 +#define LOAD_GLOBAL_BUILTIN 56 +#define LOAD_METHOD_ADAPTIVE 58 +#define LOAD_METHOD_CACHED 62 +#define LOAD_METHOD_CLASS 63 +#define LOAD_METHOD_MODULE 64 +#define LOAD_METHOD_NO_DICT 65 +#define STORE_ATTR_ADAPTIVE 66 +#define STORE_ATTR_INSTANCE_VALUE 75 +#define STORE_ATTR_SLOT 76 +#define STORE_ATTR_WITH_HINT 77 +#define LOAD_FAST__LOAD_FAST 78 +#define STORE_FAST__LOAD_FAST 79 +#define LOAD_FAST__LOAD_CONST 80 +#define LOAD_CONST__LOAD_FAST 81 +#define STORE_FAST__STORE_FAST 87 +#define DO_TRACING 255 #ifdef NEED_OPCODE_JUMP_TABLES static uint32_t _PyOpcode_RelativeJump[8] = { 0U, @@ -189,15 +189,15 @@ static uint32_t _PyOpcode_Jump[8] = { || ((op) == 100) \ ) -#define NB_AND 0 -#define NB_FLOOR_DIVIDE 1 -#define NB_LSHIFT 2 -#define NB_MATRIX_MULTIPLY 3 -#define NB_OR 4 -#define NB_RSHIFT 5 -#define NB_SUBTRACT 6 -#define NB_TRUE_DIVIDE 7 -#define NB_XOR 8 +#define NB_AND 0 +#define NB_FLOOR_DIVIDE 1 +#define NB_LSHIFT 2 +#define NB_MATRIX_MULTIPLY 3 +#define NB_OR 4 +#define NB_RSHIFT 5 +#define NB_SUBTRACT 6 +#define NB_TRUE_DIVIDE 7 +#define NB_XOR 8 #define HAS_ARG(op) ((op) >= HAVE_ARGUMENT) diff --git a/Lib/dis.py b/Lib/dis.py index 49446541c8fca5..c84c8124d6edac 100644 --- a/Lib/dis.py +++ b/Lib/dis.py @@ -30,10 +30,10 @@ LOAD_CONST = opmap['LOAD_CONST'] BINARY_OP = opmap['BINARY_OP'] -BINARY_OPS = {i: name for i, (_, name) in enumerate(_nb_ops)} +BINARY_OPS = [name for _, name in _nb_ops] INPLACE_OP = opmap['INPLACE_OP'] -INPLACE_OPS = {i: f"{name}=" for i, (_, name) in enumerate(_nb_ops)} +INPLACE_OPS = [f"{name}=" for _, name in _nb_ops] del _nb_ops diff --git a/Tools/scripts/generate_opcode_h.py b/Tools/scripts/generate_opcode_h.py index 18c39213a4ec43..0ee4b95bbfdebf 100644 --- a/Tools/scripts/generate_opcode_h.py +++ b/Tools/scripts/generate_opcode_h.py @@ -12,7 +12,7 @@ #endif - /* Instruction opcodes for compiled code */ +/* Instruction opcodes for compiled code */ """.lstrip() footer = """ @@ -28,6 +28,8 @@ #endif /* !Py_OPCODE_H */ """ +DEFINE = "#define {:<31} {:>3}\n" + UINT32_MASK = (1<<32)-1 def write_int_array_from_ops(name, ops, out): @@ -62,17 +64,16 @@ def main(opcode_py, outfile='Include/opcode.h'): fobj.write(header) for name in opcode['opname']: if name in opmap: - fobj.write("#define %-23s %3s\n" % (name, opmap[name])) + fobj.write(DEFINE.format(name, opmap[name])) if name == 'POP_EXCEPT': # Special entry for HAVE_ARGUMENT - fobj.write("#define %-23s %3d\n" % - ('HAVE_ARGUMENT', opcode['HAVE_ARGUMENT'])) + fobj.write(DEFINE.format("HAVE_ARGUMENT", opcode["HAVE_ARGUMENT"])) for name in opcode['_specialized_instructions']: while used[next_op]: next_op += 1 - fobj.write("#define %-23s %3s\n" % (name, next_op)) + fobj.write(DEFINE.format(name, next_op)) used[next_op] = True - fobj.write("#define DO_TRACING 255\n") + fobj.write(DEFINE.format('DO_TRACING', 255)) fobj.write("#ifdef NEED_OPCODE_JUMP_TABLES\n") write_int_array_from_ops("_PyOpcode_RelativeJump", opcode['hasjrel'], fobj) write_int_array_from_ops("_PyOpcode_Jump", opcode['hasjrel'] + opcode['hasjabs'], fobj) @@ -86,12 +87,12 @@ def main(opcode_py, outfile='Include/opcode.h'): fobj.write("\n") for i, (op, _) in enumerate(opcode["_nb_ops"]): - fobj.write(f"#define {op} {i}\n") + fobj.write(DEFINE.format(op, i)) fobj.write(footer) - print("%s regenerated from %s" % (outfile, opcode_py)) + print(f"{outfile} regenerated from {opcode_py}") if __name__ == '__main__': From a2df29ae881bb1acc0c9196ed29187644e02cad0 Mon Sep 17 00:00:00 2001 From: Brandt Bucher Date: Wed, 27 Oct 2021 14:33:00 -0700 Subject: [PATCH 03/17] Improve the new _PyNumber_*Op implementations --- Include/abstract.h | 4 ++-- Objects/abstract.c | 12 +++++++----- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/Include/abstract.h b/Include/abstract.h index d697f68a52fd50..e76e7ffbf98fb5 100644 --- a/Include/abstract.h +++ b/Include/abstract.h @@ -633,9 +633,9 @@ PyAPI_FUNC(PyObject *) PyNumber_InPlaceOr(PyObject *o1, PyObject *o2); If n is not an int object, it is converted with PyNumber_Index first. */ PyAPI_FUNC(PyObject *) PyNumber_ToBase(PyObject *n, int base); -PyAPI_FUNC(PyObject *) _PyNumber_Op(PyObject *o1, PyObject *o2, int op); +PyAPI_FUNC(PyObject *) _PyNumber_Op(PyObject *o1, PyObject *o2, unsigned op); -PyAPI_FUNC(PyObject *) _PyNumber_InPlaceOp(PyObject *o1, PyObject *o2, int op); +PyAPI_FUNC(PyObject *) _PyNumber_InPlaceOp(PyObject *o1, PyObject *o2, unsigned op); /* === Sequence protocol ================================================ */ diff --git a/Objects/abstract.c b/Objects/abstract.c index cb08adb272bfe1..d35d2fa0d60d89 100644 --- a/Objects/abstract.c +++ b/Objects/abstract.c @@ -1716,14 +1716,14 @@ PyNumber_ToBase(PyObject *n, int base) } typedef struct { - const char *iname; const char *name; - size_t islot; size_t slot; + const char *iname; + size_t islot; } nb_info; #define NB_INFO(name, slot) \ - {name "=", name, NB_SLOT(nb_inplace_##slot), NB_SLOT(nb_##slot)} + {name, NB_SLOT(nb_##slot), name "=", NB_SLOT(nb_inplace_##slot)} static nb_info nb_infos[] = { [NB_AND] = NB_INFO("&", and), @@ -1740,15 +1740,17 @@ static nb_info nb_infos[] = { #undef NB_INFO PyObject * -_PyNumber_Op(PyObject *o1, PyObject *o2, int op) +_PyNumber_Op(PyObject *o1, PyObject *o2, unsigned op) { + assert(op < sizeof(nb_infos) / sizeof(nb_info)); nb_info *ni = &nb_infos[op]; return binary_op(o1, o2, ni->slot, ni->name); } PyObject * -_PyNumber_InPlaceOp(PyObject *o1, PyObject *o2, int op) +_PyNumber_InPlaceOp(PyObject *o1, PyObject *o2, unsigned op) { + assert(op < sizeof(nb_infos) / sizeof(nb_info)); nb_info *ni = &nb_infos[op]; return binary_iop(o1, o2, ni->islot, ni->slot, ni->iname); } From 1b8934ebc3972d5ccbe8c034d6dca07402857057 Mon Sep 17 00:00:00 2001 From: Brandt Bucher Date: Wed, 27 Oct 2021 15:06:30 -0700 Subject: [PATCH 04/17] Add docs --- Doc/library/dis.rst | 94 ++++--------------------------------------- Doc/whatsnew/3.11.rst | 6 +++ 2 files changed, 13 insertions(+), 87 deletions(-) diff --git a/Doc/library/dis.rst b/Doc/library/dis.rst index 85cc4afb7e48c0..d1422f18bc53da 100644 --- a/Doc/library/dis.rst +++ b/Doc/library/dis.rst @@ -422,23 +422,6 @@ result back on the stack. Implements ``TOS = TOS1 * TOS``. -.. opcode:: BINARY_MATRIX_MULTIPLY - - Implements ``TOS = TOS1 @ TOS``. - - .. versionadded:: 3.5 - - -.. opcode:: BINARY_FLOOR_DIVIDE - - Implements ``TOS = TOS1 // TOS``. - - -.. opcode:: BINARY_TRUE_DIVIDE - - Implements ``TOS = TOS1 / TOS``. - - .. opcode:: BINARY_MODULO Implements ``TOS = TOS1 % TOS``. @@ -449,9 +432,11 @@ result back on the stack. Implements ``TOS = TOS1 + TOS``. -.. opcode:: BINARY_SUBTRACT +.. opcode:: BINARY_OP (op) - Implements ``TOS = TOS1 - TOS``. + Implements the remaining binary operators (depending on the value of *op*). + + .. versionadded:: 3.11 .. opcode:: BINARY_SUBSCR @@ -459,31 +444,6 @@ result back on the stack. Implements ``TOS = TOS1[TOS]``. -.. opcode:: BINARY_LSHIFT - - Implements ``TOS = TOS1 << TOS``. - - -.. opcode:: BINARY_RSHIFT - - Implements ``TOS = TOS1 >> TOS``. - - -.. opcode:: BINARY_AND - - Implements ``TOS = TOS1 & TOS``. - - -.. opcode:: BINARY_XOR - - Implements ``TOS = TOS1 ^ TOS``. - - -.. opcode:: BINARY_OR - - Implements ``TOS = TOS1 | TOS``. - - **In-place operations** In-place operations are like binary operations, in that they remove TOS and @@ -501,23 +461,6 @@ the original TOS1. Implements in-place ``TOS = TOS1 * TOS``. -.. opcode:: INPLACE_MATRIX_MULTIPLY - - Implements in-place ``TOS = TOS1 @ TOS``. - - .. versionadded:: 3.5 - - -.. opcode:: INPLACE_FLOOR_DIVIDE - - Implements in-place ``TOS = TOS1 // TOS``. - - -.. opcode:: INPLACE_TRUE_DIVIDE - - Implements in-place ``TOS = TOS1 / TOS``. - - .. opcode:: INPLACE_MODULO Implements in-place ``TOS = TOS1 % TOS``. @@ -528,34 +471,11 @@ the original TOS1. Implements in-place ``TOS = TOS1 + TOS``. -.. opcode:: INPLACE_SUBTRACT - - Implements in-place ``TOS = TOS1 - TOS``. - +.. opcode:: INPLACE_OP (op) -.. opcode:: INPLACE_LSHIFT + Implements the remaining in-place operators (depending on the value of *op*). - Implements in-place ``TOS = TOS1 << TOS``. - - -.. opcode:: INPLACE_RSHIFT - - Implements in-place ``TOS = TOS1 >> TOS``. - - -.. opcode:: INPLACE_AND - - Implements in-place ``TOS = TOS1 & TOS``. - - -.. opcode:: INPLACE_XOR - - Implements in-place ``TOS = TOS1 ^ TOS``. - - -.. opcode:: INPLACE_OR - - Implements in-place ``TOS = TOS1 | TOS``. + .. versionadded:: 3.11 .. opcode:: STORE_SUBSCR diff --git a/Doc/whatsnew/3.11.rst b/Doc/whatsnew/3.11.rst index 21ad4669476ef2..58fc124fd06157 100644 --- a/Doc/whatsnew/3.11.rst +++ b/Doc/whatsnew/3.11.rst @@ -304,6 +304,12 @@ Optimizations CPython bytecode changes ======================== +* Replaced most ``BINARY_*`` instructions with a single :opcode:`BINARY_OP` + implementation. + +* Replaced most ``INPLACE_*`` instructions with a single :opcode:`INPLACE_OP` + implementation. + * Added a new :opcode:`CALL_METHOD_KW` opcode. Calls a method in a similar fashion as :opcode:`CALL_METHOD`, but also supports keyword arguments. Works in tandem with :opcode:`LOAD_METHOD`. From 3137973801729fef3d118418c90caf65b29348e0 Mon Sep 17 00:00:00 2001 From: Brandt Bucher Date: Wed, 27 Oct 2021 15:14:43 -0700 Subject: [PATCH 05/17] blurb add --- .../Core and Builtins/2021-10-27-15-14-31.bpo-45636.K2X7QS.rst | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2021-10-27-15-14-31.bpo-45636.K2X7QS.rst diff --git a/Misc/NEWS.d/next/Core and Builtins/2021-10-27-15-14-31.bpo-45636.K2X7QS.rst b/Misc/NEWS.d/next/Core and Builtins/2021-10-27-15-14-31.bpo-45636.K2X7QS.rst new file mode 100644 index 00000000000000..f65f441facac1b --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2021-10-27-15-14-31.bpo-45636.K2X7QS.rst @@ -0,0 +1,2 @@ +Merge most ``BINARY_*`` and ``INPLACE_*`` instructions into unified +:opcode:`BINARY_OP` and :opcode:`INPLACE_OP` implementations. From 99055496e84a44e7e2f3943c5306f023807f955f Mon Sep 17 00:00:00 2001 From: Brandt Bucher Date: Wed, 27 Oct 2021 15:34:05 -0700 Subject: [PATCH 06/17] Fix API leak --- Include/abstract.h | 4 ---- Include/internal/pycore_abstract.h | 3 +++ 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/Include/abstract.h b/Include/abstract.h index e76e7ffbf98fb5..9e06fbbb749138 100644 --- a/Include/abstract.h +++ b/Include/abstract.h @@ -633,10 +633,6 @@ PyAPI_FUNC(PyObject *) PyNumber_InPlaceOr(PyObject *o1, PyObject *o2); If n is not an int object, it is converted with PyNumber_Index first. */ PyAPI_FUNC(PyObject *) PyNumber_ToBase(PyObject *n, int base); -PyAPI_FUNC(PyObject *) _PyNumber_Op(PyObject *o1, PyObject *o2, unsigned op); - -PyAPI_FUNC(PyObject *) _PyNumber_InPlaceOp(PyObject *o1, PyObject *o2, unsigned op); - /* === Sequence protocol ================================================ */ diff --git a/Include/internal/pycore_abstract.h b/Include/internal/pycore_abstract.h index b791bf24321991..724381f6495873 100644 --- a/Include/internal/pycore_abstract.h +++ b/Include/internal/pycore_abstract.h @@ -16,6 +16,9 @@ _PyIndex_Check(PyObject *obj) return (tp_as_number != NULL && tp_as_number->nb_index != NULL); } +PyObject *_PyNumber_Op(PyObject *o1, PyObject *o2, unsigned op); +PyObject *_PyNumber_InPlaceOp(PyObject *o1, PyObject *o2, unsigned op); + #ifdef __cplusplus } #endif From f6970361d8cf80000a08e4e6c7d809bd439c8874 Mon Sep 17 00:00:00 2001 From: Brandt Bucher Date: Thu, 28 Oct 2021 10:57:32 -0700 Subject: [PATCH 07/17] Clean up the nb_info struct --- Objects/abstract.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Objects/abstract.c b/Objects/abstract.c index d35d2fa0d60d89..67d6b161e29ce1 100644 --- a/Objects/abstract.c +++ b/Objects/abstract.c @@ -1716,14 +1716,14 @@ PyNumber_ToBase(PyObject *n, int base) } typedef struct { - const char *name; - size_t slot; - const char *iname; - size_t islot; + const int slot; + const char name[3]; + const int islot; + const char iname[4]; } nb_info; #define NB_INFO(name, slot) \ - {name, NB_SLOT(nb_##slot), name "=", NB_SLOT(nb_inplace_##slot)} + {NB_SLOT(nb_##slot), name, NB_SLOT(nb_inplace_##slot), name "="} static nb_info nb_infos[] = { [NB_AND] = NB_INFO("&", and), From 33f6e7c50a48b04810ed5bd57345652041dc102d Mon Sep 17 00:00:00 2001 From: Brandt Bucher Date: Thu, 28 Oct 2021 11:11:25 -0700 Subject: [PATCH 08/17] Clean up some comments --- Python/ceval.c | 4 ++++ Python/compile.c | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Python/ceval.c b/Python/ceval.c index c0795b0d963a68..12a711e8dd440a 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -2004,6 +2004,10 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr PyObject *divisor = POP(); PyObject *dividend = TOP(); PyObject *res; + // NOTE: This optimization keeps us from rolling modulation into + // BINARY_OP/INPLACE_OP. Once we start specializing those, we can + // get rid of BINARY_MODULO/INPLACE_MODULO and implement this trick + // as a specialized variant. if (PyUnicode_CheckExact(dividend) && ( !PyUnicode_Check(divisor) || PyUnicode_CheckExact(divisor))) { // fast path; string formatting, but not if the RHS is a str subclass diff --git a/Python/compile.c b/Python/compile.c index 1f1b0126cf3ec7..1d2c7859c2607b 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -3708,7 +3708,7 @@ addop_binary(struct compiler *c, operator_ty binop, bool inplace) ADDOP(c, inplace ? INPLACE_MODULO : BINARY_MODULO); return 1; case Pow: - // Exponentiation is techncally ternary: + // Exponentiation is technically ternary: ADDOP(c, inplace ? INPLACE_POWER : BINARY_POWER); return 1; case LShift: From f9e388b33183ab358592f2a9982e6547756725c5 Mon Sep 17 00:00:00 2001 From: Brandt Bucher Date: Fri, 5 Nov 2021 15:51:55 -0700 Subject: [PATCH 09/17] Truly unify all binary ops --- Doc/library/dis.rst | 58 +-- Doc/whatsnew/3.11.rst | 7 +- Include/internal/pycore_code.h | 4 +- Include/opcode.h | 125 ++++--- Lib/dis.py | 5 - Lib/importlib/_bootstrap_external.py | 4 +- Lib/opcode.py | 45 ++- Lib/test/test_compile.py | 20 +- Lib/test/test_dis.py | 18 +- Lib/test/test_peepholer.py | 2 +- .../2021-10-27-15-14-31.bpo-45636.K2X7QS.rst | 4 +- Python/ceval.c | 346 ++++++++---------- Python/compile.c | 53 +-- Python/opcode_targets.h | 78 ++-- Python/specialize.c | 138 +++---- 15 files changed, 386 insertions(+), 521 deletions(-) diff --git a/Doc/library/dis.rst b/Doc/library/dis.rst index d1422f18bc53da..86d93611b8bd7c 100644 --- a/Doc/library/dis.rst +++ b/Doc/library/dis.rst @@ -406,30 +406,16 @@ result back on the stack. .. versionadded:: 3.5 -**Binary operations** +**Binary and in-place operations** Binary operations remove the top of the stack (TOS) and the second top-most stack item (TOS1) from the stack. They perform the operation, and put the result back on the stack. -.. opcode:: BINARY_POWER - - Implements ``TOS = TOS1 ** TOS``. - - -.. opcode:: BINARY_MULTIPLY - - Implements ``TOS = TOS1 * TOS``. - - -.. opcode:: BINARY_MODULO - - Implements ``TOS = TOS1 % TOS``. - - -.. opcode:: BINARY_ADD - - Implements ``TOS = TOS1 + TOS``. +In-place operations are like binary operations, in that they remove TOS and +TOS1, and push the result back on the stack, but the operation is done in-place +when TOS1 supports it, and the resulting TOS may be (but does not have to be) +the original TOS1. .. opcode:: BINARY_OP (op) @@ -444,40 +430,6 @@ result back on the stack. Implements ``TOS = TOS1[TOS]``. -**In-place operations** - -In-place operations are like binary operations, in that they remove TOS and -TOS1, and push the result back on the stack, but the operation is done in-place -when TOS1 supports it, and the resulting TOS may be (but does not have to be) -the original TOS1. - -.. opcode:: INPLACE_POWER - - Implements in-place ``TOS = TOS1 ** TOS``. - - -.. opcode:: INPLACE_MULTIPLY - - Implements in-place ``TOS = TOS1 * TOS``. - - -.. opcode:: INPLACE_MODULO - - Implements in-place ``TOS = TOS1 % TOS``. - - -.. opcode:: INPLACE_ADD - - Implements in-place ``TOS = TOS1 + TOS``. - - -.. opcode:: INPLACE_OP (op) - - Implements the remaining in-place operators (depending on the value of *op*). - - .. versionadded:: 3.11 - - .. opcode:: STORE_SUBSCR Implements ``TOS1[TOS] = TOS2``. diff --git a/Doc/whatsnew/3.11.rst b/Doc/whatsnew/3.11.rst index 58fc124fd06157..c050490ac238aa 100644 --- a/Doc/whatsnew/3.11.rst +++ b/Doc/whatsnew/3.11.rst @@ -304,11 +304,8 @@ Optimizations CPython bytecode changes ======================== -* Replaced most ``BINARY_*`` instructions with a single :opcode:`BINARY_OP` - implementation. - -* Replaced most ``INPLACE_*`` instructions with a single :opcode:`INPLACE_OP` - implementation. +* Replaced all numeric ``BINARY_*`` and ``INPLACE_*`` instructions with a single + :opcode:`BINARY_OP` implementation. * Added a new :opcode:`CALL_METHOD_KW` opcode. Calls a method in a similar fashion as :opcode:`CALL_METHOD`, but also supports keyword arguments. Works diff --git a/Include/internal/pycore_code.h b/Include/internal/pycore_code.h index 622829fccdd0dd..7fe9e74b21cfeb 100644 --- a/Include/internal/pycore_code.h +++ b/Include/internal/pycore_code.h @@ -267,9 +267,9 @@ int _Py_Specialize_StoreAttr(PyObject *owner, _Py_CODEUNIT *instr, PyObject *nam int _Py_Specialize_LoadGlobal(PyObject *globals, PyObject *builtins, _Py_CODEUNIT *instr, PyObject *name, SpecializedCacheEntry *cache); int _Py_Specialize_LoadMethod(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name, SpecializedCacheEntry *cache); int _Py_Specialize_BinarySubscr(PyObject *sub, PyObject *container, _Py_CODEUNIT *instr); -int _Py_Specialize_BinaryAdd(PyObject *left, PyObject *right, _Py_CODEUNIT *instr); -int _Py_Specialize_BinaryMultiply(PyObject *left, PyObject *right, _Py_CODEUNIT *instr); int _Py_Specialize_CallFunction(PyObject *callable, _Py_CODEUNIT *instr, int nargs, SpecializedCacheEntry *cache, PyObject *builtins); +void _Py_Specialize_BinaryOp(PyObject *lhs, PyObject *rhs, _Py_CODEUNIT *instr, + SpecializedCacheEntry *cache); #define PRINT_SPECIALIZATION_STATS 0 #define PRINT_SPECIALIZATION_STATS_DETAILED 0 diff --git a/Include/opcode.h b/Include/opcode.h index d8393c80cbad46..c7354de9a0687c 100644 --- a/Include/opcode.h +++ b/Include/opcode.h @@ -18,10 +18,6 @@ extern "C" { #define UNARY_NEGATIVE 11 #define UNARY_NOT 12 #define UNARY_INVERT 15 -#define BINARY_POWER 19 -#define BINARY_MULTIPLY 20 -#define BINARY_MODULO 22 -#define BINARY_ADD 23 #define BINARY_SUBSCR 25 #define GET_LEN 30 #define MATCH_MAPPING 31 @@ -35,12 +31,8 @@ extern "C" { #define BEFORE_ASYNC_WITH 52 #define BEFORE_WITH 53 #define END_ASYNC_FOR 54 -#define INPLACE_ADD 55 -#define INPLACE_MULTIPLY 57 -#define INPLACE_MODULO 59 #define STORE_SUBSCR 60 #define DELETE_SUBSCR 61 -#define INPLACE_POWER 67 #define GET_ITER 68 #define GET_YIELD_FROM_ITER 69 #define PRINT_EXPR 70 @@ -88,7 +80,6 @@ extern "C" { #define COPY 120 #define JUMP_IF_NOT_EXC_MATCH 121 #define BINARY_OP 122 -#define INPLACE_OP 123 #define LOAD_FAST 124 #define STORE_FAST 125 #define DELETE_FAST 126 @@ -120,47 +111,46 @@ extern "C" { #define DICT_MERGE 164 #define DICT_UPDATE 165 #define CALL_METHOD_KW 166 -#define BINARY_ADD_ADAPTIVE 7 -#define BINARY_ADD_INT 8 -#define BINARY_ADD_FLOAT 13 -#define BINARY_ADD_UNICODE 14 -#define BINARY_ADD_UNICODE_INPLACE_FAST 16 -#define BINARY_MULTIPLY_ADAPTIVE 17 -#define BINARY_MULTIPLY_INT 18 -#define BINARY_MULTIPLY_FLOAT 21 -#define BINARY_SUBSCR_ADAPTIVE 24 -#define BINARY_SUBSCR_LIST_INT 26 -#define BINARY_SUBSCR_TUPLE_INT 27 -#define BINARY_SUBSCR_DICT 28 -#define CALL_FUNCTION_ADAPTIVE 29 -#define CALL_FUNCTION_BUILTIN_O 34 -#define CALL_FUNCTION_BUILTIN_FAST 36 -#define CALL_FUNCTION_LEN 38 -#define CALL_FUNCTION_ISINSTANCE 39 -#define CALL_FUNCTION_PY_SIMPLE 40 -#define JUMP_ABSOLUTE_QUICK 41 -#define LOAD_ATTR_ADAPTIVE 42 -#define LOAD_ATTR_INSTANCE_VALUE 43 -#define LOAD_ATTR_WITH_HINT 44 -#define LOAD_ATTR_SLOT 45 -#define LOAD_ATTR_MODULE 46 -#define LOAD_GLOBAL_ADAPTIVE 47 -#define LOAD_GLOBAL_MODULE 48 -#define LOAD_GLOBAL_BUILTIN 56 -#define LOAD_METHOD_ADAPTIVE 58 -#define LOAD_METHOD_CACHED 62 -#define LOAD_METHOD_CLASS 63 -#define LOAD_METHOD_MODULE 64 -#define LOAD_METHOD_NO_DICT 65 -#define STORE_ATTR_ADAPTIVE 66 -#define STORE_ATTR_INSTANCE_VALUE 75 -#define STORE_ATTR_SLOT 76 -#define STORE_ATTR_WITH_HINT 77 -#define LOAD_FAST__LOAD_FAST 78 -#define STORE_FAST__LOAD_FAST 79 -#define LOAD_FAST__LOAD_CONST 80 -#define LOAD_CONST__LOAD_FAST 81 -#define STORE_FAST__STORE_FAST 87 +#define BINARY_OP_ADAPTIVE 7 +#define BINARY_OP_ADD_INT 8 +#define BINARY_OP_ADD_FLOAT 13 +#define BINARY_OP_ADD_UNICODE 14 +#define BINARY_OP_INPLACE_ADD_UNICODE 16 +#define BINARY_OP_MULTIPLY_INT 17 +#define BINARY_OP_MULTIPLY_FLOAT 18 +#define BINARY_SUBSCR_ADAPTIVE 19 +#define BINARY_SUBSCR_LIST_INT 20 +#define BINARY_SUBSCR_TUPLE_INT 21 +#define BINARY_SUBSCR_DICT 22 +#define CALL_FUNCTION_ADAPTIVE 23 +#define CALL_FUNCTION_BUILTIN_O 24 +#define CALL_FUNCTION_BUILTIN_FAST 26 +#define CALL_FUNCTION_LEN 27 +#define CALL_FUNCTION_ISINSTANCE 28 +#define CALL_FUNCTION_PY_SIMPLE 29 +#define JUMP_ABSOLUTE_QUICK 34 +#define LOAD_ATTR_ADAPTIVE 36 +#define LOAD_ATTR_INSTANCE_VALUE 38 +#define LOAD_ATTR_WITH_HINT 39 +#define LOAD_ATTR_SLOT 40 +#define LOAD_ATTR_MODULE 41 +#define LOAD_GLOBAL_ADAPTIVE 42 +#define LOAD_GLOBAL_MODULE 43 +#define LOAD_GLOBAL_BUILTIN 44 +#define LOAD_METHOD_ADAPTIVE 45 +#define LOAD_METHOD_CACHED 46 +#define LOAD_METHOD_CLASS 47 +#define LOAD_METHOD_MODULE 48 +#define LOAD_METHOD_NO_DICT 55 +#define STORE_ATTR_ADAPTIVE 56 +#define STORE_ATTR_INSTANCE_VALUE 57 +#define STORE_ATTR_SLOT 58 +#define STORE_ATTR_WITH_HINT 59 +#define LOAD_FAST__LOAD_FAST 62 +#define STORE_FAST__LOAD_FAST 63 +#define LOAD_FAST__LOAD_CONST 64 +#define LOAD_CONST__LOAD_FAST 65 +#define STORE_FAST__STORE_FAST 66 #define DO_TRACING 255 #ifdef NEED_OPCODE_JUMP_TABLES static uint32_t _PyOpcode_RelativeJump[8] = { @@ -189,15 +179,32 @@ static uint32_t _PyOpcode_Jump[8] = { || ((op) == 100) \ ) -#define NB_AND 0 -#define NB_FLOOR_DIVIDE 1 -#define NB_LSHIFT 2 -#define NB_MATRIX_MULTIPLY 3 -#define NB_OR 4 -#define NB_RSHIFT 5 -#define NB_SUBTRACT 6 -#define NB_TRUE_DIVIDE 7 -#define NB_XOR 8 +#define NB_ADD 0 +#define NB_AND 1 +#define NB_FLOOR_DIVIDE 2 +#define NB_LSHIFT 3 +#define NB_MATRIX_MULTIPLY 4 +#define NB_MULTIPLY 5 +#define NB_REMAINDER 6 +#define NB_OR 7 +#define NB_POWER 8 +#define NB_RSHIFT 9 +#define NB_SUBTRACT 10 +#define NB_TRUE_DIVIDE 11 +#define NB_XOR 12 +#define NB_INPLACE_ADD 13 +#define NB_INPLACE_AND 14 +#define NB_INPLACE_FLOOR_DIVIDE 15 +#define NB_INPLACE_LSHIFT 16 +#define NB_INPLACE_MATRIX_MULTIPLY 17 +#define NB_INPLACE_MULTIPLY 18 +#define NB_INPLACE_REMAINDER 19 +#define NB_INPLACE_OR 20 +#define NB_INPLACE_POWER 21 +#define NB_INPLACE_RSHIFT 22 +#define NB_INPLACE_SUBTRACT 23 +#define NB_INPLACE_TRUE_DIVIDE 24 +#define NB_INPLACE_XOR 25 #define HAS_ARG(op) ((op) >= HAVE_ARGUMENT) diff --git a/Lib/dis.py b/Lib/dis.py index c84c8124d6edac..498a2ca83f7e22 100644 --- a/Lib/dis.py +++ b/Lib/dis.py @@ -32,9 +32,6 @@ BINARY_OP = opmap['BINARY_OP'] BINARY_OPS = [name for _, name in _nb_ops] -INPLACE_OP = opmap['INPLACE_OP'] -INPLACE_OPS = [f"{name}=" for _, name in _nb_ops] - del _nb_ops def _try_compile(source, name): @@ -457,8 +454,6 @@ def _get_instructions_bytes(code, varname_from_oparg=None, if arg & (1<>"), ("NB_SUBTRACT", "-"), ("NB_TRUE_DIVIDE", "/"), ("NB_XOR", "^"), + ("NB_INPLACE_ADD", "+="), + ("NB_INPLACE_AND", "&="), + ("NB_INPLACE_FLOOR_DIVIDE", "//="), + ("NB_INPLACE_LSHIFT", "<<="), + ("NB_INPLACE_MATRIX_MULTIPLY", "@="), + ("NB_INPLACE_MULTIPLY", "*="), + ("NB_INPLACE_REMAINDER", "%="), + ("NB_INPLACE_OR", "|="), + ("NB_INPLACE_POWER", "**="), + ("NB_INPLACE_RSHIFT", ">>="), + ("NB_INPLACE_SUBTRACT", "-="), + ("NB_INPLACE_TRUE_DIVIDE", "/="), + ("NB_INPLACE_XOR", "^="), ] _specialized_instructions = [ - "BINARY_ADD_ADAPTIVE", - "BINARY_ADD_INT", - "BINARY_ADD_FLOAT", - "BINARY_ADD_UNICODE", - "BINARY_ADD_UNICODE_INPLACE_FAST", - "BINARY_MULTIPLY_ADAPTIVE", - "BINARY_MULTIPLY_INT", - "BINARY_MULTIPLY_FLOAT", + "BINARY_OP_ADAPTIVE", + "BINARY_OP_ADD_INT", + "BINARY_OP_ADD_FLOAT", + "BINARY_OP_ADD_UNICODE", + "BINARY_OP_INPLACE_ADD_UNICODE", + "BINARY_OP_MULTIPLY_INT", + "BINARY_OP_MULTIPLY_FLOAT", "BINARY_SUBSCR_ADAPTIVE", "BINARY_SUBSCR_LIST_INT", "BINARY_SUBSCR_TUPLE_INT", diff --git a/Lib/test/test_compile.py b/Lib/test/test_compile.py index 7819e9a3d56956..33b5ff1b42a618 100644 --- a/Lib/test/test_compile.py +++ b/Lib/test/test_compile.py @@ -1079,12 +1079,12 @@ def test_compiles_to_extended_op_arg(self): compiled_code, _ = self.check_positions_against_ast(snippet) - self.assertOpcodeSourcePositionIs(compiled_code, 'INPLACE_OP', + self.assertOpcodeSourcePositionIs(compiled_code, 'BINARY_OP', line=10_000 + 2, end_line=10_000 + 2, - column=2, end_column=8) - self.assertOpcodeSourcePositionIs(compiled_code, 'INPLACE_ADD', + column=2, end_column=8, occurrence=1) + self.assertOpcodeSourcePositionIs(compiled_code, 'BINARY_OP', line=10_000 + 4, end_line=10_000 + 4, - column=2, end_column=9) + column=2, end_column=9, occurrence=2) def test_multiline_expression(self): snippet = """\ @@ -1112,14 +1112,14 @@ def test_complex_single_line_expression(self): compiled_code, _ = self.check_positions_against_ast(snippet) self.assertOpcodeSourcePositionIs(compiled_code, 'BINARY_SUBSCR', line=1, end_line=1, column=13, end_column=21) - self.assertOpcodeSourcePositionIs(compiled_code, 'BINARY_MULTIPLY', - line=1, end_line=1, column=9, end_column=21) - self.assertOpcodeSourcePositionIs(compiled_code, 'BINARY_ADD', - line=1, end_line=1, column=9, end_column=26) self.assertOpcodeSourcePositionIs(compiled_code, 'BINARY_OP', - line=1, end_line=1, column=4, end_column=27, occurrence=1) + line=1, end_line=1, column=9, end_column=21, occurrence=1) + self.assertOpcodeSourcePositionIs(compiled_code, 'BINARY_OP', + line=1, end_line=1, column=9, end_column=26, occurrence=2) + self.assertOpcodeSourcePositionIs(compiled_code, 'BINARY_OP', + line=1, end_line=1, column=4, end_column=27, occurrence=3) self.assertOpcodeSourcePositionIs(compiled_code, 'BINARY_OP', - line=1, end_line=1, column=0, end_column=27, occurrence=2) + line=1, end_line=1, column=0, end_column=27, occurrence=4) class TestExpressionStackSize(unittest.TestCase): diff --git a/Lib/test/test_dis.py b/Lib/test/test_dis.py index c743851ed97c57..6ee26d2863c11e 100644 --- a/Lib/test/test_dis.py +++ b/Lib/test/test_dis.py @@ -156,7 +156,7 @@ def bug1333982(x=[]): %3d 12 LOAD_CONST 3 (1) -%3d 14 BINARY_ADD +%3d 14 BINARY_OP 0 (+) 16 CALL_FUNCTION 1 18 RAISE_VARARGS 1 """ % (bug1333982.__code__.co_firstlineno + 1, @@ -209,7 +209,7 @@ def bug42562(): dis_expr_str = """\ 1 0 LOAD_NAME 0 (x) 2 LOAD_CONST 0 (1) - 4 BINARY_ADD + 4 BINARY_OP 0 (+) 6 RETURN_VALUE """ @@ -218,7 +218,7 @@ def bug42562(): dis_simple_stmt_str = """\ 1 0 LOAD_NAME 0 (x) 2 LOAD_CONST 0 (1) - 4 BINARY_ADD + 4 BINARY_OP 0 (+) 6 STORE_NAME 0 (x) 8 LOAD_CONST 1 (None) 10 RETURN_VALUE @@ -274,7 +274,7 @@ def bug42562(): 3 >> 6 LOAD_NAME 0 (x) 8 LOAD_CONST 1 (1) - 10 INPLACE_ADD + 10 BINARY_OP 13 (+=) 12 STORE_NAME 0 (x) 2 14 JUMP_ABSOLUTE 3 (to 6) @@ -285,7 +285,7 @@ def bug42562(): %3d 2 LOAD_CONST 1 (1) 4 LOAD_CONST 2 (0) - --> 6 BINARY_OP 7 (/) + --> 6 BINARY_OP 11 (/) 8 POP_TOP %3d 10 LOAD_FAST 1 (tb) @@ -469,7 +469,7 @@ def foo(x): 6 STORE_FAST 1 (z) 8 LOAD_DEREF 2 (x) 10 LOAD_FAST 1 (z) - 12 BINARY_ADD + 12 BINARY_OP 0 (+) 14 LIST_APPEND 2 16 JUMP_ABSOLUTE 2 (to 4) >> 18 RETURN_VALUE @@ -582,7 +582,7 @@ def expected(count, w): s = ['''\ %*d LOAD_FAST 0 (x) %*d LOAD_CONST 1 (1) - %*d BINARY_ADD + %*d BINARY_OP 0 (+) %*d STORE_FAST 0 (x) ''' % (w, 8*i, w, 8*i + 2, w, 8*i + 4, w, 8*i + 6) for i in range(count)] @@ -1060,7 +1060,7 @@ def _prepare_test_cases(): Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=62, starts_line=None, is_jump_target=False, positions=None), Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='i', argrepr='i', offset=64, starts_line=13, is_jump_target=False, positions=None), Instruction(opname='LOAD_CONST', opcode=100, arg=5, argval=1, argrepr='1', offset=66, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='INPLACE_OP', opcode=123, arg=6, argval=6, argrepr='-=', offset=68, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='BINARY_OP', opcode=122, arg=23, argval=23, argrepr='-=', offset=68, starts_line=None, is_jump_target=False, positions=None), Instruction(opname='STORE_FAST', opcode=125, arg=0, argval='i', argrepr='i', offset=70, starts_line=None, is_jump_target=False, positions=None), Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='i', argrepr='i', offset=72, starts_line=14, is_jump_target=False, positions=None), Instruction(opname='LOAD_CONST', opcode=100, arg=3, argval=6, argrepr='6', offset=74, starts_line=None, is_jump_target=False, positions=None), @@ -1081,7 +1081,7 @@ def _prepare_test_cases(): Instruction(opname='NOP', opcode=9, arg=None, argval=None, argrepr='', offset=104, starts_line=20, is_jump_target=True, positions=None), Instruction(opname='LOAD_CONST', opcode=100, arg=5, argval=1, argrepr='1', offset=106, starts_line=21, is_jump_target=False, positions=None), Instruction(opname='LOAD_CONST', opcode=100, arg=7, argval=0, argrepr='0', offset=108, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='BINARY_OP', opcode=122, arg=7, argval=7, argrepr='/', offset=110, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='BINARY_OP', opcode=122, arg=11, argval=11, argrepr='/', offset=110, starts_line=None, is_jump_target=False, positions=None), Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=112, starts_line=None, is_jump_target=False, positions=None), Instruction(opname='JUMP_FORWARD', opcode=110, arg=15, argval=146, argrepr='to 146', offset=114, starts_line=None, is_jump_target=False, positions=None), Instruction(opname='PUSH_EXC_INFO', opcode=35, arg=None, argval=None, argrepr='', offset=116, starts_line=None, is_jump_target=False, positions=None), diff --git a/Lib/test/test_peepholer.py b/Lib/test/test_peepholer.py index 9478c47f7c3b3e..7086a42b095600 100644 --- a/Lib/test/test_peepholer.py +++ b/Lib/test/test_peepholer.py @@ -424,7 +424,7 @@ def f(): def g()->1+1: pass return g - self.assertNotInBytecode(f, 'BINARY_ADD') + self.assertNotInBytecode(f, 'BINARY_OP') self.check_lnotab(f) def test_constant_folding(self): diff --git a/Misc/NEWS.d/next/Core and Builtins/2021-10-27-15-14-31.bpo-45636.K2X7QS.rst b/Misc/NEWS.d/next/Core and Builtins/2021-10-27-15-14-31.bpo-45636.K2X7QS.rst index f65f441facac1b..7db562083496b0 100644 --- a/Misc/NEWS.d/next/Core and Builtins/2021-10-27-15-14-31.bpo-45636.K2X7QS.rst +++ b/Misc/NEWS.d/next/Core and Builtins/2021-10-27-15-14-31.bpo-45636.K2X7QS.rst @@ -1,2 +1,2 @@ -Merge most ``BINARY_*`` and ``INPLACE_*`` instructions into unified -:opcode:`BINARY_OP` and :opcode:`INPLACE_OP` implementations. +Replace all numeric ``BINARY_*`` and ``INPLACE_*`` instructions with a single +:opcode:`BINARY_OP` implementation. diff --git a/Python/ceval.c b/Python/ceval.c index 12a711e8dd440a..5e3721e4eea627 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1919,57 +1919,12 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr DISPATCH(); } - TARGET(BINARY_POWER) { - PyObject *exp = POP(); - PyObject *base = TOP(); - PyObject *res = PyNumber_Power(base, exp, Py_None); - Py_DECREF(base); - Py_DECREF(exp); - SET_TOP(res); - if (res == NULL) - goto error; - DISPATCH(); - } - - TARGET(BINARY_MULTIPLY) { - PREDICTED(BINARY_MULTIPLY); - STAT_INC(BINARY_MULTIPLY, unquickened); - PyObject *right = POP(); - PyObject *left = TOP(); - PyObject *res = PyNumber_Multiply(left, right); - Py_DECREF(left); - Py_DECREF(right); - SET_TOP(res); - if (res == NULL) { - goto error; - } - DISPATCH(); - } - - TARGET(BINARY_MULTIPLY_ADAPTIVE) { - if (oparg == 0) { - PyObject *left = SECOND(); - PyObject *right = TOP(); - next_instr--; - if (_Py_Specialize_BinaryMultiply(left, right, next_instr) < 0) { - goto error; - } - DISPATCH(); - } - else { - STAT_INC(BINARY_MULTIPLY, deferred); - UPDATE_PREV_INSTR_OPARG(next_instr, oparg - 1); - STAT_DEC(BINARY_MULTIPLY, unquickened); - JUMP_TO_INSTRUCTION(BINARY_MULTIPLY); - } - } - - TARGET(BINARY_MULTIPLY_INT) { + TARGET(BINARY_OP_MULTIPLY_INT) { PyObject *left = SECOND(); PyObject *right = TOP(); - DEOPT_IF(!PyLong_CheckExact(left), BINARY_MULTIPLY); - DEOPT_IF(!PyLong_CheckExact(right), BINARY_MULTIPLY); - STAT_INC(BINARY_MULTIPLY, hit); + DEOPT_IF(!PyLong_CheckExact(left), BINARY_OP); + DEOPT_IF(!PyLong_CheckExact(right), BINARY_OP); + STAT_INC(BINARY_OP, hit); PyObject *prod = _PyLong_Multiply((PyLongObject *)left, (PyLongObject *)right); SET_SECOND(prod); Py_DECREF(right); @@ -1981,12 +1936,12 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr DISPATCH(); } - TARGET(BINARY_MULTIPLY_FLOAT) { + TARGET(BINARY_OP_MULTIPLY_FLOAT) { PyObject *left = SECOND(); PyObject *right = TOP(); - DEOPT_IF(!PyFloat_CheckExact(left), BINARY_MULTIPLY); - DEOPT_IF(!PyFloat_CheckExact(right), BINARY_MULTIPLY); - STAT_INC(BINARY_MULTIPLY, hit); + DEOPT_IF(!PyFloat_CheckExact(left), BINARY_OP); + DEOPT_IF(!PyFloat_CheckExact(right), BINARY_OP); + STAT_INC(BINARY_OP, hit); double dprod = ((PyFloatObject *)left)->ob_fval * ((PyFloatObject *)right)->ob_fval; PyObject *prod = PyFloat_FromDouble(dprod); @@ -2000,69 +1955,12 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr DISPATCH(); } - TARGET(BINARY_MODULO) { - PyObject *divisor = POP(); - PyObject *dividend = TOP(); - PyObject *res; - // NOTE: This optimization keeps us from rolling modulation into - // BINARY_OP/INPLACE_OP. Once we start specializing those, we can - // get rid of BINARY_MODULO/INPLACE_MODULO and implement this trick - // as a specialized variant. - if (PyUnicode_CheckExact(dividend) && ( - !PyUnicode_Check(divisor) || PyUnicode_CheckExact(divisor))) { - // fast path; string formatting, but not if the RHS is a str subclass - // (see issue28598) - res = PyUnicode_Format(dividend, divisor); - } else { - res = PyNumber_Remainder(dividend, divisor); - } - Py_DECREF(divisor); - Py_DECREF(dividend); - SET_TOP(res); - if (res == NULL) - goto error; - DISPATCH(); - } - - TARGET(BINARY_ADD) { - PREDICTED(BINARY_ADD); - STAT_INC(BINARY_ADD, unquickened); - PyObject *right = POP(); - PyObject *left = TOP(); - PyObject *sum = PyNumber_Add(left, right); - SET_TOP(sum); - Py_DECREF(left); - Py_DECREF(right); - if (sum == NULL) { - goto error; - } - DISPATCH(); - } - - TARGET(BINARY_ADD_ADAPTIVE) { - if (oparg == 0) { - PyObject *left = SECOND(); - PyObject *right = TOP(); - next_instr--; - if (_Py_Specialize_BinaryAdd(left, right, next_instr) < 0) { - goto error; - } - DISPATCH(); - } - else { - STAT_INC(BINARY_ADD, deferred); - UPDATE_PREV_INSTR_OPARG(next_instr, oparg - 1); - STAT_DEC(BINARY_ADD, unquickened); - JUMP_TO_INSTRUCTION(BINARY_ADD); - } - } - - TARGET(BINARY_ADD_UNICODE) { + TARGET(BINARY_OP_ADD_UNICODE) { PyObject *left = SECOND(); PyObject *right = TOP(); - DEOPT_IF(!PyUnicode_CheckExact(left), BINARY_ADD); - DEOPT_IF(Py_TYPE(right) != Py_TYPE(left), BINARY_ADD); - STAT_INC(BINARY_ADD, hit); + DEOPT_IF(!PyUnicode_CheckExact(left), BINARY_OP); + DEOPT_IF(Py_TYPE(right) != Py_TYPE(left), BINARY_OP); + STAT_INC(BINARY_OP, hit); PyObject *res = PyUnicode_Concat(left, right); STACK_SHRINK(1); SET_TOP(res); @@ -2074,12 +1972,12 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr DISPATCH(); } - TARGET(BINARY_ADD_UNICODE_INPLACE_FAST) { + TARGET(BINARY_OP_INPLACE_ADD_UNICODE) { PyObject *left = SECOND(); PyObject *right = TOP(); - DEOPT_IF(!PyUnicode_CheckExact(left), BINARY_ADD); - DEOPT_IF(Py_TYPE(right) != Py_TYPE(left), BINARY_ADD); - DEOPT_IF(Py_REFCNT(left) != 2, BINARY_ADD); + DEOPT_IF(!PyUnicode_CheckExact(left), BINARY_OP); + DEOPT_IF(Py_TYPE(right) != Py_TYPE(left), BINARY_OP); + DEOPT_IF(Py_REFCNT(left) != 2, BINARY_OP); int next_oparg = _Py_OPARG(*next_instr); assert(_Py_OPCODE(*next_instr) == STORE_FAST); /* In the common case, there are 2 references to the value @@ -2089,8 +1987,8 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr * the refcnt to 1. */ PyObject *var = GETLOCAL(next_oparg); - DEOPT_IF(var != left, BINARY_ADD); - STAT_INC(BINARY_ADD, hit); + DEOPT_IF(var != left, BINARY_OP); + STAT_INC(BINARY_OP, hit); GETLOCAL(next_oparg) = NULL; Py_DECREF(left); STACK_SHRINK(1); @@ -2102,12 +2000,12 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr DISPATCH(); } - TARGET(BINARY_ADD_FLOAT) { + TARGET(BINARY_OP_ADD_FLOAT) { PyObject *left = SECOND(); PyObject *right = TOP(); - DEOPT_IF(!PyFloat_CheckExact(left), BINARY_ADD); - DEOPT_IF(Py_TYPE(right) != Py_TYPE(left), BINARY_ADD); - STAT_INC(BINARY_ADD, hit); + DEOPT_IF(!PyFloat_CheckExact(left), BINARY_OP); + DEOPT_IF(Py_TYPE(right) != Py_TYPE(left), BINARY_OP); + STAT_INC(BINARY_OP, hit); double dsum = ((PyFloatObject *)left)->ob_fval + ((PyFloatObject *)right)->ob_fval; PyObject *sum = PyFloat_FromDouble(dsum); @@ -2121,12 +2019,12 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr DISPATCH(); } - TARGET(BINARY_ADD_INT) { + TARGET(BINARY_OP_ADD_INT) { PyObject *left = SECOND(); PyObject *right = TOP(); - DEOPT_IF(!PyLong_CheckExact(left), BINARY_ADD); - DEOPT_IF(Py_TYPE(right) != Py_TYPE(left), BINARY_ADD); - STAT_INC(BINARY_ADD, hit); + DEOPT_IF(!PyLong_CheckExact(left), BINARY_OP); + DEOPT_IF(Py_TYPE(right) != Py_TYPE(left), BINARY_OP); + STAT_INC(BINARY_OP, hit); PyObject *sum = _PyLong_Add((PyLongObject *)left, (PyLongObject *)right); SET_SECOND(sum); Py_DECREF(right); @@ -2260,61 +2158,6 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr DISPATCH(); } - TARGET(INPLACE_POWER) { - PyObject *exp = POP(); - PyObject *base = TOP(); - PyObject *res = PyNumber_InPlacePower(base, exp, Py_None); - Py_DECREF(base); - Py_DECREF(exp); - SET_TOP(res); - if (res == NULL) - goto error; - DISPATCH(); - } - - TARGET(INPLACE_MULTIPLY) { - PyObject *right = POP(); - PyObject *left = TOP(); - PyObject *res = PyNumber_InPlaceMultiply(left, right); - Py_DECREF(left); - Py_DECREF(right); - SET_TOP(res); - if (res == NULL) - goto error; - DISPATCH(); - } - - TARGET(INPLACE_MODULO) { - PyObject *right = POP(); - PyObject *left = TOP(); - PyObject *mod = PyNumber_InPlaceRemainder(left, right); - Py_DECREF(left); - Py_DECREF(right); - SET_TOP(mod); - if (mod == NULL) - goto error; - DISPATCH(); - } - - TARGET(INPLACE_ADD) { - PyObject *right = POP(); - PyObject *left = TOP(); - PyObject *sum; - if (PyUnicode_CheckExact(left) && PyUnicode_CheckExact(right)) { - sum = unicode_concatenate(tstate, left, right, frame, next_instr); - /* unicode_concatenate consumed the ref to left */ - } - else { - sum = PyNumber_InPlaceAdd(left, right); - Py_DECREF(left); - } - Py_DECREF(right); - SET_TOP(sum); - if (sum == NULL) - goto error; - DISPATCH(); - } - TARGET(STORE_SUBSCR) { PyObject *sub = TOP(); PyObject *container = SECOND(); @@ -4809,28 +4652,130 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr } TARGET(BINARY_OP) { - PyObject *right = POP(); - PyObject *left = TOP(); - PyObject *res = _PyNumber_Op(left, right, oparg); - Py_DECREF(left); - Py_DECREF(right); - SET_TOP(res); + PREDICTED(BINARY_OP); + PyObject *rhs = POP(); + PyObject *lhs = TOP(); + PyObject *res; + switch (oparg) { + case NB_ADD: + res = PyNumber_Add(lhs, rhs); + break; + case NB_AND: + res = PyNumber_And(lhs, rhs); + break; + case NB_FLOOR_DIVIDE: + res = PyNumber_FloorDivide(lhs, rhs); + break; + case NB_LSHIFT: + res = PyNumber_Lshift(lhs, rhs); + break; + case NB_MATRIX_MULTIPLY: + res = PyNumber_MatrixMultiply(lhs, rhs); + break; + case NB_MULTIPLY: + res = PyNumber_Multiply(lhs, rhs); + break; + case NB_REMAINDER: + if (PyUnicode_CheckExact(lhs) && + (!PyUnicode_Check(rhs) || PyUnicode_CheckExact(rhs))) + { + // bpo-28598: Fast path for string formatting (but not + // if the RHS is a str subclass). + res = PyUnicode_Format(lhs, rhs); + break; + } + res = PyNumber_Remainder(lhs, rhs); + break; + case NB_OR: + res = PyNumber_Or(lhs, rhs); + break; + case NB_POWER: + res = PyNumber_Power(lhs, rhs, Py_None); + break; + case NB_RSHIFT: + res = PyNumber_Rshift(lhs, rhs); + break; + case NB_SUBTRACT: + res = PyNumber_Subtract(lhs, rhs); + break; + case NB_TRUE_DIVIDE: + res = PyNumber_TrueDivide(lhs, rhs); + break; + case NB_XOR: + res = PyNumber_Xor(lhs, rhs); + break; + case NB_INPLACE_ADD: + if (PyUnicode_CheckExact(lhs) && PyUnicode_CheckExact(rhs)) + { + Py_INCREF(lhs); // unicode_concatenate steals lhs! + res = unicode_concatenate(tstate, lhs, rhs, frame, + next_instr); + break; + } + res = PyNumber_InPlaceAdd(lhs, rhs); + break; + case NB_INPLACE_AND: + res = PyNumber_InPlaceAnd(lhs, rhs); + break; + case NB_INPLACE_FLOOR_DIVIDE: + res = PyNumber_InPlaceFloorDivide(lhs, rhs); + break; + case NB_INPLACE_LSHIFT: + res = PyNumber_InPlaceLshift(lhs, rhs); + break; + case NB_INPLACE_MATRIX_MULTIPLY: + res = PyNumber_InPlaceMatrixMultiply(lhs, rhs); + break; + case NB_INPLACE_MULTIPLY: + res = PyNumber_InPlaceMultiply(lhs, rhs); + break; + case NB_INPLACE_REMAINDER: + res = PyNumber_InPlaceRemainder(lhs, rhs); + break; + case NB_INPLACE_OR: + res = PyNumber_InPlaceOr(lhs, rhs); + break; + case NB_INPLACE_POWER: + res = PyNumber_InPlacePower(lhs, rhs, Py_None); + break; + case NB_INPLACE_RSHIFT: + res = PyNumber_InPlaceRshift(lhs, rhs); + break; + case NB_INPLACE_SUBTRACT: + res = PyNumber_InPlaceSubtract(lhs, rhs); + break; + case NB_INPLACE_TRUE_DIVIDE: + res = PyNumber_InPlaceTrueDivide(lhs, rhs); + break; + case NB_INPLACE_XOR: + res = PyNumber_InPlaceXor(lhs, rhs); + break; + default: + Py_UNREACHABLE(); + } if (res == NULL) { goto error; } + Py_DECREF(lhs); + Py_DECREF(rhs); + SET_TOP(res); DISPATCH(); } - TARGET(INPLACE_OP) { - PyObject *right = POP(); - PyObject *left = TOP(); - PyObject *res = _PyNumber_InPlaceOp(left, right, oparg); - Py_DECREF(left); - Py_DECREF(right); - SET_TOP(res); - if (res == NULL) { - goto error; - } + TARGET(BINARY_OP_ADAPTIVE) { + assert(cframe.use_tracing == 0); + SpecializedCacheEntry *cache = GET_CACHE(); + if (cache->adaptive.counter) { + cache->adaptive.counter--; + oparg = cache->adaptive.original_oparg; + STAT_INC(BINARY_OP, deferred); + STAT_DEC(BINARY_OP, unquickened); + JUMP_TO_INSTRUCTION(BINARY_OP); + } + PyObject *lhs = SECOND(); + PyObject *rhs = TOP(); + next_instr--; + _Py_Specialize_BinaryOp(lhs, rhs, next_instr, cache); DISPATCH(); } @@ -4939,9 +4884,8 @@ MISS_WITH_CACHE(STORE_ATTR) MISS_WITH_CACHE(LOAD_GLOBAL) MISS_WITH_CACHE(LOAD_METHOD) MISS_WITH_CACHE(CALL_FUNCTION) +MISS_WITH_CACHE(BINARY_OP) MISS_WITH_OPARG_COUNTER(BINARY_SUBSCR) -MISS_WITH_OPARG_COUNTER(BINARY_ADD) -MISS_WITH_OPARG_COUNTER(BINARY_MULTIPLY) binary_subscr_dict_error: { diff --git a/Python/compile.c b/Python/compile.c index 1d2c7859c2607b..35c779fc80780f 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -1021,25 +1021,13 @@ stack_effect(int opcode, int oparg, int jump) case MAP_ADD: return -2; - /* Binary operators */ - case BINARY_POWER: - case BINARY_MULTIPLY: - case BINARY_MODULO: - case BINARY_ADD: case BINARY_SUBSCR: return -1; - - case INPLACE_ADD: - case INPLACE_MULTIPLY: - case INPLACE_MODULO: - return -1; case STORE_SUBSCR: return -3; case DELETE_SUBSCR: return -2; - case INPLACE_POWER: - return -1; case GET_ITER: return 0; @@ -1238,7 +1226,6 @@ stack_effect(int opcode, int oparg, int jump) case COPY: return 1; case BINARY_OP: - case INPLACE_OP: return -1; default: return PY_INVALID_STACK_EFFECT; @@ -3687,54 +3674,50 @@ addop_binary(struct compiler *c, operator_ty binop, bool inplace) int oparg; switch (binop) { case Add: - // Addition interacts with sq_concat: - ADDOP(c, inplace ? INPLACE_ADD : BINARY_ADD); - return 1; + oparg = inplace ? NB_INPLACE_ADD : NB_ADD; + break; case Sub: - oparg = NB_SUBTRACT; + oparg = inplace ? NB_INPLACE_SUBTRACT : NB_SUBTRACT; break; case Mult: - // Multiplication interacts with sq_repeat: - ADDOP(c, inplace ? INPLACE_MULTIPLY : BINARY_MULTIPLY); - return 1; + oparg = inplace ? NB_INPLACE_MULTIPLY : NB_MULTIPLY; + break; case MatMult: - oparg = NB_MATRIX_MULTIPLY; + oparg = inplace ? NB_INPLACE_MATRIX_MULTIPLY : NB_MATRIX_MULTIPLY; break; case Div: - oparg = NB_TRUE_DIVIDE; + oparg = inplace ? NB_INPLACE_TRUE_DIVIDE : NB_TRUE_DIVIDE; break; case Mod: - // Modulation contains a fast path for strings: - ADDOP(c, inplace ? INPLACE_MODULO : BINARY_MODULO); - return 1; + oparg = inplace ? NB_INPLACE_REMAINDER : NB_REMAINDER; + break; case Pow: - // Exponentiation is technically ternary: - ADDOP(c, inplace ? INPLACE_POWER : BINARY_POWER); - return 1; + oparg = inplace ? NB_INPLACE_POWER : NB_POWER; + break; case LShift: - oparg = NB_LSHIFT; + oparg = inplace ? NB_INPLACE_LSHIFT : NB_LSHIFT; break; case RShift: - oparg = NB_RSHIFT; + oparg = inplace ? NB_INPLACE_RSHIFT : NB_RSHIFT; break; case BitOr: - oparg = NB_OR; + oparg = inplace ? NB_INPLACE_OR : NB_OR; break; case BitXor: - oparg = NB_XOR; + oparg = inplace ? NB_INPLACE_XOR : NB_XOR; break; case BitAnd: - oparg = NB_AND; + oparg = inplace ? NB_INPLACE_AND : NB_AND; break; case FloorDiv: - oparg = NB_FLOOR_DIVIDE; + oparg = inplace ? NB_INPLACE_FLOOR_DIVIDE : NB_FLOOR_DIVIDE; break; default: PyErr_Format(PyExc_SystemError, "%s op %d should not be possible", inplace ? "inplace" : "binary", binop); return 0; } - ADDOP_I(c, inplace ? INPLACE_OP : BINARY_OP, oparg); + ADDOP_I(c, BINARY_OP, oparg); return 1; } diff --git a/Python/opcode_targets.h b/Python/opcode_targets.h index a9096417896115..a57617e022e1db 100644 --- a/Python/opcode_targets.h +++ b/Python/opcode_targets.h @@ -6,87 +6,87 @@ static void *opcode_targets[256] = { &&TARGET_DUP_TOP, &&TARGET_DUP_TOP_TWO, &&TARGET_ROT_FOUR, - &&TARGET_BINARY_ADD_ADAPTIVE, - &&TARGET_BINARY_ADD_INT, + &&TARGET_BINARY_OP_ADAPTIVE, + &&TARGET_BINARY_OP_ADD_INT, &&TARGET_NOP, &&TARGET_UNARY_POSITIVE, &&TARGET_UNARY_NEGATIVE, &&TARGET_UNARY_NOT, - &&TARGET_BINARY_ADD_FLOAT, - &&TARGET_BINARY_ADD_UNICODE, + &&TARGET_BINARY_OP_ADD_FLOAT, + &&TARGET_BINARY_OP_ADD_UNICODE, &&TARGET_UNARY_INVERT, - &&TARGET_BINARY_ADD_UNICODE_INPLACE_FAST, - &&TARGET_BINARY_MULTIPLY_ADAPTIVE, - &&TARGET_BINARY_MULTIPLY_INT, - &&TARGET_BINARY_POWER, - &&TARGET_BINARY_MULTIPLY, - &&TARGET_BINARY_MULTIPLY_FLOAT, - &&TARGET_BINARY_MODULO, - &&TARGET_BINARY_ADD, + &&TARGET_BINARY_OP_INPLACE_ADD_UNICODE, + &&TARGET_BINARY_OP_MULTIPLY_INT, + &&TARGET_BINARY_OP_MULTIPLY_FLOAT, &&TARGET_BINARY_SUBSCR_ADAPTIVE, - &&TARGET_BINARY_SUBSCR, &&TARGET_BINARY_SUBSCR_LIST_INT, &&TARGET_BINARY_SUBSCR_TUPLE_INT, &&TARGET_BINARY_SUBSCR_DICT, &&TARGET_CALL_FUNCTION_ADAPTIVE, - &&TARGET_GET_LEN, - &&TARGET_MATCH_MAPPING, - &&TARGET_MATCH_SEQUENCE, - &&TARGET_MATCH_KEYS, &&TARGET_CALL_FUNCTION_BUILTIN_O, - &&TARGET_PUSH_EXC_INFO, + &&TARGET_BINARY_SUBSCR, &&TARGET_CALL_FUNCTION_BUILTIN_FAST, - &&TARGET_POP_EXCEPT_AND_RERAISE, &&TARGET_CALL_FUNCTION_LEN, &&TARGET_CALL_FUNCTION_ISINSTANCE, &&TARGET_CALL_FUNCTION_PY_SIMPLE, + &&TARGET_GET_LEN, + &&TARGET_MATCH_MAPPING, + &&TARGET_MATCH_SEQUENCE, + &&TARGET_MATCH_KEYS, &&TARGET_JUMP_ABSOLUTE_QUICK, + &&TARGET_PUSH_EXC_INFO, &&TARGET_LOAD_ATTR_ADAPTIVE, + &&TARGET_POP_EXCEPT_AND_RERAISE, &&TARGET_LOAD_ATTR_INSTANCE_VALUE, &&TARGET_LOAD_ATTR_WITH_HINT, &&TARGET_LOAD_ATTR_SLOT, &&TARGET_LOAD_ATTR_MODULE, &&TARGET_LOAD_GLOBAL_ADAPTIVE, &&TARGET_LOAD_GLOBAL_MODULE, + &&TARGET_LOAD_GLOBAL_BUILTIN, + &&TARGET_LOAD_METHOD_ADAPTIVE, + &&TARGET_LOAD_METHOD_CACHED, + &&TARGET_LOAD_METHOD_CLASS, + &&TARGET_LOAD_METHOD_MODULE, &&TARGET_WITH_EXCEPT_START, &&TARGET_GET_AITER, &&TARGET_GET_ANEXT, &&TARGET_BEFORE_ASYNC_WITH, &&TARGET_BEFORE_WITH, &&TARGET_END_ASYNC_FOR, - &&TARGET_INPLACE_ADD, - &&TARGET_LOAD_GLOBAL_BUILTIN, - &&TARGET_INPLACE_MULTIPLY, - &&TARGET_LOAD_METHOD_ADAPTIVE, - &&TARGET_INPLACE_MODULO, - &&TARGET_STORE_SUBSCR, - &&TARGET_DELETE_SUBSCR, - &&TARGET_LOAD_METHOD_CACHED, - &&TARGET_LOAD_METHOD_CLASS, - &&TARGET_LOAD_METHOD_MODULE, &&TARGET_LOAD_METHOD_NO_DICT, &&TARGET_STORE_ATTR_ADAPTIVE, - &&TARGET_INPLACE_POWER, - &&TARGET_GET_ITER, - &&TARGET_GET_YIELD_FROM_ITER, - &&TARGET_PRINT_EXPR, - &&TARGET_LOAD_BUILD_CLASS, - &&TARGET_YIELD_FROM, - &&TARGET_GET_AWAITABLE, - &&TARGET_LOAD_ASSERTION_ERROR, &&TARGET_STORE_ATTR_INSTANCE_VALUE, &&TARGET_STORE_ATTR_SLOT, &&TARGET_STORE_ATTR_WITH_HINT, + &&TARGET_STORE_SUBSCR, + &&TARGET_DELETE_SUBSCR, &&TARGET_LOAD_FAST__LOAD_FAST, &&TARGET_STORE_FAST__LOAD_FAST, &&TARGET_LOAD_FAST__LOAD_CONST, &&TARGET_LOAD_CONST__LOAD_FAST, + &&TARGET_STORE_FAST__STORE_FAST, + &&_unknown_opcode, + &&TARGET_GET_ITER, + &&TARGET_GET_YIELD_FROM_ITER, + &&TARGET_PRINT_EXPR, + &&TARGET_LOAD_BUILD_CLASS, + &&TARGET_YIELD_FROM, + &&TARGET_GET_AWAITABLE, + &&TARGET_LOAD_ASSERTION_ERROR, + &&_unknown_opcode, + &&_unknown_opcode, + &&_unknown_opcode, + &&_unknown_opcode, + &&_unknown_opcode, + &&_unknown_opcode, + &&_unknown_opcode, &&TARGET_LIST_TO_TUPLE, &&TARGET_RETURN_VALUE, &&TARGET_IMPORT_STAR, &&TARGET_SETUP_ANNOTATIONS, &&TARGET_YIELD_VALUE, - &&TARGET_STORE_FAST__STORE_FAST, + &&_unknown_opcode, &&_unknown_opcode, &&TARGET_POP_EXCEPT, &&TARGET_STORE_NAME, @@ -122,7 +122,7 @@ static void *opcode_targets[256] = { &&TARGET_COPY, &&TARGET_JUMP_IF_NOT_EXC_MATCH, &&TARGET_BINARY_OP, - &&TARGET_INPLACE_OP, + &&_unknown_opcode, &&TARGET_LOAD_FAST, &&TARGET_STORE_FAST, &&TARGET_DELETE_FAST, diff --git a/Python/specialize.c b/Python/specialize.c index 162728314e1003..d1d9f114ff1c7d 100644 --- a/Python/specialize.c +++ b/Python/specialize.c @@ -124,11 +124,10 @@ _Py_GetSpecializationStats(void) { err += add_stat_dict(stats, LOAD_ATTR, "load_attr"); err += add_stat_dict(stats, LOAD_GLOBAL, "load_global"); err += add_stat_dict(stats, LOAD_METHOD, "load_method"); - err += add_stat_dict(stats, BINARY_ADD, "binary_add"); - err += add_stat_dict(stats, BINARY_MULTIPLY, "binary_multiply"); err += add_stat_dict(stats, BINARY_SUBSCR, "binary_subscr"); err += add_stat_dict(stats, STORE_ATTR, "store_attr"); err += add_stat_dict(stats, CALL_FUNCTION, "call_function"); + err += add_stat_dict(stats, BINARY_OP, "binary_op"); if (err < 0) { Py_DECREF(stats); return NULL; @@ -182,11 +181,10 @@ _Py_PrintSpecializationStats(void) print_stats(out, &_specialization_stats[LOAD_ATTR], "load_attr"); print_stats(out, &_specialization_stats[LOAD_GLOBAL], "load_global"); print_stats(out, &_specialization_stats[LOAD_METHOD], "load_method"); - print_stats(out, &_specialization_stats[BINARY_ADD], "binary_add"); - print_stats(out, &_specialization_stats[BINARY_MULTIPLY], "binary_multiply"); print_stats(out, &_specialization_stats[BINARY_SUBSCR], "binary_subscr"); print_stats(out, &_specialization_stats[STORE_ATTR], "store_attr"); print_stats(out, &_specialization_stats[CALL_FUNCTION], "call_function"); + print_stats(out, &_specialization_stats[BINARY_OP], "binary_op"); if (out != stderr) { fclose(out); } @@ -234,11 +232,10 @@ static uint8_t adaptive_opcodes[256] = { [LOAD_ATTR] = LOAD_ATTR_ADAPTIVE, [LOAD_GLOBAL] = LOAD_GLOBAL_ADAPTIVE, [LOAD_METHOD] = LOAD_METHOD_ADAPTIVE, - [BINARY_ADD] = BINARY_ADD_ADAPTIVE, - [BINARY_MULTIPLY] = BINARY_MULTIPLY_ADAPTIVE, [BINARY_SUBSCR] = BINARY_SUBSCR_ADAPTIVE, [CALL_FUNCTION] = CALL_FUNCTION_ADAPTIVE, [STORE_ATTR] = STORE_ATTR_ADAPTIVE, + [BINARY_OP] = BINARY_OP_ADAPTIVE, }; /* The number of cache entries required for a "family" of instructions. */ @@ -246,11 +243,10 @@ static uint8_t cache_requirements[256] = { [LOAD_ATTR] = 2, /* _PyAdaptiveEntry and _PyAttrCache */ [LOAD_GLOBAL] = 2, /* _PyAdaptiveEntry and _PyLoadGlobalCache */ [LOAD_METHOD] = 3, /* _PyAdaptiveEntry, _PyAttrCache and _PyObjectCache */ - [BINARY_ADD] = 0, - [BINARY_MULTIPLY] = 0, [BINARY_SUBSCR] = 0, [CALL_FUNCTION] = 2, /* _PyAdaptiveEntry and _PyObjectCache/_PyCallCache */ [STORE_ATTR] = 2, /* _PyAdaptiveEntry and _PyAttrCache */ + [BINARY_OP] = 1, // _PyAdaptiveEntry }; /* Return the oparg for the cache_offset and instruction index. @@ -1183,76 +1179,6 @@ _Py_Specialize_BinarySubscr( return 0; } -int -_Py_Specialize_BinaryAdd(PyObject *left, PyObject *right, _Py_CODEUNIT *instr) -{ - PyTypeObject *left_type = Py_TYPE(left); - if (left_type != Py_TYPE(right)) { - SPECIALIZATION_FAIL(BINARY_ADD, SPEC_FAIL_DIFFERENT_TYPES); - goto fail; - } - if (left_type == &PyUnicode_Type) { - int next_opcode = _Py_OPCODE(instr[1]); - if (next_opcode == STORE_FAST) { - *instr = _Py_MAKECODEUNIT(BINARY_ADD_UNICODE_INPLACE_FAST, initial_counter_value()); - } - else { - *instr = _Py_MAKECODEUNIT(BINARY_ADD_UNICODE, initial_counter_value()); - } - goto success; - } - else if (left_type == &PyLong_Type) { - *instr = _Py_MAKECODEUNIT(BINARY_ADD_INT, initial_counter_value()); - goto success; - } - else if (left_type == &PyFloat_Type) { - *instr = _Py_MAKECODEUNIT(BINARY_ADD_FLOAT, initial_counter_value()); - goto success; - - } - else { - SPECIALIZATION_FAIL(BINARY_ADD, SPEC_FAIL_OTHER); - } -fail: - STAT_INC(BINARY_ADD, specialization_failure); - assert(!PyErr_Occurred()); - *instr = _Py_MAKECODEUNIT(_Py_OPCODE(*instr), ADAPTIVE_CACHE_BACKOFF); - return 0; -success: - STAT_INC(BINARY_ADD, specialization_success); - assert(!PyErr_Occurred()); - return 0; -} - -int -_Py_Specialize_BinaryMultiply(PyObject *left, PyObject *right, _Py_CODEUNIT *instr) -{ - if (!Py_IS_TYPE(left, Py_TYPE(right))) { - SPECIALIZATION_FAIL(BINARY_MULTIPLY, SPEC_FAIL_DIFFERENT_TYPES); - goto fail; - } - if (PyLong_CheckExact(left)) { - *instr = _Py_MAKECODEUNIT(BINARY_MULTIPLY_INT, initial_counter_value()); - goto success; - } - else if (PyFloat_CheckExact(left)) { - *instr = _Py_MAKECODEUNIT(BINARY_MULTIPLY_FLOAT, initial_counter_value()); - goto success; - } - else { - SPECIALIZATION_FAIL(BINARY_MULTIPLY, SPEC_FAIL_OTHER); - } -fail: - STAT_INC(BINARY_MULTIPLY, specialization_failure); - assert(!PyErr_Occurred()); - *instr = _Py_MAKECODEUNIT(_Py_OPCODE(*instr), ADAPTIVE_CACHE_BACKOFF); - return 0; -success: - STAT_INC(BINARY_MULTIPLY, specialization_success); - assert(!PyErr_Occurred()); - return 0; -} - static int specialize_class_call( PyObject *callable, _Py_CODEUNIT *instr, @@ -1448,3 +1374,59 @@ _Py_Specialize_CallFunction( } return 0; } + +void +_Py_Specialize_BinaryOp(PyObject *lhs, PyObject *rhs, _Py_CODEUNIT *instr, + SpecializedCacheEntry *cache) +{ + _PyAdaptiveEntry *adaptive = &cache->adaptive; + if (!Py_IS_TYPE(lhs, Py_TYPE(rhs))) { + SPECIALIZATION_FAIL(BINARY_OP, SPEC_FAIL_DIFFERENT_TYPES); + goto failure; + } + switch (adaptive->original_oparg) { + case NB_ADD: + case NB_INPLACE_ADD: + if (PyUnicode_CheckExact(lhs)) { + if (_Py_OPCODE(instr[1]) == STORE_FAST && Py_REFCNT(lhs) == 2) { + *instr = _Py_MAKECODEUNIT(BINARY_OP_INPLACE_ADD_UNICODE, + _Py_OPARG(*instr)); + goto success; + } + *instr = _Py_MAKECODEUNIT(BINARY_OP_ADD_UNICODE, + _Py_OPARG(*instr)); + goto success; + } + if (PyLong_CheckExact(lhs)) { + *instr = _Py_MAKECODEUNIT(BINARY_OP_ADD_INT, _Py_OPARG(*instr)); + goto success; + } + if (PyFloat_CheckExact(lhs)) { + *instr = _Py_MAKECODEUNIT(BINARY_OP_ADD_FLOAT, + _Py_OPARG(*instr)); + goto success; + } + break; + case NB_MULTIPLY: + case NB_INPLACE_MULTIPLY: + if (PyLong_CheckExact(lhs)) { + *instr = _Py_MAKECODEUNIT(BINARY_OP_MULTIPLY_INT, + _Py_OPARG(*instr)); + goto success; + } + if (PyFloat_CheckExact(lhs)) { + *instr = _Py_MAKECODEUNIT(BINARY_OP_MULTIPLY_FLOAT, + _Py_OPARG(*instr)); + goto success; + } + break; + } + SPECIALIZATION_FAIL(BINARY_OP, SPEC_FAIL_OTHER); +failure: + STAT_INC(BINARY_OP, specialization_failure); + cache_backoff(adaptive); + return; +success: + STAT_INC(BINARY_OP, specialization_success); + adaptive->counter = initial_counter_value(); +} \ No newline at end of file From f5b192360c12e351d9b5796f6d20115a11d87ccb Mon Sep 17 00:00:00 2001 From: Brandt Bucher Date: Fri, 5 Nov 2021 17:00:29 -0700 Subject: [PATCH 10/17] Fix BINARY_OP stats --- Python/ceval.c | 1 + 1 file changed, 1 insertion(+) diff --git a/Python/ceval.c b/Python/ceval.c index 5e3721e4eea627..67bfd05c68af4d 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -4653,6 +4653,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr TARGET(BINARY_OP) { PREDICTED(BINARY_OP); + STAT_INC(BINARY_OP, unquickened); PyObject *rhs = POP(); PyObject *lhs = TOP(); PyObject *res; From 2a8072c745343e2d5ede326fd34a7b5b8c470ebe Mon Sep 17 00:00:00 2001 From: Brandt Bucher Date: Fri, 5 Nov 2021 17:02:24 -0700 Subject: [PATCH 11/17] Don't defer operators that can't be specialized --- Python/specialize.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Python/specialize.c b/Python/specialize.c index d1d9f114ff1c7d..3bedd9f82b7ad1 100644 --- a/Python/specialize.c +++ b/Python/specialize.c @@ -1420,6 +1420,8 @@ _Py_Specialize_BinaryOp(PyObject *lhs, PyObject *rhs, _Py_CODEUNIT *instr, goto success; } break; + default: + *instr = _Py_MAKECODEUNIT(BINARY_OP, adaptive->original_oparg); } SPECIALIZATION_FAIL(BINARY_OP, SPEC_FAIL_OTHER); failure: From 6c6b78f5cc63e9ab587f60b36ae5a6172cf9a242 Mon Sep 17 00:00:00 2001 From: Brandt Bucher Date: Mon, 8 Nov 2021 15:11:21 -0800 Subject: [PATCH 12/17] Remove some old changes --- Doc/library/dis.rst | 3 +- Include/internal/pycore_abstract.h | 3 -- Lib/importlib/_bootstrap_external.py | 4 +-- Lib/test/test_compile.py | 2 +- Objects/abstract.c | 41 ---------------------------- 5 files changed, 5 insertions(+), 48 deletions(-) diff --git a/Doc/library/dis.rst b/Doc/library/dis.rst index 86d93611b8bd7c..4a804044df000a 100644 --- a/Doc/library/dis.rst +++ b/Doc/library/dis.rst @@ -420,7 +420,8 @@ the original TOS1. .. opcode:: BINARY_OP (op) - Implements the remaining binary operators (depending on the value of *op*). + Implements the binary and in-place operators (depending on the value of + *op*). .. versionadded:: 3.11 diff --git a/Include/internal/pycore_abstract.h b/Include/internal/pycore_abstract.h index 724381f6495873..b791bf24321991 100644 --- a/Include/internal/pycore_abstract.h +++ b/Include/internal/pycore_abstract.h @@ -16,9 +16,6 @@ _PyIndex_Check(PyObject *obj) return (tp_as_number != NULL && tp_as_number->nb_index != NULL); } -PyObject *_PyNumber_Op(PyObject *o1, PyObject *o2, unsigned op); -PyObject *_PyNumber_InPlaceOp(PyObject *o1, PyObject *o2, unsigned op); - #ifdef __cplusplus } #endif diff --git a/Lib/importlib/_bootstrap_external.py b/Lib/importlib/_bootstrap_external.py index f1c9c2a2eae18e..f6f54af68488fd 100644 --- a/Lib/importlib/_bootstrap_external.py +++ b/Lib/importlib/_bootstrap_external.py @@ -366,7 +366,7 @@ def _write_atomic(path, data, mode=0o666): # Python 3.11a1 3461 (JUMP_ABSOLUTE must jump backwards) # Python 3.11a2 3462 (bpo-44511: remove COPY_DICT_WITHOUT_KEYS, change # MATCH_CLASS and MATCH_KEYS, and add COPY) -# Python 3.11a3 3464 (Merge numeric BINARY_*/INPLACE_* into BINARY_OP) +# Python 3.11a3 3463 (Merge numeric BINARY_*/INPLACE_* into BINARY_OP) # # MAGIC must change whenever the bytecode emitted by the compiler may no @@ -376,7 +376,7 @@ def _write_atomic(path, data, mode=0o666): # Whenever MAGIC_NUMBER is changed, the ranges in the magic_values array # in PC/launcher.c must also be updated. -MAGIC_NUMBER = (3464).to_bytes(2, 'little') + b'\r\n' +MAGIC_NUMBER = (3463).to_bytes(2, 'little') + b'\r\n' _RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c _PYCACHE = '__pycache__' diff --git a/Lib/test/test_compile.py b/Lib/test/test_compile.py index 33b5ff1b42a618..f72c7ca68f363a 100644 --- a/Lib/test/test_compile.py +++ b/Lib/test/test_compile.py @@ -1053,7 +1053,7 @@ def assertOpcodeSourcePositionIs(self, code, opcode, for instr, position in zip(dis.Bytecode(code), code.co_positions()): if instr.opname == opcode: occurrence -= 1 - if occurrence == 0: + if not occurrence: self.assertEqual(position[0], line) self.assertEqual(position[1], end_line) self.assertEqual(position[2], column) diff --git a/Objects/abstract.c b/Objects/abstract.c index 67d6b161e29ce1..6227ad5a18bb95 100644 --- a/Objects/abstract.c +++ b/Objects/abstract.c @@ -1,7 +1,6 @@ /* Abstract Object Interface (many thanks to Jim Fulton) */ #include "Python.h" -#include "opcode.h" #include "pycore_abstract.h" // _PyIndex_Check() #include "pycore_call.h" // _PyObject_CallNoArgs() #include "pycore_ceval.h" // _Py_EnterRecursiveCall() @@ -1715,46 +1714,6 @@ PyNumber_ToBase(PyObject *n, int base) return res; } -typedef struct { - const int slot; - const char name[3]; - const int islot; - const char iname[4]; -} nb_info; - -#define NB_INFO(name, slot) \ - {NB_SLOT(nb_##slot), name, NB_SLOT(nb_inplace_##slot), name "="} - -static nb_info nb_infos[] = { - [NB_AND] = NB_INFO("&", and), - [NB_FLOOR_DIVIDE] = NB_INFO("//", floor_divide), - [NB_LSHIFT] = NB_INFO("<<", lshift), - [NB_MATRIX_MULTIPLY] = NB_INFO("@", matrix_multiply), - [NB_OR] = NB_INFO("|", or), - [NB_RSHIFT] = NB_INFO(">>", rshift), - [NB_SUBTRACT] = NB_INFO("-", subtract), - [NB_TRUE_DIVIDE] = NB_INFO("/", true_divide), - [NB_XOR] = NB_INFO("^", xor), -}; - -#undef NB_INFO - -PyObject * -_PyNumber_Op(PyObject *o1, PyObject *o2, unsigned op) -{ - assert(op < sizeof(nb_infos) / sizeof(nb_info)); - nb_info *ni = &nb_infos[op]; - return binary_op(o1, o2, ni->slot, ni->name); -} - -PyObject * -_PyNumber_InPlaceOp(PyObject *o1, PyObject *o2, unsigned op) -{ - assert(op < sizeof(nb_infos) / sizeof(nb_info)); - nb_info *ni = &nb_infos[op]; - return binary_iop(o1, o2, ni->islot, ni->slot, ni->iname); -} - /* Operations on sequences */ From 51c4b5f999b07ab1cd7c3267d8650d60ff642590 Mon Sep 17 00:00:00 2001 From: Brandt Bucher Date: Mon, 8 Nov 2021 15:34:07 -0800 Subject: [PATCH 13/17] Clarify behavior for non-specializing operators --- Python/specialize.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Python/specialize.c b/Python/specialize.c index 3bedd9f82b7ad1..7e72013cd8359a 100644 --- a/Python/specialize.c +++ b/Python/specialize.c @@ -1421,6 +1421,9 @@ _Py_Specialize_BinaryOp(PyObject *lhs, PyObject *rhs, _Py_CODEUNIT *instr, } break; default: + // These operators don't have any available specializations. Rather + // than repeatedly attempting to specialize them, just convert them + // back to BINARY_OP (while still recording a failure, of course)! *instr = _Py_MAKECODEUNIT(BINARY_OP, adaptive->original_oparg); } SPECIALIZATION_FAIL(BINARY_OP, SPEC_FAIL_OTHER); @@ -1431,4 +1434,4 @@ _Py_Specialize_BinaryOp(PyObject *lhs, PyObject *rhs, _Py_CODEUNIT *instr, success: STAT_INC(BINARY_OP, specialization_success); adaptive->counter = initial_counter_value(); -} \ No newline at end of file +} From f0d2056d30e25b0c94c93ad9675bc389e93972e6 Mon Sep 17 00:00:00 2001 From: Brandt Bucher Date: Mon, 8 Nov 2021 15:36:16 -0800 Subject: [PATCH 14/17] Clean up dis.py a bit --- Lib/dis.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/Lib/dis.py b/Lib/dis.py index 498a2ca83f7e22..a817d938a8d87c 100644 --- a/Lib/dis.py +++ b/Lib/dis.py @@ -28,11 +28,7 @@ MAKE_FUNCTION_FLAGS = ('defaults', 'kwdefaults', 'annotations', 'closure') LOAD_CONST = opmap['LOAD_CONST'] - BINARY_OP = opmap['BINARY_OP'] -BINARY_OPS = [name for _, name in _nb_ops] - -del _nb_ops def _try_compile(source, name): """Attempts to compile the given source, first as an expression and @@ -453,7 +449,7 @@ def _get_instructions_bytes(code, varname_from_oparg=None, argrepr = ', '.join(s for i, s in enumerate(MAKE_FUNCTION_FLAGS) if arg & (1< Date: Mon, 8 Nov 2021 18:29:36 -0800 Subject: [PATCH 15/17] Remove trailing whitespace --- .../Core and Builtins/2021-10-27-15-14-31.bpo-45636.K2X7QS.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Core and Builtins/2021-10-27-15-14-31.bpo-45636.K2X7QS.rst b/Misc/NEWS.d/next/Core and Builtins/2021-10-27-15-14-31.bpo-45636.K2X7QS.rst index 7db562083496b0..ca687687e79f28 100644 --- a/Misc/NEWS.d/next/Core and Builtins/2021-10-27-15-14-31.bpo-45636.K2X7QS.rst +++ b/Misc/NEWS.d/next/Core and Builtins/2021-10-27-15-14-31.bpo-45636.K2X7QS.rst @@ -1,2 +1,2 @@ -Replace all numeric ``BINARY_*`` and ``INPLACE_*`` instructions with a single +Replace all numeric ``BINARY_*`` and ``INPLACE_*`` instructions with a single :opcode:`BINARY_OP` implementation. From 8d23dada412c9f0684e2a585bd6315bf5fee9f93 Mon Sep 17 00:00:00 2001 From: Brandt Bucher Date: Tue, 9 Nov 2021 17:27:02 -0800 Subject: [PATCH 16/17] Apply feedback from code review --- Python/ceval.c | 79 ++++++-------------------------------------------- 1 file changed, 9 insertions(+), 70 deletions(-) diff --git a/Python/ceval.c b/Python/ceval.c index 67bfd05c68af4d..443e18d97469e8 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -92,8 +92,6 @@ static PyObject * import_from(PyThreadState *, PyObject *, PyObject *); static int import_all_from(PyThreadState *, PyObject *, PyObject *); static void format_exc_check_arg(PyThreadState *, PyObject *, const char *, PyObject *); static void format_exc_unbound(PyThreadState *tstate, PyCodeObject *co, int oparg); -static PyObject * unicode_concatenate(PyThreadState *, PyObject *, PyObject *, - InterpreterFrame *, const _Py_CODEUNIT *); static int check_args_iterable(PyThreadState *, PyObject *func, PyObject *vararg); static void format_kwargs_error(PyThreadState *, PyObject *func, PyObject *kwargs); static void format_awaitable_error(PyThreadState *, PyTypeObject *, int, int); @@ -4706,13 +4704,6 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr res = PyNumber_Xor(lhs, rhs); break; case NB_INPLACE_ADD: - if (PyUnicode_CheckExact(lhs) && PyUnicode_CheckExact(rhs)) - { - Py_INCREF(lhs); // unicode_concatenate steals lhs! - res = unicode_concatenate(tstate, lhs, rhs, frame, - next_instr); - break; - } res = PyNumber_InPlaceAdd(lhs, rhs); break; case NB_INPLACE_AND: @@ -4766,18 +4757,20 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr TARGET(BINARY_OP_ADAPTIVE) { assert(cframe.use_tracing == 0); SpecializedCacheEntry *cache = GET_CACHE(); - if (cache->adaptive.counter) { + if (cache->adaptive.counter == 0) { + PyObject *lhs = SECOND(); + PyObject *rhs = TOP(); + next_instr--; + _Py_Specialize_BinaryOp(lhs, rhs, next_instr, cache); + DISPATCH(); + } + else { + STAT_INC(BINARY_OP, deferred); cache->adaptive.counter--; oparg = cache->adaptive.original_oparg; - STAT_INC(BINARY_OP, deferred); STAT_DEC(BINARY_OP, unquickened); JUMP_TO_INSTRUCTION(BINARY_OP); } - PyObject *lhs = SECOND(); - PyObject *rhs = TOP(); - next_instr--; - _Py_Specialize_BinaryOp(lhs, rhs, next_instr, cache); - DISPATCH(); } TARGET(EXTENDED_ARG) { @@ -6901,60 +6894,6 @@ format_awaitable_error(PyThreadState *tstate, PyTypeObject *type, int prevprevop } } -static PyObject * -unicode_concatenate(PyThreadState *tstate, PyObject *v, PyObject *w, - InterpreterFrame *frame, const _Py_CODEUNIT *next_instr) -{ - PyObject *res; - if (Py_REFCNT(v) == 2) { - /* In the common case, there are 2 references to the value - * stored in 'variable' when the += is performed: one on the - * value stack (in 'v') and one still stored in the - * 'variable'. We try to delete the variable now to reduce - * the refcnt to 1. - */ - int opcode, oparg; - NEXTOPARG(); - next_instr++; - switch (opcode) { - case STORE_FAST: - { - if (GETLOCAL(oparg) == v) - SETLOCAL(oparg, NULL); - break; - } - case STORE_DEREF: - { - PyObject *c = _PyFrame_GetLocalsArray(frame)[oparg]; - if (PyCell_GET(c) == v) { - PyCell_SET(c, NULL); - Py_DECREF(v); - } - break; - } - case STORE_NAME: - { - PyObject *names = frame->f_code->co_names; - PyObject *name = GETITEM(names, oparg); - PyObject *locals = frame->f_locals; - if (locals && PyDict_CheckExact(locals)) { - PyObject *w = PyDict_GetItemWithError(locals, name); - if ((w == v && PyDict_DelItem(locals, name) != 0) || - (w == NULL && _PyErr_Occurred(tstate))) - { - Py_DECREF(v); - return NULL; - } - } - break; - } - } - } - res = v; - PyUnicode_Append(&res, w); - return res; -} - #ifdef DYNAMIC_EXECUTION_PROFILE static PyObject * From bea2cff70fd29adadbff9b7c5b841a8b6e8671dc Mon Sep 17 00:00:00 2001 From: Brandt Bucher Date: Wed, 10 Nov 2021 12:55:49 -0800 Subject: [PATCH 17/17] Fix a refleak during error handling --- Python/ceval.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Python/ceval.c b/Python/ceval.c index 443e18d97469e8..d61325324d4414 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -4745,12 +4745,12 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr default: Py_UNREACHABLE(); } - if (res == NULL) { - goto error; - } Py_DECREF(lhs); Py_DECREF(rhs); SET_TOP(res); + if (res == NULL) { + goto error; + } DISPATCH(); }