tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

basic.js (86144B)


      1 // |jit-test| heavy; allow-oom
      2 
      3 // Basic tests around creating and linking memories with i64 indices
      4 
      5 // test the validity of different i64 memory types in validation, compilation,
      6 // and the JS-API.
      7 function memoryTypeModuleText(shared, initial, max) {
      8  return `(module
      9            (memory i64 ${initial} ${max ?? ''} ${shared ? `shared` : ''}))`;
     10 }
     11 function memoryTypeDescriptor(shared, initial, max) {
     12  return {
     13    address: 'i64',
     14    initial,
     15    maximum: max,
     16    shared,
     17  };
     18 }
     19 function validAndInstantiableMemoryType(shared, initial, max) {
     20  wasmValidateText(memoryTypeModuleText(shared, initial, max));
     21  wasmEvalText(memoryTypeModuleText(shared, initial, max));
     22  new WebAssembly.Memory(memoryTypeDescriptor(shared, initial, max));
     23 }
     24 function validButNotInstantiableMemoryType(shared, initial, max, errorMessage) {
     25  wasmValidateText(memoryTypeModuleText(shared, initial, max));
     26  assertErrorMessage(() => wasmEvalText(memoryTypeModuleText(shared, initial, max)), WebAssembly.RuntimeError, errorMessage);
     27  assertErrorMessage(() => new WebAssembly.Memory(memoryTypeDescriptor(shared, initial, max)), WebAssembly.RuntimeError, errorMessage);
     28 }
     29 function invalidMemoryType(shared, initial, max, compileMessage, jsMessage) {
     30  wasmFailValidateText(memoryTypeModuleText(shared, initial, max), compileMessage);
     31  assertErrorMessage(() => wasmEvalText(memoryTypeModuleText(shared, initial, max)), WebAssembly.CompileError, compileMessage);
     32  assertErrorMessage(() => new WebAssembly.Memory(memoryTypeDescriptor(shared, initial, max)), Error, jsMessage);
     33 }
     34 
     35 function tableTypeModuleText(element, initial, max) {
     36  return `(module
     37    (table i64 ${initial} ${max ?? ''} ${element})
     38  )`;
     39 }
     40 function tableTypeDescriptor(element, initial, max) {
     41  return {
     42    address: 'i64',
     43    element,
     44    initial,
     45    maximum: max,
     46  };
     47 }
     48 function validAndInstantiableTableType(element, initial, max) {
     49  wasmValidateText(tableTypeModuleText(element, initial, max));
     50  wasmEvalText(tableTypeModuleText(element, initial, max));
     51  new WebAssembly.Table(tableTypeDescriptor(element, initial, max));
     52 }
     53 function validButNotInstantiableTableType(element, initial, max, errorMessage) {
     54  wasmValidateText(tableTypeModuleText(element, initial, max));
     55  assertErrorMessage(() => wasmEvalText(tableTypeModuleText(element, initial, max)), WebAssembly.RuntimeError, errorMessage);
     56  assertErrorMessage(() => new WebAssembly.Table(tableTypeDescriptor(element, initial, max)), WebAssembly.RuntimeError, errorMessage);
     57 }
     58 function invalidTableType(element, initial, max, compileMessage, jsMessage) {
     59  wasmFailValidateText(tableTypeModuleText(element, initial, max), compileMessage);
     60  assertErrorMessage(() => wasmEvalText(tableTypeModuleText(element, initial, max)), WebAssembly.CompileError, compileMessage);
     61  assertErrorMessage(() => new WebAssembly.Table(tableTypeDescriptor(element, initial, max)), Error, jsMessage);
     62 }
     63 
     64 // valid to define a memory with i64
     65 validAndInstantiableMemoryType(false, 0n);
     66 // valid to define max with i64
     67 validAndInstantiableMemoryType(false, 0n, 1n);
     68 // invalid for min to be greater than max with i64
     69 invalidMemoryType(false, 2n, 1n, /minimum must not be greater than maximum/, /initial Memory size cannot be greater than maximum/);
     70 // valid to define shared memory with max with i64
     71 validAndInstantiableMemoryType(true, 1n, 2n);
     72 // invalid to define shared memory without max with i64
     73 invalidMemoryType(true, 1n, undefined, /maximum length required for shared memory/, /maximum is not specified/);
     74 
     75 // valid to define a table with i64
     76 validAndInstantiableTableType('funcref', 0n);
     77 // valid to define table max with i64
     78 validAndInstantiableTableType('funcref', 0n, 1n);
     79 // invalid for table min to be greater than max with i64
     80 invalidTableType('funcref', 2n, 1n, /minimum must not be greater than maximum/, /initial Table size cannot be greater than maximum/);
     81 
     82 // test the validation limits of memory64 memories
     83 validButNotInstantiableMemoryType(false, MaxMemory64PagesValidation, undefined, /too many memory pages/);
     84 validButNotInstantiableMemoryType(false, MaxMemory64PagesValidation, MaxMemory64PagesValidation, /too many memory pages/);
     85 validAndInstantiableMemoryType(false, 0n, MaxMemory64PagesValidation);
     86 invalidMemoryType(false, 0n, MaxMemory64PagesValidation + 1n, /maximum memory size too big/, /bad Memory maximum/);
     87 validAndInstantiableMemoryType(true, 0n, MaxMemory64PagesValidation);
     88 invalidMemoryType(true, 0n, MaxMemory64PagesValidation + 1n, /maximum memory size too big/, /bad Memory maximum/);
     89 
     90 // test the validation limits of memory64 tables
     91 validButNotInstantiableTableType('funcref', MaxTable64ElemsValidation, undefined, /too many table elements/);
     92 validButNotInstantiableTableType('funcref', MaxTable64ElemsValidation, MaxTable64ElemsValidation, /too many table elements/);
     93 validAndInstantiableTableType('funcref', 0n, MaxTable64ElemsValidation);
     94 // cannot create oversize table via either text or binary format since the full u64 range is valid
     95 assertErrorMessage(() => new WebAssembly.Table(tableTypeDescriptor('funcref', 0n, MaxTable64ElemsValidation + 1n)), TypeError, /bad Table maximum/);
     96 
     97 // further validation of memory64 descriptor params
     98 assertErrorMessage(() => new WebAssembly.Memory(memoryTypeDescriptor(false, 0)), TypeError, /can't convert 0 to BigInt/);
     99 assertErrorMessage(() => new WebAssembly.Memory(memoryTypeDescriptor(false, -1n)), TypeError, /bad Memory initial size/);
    100 assertErrorMessage(() => new WebAssembly.Memory(memoryTypeDescriptor(false, 2n**64n)), TypeError, /bad Memory initial size/);
    101 assertErrorMessage(() => new WebAssembly.Memory(memoryTypeDescriptor(false, 0n, 1)), TypeError, /can't convert 1 to BigInt/);
    102 assertErrorMessage(() => new WebAssembly.Memory(memoryTypeDescriptor(false, 0n, -1n)), TypeError, /bad Memory maximum size/);
    103 assertErrorMessage(() => new WebAssembly.Memory(memoryTypeDescriptor(false, 0n, 2n**64n)), TypeError, /bad Memory maximum size/);
    104 assertErrorMessage(() => new WebAssembly.Table(tableTypeDescriptor('funcref', 0)), TypeError, /can't convert 0 to BigInt/);
    105 assertErrorMessage(() => new WebAssembly.Table(tableTypeDescriptor('funcref', -1n)), TypeError, /bad Table initial size/);
    106 assertErrorMessage(() => new WebAssembly.Table(tableTypeDescriptor('funcref', 2n**64n)), TypeError, /bad Table initial size/);
    107 assertErrorMessage(() => new WebAssembly.Table(tableTypeDescriptor('funcref', 0n, 1)), TypeError, /can't convert 1 to BigInt/);
    108 assertErrorMessage(() => new WebAssembly.Table(tableTypeDescriptor('funcref', 0n, -1n)), TypeError, /bad Table maximum size/);
    109 assertErrorMessage(() => new WebAssembly.Table(tableTypeDescriptor('funcref', 0n, 2n**64n)), TypeError, /bad Table maximum size/);
    110 
    111 // test that linking requires address types to be equal
    112 function testLinkMemory(importedAddressType, importAddressType) {
    113  let imported = new WebAssembly.Memory({
    114    address: importedAddressType,
    115    initial: importedAddressType === 'i64' ? 0n : 0,
    116  });
    117  let testModule =
    118      `(module
    119         (memory (import "" "imported") ${importAddressType} 0))`;
    120  if (importedAddressType === importAddressType) {
    121    wasmEvalText(testModule, {"": {imported}});
    122  } else {
    123    assertErrorMessage(() => wasmEvalText(testModule, {"": {imported}}), WebAssembly.LinkError, /address type/);
    124  }
    125 }
    126 function testLinkTable(importedAddressType, importAddressType) {
    127  const imported = new WebAssembly.Table({
    128    element: 'funcref',
    129    address: importedAddressType,
    130    initial: importedAddressType === 'i64' ? 0n : 0,
    131  });
    132  const testModule =
    133      `(module
    134         (table (import "" "imported") ${importAddressType} 0 funcref))`;
    135  if (importedAddressType === importAddressType) {
    136    wasmEvalText(testModule, {"": {imported}});
    137  } else {
    138    assertErrorMessage(() => wasmEvalText(testModule, {"": {imported}}), WebAssembly.LinkError, /address type/);
    139  }
    140 }
    141 
    142 var types = [
    143  ['i64', 'i64'],
    144  ['i32', 'i32'],
    145  ['i64', 'i32'],
    146  ['i32', 'i64']
    147 ];
    148 
    149 for ( let [a,b] of types ) {
    150  testLinkMemory(a, b);
    151  testLinkTable(a, b);
    152 }
    153 
    154 // Active data segments use the address type for the init expression
    155 
    156 for ( let [memType, exprType] of types ) {
    157  const moduleText = `
    158  (module
    159    (memory ${memType} 1)
    160    (data (${exprType}.const 0) "abcde"))`;
    161  if (memType == exprType) {
    162    wasmEvalText(moduleText);
    163  } else {
    164    wasmFailValidateText(moduleText, new RegExp(`expression has type ${exprType} but expected ${memType}`));
    165  }
    166 }
    167 
    168 // Active element segments use the address type for the init expression
    169 
    170 for ( let [tableType, exprType] of types ) {
    171  const moduleText = `
    172  (module
    173    (table ${tableType} 1 funcref)
    174    (elem (table 0) (offset ${exprType}.const 0) funcref (ref.null func)))`;
    175  if (tableType == exprType) {
    176    wasmEvalText(moduleText);
    177  } else {
    178    wasmFailValidateText(moduleText, new RegExp(`expression has type ${exprType} but expected ${tableType}`));
    179  }
    180 }
    181 
    182 // Validate instructions using 32/64-bit pointers in 32/64-bit memories.
    183 
    184 var validOffsets = {i32: ['', 'offset=0x10000000'],
    185                    i64: ['', 'offset=0x10000000', 'offset=0x200000000']}
    186 
    187 // Basic load/store
    188 for (let [memType, ptrType] of types ) {
    189    for (let offs of validOffsets[memType]) {
    190        assertEq(WebAssembly.validate(wasmTextToBinary(`
    191 (module
    192  (memory ${memType} 1)
    193  (func (param $p ${ptrType}) (param $i i32) (param $l i64) (param $f f32) (param $d f64)
    194    (drop (i32.add (i32.const 1) (i32.load8_s ${offs} (local.get $p))))
    195    (drop (i32.add (i32.const 1) (i32.load8_u ${offs} (local.get $p))))
    196    (drop (i32.add (i32.const 1) (i32.load16_s ${offs} (local.get $p))))
    197    (drop (i32.add (i32.const 1) (i32.load16_u ${offs} (local.get $p))))
    198    (drop (i32.add (i32.const 1) (i32.load ${offs} (local.get $p))))
    199    (i32.store8 ${offs} (local.get $p) (local.get $i))
    200    (i32.store16 ${offs} (local.get $p) (local.get $i))
    201    (i32.store ${offs} (local.get $p) (local.get $i))
    202    (drop (i64.add (i64.const 1) (i64.load8_s ${offs} (local.get $p))))
    203    (drop (i64.add (i64.const 1) (i64.load8_u ${offs} (local.get $p))))
    204    (drop (i64.add (i64.const 1) (i64.load16_s ${offs} (local.get $p))))
    205    (drop (i64.add (i64.const 1) (i64.load16_u ${offs} (local.get $p))))
    206    (drop (i64.add (i64.const 1) (i64.load32_s ${offs} (local.get $p))))
    207    (drop (i64.add (i64.const 1) (i64.load32_u ${offs} (local.get $p))))
    208    (drop (i64.add (i64.const 1) (i64.load ${offs} (local.get $p))))
    209    (i64.store8 ${offs} (local.get $p) (local.get $l))
    210    (i64.store16 ${offs} (local.get $p) (local.get $l))
    211    (i64.store32 ${offs} (local.get $p) (local.get $l))
    212    (i64.store ${offs} (local.get $p) (local.get $l))
    213    (drop (f32.add (f32.const 1) (f32.load ${offs} (local.get $p))))
    214    (f32.store ${offs} (local.get $p) (local.get $f))
    215    (drop (f64.add (f64.const 1) (f64.load ${offs} (local.get $p))))
    216    (f64.store ${offs} (local.get $p) (local.get $d))
    217 ))`)), memType == ptrType);
    218    }
    219 }
    220 
    221 // Basic table get/set
    222 for (const [tableType, ptrType] of types) {
    223  function mod(ins) {
    224    return `(module
    225      (table ${tableType} 1 funcref)
    226      (func (param $p ${ptrType})
    227        ${ins}
    228      )
    229    )`;
    230  }
    231 
    232  if (tableType === ptrType) {
    233    wasmValidateText(mod(`(drop (table.get (${ptrType}.const 0)))`));
    234    wasmValidateText(mod(`(table.set (${ptrType}.const 0) (ref.null func))`));
    235  } else {
    236    wasmFailValidateText(
    237      mod(`(drop (table.get (${ptrType}.const 0)))`),
    238      new RegExp(`expression has type ${ptrType} but expected ${tableType}`),
    239    );
    240    wasmFailValidateText(
    241      mod(`(table.set (${ptrType}.const 0) (ref.null func))`),
    242      new RegExp(`expression has type ${ptrType} but expected ${tableType}`),
    243    );
    244  }
    245 }
    246 
    247 // Bulk memory operations
    248 for (const [memType, ptrType] of types) {
    249  function mod(ins) {
    250    return `(module
    251      (memory ${memType} 1)
    252      (data $seg "0123456789abcdef")
    253      (func (param $p ${ptrType})
    254        ${ins}
    255      )
    256    )`;
    257  }
    258 
    259  if (memType === ptrType) {
    260    wasmValidateText(mod(`(drop (${ptrType}.add (${ptrType}.const 1) (memory.size)))`));
    261    wasmValidateText(mod(`(drop (${ptrType}.add (${ptrType}.const 1) (memory.grow (${ptrType}.const 1))))`));
    262    wasmValidateText(mod(`(memory.copy (local.get $p) (${ptrType}.const 0) (${ptrType}.const 628))`));
    263    wasmValidateText(mod(`(memory.fill (local.get $p) (i32.const 37) (${ptrType}.const 1024))`));
    264    wasmValidateText(mod(`(memory.init $seg (local.get $p) (i32.const 3) (i32.const 5))`));
    265  } else {
    266    wasmFailValidateText(
    267      mod(`(drop (${ptrType}.add (${ptrType}.const 1) (memory.size)))`),
    268      new RegExp(`expression has type ${memType} but expected ${ptrType}`),
    269    );
    270    wasmFailValidateText(
    271      mod(`(drop (memory.grow (${ptrType}.const 1)))`),
    272      new RegExp(`expression has type ${ptrType} but expected ${memType}`),
    273    );
    274    wasmFailValidateText(
    275      mod(`(drop (${ptrType}.add (${ptrType}.const 1) (memory.grow (${memType}.const 1))))`),
    276      new RegExp(`expression has type ${memType} but expected ${ptrType}`),
    277    );
    278    wasmFailValidateText(
    279      mod(`(memory.copy (local.get $p) (${ptrType}.const 0) (${ptrType}.const 628))`),
    280      new RegExp(`expression has type ${ptrType} but expected ${memType}`),
    281    );
    282    wasmFailValidateText(
    283      mod(`(memory.fill (local.get $p) (i32.const 37) (${ptrType}.const 1024))`),
    284      new RegExp(`expression has type ${ptrType} but expected ${memType}`),
    285    );
    286    wasmFailValidateText(
    287      mod(`(memory.init $seg (local.get $p) (i32.const 3) (i32.const 5))`),
    288      new RegExp(`expression has type ${ptrType} but expected ${memType}`),
    289    );
    290  }
    291 }
    292 
    293 // Bulk table operations
    294 for (const [tableType, ptrType] of types) {
    295  function mod(ins) {
    296    return `(module
    297      (table ${tableType} 1 funcref)
    298      (elem $seg funcref (ref.null func))
    299      (func (param $p ${ptrType})
    300        ${ins}
    301      )
    302    )`;
    303  }
    304 
    305  if (tableType === ptrType) {
    306    wasmValidateText(mod(`(drop (${ptrType}.add (${ptrType}.const 1) (table.size)))`));
    307    wasmValidateText(mod(`(drop (${ptrType}.add (${ptrType}.const 1) (table.grow (ref.null func) (${ptrType}.const 1))))`));
    308    wasmValidateText(mod(`(table.copy (local.get $p) (${ptrType}.const 0) (${ptrType}.const 628))`));
    309    wasmValidateText(mod(`(table.fill (local.get $p) (ref.null func) (${ptrType}.const 1024))`));
    310    wasmValidateText(mod(`(table.init $seg (local.get $p) (i32.const 3) (i32.const 5))`));
    311  } else {
    312    wasmFailValidateText(
    313      mod(`(drop (${ptrType}.add (${ptrType}.const 1) (table.size)))`),
    314      new RegExp(`expression has type ${tableType} but expected ${ptrType}`),
    315    );
    316    wasmFailValidateText(
    317      mod(`(drop (table.grow (ref.null func) (${ptrType}.const 1)))`),
    318      new RegExp(`expression has type ${ptrType} but expected ${tableType}`),
    319    );
    320    wasmFailValidateText(
    321      mod(`(drop (${ptrType}.add (${ptrType}.const 1) (table.grow (ref.null func) (${tableType}.const 1))))`),
    322      new RegExp(`expression has type ${tableType} but expected ${ptrType}`),
    323    );
    324    wasmFailValidateText(
    325      mod(`(table.copy (local.get $p) (${ptrType}.const 0) (${ptrType}.const 628))`),
    326      new RegExp(`expression has type ${ptrType} but expected ${tableType}`),
    327    );
    328    wasmFailValidateText(
    329      mod(`(table.fill (local.get $p) (ref.null func) (${ptrType}.const 1024))`),
    330      new RegExp(`expression has type ${ptrType} but expected ${tableType}`),
    331    );
    332    wasmFailValidateText(
    333      mod(`(table.init $seg (local.get $p) (i32.const 3) (i32.const 5))`),
    334      new RegExp(`expression has type ${ptrType} but expected ${tableType}`),
    335    );
    336  }
    337 }
    338 
    339 // SIMD
    340 if (wasmSimdEnabled()) {
    341    for (let [memType, ptrType] of types ) {
    342        for (let offs of validOffsets[memType]) {
    343            assertEq(WebAssembly.validate(wasmTextToBinary(`
    344 (module
    345  (memory ${memType} 1)
    346  (func (param $p ${ptrType}) (param $v v128) (param $w v128)
    347    (drop (i8x16.add (local.get $w) (v128.load ${offs} (local.get $p))))
    348    (v128.store ${offs} (local.get $p) (local.get $v))
    349    (drop (i8x16.add (local.get $w) (v128.load8_splat ${offs} (local.get $p))))
    350    (drop (i8x16.add (local.get $w) (v128.load16_splat ${offs} (local.get $p))))
    351    (drop (i8x16.add (local.get $w) (v128.load32_splat ${offs} (local.get $p))))
    352    (drop (i8x16.add (local.get $w) (v128.load64_splat ${offs} (local.get $p))))
    353    (drop (i8x16.add (local.get $w) (v128.load32_zero ${offs} (local.get $p))))
    354    (drop (i8x16.add (local.get $w) (v128.load64_zero ${offs} (local.get $p))))
    355    (drop (i8x16.add (local.get $w) (v128.load8_lane ${offs} 1 (local.get $p) (local.get $v))))
    356    (drop (i8x16.add (local.get $w) (v128.load16_lane ${offs} 1 (local.get $p) (local.get $v))))
    357    (drop (i8x16.add (local.get $w) (v128.load32_lane ${offs} 1 (local.get $p) (local.get $v))))
    358    (drop (i8x16.add (local.get $w) (v128.load64_lane ${offs} 1 (local.get $p) (local.get $v))))
    359    (v128.store8_lane ${offs} 1 (local.get $p) (local.get $v))
    360    (v128.store16_lane ${offs} 1 (local.get $p) (local.get $v))
    361    (v128.store32_lane ${offs} 1 (local.get $p) (local.get $v))
    362    (v128.store64_lane ${offs} 1 (local.get $p) (local.get $v))
    363    (drop (i8x16.add (local.get $w) (v128.load8x8_s ${offs} (local.get $p))))
    364    (drop (i8x16.add (local.get $w) (v128.load8x8_u ${offs} (local.get $p))))
    365    (drop (i8x16.add (local.get $w) (v128.load16x4_s ${offs} (local.get $p))))
    366    (drop (i8x16.add (local.get $w) (v128.load16x4_u ${offs} (local.get $p))))
    367    (drop (i8x16.add (local.get $w) (v128.load32x2_s ${offs} (local.get $p))))
    368    (drop (i8x16.add (local.get $w) (v128.load32x2_u ${offs} (local.get $p))))
    369 ))`)), memType == ptrType);
    370        }
    371    }
    372 }
    373 
    374 // Threads
    375 if (wasmThreadsEnabled()) {
    376    for (let [memType, ptrType] of types ) {
    377        for (let offs of validOffsets[memType]) {
    378            assertEq(WebAssembly.validate(wasmTextToBinary(`
    379 (module
    380  (memory ${memType} 1 100 shared)
    381  (func (param $p ${ptrType}) (param $i i32) (param $l i64)
    382    (drop (i32.add (i32.const 1) (memory.atomic.wait32 ${offs} (local.get $p) (i32.const 0) (i64.const 37))))
    383    (drop (i32.add (i32.const 1) (memory.atomic.wait64 ${offs} (local.get $p) (i64.const 0) (i64.const 37))))
    384    (drop (i32.add (i32.const 1) (memory.atomic.notify ${offs} (local.get $p) (i32.const 1))))
    385 ))`)), memType == ptrType);
    386 
    387            for (let [ty,size,sx] of
    388                 [['i32','','','',],['i32','8','_u'],['i32','16','_u'],
    389                  ['i64','',''],['i64','8','_u'],['i64','16','_u'],['i64','32','_u']]) {
    390                assertEq(WebAssembly.validate(wasmTextToBinary(`
    391 (module
    392  (memory ${memType} 1 100 shared)
    393  (func (param $p ${ptrType}) (param $vi32 i32) (param $vi64 i64)
    394    (drop (${ty}.add (${ty}.const 1) (${ty}.atomic.load${size}${sx} ${offs} (local.get $p))))
    395    (${ty}.atomic.store${size} ${offs} (local.get $p) (local.get $v${ty}))
    396    (drop (${ty}.add (${ty}.const 1) (${ty}.atomic.rmw${size}.add${sx} ${offs} (local.get $p) (local.get $v${ty}))))
    397    (drop (${ty}.add (${ty}.const 1) (${ty}.atomic.rmw${size}.sub${sx} ${offs} (local.get $p) (local.get $v${ty}))))
    398    (drop (${ty}.add (${ty}.const 1) (${ty}.atomic.rmw${size}.and${sx} ${offs} (local.get $p) (local.get $v${ty}))))
    399    (drop (${ty}.add (${ty}.const 1) (${ty}.atomic.rmw${size}.or${sx} ${offs} (local.get $p) (local.get $v${ty}))))
    400    (drop (${ty}.add (${ty}.const 1) (${ty}.atomic.rmw${size}.xor${sx} ${offs} (local.get $p) (local.get $v${ty}))))
    401    (drop (${ty}.add (${ty}.const 1) (${ty}.atomic.rmw${size}.xchg${sx} ${offs} (local.get $p) (local.get $v${ty}))))
    402    (drop (${ty}.add (${ty}.const 1) (${ty}.atomic.rmw${size}.cmpxchg${sx} ${offs} (local.get $p) (local.get $v${ty}) (${ty}.const 37))))
    403 ))`)), memType == ptrType);
    404            }
    405 
    406        }
    407    }
    408 }
    409 
    410 // Cursorily check that invalid offsets are rejected.
    411 
    412 wasmFailValidateText(`
    413 (module
    414  (memory i32 1)
    415  (func (param $p i32)
    416    (drop (i32.load offset=0x100000000 (local.get $p)))))`, /offset too large for memory type/);
    417 
    418 
    419 ///////////////////////////////////////////////////////////////////////////////
    420 //
    421 // EXECUTION
    422 
    423 // Smoketest: Can we actually allocate a memory larger than 4GB?
    424 
    425 if (getBuildConfiguration("pointer-byte-size") == 8) {
    426    try {
    427        new WebAssembly.Memory({address:"i64", initial:BigInt(65536 * 1.5), maximum:BigInt(65536 * 2)});
    428    } catch (e) {
    429        // OOM is OK.
    430        if (!(e instanceof WebAssembly.RuntimeError) || !String(e).match(/too many memory pages/)) {
    431            throw e;
    432        }
    433    }
    434 }
    435 
    436 // JS-API
    437 
    438 if (WebAssembly.Function) {
    439  const m64 = new WebAssembly.Memory({ address: "i64", initial:1n });
    440  assertEq(m64.type().address, "i64");
    441 
    442  const m32 = new WebAssembly.Memory({ initial:1 });
    443  assertEq(m32.type().address, "i32");
    444 
    445  const t64 = new WebAssembly.Table({ address: "i64", element: "funcref", initial: 1n });
    446  assertEq(t64.type().address, "i64");
    447 
    448  const t32 = new WebAssembly.Table({ initial: 1, element: "funcref" });
    449  assertEq(t32.type().address, "i32");
    450 
    451  const ins = wasmEvalText(`(module
    452    (memory (export "mem") i64 1 0x100000000)
    453    (table (export "table") i64 1 0x1000 funcref)
    454  )`);
    455  assertEq(ins.exports.mem.type().address, "i64");
    456  assertEq(ins.exports.mem.type().minimum, 1n);
    457  assertEq(ins.exports.mem.type().maximum, 0x100000000n);
    458  assertEq(ins.exports.table.type().address, "i64");
    459  assertEq(ins.exports.table.type().minimum, 1n);
    460  assertEq(ins.exports.table.type().maximum, 0x1000n);
    461 }
    462 
    463 // Instructions
    464 
    465 const SMALL = 64;  // < offsetguard everywhere
    466 const BIG = 131072;  // > offsetguard on 32-bit
    467 const HUGE = 2147483656; // > offsetguard on 64-bit
    468 const VAST = 0x112001300;   // > 4GB
    469 
    470 function makeTest(LOC, INITIAL, MAXIMUM, SHARED) {
    471    const v128Prefix =
    472 ` (func $stash (param v128)
    473    (v128.store (i64.const 0) (local.get 0)))
    474 
    475  (func $unstash (result v128)
    476    (v128.load (i64.const 0)))
    477 `;
    478 
    479    const readV128Code =
    480 ` (func (export "readv128@0") (param $p i64)
    481    (call $stash (v128.load (local.get $p))))
    482 
    483  (func (export "readv128@small") (param $p i64)
    484    (call $stash (v128.load offset=${SMALL} (local.get $p))))
    485 
    486  (func (export "readv128@big") (param $p i64)
    487    (call $stash (v128.load offset=${BIG} (local.get $p))))
    488 
    489  (func (export "readv128@huge") (param $p i64)
    490    (call $stash (v128.load offset=${HUGE} (local.get $p))))
    491 
    492  (func (export "readv128/const@0")
    493    (call $stash (v128.load (i64.const ${LOC}))))
    494 
    495  (func (export "readv128/const@small")
    496    (call $stash (v128.load offset=${SMALL} (i64.const ${LOC}))))
    497 
    498  (func (export "readv128/const@big")
    499    (call $stash (v128.load offset=${BIG} (i64.const ${LOC}))))
    500 
    501  (func (export "v128.load_splat@small") (param $p i64)
    502    (call $stash (v128.load32_splat offset=${SMALL} (local.get $p))))
    503 
    504  (func (export "v128.load_zero@small") (param $p i64)
    505    (call $stash (v128.load32_zero offset=${SMALL} (local.get $p))))
    506 
    507  (func (export "v128.load_extend@small") (param $p i64)
    508    (call $stash (v128.load32x2_u offset=${SMALL} (local.get $p))))
    509 
    510  (func (export "v128.load_lane@small") (param $p i64)
    511    (call $stash (v128.load32_lane offset=${SMALL} 2 (local.get $p) (call $unstash))))
    512 `;
    513 
    514    const writeV128Code =
    515 ` (func (export "writev128@0") (param $p i64)
    516    (v128.store (local.get $p) (call $unstash)))
    517 
    518  (func (export "writev128@small") (param $p i64)
    519    (v128.store offset=${SMALL} (local.get $p) (call $unstash)))
    520 
    521  (func (export "writev128@big") (param $p i64)
    522    (v128.store offset=${BIG} (local.get $p) (call $unstash)))
    523 
    524  (func (export "writev128@huge") (param $p i64)
    525    (v128.store offset=${HUGE} (local.get $p) (call $unstash)))
    526 
    527  (func (export "writev128/const@0")
    528    (v128.store (i64.const ${LOC}) (call $unstash)))
    529 
    530  (func (export "writev128/const@small")
    531    (v128.store offset=${SMALL} (i64.const ${LOC}) (call $unstash)))
    532 
    533  (func (export "writev128/const@big")
    534    (v128.store offset=${BIG} (i64.const ${LOC}) (call $unstash)))
    535 
    536  (func (export "v128.store_lane@small") (param $p i64)
    537    (v128.store32_lane offset=${SMALL} 2 (local.get $p) (call $unstash)))
    538 `;
    539 
    540    const ins = wasmEvalText(`
    541 (module
    542  (memory (export "mem") i64 ${INITIAL} ${MAXIMUM} ${SHARED})
    543 
    544  ;; About the test cases: there are various optimizations in the engine
    545  ;; for different shapes of a pointer+offset.  Constant pointers are
    546  ;; resolved early; large offsets are folded early using explicit code
    547  ;; with an overflow check (but "large" depends on 32-bit vs 64-bit);
    548  ;; wait/notify fold offsets early regardless; zero offsets lead to
    549  ;; tighter code with variable pointers; and don't get me started on
    550  ;; alignment checks.  These test cases are not exhaustive but aim
    551  ;; to test at least some things.
    552 
    553  ;; TODO: more sizes for all operations, though this is not critical
    554  ;; TODO: sign extending loads, again not critical
    555 
    556  ${wasmSimdEnabled() ? v128Prefix : ""}
    557 
    558  ;; Read i32
    559  (func (export "readi32@0") (param $p i64) (result i32)
    560    (i32.load (local.get $p)))
    561 
    562  (func (export "readi32@small") (param $p i64) (result i32)
    563    (i32.load offset=${SMALL} (local.get $p)))
    564 
    565  (func (export "readi32@big") (param $p i64) (result i32)
    566    (i32.load offset=${BIG} (local.get $p)))
    567 
    568  (func (export "readi32@huge") (param $p i64) (result i32)
    569    (i32.load offset=${HUGE} (local.get $p)))
    570 
    571  (func (export "readi32@vast") (param $p i64) (result i32)
    572    (i32.load offset=${VAST} (local.get $p)))
    573 
    574  (func (export "readi32/const@0") (result i32)
    575    (i32.load (i64.const ${LOC})))
    576 
    577  (func (export "readi32/const@small") (result i32)
    578    (i32.load offset=${SMALL} (i64.const ${LOC})))
    579 
    580  (func (export "readi32/const@big") (result i32)
    581    (i32.load offset=${BIG} (i64.const ${LOC})))
    582 
    583  (func (export "readi32/const@vast") (result i32)
    584    (i32.load offset=${VAST} (i64.const ${LOC})))
    585 
    586  ;; Read i64
    587  (func (export "readi64@0") (param $p i64) (result i64)
    588    (i64.load (local.get $p)))
    589 
    590  (func (export "readi64@small") (param $p i64) (result i64)
    591    (i64.load offset=${SMALL} (local.get $p)))
    592 
    593  (func (export "readi64@big") (param $p i64) (result i64)
    594    (i64.load offset=${BIG} (local.get $p)))
    595 
    596  (func (export "readi64@huge") (param $p i64) (result i64)
    597    (i64.load offset=${HUGE} (local.get $p)))
    598 
    599  (func (export "readi64@vast") (param $p i64) (result i64)
    600    (i64.load offset=${VAST} (local.get $p)))
    601 
    602  (func (export "readi64/const@0") (result i64)
    603    (i64.load (i64.const ${LOC})))
    604 
    605  (func (export "readi64/const@small") (result i64)
    606    (i64.load offset=${SMALL} (i64.const ${LOC})))
    607 
    608  (func (export "readi64/const@big") (result i64)
    609    (i64.load offset=${BIG} (i64.const ${LOC})))
    610 
    611  (func (export "readi64/const@vast") (result i64)
    612    (i64.load offset=${VAST} (i64.const ${LOC})))
    613 
    614  ;; Read v128
    615  ${wasmSimdEnabled() ? readV128Code : ""}
    616 
    617  ;; write i32
    618  (func (export "writei32@0") (param $p i64) (param $v i32)
    619    (i32.store (local.get $p) (local.get $v)))
    620 
    621  (func (export "writei32@small") (param $p i64) (param $v i32)
    622    (i32.store offset=${SMALL} (local.get $p) (local.get $v)))
    623 
    624  (func (export "writei32@big") (param $p i64) (param $v i32)
    625    (i32.store offset=${BIG} (local.get $p) (local.get $v)))
    626 
    627  (func (export "writei32@huge") (param $p i64) (param $v i32)
    628    (i32.store offset=${HUGE} (local.get $p) (local.get $v)))
    629 
    630  (func (export "writei32@vast") (param $p i64) (param $v i32)
    631    (i32.store offset=${VAST} (local.get $p) (local.get $v)))
    632 
    633  (func (export "writei32/const@0") (param $v i32)
    634    (i32.store (i64.const ${LOC}) (local.get $v)))
    635 
    636  (func (export "writei32/const@small") (param $v i32)
    637    (i32.store offset=${SMALL} (i64.const ${LOC}) (local.get $v)))
    638 
    639  (func (export "writei32/const@big") (param $v i32)
    640    (i32.store offset=${BIG} (i64.const ${LOC}) (local.get $v)))
    641 
    642  (func (export "writei32/const@vast") (param $v i32)
    643    (i32.store offset=${VAST} (i64.const ${LOC}) (local.get $v)))
    644 
    645  ;; write i64
    646  (func (export "writei64@0") (param $p i64) (param $v i64)
    647    (i64.store (local.get $p) (local.get $v)))
    648 
    649  (func (export "writei64@small") (param $p i64) (param $v i64)
    650    (i64.store offset=${SMALL} (local.get $p) (local.get $v)))
    651 
    652  (func (export "writei64@big") (param $p i64) (param $v i64)
    653    (i64.store offset=${BIG} (local.get $p) (local.get $v)))
    654 
    655  (func (export "writei64@huge") (param $p i64) (param $v i64)
    656    (i64.store offset=${HUGE} (local.get $p) (local.get $v)))
    657 
    658  (func (export "writei64@vast") (param $p i64) (param $v i64)
    659    (i64.store offset=${VAST} (local.get $p) (local.get $v)))
    660 
    661  (func (export "writei64/const@0") (param $v i64)
    662    (i64.store (i64.const ${LOC}) (local.get $v)))
    663 
    664  (func (export "writei64/const@small") (param $v i64)
    665    (i64.store offset=${SMALL} (i64.const ${LOC}) (local.get $v)))
    666 
    667  (func (export "writei64/const@big") (param $v i64)
    668    (i64.store offset=${BIG} (i64.const ${LOC}) (local.get $v)))
    669 
    670  (func (export "writei64/const@vast") (param $v i64)
    671    (i64.store offset=${VAST} (i64.const ${LOC}) (local.get $v)))
    672 
    673  ;; Read v128
    674  ${wasmSimdEnabled() ? writeV128Code : ""}
    675 
    676  ;; Atomic read i32
    677 
    678  (func (export "areadi32@0") (param $p i64) (result i32)
    679    (i32.atomic.load (local.get $p)))
    680 
    681  (func (export "areadi32@small") (param $p i64) (result i32)
    682    (i32.atomic.load offset=${SMALL} (local.get $p)))
    683 
    684  (func (export "areadi32@big") (param $p i64) (result i32)
    685    (i32.atomic.load offset=${BIG} (local.get $p)))
    686 
    687  (func (export "areadi32@huge") (param $p i64) (result i32)
    688    (i32.atomic.load offset=${HUGE} (local.get $p)))
    689 
    690  (func (export "areadi32@vast") (param $p i64) (result i32)
    691    (i32.atomic.load offset=${VAST} (local.get $p)))
    692 
    693  (func (export "areadi32/const@0") (result i32)
    694    (i32.atomic.load (i64.const ${LOC})))
    695 
    696  (func (export "areadi32/const@small") (result i32)
    697    (i32.atomic.load offset=${SMALL} (i64.const ${LOC})))
    698 
    699  (func (export "areadi32/const@big") (result i32)
    700    (i32.atomic.load offset=${BIG} (i64.const ${LOC})))
    701 
    702  (func (export "areadi32/const@vast") (result i32)
    703    (i32.atomic.load offset=${VAST} (i64.const ${LOC})))
    704 
    705  ;; Atomic read i64
    706 
    707  (func (export "areadi64@0") (param $p i64) (result i64)
    708    (i64.atomic.load (local.get $p)))
    709 
    710  (func (export "areadi64@small") (param $p i64) (result i64)
    711    (i64.atomic.load offset=${SMALL} (local.get $p)))
    712 
    713  (func (export "areadi64@big") (param $p i64) (result i64)
    714    (i64.atomic.load offset=${BIG} (local.get $p)))
    715 
    716  (func (export "areadi64@huge") (param $p i64) (result i64)
    717    (i64.atomic.load offset=${HUGE} (local.get $p)))
    718 
    719  (func (export "areadi64@vast") (param $p i64) (result i64)
    720    (i64.atomic.load offset=${VAST} (local.get $p)))
    721 
    722  (func (export "areadi64/const@0") (result i64)
    723    (i64.atomic.load (i64.const ${LOC})))
    724 
    725  (func (export "areadi64/const@small") (result i64)
    726    (i64.atomic.load offset=${SMALL} (i64.const ${LOC})))
    727 
    728  (func (export "areadi64/const@big") (result i64)
    729    (i64.atomic.load offset=${BIG} (i64.const ${LOC})))
    730 
    731  (func (export "areadi64/const@vast") (result i64)
    732    (i64.atomic.load offset=${VAST} (i64.const ${LOC})))
    733 
    734  ;; Atomic write i32
    735 
    736  (func (export "awritei32@0") (param $p i64) (param $v i32)
    737    (i32.atomic.store (local.get $p) (local.get $v)))
    738 
    739  (func (export "awritei32@small") (param $p i64) (param $v i32)
    740    (i32.atomic.store offset=${SMALL} (local.get $p) (local.get $v)))
    741 
    742  (func (export "awritei32@big") (param $p i64) (param $v i32)
    743    (i32.atomic.store offset=${BIG} (local.get $p) (local.get $v)))
    744 
    745  (func (export "awritei32@huge") (param $p i64) (param $v i32)
    746    (i32.atomic.store offset=${HUGE} (local.get $p) (local.get $v)))
    747 
    748  (func (export "awritei32@vast") (param $p i64) (param $v i32)
    749    (i32.atomic.store offset=${VAST} (local.get $p) (local.get $v)))
    750 
    751  (func (export "awritei32/const@0") (param $v i32)
    752    (i32.atomic.store (i64.const ${LOC}) (local.get $v)))
    753 
    754  (func (export "awritei32/const@small") (param $v i32)
    755    (i32.atomic.store offset=${SMALL} (i64.const ${LOC}) (local.get $v)))
    756 
    757  (func (export "awritei32/const@big") (param $v i32)
    758    (i32.atomic.store offset=${BIG} (i64.const ${LOC}) (local.get $v)))
    759 
    760  (func (export "awritei32/const@vast") (param $v i32)
    761    (i32.atomic.store offset=${VAST} (i64.const ${LOC}) (local.get $v)))
    762 
    763  ;; Atomic write i64
    764 
    765  (func (export "awritei64@0") (param $p i64) (param $v i64)
    766    (i64.atomic.store (local.get $p) (local.get $v)))
    767 
    768  (func (export "awritei64@small") (param $p i64) (param $v i64)
    769    (i64.atomic.store offset=${SMALL} (local.get $p) (local.get $v)))
    770 
    771  (func (export "awritei64@big") (param $p i64) (param $v i64)
    772    (i64.atomic.store offset=${BIG} (local.get $p) (local.get $v)))
    773 
    774  (func (export "awritei64@huge") (param $p i64) (param $v i64)
    775    (i64.atomic.store offset=${HUGE} (local.get $p) (local.get $v)))
    776 
    777  (func (export "awritei64@vast") (param $p i64) (param $v i64)
    778    (i64.atomic.store offset=${VAST} (local.get $p) (local.get $v)))
    779 
    780  (func (export "awritei64/const@0") (param $v i64)
    781    (i64.atomic.store (i64.const ${LOC}) (local.get $v)))
    782 
    783  (func (export "awritei64/const@small") (param $v i64)
    784    (i64.atomic.store offset=${SMALL} (i64.const ${LOC}) (local.get $v)))
    785 
    786  (func (export "awritei64/const@big") (param $v i64)
    787    (i64.atomic.store offset=${BIG} (i64.const ${LOC}) (local.get $v)))
    788 
    789  (func (export "awritei64/const@vast") (param $v i64)
    790    (i64.atomic.store offset=${VAST} (i64.const ${LOC}) (local.get $v)))
    791 
    792  ;; xchg i32
    793 
    794  (func (export "xchgi32@0") (param $p i64) (param $v i32) (result i32)
    795    (i32.atomic.rmw.xchg (local.get $p) (local.get $v)))
    796 
    797  (func (export "xchgi32@small") (param $p i64) (param $v i32) (result i32)
    798    (i32.atomic.rmw.xchg offset=${SMALL} (local.get $p) (local.get $v)))
    799 
    800  (func (export "xchgi32@big") (param $p i64) (param $v i32) (result i32)
    801    (i32.atomic.rmw.xchg offset=${BIG} (local.get $p) (local.get $v)))
    802 
    803  (func (export "xchgi32@huge") (param $p i64) (param $v i32) (result i32)
    804    (i32.atomic.rmw.xchg offset=${HUGE} (local.get $p) (local.get $v)))
    805 
    806  (func (export "xchgi32/const@0") (param $v i32) (result i32)
    807    (i32.atomic.rmw.xchg (i64.const ${LOC}) (local.get $v)))
    808 
    809  (func (export "xchgi32/const@small") (param $v i32) (result i32)
    810    (i32.atomic.rmw.xchg offset=${SMALL} (i64.const ${LOC}) (local.get $v)))
    811 
    812  (func (export "xchgi32/const@big") (param $v i32) (result i32)
    813    (i32.atomic.rmw.xchg offset=${BIG} (i64.const ${LOC}) (local.get $v)))
    814 
    815  ;; xchg i64
    816 
    817  (func (export "xchgi64@0") (param $p i64) (param $v i64) (result i64)
    818    (i64.atomic.rmw.xchg (local.get $p) (local.get $v)))
    819 
    820  (func (export "xchgi64@small") (param $p i64) (param $v i64) (result i64)
    821    (i64.atomic.rmw.xchg offset=${SMALL} (local.get $p) (local.get $v)))
    822 
    823  (func (export "xchgi64@big") (param $p i64) (param $v i64) (result i64)
    824    (i64.atomic.rmw.xchg offset=${BIG} (local.get $p) (local.get $v)))
    825 
    826  (func (export "xchgi64@huge") (param $p i64) (param $v i64) (result i64)
    827    (i64.atomic.rmw.xchg offset=${HUGE} (local.get $p) (local.get $v)))
    828 
    829  (func (export "xchgi64/const@0") (param $v i64) (result i64)
    830    (i64.atomic.rmw.xchg (i64.const ${LOC}) (local.get $v)))
    831 
    832  (func (export "xchgi64/const@small") (param $v i64) (result i64)
    833    (i64.atomic.rmw.xchg offset=${SMALL} (i64.const ${LOC}) (local.get $v)))
    834 
    835  (func (export "xchgi64/const@big") (param $v i64) (result i64)
    836    (i64.atomic.rmw.xchg offset=${BIG} (i64.const ${LOC}) (local.get $v)))
    837 
    838  ;; add i32
    839 
    840  (func (export "addi32@0") (param $p i64) (param $v i32) (result i32)
    841    (i32.atomic.rmw.add (local.get $p) (local.get $v)))
    842 
    843  (func (export "addi32@small") (param $p i64) (param $v i32) (result i32)
    844    (i32.atomic.rmw.add offset=${SMALL} (local.get $p) (local.get $v)))
    845 
    846  (func (export "addi32@big") (param $p i64) (param $v i32) (result i32)
    847    (i32.atomic.rmw.add offset=${BIG} (local.get $p) (local.get $v)))
    848 
    849  (func (export "addi32@huge") (param $p i64) (param $v i32) (result i32)
    850    (i32.atomic.rmw.add offset=${HUGE} (local.get $p) (local.get $v)))
    851 
    852  (func (export "addi32/const@0") (param $v i32) (result i32)
    853    (i32.atomic.rmw.add (i64.const ${LOC}) (local.get $v)))
    854 
    855  (func (export "addi32/const@small") (param $v i32) (result i32)
    856    (i32.atomic.rmw.add offset=${SMALL} (i64.const ${LOC}) (local.get $v)))
    857 
    858  (func (export "addi32/const@big") (param $v i32) (result i32)
    859    (i32.atomic.rmw.add offset=${BIG} (i64.const ${LOC}) (local.get $v)))
    860 
    861  ;; add i64
    862 
    863  (func (export "addi64@0") (param $p i64) (param $v i64) (result i64)
    864    (i64.atomic.rmw.add (local.get $p) (local.get $v)))
    865 
    866  (func (export "addi64@small") (param $p i64) (param $v i64) (result i64)
    867    (i64.atomic.rmw.add offset=${SMALL} (local.get $p) (local.get $v)))
    868 
    869  (func (export "addi64@big") (param $p i64) (param $v i64) (result i64)
    870    (i64.atomic.rmw.add offset=${BIG} (local.get $p) (local.get $v)))
    871 
    872  (func (export "addi64@huge") (param $p i64) (param $v i64) (result i64)
    873    (i64.atomic.rmw.add offset=${HUGE} (local.get $p) (local.get $v)))
    874 
    875  (func (export "addi64/const@0") (param $v i64) (result i64)
    876    (i64.atomic.rmw.add (i64.const ${LOC}) (local.get $v)))
    877 
    878  (func (export "addi64/const@small") (param $v i64) (result i64)
    879    (i64.atomic.rmw.add offset=${SMALL} (i64.const ${LOC}) (local.get $v)))
    880 
    881  (func (export "addi64/const@big") (param $v i64) (result i64)
    882    (i64.atomic.rmw.add offset=${BIG} (i64.const ${LOC}) (local.get $v)))
    883 
    884  ;; sub i32
    885 
    886  (func (export "subi32@0") (param $p i64) (param $v i32) (result i32)
    887    (i32.atomic.rmw.sub (local.get $p) (local.get $v)))
    888 
    889  (func (export "subi32@small") (param $p i64) (param $v i32) (result i32)
    890    (i32.atomic.rmw.sub offset=${SMALL} (local.get $p) (local.get $v)))
    891 
    892  (func (export "subi32@big") (param $p i64) (param $v i32) (result i32)
    893    (i32.atomic.rmw.sub offset=${BIG} (local.get $p) (local.get $v)))
    894 
    895  (func (export "subi32@huge") (param $p i64) (param $v i32) (result i32)
    896    (i32.atomic.rmw.sub offset=${HUGE} (local.get $p) (local.get $v)))
    897 
    898  (func (export "subi32/const@0") (param $v i32) (result i32)
    899    (i32.atomic.rmw.sub (i64.const ${LOC}) (local.get $v)))
    900 
    901  (func (export "subi32/const@small") (param $v i32) (result i32)
    902    (i32.atomic.rmw.sub offset=${SMALL} (i64.const ${LOC}) (local.get $v)))
    903 
    904  (func (export "subi32/const@big") (param $v i32) (result i32)
    905    (i32.atomic.rmw.sub offset=${BIG} (i64.const ${LOC}) (local.get $v)))
    906 
    907  ;; sub i64
    908 
    909  (func (export "subi64@0") (param $p i64) (param $v i64) (result i64)
    910    (i64.atomic.rmw.sub (local.get $p) (local.get $v)))
    911 
    912  (func (export "subi64@small") (param $p i64) (param $v i64) (result i64)
    913    (i64.atomic.rmw.sub offset=${SMALL} (local.get $p) (local.get $v)))
    914 
    915  (func (export "subi64@big") (param $p i64) (param $v i64) (result i64)
    916    (i64.atomic.rmw.sub offset=${BIG} (local.get $p) (local.get $v)))
    917 
    918  (func (export "subi64@huge") (param $p i64) (param $v i64) (result i64)
    919    (i64.atomic.rmw.sub offset=${HUGE} (local.get $p) (local.get $v)))
    920 
    921  (func (export "subi64/const@0") (param $v i64) (result i64)
    922    (i64.atomic.rmw.sub (i64.const ${LOC}) (local.get $v)))
    923 
    924  (func (export "subi64/const@small") (param $v i64) (result i64)
    925    (i64.atomic.rmw.sub offset=${SMALL} (i64.const ${LOC}) (local.get $v)))
    926 
    927  (func (export "subi64/const@big") (param $v i64) (result i64)
    928    (i64.atomic.rmw.sub offset=${BIG} (i64.const ${LOC}) (local.get $v)))
    929 
    930  ;; and i32
    931 
    932  (func (export "andi32@0") (param $p i64) (param $v i32) (result i32)
    933    (i32.atomic.rmw.and (local.get $p) (local.get $v)))
    934 
    935  (func (export "andi32@small") (param $p i64) (param $v i32) (result i32)
    936    (i32.atomic.rmw.and offset=${SMALL} (local.get $p) (local.get $v)))
    937 
    938  (func (export "andi32@big") (param $p i64) (param $v i32) (result i32)
    939    (i32.atomic.rmw.and offset=${BIG} (local.get $p) (local.get $v)))
    940 
    941  (func (export "andi32@huge") (param $p i64) (param $v i32) (result i32)
    942    (i32.atomic.rmw.and offset=${HUGE} (local.get $p) (local.get $v)))
    943 
    944  (func (export "andi32/const@0") (param $v i32) (result i32)
    945    (i32.atomic.rmw.and (i64.const ${LOC}) (local.get $v)))
    946 
    947  (func (export "andi32/const@small") (param $v i32) (result i32)
    948    (i32.atomic.rmw.and offset=${SMALL} (i64.const ${LOC}) (local.get $v)))
    949 
    950  (func (export "andi32/const@big") (param $v i32) (result i32)
    951    (i32.atomic.rmw.and offset=${BIG} (i64.const ${LOC}) (local.get $v)))
    952 
    953  ;; and i64
    954 
    955  (func (export "andi64@0") (param $p i64) (param $v i64) (result i64)
    956    (i64.atomic.rmw.and (local.get $p) (local.get $v)))
    957 
    958  (func (export "andi64@small") (param $p i64) (param $v i64) (result i64)
    959    (i64.atomic.rmw.and offset=${SMALL} (local.get $p) (local.get $v)))
    960 
    961  (func (export "andi64@big") (param $p i64) (param $v i64) (result i64)
    962    (i64.atomic.rmw.and offset=${BIG} (local.get $p) (local.get $v)))
    963 
    964  (func (export "andi64@huge") (param $p i64) (param $v i64) (result i64)
    965    (i64.atomic.rmw.and offset=${HUGE} (local.get $p) (local.get $v)))
    966 
    967  (func (export "andi64/const@0") (param $v i64) (result i64)
    968    (i64.atomic.rmw.and (i64.const ${LOC}) (local.get $v)))
    969 
    970  (func (export "andi64/const@small") (param $v i64) (result i64)
    971    (i64.atomic.rmw.and offset=${SMALL} (i64.const ${LOC}) (local.get $v)))
    972 
    973  (func (export "andi64/const@big") (param $v i64) (result i64)
    974    (i64.atomic.rmw.and offset=${BIG} (i64.const ${LOC}) (local.get $v)))
    975 
    976  ;; or i32
    977 
    978  (func (export "ori32@0") (param $p i64) (param $v i32) (result i32)
    979    (i32.atomic.rmw.or (local.get $p) (local.get $v)))
    980 
    981  (func (export "ori32@small") (param $p i64) (param $v i32) (result i32)
    982    (i32.atomic.rmw.or offset=${SMALL} (local.get $p) (local.get $v)))
    983 
    984  (func (export "ori32@big") (param $p i64) (param $v i32) (result i32)
    985    (i32.atomic.rmw.or offset=${BIG} (local.get $p) (local.get $v)))
    986 
    987  (func (export "ori32@huge") (param $p i64) (param $v i32) (result i32)
    988    (i32.atomic.rmw.or offset=${HUGE} (local.get $p) (local.get $v)))
    989 
    990  (func (export "ori32/const@0") (param $v i32) (result i32)
    991    (i32.atomic.rmw.or (i64.const ${LOC}) (local.get $v)))
    992 
    993  (func (export "ori32/const@small") (param $v i32) (result i32)
    994    (i32.atomic.rmw.or offset=${SMALL} (i64.const ${LOC}) (local.get $v)))
    995 
    996  (func (export "ori32/const@big") (param $v i32) (result i32)
    997    (i32.atomic.rmw.or offset=${BIG} (i64.const ${LOC}) (local.get $v)))
    998 
    999  ;; or i64
   1000 
   1001  (func (export "ori64@0") (param $p i64) (param $v i64) (result i64)
   1002    (i64.atomic.rmw.or (local.get $p) (local.get $v)))
   1003 
   1004  (func (export "ori64@small") (param $p i64) (param $v i64) (result i64)
   1005    (i64.atomic.rmw.or offset=${SMALL} (local.get $p) (local.get $v)))
   1006 
   1007  (func (export "ori64@big") (param $p i64) (param $v i64) (result i64)
   1008    (i64.atomic.rmw.or offset=${BIG} (local.get $p) (local.get $v)))
   1009 
   1010  (func (export "ori64@huge") (param $p i64) (param $v i64) (result i64)
   1011    (i64.atomic.rmw.or offset=${HUGE} (local.get $p) (local.get $v)))
   1012 
   1013  (func (export "ori64/const@0") (param $v i64) (result i64)
   1014    (i64.atomic.rmw.or (i64.const ${LOC}) (local.get $v)))
   1015 
   1016  (func (export "ori64/const@small") (param $v i64) (result i64)
   1017    (i64.atomic.rmw.or offset=${SMALL} (i64.const ${LOC}) (local.get $v)))
   1018 
   1019  (func (export "ori64/const@big") (param $v i64) (result i64)
   1020    (i64.atomic.rmw.or offset=${BIG} (i64.const ${LOC}) (local.get $v)))
   1021 
   1022  ;; xor i32
   1023 
   1024  (func (export "xori32@0") (param $p i64) (param $v i32) (result i32)
   1025    (i32.atomic.rmw.xor (local.get $p) (local.get $v)))
   1026 
   1027  (func (export "xori32@small") (param $p i64) (param $v i32) (result i32)
   1028    (i32.atomic.rmw.xor offset=${SMALL} (local.get $p) (local.get $v)))
   1029 
   1030  (func (export "xori32@big") (param $p i64) (param $v i32) (result i32)
   1031    (i32.atomic.rmw.xor offset=${BIG} (local.get $p) (local.get $v)))
   1032 
   1033  (func (export "xori32@huge") (param $p i64) (param $v i32) (result i32)
   1034    (i32.atomic.rmw.xor offset=${HUGE} (local.get $p) (local.get $v)))
   1035 
   1036  (func (export "xori32/const@0") (param $v i32) (result i32)
   1037    (i32.atomic.rmw.xor (i64.const ${LOC}) (local.get $v)))
   1038 
   1039  (func (export "xori32/const@small") (param $v i32) (result i32)
   1040    (i32.atomic.rmw.xor offset=${SMALL} (i64.const ${LOC}) (local.get $v)))
   1041 
   1042  (func (export "xori32/const@big") (param $v i32) (result i32)
   1043    (i32.atomic.rmw.xor offset=${BIG} (i64.const ${LOC}) (local.get $v)))
   1044 
   1045  ;; xor i64
   1046 
   1047  (func (export "xori64@0") (param $p i64) (param $v i64) (result i64)
   1048    (i64.atomic.rmw.xor (local.get $p) (local.get $v)))
   1049 
   1050  (func (export "xori64@small") (param $p i64) (param $v i64) (result i64)
   1051    (i64.atomic.rmw.xor offset=${SMALL} (local.get $p) (local.get $v)))
   1052 
   1053  (func (export "xori64@big") (param $p i64) (param $v i64) (result i64)
   1054    (i64.atomic.rmw.xor offset=${BIG} (local.get $p) (local.get $v)))
   1055 
   1056  (func (export "xori64@huge") (param $p i64) (param $v i64) (result i64)
   1057    (i64.atomic.rmw.xor offset=${HUGE} (local.get $p) (local.get $v)))
   1058 
   1059  (func (export "xori64/const@0") (param $v i64) (result i64)
   1060    (i64.atomic.rmw.xor (i64.const ${LOC}) (local.get $v)))
   1061 
   1062  (func (export "xori64/const@small") (param $v i64) (result i64)
   1063    (i64.atomic.rmw.xor offset=${SMALL} (i64.const ${LOC}) (local.get $v)))
   1064 
   1065  (func (export "xori64/const@big") (param $v i64) (result i64)
   1066    (i64.atomic.rmw.xor offset=${BIG} (i64.const ${LOC}) (local.get $v)))
   1067 
   1068  ;; cmpxchg i32
   1069 
   1070  (func (export "cmpxchgi32@0") (param $p i64) (param $expect i32) (param $new i32) (result i32)
   1071    (i32.atomic.rmw.cmpxchg (local.get $p) (local.get $expect) (local.get $new)))
   1072 
   1073  (func (export "cmpxchgi32@small") (param $p i64) (param $expect i32) (param $new i32) (result i32)
   1074    (i32.atomic.rmw.cmpxchg offset=${SMALL} (local.get $p) (local.get $expect) (local.get $new)))
   1075 
   1076  (func (export "cmpxchgi32@big") (param $p i64) (param $expect i32) (param $new i32) (result i32)
   1077    (i32.atomic.rmw.cmpxchg offset=${BIG} (local.get $p) (local.get $expect) (local.get $new)))
   1078 
   1079  (func (export "cmpxchgi32@huge") (param $p i64) (param $expect i32) (param $new i32) (result i32)
   1080    (i32.atomic.rmw.cmpxchg offset=${HUGE} (local.get $p) (local.get $expect) (local.get $new)))
   1081 
   1082  (func (export "cmpxchgi32/const@0") (param $expect i32) (param $new i32) (result i32)
   1083    (i32.atomic.rmw.cmpxchg (i64.const ${LOC}) (local.get $expect) (local.get $new)))
   1084 
   1085  (func (export "cmpxchgi32/const@small") (param $expect i32) (param $new i32) (result i32)
   1086    (i32.atomic.rmw.cmpxchg offset=${SMALL} (i64.const ${LOC}) (local.get $expect) (local.get $new)))
   1087 
   1088  (func (export "cmpxchgi32/const@big") (param $expect i32) (param $new i32) (result i32)
   1089    (i32.atomic.rmw.cmpxchg offset=${BIG} (i64.const ${LOC}) (local.get $expect) (local.get $new)))
   1090 
   1091  ;; cmpxchg i64
   1092 
   1093  (func (export "cmpxchgi64@0") (param $p i64) (param $expect i64) (param $new i64) (result i64)
   1094    (i64.atomic.rmw.cmpxchg (local.get $p) (local.get $expect) (local.get $new)))
   1095 
   1096  (func (export "cmpxchgi64@small") (param $p i64) (param $expect i64) (param $new i64) (result i64)
   1097    (i64.atomic.rmw.cmpxchg offset=${SMALL} (local.get $p) (local.get $expect) (local.get $new)))
   1098 
   1099  (func (export "cmpxchgi64@big") (param $p i64) (param $expect i64) (param $new i64) (result i64)
   1100    (i64.atomic.rmw.cmpxchg offset=${BIG} (local.get $p) (local.get $expect) (local.get $new)))
   1101 
   1102  (func (export "cmpxchgi64@huge") (param $p i64) (param $expect i64) (param $new i64) (result i64)
   1103    (i64.atomic.rmw.cmpxchg offset=${HUGE} (local.get $p) (local.get $expect) (local.get $new)))
   1104 
   1105  (func (export "cmpxchgi64/const@0") (param $expect i64) (param $new i64) (result i64)
   1106    (i64.atomic.rmw.cmpxchg (i64.const ${LOC}) (local.get $expect) (local.get $new)))
   1107 
   1108  (func (export "cmpxchgi64/const@small") (param $expect i64) (param $new i64) (result i64)
   1109    (i64.atomic.rmw.cmpxchg offset=${SMALL} (i64.const ${LOC}) (local.get $expect) (local.get $new)))
   1110 
   1111  (func (export "cmpxchgi64/const@big") (param $expect i64) (param $new i64) (result i64)
   1112    (i64.atomic.rmw.cmpxchg offset=${BIG} (i64.const ${LOC}) (local.get $expect) (local.get $new)))
   1113 
   1114  ;; wait
   1115 
   1116  (func (export "waiti32@small") (param $p i64) (result i32)
   1117    (memory.atomic.wait32 offset=${SMALL} (local.get $p) (i32.const 1) (i64.const 0)))
   1118 
   1119  (func (export "waiti32@huge") (param $p i64) (result i32)
   1120    (memory.atomic.wait32 offset=${HUGE} (local.get $p) (i32.const 1) (i64.const 0)))
   1121 
   1122  (func (export "waiti64@small") (param $p i64) (result i32)
   1123    (memory.atomic.wait64 offset=${SMALL} (local.get $p) (i64.const 1) (i64.const 0)))
   1124 
   1125  (func (export "waiti64@huge") (param $p i64) (result i32)
   1126    (memory.atomic.wait64 offset=${HUGE} (local.get $p) (i64.const 1) (i64.const 0)))
   1127 
   1128  ;; wake
   1129 
   1130  (func (export "wake@0") (param $p i64) (result i32)
   1131    (memory.atomic.notify (local.get $p) (i32.const 1)))
   1132 
   1133  (func (export "wake@small") (param $p i64) (result i32)
   1134    (memory.atomic.notify offset=${SMALL} (local.get $p) (i32.const 1)))
   1135 
   1136  (func (export "wake@big") (param $p i64) (result i32)
   1137    (memory.atomic.notify offset=${BIG} (local.get $p) (i32.const 1)))
   1138 
   1139  (func (export "wake@huge") (param $p i64) (result i32)
   1140    (memory.atomic.notify offset=${HUGE} (local.get $p) (i32.const 1)))
   1141 )
   1142 `);
   1143    return ins;
   1144 }
   1145 
   1146 function i32Random() {
   1147    // Limit this to small positive numbers to keep life simple.
   1148    for (;;) {
   1149        let r = (Math.random() * 0x3FFF_FFFF) | 0;
   1150        if (r != 0)
   1151            return r;
   1152    }
   1153 }
   1154 
   1155 function i64Random() {
   1156    return (BigInt(i32Random()) << 32n) | BigInt(i32Random());
   1157 }
   1158 
   1159 function Random(sz) {
   1160    if (sz == 4)
   1161        return i32Random();
   1162    return i64Random();
   1163 }
   1164 
   1165 function Random2(sz) {
   1166    return [Random(sz), Random(sz)];
   1167 }
   1168 
   1169 function Random4(sz) {
   1170    return [Random(sz), Random(sz), Random(sz), Random(sz)];
   1171 }
   1172 
   1173 function Zero(sz) {
   1174    if (sz == 4)
   1175        return 0;
   1176    return 0n;
   1177 }
   1178 
   1179 function testRead(ins, mem, LOC, prefix) {
   1180    let r = 0;
   1181    let SZ = mem.BYTES_PER_ELEMENT;
   1182    let len = mem.length * SZ;
   1183    let NM = prefix + "readi" + (SZ * 8);
   1184 
   1185    // Read in-bounds
   1186 
   1187    r = Random(SZ);
   1188    mem[LOC / SZ] = r;
   1189    assertEq(ins.exports[NM + "@0"](BigInt(LOC)), r);
   1190    assertEq(ins.exports[NM + "/const@0"](), r);
   1191 
   1192    mem[(len / SZ) - 1] = Zero(SZ);
   1193    assertEq(ins.exports[NM + "@0"](BigInt(len - SZ)), Zero(SZ)); // Just barely in-bounds
   1194 
   1195    r = Random(SZ);
   1196    mem[(LOC + SMALL) / SZ] = r;
   1197    assertEq(ins.exports[NM + "@small"](BigInt(LOC)), r);
   1198    assertEq(ins.exports[NM + "/const@small"](), r);
   1199 
   1200    if (len >= LOC + BIG + SZ) {
   1201        r = Random(SZ);
   1202        mem[(LOC + BIG) / SZ] = r;
   1203        assertEq(ins.exports[NM + "@big"](BigInt(LOC)), r);
   1204        assertEq(ins.exports[NM + "/const@big"](), r);
   1205    }
   1206 
   1207    if (len >= LOC + VAST + SZ) {
   1208        r = Random(SZ);
   1209        mem[(LOC + VAST) / SZ] = r;
   1210        assertEq(ins.exports[NM + "@vast"](BigInt(LOC)), r);
   1211        assertEq(ins.exports[NM + "/const@vast"](), r);
   1212    }
   1213 
   1214    // Read out-of-bounds
   1215 
   1216    assertErrorMessage(() => ins.exports[NM + "@0"](BigInt(len)),
   1217                       WebAssembly.RuntimeError,
   1218                       /out of bounds/);
   1219 
   1220    assertErrorMessage(() => ins.exports[NM + "@0"](BigInt(len-(SZ-1))),
   1221                       WebAssembly.RuntimeError,
   1222                       prefix == "" ? /out of bounds/ : /unaligned memory access/);
   1223 
   1224    // This is OOB if we consider the whole pointer as we must, but if we
   1225    // mistakenly only look at the low bits then it's in-bounds.
   1226    if (len < 0x1_0000_0000) {
   1227        assertErrorMessage(() => ins.exports[NM + "@0"](0x1_0000_0000n),
   1228                           WebAssembly.RuntimeError,
   1229                           /out of bounds/);
   1230    }
   1231 
   1232    if (len < HUGE) {
   1233        assertErrorMessage(() => ins.exports[NM + "@huge"](0n),
   1234                           WebAssembly.RuntimeError,
   1235                           /out of bounds/);
   1236    }
   1237 
   1238    if (len < VAST) {
   1239        assertErrorMessage(() => ins.exports[NM + "@vast"](0n),
   1240                           WebAssembly.RuntimeError,
   1241                           /out of bounds/);
   1242    }
   1243 }
   1244 
   1245 function testReadV128(ins, mem, LOC) {
   1246    let r = 0;
   1247    let SZ = mem.BYTES_PER_ELEMENT;
   1248    let len = mem.length * SZ;
   1249    let NM = "readv128";
   1250 
   1251    assertEq(SZ, 4);
   1252 
   1253    // Read in-bounds
   1254 
   1255    r = Random4(4);
   1256    mem.set(r, LOC / SZ);
   1257    ins.exports[NM + "@0"](BigInt(LOC))
   1258    assertSame(mem.slice(0, 4), r);
   1259    ins.exports[NM + "/const@0"]();
   1260    assertSame(mem.slice(0, 4), r);
   1261 
   1262    r = new Int32Array([0,0,0,0]);
   1263    mem.set(r, (len / SZ) - 4);
   1264    ins.exports[NM + "@0"](BigInt(len - (SZ * 4)));  // Just barely in-bounds
   1265    assertSame(mem.slice(0, 4), r);
   1266 
   1267    r = Random4(SZ);
   1268    mem.set(r, (LOC + SMALL) / SZ);
   1269    ins.exports[NM + "@small"](BigInt(LOC))
   1270    assertSame(mem.slice(0, 4), r);
   1271    ins.exports[NM + "/const@small"]();
   1272    assertSame(mem.slice(0, 4), r);
   1273 
   1274    if (len >= LOC + BIG + SZ * 4) {
   1275        r = Random4(SZ);
   1276        mem.set(r, (LOC + BIG) / SZ);
   1277        ins.exports[NM + "@big"](BigInt(LOC));
   1278        assertSame(mem.slice(0, 4), r);
   1279        ins.exports[NM + "/const@big"]();
   1280        assertSame(mem.slice(0, 4), r);
   1281    }
   1282 
   1283    // Read out-of-bounds
   1284 
   1285    assertErrorMessage(() => ins.exports[NM + "@0"](BigInt(len)),
   1286                       WebAssembly.RuntimeError,
   1287                       /out of bounds/);
   1288 
   1289    assertErrorMessage(() => ins.exports[NM + "@0"](BigInt(len-((SZ*4)-1))),
   1290                       WebAssembly.RuntimeError,
   1291                       /out of bounds/);
   1292 
   1293    // This is OOB if we consider the whole pointer as we must, but if we
   1294    // mistakenly only look at the low bits then it's in-bounds.
   1295    if (len < 0x1_0000_0000) {
   1296        assertErrorMessage(() => ins.exports[NM + "@0"](0x1_0000_0000n),
   1297                           WebAssembly.RuntimeError,
   1298                           /out of bounds/);
   1299    }
   1300 
   1301    if (len < HUGE) {
   1302        assertErrorMessage(() => ins.exports[NM + "@huge"](0n),
   1303                           WebAssembly.RuntimeError,
   1304                           /out of bounds/);
   1305    }
   1306 
   1307    // Superficial testing of other load operations
   1308 
   1309    r = i32Random()
   1310    mem[(LOC + SMALL) / SZ] = r;
   1311    ins.exports["v128.load_splat@small"](BigInt(LOC));
   1312    assertSame(mem.slice(0, 4), [r, r, r, r]);
   1313 
   1314    r = i32Random()
   1315    mem[(LOC + SMALL) / SZ] = r;
   1316    ins.exports["v128.load_zero@small"](BigInt(LOC));
   1317    assertSame(mem.slice(0, 4), [r, 0, 0, 0]);
   1318 
   1319    r = Random2(SZ)
   1320    mem.set(r, (LOC + SMALL) / SZ);
   1321    ins.exports["v128.load_extend@small"](BigInt(LOC));
   1322    assertSame(mem.slice(0, 4), [r[0], 0, r[1], 0]);
   1323 
   1324    r = Random4(SZ)
   1325    mem.set(r, 0);
   1326    let s = i32Random()
   1327    mem[(LOC + SMALL) / SZ] = s;
   1328    ins.exports["v128.load_lane@small"](BigInt(LOC));
   1329    assertSame(mem.slice(0, 4), [r[0], r[1], s, r[3]]);
   1330 }
   1331 
   1332 function testWrite(ins, mem, LOC, prefix) {
   1333    let r = 0;
   1334    let SZ = mem.BYTES_PER_ELEMENT;
   1335    let len = mem.length * SZ;
   1336    let WNM = prefix + "writei" + (SZ * 8);
   1337    let RNM = prefix + "readi" + (SZ * 8);
   1338 
   1339    // Write in-bounds
   1340 
   1341    r = Random(SZ);
   1342    ins.exports[WNM + "@0"](BigInt(LOC), r);
   1343    assertEq(ins.exports[RNM + "@0"](BigInt(LOC)), r);
   1344 
   1345    r = Random(SZ);
   1346    ins.exports[WNM + "@small"](BigInt(LOC), r);
   1347    assertEq(ins.exports[RNM + "@small"](BigInt(LOC)), r);
   1348 
   1349    if (len >= LOC + BIG + SZ) {
   1350        r = Random(SZ);
   1351        ins.exports[WNM + "@big"](BigInt(LOC), r);
   1352        assertEq(ins.exports[RNM + "@big"](BigInt(LOC)), r);
   1353    }
   1354 
   1355    if (len >= LOC + VAST + SZ) {
   1356        r = Random(SZ);
   1357        ins.exports[WNM + "@vast"](BigInt(LOC), r);
   1358        assertEq(ins.exports[RNM + "@vast"](BigInt(LOC)), r);
   1359    }
   1360 
   1361    r = Random(SZ);
   1362    ins.exports[WNM + "@0"](BigInt(len - SZ), r); // Just barely in-bounds
   1363    assertEq(ins.exports[RNM + "@0"](BigInt(len - SZ)), r);
   1364 
   1365    // Write out-of-bounds
   1366 
   1367    assertErrorMessage(() => ins.exports[WNM + "@0"](BigInt(len), Random(SZ)),
   1368                       WebAssembly.RuntimeError,
   1369                       /out of bounds/);
   1370 
   1371    assertErrorMessage(() => ins.exports[WNM + "@0"](BigInt(len - (SZ - 1)), Random(SZ)),
   1372                       WebAssembly.RuntimeError,
   1373                       prefix == "" ? /out of bounds/ : /unaligned memory access/);
   1374 
   1375    if (len < 0x1_0000_0000) {
   1376        let xs = ins.exports[RNM + "@0"](0n);
   1377        assertErrorMessage(() => ins.exports[WNM + "@0"](0x1_0000_0000n, Random(SZ)),
   1378                           WebAssembly.RuntimeError,
   1379                           /out of bounds/);
   1380        // Check for scribbling
   1381        assertEq(ins.exports[RNM + "@0"](0n), xs);
   1382    }
   1383 
   1384    if (len < HUGE) {
   1385        assertErrorMessage(() => ins.exports[WNM + "@huge"](0n, Random(SZ)),
   1386                           WebAssembly.RuntimeError,
   1387                           /out of bounds/);
   1388    }
   1389 
   1390    if (len < VAST) {
   1391        assertErrorMessage(() => ins.exports[WNM + "@vast"](0n, Random(SZ)),
   1392                           WebAssembly.RuntimeError,
   1393                           /out of bounds/);
   1394    }
   1395 }
   1396 
   1397 function testWriteV128(ins, mem, LOC) {
   1398    let r = 0;
   1399    let p = 0;
   1400    let SZ = mem.BYTES_PER_ELEMENT;
   1401    let len = mem.length * SZ;
   1402    let WNM = "writev128";
   1403    let RNM = "readv128";
   1404 
   1405    assertEq(SZ, 4);
   1406 
   1407    // Write in-bounds
   1408 
   1409    r = Random4(SZ);
   1410    mem.set(r, 0);
   1411    p = LOC / SZ;
   1412    ins.exports[WNM + "@0"](BigInt(LOC));
   1413    assertSame(mem.slice(p, p + 4), r);
   1414 
   1415    r = Random4(SZ);
   1416    mem.set(r, 0);
   1417    p = (LOC + SMALL) / SZ;
   1418    ins.exports[WNM + "@small"](BigInt(LOC));
   1419    assertSame(mem.slice(p, p + 4), r);
   1420 
   1421    if (len >= LOC + BIG + SZ) {
   1422        r = Random4(SZ);
   1423        mem.set(r, 0);
   1424        p = (LOC + BIG) / SZ;
   1425        ins.exports[WNM + "@big"](BigInt(LOC));
   1426        assertSame(mem.slice(p, p + 4), r);
   1427    }
   1428 
   1429    r = Random4(SZ);
   1430    mem.set(r, 0);
   1431    p = (len - (SZ * 4)) / SZ;
   1432    ins.exports[WNM + "@0"](BigInt(len - (SZ * 4))); // Just barely in-bounds
   1433    assertSame(mem.slice(p, p + 4), r);
   1434 
   1435    // Write out-of-bounds
   1436 
   1437    assertErrorMessage(() => ins.exports[WNM + "@0"](BigInt(len)),
   1438                       WebAssembly.RuntimeError,
   1439                       /out of bounds/);
   1440 
   1441    assertErrorMessage(() => ins.exports[WNM + "@0"](BigInt(len - ((SZ * 4) - 1))),
   1442                       WebAssembly.RuntimeError,
   1443                       /out of bounds/);
   1444 
   1445    if (len < HUGE) {
   1446        assertErrorMessage(() => ins.exports[WNM + "@huge"](0n),
   1447                           WebAssembly.RuntimeError,
   1448                           /out of bounds/);
   1449    }
   1450 
   1451    // Superficial testing of other store operations
   1452 
   1453    r = Random4(SZ);
   1454    mem.set(r, 0);
   1455    ins.exports["v128.store_lane@small"](BigInt(LOC));
   1456    assertEq(mem[(LOC + SMALL) / SZ], r[2]);
   1457 }
   1458 
   1459 function testAtomicRMW(ins, mem, LOC, op, fn) {
   1460    let r = 0, s = 0;
   1461    let SZ = mem.BYTES_PER_ELEMENT;
   1462    let len = mem.length * SZ;
   1463    let NM = op + "i" + (SZ * 8);
   1464 
   1465    [r,s] = Random2(SZ);
   1466    mem[LOC / SZ] = r;
   1467    assertEq(ins.exports[NM + "@0"](BigInt(LOC), s), r);
   1468    assertEq(mem[LOC / SZ], fn(r, s));
   1469 
   1470    [r,s] = Random2(SZ);
   1471    mem[(LOC + SMALL) / SZ] = r;
   1472    assertEq(ins.exports[NM + "@small"](BigInt(LOC), s), r);
   1473    assertEq(mem[(LOC + SMALL) / SZ], fn(r, s));
   1474 
   1475    if (len >= LOC + BIG + SZ) {
   1476        [r,s] = Random2(SZ);
   1477        mem[(LOC + BIG) / SZ] = r;
   1478        assertEq(ins.exports[NM + "@big"](BigInt(LOC), s), r);
   1479        assertEq(mem[(LOC + BIG) / SZ], fn(r, s));
   1480    }
   1481 
   1482 
   1483    assertErrorMessage(() => ins.exports[NM + "@0"](BigInt(len), Zero(SZ)),
   1484                       WebAssembly.RuntimeError,
   1485                       /out of bounds/);
   1486 
   1487    assertErrorMessage(() => ins.exports[NM + "@0"](BigInt(len - (SZ - 1)), Zero(SZ)),
   1488                       WebAssembly.RuntimeError,
   1489                       /unaligned memory access/);
   1490 
   1491    if (len < HUGE) {
   1492        assertErrorMessage(() => ins.exports[NM + "@huge"](0n, Zero(SZ)),
   1493                           WebAssembly.RuntimeError,
   1494                           /out of bounds/);
   1495    }
   1496 }
   1497 
   1498 function testAtomicCmpxchg(ins, mem, LOC) {
   1499    let r = 0, s = 0;
   1500    let SZ = mem.BYTES_PER_ELEMENT;
   1501    let len = mem.length * SZ;
   1502    let NM = "cmpxchgi" + (SZ * 8);
   1503 
   1504    [r,s] = Random2(SZ);
   1505    mem[LOC / SZ] = r;
   1506    assertEq(ins.exports[NM + "@0"](BigInt(LOC), Zero(SZ), s), r);
   1507    assertEq(ins.exports[NM + "@0"](BigInt(LOC), r, s), r);
   1508    assertEq(mem[LOC / SZ], s);
   1509 
   1510    [r,s] = Random2(SZ);
   1511    mem[(LOC + SMALL) / SZ] = r;
   1512    assertEq(ins.exports[NM + "@0"](BigInt(LOC + SMALL), Zero(SZ), s), r);
   1513    assertEq(ins.exports[NM + "@0"](BigInt(LOC + SMALL), r, s), r);
   1514    assertEq(mem[(LOC + SMALL) / SZ], s);
   1515 
   1516    if (len >= LOC + BIG + SZ) {
   1517        [r,s] = Random2(SZ);
   1518        mem[(LOC + BIG) / SZ] = r;
   1519        assertEq(ins.exports[NM + "@0"](BigInt(LOC + BIG), Zero(SZ), s), r);
   1520        assertEq(ins.exports[NM + "@0"](BigInt(LOC + BIG), r, s), r);
   1521        assertEq(mem[(LOC + BIG) / SZ], s);
   1522    }
   1523 
   1524    assertErrorMessage(() => ins.exports[NM + "@0"](BigInt(len), Zero(SZ), Zero(SZ)),
   1525                       WebAssembly.RuntimeError,
   1526                       /out of bounds/);
   1527 
   1528    assertErrorMessage(() => ins.exports[NM + "@0"](BigInt(len - (SZ - 1)), Zero(SZ), Zero(SZ)),
   1529                       WebAssembly.RuntimeError,
   1530                       /unaligned memory access/);
   1531 
   1532    if (len < HUGE) {
   1533        assertErrorMessage(() => ins.exports[NM + "@huge"](0n, Zero(SZ), Zero(SZ)),
   1534                           WebAssembly.RuntimeError,
   1535                           /out of bounds/);
   1536    }
   1537 }
   1538 
   1539 function testAtomicWake(ins, mem, LOC) {
   1540    let SZ = mem.BYTES_PER_ELEMENT;
   1541    let len = mem.length * SZ;
   1542 
   1543    assertEq(ins.exports["wake@0"](BigInt(LOC)), 0);
   1544    assertEq(ins.exports["wake@small"](BigInt(LOC)), 0);
   1545    if (len >= LOC + BIG + SZ) {
   1546        assertEq(ins.exports["wake@big"](BigInt(LOC)), 0);
   1547    }
   1548 
   1549    assertErrorMessage(() => ins.exports["wake@0"](BigInt(len)),
   1550                       WebAssembly.RuntimeError,
   1551                       /out of bounds/);
   1552 
   1553    assertErrorMessage(() => ins.exports["wake@0"](BigInt(len - (SZ - 1))),
   1554                       WebAssembly.RuntimeError,
   1555                       /unaligned memory access/);
   1556 
   1557    if (len < HUGE) {
   1558        assertErrorMessage(() => ins.exports["wake@huge"](BigInt(LOC)),
   1559                           WebAssembly.RuntimeError,
   1560                           /out of bounds/);
   1561    }
   1562 }
   1563 
   1564 // Sometimes we start memory at zero to disable certain bounds checking
   1565 // optimizations.  Other times we start memory at something beyond most of
   1566 // our references to enable those optimizations.
   1567 let configs = [[40, 0, 3], [40, 3, '']];
   1568 
   1569 // On 64-bit systems, test beyond 2GB and also beyond 4GB
   1570 if (getBuildConfiguration("pointer-byte-size") == 8) {
   1571    configs.push([Math.pow(2, 31) + 40, 32771, '']);
   1572    configs.push([Math.pow(2, 32) + 40, 65539, '']);
   1573    configs.push([Math.pow(2, 31) + 40, 32771, 32773]);
   1574    configs.push([Math.pow(2, 32) + 40, 65539, 65541]);
   1575 }
   1576 
   1577 for ( let shared of ['','shared'] ) {
   1578    for (let [LOC, start, max] of configs) {
   1579        if (shared != '' && max == '') {
   1580            continue;
   1581        }
   1582        const ins = makeTest(LOC, start, max, shared);
   1583        if (max != '') {
   1584            // This can OOM legitimately; let it.
   1585            let res = Number(ins.exports.mem.grow(BigInt(max - start)));
   1586            if (res == -1) {
   1587                print("SPURIOUS OOM");
   1588                continue;
   1589            }
   1590            assertEq(res, start);
   1591        }
   1592 
   1593        const mem32 = new Int32Array(ins.exports.mem.buffer);
   1594        const mem64 = new BigInt64Array(ins.exports.mem.buffer);
   1595 
   1596        for ( let m of [mem32, mem64] ) {
   1597            testRead(ins, m, LOC, "");
   1598            testWrite(ins, m, LOC, "");
   1599            testRead(ins, m, LOC, "a");
   1600            testWrite(ins, m, LOC, "a");
   1601            testAtomicRMW(ins, m, LOC, "add", (r,s) => r+s);
   1602            testAtomicRMW(ins, m, LOC, "sub", (r,s) => r-s);
   1603            testAtomicRMW(ins, m, LOC, "and", (r,s) => r&s);
   1604            testAtomicRMW(ins, m, LOC, "or", (r,s) => r|s);
   1605            testAtomicRMW(ins, m, LOC, "xor", (r,s) => r^s);
   1606            testAtomicRMW(ins, m, LOC, "xchg", (r,s) => s);
   1607            testAtomicCmpxchg(ins, m, LOC);
   1608            testAtomicWake(ins, m, LOC);
   1609        }
   1610 
   1611        if (wasmSimdEnabled()) {
   1612            testReadV128(ins, mem32, LOC);
   1613            testWriteV128(ins, mem32, LOC);
   1614        }
   1615    }
   1616 }
   1617 
   1618 // Bulk memory operations
   1619 
   1620 function makeModule(initial, maximum, shared) {
   1621    return `
   1622 (module
   1623  (memory (export "mem") i64 ${initial} ${maximum} ${shared})
   1624 
   1625  (data $seg "0123456789")
   1626 
   1627  (func (export "size") (result i64)
   1628    memory.size)
   1629 
   1630  (func (export "grow") (param $delta i64) (result i64)
   1631    (memory.grow (local.get $delta)))
   1632 
   1633  (func (export "copy") (param $to i64) (param $from i64) (param $len i64)
   1634    (memory.copy (local.get $to) (local.get $from) (local.get $len)))
   1635 
   1636  (func (export "fill") (param $to i64) (param $val i32) (param $len i64)
   1637    (memory.fill (local.get $to) (local.get $val) (local.get $len)))
   1638 
   1639  (func (export "init") (param $to i64) (param $src i32) (param $count i32)
   1640    (memory.init $seg (local.get $to) (local.get $src) (local.get $count)))
   1641 )`;
   1642 }
   1643 
   1644 for ( let shared of ['','shared'] ) {
   1645    let ins = wasmEvalText(makeModule(1, 3, shared));
   1646 
   1647    assertEq(ins.exports.size(), 1n);
   1648 
   1649    // OOM with very low probability will result in test failure
   1650    assertEq(ins.exports.grow(2n), 1n);
   1651    assertEq(ins.exports.size(), 3n);
   1652 
   1653    // OOM with very low probability will result in test failure
   1654    assertEq(ins.exports.grow(1n), -1n);
   1655    assertEq(ins.exports.size(), 3n);
   1656 
   1657    // More than max pages
   1658    assertEq(ins.exports.grow(100000n), -1n);
   1659    assertEq(ins.exports.size(), 3n);
   1660 
   1661    // More than 2^48 pages
   1662    assertEq(ins.exports.grow(0x1_0000_0000_0000n), -1n);
   1663    assertEq(ins.exports.size(), 3n);
   1664 
   1665    // More than 2^48 pages - interpreted as unsigned
   1666    assertEq(ins.exports.grow(-1n), -1n);
   1667    assertEq(ins.exports.size(), 3n);
   1668 
   1669    var mem = new Uint8Array(ins.exports.mem.buffer);
   1670    var val = [1,2,3,4,5];
   1671    mem.set(val, 20);
   1672    ins.exports.copy(40n, 20n, 5n);
   1673    assertSame(val, mem.slice(40, 45));
   1674 
   1675    ins.exports.fill(39n, 37, 8n);
   1676    assertSame(iota(8).map(_ => 37), mem.slice(39, 47));
   1677 
   1678    ins.exports.init(128n, 1, 5);
   1679    assertSame(iota(5).map(x => x+49), mem.slice(128, 133));
   1680 }
   1681 
   1682 if (getBuildConfiguration("pointer-byte-size") == 8) {
   1683    for ( let shared of ['','shared'] ) {
   1684        let limit = wasmMaxMemoryPages('i64');
   1685        let initial = 65537;
   1686        let maximum = limit + 1;
   1687        let pagesize = 65536n;
   1688 
   1689        let ins = wasmEvalText(makeModule(initial, maximum, shared));
   1690 
   1691        assertEq(ins.exports.size(), BigInt(initial));
   1692 
   1693        // This can OOM legitimately; let it.
   1694        {
   1695            let res = ins.exports.grow(2n);
   1696            if (res == -1) {
   1697                print("SPURIOUS OOM");
   1698                continue;
   1699            }
   1700            assertEq(res, BigInt(initial));
   1701        }
   1702        assertEq(ins.exports.size(), BigInt(initial + 2));
   1703 
   1704        // This must fail
   1705        assertEq(ins.exports.grow(BigInt(limit - (initial + 2) + 1)), -1n);
   1706        assertEq(ins.exports.size(), BigInt(initial + 2));
   1707 
   1708        // This can OOM legitimately; let it.
   1709        {
   1710            let res = ins.exports.grow(BigInt(limit - (initial + 2)));
   1711            if (res == -1) {
   1712                print("SPURIOUS OOM");
   1713                continue;
   1714            }
   1715            assertEq(res, BigInt(initial + 2));
   1716        }
   1717        assertEq(ins.exports.size(), BigInt(limit));
   1718 
   1719        let mem = new Uint8Array(ins.exports.mem.buffer);
   1720 
   1721        let copyval = [1,2,3,4,5];
   1722        let source = 20n;
   1723        let target = BigInt(initial) * pagesize + 40n;
   1724        let oobTarget = BigInt(limit) * pagesize - 1n;
   1725 
   1726        // Copy from memory below 4GB to memory beyond 4GB
   1727        mem.set(copyval, Number(source));
   1728        ins.exports.copy(target, source, BigInt(copyval.length));
   1729        assertSame(copyval, mem.slice(Number(target), Number(target) + copyval.length))
   1730 
   1731        // Try to copy out of bounds
   1732        // src and target are both in bounds but len brings it oob
   1733        assertErrorMessage(() => ins.exports.copy(oobTarget, source, BigInt(copyval.length)),
   1734                           WebAssembly.RuntimeError,
   1735                           /out of bounds/);
   1736        assertEq(mem[Number(oobTarget-1n)], 0);
   1737        assertErrorMessage(() => ins.exports.copy(-1n, source, BigInt(copyval.length)),
   1738                           WebAssembly.RuntimeError,
   1739                           /out of bounds/);
   1740        assertEq(mem[Number(oobTarget-1n)], 0);
   1741 
   1742        // Fill above 4GB
   1743        ins.exports.fill(target, 37, 30n);
   1744        assertSame(iota(30).map(_ => 37), mem.slice(Number(target), Number(target) + 30));
   1745 
   1746        // Try to fill out of bounds
   1747        assertErrorMessage(() => ins.exports.fill(oobTarget, 37, 2n),
   1748                           WebAssembly.RuntimeError,
   1749                           /out of bounds/);
   1750        assertEq(mem[Number(oobTarget-1n)], 0);
   1751 
   1752        // Init above 4GB
   1753        ins.exports.init(target, 1, 5);
   1754        assertSame(iota(5).map(x => x+49), mem.slice(Number(target), Number(target)+5));
   1755 
   1756        // Try to init out of bounds
   1757        assertErrorMessage(() => ins.exports.init(oobTarget, 1, 5),
   1758                           WebAssembly.RuntimeError,
   1759                           /out of bounds/);
   1760        assertEq(mem[Number(oobTarget-1n)], 0);
   1761    }
   1762 }
   1763 
   1764 ////////////////////////////////////
   1765 // Table64 execution
   1766 
   1767 // get / set
   1768 const table64Tests = [
   1769  {
   1770    elem: "anyref",
   1771    jsval: "haha you cannot represent me",
   1772    wasmval: `(struct.new_default $s)`,
   1773  },
   1774 ];
   1775 if (WebAssembly.Function) {
   1776  table64Tests.push({
   1777    elem: "funcref",
   1778    jsval: new WebAssembly.Function({ parameters: ["i32"], results: [] }, () => {}),
   1779    wasmval: `(ref.func 0)`,
   1780  });
   1781 }
   1782 for (const test of table64Tests) {
   1783  const externalTable = new WebAssembly.Table({ address: "i64", element: test.elem, initial: 2n });
   1784  externalTable.set(0n, test.jsval);
   1785  externalTable.set(1n, null);
   1786 
   1787  const {
   1788    internalTable,
   1789    extIsNull,
   1790    swapExt,
   1791    getInternal,
   1792    getExternal,
   1793    setInternal,
   1794    setExternal,
   1795  } = wasmEvalText(`(module
   1796    (import "" "externalTable" (table $ext i64 2 ${test.elem}))
   1797 
   1798    (table $int (export "internalTable") i64 2 ${test.elem})
   1799    (elem declare func 0)
   1800    (type $s (struct))
   1801 
   1802    (func $start
   1803      (table.set $int (i64.const 0) ${test.wasmval})
   1804    )
   1805    (start $start)
   1806 
   1807    (func (export "extIsNull") (param $i i64) (result i32)
   1808      (ref.is_null (table.get $ext (local.get $i)))
   1809    )
   1810    (func (export "swapExt")
   1811      (local $tmp ${test.elem})
   1812 
   1813      (local.set $tmp (table.get $ext (i64.const 0)))
   1814      (table.set $ext (i64.const 0) (table.get $ext (i64.const 1)))
   1815      (table.set $ext (i64.const 1) (local.get $tmp))
   1816    )
   1817 
   1818    (func (export "getInternal") (param i64) (result ${test.elem})
   1819      (table.get $int (local.get 0))
   1820    )
   1821    (func (export "getExternal") (param i64) (result ${test.elem})
   1822      (table.get $ext (local.get 0))
   1823    )
   1824    (func (export "setInternal") (param i64 ${test.elem})
   1825      (table.set $int (local.get 0) (local.get 1))
   1826    )
   1827    (func (export "setExternal") (param i64 ${test.elem})
   1828      (table.set $ext (local.get 0) (local.get 1))
   1829    )
   1830  )`, { "": { externalTable } }).exports;
   1831 
   1832  assertEq(internalTable.get(0n) === null, false);
   1833  assertEq(internalTable.get(1n) === null, true);
   1834  assertEq(extIsNull(0n), 0);
   1835  assertEq(extIsNull(1n), 1);
   1836 
   1837  swapExt();
   1838  const tmp = internalTable.get(0n);
   1839  internalTable.set(0n, internalTable.get(1n));
   1840  internalTable.set(1n, tmp);
   1841 
   1842  assertEq(internalTable.get(0n) === null, true);
   1843  assertEq(internalTable.get(1n) === null, false);
   1844  assertEq(extIsNull(0n), 1);
   1845  assertEq(extIsNull(1n), 0);
   1846 
   1847  // Test bounds checks
   1848  const indexes = [
   1849    [-1n, false, TypeError],
   1850    [2n, false, RangeError],
   1851    [BigInt(0 + (MaxUint32 + 1)), false, RangeError],
   1852    [BigInt(1 + (MaxUint32 + 1)), false, RangeError],
   1853    [BigInt(2 + (MaxUint32 + 1)), false, RangeError],
   1854    [BigInt(Number.MAX_SAFE_INTEGER), false, RangeError],
   1855    [BigInt(Number.MAX_SAFE_INTEGER + 1), false, RangeError],
   1856    [2n**64n - 1n, false, RangeError],
   1857    [2n**64n, true, TypeError],
   1858  ];
   1859  for (const [index, jsOnly, jsError] of indexes) {
   1860    if (!jsOnly) {
   1861      assertErrorMessage(() => getInternal(index), WebAssembly.RuntimeError, /index out of bounds/);
   1862      assertErrorMessage(() => setInternal(index, null), WebAssembly.RuntimeError, /index out of bounds/);
   1863    }
   1864    assertErrorMessage(() => internalTable.get(index), jsError, /bad Table get address/);
   1865    assertErrorMessage(() => internalTable.set(index, null), jsError, /bad Table set address/);
   1866 
   1867    if (!jsOnly) {
   1868      assertErrorMessage(() => getExternal(index), WebAssembly.RuntimeError, /index out of bounds/);
   1869      assertErrorMessage(() => setExternal(index, null), WebAssembly.RuntimeError, /index out of bounds/);
   1870    }
   1871    assertErrorMessage(() => externalTable.get(index), jsError, /bad Table get address/);
   1872    assertErrorMessage(() => externalTable.set(index, null), jsError, /bad Table set address/);
   1873  }
   1874 }
   1875 
   1876 // call_indirect / call_ref / return_call_indirect / return_call_ref
   1877 {
   1878  const {
   1879    callIndirect,
   1880    callRef,
   1881    returnCallIndirect,
   1882    returnCallRef,
   1883  } = wasmEvalText(`(module
   1884    (table $int (export "internalTable") i64 funcref
   1885      (elem (ref.func 0) (ref.null func))
   1886    )
   1887 
   1888    (type $ft (func (param i32) (result i32)))
   1889    (func (type $ft)
   1890      (i32.add (local.get 0) (i32.const 10))
   1891    )
   1892    (func (export "callIndirect") (param i64 i32) (result i32)
   1893      (call_indirect $int (type $ft) (local.get 1) (local.get 0))
   1894    )
   1895    (func (export "callRef") (param i64 i32) (result i32)
   1896      (call_ref $ft (local.get 1) (ref.cast (ref null $ft) (table.get $int (local.get 0))))
   1897    )
   1898    (func (export "returnCallIndirect") (param i64 i32) (result i32)
   1899      (return_call_indirect $int (type $ft) (local.get 1) (local.get 0))
   1900    )
   1901    (func (export "returnCallRef") (param i64 i32) (result i32)
   1902      (return_call_ref $ft (local.get 1) (ref.cast (ref null $ft) (table.get $int (local.get 0))))
   1903    )
   1904  )`).exports;
   1905 
   1906  assertEq(callIndirect(0n, 1), 11);
   1907  assertEq(callRef(0n, 2), 12);
   1908  assertErrorMessage(() => callIndirect(1n, 1), WebAssembly.RuntimeError, /indirect call to null/);
   1909  assertErrorMessage(() => callRef(1n, 2), WebAssembly.RuntimeError, /dereferencing null pointer/);
   1910  assertEq(returnCallIndirect(0n, 3), 13);
   1911  assertEq(returnCallRef(0n, 4), 14);
   1912  assertErrorMessage(() => returnCallIndirect(1n, 3), WebAssembly.RuntimeError, /indirect call to null/);
   1913  assertErrorMessage(() => returnCallRef(1n, 4), WebAssembly.RuntimeError, /dereferencing null pointer/);
   1914 
   1915  // Test bounds checks
   1916  const indexes = [
   1917    -1,
   1918    2,
   1919    0 + (MaxUint32 + 1),
   1920    1 + (MaxUint32 + 1),
   1921    2 + (MaxUint32 + 1),
   1922    Number.MAX_SAFE_INTEGER,
   1923    Number.MAX_SAFE_INTEGER + 1,
   1924  ];
   1925  for (const index of indexes) {
   1926    assertErrorMessage(() => callIndirect(BigInt(index), 1), WebAssembly.RuntimeError, /index out of bounds/);
   1927    assertErrorMessage(() => callRef(BigInt(index), 2), WebAssembly.RuntimeError, /table index out of bounds/);
   1928    assertErrorMessage(() => returnCallIndirect(BigInt(index), 3), WebAssembly.RuntimeError, /index out of bounds/);
   1929    assertErrorMessage(() => returnCallRef(BigInt(index), 4), WebAssembly.RuntimeError, /table index out of bounds/);
   1930  }
   1931 }
   1932 
   1933 // Bulk table operations
   1934 for (const [addrType1, addrType2] of types) {
   1935  const lenType = addrType1 === "i64" && addrType2 === "i64" ? "i64" : "i32";
   1936 
   1937  const {
   1938    f1, f2, e1, e2,
   1939    callF1, callF2,
   1940    getE1, getE2,
   1941    initF1, initE1,
   1942    copyF, copyE,
   1943    growF1, growE1,
   1944    growF2, growE2,
   1945    sizeF1, sizeE1,
   1946    sizeF2, sizeE2,
   1947    fillF1WithA,
   1948    fillE1WithA,
   1949  } = wasmEvalText(`(module
   1950    (global $ea (import "" "ea") externref)
   1951    (global $eb (import "" "eb") externref)
   1952    (global $ec (import "" "ec") externref)
   1953    (global $ed (import "" "ed") externref)
   1954 
   1955    (table $f1 (export "f1") ${addrType1} 8 funcref)
   1956    (table $f2 (export "f2") ${addrType2} 8 funcref)
   1957    (table $e1 (export "e1") ${addrType1} 8 externref)
   1958    (table $e2 (export "e2") ${addrType2} 8 externref)
   1959 
   1960    (elem (table $f1) (offset ${addrType1}.const 2) funcref (ref.func $a) (ref.func $b))
   1961    (elem $fcd funcref (ref.func $c) (ref.func $d))
   1962    (elem (table $e1) (offset ${addrType1}.const 2) externref (global.get $ea) (global.get $eb))
   1963    (elem $ecd externref (global.get $ec) (global.get $ed))
   1964 
   1965    (type $ft (func (result i32)))
   1966    (func $a (result i32) i32.const 97)   ;; 'a'
   1967    (func $b (result i32) i32.const 98)   ;; 'b'
   1968    (func $c (result i32) i32.const 99)   ;; 'c'
   1969    (func $d (result i32) i32.const 100)  ;; 'd'
   1970 
   1971    (func (export "callF1") (param ${addrType1}) (result i32)
   1972      (if (ref.is_null (table.get $f1 (local.get 0)))
   1973        (then
   1974          (return (i32.const 0))
   1975        )
   1976      )
   1977 
   1978      (table.get $f1 (local.get 0))
   1979      ref.cast (ref $ft)
   1980      call_ref $ft
   1981    )
   1982    (func (export "callF2") (param ${addrType2}) (result i32)
   1983      (if (ref.is_null (table.get $f2 (local.get 0)))
   1984        (then
   1985          (return (i32.const 0))
   1986        )
   1987      )
   1988 
   1989      (table.get $f2 (local.get 0))
   1990      ref.cast (ref $ft)
   1991      call_ref $ft
   1992    )
   1993 
   1994    (func (export "getE1") (param ${addrType1}) (result externref)
   1995      (table.get $e1 (local.get 0))
   1996    )
   1997    (func (export "getE2") (param ${addrType2}) (result externref)
   1998      (table.get $e2 (local.get 0))
   1999    )
   2000 
   2001    (func (export "sizeF1") (result ${addrType1}) table.size $f1)
   2002    (func (export "sizeF2") (result ${addrType2}) table.size $f2)
   2003    (func (export "sizeE1") (result ${addrType1}) table.size $e1)
   2004    (func (export "sizeE2") (result ${addrType2}) table.size $e2)
   2005 
   2006    (func (export "initF1") (param ${addrType1} i32 i32)
   2007      (table.init $f1 $fcd (local.get 0) (local.get 1) (local.get 2))
   2008    )
   2009    (func (export "initE1") (param ${addrType1} i32 i32)
   2010      (table.init $e1 $ecd (local.get 0) (local.get 1) (local.get 2))
   2011    )
   2012 
   2013    (func (export "copyF") (param ${addrType2} ${addrType1} ${lenType})
   2014      (table.copy $f2 $f1 (local.get 0) (local.get 1) (local.get 2))
   2015    )
   2016    (func (export "copyE") (param ${addrType2} ${addrType1} ${lenType})
   2017      (table.copy $e2 $e1 (local.get 0) (local.get 1) (local.get 2))
   2018    )
   2019 
   2020    (func (export "growF1") (param ${addrType1}) (result ${addrType1})
   2021      (table.grow $f1 (ref.null func) (local.get 0))
   2022    )
   2023    (func (export "growE1") (param ${addrType1}) (result ${addrType1})
   2024      (table.grow $e1 (ref.null extern) (local.get 0))
   2025    )
   2026 
   2027    (func (export "growF2") (param ${addrType2}) (result ${addrType2})
   2028      (table.grow $f2 (ref.null func) (local.get 0))
   2029    )
   2030    (func (export "growE2") (param ${addrType2}) (result ${addrType2})
   2031      (table.grow $e2 (ref.null extern) (local.get 0))
   2032    )
   2033 
   2034    (func (export "fillF1WithA") (param ${addrType1} ${addrType1})
   2035      (table.fill $f1 (local.get 0) (ref.func $a) (local.get 1))
   2036    )
   2037    (func (export "fillE1WithA") (param ${addrType1} ${addrType1})
   2038      (table.fill $e1 (local.get 0) (global.get $ea) (local.get 1))
   2039    )
   2040  )`, {
   2041    "": {
   2042      "ea": "a",
   2043      "eb": "b",
   2044      "ec": "c",
   2045      "ed": "d",
   2046    },
   2047  }).exports;
   2048 
   2049  function addr1(n) {
   2050    return addrType1 === "i64" ? BigInt(n) : n;
   2051  }
   2052 
   2053  function addr2(n) {
   2054    return addrType2 === "i64" ? BigInt(n) : n;
   2055  }
   2056 
   2057  function len(n) {
   2058    return lenType === "i64" ? BigInt(n) : n;
   2059  }
   2060 
   2061  function tryGrow(growFunc) {
   2062    const res = growFunc();
   2063    if (res < 0) {
   2064      throw new RangeError(`failed table grow inside wasm (result ${res})`);
   2065    }
   2066  }
   2067 
   2068  function testFuncTable(table, vals) {
   2069    const actual = (table === 1 ? sizeF1 : sizeF2)();
   2070    const expected = table === 1 ? addr1(vals.length) : addr2(vals.length);
   2071    assertEq(actual, expected, `table $f${table} had wrong size`);
   2072 
   2073    for (let i = 0; i < vals.length; i++) {
   2074      const addr = table === 1 ? addr1(i) : addr2(i);
   2075      const expected = typeof vals[i] === "string" ? vals[i].charCodeAt(0) : vals[i];
   2076      const actual = (table === 1 ? callF1 : callF2)(addr);
   2077      assertEq(actual, expected, `table $e${table} index ${i}`);
   2078    }
   2079  }
   2080 
   2081  // "extern" is "extn" here to line up with "func" for aesthetic reasons.
   2082  // I will not apologize for this.
   2083  function testExtnTable(table, vals) {
   2084    const actual = (table === 1 ? sizeE1 : sizeE2)();
   2085    const expected = table === 1 ? addr1(vals.length) : addr2(vals.length);
   2086    assertEq(actual, expected, `table $e${table} had wrong size`);
   2087 
   2088    for (let i = 0; i < vals.length; i++) {
   2089      const addr = table === 1 ? addr1(i) : addr2(i);
   2090      const expected = vals[i];
   2091      const actual = (table === 1 ? getE1 : getE2)(addr);
   2092      assertEq(actual, expected, `table $e${table} index ${i}`);
   2093    }
   2094  }
   2095 
   2096  testFuncTable(1, [0, 0, "a", "b", 0, 0, 0, 0]);
   2097  testFuncTable(2, [0, 0, 0, 0, 0, 0, 0, 0]);
   2098  testExtnTable(1, [null, null, "a", "b", null, null, null, null]);
   2099  testExtnTable(2, [null, null, null, null, null, null, null, null]);
   2100 
   2101  initF1(addr1(4), 0, 2);
   2102  initE1(addr1(4), 0, 2);
   2103 
   2104  testFuncTable(1, [0, 0, "a", "b", "c", "d", 0, 0]);
   2105  testFuncTable(2, [0, 0, 0, 0, 0, 0, 0, 0]);
   2106  testExtnTable(1, [null, null, "a", "b", "c", "d", null, null]);
   2107  testExtnTable(2, [null, null, null, null, null, null, null, null]);
   2108 
   2109  copyF(addr2(4), addr1(2), len(4));
   2110  copyE(addr2(4), addr1(2), len(4));
   2111 
   2112  testFuncTable(1, [0, 0, "a", "b", "c", "d", 0, 0]);
   2113  testFuncTable(2, [0, 0, 0, 0, "a", "b", "c", "d"]);
   2114  testExtnTable(1, [null, null, "a", "b", "c", "d", null, null]);
   2115  testExtnTable(2, [null, null, null, null, "a", "b", "c", "d"]);
   2116 
   2117  tryGrow(() => growF1(addr1(4)));
   2118  tryGrow(() => growF2(addr2(4)));
   2119  tryGrow(() => growE1(addr1(4)));
   2120  tryGrow(() => growE2(addr2(4)));
   2121 
   2122  testFuncTable(1, [0, 0, "a", "b", "c", "d", 0, 0, 0, 0, 0, 0]);
   2123  testFuncTable(2, [0, 0, 0, 0, "a", "b", "c", "d", 0, 0, 0, 0]);
   2124  testExtnTable(1, [null, null, "a", "b", "c", "d", null, null, null, null, null, null]);
   2125  testExtnTable(2, [null, null, null, null, "a", "b", "c", "d", null, null, null, null]);
   2126 
   2127  fillF1WithA(addr1(8), addr1(4));
   2128  fillE1WithA(addr1(8), addr1(4));
   2129 
   2130  testFuncTable(1, [0, 0, "a", "b", "c", "d", 0, 0, "a", "a", "a", "a"]);
   2131  testFuncTable(2, [0, 0, 0, 0, "a", "b", "c", "d", 0, 0, 0, 0]);
   2132  testExtnTable(1, [null, null, "a", "b", "c", "d", null, null, "a", "a", "a", "a"]);
   2133  testExtnTable(2, [null, null, null, null, "a", "b", "c", "d", null, null, null, null]);
   2134 
   2135  // and now explode!
   2136 
   2137  if (addrType1 === "i64") {
   2138    // JS API must use BigInt for i64
   2139    assertErrorMessage(() => f1.grow(1), TypeError, /can't convert 1 to BigInt/);
   2140    assertErrorMessage(() => f1.get(0), TypeError, /can't convert 0 to BigInt/);
   2141    assertErrorMessage(() => f1.set(0, null), TypeError, /can't convert 0 to BigInt/);
   2142    assertEq(typeof f1.length, "bigint");
   2143  }
   2144 
   2145  const indexes = [
   2146    -1,
   2147    11, // length is 12, so test around the boundary
   2148    12,
   2149    13,
   2150    0 + (MaxUint32 + 1),
   2151    1 + (MaxUint32 + 1),
   2152    2 + (MaxUint32 + 1),
   2153    Number.MAX_SAFE_INTEGER,
   2154    Number.MAX_SAFE_INTEGER + 1,
   2155  ];
   2156  if (addrType1 === "i64") {
   2157    for (const i of indexes) {
   2158      assertErrorMessage(
   2159        () => fillF1WithA(addr1(i), addr1(2)),
   2160        WebAssembly.RuntimeError, /index out of bounds/,
   2161        `start index ${i}`,
   2162      );
   2163      assertErrorMessage(
   2164        () => fillE1WithA(addr1(i), addr1(2)),
   2165        WebAssembly.RuntimeError, /index out of bounds/,
   2166        `start index ${i}`,
   2167      );
   2168 
   2169      assertErrorMessage(
   2170        () => copyF(addr2(0), addr1(i), len(2)),
   2171        WebAssembly.RuntimeError, /index out of bounds/,
   2172        `src index ${i}`,
   2173      );
   2174      assertErrorMessage(
   2175        () => copyE(addr2(0), addr1(i), len(2)),
   2176        WebAssembly.RuntimeError, /index out of bounds/,
   2177        `src index ${i}`,
   2178      );
   2179 
   2180      assertErrorMessage(
   2181        () => initF1(addr1(i), 0, 2),
   2182        WebAssembly.RuntimeError, /index out of bounds/,
   2183        `dst index ${i}`,
   2184      );
   2185      assertErrorMessage(
   2186        () => initE1(addr1(i), 0, 2),
   2187        WebAssembly.RuntimeError, /index out of bounds/,
   2188        `dst index ${i}`,
   2189      );
   2190    }
   2191  }
   2192  if (addrType2 === "i64") {
   2193    for (const i of indexes) {
   2194      assertErrorMessage(
   2195        () => copyF(addr2(i), addr1(0), len(2)),
   2196        WebAssembly.RuntimeError, /index out of bounds/,
   2197        `dst index ${i}`,
   2198      );
   2199      assertErrorMessage(
   2200        () => copyE(addr2(i), addr1(0), len(2)),
   2201        WebAssembly.RuntimeError, /index out of bounds/,
   2202        `dst index ${i}`,
   2203      );
   2204    }
   2205  }
   2206 
   2207  const maxDelta = MaxTableElemsRuntime - 12;
   2208  assertEq(maxDelta % 2, 0, "maxDelta needs to be even for this test to work");
   2209 
   2210  try {
   2211    // Grow the tables up to max size using both the instruction and the JS API
   2212    tryGrow(() => growF1(addr1(maxDelta / 2)));
   2213    tryGrow(() => growF2(addr2(maxDelta / 2)));
   2214    tryGrow(() => growE1(addr1(maxDelta / 2)));
   2215    tryGrow(() => growE2(addr2(maxDelta / 2)));
   2216    f1.grow(addr1(maxDelta / 2), null);
   2217    f2.grow(addr2(maxDelta / 2), null);
   2218    e1.grow(addr1(maxDelta / 2), null);
   2219    e2.grow(addr2(maxDelta / 2), null);
   2220 
   2221    assertEq(sizeF1(), addr1(MaxTableElemsRuntime));
   2222    assertEq(sizeF2(), addr2(MaxTableElemsRuntime));
   2223    assertEq(sizeE1(), addr1(MaxTableElemsRuntime));
   2224    assertEq(sizeE2(), addr2(MaxTableElemsRuntime));
   2225 
   2226    for (const delta of indexes) {
   2227      if (addrType1 === "i64") {
   2228        console.log(delta);
   2229        assertEq(growF1(addr1(delta)), addr1(-1), `growing by ${delta}`);
   2230        assertEq(growE1(addr1(delta)), addr1(-1), `growing by ${delta}`);
   2231        assertErrorMessage(() => f1.grow(addr1(delta), null), Error, /grow/); // Loose to accept either TypeError or RangeError
   2232        assertErrorMessage(() => e1.grow(addr1(delta), null), Error, /grow/);
   2233      }
   2234      if (addrType2 === "i64") {
   2235        assertEq(growF2(addr2(delta)), addr2(-1), `growing by ${delta}`);
   2236        assertEq(growE2(addr2(delta)), addr2(-1), `growing by ${delta}`);
   2237        assertErrorMessage(() => f2.grow(addr2(delta), null), Error, /grow/);
   2238        assertErrorMessage(() => e2.grow(addr2(delta), null), Error, /grow/);
   2239      }
   2240    }
   2241 
   2242    assertEq(sizeF1(), addr1(MaxTableElemsRuntime));
   2243    assertEq(sizeF2(), addr2(MaxTableElemsRuntime));
   2244    assertEq(sizeE1(), addr1(MaxTableElemsRuntime));
   2245    assertEq(sizeE2(), addr2(MaxTableElemsRuntime));
   2246  } catch (e) {
   2247    if (e instanceof RangeError) {
   2248      // This can happen due to resource exhaustion on some platforms and is not worth
   2249      // failing the whole test over.
   2250      print("WARNING: Failed to test all cases of table grow.", e);
   2251    } else {
   2252      throw e;
   2253    }
   2254  }
   2255 }