[Webkit-unassigned] [Bug 253307] Using WASM SIMD on https://squoosh.app breaks WebP decoding

bugzilla-daemon at webkit.org bugzilla-daemon at webkit.org
Mon Mar 6 18:36:07 PST 2023


https://bugs.webkit.org/show_bug.cgi?id=253307

--- Comment #8 from Justin Michaud <justin_michaud at apple.com> ---
Safari compiles this simplified test case incorrectly:

(func $main (export "main") (result i32)
        v128.const i64x2 -27866447905751188 -27866447902605412
        v128.const i32x4 0x00080008 0x00080008 0x00080008 0x00080008
        i32x4.dot_i16x8_s
        i64x2.extract_lane 0
        i64.const -6867652708672
        i64.eq
    )

Thank you so much for this test case!

Let me explain how I ended up debugging this, just for future reference for any future WebKit contributors.

First, I extracted a CLI version (attached above). Then, I ran binaryen to instrument the code:

```
bin/wasm-opt ~/Desktop/case/webp_enc_simd.wasm -o ~/Desktop/case/webp_enc_simd-instrumented.wasm --enable-simd --disable-gc --log-execution --instrument-memory
```

And the following environment:

```
let START_DEBUG = 3200
    let count = 0;
    function createWasm() {
      var info = { a: asmLibraryArg };
      let memPrint = function(...args) {
        if (count >= START_DEBUG)
          print(...args)
      }
      let env = {
        log_i32: function(x) {
          memPrint('[LoggingExternalInterface logging ' + literal(x, 'i32') + ']');
        },
        log_i64: function(x, h) {
          memPrint('[LoggingExternalInterface logging ' + literal(x, 'i32') + ' ' + literal(h, 'i32') + ']');
        },
        log_f32: function(x) {
          memPrint('[LoggingExternalInterface logging ' + literal(x, 'f64') + ']');
        },
        log_f64: function(x) {
          memPrint('[LoggingExternalInterface logging ' + literal(x, 'f64') + ']');
        },
        my_log_i32: function(i) {
          print("My log: " + i)
          return i
        },
        my_log_i64: function(i) {
          print("My log 64: " + i)
          return i
        },
        log_execution: function(loc) {
          if (count >= START_DEBUG)
            print('log_execution ' + loc + " count: " + count);
          if (loc < 133713370)
            ++count;
          if (count == 3230+1) {
            print("******* " + loc)
            $vm.breakpoint()
          }
        },
        setTempRet0: function(x) {
          tempRet0 = x;
        },
        getTempRet0: function() {
          return x;
        },
        get_i32: function(loc, index, value) {
          memPrint('get_i32 ' + [loc, index, value]);
          return value;
        },
        get_i64: function(loc, index, low, high) {
          memPrint('get_i64 ' + [loc, index, low, high]);
          env['setTempRet0'](high);
          return low;
        },
        get_f32: function(loc, index, value) {
          memPrint('get_f32 ' + [loc, index, value]);
          return value;
        },
        get_f64: function(loc, index, value) {
          memPrint('get_f64 ' + [loc, index, value]);
          return value;
        },
        set_i32: function(loc, index, value) {
          memPrint('set_i32 ' + [loc, index, value]);
          return value;
        },
        set_i64: function(loc, index, low, high) {
          memPrint('set_i64 ' + [loc, index, low, high]);
          env['setTempRet0'](high);
          return low;
        },
        set_f32: function(loc, index, value) {
          memPrint('set_f32 ' + [loc, index, value]);
          return value;
        },
        set_f64: function(loc, index, value) {
          memPrint('set_f64 ' + [loc, index, value]);
          return value;
        },
        load_ptr: function(loc, bytes, offset, ptr) {
          memPrint('load_ptr ' + [loc, bytes, offset, ptr]);
          if (count >= START_DEBUG) {
            print('*load_ptr ' + [loc, bytes, offset, ptr]);
            let arr = new Uint32Array(wasmMemory.buffer)
            for(let i = 0; i < 4; ++i)
              print("Val: " + arr[(ptr + offset + i * 4) / 4])
          }
          return ptr;
        },
        load_val_i32: function(loc, value) {
          memPrint('load_val_i32 ' + [loc, value]);
          return value;
        },
        load_val_i64: function(loc, low, high) {
          memPrint('load_val_i64 ' + [loc, low, high]);
          env['setTempRet0'](high);
          return low;
        },
        load_val_f32: function(loc, value) {
          memPrint('load_val_f32 ' + [loc, value]);
          return value;
        },
        load_val_f64: function(loc, value) {
          memPrint('load_val_f64 ' + [loc, value]);
          return value;
        },
        store_ptr: function(loc, bytes, offset, ptr) {
          memPrint('store_ptr ' + [loc, bytes, offset, ptr]);

          if (count >= START_DEBUG) {
            print('*store_ptr ' + [loc, bytes, offset, ptr]);
            let arr = new Uint32Array(wasmMemory.buffer)
            for(let i = 0; i < 4; ++i)
              print("Val: " + arr[(ptr + offset + i * 4) / 4])
          }

          if (loc == 12554 && count >= 3230) {
            print("*********** end")
            $vm.breakpoint()
          }
          return ptr;
        },
        store_val_i32: function(loc, value) {
          memPrint('store_val_i32 ' + [loc, value]);
          if (loc == 12552 && count >= 3230) {
            print("*********** start")
            // $vm.breakpoint()
          }
          return value;
        },
        store_val_i64: function(loc, low, high) {
          memPrint('store_val_i64 ' + [loc, low, high]);
          env['setTempRet0'](high);
          return low;
        },
        store_val_f32: function(loc, value) {
          memPrint('store_val_f32 ' + [loc, value]);
          return value;
        },
        store_val_f64: function(loc, value) {
          memPrint('store_val_f64 ' + [loc, value]);
          return value;
        },

        struct_get_val_i32: function(loc, value) {
          memPrint('struct_get_val_i32 ' + [loc, value]);
          return value;
        },
        struct_get_val_i64: function(loc, value) {
          memPrint('struct_get_val_i64 ' + [loc, value]);
          return value;
        },
        struct_get_val_f32: function(loc, value) {
          memPrint('struct_get_val_f32 ' + [loc, value]);
          return value;
        },
        struct_get_val_f64: function(loc, value) {
          memPrint('struct_get_val_f64 ' + [loc, value]);
          return value;
        },
        struct_set_val_i32: function(loc, value) {
          memPrint('struct_set_val_i32 ' + [loc, value]);
          return value;
        },
        struct_set_val_i64: function(loc, value) {
          memPrint('struct_set_val_i64 ' + [loc, value]);
          return value;
        },
        struct_set_val_f32: function(loc, value) {
          memPrint('struct_set_val_f32 ' + [loc, value]);
          return value;
        },
        struct_set_val_f64: function(loc, value) {
          memPrint('struct_set_val_f64 ' + [loc, value]);
          return value;
        },

        array_get_val_i32: function(loc, value) {
          memPrint('array_get_val_i32 ' + [loc, value]);
          return value;
        },
        array_get_val_i64: function(loc, value) {
          memPrint('array_get_val_i64 ' + [loc, value]);
          return value;
        },
        array_get_val_f32: function(loc, value) {
          memPrint('array_get_val_f32 ' + [loc, value]);
          return value;
        },
        array_get_val_f64: function(loc, value) {
          memPrint('array_get_val_f64 ' + [loc, value]);
          return value;
        },
        array_set_val_i32: function(loc, value) {
          memPrint('array_set_val_i32 ' + [loc, value]);
          return value;
        },
        array_set_val_i64: function(loc, value) {
          memPrint('array_set_val_i64 ' + [loc, value]);
          return value;
        },
        array_set_val_f32: function(loc, value) {
          memPrint('array_set_val_f32 ' + [loc, value]);
          return value;
        },
        array_set_val_f64: function(loc, value) {
          memPrint('array_set_val_f64 ' + [loc, value]);
          return value;
        },
        array_get_index: function(loc, value) {
          memPrint('array_get_index ' + [loc, value]);
          return value;
        },
        array_set_index: function(loc, value) {
          memPrint('array_set_index ' + [loc, value]);
          return value;
        },
      };
      info["env"] = env;
      info["a"]["log_execution"] = env.log_execution
```

I ran it in both v8 and chrome:

```
/Volumes/WebKit/wabt/bin/wasm2wat --inline-exports --inline-imports --generate-names --enable-annotations --enable-exceptions --enable-threads --enable-code-metadata webp_enc_simd-instrumented.wasm -o webp_enc_simd-instrumented.wat 
[ add any extra debug calls ]
wat2wasm webp_enc_simd-instrumented.wat -o webp_enc_simd-instrumented-2.wasm 
jsc -m with-simd.js --useConcurrentJIT=0 --useOMGJIT=0 --useDollarVM=1 --dumpDisassembly=0 > ./jsc-cfg.txt
v8 --single-threaded --module with-simd.js > ./v8-cfg.txt
```

Then I used beyond compare to diff the output. 

Another useful trick to see where you are is to add a $vm.breakpoint call, and then catch it in lldb from the jsc shell.

Finally, I added my own debug call to narrow in on the precise divergence in execution.

I have attached my working setup in case I need to debug a similar issue in the future.

-- 
You are receiving this mail because:
You are the assignee for the bug.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.webkit.org/pipermail/webkit-unassigned/attachments/20230307/03690d92/attachment-0001.htm>


More information about the webkit-unassigned mailing list