Cheng Shao pushed to branch wip/wasm-remove-webkit-workaround at Glasgow Haskell Compiler / GHC

Commits:

1 changed file:

Changes:

  • utils/jsffi/dyld.mjs
    ... ... @@ -783,25 +783,7 @@ class DyLD {
    783 783
     
    
    784 784
       // Wasm memory & table
    
    785 785
       #memory = new WebAssembly.Memory({ initial: 1 });
    
    786
    -
    
    787 786
       #table = new WebAssembly.Table({ element: "anyfunc", initial: 1 });
    
    788
    -  // First free slot, might be invalid when it advances to #table.length
    
    789
    -  #tableFree = 1;
    
    790
    -  // See Note [The evil wasm table grower]
    
    791
    -  #tableGrowInstance = new WebAssembly.Instance(
    
    792
    -    new WebAssembly.Module(
    
    793
    -      new Uint8Array([
    
    794
    -        0, 97, 115, 109, 1, 0, 0, 0, 1, 6, 1, 96, 1, 127, 1, 127, 2, 35, 1, 3,
    
    795
    -        101, 110, 118, 25, 95, 95, 105, 110, 100, 105, 114, 101, 99, 116, 95,
    
    796
    -        102, 117, 110, 99, 116, 105, 111, 110, 95, 116, 97, 98, 108, 101, 1,
    
    797
    -        112, 0, 0, 3, 2, 1, 0, 7, 31, 1, 27, 95, 95, 103, 104, 99, 95, 119, 97,
    
    798
    -        115, 109, 95, 106, 115, 102, 102, 105, 95, 116, 97, 98, 108, 101, 95,
    
    799
    -        103, 114, 111, 119, 0, 0, 10, 11, 1, 9, 0, 208, 112, 32, 0, 252, 15, 0,
    
    800
    -        11,
    
    801
    -      ])
    
    802
    -    ),
    
    803
    -    { env: { __indirect_function_table: this.#table } }
    
    804
    -  );
    
    805 787
     
    
    806 788
       // __stack_pointer
    
    807 789
       #sp = new WebAssembly.Global(
    
    ... ... @@ -846,82 +828,6 @@ class DyLD {
    846 828
       // Global STG registers
    
    847 829
       #regs = {};
    
    848 830
     
    
    849
    -  // Note [The evil wasm table grower]
    
    850
    -  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    851
    -  // We need to grow the wasm table as we load shared libraries in
    
    852
    -  // wasm dyld. We used to directly call the table.grow() JS API,
    
    853
    -  // which works as expected in Firefox/Chrome, but unfortunately,
    
    854
    -  // WebKit's implementation of the table.grow() JS API is broken:
    
    855
    -  // https://bugs.webkit.org/show_bug.cgi?id=290681, which means that
    
    856
    -  // the wasm dyld simply does not work in WebKit-based browsers like
    
    857
    -  // Safari.
    
    858
    -  //
    
    859
    -  // Now, one simple workaround would be to avoid growing the table at
    
    860
    -  // all: just allocate a huge table upfront (current limitation
    
    861
    -  // agreed by all vendors is 10000000). To avoid unnecessary space
    
    862
    -  // waste on non-WebKit platforms, we could additionally check
    
    863
    -  // navigator.userAgent against some regexes and only allocate
    
    864
    -  // fixed-length table when there's no blink/gecko mention. But this
    
    865
    -  // is fragile and gross, and it's better to stick to a uniform code
    
    866
    -  // path for all browsers.
    
    867
    -  //
    
    868
    -  // Fortunately, it turns out the table.grow wasm instruction work as
    
    869
    -  // expected in WebKit! So we can invoke a wasm function that grows
    
    870
    -  // the table for us. But don't open a champagne yet, where would
    
    871
    -  // that wasm function come from? It can't be put into RTS, or even
    
    872
    -  // libc.so, because loading those libraries would require growing
    
    873
    -  // the table in the first place! Or perhaps, reserve a table upfront
    
    874
    -  // that's just large enough to load RTS and then we can access that
    
    875
    -  // function for subsequent table grows? But then we need to
    
    876
    -  // experiment for a reasonable initial size, and add a magic number
    
    877
    -  // here, which is also fragile and gross and not future-proof!
    
    878
    -  //
    
    879
    -  // So this special wasm function needs to live in a single wasm
    
    880
    -  // module, which is loaded before we load anything else. The full
    
    881
    -  // source code for this module is:
    
    882
    -  //
    
    883
    -  // (module
    
    884
    -  //   (type (func (param i32) (result i32)))
    
    885
    -  //   (import "env" "__indirect_function_table" (table 0 funcref))
    
    886
    -  //   (export "__ghc_wasm_jsffi_table_grow" (func 0))
    
    887
    -  //   (func (type 0) (param i32) (result i32)
    
    888
    -  //     ref.null func
    
    889
    -  //     local.get 0
    
    890
    -  //     table.grow 0
    
    891
    -  //   )
    
    892
    -  // )
    
    893
    -  //
    
    894
    -  // This module is 103 bytes so that we can inline its blob in dyld,
    
    895
    -  // and use the usually discouraged synchronous
    
    896
    -  // WebAssembly.Instance/WebAssembly.Module constructors to load it.
    
    897
    -  // On non-WebKit platforms, growing tables this way would introduce
    
    898
    -  // a bit of extra JS/Wasm interop overhead, which can be amplified
    
    899
    -  // as we used to call table.grow(1, foo) for every GOT.func item.
    
    900
    -  // Therefore, unless we're about to exceed the hard limit of table
    
    901
    -  // size, we now grow the table exponentially, and use bump
    
    902
    -  // allocation to calculate the table index to be returned.
    
    903
    -  // Exponential growth is only implemented to minimize the JS/Wasm
    
    904
    -  // interop overhead when calling __ghc_wasm_jsffi_table_grow;
    
    905
    -  // V8/SpiderMonkey/WebKit already do their own exponential growth of
    
    906
    -  // the table's backing buffer in their table growth logic.
    
    907
    -  //
    
    908
    -  // Invariants: n >= 0; when v is non-null, n === 1
    
    909
    -  #tableGrow(n, v) {
    
    910
    -    const prev_free = this.#tableFree;
    
    911
    -    if (prev_free + n > this.#table.length) {
    
    912
    -      const min_delta = prev_free + n - this.#table.length;
    
    913
    -      const delta = Math.max(min_delta, this.#table.length);
    
    914
    -      this.#tableGrowInstance.exports.__ghc_wasm_jsffi_table_grow(
    
    915
    -        this.#table.length + delta <= 10000000 ? delta : min_delta
    
    916
    -      );
    
    917
    -    }
    
    918
    -    if (v) {
    
    919
    -      this.#table.set(prev_free, v);
    
    920
    -    }
    
    921
    -    this.#tableFree += n;
    
    922
    -    return prev_free;
    
    923
    -  }
    
    924
    -
    
    925 831
       constructor({ args, rpc }) {
    
    926 832
         this.#rpc = rpc;
    
    927 833
     
    
    ... ... @@ -1116,7 +1022,7 @@ class DyLD {
    1116 1022
     
    
    1117 1023
           // __memory_base & __table_base, different for each .so
    
    1118 1024
           let memory_base;
    
    1119
    -      let table_base = this.#tableGrow(tableSize);
    
    1025
    +      let table_base = this.#table.grow(tableSize);
    
    1120 1026
           console.assert(tableP2Align === 0);
    
    1121 1027
     
    
    1122 1028
           // libc.so is always the first one to be ever loaded and has VIP
    
    ... ... @@ -1220,7 +1126,7 @@ class DyLD {
    1220 1126
               if (this.exportFuncs[name]) {
    
    1221 1127
                 this.#gotFunc[name] = new WebAssembly.Global(
    
    1222 1128
                   { value: "i32", mutable: true },
    
    1223
    -              this.#tableGrow(1, this.exportFuncs[name])
    
    1129
    +              this.#table.grow(1, this.exportFuncs[name])
    
    1224 1130
                 );
    
    1225 1131
                 continue;
    
    1226 1132
               }
    
    ... ... @@ -1271,7 +1177,7 @@ class DyLD {
    1271 1177
               if (this.#gotFunc[k]) {
    
    1272 1178
                 const got = this.#gotFunc[k];
    
    1273 1179
                 if (got.value === DyLD.#poison) {
    
    1274
    -              const idx = this.#tableGrow(1, v);
    
    1180
    +              const idx = this.#table.grow(1, v);
    
    1275 1181
                   got.value = idx;
    
    1276 1182
                 } else {
    
    1277 1183
                   this.#table.set(got.value, v);
    
    ... ... @@ -1325,7 +1231,7 @@ class DyLD {
    1325 1231
         // Not in GOT.func yet, create the entry on demand
    
    1326 1232
         if (this.exportFuncs[sym]) {
    
    1327 1233
           console.assert(!this.#gotFunc[sym]);
    
    1328
    -      const addr = this.#tableGrow(1, this.exportFuncs[sym]);
    
    1234
    +      const addr = this.#table.grow(1, this.exportFuncs[sym]);
    
    1329 1235
           this.#gotFunc[sym] = new WebAssembly.Global(
    
    1330 1236
             { value: "i32", mutable: true },
    
    1331 1237
             addr