Skip to content

[NFC] Add more overview docs for stack switching #7806

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

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 35 additions & 3 deletions src/wasm-interpreter.h
Original file line number Diff line number Diff line change
Expand Up @@ -170,9 +170,41 @@ struct FuncData {
// but the shared idea is that to resume code we simply need to get to where we
// were when we suspended, so we have a "resuming" mode in which we walk the IR
// but do not execute normally. While resuming we basically re-wind the stack,
// using data we stashed on the side while unwinding. For example, if we unwind
// an If instruction then we note which arm of the If we unwound from, and then
// when we re-wind we enter that proper arm, etc.
// using data we stashed on the side while unwinding.
//
// The key idea in this approach to suspending and resuming is that to suspend
// you want to unwind the stack - you "jump" back to some outer scope - and to
// reume, we want to rewind the stack - to get everything back exactly the way
// it was, so we can pick things back up. And, to achieve that, we really just
// need two things:
// * To rewind the call stack. If we called foo() and then bar(), we want to
// have foo and bar on the stack, so that when bar finishes, we return to
// foo, etc., as if we never suspended/resumed.
// * To have the same values as before. If we are an i32.add, and we
// suspended in the second arm, we need to have the same value for the
// first arm as before the suspend.
//
// Implementing these is conceptually simple:
// * For control flow, each structure handles itself. For example, if we
// unwind an If instruction then we note which arm of the If we unwound
// from, and then when we re-wind we enter that proper arm. For a Block,
// we can note the index we had executed up to, etc.
// * For values, we just save them (see below on |valueStack| however, as we
Copy link
Member

Choose a reason for hiding this comment

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

Maybe note that this will be done automatically and generically in visit, so the individual visitors do not need to worry about it.

// do an optimization for speed that avoids using that stack unless
// actually necessary).
//
// Once we have those two things handled, pretty much everything else "just
// works," and 99% of instructions need no special handling at all. Even some
// instructions you might think would need custom code do not, like CallRef:
// while that instruction does a call and changes the call stack, it calls the
// value of its last child, so if we restore that child's value while resuming,
// the normal code is exactly what we want (calling that child rewinds the stack
// in exactly the right way). That is, once control flow structures know what to
// do (which is unique to each one, but trivial), and once we have values
// restored, the interpreter "wants" to return to the exact place we suspended
// at, and we just let it do that. (And when it reaches the place we suspended
// from, we do a special operation to stop resuming, and to proceed with normal
// execution, as if we never suspended.)
//
// This is not the most efficient way to pause and resume execution (a program
// counter/goto would be much faster!) but this is very simple to implement in
Expand Down
Loading