gh-93143: Avoid NULL check in LOAD_FAST based on analysis in the compiler (GH-93144)

This commit is contained in:
Dennis Sweeney 2022-05-31 16:32:30 -04:00 committed by GitHub
parent 8a5e3c2ec6
commit f425f3bb27
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 371 additions and 52 deletions

View file

@ -158,6 +158,7 @@ const uint8_t _PyOpcode_Deopt[256] = {
[LOAD_CONST__LOAD_FAST] = LOAD_CONST,
[LOAD_DEREF] = LOAD_DEREF,
[LOAD_FAST] = LOAD_FAST,
[LOAD_FAST_CHECK] = LOAD_FAST_CHECK,
[LOAD_FAST__LOAD_CONST] = LOAD_FAST,
[LOAD_FAST__LOAD_FAST] = LOAD_FAST,
[LOAD_GLOBAL] = LOAD_GLOBAL,
@ -340,6 +341,7 @@ const uint8_t _PyOpcode_Original[256] = {
[LOAD_CONST__LOAD_FAST] = LOAD_CONST,
[LOAD_DEREF] = LOAD_DEREF,
[LOAD_FAST] = LOAD_FAST,
[LOAD_FAST_CHECK] = LOAD_FAST_CHECK,
[LOAD_FAST__LOAD_CONST] = LOAD_FAST,
[LOAD_FAST__LOAD_FAST] = LOAD_FAST,
[LOAD_GLOBAL] = LOAD_GLOBAL,
@ -547,7 +549,7 @@ static const char *const _PyOpcode_OpName[256] = {
[LOAD_FAST] = "LOAD_FAST",
[STORE_FAST] = "STORE_FAST",
[DELETE_FAST] = "DELETE_FAST",
[LOAD_METHOD_WITH_VALUES] = "LOAD_METHOD_WITH_VALUES",
[LOAD_FAST_CHECK] = "LOAD_FAST_CHECK",
[POP_JUMP_FORWARD_IF_NOT_NONE] = "POP_JUMP_FORWARD_IF_NOT_NONE",
[POP_JUMP_FORWARD_IF_NONE] = "POP_JUMP_FORWARD_IF_NONE",
[RAISE_VARARGS] = "RAISE_VARARGS",
@ -561,9 +563,9 @@ static const char *const _PyOpcode_OpName[256] = {
[STORE_DEREF] = "STORE_DEREF",
[DELETE_DEREF] = "DELETE_DEREF",
[JUMP_BACKWARD] = "JUMP_BACKWARD",
[RESUME_QUICK] = "RESUME_QUICK",
[LOAD_METHOD_WITH_VALUES] = "LOAD_METHOD_WITH_VALUES",
[CALL_FUNCTION_EX] = "CALL_FUNCTION_EX",
[STORE_ATTR_ADAPTIVE] = "STORE_ATTR_ADAPTIVE",
[RESUME_QUICK] = "RESUME_QUICK",
[EXTENDED_ARG] = "EXTENDED_ARG",
[LIST_APPEND] = "LIST_APPEND",
[SET_ADD] = "SET_ADD",
@ -573,33 +575,33 @@ static const char *const _PyOpcode_OpName[256] = {
[YIELD_VALUE] = "YIELD_VALUE",
[RESUME] = "RESUME",
[MATCH_CLASS] = "MATCH_CLASS",
[STORE_ATTR_ADAPTIVE] = "STORE_ATTR_ADAPTIVE",
[STORE_ATTR_INSTANCE_VALUE] = "STORE_ATTR_INSTANCE_VALUE",
[STORE_ATTR_SLOT] = "STORE_ATTR_SLOT",
[FORMAT_VALUE] = "FORMAT_VALUE",
[BUILD_CONST_KEY_MAP] = "BUILD_CONST_KEY_MAP",
[BUILD_STRING] = "BUILD_STRING",
[STORE_ATTR_SLOT] = "STORE_ATTR_SLOT",
[STORE_ATTR_WITH_HINT] = "STORE_ATTR_WITH_HINT",
[STORE_FAST__LOAD_FAST] = "STORE_FAST__LOAD_FAST",
[LOAD_METHOD] = "LOAD_METHOD",
[STORE_FAST__STORE_FAST] = "STORE_FAST__STORE_FAST",
[STORE_FAST__LOAD_FAST] = "STORE_FAST__LOAD_FAST",
[LIST_EXTEND] = "LIST_EXTEND",
[SET_UPDATE] = "SET_UPDATE",
[DICT_MERGE] = "DICT_MERGE",
[DICT_UPDATE] = "DICT_UPDATE",
[STORE_FAST__STORE_FAST] = "STORE_FAST__STORE_FAST",
[STORE_SUBSCR_ADAPTIVE] = "STORE_SUBSCR_ADAPTIVE",
[STORE_SUBSCR_DICT] = "STORE_SUBSCR_DICT",
[STORE_SUBSCR_LIST_INT] = "STORE_SUBSCR_LIST_INT",
[UNPACK_SEQUENCE_ADAPTIVE] = "UNPACK_SEQUENCE_ADAPTIVE",
[UNPACK_SEQUENCE_LIST] = "UNPACK_SEQUENCE_LIST",
[CALL] = "CALL",
[KW_NAMES] = "KW_NAMES",
[POP_JUMP_BACKWARD_IF_NOT_NONE] = "POP_JUMP_BACKWARD_IF_NOT_NONE",
[POP_JUMP_BACKWARD_IF_NONE] = "POP_JUMP_BACKWARD_IF_NONE",
[POP_JUMP_BACKWARD_IF_FALSE] = "POP_JUMP_BACKWARD_IF_FALSE",
[POP_JUMP_BACKWARD_IF_TRUE] = "POP_JUMP_BACKWARD_IF_TRUE",
[UNPACK_SEQUENCE_LIST] = "UNPACK_SEQUENCE_LIST",
[UNPACK_SEQUENCE_TUPLE] = "UNPACK_SEQUENCE_TUPLE",
[UNPACK_SEQUENCE_TWO_TUPLE] = "UNPACK_SEQUENCE_TWO_TUPLE",
[179] = "<179>",
[180] = "<180>",
[181] = "<181>",
[182] = "<182>",
@ -680,7 +682,6 @@ static const char *const _PyOpcode_OpName[256] = {
#endif
#define EXTRA_CASES \
case 179: \
case 180: \
case 181: \
case 182: \

31
Include/opcode.h generated
View file

@ -81,6 +81,7 @@ extern "C" {
#define LOAD_FAST 124
#define STORE_FAST 125
#define DELETE_FAST 126
#define LOAD_FAST_CHECK 127
#define POP_JUMP_FORWARD_IF_NOT_NONE 128
#define POP_JUMP_FORWARD_IF_NONE 129
#define RAISE_VARARGS 130
@ -173,21 +174,21 @@ extern "C" {
#define LOAD_METHOD_MODULE 86
#define LOAD_METHOD_NO_DICT 113
#define LOAD_METHOD_WITH_DICT 121
#define LOAD_METHOD_WITH_VALUES 127
#define RESUME_QUICK 141
#define STORE_ATTR_ADAPTIVE 143
#define STORE_ATTR_INSTANCE_VALUE 153
#define STORE_ATTR_SLOT 154
#define STORE_ATTR_WITH_HINT 158
#define STORE_FAST__LOAD_FAST 159
#define STORE_FAST__STORE_FAST 161
#define STORE_SUBSCR_ADAPTIVE 166
#define STORE_SUBSCR_DICT 167
#define STORE_SUBSCR_LIST_INT 168
#define UNPACK_SEQUENCE_ADAPTIVE 169
#define UNPACK_SEQUENCE_LIST 170
#define UNPACK_SEQUENCE_TUPLE 177
#define UNPACK_SEQUENCE_TWO_TUPLE 178
#define LOAD_METHOD_WITH_VALUES 141
#define RESUME_QUICK 143
#define STORE_ATTR_ADAPTIVE 153
#define STORE_ATTR_INSTANCE_VALUE 154
#define STORE_ATTR_SLOT 158
#define STORE_ATTR_WITH_HINT 159
#define STORE_FAST__LOAD_FAST 161
#define STORE_FAST__STORE_FAST 166
#define STORE_SUBSCR_ADAPTIVE 167
#define STORE_SUBSCR_DICT 168
#define STORE_SUBSCR_LIST_INT 169
#define UNPACK_SEQUENCE_ADAPTIVE 170
#define UNPACK_SEQUENCE_LIST 177
#define UNPACK_SEQUENCE_TUPLE 178
#define UNPACK_SEQUENCE_TWO_TUPLE 179
#define DO_TRACING 255
#define HAS_CONST(op) (false\

View file

@ -406,6 +406,7 @@ def _write_atomic(path, data, mode=0o666):
# Python 3.12a1 3500 (Remove PRECALL opcode)
# Python 3.12a1 3501 (YIELD_VALUE oparg == stack_depth)
# Python 3.12a1 3502 (LOAD_FAST_CHECK, no NULL-check in LOAD_FAST)
# Python 3.13 will start with 3550
@ -419,7 +420,7 @@ def _write_atomic(path, data, mode=0o666):
# Whenever MAGIC_NUMBER is changed, the ranges in the magic_values array
# in PC/launcher.c must also be updated.
MAGIC_NUMBER = (3501).to_bytes(2, 'little') + b'\r\n'
MAGIC_NUMBER = (3502).to_bytes(2, 'little') + b'\r\n'
_RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c

View file

@ -139,12 +139,14 @@ def jabs_op(name, op):
def_op('COPY', 120)
def_op('BINARY_OP', 122)
jrel_op('SEND', 123) # Number of bytes to skip
def_op('LOAD_FAST', 124) # Local variable number
def_op('LOAD_FAST', 124) # Local variable number, no null check
haslocal.append(124)
def_op('STORE_FAST', 125) # Local variable number
haslocal.append(125)
def_op('DELETE_FAST', 126) # Local variable number
haslocal.append(126)
def_op('LOAD_FAST_CHECK', 127) # Local variable number
haslocal.append(127)
jrel_op('POP_JUMP_FORWARD_IF_NOT_NONE', 128)
jrel_op('POP_JUMP_FORWARD_IF_NONE', 129)
def_op('RAISE_VARARGS', 130) # Number of raise arguments (1, 2, or 3)

View file

@ -360,7 +360,7 @@ def bug42562():
--> BINARY_OP 11 (/)
POP_TOP
%3d LOAD_FAST 1 (tb)
%3d LOAD_FAST_CHECK 1 (tb)
RETURN_VALUE
>> PUSH_EXC_INFO
@ -1399,7 +1399,7 @@ def _prepare_test_cases():
Instruction(opname='LOAD_CONST', opcode=100, arg=4, argval='I can haz else clause?', argrepr="'I can haz else clause?'", offset=100, starts_line=None, is_jump_target=False, positions=None),
Instruction(opname='CALL', opcode=171, arg=1, argval=1, argrepr='', offset=102, starts_line=None, is_jump_target=False, positions=None),
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=112, starts_line=None, is_jump_target=False, positions=None),
Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='i', argrepr='i', offset=114, starts_line=11, is_jump_target=True, positions=None),
Instruction(opname='LOAD_FAST_CHECK', opcode=127, arg=0, argval='i', argrepr='i', offset=114, starts_line=11, is_jump_target=True, positions=None),
Instruction(opname='POP_JUMP_FORWARD_IF_FALSE', opcode=114, arg=34, argval=186, argrepr='to 186', offset=116, starts_line=None, is_jump_target=False, positions=None),
Instruction(opname='LOAD_GLOBAL', opcode=116, arg=3, argval='print', argrepr='NULL + print', offset=118, starts_line=12, is_jump_target=True, positions=None),
Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='i', argrepr='i', offset=130, starts_line=None, is_jump_target=False, positions=None),

View file

@ -1,5 +1,6 @@
import dis
from itertools import combinations, product
import sys
import textwrap
import unittest
@ -682,5 +683,184 @@ def test_bpo_45773_pop_jump_if_false(self):
compile("while True or not spam: pass", "<test>", "exec")
class TestMarkingVariablesAsUnKnown(BytecodeTestCase):
def setUp(self):
self.addCleanup(sys.settrace, sys.gettrace())
sys.settrace(None)
def test_load_fast_known_simple(self):
def f():
x = 1
y = x + x
self.assertInBytecode(f, 'LOAD_FAST')
def test_load_fast_unknown_simple(self):
def f():
if condition():
x = 1
print(x)
self.assertInBytecode(f, 'LOAD_FAST_CHECK')
self.assertNotInBytecode(f, 'LOAD_FAST')
def test_load_fast_unknown_because_del(self):
def f():
x = 1
del x
print(x)
self.assertInBytecode(f, 'LOAD_FAST_CHECK')
self.assertNotInBytecode(f, 'LOAD_FAST')
def test_load_fast_known_because_parameter(self):
def f1(x):
print(x)
self.assertInBytecode(f1, 'LOAD_FAST')
self.assertNotInBytecode(f1, 'LOAD_FAST_CHECK')
def f2(*, x):
print(x)
self.assertInBytecode(f2, 'LOAD_FAST')
self.assertNotInBytecode(f2, 'LOAD_FAST_CHECK')
def f3(*args):
print(args)
self.assertInBytecode(f3, 'LOAD_FAST')
self.assertNotInBytecode(f3, 'LOAD_FAST_CHECK')
def f4(**kwargs):
print(kwargs)
self.assertInBytecode(f4, 'LOAD_FAST')
self.assertNotInBytecode(f4, 'LOAD_FAST_CHECK')
def f5(x=0):
print(x)
self.assertInBytecode(f5, 'LOAD_FAST')
self.assertNotInBytecode(f5, 'LOAD_FAST_CHECK')
def test_load_fast_known_because_already_loaded(self):
def f():
if condition():
x = 1
print(x)
print(x)
self.assertInBytecode(f, 'LOAD_FAST_CHECK')
self.assertInBytecode(f, 'LOAD_FAST')
def test_load_fast_known_multiple_branches(self):
def f():
if condition():
x = 1
else:
x = 2
print(x)
self.assertInBytecode(f, 'LOAD_FAST')
self.assertNotInBytecode(f, 'LOAD_FAST_CHECK')
def test_load_fast_unknown_after_error(self):
def f():
try:
res = 1 / 0
except ZeroDivisionError:
pass
return res
# LOAD_FAST (known) still occurs in the no-exception branch.
# Assert that it doesn't occur in the LOAD_FAST_CHECK branch.
self.assertInBytecode(f, 'LOAD_FAST_CHECK')
def test_load_fast_unknown_after_error_2(self):
def f():
try:
1 / 0
except:
print(a, b, c, d, e, f, g)
a = b = c = d = e = f = g = 1
self.assertInBytecode(f, 'LOAD_FAST_CHECK')
self.assertNotInBytecode(f, 'LOAD_FAST')
def test_setting_lineno_adds_check(self):
code = textwrap.dedent("""\
def f():
x = 2
L = 3
L = 4
for i in range(55):
x + 6
del x
L = 8
L = 9
L = 10
""")
ns = {}
exec(code, ns)
f = ns['f']
self.assertInBytecode(f, "LOAD_FAST")
def trace(frame, event, arg):
if event == 'line' and frame.f_lineno == 9:
frame.f_lineno = 2
sys.settrace(None)
return None
return trace
sys.settrace(trace)
f()
self.assertNotInBytecode(f, "LOAD_FAST")
def make_function_with_no_checks(self):
code = textwrap.dedent("""\
def f():
x = 2
L = 3
L = 4
L = 5
if not L:
x + 7
y = 2
""")
ns = {}
exec(code, ns)
f = ns['f']
self.assertInBytecode(f, "LOAD_FAST")
self.assertNotInBytecode(f, "LOAD_FAST_CHECK")
return f
def test_deleting_local_adds_check(self):
f = self.make_function_with_no_checks()
def trace(frame, event, arg):
if event == 'line' and frame.f_lineno == 4:
del frame.f_locals["x"]
sys.settrace(None)
return None
return trace
sys.settrace(trace)
f()
self.assertNotInBytecode(f, "LOAD_FAST")
self.assertInBytecode(f, "LOAD_FAST_CHECK")
def test_modifying_local_does_not_add_check(self):
f = self.make_function_with_no_checks()
def trace(frame, event, arg):
if event == 'line' and frame.f_lineno == 4:
frame.f_locals["x"] = 42
sys.settrace(None)
return None
return trace
sys.settrace(trace)
f()
self.assertInBytecode(f, "LOAD_FAST")
self.assertNotInBytecode(f, "LOAD_FAST_CHECK")
def test_initializing_local_does_not_add_check(self):
f = self.make_function_with_no_checks()
def trace(frame, event, arg):
if event == 'line' and frame.f_lineno == 4:
frame.f_locals["y"] = 42
sys.settrace(None)
return None
return trace
sys.settrace(trace)
f()
self.assertInBytecode(f, "LOAD_FAST")
self.assertNotInBytecode(f, "LOAD_FAST_CHECK")
if __name__ == "__main__":
unittest.main()

View file

@ -0,0 +1 @@
Avoid ``NULL`` checks for uninitialized local variables by determining at compile time which variables must be initialized.

View file

@ -455,6 +455,26 @@ _PyFrame_GetState(PyFrameObject *frame)
Py_UNREACHABLE();
}
static void
add_load_fast_null_checks(PyCodeObject *co)
{
_Py_CODEUNIT *instructions = _PyCode_CODE(co);
for (Py_ssize_t i = 0; i < Py_SIZE(co); i++) {
switch (_Py_OPCODE(instructions[i])) {
case LOAD_FAST:
case LOAD_FAST__LOAD_FAST:
case LOAD_FAST__LOAD_CONST:
_Py_SET_OPCODE(instructions[i], LOAD_FAST_CHECK);
break;
case LOAD_CONST__LOAD_FAST:
_Py_SET_OPCODE(instructions[i], LOAD_CONST);
break;
case STORE_FAST__LOAD_FAST:
_Py_SET_OPCODE(instructions[i], STORE_FAST);
break;
}
}
}
/* Setter for f_lineno - you can set f_lineno from within a trace function in
* order to jump to a given line of code, subject to some restrictions. Most
@ -545,6 +565,8 @@ frame_setlineno(PyFrameObject *f, PyObject* p_new_lineno, void *Py_UNUSED(ignore
return -1;
}
add_load_fast_null_checks(f->f_frame->f_code);
/* PyCode_NewWithPosOnlyArgs limits co_code to be under INT_MAX so this
* should never overflow. */
int len = (int)Py_SIZE(f->f_frame->f_code);
@ -1047,6 +1069,7 @@ _PyFrame_LocalsToFast(_PyInterpreterFrame *frame, int clear)
}
fast = _PyFrame_GetLocalsArray(frame);
co = frame->f_code;
bool added_null_checks = false;
PyErr_Fetch(&error_type, &error_value, &error_traceback);
for (int i = 0; i < co->co_nlocalsplus; i++) {
@ -1066,6 +1089,10 @@ _PyFrame_LocalsToFast(_PyInterpreterFrame *frame, int clear)
}
}
PyObject *oldvalue = fast[i];
if (!added_null_checks && oldvalue != NULL && value == NULL) {
add_load_fast_null_checks(co);
added_null_checks = true;
}
PyObject *cell = NULL;
if (kind == CO_FAST_FREE) {
// The cell was set when the frame was created from

View file

@ -1813,7 +1813,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
DISPATCH();
}
TARGET(LOAD_FAST) {
TARGET(LOAD_FAST_CHECK) {
PyObject *value = GETLOCAL(oparg);
if (value == NULL) {
goto unbound_local_error;
@ -1823,6 +1823,14 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
DISPATCH();
}
TARGET(LOAD_FAST) {
PyObject *value = GETLOCAL(oparg);
assert(value != NULL);
Py_INCREF(value);
PUSH(value);
DISPATCH();
}
TARGET(LOAD_CONST) {
PREDICTED(LOAD_CONST);
PyObject *value = GETITEM(consts, oparg);
@ -1840,17 +1848,13 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
TARGET(LOAD_FAST__LOAD_FAST) {
PyObject *value = GETLOCAL(oparg);
if (value == NULL) {
goto unbound_local_error;
}
assert(value != NULL);
NEXTOPARG();
next_instr++;
Py_INCREF(value);
PUSH(value);
value = GETLOCAL(oparg);
if (value == NULL) {
goto unbound_local_error;
}
assert(value != NULL);
Py_INCREF(value);
PUSH(value);
NOTRACE_DISPATCH();
@ -1858,9 +1862,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
TARGET(LOAD_FAST__LOAD_CONST) {
PyObject *value = GETLOCAL(oparg);
if (value == NULL) {
goto unbound_local_error;
}
assert(value != NULL);
NEXTOPARG();
next_instr++;
Py_INCREF(value);
@ -1877,9 +1879,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
NEXTOPARG();
next_instr++;
value = GETLOCAL(oparg);
if (value == NULL) {
goto unbound_local_error;
}
assert(value != NULL);
Py_INCREF(value);
PUSH(value);
NOTRACE_DISPATCH();
@ -1902,9 +1902,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
Py_INCREF(value);
PUSH(value);
value = GETLOCAL(oparg);
if (value == NULL) {
goto unbound_local_error;
}
assert(value != NULL);
Py_INCREF(value);
PUSH(value);
NOTRACE_DISPATCH();

View file

@ -1109,6 +1109,7 @@ stack_effect(int opcode, int oparg, int jump)
return 1;
case LOAD_FAST:
case LOAD_FAST_CHECK:
return 1;
case STORE_FAST:
return -1;
@ -7746,6 +7747,109 @@ assemble_jump_offsets(struct assembler *a, struct compiler *c)
} while (extended_arg_recompile);
}
// Ensure each basicblock is only put onto the stack once.
#define MAYBE_PUSH(B) do { \
if ((B)->b_visited == 0) { \
*(*stack_top)++ = (B); \
(B)->b_visited = 1; \
} \
} while (0)
static void
scan_block_for_local(int target, basicblock *b, bool unsafe_to_start,
basicblock ***stack_top)
{
bool unsafe = unsafe_to_start;
for (int i = 0; i < b->b_iused; i++) {
struct instr *instr = &b->b_instr[i];
assert(instr->i_opcode != EXTENDED_ARG);
assert(instr->i_opcode != EXTENDED_ARG_QUICK);
assert(instr->i_opcode != LOAD_FAST__LOAD_FAST);
assert(instr->i_opcode != STORE_FAST__LOAD_FAST);
assert(instr->i_opcode != LOAD_CONST__LOAD_FAST);
assert(instr->i_opcode != STORE_FAST__STORE_FAST);
assert(instr->i_opcode != LOAD_FAST__LOAD_CONST);
if (unsafe && instr->i_except != NULL) {
MAYBE_PUSH(instr->i_except);
}
if (instr->i_oparg != target) {
continue;
}
switch (instr->i_opcode) {
case LOAD_FAST_CHECK:
// if this doesn't raise, then var is defined
unsafe = false;
break;
case LOAD_FAST:
if (unsafe) {
instr->i_opcode = LOAD_FAST_CHECK;
}
unsafe = false;
break;
case STORE_FAST:
unsafe = false;
break;
case DELETE_FAST:
unsafe = true;
break;
}
}
if (unsafe) {
// unsafe at end of this block,
// so unsafe at start of next blocks
if (b->b_next && !b->b_nofallthrough) {
MAYBE_PUSH(b->b_next);
}
if (b->b_iused > 0) {
struct instr *last = &b->b_instr[b->b_iused-1];
if (is_jump(last)) {
assert(last->i_target != NULL);
MAYBE_PUSH(last->i_target);
}
}
}
}
#undef MAYBE_PUSH
static int
add_checks_for_loads_of_unknown_variables(struct assembler *a,
struct compiler *c)
{
basicblock **stack = make_cfg_traversal_stack(a->a_entry);
if (stack == NULL) {
return -1;
}
Py_ssize_t nparams = PyList_GET_SIZE(c->u->u_ste->ste_varnames);
int nlocals = (int)PyDict_GET_SIZE(c->u->u_varnames);
for (int target = 0; target < nlocals; target++) {
for (basicblock *b = a->a_entry; b != NULL; b = b->b_next) {
b->b_visited = 0;
}
basicblock **stack_top = stack;
// First pass: find the relevant DFS starting points:
// the places where "being uninitialized" originates,
// which are the entry block and any DELETE_FAST statements.
if (target >= nparams) {
// only non-parameter locals start out uninitialized.
*(stack_top++) = a->a_entry;
a->a_entry->b_visited = 1;
}
for (basicblock *b = a->a_entry; b != NULL; b = b->b_next) {
scan_block_for_local(target, b, false, &stack_top);
}
// Second pass: Depth-first search to propagate uncertainty
while (stack_top > stack) {
basicblock *b = *--stack_top;
scan_block_for_local(target, b, true, &stack_top);
}
}
PyMem_Free(stack);
return 0;
}
static PyObject *
dict_keys_inorder(PyObject *dict, Py_ssize_t offset)
{
@ -8385,6 +8489,10 @@ assemble(struct compiler *c, int addNone)
/* Order of basic blocks must have been determined by now */
normalize_jumps(&a);
if (add_checks_for_loads_of_unknown_variables(&a, c) < 0) {
goto error;
}
/* Can't modify the bytecode after computing jump offsets. */
assemble_jump_offsets(&a, c);

View file

@ -126,7 +126,7 @@ static void *opcode_targets[256] = {
&&TARGET_LOAD_FAST,
&&TARGET_STORE_FAST,
&&TARGET_DELETE_FAST,
&&TARGET_LOAD_METHOD_WITH_VALUES,
&&TARGET_LOAD_FAST_CHECK,
&&TARGET_POP_JUMP_FORWARD_IF_NOT_NONE,
&&TARGET_POP_JUMP_FORWARD_IF_NONE,
&&TARGET_RAISE_VARARGS,
@ -140,9 +140,9 @@ static void *opcode_targets[256] = {
&&TARGET_STORE_DEREF,
&&TARGET_DELETE_DEREF,
&&TARGET_JUMP_BACKWARD,
&&TARGET_RESUME_QUICK,
&&TARGET_LOAD_METHOD_WITH_VALUES,
&&TARGET_CALL_FUNCTION_EX,
&&TARGET_STORE_ATTR_ADAPTIVE,
&&TARGET_RESUME_QUICK,
&&TARGET_EXTENDED_ARG,
&&TARGET_LIST_APPEND,
&&TARGET_SET_ADD,
@ -152,30 +152,31 @@ static void *opcode_targets[256] = {
&&TARGET_YIELD_VALUE,
&&TARGET_RESUME,
&&TARGET_MATCH_CLASS,
&&TARGET_STORE_ATTR_ADAPTIVE,
&&TARGET_STORE_ATTR_INSTANCE_VALUE,
&&TARGET_STORE_ATTR_SLOT,
&&TARGET_FORMAT_VALUE,
&&TARGET_BUILD_CONST_KEY_MAP,
&&TARGET_BUILD_STRING,
&&TARGET_STORE_ATTR_SLOT,
&&TARGET_STORE_ATTR_WITH_HINT,
&&TARGET_STORE_FAST__LOAD_FAST,
&&TARGET_LOAD_METHOD,
&&TARGET_STORE_FAST__STORE_FAST,
&&TARGET_STORE_FAST__LOAD_FAST,
&&TARGET_LIST_EXTEND,
&&TARGET_SET_UPDATE,
&&TARGET_DICT_MERGE,
&&TARGET_DICT_UPDATE,
&&TARGET_STORE_FAST__STORE_FAST,
&&TARGET_STORE_SUBSCR_ADAPTIVE,
&&TARGET_STORE_SUBSCR_DICT,
&&TARGET_STORE_SUBSCR_LIST_INT,
&&TARGET_UNPACK_SEQUENCE_ADAPTIVE,
&&TARGET_UNPACK_SEQUENCE_LIST,
&&TARGET_CALL,
&&TARGET_KW_NAMES,
&&TARGET_POP_JUMP_BACKWARD_IF_NOT_NONE,
&&TARGET_POP_JUMP_BACKWARD_IF_NONE,
&&TARGET_POP_JUMP_BACKWARD_IF_FALSE,
&&TARGET_POP_JUMP_BACKWARD_IF_TRUE,
&&TARGET_UNPACK_SEQUENCE_LIST,
&&TARGET_UNPACK_SEQUENCE_TUPLE,
&&TARGET_UNPACK_SEQUENCE_TWO_TUPLE,
&&_unknown_opcode,
@ -253,6 +254,5 @@ static void *opcode_targets[256] = {
&&_unknown_opcode,
&&_unknown_opcode,
&&_unknown_opcode,
&&_unknown_opcode,
&&TARGET_DO_TRACING
};