Skip to content
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

wasm-opt: Optimize for label count #5530

Closed
iBicha opened this issue Mar 1, 2023 · 7 comments
Closed

wasm-opt: Optimize for label count #5530

iBicha opened this issue Mar 1, 2023 · 7 comments

Comments

@iBicha
Copy link

iBicha commented Mar 1, 2023

Looking for advice on a long shot.

The goal here is to run arbitrary wasm code on a Roku TV using brightscript

There is a tool that translates wasm modules to brightscript (wasm2brs). Brilliant!

The problem is that the brightscript runtime has limitations that wasm don't have.
One of these limitations is label count within a funciton (goto instruction labels).
Code fails to run as the label count gets closer to 256 (128 is a soft limit, and 256 is a hard limit, based on testing, see here)

For bigger projects, it's normal to a wasm function with hundreds of labels.
wasm-opt is being used to help with that (using -O4), but a lot of the times it's not good enough to keep the count level below the limit.

My question is, is there a way to transform a wasm module, in a way that functions do not exceed a certain number of labels?


Another approach we tried is to translate a wasm runtime (e.g. wasm3) from wasm format to brightscript, and use that to load another wasm module. This approach does not hit the label count issue, but it faces another limitation of the BrightScript runtime, which is stack depth (even loading a hello world wasm leads to a stack overflow).

@kripken
Copy link
Member

kripken commented Mar 1, 2023

To limit the number of labels you'd need to do two things:

  1. Break up functions with too many labels. A function with 300 labels after each other would need to be two functions with less than 256, and the first calls the second.
  2. Break up deeply-nested labels. It's common to have a single br_table that targets much more than 256 labels, so simple function splitting wouldn't help. You'd need to do something like have a chain of ifs, and to split out code to other functions as needed.

Both are doable but would take some serious work.

Getting a wasm runtime to work seems easier to me. Do you know why it wasm3 hits a stack depth issue? Note that even on hello world there may well be one of those big br_table instructions, inside printf or such; if that's the issue then perhaps wasm3 could be written to use a stack instead of recursion or something like that. Or you can try another interpreter. The Binaryen interpreter has special code to avoid recursion in those places:

// special-case Block, because Block nesting (in their first element) can be
// incredibly deep
std::vector<Block*> stack;
stack.push_back(curr);
while (curr->list.size() > 0 && curr->list[0]->is<Block>()) {
curr = curr->list[0]->cast<Block>();
stack.push_back(curr);
}

You can also try the wabt interpreter.

@iBicha
Copy link
Author

iBicha commented Mar 1, 2023

Thanks for the reply @kripken !

Break up functions with too many labels

This is not as easy as it sounds. Some functions are simply too big and complex, and there's a good chance to break it when attempting to do so.
Besides, this does not scale for some projects where there's plenty of functions like this unfortunately. This is why my thoughts where "Oh wasm-opt might perhaps be able to do that by splitting functions etc"

Do you know why it wasm3 hits a stack depth issue?

This might be due to how it works https://github.com/wasm3/wasm3/blob/main/docs/Interpreter.md

because M3 execution leans heavily on the native stack, this does create one runtime usage issue

Or you can try another interpreter.

Yeah I would need to find an interpreter that does not use the stack natively, and that can build to wasm, and hopefully 🤞 works.

@MaxGraey
Copy link
Contributor

MaxGraey commented Mar 1, 2023

wasm3 has a quite small stack limits by default due to it applications (IoT) but it has ability to config this: https://github.com/wasm3/wasm3/blob/main/source/m3_config.h#L20

@iBicha
Copy link
Author

iBicha commented Mar 3, 2023

wasm3 has a quite small stack limits by default due to it applications (IoT) but it has ability to config this: https://github.com/wasm3/wasm3/blob/main/source/m3_config.h#L20

That's cool! unfortunately it still hits an overflow for some reason

Edit: If I'm not mistaken, d_m3MaxFunctionStackHeight is to ensure the stack at the wasm runtime level, not at the native stack level. What I need is for the wasm runtime to not use the native stack at all, because it is very limited.

The alternative might be to try another interpreter

@iBicha
Copy link
Author

iBicha commented Mar 3, 2023

@kripken is there a simple way to build the Binaryen interpreter into wasm?

@kripken
Copy link
Member

kripken commented Mar 3, 2023

@iBicha There is already an emscripten port of binaryen, see the CMakeFiles. That emits wasm+JS. Porting to pure wasm with wasi-libc should be easy except that we use C++ exceptions, which they don't support yet, but they will eventually I hope.

@iBicha
Copy link
Author

iBicha commented Apr 7, 2023

Thank you everyone for the help, I'll close this for now

@iBicha iBicha closed this as completed Apr 7, 2023
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

No branches or pull requests

3 participants