jameysharp requested wasmtime-compiler-reviewers for a review on PR #8935.
jameysharp requested fitzgen for a review on PR #8935.
jameysharp opened PR #8935 from jameysharp:isle-no-recursion
to bytecodealliance:main
:
In #8919 we learned that
Codegen::emit_block
can overrun the stack when generating Rust source for deeply-nested blocks. Stack usage can be worse than one might expect because ISLE is called from a build script, and those are always built in debug mode.This commit avoids the problem by using an explicit heap-allocated stack instead of recursion.
I recommend turning on the "ignore whitespace" option in your diff tool of choice when reviewing this commit.
The amount of stack space that this recursive function uses is affected by the version of rustc used to compile it, and the amount of stack space available depends on the host platform. As a result, this was only observed on Windows with a recent nightly version of rustc, but it could eventually affect other platforms or compiler versions, especially as ISLE rules grow in complexity.
Note that there are several other places in ISLE which recurse over the user-provided rules. This commit does not change those, but they could also become a problem in the future.
fitzgen submitted PR review:
Broadly LGTM, but the details of the stack's management is a little obscured IMO, so I have some suggestions below. If you feel strongly that I am off base here, however, I can be convinced otherwise. Let me know!
fitzgen created PR review comment:
Can this method take these things as arguments, instead of popping them? IIUC, that would allow the
while let
loop to doctx.stack.pop()
instead ofctx.stack.last_mut()
which would make reasoning about control flow and termination much more straightforward, IMO, since it would all be localized within the same function.
fitzgen created PR review comment:
I can't seem to find anything that pops the
Nested::Arms
entries from the stack -- does that ever happen? If not, is that a bug? I would assume it is?
fitzgen submitted PR review:
Broadly LGTM, but the details of the stack's management is a little obscured IMO, so I have some suggestions below. If you feel strongly that I am off base here, however, I can be convinced otherwise. Let me know!
fitzgen created PR review comment:
Similarly, soft preference for having
begin_nested
(orbegin_block
, and removingbegin_nested
) return the stack entry and having callers do something likelet entry = ctx.begin_nested()?; ctx.stack.push(entry);
For the same reasons, I think this would make it a little easier to understand what's happening here.
fitzgen created PR review comment:
D'oh it is the
else { ctx.end_nested() }
that does the pop.This kinda highlights what I was talking about w.r.t. following control and reasoning about termination, though.
I see that it is a little tricky though because of how we are pumping this iterator and only actually popping this stack entry once the iterator is exhausted. It is, of course, possible to pop the iterator and then push it back on if it is not empty (would require a peekable iterator) and this would be fine. But it might be more natural or "simpler" (depending on your taste) to instead push a separate stack entry for each item in the iterator. This latter approach doesn't require any extra is-it-done-or-not wrangling, which is kind of nice. It does use probably mean more entries pushed onto the stack and therefore potentially a larger heap allocation, but I don't think that matters at all here.
github-actions[bot] commented on PR #8935:
Subscribe to Label Action
cc @cfallin, @fitzgen
<details>
This issue or pull request has been labeled: "cranelift", "isle"Thus the following users have been cc'd because of the following labels:
- cfallin: isle
- fitzgen: isle
To subscribe or unsubscribe from this label, edit the <code>.github/subscribe-to-label.json</code> configuration file.
Learn more.
</details>
jameysharp updated PR #8935.
jameysharp commented on PR #8935:
I've now made the stack local to
emit_block
, and also usedpop
instead oflast
. I don't think the result can be honestly said to be clear, but I agree that it's less unclear than it was before :sweat_smile:It's actually important to have some special treatment of the end of a list of cases or match-arms: we need to print some stuff and reset some state about what's still in scope. So if I take your suggestion of pushing each case/arm individually onto the stack, I also would need to push something like
Nested::EndBlock { last_line, scope }
first, which is certainly possible but is a fair bit more complexity each time stuff gets pushed onto the stack. Since that happens in more places (five) than calls toend_block
(two), I feel like that's overall worse. But I'm still open to suggestions, and also may think differently about this after I get some sleep.
fitzgen submitted PR review:
I like this version a lot better, thank you!
fitzgen merged PR #8935.
Last updated: Dec 23 2024 at 12:05 UTC