[Python-checkins] bpo-45636: Merge all numeric operators (GH-29482)

brandtbucher webhook-mailer at python.org
Thu Nov 11 01:56:36 EST 2021


https://github.com/python/cpython/commit/9178f533ff5ea7462a2ca22cfa67afd78dad433b
commit: 9178f533ff5ea7462a2ca22cfa67afd78dad433b
branch: main
author: Brandt Bucher <brandt at python.org>
committer: brandtbucher <brandtbucher at gmail.com>
date: 2021-11-10T22:56:22-08:00
summary:

bpo-45636: Merge all numeric operators (GH-29482)

files:
A Misc/NEWS.d/next/Core and Builtins/2021-10-27-15-14-31.bpo-45636.K2X7QS.rst
M Doc/library/dis.rst
M Doc/whatsnew/3.11.rst
M Include/internal/pycore_code.h
M Include/opcode.h
M Lib/dis.py
M Lib/importlib/_bootstrap_external.py
M Lib/opcode.py
M Lib/test/test_compile.py
M Lib/test/test_dis.py
M Lib/test/test_peepholer.py
M Python/ceval.c
M Python/compile.c
M Python/opcode_targets.h
M Python/specialize.c
M Tools/scripts/generate_opcode_h.py

diff --git a/Doc/library/dis.rst b/Doc/library/dis.rst
index 85cc4afb7e48c..4a804044df000 100644
--- a/Doc/library/dis.rst
+++ b/Doc/library/dis.rst
@@ -406,156 +406,29 @@ 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_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``.
-
-
-.. opcode:: BINARY_ADD
-
-   Implements ``TOS = TOS1 + TOS``.
-
-
-.. opcode:: BINARY_SUBTRACT
-
-   Implements ``TOS = TOS1 - TOS``.
-
-
-.. opcode:: BINARY_SUBSCR
-
-   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
 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_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
+.. opcode:: BINARY_OP (op)
 
-   Implements in-place ``TOS = TOS1 % TOS``.
+   Implements the binary and in-place operators (depending on the value of
+   *op*).
 
-
-.. opcode:: INPLACE_ADD
-
-   Implements in-place ``TOS = TOS1 + TOS``.
-
-
-.. opcode:: INPLACE_SUBTRACT
-
-   Implements in-place ``TOS = TOS1 - TOS``.
-
-
-.. opcode:: INPLACE_LSHIFT
-
-   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``.
+   .. versionadded:: 3.11
 
 
-.. opcode:: INPLACE_OR
+.. opcode:: BINARY_SUBSCR
 
-   Implements in-place ``TOS = TOS1 | TOS``.
+   Implements ``TOS = TOS1[TOS]``.
 
 
 .. opcode:: STORE_SUBSCR
diff --git a/Doc/whatsnew/3.11.rst b/Doc/whatsnew/3.11.rst
index c0a6ce4c35ad8..59a230f26ab42 100644
--- a/Doc/whatsnew/3.11.rst
+++ b/Doc/whatsnew/3.11.rst
@@ -315,6 +315,9 @@ Optimizations
 CPython bytecode changes
 ========================
 
+* 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
   in tandem with :opcode:`LOAD_METHOD`.
diff --git a/Include/internal/pycore_code.h b/Include/internal/pycore_code.h
index 622829fccdd0d..7fe9e74b21cfe 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 87ed32c019909..c7354de9a0687 100644
--- a/Include/opcode.h
+++ b/Include/opcode.h
@@ -6,178 +6,152 @@ 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_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
-#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_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
-#define PRINT_EXPR               70
-#define LOAD_BUILD_CLASS         71
-#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
-#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 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  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 DO_TRACING              255
+/* 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_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 STORE_SUBSCR                     60
+#define DELETE_SUBSCR                    61
+#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 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_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] = {
     0U,
@@ -205,6 +179,33 @@ static uint32_t _PyOpcode_Jump[8] = {
     || ((op) == 100) \
     )
 
+#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)
 
 /* Reserve some bytecodes for internal use in the compiler.
diff --git a/Lib/dis.py b/Lib/dis.py
index 8b429b5b72522..ac0c6e7f04c45 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",
@@ -27,6 +28,7 @@
 MAKE_FUNCTION_FLAGS = ('defaults', 'kwdefaults', 'annotations', 'closure')
 
 LOAD_CONST = opmap['LOAD_CONST']
+BINARY_OP = opmap['BINARY_OP']
 
 def _try_compile(source, name):
     """Attempts to compile the given source, first as an expression and
@@ -446,6 +448,8 @@ 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<<i))
+            elif op == BINARY_OP:
+                _, argrepr = _nb_ops[arg]
         yield Instruction(opname[op], op,
                           arg, argval, argrepr,
                           offset, starts_line, is_jump_target, positions)
diff --git a/Lib/importlib/_bootstrap_external.py b/Lib/importlib/_bootstrap_external.py
index e492dc22d911e..f6f54af68488f 100644
--- a/Lib/importlib/_bootstrap_external.py
+++ b/Lib/importlib/_bootstrap_external.py
@@ -366,6 +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 3463 (Merge numeric BINARY_*/INPLACE_* into BINARY_OP)
 
 #
 # MAGIC must change whenever the bytecode emitted by the compiler may no
diff --git a/Lib/opcode.py b/Lib/opcode.py
index 66d5ca766bec6..940e169d5597d 100644
--- a/Lib/opcode.py
+++ b/Lib/opcode.py
@@ -67,20 +67,9 @@ def jabs_op(name, op):
 def_op('UNARY_NOT', 12)
 
 def_op('UNARY_INVERT', 15)
-def_op('BINARY_MATRIX_MULTIPLY', 16)
-def_op('INPLACE_MATRIX_MULTIPLY', 17)
 
-def_op('BINARY_POWER', 19)
-def_op('BINARY_MULTIPLY', 20)
-
-def_op('BINARY_MODULO', 22)
-def_op('BINARY_ADD', 23)
-def_op('BINARY_SUBTRACT', 24)
 def_op('BINARY_SUBSCR', 25)
-def_op('BINARY_FLOOR_DIVIDE', 26)
-def_op('BINARY_TRUE_DIVIDE', 27)
-def_op('INPLACE_FLOOR_DIVIDE', 28)
-def_op('INPLACE_TRUE_DIVIDE', 29)
+
 def_op('GET_LEN', 30)
 def_op('MATCH_MAPPING', 31)
 def_op('MATCH_SEQUENCE', 32)
@@ -95,21 +84,11 @@ def jabs_op(name, op):
 def_op('GET_ANEXT', 51)
 def_op('BEFORE_ASYNC_WITH', 52)
 def_op('BEFORE_WITH', 53)
-
 def_op('END_ASYNC_FOR', 54)
-def_op('INPLACE_ADD', 55)
-def_op('INPLACE_SUBTRACT', 56)
-def_op('INPLACE_MULTIPLY', 57)
 
-def_op('INPLACE_MODULO', 59)
 def_op('STORE_SUBSCR', 60)
 def_op('DELETE_SUBSCR', 61)
-def_op('BINARY_LSHIFT', 62)
-def_op('BINARY_RSHIFT', 63)
-def_op('BINARY_AND', 64)
-def_op('BINARY_XOR', 65)
-def_op('BINARY_OR', 66)
-def_op('INPLACE_POWER', 67)
+
 def_op('GET_ITER', 68)
 def_op('GET_YIELD_FROM_ITER', 69)
 def_op('PRINT_EXPR', 70)
@@ -117,11 +96,6 @@ def jabs_op(name, op):
 def_op('YIELD_FROM', 72)
 def_op('GET_AWAITABLE', 73)
 def_op('LOAD_ASSERTION_ERROR', 74)
-def_op('INPLACE_LSHIFT', 75)
-def_op('INPLACE_RSHIFT', 76)
-def_op('INPLACE_AND', 77)
-def_op('INPLACE_XOR', 78)
-def_op('INPLACE_OR', 79)
 
 def_op('LIST_TO_TUPLE', 82)
 def_op('RETURN_VALUE', 83)
@@ -167,6 +141,7 @@ def jabs_op(name, op):
 def_op('RERAISE', 119)
 def_op('COPY', 120)
 jabs_op('JUMP_IF_NOT_EXC_MATCH', 121)
+def_op('BINARY_OP', 122)
 
 def_op('LOAD_FAST', 124)        # Local variable number
 haslocal.append(124)
@@ -219,15 +194,43 @@ def jabs_op(name, op):
 
 del def_op, name_op, jrel_op, jabs_op
 
+_nb_ops = [
+    ("NB_ADD", "+"),
+    ("NB_AND", "&"),
+    ("NB_FLOOR_DIVIDE", "//"),
+    ("NB_LSHIFT", "<<"),
+    ("NB_MATRIX_MULTIPLY", "@"),
+    ("NB_MULTIPLY", "*"),
+    ("NB_REMAINDER", "%"),
+    ("NB_OR", "|"),
+    ("NB_POWER", "**"),
+    ("NB_RSHIFT", ">>"),
+    ("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 495a223fc3da7..f72c7ca68f363 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 not occurrence:
+                    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,12 +1079,12 @@ 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, '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 = """\
@@ -1110,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_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=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=4)
 
 
 class TestExpressionStackSize(unittest.TestCase):
diff --git a/Lib/test/test_dis.py b/Lib/test/test_dis.py
index d0435e494d1e7..3b8ebb5dba95b 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,
@@ -226,7 +226,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
 """
 
@@ -235,7 +235,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
@@ -291,7 +291,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)
@@ -302,7 +302,7 @@ def bug42562():
 
 %3d           2 LOAD_CONST               1 (1)
               4 LOAD_CONST               2 (0)
-    -->       6 BINARY_TRUE_DIVIDE
+    -->       6 BINARY_OP               11 (/)
               8 POP_TOP
 
 %3d          10 LOAD_FAST                1 (tb)
@@ -485,7 +485,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
@@ -602,7 +602,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)]
@@ -1080,7 +1080,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='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),
@@ -1101,7 +1101,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=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=14, argval=144, argrepr='to 144', 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 9478c47f7c3b3..7086a42b09560 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
new file mode 100644
index 0000000000000..ca687687e79f2
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2021-10-27-15-14-31.bpo-45636.K2X7QS.rst	
@@ -0,0 +1,2 @@
+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 fadb97adefb2b..030d98396780b 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);
@@ -1921,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);
@@ -1983,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);
@@ -2002,101 +1955,12 @@ _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();
-            PyObject *res;
-            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);
@@ -2108,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
@@ -2123,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);
@@ -2136,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);
@@ -2155,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);
@@ -2172,18 +2036,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);
@@ -2282,66 +2134,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);
@@ -2366,169 +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_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();
-            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(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();
@@ -5032,6 +4661,130 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr
             DISPATCH();
         }
 
+        TARGET(BINARY_OP) {
+            PREDICTED(BINARY_OP);
+            STAT_INC(BINARY_OP, unquickened);
+            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:
+                    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();
+            }
+            Py_DECREF(lhs);
+            Py_DECREF(rhs);
+            SET_TOP(res);
+            if (res == NULL) {
+                goto error;
+            }
+            DISPATCH();
+        }
+
+        TARGET(BINARY_OP_ADAPTIVE) {
+            assert(cframe.use_tracing == 0);
+            SpecializedCacheEntry *cache = GET_CACHE();
+            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_DEC(BINARY_OP, unquickened);
+                JUMP_TO_INSTRUCTION(BINARY_OP);
+            }
+        }
+
         TARGET(EXTENDED_ARG) {
             int oldoparg = oparg;
             NEXTOPARG();
@@ -5137,9 +4890,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:
         {
@@ -7173,60 +6925,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 *
diff --git a/Python/compile.c b/Python/compile.c
index 47889abe61223..8135f18755980 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);
 
 
@@ -1022,40 +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_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:
             return -3;
         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:
             return 0;
 
@@ -1063,12 +1035,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 +1225,8 @@ stack_effect(int opcode, int oparg, int jump)
             return 0;
         case COPY:
             return 1;
+        case BINARY_OP:
+            return -1;
         default:
             return PY_INVALID_STACK_EFFECT;
     }
@@ -1685,6 +1653,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.
 */
@@ -3693,77 +3667,56 @@ 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:
+            oparg = inplace ? NB_INPLACE_ADD : NB_ADD;
+            break;
+        case Sub:
+            oparg = inplace ? NB_INPLACE_SUBTRACT : NB_SUBTRACT;
+            break;
+        case Mult:
+            oparg = inplace ? NB_INPLACE_MULTIPLY : NB_MULTIPLY;
+            break;
+        case MatMult:
+            oparg = inplace ? NB_INPLACE_MATRIX_MULTIPLY : NB_MATRIX_MULTIPLY;
+            break;
+        case Div:
+            oparg = inplace ? NB_INPLACE_TRUE_DIVIDE : NB_TRUE_DIVIDE;
+            break;
+        case Mod:
+            oparg = inplace ? NB_INPLACE_REMAINDER : NB_REMAINDER;
+            break;
+        case Pow:
+            oparg = inplace ? NB_INPLACE_POWER : NB_POWER;
+            break;
+        case LShift:
+            oparg = inplace ? NB_INPLACE_LSHIFT : NB_LSHIFT;
+            break;
+        case RShift:
+            oparg = inplace ? NB_INPLACE_RSHIFT : NB_RSHIFT;
+            break;
+        case BitOr:
+            oparg = inplace ? NB_INPLACE_OR : NB_OR;
+            break;
+        case BitXor:
+            oparg = inplace ? NB_INPLACE_XOR : NB_XOR;
+            break;
+        case BitAnd:
+            oparg = inplace ? NB_INPLACE_AND : NB_AND;
+            break;
+        case FloorDiv:
+            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, BINARY_OP, oparg);
+    return 1;
 }
 
 static int
@@ -5354,7 +5307,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);
@@ -5540,7 +5493,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);
 
@@ -6000,7 +5953,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 93a9e4ab42cfb..a57617e022e1d 100644
--- a/Python/opcode_targets.h
+++ b/Python/opcode_targets.h
@@ -6,67 +6,67 @@ 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_MATRIX_MULTIPLY,
-    &&TARGET_INPLACE_MATRIX_MULTIPLY,
-    &&TARGET_BINARY_ADD_UNICODE_INPLACE_FAST,
-    &&TARGET_BINARY_POWER,
-    &&TARGET_BINARY_MULTIPLY,
-    &&TARGET_BINARY_MULTIPLY_ADAPTIVE,
-    &&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_OP_INPLACE_ADD_UNICODE,
+    &&TARGET_BINARY_OP_MULTIPLY_INT,
+    &&TARGET_BINARY_OP_MULTIPLY_FLOAT,
     &&TARGET_BINARY_SUBSCR_ADAPTIVE,
     &&TARGET_BINARY_SUBSCR_LIST_INT,
     &&TARGET_BINARY_SUBSCR_TUPLE_INT,
     &&TARGET_BINARY_SUBSCR_DICT,
     &&TARGET_CALL_FUNCTION_ADAPTIVE,
     &&TARGET_CALL_FUNCTION_BUILTIN_O,
+    &&TARGET_BINARY_SUBSCR,
     &&TARGET_CALL_FUNCTION_BUILTIN_FAST,
     &&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_INPLACE_SUBTRACT,
-    &&TARGET_INPLACE_MULTIPLY,
-    &&TARGET_LOAD_ATTR_ADAPTIVE,
-    &&TARGET_INPLACE_MODULO,
+    &&TARGET_LOAD_METHOD_NO_DICT,
+    &&TARGET_STORE_ATTR_ADAPTIVE,
+    &&TARGET_STORE_ATTR_INSTANCE_VALUE,
+    &&TARGET_STORE_ATTR_SLOT,
+    &&TARGET_STORE_ATTR_WITH_HINT,
     &&TARGET_STORE_SUBSCR,
     &&TARGET_DELETE_SUBSCR,
-    &&TARGET_BINARY_LSHIFT,
-    &&TARGET_BINARY_RSHIFT,
-    &&TARGET_BINARY_AND,
-    &&TARGET_BINARY_XOR,
-    &&TARGET_BINARY_OR,
-    &&TARGET_INPLACE_POWER,
+    &&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,
@@ -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,
+    &&_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_LOAD_ATTR_SLOT,
-    &&TARGET_LOAD_ATTR_MODULE,
+    &&_unknown_opcode,
+    &&_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,
+    &&_unknown_opcode,
     &&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/Python/specialize.c b/Python/specialize.c
index 162728314e100..7e72013cd8359 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,64 @@ _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;
+        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);
+failure:
+    STAT_INC(BINARY_OP, specialization_failure);
+    cache_backoff(adaptive);
+    return;
+success:
+    STAT_INC(BINARY_OP, specialization_success);
+    adaptive->counter = initial_counter_value();
+}
diff --git a/Tools/scripts/generate_opcode_h.py b/Tools/scripts/generate_opcode_h.py
index dca9bbcfcbc83..0ee4b95bbfdeb 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)
@@ -84,10 +85,14 @@ 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(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__':



More information about the Python-checkins mailing list