Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 782d6fe

Browse files
bpo-31113: Get rid of recursion in the compiler for normal control flow. (#3015)
1 parent 0a2da50 commit 782d6fe

3 files changed

Lines changed: 110 additions & 78 deletions

File tree

Lib/test/test_compile.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -671,6 +671,11 @@ def __fspath__(self):
671671

672672
compile("42", PathLike("test_compile_pathlike"), "single")
673673

674+
def test_stack_overflow(self):
675+
# bpo-31113: Stack overflow when compile a long sequence of
676+
# complex statements.
677+
compile("if a: b\n" * 200000, "<dummy>", "exec")
678+
674679

675680
class TestExpressionStackSize(unittest.TestCase):
676681
# These tests check that the computed stack size for a code object
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Get rid of recursion in the compiler for normal control flow.

Python/compile.c

Lines changed: 104 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -4920,83 +4920,42 @@ struct assembler {
49204920
};
49214921

49224922
static void
4923-
dfs(struct compiler *c, basicblock *b, struct assembler *a)
4923+
dfs(struct compiler *c, basicblock *b, struct assembler *a, int end)
49244924
{
4925-
int i;
4926-
struct instr *instr = NULL;
4927-
4928-
if (b->b_seen)
4929-
return;
4930-
b->b_seen = 1;
4931-
if (b->b_next != NULL)
4932-
dfs(c, b->b_next, a);
4933-
for (i = 0; i < b->b_iused; i++) {
4934-
instr = &b->b_instr[i];
4935-
if (instr->i_jrel || instr->i_jabs)
4936-
dfs(c, instr->i_target, a);
4925+
int i, j;
4926+
4927+
/* Get rid of recursion for normal control flow.
4928+
Since the number of blocks is limited, unused space in a_postorder
4929+
(from a_nblocks to end) can be used as a stack for still not ordered
4930+
blocks. */
4931+
for (j = end; b && !b->b_seen; b = b->b_next) {
4932+
b->b_seen = 1;
4933+
assert(a->a_nblocks < j);
4934+
a->a_postorder[--j] = b;
4935+
}
4936+
while (j < end) {
4937+
b = a->a_postorder[j++];
4938+
for (i = 0; i < b->b_iused; i++) {
4939+
struct instr *instr = &b->b_instr[i];
4940+
if (instr->i_jrel || instr->i_jabs)
4941+
dfs(c, instr->i_target, a, j);
4942+
}
4943+
assert(a->a_nblocks < j);
4944+
a->a_postorder[a->a_nblocks++] = b;
49374945
}
4938-
a->a_postorder[a->a_nblocks++] = b;
49394946
}
49404947

4941-
static int
4942-
stackdepth_walk(struct compiler *c, basicblock *b, int depth, int maxdepth)
4948+
Py_LOCAL_INLINE(void)
4949+
stackdepth_push(basicblock ***sp, basicblock *b, int depth)
49434950
{
4944-
int i, new_depth, target_depth, effect;
4945-
struct instr *instr;
4946-
assert(!b->b_seen || b->b_startdepth == depth);
4947-
if (b->b_seen || b->b_startdepth >= depth) {
4948-
return maxdepth;
4949-
}
4950-
/* Guard against infinite recursion */
4951-
b->b_seen = 1;
4952-
b->b_startdepth = depth;
4953-
for (i = 0; i < b->b_iused; i++) {
4954-
instr = &b->b_instr[i];
4955-
effect = stack_effect(instr->i_opcode, instr->i_oparg, 0);
4956-
if (effect == PY_INVALID_STACK_EFFECT) {
4957-
fprintf(stderr, "opcode = %d\n", instr->i_opcode);
4958-
Py_FatalError("PyCompile_OpcodeStackEffect()");
4959-
}
4960-
new_depth = depth + effect;
4961-
if (new_depth > maxdepth) {
4962-
maxdepth = new_depth;
4963-
}
4964-
assert(new_depth >= 0); /* invalid code or bug in stackdepth() */
4965-
if (instr->i_jrel || instr->i_jabs) {
4966-
/* Recursively inspect jump target */
4967-
effect = stack_effect(instr->i_opcode, instr->i_oparg, 1);
4968-
assert(effect != PY_INVALID_STACK_EFFECT);
4969-
target_depth = depth + effect;
4970-
if (target_depth > maxdepth) {
4971-
maxdepth = target_depth;
4972-
}
4973-
assert(target_depth >= 0); /* invalid code or bug in stackdepth() */
4974-
if (instr->i_opcode == CONTINUE_LOOP) {
4975-
/* Pops a variable number of values from the stack,
4976-
* but the target should be already proceeding.
4977-
*/
4978-
assert(instr->i_target->b_seen);
4979-
assert(instr->i_target->b_startdepth <= depth);
4980-
goto out; /* remaining code is dead */
4981-
}
4982-
maxdepth = stackdepth_walk(c, instr->i_target,
4983-
target_depth, maxdepth);
4984-
}
4985-
depth = new_depth;
4986-
if (instr->i_opcode == JUMP_ABSOLUTE ||
4987-
instr->i_opcode == JUMP_FORWARD ||
4988-
instr->i_opcode == RETURN_VALUE ||
4989-
instr->i_opcode == RAISE_VARARGS ||
4990-
instr->i_opcode == BREAK_LOOP)
4991-
{
4992-
goto out; /* remaining code is dead */
4993-
}
4951+
/* XXX b->b_startdepth > depth only for the target of SETUP_FINALLY,
4952+
* SETUP_WITH and SETUP_ASYNC_WITH. */
4953+
assert(b->b_startdepth < 0 || b->b_startdepth >= depth);
4954+
if (b->b_startdepth < depth) {
4955+
assert(b->b_startdepth < 0);
4956+
b->b_startdepth = depth;
4957+
*(*sp)++ = b;
49944958
}
4995-
if (b->b_next)
4996-
maxdepth = stackdepth_walk(c, b->b_next, depth, maxdepth);
4997-
out:
4998-
b->b_seen = 0;
4999-
return maxdepth;
50004959
}
50014960

50024961
/* Find the flow path that needs the largest stack. We assume that
@@ -5005,16 +4964,79 @@ stackdepth_walk(struct compiler *c, basicblock *b, int depth, int maxdepth)
50054964
static int
50064965
stackdepth(struct compiler *c)
50074966
{
5008-
basicblock *b, *entryblock;
5009-
entryblock = NULL;
4967+
basicblock *b, *entryblock = NULL;
4968+
basicblock **stack, **sp;
4969+
int nblocks = 0, maxdepth = 0;
50104970
for (b = c->u->u_blocks; b != NULL; b = b->b_list) {
5011-
b->b_seen = 0;
50124971
b->b_startdepth = INT_MIN;
50134972
entryblock = b;
4973+
nblocks++;
50144974
}
50154975
if (!entryblock)
50164976
return 0;
5017-
return stackdepth_walk(c, entryblock, 0, 0);
4977+
stack = (basicblock **)PyObject_Malloc(sizeof(basicblock *) * nblocks);
4978+
if (!stack) {
4979+
PyErr_NoMemory();
4980+
return -1;
4981+
}
4982+
4983+
sp = stack;
4984+
stackdepth_push(&sp, entryblock, 0);
4985+
while (sp != stack) {
4986+
b = *--sp;
4987+
int depth = b->b_startdepth;
4988+
assert(depth >= 0);
4989+
basicblock *next = b->b_next;
4990+
for (int i = 0; i < b->b_iused; i++) {
4991+
struct instr *instr = &b->b_instr[i];
4992+
int effect = stack_effect(instr->i_opcode, instr->i_oparg, 0);
4993+
if (effect == PY_INVALID_STACK_EFFECT) {
4994+
fprintf(stderr, "opcode = %d\n", instr->i_opcode);
4995+
Py_FatalError("PyCompile_OpcodeStackEffect()");
4996+
}
4997+
int new_depth = depth + effect;
4998+
if (new_depth > maxdepth) {
4999+
maxdepth = new_depth;
5000+
}
5001+
assert(depth >= 0); /* invalid code or bug in stackdepth() */
5002+
if (instr->i_jrel || instr->i_jabs) {
5003+
effect = stack_effect(instr->i_opcode, instr->i_oparg, 1);
5004+
assert(effect != PY_INVALID_STACK_EFFECT);
5005+
int target_depth = depth + effect;
5006+
if (target_depth > maxdepth) {
5007+
maxdepth = target_depth;
5008+
}
5009+
assert(target_depth >= 0); /* invalid code or bug in stackdepth() */
5010+
if (instr->i_opcode == CONTINUE_LOOP) {
5011+
/* Pops a variable number of values from the stack,
5012+
* but the target should be already proceeding.
5013+
*/
5014+
assert(instr->i_target->b_startdepth >= 0);
5015+
assert(instr->i_target->b_startdepth <= depth);
5016+
/* remaining code is dead */
5017+
next = NULL;
5018+
break;
5019+
}
5020+
stackdepth_push(&sp, instr->i_target, target_depth);
5021+
}
5022+
depth = new_depth;
5023+
if (instr->i_opcode == JUMP_ABSOLUTE ||
5024+
instr->i_opcode == JUMP_FORWARD ||
5025+
instr->i_opcode == RETURN_VALUE ||
5026+
instr->i_opcode == RAISE_VARARGS ||
5027+
instr->i_opcode == BREAK_LOOP)
5028+
{
5029+
/* remaining code is dead */
5030+
next = NULL;
5031+
break;
5032+
}
5033+
}
5034+
if (next != NULL) {
5035+
stackdepth_push(&sp, next, depth);
5036+
}
5037+
}
5038+
PyObject_Free(stack);
5039+
return maxdepth;
50185040
}
50195041

50205042
static int
@@ -5320,7 +5342,7 @@ makecode(struct compiler *c, struct assembler *a)
53205342
Py_ssize_t nlocals;
53215343
int nlocals_int;
53225344
int flags;
5323-
int argcount, kwonlyargcount;
5345+
int argcount, kwonlyargcount, maxdepth;
53245346

53255347
tmp = dict_keys_inorder(c->u->u_consts, 0);
53265348
if (!tmp)
@@ -5360,8 +5382,12 @@ makecode(struct compiler *c, struct assembler *a)
53605382

53615383
argcount = Py_SAFE_DOWNCAST(c->u->u_argcount, Py_ssize_t, int);
53625384
kwonlyargcount = Py_SAFE_DOWNCAST(c->u->u_kwonlyargcount, Py_ssize_t, int);
5385+
maxdepth = stackdepth(c);
5386+
if (maxdepth < 0) {
5387+
goto error;
5388+
}
53635389
co = PyCode_New(argcount, kwonlyargcount,
5364-
nlocals_int, stackdepth(c), flags,
5390+
nlocals_int, maxdepth, flags,
53655391
bytecode, consts, names, varnames,
53665392
freevars, cellvars,
53675393
c->c_filename, c->u->u_name,
@@ -5448,7 +5474,7 @@ assemble(struct compiler *c, int addNone)
54485474
}
54495475
if (!assemble_init(&a, nblocks, c->u->u_firstlineno))
54505476
goto error;
5451-
dfs(c, entryblock, &a);
5477+
dfs(c, entryblock, &a, nblocks);
54525478

54535479
/* Can't modify the bytecode after computing jump offsets. */
54545480
assemble_jump_offsets(&a, c);

0 commit comments

Comments
 (0)