-
-
Notifications
You must be signed in to change notification settings - Fork 609
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix Issue 18026 - Stack overflow in dmd/dtemplate.d:6248, TemplateInstance::needsCodegen() #14787
Conversation
Thanks for your pull request, @ibuclaw! Bugzilla references
Testing this PR locallyIf you don't have a local development environment setup, you can use Digger to test this PR: dub run digger -- build "stable + dmd#14787" |
Tested on weka's codebase, no issues. |
128cb88
to
24c08e8
Compare
…tance::needsCodegen()
Changed so that the nested function returns a ThreeState instead. |
Thx for working on this. - My biggest concern is that iterating over the siblings doesn't clear their |
One nice effect of this, AFAICT, is that |
Though calling
|
I don't like this side effect of |
I'd prefer the assert, as it really shouldn't happen anymore, and then makes my point wrt. missing reset for secondary siblings moot. This was an observation last time I looked at this brainfuck mess, and I was thinking about adding a 2nd Every secondary sibling should only be checked at most once then, when checking the siblings of the primary instance. And the primary one gets its |
Looks like there's no guarantee that non-primary instantiations have been set-up correctly, getting asserts from the |
e6d392f
to
390b448
Compare
@kinke and looks like I still need a https://buildkite.com/dlang/dmd/builds/29578#018598fd-7e4b-47c8-827a-7fb55b19597e |
Yep that should be fine. All the |
Note that I came across this sometime later: https://github.com/dlang/dmd/pull/13731/files |
2363567
to
6739d65
Compare
…st' was set to null
Is this ready? |
How lucky do you feel? |
@@ -6293,6 +6300,8 @@ extern (C++) class TemplateInstance : ScopeDsymbol | |||
minst = tnext.minst; // cache result from non-speculative sibling | |||
return false; | |||
} | |||
else if (needsCodegen != ThreeState.none) | |||
break; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Codecov says this is not covered, is it Codecov's issue or really not covered ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's really not covered, because the interior function never returns ThreeState.no
.
I left it in to guard us in case that were to change in the future, and also to align with the non-allinst branch.
I think these two branches could be factored because the outer shell is mostly a duplicate now, but I'll leave that for something to look at in far future - maybe one of the strategies will have to go anyway because missing symbols is one of the "big problems" at the moment, and the default strategy to prune prune prune is a massive PITA that isn't helping any cause.
This is best reviewed with whitespace changes hidden. https://github.com/dlang/dmd/pull/14787/files?w=1
Problem:
needsCodegen
calls itself recursively, this results in a stack overflow when checking an instance that has a sufficient number of siblings, or is lexically nested deep enough in other template instances, or a quadratic combination of both.To solve: I have isolated the meat of the needsCodegen algorithm (there are two; (1) for
-allinst
, (2) for the default emission strategy). Then, the recursive call for instance siblings has been replaced with afor
loop.How was the "meat" determined?
The first check is done on the "main" template instance (stored in the
tempdecl.instances
AA), which is the same for every sibling. No need to check this repeatedly, so we leave this block alone and save some processing time as well.dmd/compiler/src/dmd/dtemplate.d
Lines 6258 to 6262 in fc695ff
The
tnext
branch in both the default and allinst emission strategies are there for caching the result ofneedsCodegen
only. They can be part of thefor
loop body, no need to include them in the nested function.dmd/compiler/src/dmd/dtemplate.d
Lines 6282 to 6296 in fc695ff
dmd/compiler/src/dmd/dtemplate.d
Lines 6339 to 6356 in fc695ff
Everything between (1) and (2) then is the part of the algorithm used to detect whether codegen is required. These have been moved into static nested functions named
needsCodegenAllInst
(returnsfalse
if no condition hit).dmd/compiler/src/dmd/dtemplate.d
Lines 6268 to 6279 in fc695ff
and
needsCodegenRootOnly
(returnstrue
if no condition hit).dmd/compiler/src/dmd/dtemplate.d
Lines 6322 to 6336 in fc695ff
There is a case to handle in
needsCodegenRootOnly
for when theneedsCodegen
variable istrue
- meaning "do codegen if the ancestor needs it". For this, thetnext
of the instance is set tonull
so that no siblings (or further siblings) are checked.There is still recursive checking done for parents (
tinst
) of the template instance, so the possibility for a stack overflow is still present if we are handling a deeply nested instance (as in, 100,000+ levels of lexical nestedness). I'm willing to leave that risk in until someone presents a reasonable example which triggers that.Are there any cases that I might have missed here? Do we need to check
tnext.errors
on every sibling too?