Skip to content

bpo-31113: Get rid of recursion in the compiler for normal control flow.#3015

Merged
serhiy-storchaka merged 12 commits intopython:masterfrom
serhiy-storchaka:compile-dfs
Jan 11, 2018
Merged

bpo-31113: Get rid of recursion in the compiler for normal control flow.#3015
serhiy-storchaka merged 12 commits intopython:masterfrom
serhiy-storchaka:compile-dfs

Conversation

@serhiy-storchaka
Copy link
Member

@serhiy-storchaka serhiy-storchaka commented Aug 7, 2017

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

def test_dfs_next(self):
# Issue #31113: Stack overflow when compile a long sequence of
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use the new "bpo-xxx" format.

@vstinner vstinner requested a review from benjaminp August 7, 2017 13:55
for (j = nblocks; b && !b->b_seen; b = b->b_next) {
b->b_seen = 1;
assert(a->a_nblocks < j);
a->a_postorder[--j] = b;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This only works because blocks are emitted in topological order, right? It would be nice to add a comment.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or are you using a_postorder as a stack or worklist?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, since the number of blocks is limited, the space at the end of a_postorder can be used as a stack.

Python/compile.c Outdated
while (j < nblocks) {
b = a->a_postorder[j++];
for (i = 0; i < b->b_iused; i++) {
struct instr *instr = instr = &b->b_instr[i];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand the struct instr *instr = instr part.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch! This is just a copying error.

@pitrou
Copy link
Member

pitrou commented Aug 7, 2017

It's telling that you're still getting a stack overflow on Windows :-)

dfs(c, instr->i_target, a);
int i, j;

/* Get rid of recursion for normal control flow.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps it is possible to get rid of recursion entirely -- perhaps by allocating a separate stack? Or is it unlikely to have a stack overflow though i_target?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps, though this likely will complicate the code. I don't have an example that overflows a stack though i_target. After getting such example I'll try to get rid of recursion entirely.

Python/compile.c Outdated
int i;
basicblock *next;
b = sp[-1];
if (b->b_seen == 2) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps add some comment as to the different values of b_seen? Does 1 mean "queued for processing" and 2 "already processed"?

if (instr->i_jrel || instr->i_jabs)
dfs(c, instr->i_target, a, j);
}
a->a_postorder[a->a_nblocks++] = b;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add something like assert(a->a_nblocks <= j) here? The stack trick is smart but I'd like to make sure it's not buggy.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Python/compile.c Outdated
while (sp != stack) {
int i;
basicblock *next;
b = sp[-1];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why don't we decrement sp here instead of setting b->b_seen = 2 below?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is closer to the current implementation. If just pop a block from the stack and keep b->b_seen == 1, all seems working, but the result is different. I don't know what result is more correct, but larger depth is safer. Clearing b->b_seen after popping a block causes an infinite loop. Current code contains bugs (see bpo-24340), may be after fixing them this code could be simplified.

@pitrou
Copy link
Member

pitrou commented Aug 30, 2017

What is the status of this?

@serhiy-storchaka
Copy link
Member Author

If it is allowed to backport this change to other branches, I think it is ready for committing if you have no objections. If it is 3.7 only, we can wait until bpo-17611 and bpo-24340 be resolved. May be this will make this change slightly simpler (I'm not sure).

@pitrou
Copy link
Member

pitrou commented Aug 30, 2017

I'm wary of backporting this and would prefer it to be 3.7-only.

@pitrou
Copy link
Member

pitrou commented Aug 30, 2017

I think a good experiment would be to take the PR for bpo-17611 and make it non-recursive. It should be similar to this PR in principle.

@serhiy-storchaka serhiy-storchaka merged commit 782d6fe into python:master Jan 11, 2018
@serhiy-storchaka serhiy-storchaka deleted the compile-dfs branch January 11, 2018 18:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants