Skip to content

Commit 1cb19c0

Browse files
remove all egregious uses of local var references
1 parent eb3ae7e commit 1cb19c0

11 files changed

Lines changed: 228 additions & 77 deletions

Include/internal/pycore_abstract.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,14 @@ PyAPI_FUNC(int) _Py_convert_optional_to_ssize_t(PyObject *, void *);
5555
// Export for 'math' shared extension.
5656
PyAPI_FUNC(PyObject*) _PyNumber_Index(PyObject *o);
5757

58+
typedef struct _PyCevalIntAndPyObject {
59+
int num;
60+
PyObject *obj;
61+
} _PyCevalIntAndPyObject;
62+
63+
PyAPI_FUNC(_PyCevalIntAndPyObject)
64+
_PyCeval_Mapping_GetOptionalItem(PyObject *obj, PyObject *key);
65+
5866
#ifdef __cplusplus
5967
}
6068
#endif

Include/internal/pycore_typeobject.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ extern "C" {
1010

1111
#include "pycore_interp_structs.h" // managed_static_type_state
1212
#include "pycore_moduleobject.h" // PyModuleObject
13-
13+
#include "pycore_abstract.h" // _PyCevalIntAndPyObject
1414

1515
/* state */
1616

@@ -118,6 +118,7 @@ extern PyTypeObject _PyBufferWrapper_Type;
118118

119119
PyAPI_FUNC(PyObject*) _PySuper_Lookup(PyTypeObject *su_type, PyObject *su_obj,
120120
PyObject *name, int *meth_found);
121+
PyAPI_FUNC(_PyCevalIntAndPyObject) _PyCeval_Super_Lookup(PyTypeObject *su_type, PyObject *su_obj, PyObject *name);
121122

122123
extern PyObject* _PyType_GetFullyQualifiedName(PyTypeObject *type, char sep);
123124

Include/pyport.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,12 @@ extern "C" {
385385
# define Py_NO_INLINE
386386
#endif
387387

388+
#if defined(_MSC_VER)
389+
# define Py_NO_INLINE_MSVC_TAILCALL __declspec(noinline)
390+
#else
391+
# define Py_NO_INLINE
392+
#endif
393+
388394
#include "exports.h"
389395

390396
#ifdef Py_LIMITED_API

Objects/abstract.c

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,29 @@ PyMapping_GetOptionalItem(PyObject *obj, PyObject *key, PyObject **result)
222222
return 0;
223223
}
224224

225+
// Same as PyMapping_GetOptionalItem, but for use within ceval.c
226+
Py_NO_INLINE_MSVC_TAILCALL _PyCevalIntAndPyObject
227+
_PyCeval_Mapping_GetOptionalItem(PyObject *obj, PyObject *key)
228+
{
229+
PyObject *result;
230+
int err;
231+
if (PyDict_CheckExact(obj)) {
232+
err = PyDict_GetItemRef(obj, key, &result);
233+
return (_PyCevalIntAndPyObject) { err, result };
234+
}
235+
236+
result = PyObject_GetItem(obj, key);
237+
if (result) {
238+
return (_PyCevalIntAndPyObject) { 1, result };
239+
}
240+
assert(PyErr_Occurred());
241+
if (!PyErr_ExceptionMatches(PyExc_KeyError)) {
242+
return (_PyCevalIntAndPyObject) { -1, result };
243+
}
244+
PyErr_Clear();
245+
return (_PyCevalIntAndPyObject) { 0, result };
246+
}
247+
225248
int
226249
PyObject_SetItem(PyObject *o, PyObject *key, PyObject *value)
227250
{

Objects/typeobject.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12273,6 +12273,19 @@ _PySuper_Lookup(PyTypeObject *su_type, PyObject *su_obj, PyObject *name, int *me
1227312273
return res;
1227412274
}
1227512275

12276+
Py_NO_INLINE_MSVC_TAILCALL _PyCevalIntAndPyObject
12277+
_PyCeval_Super_Lookup(PyTypeObject *su_type, PyObject *su_obj, PyObject *name)
12278+
{
12279+
int method;
12280+
PyTypeObject *su_obj_type = supercheck(su_type, su_obj);
12281+
if (su_obj_type == NULL) {
12282+
return (_PyCevalIntAndPyObject){ 0, NULL};
12283+
}
12284+
PyObject *res = do_super_lookup(NULL, su_type, su_obj, su_obj_type, name, method);
12285+
Py_DECREF(su_obj_type);
12286+
return (_PyCevalIntAndPyObject) { method, res };
12287+
}
12288+
1227612289
static PyObject *
1227712290
super_descr_get(PyObject *self, PyObject *obj, PyObject *type)
1227812291
{

Python/bytecodes.c

Lines changed: 52 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1530,7 +1530,9 @@ dummy_func(
15301530

15311531
inst(LOAD_BUILD_CLASS, ( -- bc)) {
15321532
PyObject *bc_o;
1533-
int err = PyMapping_GetOptionalItem(BUILTINS(), &_Py_ID(__build_class__), &bc_o);
1533+
_PyCevalIntAndPyObject pair = _PyCeval_Mapping_GetOptionalItem(BUILTINS(), &_Py_ID(__build_class__));
1534+
int err = pair.num;
1535+
bc_o = pair.obj;
15341536
ERROR_IF(err < 0);
15351537
if (bc_o == NULL) {
15361538
_PyErr_SetString(tstate, PyExc_NameError,
@@ -1734,7 +1736,10 @@ dummy_func(
17341736
inst(LOAD_FROM_DICT_OR_GLOBALS, (mod_or_class_dict -- v)) {
17351737
PyObject *name = GETITEM(FRAME_CO_NAMES, oparg);
17361738
PyObject *v_o;
1737-
int err = PyMapping_GetOptionalItem(PyStackRef_AsPyObjectBorrow(mod_or_class_dict), name, &v_o);
1739+
_PyCevalIntAndPyObject pair = _PyCeval_Mapping_GetOptionalItem(
1740+
PyStackRef_AsPyObjectBorrow(mod_or_class_dict), name);
1741+
int err = pair.num;
1742+
v_o = pair.obj;
17381743
PyStackRef_CLOSE(mod_or_class_dict);
17391744
ERROR_IF(err < 0);
17401745
if (v_o == NULL) {
@@ -1757,11 +1762,15 @@ dummy_func(
17571762
else {
17581763
/* Slow-path if globals or builtins is not a dict */
17591764
/* namespace 1: globals */
1760-
int err = PyMapping_GetOptionalItem(GLOBALS(), name, &v_o);
1765+
_PyCevalIntAndPyObject pair = _PyCeval_Mapping_GetOptionalItem(GLOBALS(), name);
1766+
int err = pair.num;
1767+
v_o = pair.obj;
17611768
ERROR_IF(err < 0);
17621769
if (v_o == NULL) {
17631770
/* namespace 2: builtins */
1764-
int err = PyMapping_GetOptionalItem(BUILTINS(), name, &v_o);
1771+
pair = _PyCeval_Mapping_GetOptionalItem(BUILTINS(), name);
1772+
err = pair.num;
1773+
v_o = pair.obj;
17651774
ERROR_IF(err < 0);
17661775
if (v_o == NULL) {
17671776
_PyEval_FormatExcCheckArg(
@@ -1927,7 +1936,9 @@ dummy_func(
19271936
assert(class_dict);
19281937
assert(oparg >= 0 && oparg < _PyFrame_GetCode(frame)->co_nlocalsplus);
19291938
name = PyTuple_GET_ITEM(_PyFrame_GetCode(frame)->co_localsplusnames, oparg);
1930-
int err = PyMapping_GetOptionalItem(class_dict, name, &value_o);
1939+
_PyCevalIntAndPyObject pair = _PyCeval_Mapping_GetOptionalItem(class_dict, name);
1940+
int err = pair.num;
1941+
value_o = pair.obj;
19311942
if (err < 0) {
19321943
ERROR_NO_POP();
19331944
}
@@ -2117,7 +2128,9 @@ dummy_func(
21172128
ERROR_IF(true);
21182129
}
21192130
/* check if __annotations__ in locals()... */
2120-
int err = PyMapping_GetOptionalItem(LOCALS(), &_Py_ID(__annotations__), &ann_dict);
2131+
_PyCevalIntAndPyObject pair = _PyCeval_Mapping_GetOptionalItem(LOCALS(), &_Py_ID(__annotations__));
2132+
int err = pair.num;
2133+
ann_dict = pair.obj;
21212134
ERROR_IF(err < 0);
21222135
if (ann_dict == NULL) {
21232136
ann_dict = PyDict_New();
@@ -2221,8 +2234,13 @@ dummy_func(
22212234
}
22222235
// we make no attempt to optimize here; specializations should
22232236
// handle any case whose performance we care about
2224-
PyObject *stack[] = {class, self};
2225-
PyObject *super = PyObject_Vectorcall(global_super, stack, oparg & 2, NULL);
2237+
PyObject *super;
2238+
if (oparg & 2) {
2239+
super = PyObject_CallFunctionObjArgs(global_super, class, self, NULL);
2240+
}
2241+
else {
2242+
super = PyObject_CallNoArgs(global_super);
2243+
}
22262244
if (opcode == INSTRUMENTED_LOAD_SUPER_ATTR) {
22272245
PyObject *arg = oparg & 2 ? class : &_PyInstrumentation_MISSING;
22282246
if (super == NULL) {
@@ -2280,9 +2298,17 @@ dummy_func(
22802298
STAT_INC(LOAD_SUPER_ATTR, hit);
22812299
PyObject *name = GETITEM(FRAME_CO_NAMES, oparg >> 2);
22822300
PyTypeObject *cls = (PyTypeObject *)class;
2301+
PyObject *attr_o;
22832302
int method_found = 0;
2284-
PyObject *attr_o = _PySuper_Lookup(cls, self, name,
2285-
Py_TYPE(self)->tp_getattro == PyObject_GenericGetAttr ? &method_found : NULL);
2303+
if (Py_TYPE(self)->tp_getattro == PyObject_GenericGetAttr) {
2304+
_PyCevalIntAndPyObject pair = _PyCeval_Super_Lookup(cls, self, name);
2305+
method_found = pair.num;
2306+
attr_o = pair.obj;
2307+
}
2308+
else {
2309+
attr_o = _PySuper_Lookup(cls, self, name, NULL);
2310+
}
2311+
22862312
if (attr_o == NULL) {
22872313
ERROR_NO_POP();
22882314
}
@@ -2327,19 +2353,18 @@ dummy_func(
23272353
#endif /* ENABLE_SPECIALIZATION_FT */
23282354
}
23292355

2330-
op(_LOAD_ATTR, (owner -- attr, self_or_null[oparg&1])) {
2356+
op(_LOAD_ATTR, (owner -- attr[1], self_or_null[oparg&1])) {
23312357
PyObject *name = GETITEM(FRAME_CO_NAMES, oparg >> 1);
2332-
PyObject *attr_o;
23332358
if (oparg & 1) {
23342359
/* Designed to work in tandem with CALL, pushes two values. */
2335-
attr_o = NULL;
2336-
int is_meth = _PyObject_GetMethod(PyStackRef_AsPyObjectBorrow(owner), name, &attr_o);
2360+
attr[0] = PyStackRef_NULL;
2361+
int is_meth = _PyObject_GetMethodStackRef(tstate, PyStackRef_AsPyObjectBorrow(owner), name, attr);
23372362
if (is_meth) {
23382363
/* We can bypass temporary bound method object.
23392364
meth is unbound method and obj is self.
23402365
meth | self | arg1 | ... | argN
23412366
*/
2342-
assert(attr_o != NULL); // No errors on this branch
2367+
assert(!PyStackRef_IsNull(attr[0])); // No errors on this branch
23432368
self_or_null[0] = owner; // Transfer ownership
23442369
DEAD(owner);
23452370
}
@@ -2351,17 +2376,19 @@ dummy_func(
23512376
meth | NULL | arg1 | ... | argN
23522377
*/
23532378
PyStackRef_CLOSE(owner);
2354-
ERROR_IF(attr_o == NULL);
2379+
ERROR_IF(PyStackRef_IsNull(attr[0]));
23552380
self_or_null[0] = PyStackRef_NULL;
23562381
}
23572382
}
23582383
else {
2384+
PyObject *attr_o;
23592385
/* Classic, pushes one value. */
23602386
attr_o = PyObject_GetAttr(PyStackRef_AsPyObjectBorrow(owner), name);
23612387
PyStackRef_CLOSE(owner);
23622388
ERROR_IF(attr_o == NULL);
2389+
attr[0] = PyStackRef_FromPyObjectSteal(attr_o);
23632390
}
2364-
attr = PyStackRef_FromPyObjectSteal(attr_o);
2391+
assert(!PyStackRef_IsNull(attr));
23652392
}
23662393

23672394
macro(LOAD_ATTR) =
@@ -3507,10 +3534,14 @@ dummy_func(
35073534
}
35083535
assert(PyStackRef_IsTaggedInt(lasti));
35093536
(void)lasti; // Shut up compiler warning if asserts are off
3510-
PyObject *stack[5] = {NULL, PyStackRef_AsPyObjectBorrow(exit_self), exc, val_o, tb};
3511-
int has_self = !PyStackRef_IsNull(exit_self);
3512-
PyObject *res_o = PyObject_Vectorcall(exit_func_o, stack + 2 - has_self,
3513-
(3 + has_self) | PY_VECTORCALL_ARGUMENTS_OFFSET, NULL);
3537+
PyObject *res_o;
3538+
if (PyStackRef_IsNull(exit_self)) {
3539+
res_o = PyObject_CallFunctionObjArgs(exit_func_o, exc, val_o, tb, NULL);
3540+
}
3541+
else {
3542+
PyObject *exit_self_o = PyStackRef_AsPyObjectBorrow(exit_self);
3543+
res_o = PyObject_CallFunctionObjArgs(exit_func_o, exit_self_o, exc, val_o, tb, NULL);
3544+
}
35143545
Py_XDECREF(original_tb);
35153546
ERROR_IF(res_o == NULL);
35163547
res = PyStackRef_FromPyObjectSteal(res_o);

Python/ceval_macros.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@
9090

9191
typedef PyObject* (Py_PRESERVE_NONE_CC *py_tail_call_funcptr)(TAIL_CALL_PARAMS);
9292

93-
# define TARGET(op) PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_##op(TAIL_CALL_PARAMS)
93+
# define TARGET(op) Py_NO_INLINE_MSVC_TAILCALL PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_##op(TAIL_CALL_PARAMS)
9494
# define DISPATCH_GOTO() \
9595
do { \
9696
Py_MUSTTAIL return (INSTRUCTION_TABLE[opcode])(TAIL_CALL_ARGS); \

0 commit comments

Comments
 (0)