-
Notifications
You must be signed in to change notification settings - Fork 293
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
Wasmi versions 0.32.0-beta.6
cannot run ffmpeg.wasm
correctly
#934
Comments
@yamt thanks a lot for the bug report. I am going to inspect and fix it asap. |
Ideally can you provide me with the |
ffmpeg.wasm
correctly
you can download it from https://assets.runno.dev/ffmpeg/ffmpeg.wasm the script automatically download it: https://github.com/yamt/toywasm/blob/b73e2ae6403ce18e477a884ab8634d1d7c840dda/test/run-ffmpeg.sh#L48 |
forgot to note: it's macOS/x86-64 if it matters.
|
Thanks a lot for all the information, @yamt ! |
ffmpeg.wasm
correctly0.32.0-beta.N
cannot run ffmpeg.wasm
correctly
I traces loads and stores for both Wasmi (stack) and Wasmi (register) while executing the
On line 812 both files start to differ. Wasmi (stack) interleaves some store instructions whereas Wasmi (register) does not. This helps me to track down where exactly things are going side ways and is the first step towards fixing the issue. Note the swapped edit: I refined bounds and found a more precise point where the executions diverge. The difference starts on line 76 where Wasmi (stack) stores
edit 2: I found the location in the Wasm file that causes the corruption. The |
I think I have found the underlying issue. The issue unfortunately is in the Wasm -> Wasmi bytecode translation phase. Given the following (module
(func (param i32 i32 i32) (result i32)
local.get 0
block (result i32) ;; label = @1
local.get 1
local.get 2
br_if 0 (;@1;)
drop
i32.const 0
local.set 0
i32.const 1
end
local.get 1
local.get 2
i32.lt_s
local.tee 0
select
)
) Or the equivalent (module
(func (param i32 i32 i32) (result i32)
(select
(local.get 0) ;; lhs - uses (local 0) that is conditionally overwritten
(block (result i32) ;; rhs - conditionally overwriting (local 0)
(drop (br_if 0
(local.get 1) ;; br_if return value
(local.get 2) ;; br_if condition
))
(local.set 0 (i32.const 0)) ;; overwrites (local 0) after `br_if` conditionally
(i32.const 1)
)
;; condition - overwriting (local 0)
(local.tee 0
(i32.lt_s (local.get 1) (local.get 2))
)
)
)
) We see that the
While the left-hand side input is trivially just The problem with our current Wasm -> Wasmi bytecode translation is that it does not properly respect the divergent control flow in this situation and produces the following sequence of Wasmi bytecode instructions: [
Instruction::branch_i32_eq_imm(Register::from_i16(2), 0, BranchOffset16::from(3)),
Instruction::copy(3, 1),
Instruction::branch(BranchOffset::from(4)),
Instruction::copy(5, 0),
Instruction::copy_imm32(Register::from_i16(0), 0),
Instruction::copy_imm32(Register::from_i16(3), 1),
Instruction::i32_lt_s(Register::from_i16(0), Register::from_i16(1), Register::from_i16(2)),
Instruction::select(Register::from_i16(3), Register::from_i16(0), Register::from_i16(5)),
Instruction::Register(Register::from_i16(3)), // select right-hand side input
Instruction::return_reg(3),
] With the following register table:
The There are 2 potential fixes that come to my mind right now:
The problems with each of the proposed solutions are these:
@yamt As one of the WAMR authors I suppose you had to deal with the same issue with the fast interpreter. |
Do you think other instructions could be affected, too? |
@athei This bug is not tied to any single instruction such as So I assume this could have happened with other instructions, too. It just happened to expose itself with the It is interesting that this bug was not discovered earlier with all the Polkadot runtime execution and our fuzzing. So I assume code like this is not encoded very often. edit: To give an example to your question: Similar control flow could have been achieved with |
i'm not so familiar with the fast interpreter. a bit simpler function which wasmi seems to misinterpret:
spacetanuki% wasmi_cli --invoke test a.wasm 1 1
executing File("a.wasm")::test(1, 1) ...
0
spacetanuki% toywasm --load a.wasm --invoke "test 1 1"
Result: 1:i32
spacetanuki% iwasm.fast -f test a.wasm 1 1
0x1:i32
spacetanuki% wamr fast interpreter would compile it to:
where |
@yamt Indeed that is simpler because it takes the other route of the conditional local set in order to evaluate incorrectly. With my Wasm test cases I wanted to stay as close as possible to the original inputs. How did you make WAMR fast interpreter print the instructions? That's pretty handy. I also wanted to implement something like this in Wasmi with its own bytecode for inspection and debugging purposes. |
it only has a primitive printf-style logging. (WASM_DEBUG_PREPROCESSOR)
yea, it would be great to have. almost must to have i suspect. |
@yamt In that case thanks a lot for your manual post-processing and providing me with this information to help me fix the bug! :) |
In an attempt to explain (also to myself) what is going on currently, what should be done practically and what should be done ideally let us look at the example given by @yamt : (module
(func (param i32 i32) (result i32)
local.get 0
block
local.get 1
br_if 0
i32.const 10
local.set 0
end
)
) Translated to Wasmi bytecode WAMR fast-interpreter produces roughly the following bytecode. (Please correct me if I am wrong.)
Note that at this point I do not fully understand the difference or need for difference between WAMR's So what WAMR seems to be doing is to analyze which locals are set (via The code that Wasmi currently produces is: Instruction::branch_i32_ne_imm(Register::from_i16(1), 0, BranchOffset16::from(3)),
Instruction::copy(2, 0),
Instruction::copy_imm32(Register::from_i16(0), 10_i32),
Instruction::return_reg(2), The code that I would like Wasmi to produce is: Instruction::copy(2, 0),
Instruction::branch_i32_ne_imm(Register::from_i16(1), 0, BranchOffset16::from(2)),
Instruction::copy_imm32(Register::from_i16(0), 10_i32),
Instruction::return_reg(2), So basically in this example just swap the first 2 instructions around. I am not yet sure which route I am wiling to go down and which is the most promising and most efficient resulting in the best possible codegen while not being super hard to code and maintain. For the more complicated nested version of the above Wasm code example: (module
(func (param i32 i32) (param $c0 i32) (param $c1 i32) (result i32 i32)
local.get 0 ;; 1st return value
local.get 1 ;; 2nd return value
block
local.get $c0
br_if 0
i32.const 10
local.set 0 ;; conditionally overwrites (local 0) on stack
block
local.get $c1
br_if 1
i32.const 20
local.set 1 ;; conditionally overwrites (local 1) on stack
end
end
)
) Wasmi currently produces the following Wasmi bytecode:
Which is incorrect and we actually want to produce the following Wasmi bytecode ideally:
Thus when scanning for a |
I implemented some naive way for local preservation before entering control flow structures which showed no significant negative impact on performance which is a strong relieve for me since I expected that. Some WIP downsides of the PR are listed in its description here: #945 I already tested |
While #945 successfully fixes the bug reported in this issue, running
Tracking it down again via load and store instructions we see that the failure now happens quite a lot later than previously at Therefore I am going to merge #945 and start working on an investigation and follow-up on the remaining bug(s) until Execution log of selected instructions while running |
The system automatically closed this issue but I am going to re-open it immediately since, while the originally reported bug has been fixed, the edit: Upon further investigation it seems that the |
@yamt #948 and #945 together fixed this issue. I timed the
Thus ~120% speedup. I am going to release another beta release later today so it is easier to give it a try for yourself. :) edit: Just released Wasmi |
thank you! |
0.32.0-beta.N
cannot run ffmpeg.wasm
correctly0.32.0-beta.6
cannot run ffmpeg.wasm
correctly
Thank you again for reporting the issue in the first place. Now that these bugs have been fixed and |
0.32.0-beta.5 doesn't work.
0.32.0-beta.6 doesn't work.
today's master (352f8ae) doesn't work.
v0.31.0 works.
The text was updated successfully, but these errors were encountered: