Skip to content
Prev Previous commit
Next Next commit
Use slot offsets for opargs
  • Loading branch information
brandtbucher committed Oct 31, 2021
commit befdc3f914e0b2828d67951b119bb6a8abcd69bf
2 changes: 1 addition & 1 deletion Include/cpython/object.h
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ typedef struct {
binaryfunc nb_xor;
binaryfunc nb_or;
unaryfunc nb_int;
void *nb_reserved; /* the slot formerly known as nb_long */
unaryfunc nb_reserved; /* the slot formerly known as nb_long */
unaryfunc nb_float;

binaryfunc nb_inplace_add;
Expand Down
71 changes: 62 additions & 9 deletions Include/opcode.h

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

15 changes: 6 additions & 9 deletions Lib/dis.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

from opcode import *
from opcode import __all__ as _opcodes_all
from opcode import _nb_ops
from opcode import _nb_slots

__all__ = ["code_info", "dis", "disassemble", "distb", "disco",
"findlinestarts", "findlabels", "show_code",
Expand All @@ -30,12 +30,11 @@
LOAD_CONST = opmap['LOAD_CONST']

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
NB_NAMES = {i: name for i, _, name in _nb_slots}

del _nb_slots

def _try_compile(source, name):
"""Attempts to compile the given source, first as an expression and
Expand Down Expand Up @@ -455,10 +454,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 = BINARY_OPS[arg]
elif op == INPLACE_OP:
argrepr = INPLACE_OPS[arg]
elif op == BINARY_OP or op == INPLACE_OP:
argrepr = NB_NAMES[arg]
yield Instruction(opname[op], op,
arg, argval, argrepr,
offset, starts_line, is_jump_target, positions)
Expand Down
47 changes: 37 additions & 10 deletions Lib/opcode.py
Original file line number Diff line number Diff line change
Expand Up @@ -205,16 +205,43 @@ def jabs_op(name, op):

del def_op, name_op, jrel_op, jabs_op

_nb_ops = [
("NB_AND", "&"),
("NB_FLOOR_DIVIDE", "//"),
("NB_LSHIFT", "<<"),
("NB_MATRIX_MULTIPLY", "@"),
("NB_OR", "|"),
("NB_RSHIFT", ">>"),
("NB_SUBTRACT", "-"),
("NB_TRUE_DIVIDE", "/"),
("NB_XOR", "^"),
_nb_slots = [
Comment thread
brandtbucher marked this conversation as resolved.
Outdated
# nb_add
(1, "nb_subtract", "-"),
# nb_multiply
# nb_remainder
# nb_divmod
# nb_power
# nb_negative
# nb_positive
# nb_absolute
# nb_bool
# nb_invert
(11, "nb_lshift", "<<"),
(12, "nb_rshift", ">>"),
(13, "nb_and", "&"),
(14, "nb_xor", "^"),
(15, "nb_or", "|"),
# nb_int
# nb_reserved
# nb_float
# nb_inplace_add
(20, "nb_inplace_subtract", "-="),
# nb_inplace_multiply
# nb_inplace_remainder
# nb_inplace_power
(24, "nb_inplace_lshift", "<<="),
(25, "nb_inplace_rshift", ">>="),
(26, "nb_inplace_and", "&="),
(27, "nb_inplace_xor", "^="),
(28, "nb_inplace_or", "|="),
(29, "nb_floor_divide", "//"),
(30, "nb_true_divide", "/"),
(31, "nb_inplace_floor_divide", "//="),
(32, "nb_inplace_true_divide", "/="),
# nb_index
(34, "nb_matrix_multiply", "@"),
(35, "nb_inplace_matrix_multiply", "@="),
]

_specialized_instructions = [
Expand Down
6 changes: 3 additions & 3 deletions Lib/test/test_dis.py
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@ def bug42562():

%3d 2 LOAD_CONST 1 (1)
4 LOAD_CONST 2 (0)
--> 6 BINARY_OP 7 (/)
--> 6 BINARY_OP 30 (/)
8 POP_TOP

%3d 10 LOAD_FAST 1 (tb)
Expand Down Expand Up @@ -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='INPLACE_OP', opcode=123, arg=20, argval=20, 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),
Expand All @@ -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=30, argval=30, 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),
Expand Down
61 changes: 34 additions & 27 deletions Objects/abstract.c
Original file line number Diff line number Diff line change
Expand Up @@ -1715,44 +1715,51 @@ 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),
#define NB_NAMES(op) \
[NB_##op] = NB_##op##_NAME, \
[NB_INPLACE_##op] = NB_INPLACE_##op##_NAME

static const char nb_names[][4] = {
NB_NAMES(AND),
NB_NAMES(FLOOR_DIVIDE),
NB_NAMES(LSHIFT),
NB_NAMES(MATRIX_MULTIPLY),
NB_NAMES(OR),
NB_NAMES(RSHIFT),
NB_NAMES(SUBTRACT),
NB_NAMES(TRUE_DIVIDE),
NB_NAMES(XOR),
};

#undef NB_INFO
#undef NB_NAMES

#define NB_BINSLOT(op) [NB_INPLACE_##op] = NB_##op

static const uint8_t nb_binary_slots[] = {
NB_BINSLOT(AND),
NB_BINSLOT(FLOOR_DIVIDE),
NB_BINSLOT(LSHIFT),
NB_BINSLOT(MATRIX_MULTIPLY),
NB_BINSLOT(OR),
NB_BINSLOT(RSHIFT),
NB_BINSLOT(SUBTRACT),
NB_BINSLOT(TRUE_DIVIDE),
NB_BINSLOT(XOR),
};

#undef NB_BINSLOT

PyObject *
_PyNumber_Op(PyObject *o1, PyObject *o2, unsigned op)
Comment thread
brandtbucher marked this conversation as resolved.
Outdated
{
assert(op < sizeof(nb_infos) / sizeof(nb_info));
nb_info *ni = &nb_infos[op];
return binary_op(o1, o2, ni->slot, ni->name);
return binary_op(o1, o2, op * NB_SCALE, nb_names[op]);
}

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);
return binary_iop(o1, o2, op * NB_SCALE, nb_binary_slots[op] * NB_SCALE,
nb_names[op]);
}


Expand Down
19 changes: 10 additions & 9 deletions Python/compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -3684,24 +3684,25 @@ unaryop(unaryop_ty op)
static int
addop_binary(struct compiler *c, operator_ty binop, bool inplace)
{
assert(HAVE_SANE_NB_OFFSETS);
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;
oparg = inplace ? NB_INPLACE_SUBTRACT : 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;
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:
Comment thread
brandtbucher marked this conversation as resolved.
Expand All @@ -3712,22 +3713,22 @@ addop_binary(struct compiler *c, operator_ty binop, bool inplace)
ADDOP(c, inplace ? INPLACE_POWER : BINARY_POWER);
return 1;
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",
Expand Down
18 changes: 16 additions & 2 deletions Tools/scripts/generate_opcode_h.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,15 @@
extern "C" {
#endif

#include <stddef.h>


/* Instruction opcodes for compiled code */
""".lstrip()

footer = """
#define NB_SCALE offsetof(PyNumberMethods, nb_subtract)

#define HAS_ARG(op) ((op) >= HAVE_ARGUMENT)

/* Reserve some bytecodes for internal use in the compiler.
Expand Down Expand Up @@ -86,8 +90,18 @@ def main(opcode_py, outfile='Include/opcode.h'):
fobj.write("\n )\n")

fobj.write("\n")
for i, (op, _) in enumerate(opcode["_nb_ops"]):
fobj.write(DEFINE.format(op, i))
for i, slot, _ in opcode["_nb_slots"]:
fobj.write(DEFINE.format(slot.upper(), i))

fobj.write("\n")
for _, slot, name in opcode["_nb_slots"]:
fobj.write(DEFINE.format(f"{slot.upper()}_NAME", f'"{name}"'))

fobj.write("\n")
fobj.write("#define HAVE_SANE_NB_OFFSETS ( \\\n")
for _, slot, _ in opcode["_nb_slots"]:
fobj.write(f" {slot.upper()} * NB_SCALE == offsetof(PyNumberMethods, {slot}) && \\\n")
fobj.write(" true)\n")

fobj.write(footer)

Expand Down