void _PyCfgBuilder_Fini(_PyCfgBuilder *g);
int _PyCfg_OptimizeCodeUnit(_PyCfgBuilder *g, PyObject *consts, PyObject *const_cache,
- int code_flags, int nlocals, int nparams, int firstlineno);
-int _PyCfg_Stackdepth(_PyCfgBasicblock *entryblock, int code_flags);
+ int nlocals, int nparams, int firstlineno);
+int _PyCfg_Stackdepth(_PyCfgBuilder *g);
void _PyCfg_ConvertPseudoOps(_PyCfgBasicblock *entryblock);
int _PyCfg_ResolveJumps(_PyCfgBuilder *g);
('LOAD_NAME', 1, 11),
('POP_JUMP_IF_TRUE', lbl := self.Label(), 12),
('LOAD_CONST', 2, 13),
+ ('RETURN_VALUE', 13),
lbl,
('LOAD_CONST', 3, 14),
('RETURN_VALUE', 14),
expected_insts = [
('LOAD_NAME', 1, 11),
('POP_JUMP_IF_TRUE', lbl := self.Label(), 12),
- ('LOAD_CONST', 1, 13),
+ ('RETURN_CONST', 1, 13),
lbl,
('RETURN_CONST', 2, 14),
]
('STORE_FAST', 1, 4),
('STORE_FAST', 1, 4),
('POP_TOP', 0, 4),
+ ('LOAD_CONST', 0, 5),
('RETURN_VALUE', 5)
]
expected_insts = [
('NOP', 0, 3),
('STORE_FAST', 1, 4),
('POP_TOP', 0, 4),
- ('RETURN_VALUE', 5)
+ ('RETURN_CONST', 0)
]
self.cfg_optimization_test(insts, expected_insts, consts=list(range(3)), nlocals=1)
('STORE_FAST', 1, 4),
('STORE_FAST', 1, 4),
('STORE_FAST', 1, 4),
+ ('LOAD_CONST', 0, 5),
('RETURN_VALUE', 5)
]
expected_insts = [
('NOP', 0, 3),
('POP_TOP', 0, 4),
('STORE_FAST', 1, 4),
- ('RETURN_VALUE', 5)
+ ('RETURN_CONST', 0, 5)
]
self.cfg_optimization_test(insts, expected_insts, consts=list(range(3)), nlocals=1)
('STORE_FAST', 1, 4),
('STORE_FAST', 1, 5),
('STORE_FAST', 1, 6),
+ ('LOAD_CONST', 0, 5),
('RETURN_VALUE', 5)
]
- self.cfg_optimization_test(insts, insts, consts=list(range(3)), nlocals=1)
+ expected_insts = [
+ ('LOAD_CONST', 0, 1),
+ ('LOAD_CONST', 1, 2),
+ ('LOAD_CONST', 2, 3),
+ ('STORE_FAST', 1, 4),
+ ('STORE_FAST', 1, 5),
+ ('STORE_FAST', 1, 6),
+ ('RETURN_CONST', 0, 5)
+ ]
+ self.cfg_optimization_test(insts, expected_insts, consts=list(range(3)), nlocals=1)
if __name__ == "__main__":
return fixed;
}
+#define IS_GENERATOR(CF) \
+ ((CF) & (CO_GENERATOR | CO_COROUTINE | CO_ASYNC_GENERATOR))
+
static int
insert_prefix_instructions(_PyCompile_CodeUnitMetadata *umd, basicblock *entryblock,
int *fixed, int nfreevars, int code_flags)
assert(umd->u_firstlineno > 0);
/* Add the generator prefix instructions. */
- if (code_flags & (CO_GENERATOR | CO_COROUTINE | CO_ASYNC_GENERATOR)) {
+ if (IS_GENERATOR(code_flags)) {
+ /* Note that RETURN_GENERATOR + POP_TOP have a net stack effect
+ * of 0. This is because RETURN_GENERATOR pushes an element
+ * with _PyFrame_StackPush before switching stacks.
+ */
cfg_instr make_gen = {
.i_opcode = RETURN_GENERATOR,
.i_oparg = 0,
int nparams = (int)PyList_GET_SIZE(u->u_ste->ste_varnames);
int nlocals = (int)PyDict_GET_SIZE(u->u_metadata.u_varnames);
assert(u->u_metadata.u_firstlineno);
- if (_PyCfg_OptimizeCodeUnit(&g, consts, const_cache, code_flags, nlocals,
+ if (_PyCfg_OptimizeCodeUnit(&g, consts, const_cache, nlocals,
nparams, u->u_metadata.u_firstlineno) < 0) {
goto error;
}
- /** Assembly **/
- int nlocalsplus = prepare_localsplus(&u->u_metadata, &g, code_flags);
- if (nlocalsplus < 0) {
+ int stackdepth = _PyCfg_Stackdepth(&g);
+ if (stackdepth < 0) {
goto error;
}
- int maxdepth = _PyCfg_Stackdepth(g.g_entryblock, code_flags);
- if (maxdepth < 0) {
+ /* prepare_localsplus adds instructions for generators that push
+ * and pop an item on the stack. This assertion makes sure there
+ * is space on the stack for that.
+ * It should always be true, because at least one expression is
+ * required to turn a function into a generator.
+ */
+ assert(!(IS_GENERATOR(code_flags) && stackdepth == 0));
+
+ /** Assembly **/
+ int nlocalsplus = prepare_localsplus(&u->u_metadata, &g, code_flags);
+ if (nlocalsplus < 0) {
goto error;
}
}
co = _PyAssemble_MakeCodeObject(&u->u_metadata, const_cache, consts,
- maxdepth, &optimized_instrs, nlocalsplus,
+ stackdepth, &optimized_instrs, nlocalsplus,
code_flags, filename);
error:
if (instructions_to_cfg(instructions, &g) < 0) {
goto error;
}
- int code_flags = 0, nparams = 0, firstlineno = 1;
- if (_PyCfg_OptimizeCodeUnit(&g, consts, const_cache, code_flags, nlocals,
+ int nparams = 0, firstlineno = 1;
+ if (_PyCfg_OptimizeCodeUnit(&g, consts, const_cache, nlocals,
nparams, firstlineno) < 0) {
goto error;
}
goto error;
}
- int code_flags = 0;
- int nlocalsplus = prepare_localsplus(umd, &g, code_flags);
- if (nlocalsplus < 0) {
+ int stackdepth = _PyCfg_Stackdepth(&g);
+ if (stackdepth < 0) {
goto error;
}
- int maxdepth = _PyCfg_Stackdepth(g.g_entryblock, code_flags);
- if (maxdepth < 0) {
+ int code_flags = 0;
+ int nlocalsplus = prepare_localsplus(umd, &g, code_flags);
+ if (nlocalsplus < 0) {
goto error;
}
goto error;
}
co = _PyAssemble_MakeCodeObject(umd, const_cache,
- consts, maxdepth, &optimized_instrs,
+ consts, stackdepth, &optimized_instrs,
nlocalsplus, code_flags, filename);
Py_DECREF(consts);
return stack;
}
-Py_LOCAL_INLINE(void)
+Py_LOCAL_INLINE(int)
stackdepth_push(basicblock ***sp, basicblock *b, int depth)
{
- assert(b->b_startdepth < 0 || b->b_startdepth == depth);
+ if (!(b->b_startdepth < 0 || b->b_startdepth == depth)) {
+ PyErr_Format(PyExc_ValueError, "Invalid CFG, inconsistent stackdepth");
+ return ERROR;
+ }
if (b->b_startdepth < depth && b->b_startdepth < 100) {
assert(b->b_startdepth < 0);
b->b_startdepth = depth;
*(*sp)++ = b;
}
+ return SUCCESS;
}
/* Find the flow path that needs the largest stack. We assume that
* cycles in the flow graph have no net effect on the stack depth.
*/
int
-_PyCfg_Stackdepth(basicblock *entryblock, int code_flags)
+_PyCfg_Stackdepth(cfg_builder *g)
{
+ basicblock *entryblock = g->g_entryblock;
for (basicblock *b = entryblock; b != NULL; b = b->b_next) {
b->b_startdepth = INT_MIN;
}
return ERROR;
}
+
+ int stackdepth = -1;
int maxdepth = 0;
basicblock **sp = stack;
- if (code_flags & (CO_GENERATOR | CO_COROUTINE | CO_ASYNC_GENERATOR)) {
- stackdepth_push(&sp, entryblock, 1);
- } else {
- stackdepth_push(&sp, entryblock, 0);
+ if (stackdepth_push(&sp, entryblock, 0) < 0) {
+ goto error;
}
-
while (sp != stack) {
basicblock *b = *--sp;
int depth = b->b_startdepth;
basicblock *next = b->b_next;
for (int i = 0; i < b->b_iused; i++) {
cfg_instr *instr = &b->b_instr[i];
- int effect = PyCompile_OpcodeStackEffectWithJump(instr->i_opcode, instr->i_oparg, 0);
+ int effect = PyCompile_OpcodeStackEffectWithJump(
+ instr->i_opcode, instr->i_oparg, 0);
if (effect == PY_INVALID_STACK_EFFECT) {
PyErr_Format(PyExc_SystemError,
- "compiler PyCompile_OpcodeStackEffectWithJump(opcode=%d, arg=%i) failed",
+ "Invalid stack effect for opcode=%d, arg=%i",
instr->i_opcode, instr->i_oparg);
- return ERROR;
+ goto error;
}
int new_depth = depth + effect;
- assert(new_depth >= 0); /* invalid code or bug in stackdepth() */
+ if (new_depth < 0) {
+ PyErr_Format(PyExc_ValueError,
+ "Invalid CFG, stack underflow");
+ goto error;
+ }
if (new_depth > maxdepth) {
maxdepth = new_depth;
}
if (HAS_TARGET(instr->i_opcode)) {
- effect = PyCompile_OpcodeStackEffectWithJump(instr->i_opcode, instr->i_oparg, 1);
- assert(effect != PY_INVALID_STACK_EFFECT);
+ effect = PyCompile_OpcodeStackEffectWithJump(
+ instr->i_opcode, instr->i_oparg, 1);
+ if (effect == PY_INVALID_STACK_EFFECT) {
+ PyErr_Format(PyExc_SystemError,
+ "Invalid stack effect for opcode=%d, arg=%i",
+ instr->i_opcode, instr->i_oparg);
+ goto error;
+ }
int target_depth = depth + effect;
assert(target_depth >= 0); /* invalid code or bug in stackdepth() */
if (target_depth > maxdepth) {
maxdepth = target_depth;
}
- stackdepth_push(&sp, instr->i_target, target_depth);
+ if (stackdepth_push(&sp, instr->i_target, target_depth) < 0) {
+ goto error;
+ }
}
depth = new_depth;
assert(!IS_ASSEMBLER_OPCODE(instr->i_opcode));
}
if (next != NULL) {
assert(BB_HAS_FALLTHROUGH(b));
- stackdepth_push(&sp, next, depth);
+ if (stackdepth_push(&sp, next, depth) < 0) {
+ goto error;
+ }
}
}
+ stackdepth = maxdepth;
+error:
PyMem_Free(stack);
- return maxdepth;
+ return stackdepth;
}
static int
static int
-push_cold_blocks_to_end(cfg_builder *g, int code_flags) {
+push_cold_blocks_to_end(cfg_builder *g) {
basicblock *entryblock = g->g_entryblock;
if (entryblock->b_next == NULL) {
/* single basicblock, no need to reorder */
int
_PyCfg_OptimizeCodeUnit(cfg_builder *g, PyObject *consts, PyObject *const_cache,
- int code_flags, int nlocals, int nparams, int firstlineno)
+ int nlocals, int nparams, int firstlineno)
{
assert(cfg_builder_check(g));
/** Preprocessing **/
g->g_entryblock, nlocals, nparams));
insert_superinstructions(g);
- RETURN_IF_ERROR(push_cold_blocks_to_end(g, code_flags));
+ RETURN_IF_ERROR(push_cold_blocks_to_end(g));
RETURN_IF_ERROR(resolve_line_numbers(g, firstlineno));
return SUCCESS;
}