Skip to content

Commit 8863a0f

Browse files
authored
bpo-44525: Specialize simple Python calls. (GH-29033)
1 parent 8d6740f commit 8863a0f

File tree

7 files changed

+176
-60
lines changed

7 files changed

+176
-60
lines changed

Include/internal/pycore_code.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,12 @@ typedef struct {
3535
PyObject *obj;
3636
} _PyObjectCache;
3737

38+
typedef struct {
39+
uint32_t func_version;
40+
uint16_t defaults_start;
41+
uint16_t defaults_len;
42+
} _PyCallCache;
43+
3844
/* Add specialized versions of entries to this union.
3945
*
4046
* Do not break the invariant: sizeof(SpecializedCacheEntry) == 8
@@ -51,6 +57,7 @@ typedef union {
5157
_PyAttrCache attr;
5258
_PyLoadGlobalCache load_global;
5359
_PyObjectCache obj;
60+
_PyCallCache call;
5461
} SpecializedCacheEntry;
5562

5663
#define INSTRUCTIONS_PER_ENTRY (sizeof(SpecializedCacheEntry)/sizeof(_Py_CODEUNIT))

Include/opcode.h

Lines changed: 24 additions & 23 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Lib/opcode.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,7 @@ def jabs_op(name, op):
237237
"CALL_FUNCTION_BUILTIN_FAST",
238238
"CALL_FUNCTION_LEN",
239239
"CALL_FUNCTION_ISINSTANCE",
240+
"CALL_FUNCTION_PY_SIMPLE",
240241
"JUMP_ABSOLUTE_QUICK",
241242
"LOAD_ATTR_ADAPTIVE",
242243
"LOAD_ATTR_INSTANCE_VALUE",
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Specialize simple calls to Python functions (no starargs, keyowrd dict, or closure)

Python/ceval.c

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4720,9 +4720,9 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr
47204720

47214721
TARGET(CALL_FUNCTION_ADAPTIVE) {
47224722
SpecializedCacheEntry *cache = GET_CACHE();
4723+
nargs = cache->adaptive.original_oparg;
47234724
if (cache->adaptive.counter == 0) {
47244725
next_instr--;
4725-
int nargs = cache->adaptive.original_oparg;
47264726
if (_Py_Specialize_CallFunction(
47274727
PEEK(nargs + 1), next_instr, nargs, cache, BUILTINS()) < 0) {
47284728
goto error;
@@ -4732,9 +4732,48 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr
47324732
else {
47334733
STAT_INC(CALL_FUNCTION, deferred);
47344734
cache->adaptive.counter--;
4735-
oparg = cache->adaptive.original_oparg;
4736-
JUMP_TO_INSTRUCTION(CALL_FUNCTION);
4735+
oparg = nargs;
4736+
kwnames = NULL;
4737+
postcall_shrink = 1;
4738+
goto call_function;
4739+
}
4740+
}
4741+
4742+
TARGET(CALL_FUNCTION_PY_SIMPLE) {
4743+
SpecializedCacheEntry *caches = GET_CACHE();
4744+
_PyAdaptiveEntry *cache0 = &caches[0].adaptive;
4745+
int argcount = cache0->original_oparg;
4746+
_PyCallCache *cache1 = &caches[-1].call;
4747+
PyObject *callable = PEEK(argcount+1);
4748+
DEOPT_IF(!PyFunction_Check(callable), CALL_FUNCTION);
4749+
PyFunctionObject *func = (PyFunctionObject *)callable;
4750+
DEOPT_IF(func->func_version != cache1->func_version, CALL_FUNCTION);
4751+
/* PEP 523 */
4752+
DEOPT_IF(tstate->interp->eval_frame != NULL, CALL_FUNCTION);
4753+
STAT_INC(CALL_FUNCTION, hit);
4754+
record_cache_hit(cache0);
4755+
InterpreterFrame *new_frame = _PyThreadState_PushFrame(
4756+
tstate, PyFunction_AS_FRAME_CONSTRUCTOR(func), NULL);
4757+
if (new_frame == NULL) {
4758+
goto error;
4759+
}
4760+
STACK_SHRINK(argcount);
4761+
for (int i = 0; i < argcount; i++) {
4762+
new_frame->localsplus[i] = stack_pointer[i];
4763+
}
4764+
int deflen = cache1->defaults_len;
4765+
for (int i = 0; i < deflen; i++) {
4766+
PyObject *def = PyTuple_GET_ITEM(func->func_defaults, cache1->defaults_start+i);
4767+
Py_INCREF(def);
4768+
new_frame->localsplus[argcount+i] = def;
47374769
}
4770+
STACK_SHRINK(1);
4771+
Py_DECREF(func);
4772+
_PyFrame_SetStackPointer(frame, stack_pointer);
4773+
new_frame->previous = tstate->frame;
4774+
new_frame->depth = frame->depth + 1;
4775+
tstate->frame = frame = new_frame;
4776+
goto start_frame;
47384777
}
47394778

47404779
TARGET(CALL_FUNCTION_BUILTIN_O) {

Python/opcode_targets.h

Lines changed: 14 additions & 14 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)