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 }