Skip to content

Commit 400d574

Browse files
committed
Update stack depth calculation to account for new style of finally blocks. Fixes bpo-24340.
1 parent e22251d commit 400d574

File tree

1 file changed

+118
-42
lines changed

1 file changed

+118
-42
lines changed

Python/compile.c

Lines changed: 118 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,12 @@ typedef struct basicblock_ {
6868
unsigned b_seen : 1;
6969
/* b_return is true if a RETURN_VALUE opcode is inserted. */
7070
unsigned b_return : 1;
71+
/* b_finally is true if block is entry to a `finally` section */
72+
unsigned b_finally : 1;
7173
/* depth of stack upon entry of block, computed by stackdepth() */
7274
int b_startdepth;
75+
/* max relative stack depth for this `finally` subgraph */
76+
int b_finallydepth;
7377
/* instruction offset for block, computed by assemble_jump_offsets() */
7478
int b_offset;
7579
} basicblock;
@@ -792,6 +796,13 @@ compiler_use_next_block(struct compiler *c, basicblock *block)
792796
return block;
793797
}
794798

799+
static void
800+
compiler_starts_finally(basicblock *block)
801+
{
802+
assert(block != NULL);
803+
block->b_finally = 1;
804+
}
805+
795806
/* Returns the offset of the next instruction in the current block's
796807
b_instr array. Resizes the b_instr as necessary.
797808
Returns -1 on failure.
@@ -863,8 +874,8 @@ compiler_set_lineno(struct compiler *c, int off)
863874
b->b_instr[off].i_lineno = c->u->u_lineno;
864875
}
865876

866-
int
867-
PyCompile_OpcodeStackEffect(int opcode, int oparg)
877+
static int
878+
inline_delta(int opcode, int oparg)
868879
{
869880
switch (opcode) {
870881
case POP_TOP:
@@ -937,7 +948,7 @@ PyCompile_OpcodeStackEffect(int opcode, int oparg)
937948
case INPLACE_OR:
938949
return -1;
939950
case WITH_EXCEPT_START:
940-
return 2;
951+
return 1;
941952
case RETURN_VALUE:
942953
return -1;
943954
case IMPORT_STAR:
@@ -951,7 +962,7 @@ PyCompile_OpcodeStackEffect(int opcode, int oparg)
951962
case POP_BLOCK:
952963
return 0;
953964
case POP_EXCEPT:
954-
return 0; /* -3 except if bad bytecode */
965+
return -3;
955966
case RERAISE:
956967
return -3;
957968

@@ -964,7 +975,7 @@ PyCompile_OpcodeStackEffect(int opcode, int oparg)
964975
case UNPACK_EX:
965976
return (oparg&0xFF) + (oparg>>8);
966977
case FOR_ITER:
967-
return 1; /* or -1, at end of iterator */
978+
return 1;
968979

969980
case STORE_ATTR:
970981
return -2;
@@ -1003,14 +1014,17 @@ PyCompile_OpcodeStackEffect(int opcode, int oparg)
10031014
case IMPORT_FROM:
10041015
return 1;
10051016

1006-
case JUMP_FORWARD:
1007-
case JUMP_IF_TRUE_OR_POP: /* -1 if jump not taken */
1008-
case JUMP_IF_FALSE_OR_POP: /* "" */
1017+
case JUMP_FORWARD: /* These always branch, so this value is meaningless. */
10091018
case JUMP_ABSOLUTE:
10101019
case END_ITER:
1020+
return 0;
10111021
case JUMP_FINALLY:
10121022
return 0;
10131023

1024+
case JUMP_IF_TRUE_OR_POP: /* 0 if jump taken */
1025+
case JUMP_IF_FALSE_OR_POP: /* "" */
1026+
return -1;
1027+
10141028
case LOAD_ADDR:
10151029
return 1;
10161030

@@ -1022,12 +1036,8 @@ PyCompile_OpcodeStackEffect(int opcode, int oparg)
10221036
return 1;
10231037

10241038
case SETUP_EXCEPT:
1025-
return 6; /* can push 3 values for the new exception
1026-
+ 3 others for the previous exception state */
10271039
case SETUP_WITH:
1028-
return 5; /* can push 3 values for the new exception
1029-
+ 3 others for the previous exception state
1030-
-1 for the context manager. */
1040+
return 0;
10311041

10321042
case BEFORE_WITH:
10331043
return 1;
@@ -1093,6 +1103,56 @@ PyCompile_OpcodeStackEffect(int opcode, int oparg)
10931103
return PY_INVALID_STACK_EFFECT; /* not reachable */
10941104
}
10951105

1106+
static int
1107+
branch_delta(int opcode) {
1108+
switch(opcode) {
1109+
case POP_JUMP_IF_FALSE:
1110+
case POP_JUMP_IF_TRUE:
1111+
return -1;
1112+
case FOR_ITER:
1113+
return -1;
1114+
case JUMP_FORWARD:
1115+
case END_ITER:
1116+
case JUMP_ABSOLUTE:
1117+
case LOAD_ADDR:
1118+
case JUMP_IF_TRUE_OR_POP: /* -1 if jump not taken */
1119+
case JUMP_IF_FALSE_OR_POP: /* "" */
1120+
return 0;
1121+
case JUMP_FINALLY:
1122+
return 1;
1123+
case SETUP_EXCEPT:
1124+
return 6; /* can push 3 values for the new exception
1125+
+ 3 others for the previous exception state */
1126+
case SETUP_WITH:
1127+
return 5; /* can push 3 values for the new exception
1128+
+ 3 others for the previous exception state
1129+
-1 for the context manager. */
1130+
default:
1131+
return PY_INVALID_STACK_EFFECT;
1132+
}
1133+
}
1134+
1135+
static int
1136+
is_terminator(int opcode) {
1137+
switch(opcode) {
1138+
case RETURN_VALUE:
1139+
case RAISE_VARARGS:
1140+
case RERAISE:
1141+
case JUMP_ABSOLUTE:
1142+
case JUMP_FORWARD:
1143+
case END_FINALLY:
1144+
return 1;
1145+
default:
1146+
return 0;
1147+
}
1148+
}
1149+
1150+
int
1151+
PyCompile_OpcodeStackEffect(int opcode, int oparg)
1152+
{
1153+
return inline_delta(opcode, oparg);
1154+
}
1155+
10961156
/* Add an opcode with no argument.
10971157
Returns 0 on failure, 1 on success.
10981158
*/
@@ -2753,6 +2813,7 @@ compiler_try_finally(struct compiler *c, stmt_ty s)
27532813
if (!compiler_push_finally_end(c, finalbody))
27542814
return 0;
27552815
compiler_use_next_block(c, finalbody);
2816+
compiler_starts_finally(finalbody);
27562817
VISIT_SEQ(c, stmt, s->v.Try.finalbody);
27572818
ADDOP(c, END_FINALLY);
27582819
compiler_pop_fblock(c, FINALLY_END, finalbody);
@@ -5173,54 +5234,69 @@ dfs(struct compiler *c, basicblock *b, struct assembler *a)
51735234
}
51745235

51755236
static int
5176-
stackdepth_walk(struct compiler *c, basicblock *b, int depth, int maxdepth)
5237+
stackdepth_walk(struct compiler *c, basicblock *b, int start_depth, int input_maxdepth)
51775238
{
5178-
int i, target_depth, effect;
5239+
int i, depth, effect, maxdepth;
51795240
struct instr *instr;
5180-
if (b->b_seen || b->b_startdepth >= depth)
5241+
depth = start_depth;
5242+
maxdepth = input_maxdepth;
5243+
if (b->b_finally) {
5244+
/* Finally blocks are special. We treat them as a separate CFG, computing their
5245+
* max-depth from 0. Then we add that to the stack depth for each jump to
5246+
* the finally block. */
5247+
if (b->b_seen) {
5248+
int total = start_depth + b->b_finallydepth;
5249+
return total > input_maxdepth ? total : input_maxdepth;
5250+
}
5251+
b->b_finallydepth = 0;
5252+
depth = 0;
5253+
maxdepth = 0;
5254+
}
5255+
else if (b->b_seen || b->b_startdepth >= depth) {
51815256
return maxdepth;
5257+
}
51825258
b->b_seen = 1;
51835259
b->b_startdepth = depth;
5260+
if (depth > maxdepth)
5261+
maxdepth = depth;
51845262
for (i = 0; i < b->b_iused; i++) {
51855263
instr = &b->b_instr[i];
5186-
effect = PyCompile_OpcodeStackEffect(instr->i_opcode, instr->i_oparg);
5264+
effect = inline_delta(instr->i_opcode, instr->i_oparg);
51875265
if (effect == PY_INVALID_STACK_EFFECT) {
51885266
fprintf(stderr, "opcode = %d\n", instr->i_opcode);
51895267
Py_FatalError("PyCompile_OpcodeStackEffect()");
51905268
}
5269+
if (instr->i_jrel || instr->i_jabs) {
5270+
int branch_effect = branch_delta(instr->i_opcode);
5271+
if (branch_effect == PY_INVALID_STACK_EFFECT) {
5272+
fprintf(stderr, "opcode = %d\n", instr->i_opcode);
5273+
Py_FatalError("branch_delta()");
5274+
}
5275+
b->b_finallydepth = maxdepth;
5276+
maxdepth = stackdepth_walk(c, instr->i_target,
5277+
depth + branch_effect, maxdepth);
5278+
}
51915279
depth += effect;
5192-
51935280
if (depth > maxdepth)
51945281
maxdepth = depth;
51955282
assert(depth >= 0); /* invalid code or bug in stackdepth() */
5196-
if (instr->i_jrel || instr->i_jabs) {
5197-
target_depth = depth;
5198-
if (instr->i_opcode == FOR_ITER) {
5199-
target_depth = depth-2;
5200-
}
5201-
else if (instr->i_opcode == SETUP_EXCEPT ||
5202-
instr->i_opcode == SETUP_WITH) {
5203-
target_depth = depth+3;
5204-
if (target_depth > maxdepth)
5205-
maxdepth = target_depth;
5206-
}
5207-
else if (instr->i_opcode == JUMP_IF_TRUE_OR_POP ||
5208-
instr->i_opcode == JUMP_IF_FALSE_OR_POP)
5209-
depth = depth - 1;
5210-
maxdepth = stackdepth_walk(c, instr->i_target,
5211-
target_depth, maxdepth);
5212-
if (instr->i_opcode == JUMP_ABSOLUTE ||
5213-
instr->i_opcode == JUMP_FORWARD ||
5214-
instr->i_opcode == END_ITER) {
5215-
goto out; /* remaining code is dead */
5216-
}
5283+
if (is_terminator(instr->i_opcode)) {
5284+
goto out; /* remaining code is dead */
52175285
}
52185286
}
5219-
if (b->b_next)
5287+
if (b->b_next) {
5288+
b->b_finallydepth = maxdepth;
52205289
maxdepth = stackdepth_walk(c, b->b_next, depth, maxdepth);
5290+
}
52215291
out:
5222-
b->b_seen = 0;
5223-
return maxdepth;
5292+
if (b->b_finally) {
5293+
b->b_finallydepth = maxdepth;
5294+
int total = start_depth + maxdepth;
5295+
return total > input_maxdepth ? total : input_maxdepth;
5296+
} else {
5297+
b->b_seen = 0;
5298+
return maxdepth;
5299+
}
52245300
}
52255301

52265302
/* Find the flow path that needs the largest stack. We assume that

0 commit comments

Comments
 (0)