[Python-Dev] MAKE_FUNCTION simplification

Nikita Nemkin nikita at nemkin.ru
Thu Apr 14 05:04:34 EDT 2016

MAKE_FUNCTION opcode is complex due to the way it receives
input arguments:

 1) default args, individually;
 2) default kwonly args, individual name-value pairs;
 3) a tuple of parameter names (single constant);
 4) annotation values, individually;
 5) code object;
 6) qualname.

The counts for 1,2,4 are packed into oparg bitfields, making oparg large.

My suggestion is to pre-package 1-4 before calling MAKE_FUNCTION,
i.e. explicitly emit BUILD_TUPLE for defaults args and BUILD_MAPs
for keyword defaults and annotations.

Then, MAKE_FUNCTION will become a dramatically simpler
5 argument opcode, taking

 1) default args tuple (optional);
 2) default keyword only args dict (optional);
 3) annotations dict (optional);
 4) code object;
 5) qualname.

These arguments correspond exactly to __annotations__, __kwdefaults__,
__defaults__, __code__ and __qualname__ attributes.

For optional args, oparg bits should indicate individual arg presence.
(This also saves None checks in opcode implementation.)

If we add another optional argument (and oparg bit) for __closure__
attribute, then separate MAKE_CLOSURE opcode becomes unnecessary.

Default args tuple is likely to be a constant and can be packaged whole,
compensating for the extra size of explicit BUILD_* instructions.

Compare the current implementation:


with this provisional implementation (untested):

        PyObject *qualname = POP();
        PyObject *codeobj = POP();
        PyFunctionObject *func;
        func = (PyFunctionObject *)PyFunction_NewWithQualName(
                                       codeobj, f->f_globals, qualname);
        if (func == NULL)
            goto error;

        /* NB: Py_None is not an acceptable value for these. */
        if (oparg & 0x08)
            func->func_closure = POP();
        if (oparg & 0x04)
            func->func_annotations = POP();
        if (oparg & 0x02)
            func->func_kwdefaults = POP();
        if (oparg & 0x01)
            func->func_defaults = POP();

        PUSH((PyObject *)func);

compile.c also gets a bit simpler, but not much.

What do you think?

More information about the Python-Dev mailing list