Added LOAD_METHOD, CALL_METHOD opcodes

these were added in Python 3.7a1, so bytecode magic number has been
updated

refer python/cpython@f2392133eb
This commit is contained in:
ahgamut 2021-11-19 08:51:54 +05:30
parent 2b53efd719
commit 7328c09cd4
11 changed files with 245 additions and 5 deletions

View file

@ -125,6 +125,8 @@ COSMOPOLITAN_C_START_
#define BUILD_CONST_KEY_MAP 156 #define BUILD_CONST_KEY_MAP 156
#define BUILD_STRING 157 #define BUILD_STRING 157
#define BUILD_TUPLE_UNPACK_WITH_CALL 158 #define BUILD_TUPLE_UNPACK_WITH_CALL 158
#define LOAD_METHOD 160
#define CALL_METHOD 161
/* EXCEPT_HANDLER is a special, implicit block type which is created when /* EXCEPT_HANDLER is a special, implicit block type which is created when
entering an except handler. It is not an opcode but we define it here entering an except handler. It is not an opcode but we define it here

View file

@ -240,6 +240,7 @@ _code_type = type(_write_atomic.__code__)
# Python 3.6b1 3377 (set __class__ cell from type.__new__ #23722) # Python 3.6b1 3377 (set __class__ cell from type.__new__ #23722)
# Python 3.6b2 3378 (add BUILD_TUPLE_UNPACK_WITH_CALL #28257) # Python 3.6b2 3378 (add BUILD_TUPLE_UNPACK_WITH_CALL #28257)
# Python 3.6rc1 3379 (more thorough __class__ validation #23722) # Python 3.6rc1 3379 (more thorough __class__ validation #23722)
# Python 3.7a1 3390 (add LOAD_METHOD and CALL_METHOD opcodes #26110)
# #
# MAGIC must change whenever the bytecode emitted by the compiler may no # MAGIC must change whenever the bytecode emitted by the compiler may no
# longer be understood by older implementations of the eval loop (usually # longer be understood by older implementations of the eval loop (usually
@ -248,7 +249,7 @@ _code_type = type(_write_atomic.__code__)
# Whenever MAGIC_NUMBER is changed, the ranges in the magic_values array # Whenever MAGIC_NUMBER is changed, the ranges in the magic_values array
# in PC/launcher.c must also be updated. # in PC/launcher.c must also be updated.
MAGIC_NUMBER = (3379).to_bytes(2, 'little') + b'\r\n' MAGIC_NUMBER = (3390).to_bytes(2, 'little') + b'\r\n'
_RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c _RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c
_PYCACHE = '__pycache__' _PYCACHE = '__pycache__'

View file

@ -208,4 +208,7 @@ def_op('BUILD_CONST_KEY_MAP', 156)
def_op('BUILD_STRING', 157) def_op('BUILD_STRING', 157)
def_op('BUILD_TUPLE_UNPACK_WITH_CALL', 158) def_op('BUILD_TUPLE_UNPACK_WITH_CALL', 158)
name_op('LOAD_METHOD', 160)
def_op('CALL_METHOD', 161)
del def_op, name_op, jrel_op, jabs_op del def_op, name_op, jrel_op, jabs_op

View file

@ -203,6 +203,30 @@ three.
Traceback (most recent call last): Traceback (most recent call last):
SyntaxError: more than 255 arguments SyntaxError: more than 255 arguments
>>> class C:
... def meth(self, *args):
... return args
>>> obj = C()
>>> obj.meth(
... 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
... 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37,
... 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55,
... 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73,
... 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91,
... 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107,
... 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121,
... 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135,
... 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149,
... 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163,
... 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177,
... 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191,
... 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205,
... 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219,
... 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233,
... 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247,
... 248, 249, 250, 251, 252, 253, 254) # doctest: +ELLIPSIS
(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, ..., 252, 253, 254)
>>> f(lambda x: x[0] = 3) >>> f(lambda x: x[0] = 3)
Traceback (most recent call last): Traceback (most recent call last):
SyntaxError: lambda cannot contain assignment SyntaxError: lambda cannot contain assignment

View file

@ -1091,6 +1091,89 @@ _PyObject_NextNotImplemented(PyObject *self)
return NULL; return NULL;
} }
/* Specialized version of _PyObject_GenericGetAttrWithDict
specifically for the LOAD_METHOD opcode.
Return 1 if a method is found, 0 if it's a regular attribute
from __dict__ or something returned by using a descriptor
protocol.
`method` will point to the resolved attribute or NULL. In the
latter case, an error will be set.
*/
int
_PyObject_GetMethod(PyObject *obj, PyObject *name, PyObject **method)
{
PyTypeObject *tp = Py_TYPE(obj);
PyObject *descr;
descrgetfunc f = NULL;
PyObject **dictptr, *dict;
PyObject *attr;
int meth_found = 0;
assert(*method == NULL);
if (Py_TYPE(obj)->tp_getattro != PyObject_GenericGetAttr
|| !PyUnicode_Check(name)) {
*method = PyObject_GetAttr(obj, name);
return 0;
}
if (tp->tp_dict == NULL && PyType_Ready(tp) < 0)
return 0;
descr = _PyType_Lookup(tp, name);
if (descr != NULL) {
Py_INCREF(descr);
if (PyFunction_Check(descr) ||
(Py_TYPE(descr) == &PyMethodDescr_Type)) {
meth_found = 1;
} else {
f = descr->ob_type->tp_descr_get;
if (f != NULL && PyDescr_IsData(descr)) {
*method = f(descr, obj, (PyObject *)obj->ob_type);
Py_DECREF(descr);
return 0;
}
}
}
dictptr = _PyObject_GetDictPtr(obj);
if (dictptr != NULL && (dict = *dictptr) != NULL) {
Py_INCREF(dict);
attr = PyDict_GetItem(dict, name);
if (attr != NULL) {
Py_INCREF(attr);
*method = attr;
Py_DECREF(dict);
Py_XDECREF(descr);
return 0;
}
Py_DECREF(dict);
}
if (meth_found) {
*method = descr;
return 1;
}
if (f != NULL) {
*method = f(descr, obj, (PyObject *)Py_TYPE(obj));
Py_DECREF(descr);
return 0;
}
if (descr != NULL) {
*method = descr;
return 0;
}
PyErr_Format(PyExc_AttributeError,
"'%.50s' object has no attribute '%U'",
tp->tp_name, name);
return 0;
}
/* Generic GetAttr functions - put these in your tp_[gs]etattro slot */ /* Generic GetAttr functions - put these in your tp_[gs]etattro slot */
PyObject * PyObject *

View file

@ -1095,6 +1095,7 @@ static PYC_MAGIC magic_values[] = {
{ 3250, 3310, L"3.4" }, { 3250, 3310, L"3.4" },
{ 3320, 3351, L"3.5" }, { 3320, 3351, L"3.5" },
{ 3360, 3379, L"3.6" }, { 3360, 3379, L"3.6" },
{ 3390, 3399, L"3.7" },
{ 0 } { 0 }
}; };

View file

@ -51,6 +51,7 @@
#define CHECKEXC 1 /* Double-check exception checking */ #define CHECKEXC 1 /* Double-check exception checking */
#endif #endif
extern int _PyObject_GetMethod(PyObject *, PyObject *, PyObject **);
typedef PyObject *(*callproc)(PyObject *, PyObject *, PyObject *); typedef PyObject *(*callproc)(PyObject *, PyObject *, PyObject *);
#ifdef LLTRACE #ifdef LLTRACE
@ -3312,6 +3313,92 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
DISPATCH(); DISPATCH();
} }
TARGET(LOAD_METHOD) {
/* Designed to work in tamdem with CALL_METHOD. */
PyObject *name = GETITEM(names, oparg);
PyObject *obj = TOP();
PyObject *meth = NULL;
int meth_found = _PyObject_GetMethod(obj, name, &meth);
if (meth == NULL) {
/* Most likely attribute wasn't found. */
goto error;
}
if (meth_found) {
/* We can bypass temporary bound method object.
meth is unbound method and obj is self.
meth | self | arg1 | ... | argN
*/
SET_TOP(meth);
PUSH(obj); // self
}
else {
/* meth is not an unbound method (but a regular attr, or
something was returned by a descriptor protocol). Set
the second element of the stack to NULL, to signal
CALL_METHOD that it's not a method call.
NULL | meth | arg1 | ... | argN
*/
SET_TOP(NULL);
Py_DECREF(obj);
PUSH(meth);
}
DISPATCH();
}
TARGET(CALL_METHOD) {
/* Designed to work in tamdem with LOAD_METHOD. */
PyObject **sp, *res, *meth;
sp = stack_pointer;
meth = PEEK(oparg + 2);
if (meth == NULL) {
/* `meth` is NULL when LOAD_METHOD thinks that it's not
a method call.
Stack layout:
... | NULL | callable | arg1 | ... | argN
^- TOP()
^- (-oparg)
^- (-oparg-1)
^- (-oparg-2)
`callable` will be POPed by call_function.
NULL will will be POPed manually later.
*/
res = call_function(&sp, oparg, NULL);
stack_pointer = sp;
(void)POP(); /* POP the NULL. */
}
else {
/* This is a method call. Stack layout:
... | method | self | arg1 | ... | argN
^- TOP()
^- (-oparg)
^- (-oparg-1)
^- (-oparg-2)
`self` and `method` will be POPed by call_function.
We'll be passing `oparg + 1` to call_function, to
make it accept the `self` as a first argument.
*/
res = call_function(&sp, oparg + 1, NULL);
stack_pointer = sp;
}
PUSH(res);
if (res == NULL)
goto error;
DISPATCH();
}
PREDICTED(CALL_FUNCTION); PREDICTED(CALL_FUNCTION);
TARGET(CALL_FUNCTION) { TARGET(CALL_FUNCTION) {
PyObject **sp, *res; PyObject **sp, *res;

View file

@ -1058,6 +1058,8 @@ PyCompile_OpcodeStackEffect(int opcode, int oparg)
return -oparg; return -oparg;
case CALL_FUNCTION: case CALL_FUNCTION:
return -oparg; return -oparg;
case CALL_METHOD:
return -oparg-1;
case CALL_FUNCTION_KW: case CALL_FUNCTION_KW:
return -oparg-1; return -oparg-1;
case CALL_FUNCTION_EX: case CALL_FUNCTION_EX:
@ -1096,6 +1098,8 @@ PyCompile_OpcodeStackEffect(int opcode, int oparg)
/* If there's a fmt_spec on the stack, we go from 2->1, /* If there's a fmt_spec on the stack, we go from 2->1,
else 1->1. */ else 1->1. */
return (oparg & FVS_MASK) == FVS_HAVE_SPEC ? -1 : 0; return (oparg & FVS_MASK) == FVS_HAVE_SPEC ? -1 : 0;
case LOAD_METHOD:
return 1;
default: default:
return PY_INVALID_STACK_EFFECT; return PY_INVALID_STACK_EFFECT;
} }
@ -3415,9 +3419,44 @@ compiler_compare(struct compiler *c, expr_ty e)
return 1; return 1;
} }
// Return 1 if the method call was optimized, -1 if not, and 0 on error.
static int
maybe_optimize_method_call(struct compiler *c, expr_ty e)
{
Py_ssize_t argsl, i;
expr_ty meth = e->v.Call.func;
asdl_seq *args = e->v.Call.args;
/* Check that the call node is an attribute access, and that
the call doesn't have keyword parameters. */
if (meth->kind != Attribute_kind || meth->v.Attribute.ctx != Load ||
asdl_seq_LEN(e->v.Call.keywords))
return -1;
/* Check that there are no *varargs types of arguments. */
argsl = asdl_seq_LEN(args);
for (i = 0; i < argsl; i++) {
expr_ty elt = asdl_seq_GET(args, i);
if (elt->kind == Starred_kind) {
return -1;
}
}
/* Alright, we can optimize the code. */
VISIT(c, expr, meth->v.Attribute.value);
ADDOP_NAME(c, LOAD_METHOD, meth->v.Attribute.attr, names);
VISIT_SEQ(c, expr, e->v.Call.args);
ADDOP_I(c, CALL_METHOD, asdl_seq_LEN(e->v.Call.args));
return 1;
}
static int static int
compiler_call(struct compiler *c, expr_ty e) compiler_call(struct compiler *c, expr_ty e)
{ {
int ret = maybe_optimize_method_call(c, e);
if (ret >= 0) {
return ret;
}
VISIT(c, expr, e->v.Call.func); VISIT(c, expr, e->v.Call.func);
return compiler_call_helper(c, 0, return compiler_call_helper(c, 0,
e->v.Call.args, e->v.Call.args,

View file

@ -167,8 +167,8 @@ static void *const opcode_targets[256] = {
&&TARGET_BUILD_STRING, &&TARGET_BUILD_STRING,
&&TARGET_BUILD_TUPLE_UNPACK_WITH_CALL, &&TARGET_BUILD_TUPLE_UNPACK_WITH_CALL,
&&_unknown_opcode, &&_unknown_opcode,
&&_unknown_opcode, &&TARGET_LOAD_METHOD,
&&_unknown_opcode, &&TARGET_CALL_METHOD,
&&_unknown_opcode, &&_unknown_opcode,
&&_unknown_opcode, &&_unknown_opcode,
&&_unknown_opcode, &&_unknown_opcode,

View file

@ -137,7 +137,7 @@ main(int argc, char *argv[])
p = PyBytes_AS_STRING(marshalled); p = PyBytes_AS_STRING(marshalled);
n = PyBytes_GET_SIZE(marshalled); n = PyBytes_GET_SIZE(marshalled);
CHECK_NE(-1, (fd = open(outpath, O_CREAT|O_TRUNC|O_WRONLY, 0644))); CHECK_NE(-1, (fd = open(outpath, O_CREAT|O_TRUNC|O_WRONLY, 0644)));
WRITE16LE(m+0, 3379); /* Python 3.6rc1 */ WRITE16LE(m+0, 3390); /* Python 3.7a1 */
WRITE16LE(m+2, READ16LE("\r\n")); WRITE16LE(m+2, READ16LE("\r\n"));
WRITE32LE(m+4, st.st_mtim.tv_sec); /* tsk tsk y2038 */ WRITE32LE(m+4, st.st_mtim.tv_sec); /* tsk tsk y2038 */
WRITE32LE(m+8, n); WRITE32LE(m+8, n);

View file

@ -641,7 +641,7 @@ Objectify(void)
assert(PyBytes_CheckExact(marsh)); assert(PyBytes_CheckExact(marsh));
mardata = PyBytes_AS_STRING(marsh); mardata = PyBytes_AS_STRING(marsh);
marsize = PyBytes_GET_SIZE(marsh); marsize = PyBytes_GET_SIZE(marsh);
WRITE16LE(header+0, 3379); /* Python 3.6rc1 */ WRITE16LE(header+0, 3390); /* Python 3.7a1 */
WRITE16LE(header+2, READ16LE("\r\n")); WRITE16LE(header+2, READ16LE("\r\n"));
WRITE32LE(header+4, timestamp.tv_sec); WRITE32LE(header+4, timestamp.tv_sec);
WRITE32LE(header+8, marsize); WRITE32LE(header+8, marsize);