[Python-Dev] Accessing globals without dict lookup

Tim Peters tim.one@comcast.net
Mon, 11 Feb 2002 23:31:39 -0500


[Skip Montanaro]
> ...
> The whole think makes sense to me if it avoids the possible two
> PyDict_GetItem calls in the LOAD_GLOBAL opcode.  As I understand it, if
> accessed inside a function, LOAD_GLOBAL could be implemented
> something like this:
>
>     case LOAD_GLOBAL:
>         cell = func_cells[oparg];
>         if (cell.objptr) x = cell->objptr;
>         else x = cell->cellptr->objptr;
>         if (x == NULL) {
>             ... error recovery ...
>             break;
>         }
>         Py_INCREF(x);
>         continue;
>
> This looks a lot better to me (no complex function calls).

Something much like that.  Guido added code to the PEP (280).  My suggested
modifications reduce it to:

       case LOAD_GLOBAL_CELL:
           cell = func_cells[oparg];
           x = cell->objptr;
           if (x != NULL) {
               Py_INCREF(x);
               continue;
           }
           ... error recovery ...
           break;

Another difference is hiding in the "... error recovery ..." elisions.  In
Guido's scheme, this must also include code to deal with the possibility
that a global went away and thereby uncovered a builtin that popped into
existence after the module globals were initialized.  Then it's still a
non-error case, but the cell->cellptr has gotten out of synch with reality.
In the variation, the caches are never allowed to get out of synch, so "...
error recovery .." there should really be "... error reporting ...":  you
can't there in the variant unless NameError is certain.

Hmm:  We *all* seem to be missing a PUSH(x), so all of our schemes are dead
wrong <wink>.

Speaking of which, why does LOAD_FAST waste time checking against NULL
twice?!

		case LOAD_FAST:
			x = GETLOCAL(oparg);
			if (x == NULL) {
				format_exc_check_arg(
					PyExc_UnboundLocalError,
					UNBOUNDLOCAL_ERROR_MSG,
					PyTuple_GetItem(co->co_varnames, oparg)
					);
				break;
			}
			Py_INCREF(x);
			PUSH(x);
			if (x != NULL) continue;
			break;

I'll fix that ...