MotleyCoderDev / wasm2brs

Bringing the power of WebAssembly to the Roku and supporting languages like C/C++/Rust.
https://motley-coder.com/2020/12/20/webassembly-on-roku/
Other
50 stars 5 forks source link

label count soft limit #7

Closed iBicha closed 1 year ago

iBicha commented 1 year ago

I've been trying to bring label count to under 256, and but I still have a few between 128 and 256. Even though all functions are below the hard limit, I'm still hitting the Label/Line Not Found. (compile error &h0e) kind of error. Is 128 actually a hard limit?

TrevorSundberg commented 1 year ago

If I remember correctly it was difficult to determine their actual behavior of the brightscript compiler. For sure if you go beyond 256 labels it will always fail (they obviously have some instruction that uses a 1 byte label address or something).

However, it also appears like there is some optimization or something they are running that can inadvertently create more labels. I wasn’t able to reverse this behavior and figure out exactly why, hence the soft warning limit of 128 (I found that programs that kept below 128 generally always compiled in BrightScript).

I wish I could provide a better answer but only Roku can :/

TrevorSundberg commented 1 year ago

Whoops didn’t mean to close

iBicha commented 1 year ago

Either way it looks like Roku is not going to do anything about it. https://community.roku.com/t5/Roku-Developer-Program/Label-limit-count-goto-label/m-p/853744

TrevorSundberg commented 1 year ago

I just wanted to mention something that I had some success with was changing optimizations. Some wasm optimizations end up producing a lot of labels for brightscript code, you can try adjusting the optimization level to see if it helps.

I ran into this constantly when trying to compile any of the different JavaScript implementations into BrightScript (even the Rust one). The huge instruction switch statement in every JS implementation always causes this problem.

iBicha commented 1 year ago

Interesting! I'll give it a shot! (although, I don't know if it will help in the case of very large functions)

Edit: For that particular function, optimization brought the label count down from 678 to 317. It helps, but not good enough

iBicha commented 1 year ago

At this point I'm wondering if it would be better to wasm2brs some form of a tiny WASM runtime, and then use that to load the actual wasm we need... but I'm not sure if it's doable or the performance will be good enough or not

TrevorSundberg commented 1 year ago

If you have any interest in extending wasm2brs, I could easily see a mode that if the number of labels exceeds a certain amount, that entire function switches to being interpreted WASM. That way entire program doesn’t have to be interpreted (can get speed benefits in some areas).

Basically the wasm2brs runtime ships with a compiled Wasm interpreter.

iBicha commented 1 year ago

That sounds like it can get pretty complicated, with how to potentially get the interpreted wasm and AOT wasm to play nice together, or share memory.

I'll try to test the wasm runtime when I have some time (it could be added as a sample I guess) then we'll explore from there

iBicha commented 1 year ago

I put wasm3-wasi.wasm in there, and tried running it with

wasi_init(m.w2b_memory, "wasm3-wasi.wasm", {})

To my surprise, it prints fine

Usage:
  wasm3 [options] <file> [args...]
  wasm3 --repl [file]
Options:
  --func <function>     function to run       default: _start
  --stack-size <size>   stack size in bytes   default: 64KB
  --dump-on-trap        dump wasm memory

Which is a very good sign.

Then I tried to add this test file (like the docs) with this

wasi_init(m.w2b_memory, "wasm3-wasi.wasm", {args: ["pkg:/test.wasm"]})

Then it prints absolutely nothing, while locally it should print something like

Hello world
Constructor OK
Hello printf!
Args: test.wasm; 
Now: 1675892696 sec, 413791000 ns
2 + 0 = 2
fib(20) = 6765 [1.526 ms]
=== done ===

I'm not sure if wasm3 can't read the file, or if it's the test wasm that's not printing

=======

wasi_init(m.w2b_memory, "wasm3-wasi.wasm", {args: ["-h"]}) => does not work

I see that wasi_snapshot_preview1_args_sizes_get gets called twice, but wasi_snapshot_preview1_args_get was never called. Perhaps an issue with argv stuff

iBicha commented 1 year ago

Update: Using #8 I got wasm to start running a simple test file. I got hit with a stack overflow. I also tried few other things, and they are all hitting a ERR_STACK_OVERFLOW. At this point I think I'm just going to give up on bringing external libs to BrightScript, because other wasm interpreters are very likely to hit this kind of limit (I could be wrong though).

TrevorSundberg commented 1 year ago

Bravo on getting it running, but I think you're hitting the exact same thing that burned me out on trying to work on Roku as a platform.

And to answer I think that depends on if the WASM interpreter uses the stack for invoking function calls. I've seen different approaches for byte code interpreters where when the call is internally (from a byte code function to another byte code function) they use a heap allocated "stack" rather than making an actual function call. This at least prevents interpreters from being bound by the actual host stack size (somewhat). It may be that a WASM interpreter exists out there that has this property, but I don't know the implementation details of each.

But to be honest if it's not the stack overflow, you may hit the label limit with another WASM interpreter especially if it uses the common giant switch for bytecode. I really wish BrightScript limitations were just a bit higher.

As for the point of sharing WASM memory with an interpreter, I think that's very possible (not complicated), and if you get to that point ever I can certainly help.

TrevorSundberg commented 1 year ago

Just to add my research here:

Without actually debugging (just reading code) this appears to be where the recursive call happens. Given they have a "ReportNativeStackUsage" call directly below it, I think it pretty heavily implies they hit this similar limit in other host environments: https://github.com/wasm3/wasm3/blob/7dd43c11d86d5006b70f210d6d282bc1ff9daff1/source/m3_env.c#L878

Moreover from https://github.com/wasm3/wasm3/blob/main/docs/Interpreter.md

"M3 execution leans heavily on the native stack"

iBicha commented 1 year ago

Thanks a lot for the detailed response! What you did in this repo is really impressive work, it's sad that it never reached its full potential because of these limitations (limitations that should belong to languages from 40 years ago, not something we use in 2023).

I shared my sentiment with Roku at least, maybe they'll do something about it someday.

The answer might be a search for another wasm runtime. But at the same time, I doubt there will be a system designed with such constraints in mind (I mean, I got a stack overflow with a hello world wasm, stacktrace shows 55 levels...). As you said, it's going to be either the stack of label limit.

Given everything, maybe I'll try another wasm runtime someday, even if it was a long shot

iBicha commented 1 year ago

I'm trying to dig further, so I tried asking here if there was a way for wasm-opt to reduce label count (like auto-split functions)

Someone suggested that wasm3 has configurable stacksize, so I set it to a very small number, but I still get an overflow unfortunately...

#52 Function w2b_readlebsigned_563807466(w2b_p0 As Integer, w2b_p1 As Integer, w2b_p2 As Integer) As Integer
   file/line: pkg:/source/wasm3.out.brs(16442)
#51 Function w2b_compile_looporblock_1242105711(w2b_p0 As Integer, w2b_p1 As Integer) As Integer
   file/line: pkg:/source/wasm3.out.brs(30430)
#50 Function w2b_compileblockstatements_1669925085(w2b_p0 As Integer) As Integer
   file/line: pkg:/source/wasm3.out.brs(32543)
#49 Function w2b_compileblock_507053237(w2b_p0 As Integer, w2b_p1 As Integer, w2b_p2 As Integer) As Integer
   file/line: pkg:/source/wasm3.out.brs(31598)
#48 Function w2b_compile_looporblock_1242105711(w2b_p0 As Integer, w2b_p1 As Integer) As Integer
   file/line: pkg:/source/wasm3.out.brs(30802)
#47 Function w2b_compileblockstatements_1669925085(w2b_p0 As Integer) As Integer
   file/line: pkg:/source/wasm3.out.brs(32543)
#46 Function w2b_compileblock_507053237(w2b_p0 As Integer, w2b_p1 As Integer, w2b_p2 As Integer) As Integer
   file/line: pkg:/source/wasm3.out.brs(31598)
#45 Function w2b_compile_looporblock_1242105711(w2b_p0 As Integer, w2b_p1 As Integer) As Integer
   file/line: pkg:/source/wasm3.out.brs(30802)
#44 Function w2b_compileblockstatements_1669925085(w2b_p0 As Integer) As Integer
   file/line: pkg:/source/wasm3.out.brs(32543)
#43 Function w2b_compileblock_507053237(w2b_p0 As Integer, w2b_p1 As Integer, w2b_p2 As Integer) As Integer
   file/line: pkg:/source/wasm3.out.brs(31598)
#42 Function w2b_compile_looporblock_1242105711(w2b_p0 As Integer, w2b_p1 As Integer) As Integer
   file/line: pkg:/source/wasm3.out.brs(30802)
#41 Function w2b_compileblockstatements_1669925085(w2b_p0 As Integer) As Integer
   file/line: pkg:/source/wasm3.out.brs(32543)
#40 Function w2b_compileblock_507053237(w2b_p0 As Integer, w2b_p1 As Integer, w2b_p2 As Integer) As Integer
   file/line: pkg:/source/wasm3.out.brs(31598)
#39 Function w2b_compile_looporblock_1242105711(w2b_p0 As Integer, w2b_p1 As Integer) As Integer
   file/line: pkg:/source/wasm3.out.brs(30802)
#38 Function w2b_compileblockstatements_1669925085(w2b_p0 As Integer) As Integer
   file/line: pkg:/source/wasm3.out.brs(32543)
#37 Function w2b_compilefunction_794560016(w2b_p0 As Integer) As Integer
   file/line: pkg:/source/wasm3.out.brs(29000)
#36 Function w2b_op_compile_370148360(w2b_p0 As Integer, w2b_p1 As Integer, w2b_p2 As Integer, w2b_p3 As LongInteger, w2b_p4 As Double) As Integer
   file/line: pkg:/source/wasm3.out.brs(28202)
#35 Function w2b_op_copyslot_32_699401536(w2b_p0 As Integer, w2b_p1 As Integer, w2b_p2 As Integer, w2b_p3 As LongInteger, w2b_p4 As Double) As Integer
   file/line: pkg:/source/wasm3.out.brs(20472)
#34 Function w2b_op_entry_244581201(w2b_p0 As Integer, w2b_p1 As Integer, w2b_p2 As Integer, w2b_p3 As LongInteger, w2b_p4 As Double) As Integer
   file/line: pkg:/source/wasm3.out.brs(57576)
#33 Function w2b_op_call_184419003(w2b_p0 As Integer, w2b_p1 As Integer, w2b_p2 As Integer, w2b_p3 As LongInteger, w2b_p4 As Double) As Integer
   file/line: pkg:/source/wasm3.out.brs(28133)
#32 Function w2b_op_compile_370148360(w2b_p0 As Integer, w2b_p1 As Integer, w2b_p2 As Integer, w2b_p3 As LongInteger, w2b_p4 As Double) As Integer
   file/line: pkg:/source/wasm3.out.brs(28231)
#31 Function w2b_op_f32_reinterpret_i32_s_r_2208500173(w2b_p0 As Integer, w2b_p1 As Integer, w2b_p2 As Integer, w2b_p3 As LongInteger, w2b_p4 As Double) As Integer
   file/line: pkg:/source/wasm3.out.brs(54100)
#30 Function w2b_op_i32_add_rs_546374841(w2b_p0 As Integer, w2b_p1 As Integer, w2b_p2 As Integer, w2b_p3 As LongInteger, w2b_p4 As Double) As Integer
   file/line: pkg:/source/wasm3.out.brs(42462)
#29 Function w2b_op_f32_reinterpret_i32_s_r_2208500173(w2b_p0 As Integer, w2b_p1 As Integer, w2b_p2 As Integer, w2b_p3 As LongInteger, w2b_p4 As Double) As Integer
   file/line: pkg:/source/wasm3.out.brs(54100)
#28 Function w2b_op_i32_load_i32_s_910165483(w2b_p0 As Integer, w2b_p1 As Integer, w2b_p2 As Integer, w2b_p3 As LongInteger, w2b_p4 As Double) As Integer
   file/line: pkg:/source/wasm3.out.brs(37117)
#27 Function w2b_op_branchif_r_595920141(w2b_p0 As Integer, w2b_p1 As Integer, w2b_p2 As Integer, w2b_p3 As LongInteger, w2b_p4 As Double) As Integer
   file/line: pkg:/source/wasm3.out.brs(25954)
#26 Function w2b_op_i32_load_u8_s_822216138(w2b_p0 As Integer, w2b_p1 As Integer, w2b_p2 As Integer, w2b_p3 As LongInteger, w2b_p4 As Double) As Integer
   file/line: pkg:/source/wasm3.out.brs(37629)
#25 Function w2b_op_branch_298517389(w2b_p0 As Integer, w2b_p1 As Integer, w2b_p2 As Integer, w2b_p3 As LongInteger, w2b_p4 As Double) As Integer
   file/line: pkg:/source/wasm3.out.brs(18153)
#24 Function w2b_op_branchtable_689374581(w2b_p0 As Integer, w2b_p1 As Integer, w2b_p2 As Integer, w2b_p3 As LongInteger, w2b_p4 As Double) As Integer
   file/line: pkg:/source/wasm3.out.brs(26894)
#23 Function w2b_op_call_184419003(w2b_p0 As Integer, w2b_p1 As Integer, w2b_p2 As Integer, w2b_p3 As LongInteger, w2b_p4 As Double) As Integer
   file/line: pkg:/source/wasm3.out.brs(28158)
#22 Function w2b_op_copyslot_32_699401536(w2b_p0 As Integer, w2b_p1 As Integer, w2b_p2 As Integer, w2b_p3 As LongInteger, w2b_p4 As Double) As Integer
   file/line: pkg:/source/wasm3.out.brs(20472)
#21 Function w2b_op_f32_reinterpret_i32_s_r_2208500173(w2b_p0 As Integer, w2b_p1 As Integer, w2b_p2 As Integer, w2b_p3 As LongInteger, w2b_p4 As Double) As Integer
   file/line: pkg:/source/wasm3.out.brs(54100)
#20 Function w2b_op_i32_add_ss_546505914(w2b_p0 As Integer, w2b_p1 As Integer, w2b_p2 As Integer, w2b_p3 As LongInteger, w2b_p4 As Double) As Integer
   file/line: pkg:/source/wasm3.out.brs(42513)
#19 Function w2b_op_loop_190186201(w2b_p0 As Integer, w2b_p1 As Integer, w2b_p2 As Integer, w2b_p3 As LongInteger, w2b_p4 As Double) As Integer
   file/line: pkg:/source/wasm3.out.brs(30842)
#18 Function w2b_op_copyslot_32_699401536(w2b_p0 As Integer, w2b_p1 As Integer, w2b_p2 As Integer, w2b_p3 As LongInteger, w2b_p4 As Double) As Integer
   file/line: pkg:/source/wasm3.out.brs(20472)
#17 Function w2b_op_setglobal_i32_890897897(w2b_p0 As Integer, w2b_p1 As Integer, w2b_p2 As Integer, w2b_p3 As LongInteger, w2b_p4 As Double) As Integer
   file/line: pkg:/source/wasm3.out.brs(57846)
#16 Function w2b_op_f32_reinterpret_i32_s_r_2208500173(w2b_p0 As Integer, w2b_p1 As Integer, w2b_p2 As Integer, w2b_p3 As LongInteger, w2b_p4 As Double) As Integer
   file/line: pkg:/source/wasm3.out.brs(54100)
#15 Function w2b_op_i32_subtract_ss_1073547001(w2b_p0 As Integer, w2b_p1 As Integer, w2b_p2 As Integer, w2b_p3 As LongInteger, w2b_p4 As Double) As Integer
   file/line: pkg:/source/wasm3.out.brs(42638)
#14 Function w2b_op_getglobal_s32_882640359(w2b_p0 As Integer, w2b_p1 As Integer, w2b_p2 As Integer, w2b_p3 As LongInteger, w2b_p4 As Double) As Integer
   file/line: pkg:/source/wasm3.out.brs(24305)
#13 Function w2b_op_entry_244581201(w2b_p0 As Integer, w2b_p1 As Integer, w2b_p2 As Integer, w2b_p3 As LongInteger, w2b_p4 As Double) As Integer
   file/line: pkg:/source/wasm3.out.brs(57576)
#12 Function w2b_op_call_184419003(w2b_p0 As Integer, w2b_p1 As Integer, w2b_p2 As Integer, w2b_p3 As LongInteger, w2b_p4 As Double) As Integer
   file/line: pkg:/source/wasm3.out.brs(28133)
#11 Function w2b_op_compile_370148360(w2b_p0 As Integer, w2b_p1 As Integer, w2b_p2 As Integer, w2b_p3 As LongInteger, w2b_p4 As Double) As Integer
   file/line: pkg:/source/wasm3.out.brs(28231)
#10 Function w2b_op_entry_244581201(w2b_p0 As Integer, w2b_p1 As Integer, w2b_p2 As Integer, w2b_p3 As LongInteger, w2b_p4 As Double) As Integer
   file/line: pkg:/source/wasm3.out.brs(57576)
#9  Function w2b_op_call_184419003(w2b_p0 As Integer, w2b_p1 As Integer, w2b_p2 As Integer, w2b_p3 As LongInteger, w2b_p4 As Double) As Integer
   file/line: pkg:/source/wasm3.out.brs(28133)
#8  Function w2b_op_compile_370148360(w2b_p0 As Integer, w2b_p1 As Integer, w2b_p2 As Integer, w2b_p3 As LongInteger, w2b_p4 As Double) As Integer
   file/line: pkg:/source/wasm3.out.brs(28231)
#7  Function w2b_op_entry_244581201(w2b_p0 As Integer, w2b_p1 As Integer, w2b_p2 As Integer, w2b_p3 As LongInteger, w2b_p4 As Double) As Integer
   file/line: pkg:/source/wasm3.out.brs(57576)
#6  Function w2b_m3_callargv_386925580(w2b_p0 As Integer, w2b_p1 As Integer, w2b_p2 As Integer) As Integer
   file/line: pkg:/source/wasm3.out.brs(63474)
#5  Function w2b_repl_call(w2b_p0 As Integer, w2b_p1 As Integer, w2b_p2 As Integer) As Integer
   file/line: pkg:/source/wasm3.out.brs(2072)
#4  Function w2b_main(w2b_p0 As Integer, w2b_p1 As Integer) As Integer
   file/line: pkg:/source/wasm3.out.brs(3988)
#3  Function w2b___original_main() As Integer
   file/line: pkg:/source/wasm3.out1.brs(13169)
#2  Function w2b__start() As Void
   file/line: pkg:/source/wasm3.out.brs(1190)
#1  Function start() As Dynamic
   file/line: pkg:/source/wasm3-demo.out.brs(13)
#0  Function main() As Void
   file/line: pkg:/source/Main.brs(82)
iBicha commented 1 year ago

I was able to build wasm-interp (https://github.com/WebAssembly/wabt/issues/2157#issuecomment-1455165825) And optimize it with wasm-opt --enable-sign-ext -g -O4

Why --enable-sign-ext? Well, without any feature enabled, I get validation errors [wasm-validator error in function 2123] unexpected false: all used features should be allowed so I figured I needed to enable some feature. --enable-sign-ext Happens to fix it.

But then

Function $1853 had 330 labels (soft limit 128, hard limit 256 due to BrightScript)

Another dead end unfortunately