Skip to main content

Python/flowgraph.c (part 3)

Source:

cpython 3.14 @ ab2d84fe1023/Python/flowgraph.c

This annotation covers the compile-time optimizer. See python_compile9_detail for exception table compilation and compile_try_finally.

Map

LinesSymbolRole
1-80Constant foldingEvaluate pure operations at compile time
81-180Dead code eliminationRemove unreachable instructions
181-280Jump threadingJUMP_FORWARD to JUMP_FORWARD chains
281-380LOAD_CONST + POP_TOPEliminate unused constants
381-500Nop removalStrip no-ops after optimization passes

Reading

Constant folding

// CPython: Python/flowgraph.c:1480 fold_binop
static int
fold_binop(cfg_instr *inst, PyObject **stack, int stack_height)
{
/* Evaluate binary operations on constants at compile time */
if (stack_height < 2) return 0;
PyObject *right = stack[stack_height - 1];
PyObject *left = stack[stack_height - 2];
if (!PyUnicode_CheckExact(left) && !PyLong_CheckExact(left) &&
!PyFloat_CheckExact(left) && !PyTuple_CheckExact(left)) return 0;
PyObject *result = NULL;
switch (inst->i_opcode) {
case BINARY_OP:
if (inst->i_oparg == NB_ADD)
result = PyNumber_Add(left, right);
...
}
if (result == NULL) { PyErr_Clear(); return 0; }
/* Replace with LOAD_CONST result */
...
}

1 + 2 compiles to LOAD_CONST 3 (not LOAD_CONST 1, LOAD_CONST 2, BINARY_OP +). String concatenation ('a' + 'b'LOAD_CONST 'ab'), tuple building ((1, 2, 3) as a literal), and membership tests (x in (1, 2, 3)x in frozenset({1, 2, 3})) are all constant-folded.

Dead code elimination

// CPython: Python/flowgraph.c:1620 mark_reachable
static void
mark_reachable(cfg_builder *g)
{
/* BFS from entry block; unmarked blocks are unreachable */
basicblock **stack = ...;
basicblock *b = g->g_entryblock;
b->b_visited = 1;
while (b != NULL) {
for (int i = 0; i < b->b_iused; i++) {
cfg_instr *instr = &b->b_instr[i];
if (IS_JUMP(instr)) {
basicblock *target = instr->i_target;
if (!target->b_visited) {
target->b_visited = 1;
*++stack = target;
}
}
}
b = *stack--;
}
}

After mark_reachable, any block with b_visited == 0 is unreachable. if False: ... compiles the body but the optimizer removes it. After RETURN_VALUE, any following instructions in the same block are dead.

Jump threading

// CPython: Python/flowgraph.c:1720 jump_thread
static int
jump_thread(cfg_instr *inst, cfg_instr *target)
{
/* If inst jumps to a block whose only instruction is another jump,
update inst to jump directly to the final target */
if (inst->i_opcode == JUMP_FORWARD &&
target->i_opcode == JUMP_FORWARD) {
inst->i_target = target->i_target;
return 1;
}
return 0;
}

Chains like JUMP_FORWARD L1; L1: JUMP_FORWARD L2 are simplified to JUMP_FORWARD L2. This commonly arises from if/elif chains and try/except blocks.

gopy notes

The peephole optimizer is compile/flowgraph_passes.go in gopy. Constant folding uses objects.BinaryOp on objects.Const values. Dead code elimination is flowgraph.MarkReachable. Jump threading is flowgraph.ThreadJumps. The optimizer runs after the initial flowgraph is built, before instruction encoding.