structs.js (23241B)
1 // |jit-test| test-also=--gc-zeal=2 2 3 // This tests a bunch of wasm struct stuff, but not i8 or i16 fields. 4 // See structs2.js for i8/i16 field tests. 5 6 var bin = wasmTextToBinary( 7 `(module 8 (func $x1 (import "m" "x1") (type $f1)) 9 (func $x2 (import "m" "x2") (type $f2)) 10 11 (table 2 funcref) 12 (elem (i32.const 0) $doit $doitagain) 13 14 ;; Type array has a mix of types 15 16 (type $f1 (func (param i32) (result i32))) 17 18 (type $point (struct 19 (field $point_x i32) 20 (field $point_y i32))) 21 22 (type $f2 (func (param f64) (result f64))) 23 24 (type $int_node (struct 25 (field $intbox_val (mut i32)) 26 (field $intbox_next (mut externref)))) 27 28 ;; Test all the types. 29 30 (type $omni (struct 31 (field $omni_i32 i32) 32 (field $omni_i32m (mut i32)) 33 (field $omni_i64 i64) 34 (field $omni_i64m (mut i64)) 35 (field $omni_f32 f32) 36 (field $omni_f32m (mut f32)) 37 (field $omni_f64 f64) 38 (field $omni_f64m (mut f64)) 39 (field $omni_externref externref) 40 (field $omni_externrefm (mut externref)))) 41 42 ;; Various ways to reference a type in the middle of the 43 ;; type array, make sure we get the right one 44 45 (func (export "hello") (param f64) (param i32) (result f64) 46 (call_indirect (type $f2) (local.get 0) (local.get 1))) 47 48 (func $doit (param f64) (result f64) 49 (f64.sqrt (local.get 0))) 50 51 (func $doitagain (param f64) (result f64) 52 (f64.mul (local.get 0) (local.get 0))) 53 54 (func (export "x1") (param i32) (result i32) 55 (call $x1 (local.get 0))) 56 57 (func (export "x2") (param f64) (result f64) 58 (call $x2 (local.get 0))) 59 60 ;; Useful for testing to ensure that the type is not type #0 here. 61 62 (func (export "mk_point") (result eqref) 63 (struct.new $point (i32.const 37) (i32.const 42))) 64 65 (func (export "mk_int_node") (param i32) (param externref) (result eqref) 66 (struct.new $int_node (local.get 0) (local.get 1))) 67 68 ;; Too big to fit in an inline WasmGcObject. 69 70 (type $bigger (struct 71 (field $a i32) 72 (field $b i32) 73 (field $c i32) 74 (field $d i32) 75 (field $e i32) 76 (field $f i32) 77 (field $g i32) 78 (field $h i32) 79 (field $i i32) 80 (field $j i32) 81 (field $k i32) 82 (field $l i32) 83 (field $m i32) 84 (field $n i32) 85 (field $o i32) 86 (field $p i32) 87 (field $q i32) 88 (field $r i32) 89 (field $s i32) 90 (field $t i32) 91 (field $u i32) 92 (field $v i32) 93 (field $w i32) 94 (field $x i32) 95 (field $y i32) 96 (field $z i32) 97 (field $aa i32) 98 (field $ab i32) 99 (field $ac i32) 100 (field $ad i32) 101 (field $ae i32) 102 (field $af i32) 103 (field $ag i32) 104 (field $ah i32) 105 (field $ai i32) 106 (field $aj i32) 107 (field $ak i32) 108 (field $al i32) 109 (field $am i32) 110 (field $an i32) 111 (field $ao i32) 112 (field $ap i32) 113 (field $aq i32) 114 (field $ar i32) 115 (field $as i32) 116 (field $at i32) 117 (field $au i32) 118 (field $av i32) 119 (field $aw i32) 120 (field $ax i32) 121 (field $ay i32) 122 (field $az i32))) 123 124 (func (export "mk_bigger") (result eqref) 125 (struct.new $bigger 126 (i32.const 0) 127 (i32.const 1) 128 (i32.const 2) 129 (i32.const 3) 130 (i32.const 4) 131 (i32.const 5) 132 (i32.const 6) 133 (i32.const 7) 134 (i32.const 8) 135 (i32.const 9) 136 (i32.const 10) 137 (i32.const 11) 138 (i32.const 12) 139 (i32.const 13) 140 (i32.const 14) 141 (i32.const 15) 142 (i32.const 16) 143 (i32.const 17) 144 (i32.const 18) 145 (i32.const 19) 146 (i32.const 20) 147 (i32.const 21) 148 (i32.const 22) 149 (i32.const 23) 150 (i32.const 24) 151 (i32.const 25) 152 (i32.const 26) 153 (i32.const 27) 154 (i32.const 28) 155 (i32.const 29) 156 (i32.const 30) 157 (i32.const 31) 158 (i32.const 32) 159 (i32.const 33) 160 (i32.const 34) 161 (i32.const 35) 162 (i32.const 36) 163 (i32.const 37) 164 (i32.const 38) 165 (i32.const 39) 166 (i32.const 40) 167 (i32.const 41) 168 (i32.const 42) 169 (i32.const 43) 170 (i32.const 44) 171 (i32.const 45) 172 (i32.const 46) 173 (i32.const 47) 174 (i32.const 48) 175 (i32.const 49) 176 (i32.const 50) 177 (i32.const 51))) 178 179 (type $withfloats (struct 180 (field $f1 f32) 181 (field $f2 f64) 182 (field $f3 externref) 183 (field $f4 f32) 184 (field $f5 i32))) 185 186 (func (export "mk_withfloats") 187 (param f32) (param f64) (param externref) (param f32) (param i32) 188 (result eqref) 189 (struct.new $withfloats (local.get 0) (local.get 1) (local.get 2) (local.get 3) (local.get 4))) 190 191 )`) 192 193 var mod = new WebAssembly.Module(bin); 194 var ins = new WebAssembly.Instance(mod, {m:{x1(x){ return x*3 }, x2(x){ return Math.PI }}}).exports; 195 196 assertEq(ins.hello(4.0, 0), 2.0) 197 assertEq(ins.hello(4.0, 1), 16.0) 198 199 assertEq(ins.x1(12), 36) 200 assertEq(ins.x2(8), Math.PI) 201 202 var point = ins.mk_point(); 203 assertEq(wasmGcReadField(point, 0), 37); 204 assertEq(wasmGcReadField(point, 1), 42); 205 206 var int_node = ins.mk_int_node(78, point); 207 assertEq(wasmGcReadField(int_node, 0), 78); 208 assertEq(wasmGcReadField(int_node, 1), point); 209 210 var bigger = ins.mk_bigger(); 211 for ( let i=0; i < 52; i++ ) 212 assertEq(wasmGcReadField(bigger, i), i); 213 214 var withfloats = ins.mk_withfloats(1/3, Math.PI, bigger, 5/6, 0x1337); 215 assertEq(wasmGcReadField(withfloats, 0), Math.fround(1/3)); 216 assertEq(wasmGcReadField(withfloats, 1), Math.PI); 217 assertEq(wasmGcReadField(withfloats, 2), bigger); 218 assertEq(wasmGcReadField(withfloats, 3), Math.fround(5/6)); 219 assertEq(wasmGcReadField(withfloats, 4), 0x1337); 220 221 // A simple stress test 222 223 var stress = wasmTextToBinary( 224 `(module 225 (type $node (struct (field i32) (field (ref null $node)))) 226 (func (export "iota1") (param $n i32) (result eqref) 227 (local $list (ref null $node)) 228 (block $exit 229 (loop $loop 230 (br_if $exit (i32.eqz (local.get $n))) 231 (local.set $list (struct.new $node (local.get $n) (local.get $list))) 232 (local.set $n (i32.sub (local.get $n) (i32.const 1))) 233 (br $loop))) 234 (local.get $list)))`); 235 var stressIns = new WebAssembly.Instance(new WebAssembly.Module(stress)).exports; 236 var stressLevel = 237 getBuildConfiguration("x64") && !getBuildConfiguration("tsan") && 238 !getBuildConfiguration("asan") && !getBuildConfiguration("valgrind") 239 ? 100000 240 : 1000; 241 var the_list = stressIns.iota1(stressLevel); 242 for (let i=1; i <= stressLevel; i++) { 243 assertEq(wasmGcReadField(the_list, 0), i); 244 the_list = wasmGcReadField(the_list, 1); 245 } 246 assertEq(the_list, null); 247 248 // Fields and their exposure in JS. We can't export types yet so hide them 249 // inside the module with globals. 250 251 // i64 fields. 252 253 { 254 let txt = 255 `(module 256 (type $big (struct 257 (field (mut i32)) 258 (field (mut i64)) 259 (field (mut i32)))) 260 261 (func (export "set") (param eqref) 262 (local (ref null $big)) 263 (local.set 1 (ref.cast (ref null $big) (local.get 0))) 264 (struct.set $big 1 (local.get 1) (i64.const 0x3333333376544567))) 265 266 (func (export "set2") (param $p eqref) 267 (struct.set $big 1 268 (ref.cast (ref null $big) (local.get $p)) 269 (i64.const 0x3141592653589793))) 270 271 (func (export "low") (param $p eqref) (result i32) 272 (i32.wrap_i64 (struct.get $big 1 (ref.cast (ref null $big) (local.get $p))))) 273 274 (func (export "high") (param $p eqref) (result i32) 275 (i32.wrap_i64 (i64.shr_u 276 (struct.get $big 1 (ref.cast (ref null $big) (local.get $p))) 277 (i64.const 32)))) 278 279 (func (export "mk") (result eqref) 280 (struct.new $big (i32.const 0x7aaaaaaa) (i64.const 0x4201020337) (i32.const 0x6bbbbbbb))) 281 282 )`; 283 284 let ins = wasmEvalText(txt).exports; 285 286 let v = ins.mk(); 287 assertEq(typeof v, "object"); 288 assertEq(wasmGcReadField(v, 0), 0x7aaaaaaa); 289 assertEq(wasmGcReadField(v, 1), 0x4201020337n); 290 assertEq(ins.low(v), 0x01020337); 291 assertEq(ins.high(v), 0x42); 292 assertEq(wasmGcReadField(v, 2), 0x6bbbbbbb); 293 294 ins.set(v); 295 assertEq(wasmGcReadField(v, 0), 0x7aaaaaaa); 296 assertEq(wasmGcReadField(v, 1), 0x3333333376544567n); 297 assertEq(wasmGcReadField(v, 2), 0x6bbbbbbb); 298 299 ins.set2(v); 300 assertEq(wasmGcReadField(v, 1), 0x3141592653589793n); 301 assertEq(ins.low(v), 0x53589793); 302 assertEq(ins.high(v), 0x31415926) 303 } 304 305 { 306 let txt = 307 `(module 308 (type $big (struct 309 (field (mut i32)) 310 (field (mut i64)) 311 (field (mut i32)))) 312 313 (global $g (mut (ref null $big)) (ref.null $big)) 314 315 (func (export "make") (result eqref) 316 (global.set $g 317 (struct.new $big (i32.const 0x7aaaaaaa) (i64.const 0x4201020337) (i32.const 0x6bbbbbbb))) 318 (global.get $g)) 319 320 (func (export "update0") (param $x i32) 321 (struct.set $big 0 (global.get $g) (local.get $x))) 322 323 (func (export "get0") (result i32) 324 (struct.get $big 0 (global.get $g))) 325 326 (func (export "update1") (param $hi i32) (param $lo i32) 327 (struct.set $big 1 (global.get $g) 328 (i64.or 329 (i64.shl (i64.extend_i32_u (local.get $hi)) (i64.const 32)) 330 (i64.extend_i32_u (local.get $lo))))) 331 332 (func (export "get1_low") (result i32) 333 (i32.wrap_i64 (struct.get $big 1 (global.get $g)))) 334 335 (func (export "get1_high") (result i32) 336 (i32.wrap_i64 337 (i64.shr_u (struct.get $big 1 (global.get $g)) (i64.const 32)))) 338 339 (func (export "update2") (param $x i32) 340 (struct.set $big 2 (global.get $g) (local.get $x))) 341 342 (func (export "get2") (result i32) 343 (struct.get $big 2 (global.get $g))) 344 345 )`; 346 347 let ins = wasmEvalText(txt).exports; 348 349 let v = ins.make(); 350 assertEq(wasmGcReadField(v, 0), 0x7aaaaaaa); 351 assertEq(wasmGcReadField(v, 1), 0x4201020337n); 352 assertEq(wasmGcReadField(v, 2), 0x6bbbbbbb); 353 354 ins.update0(0x45367101); 355 assertEq(wasmGcReadField(v, 0), 0x45367101); 356 assertEq(ins.get0(), 0x45367101); 357 assertEq(wasmGcReadField(v, 1), 0x4201020337n); 358 assertEq(wasmGcReadField(v, 2), 0x6bbbbbbb); 359 360 ins.update2(0x62345123); 361 assertEq(wasmGcReadField(v, 0), 0x45367101); 362 assertEq(wasmGcReadField(v, 1), 0x4201020337n); 363 assertEq(ins.get2(), 0x62345123); 364 assertEq(wasmGcReadField(v, 2), 0x62345123); 365 366 ins.update1(0x77777777, 0x22222222); 367 assertEq(wasmGcReadField(v, 0), 0x45367101); 368 assertEq(ins.get1_low(), 0x22222222); 369 assertEq(ins.get1_high(), 0x77777777); 370 assertEq(wasmGcReadField(v, 1), 0x7777777722222222n); 371 assertEq(wasmGcReadField(v, 2), 0x62345123); 372 } 373 374 375 var bin = wasmTextToBinary( 376 `(module 377 (type $cons (struct (field i32) (field (ref null $cons)))) 378 379 (global $g (mut (ref null $cons)) (ref.null $cons)) 380 381 (func (export "push") (param i32) 382 (global.set $g (struct.new $cons (local.get 0) (global.get $g)))) 383 384 (func (export "top") (result i32) 385 (struct.get $cons 0 (global.get $g))) 386 387 (func (export "pop") 388 (global.set $g (struct.get $cons 1 (global.get $g)))) 389 390 (func (export "is_empty") (result i32) 391 (ref.is_null (global.get $g))) 392 393 )`); 394 395 var mod = new WebAssembly.Module(bin); 396 var ins = new WebAssembly.Instance(mod).exports; 397 ins.push(37); 398 ins.push(42); 399 ins.push(86); 400 assertEq(ins.top(), 86); 401 ins.pop(); 402 assertEq(ins.top(), 42); 403 ins.pop(); 404 assertEq(ins.top(), 37); 405 ins.pop(); 406 assertEq(ins.is_empty(), 1); 407 assertErrorMessage(() => ins.pop(), 408 WebAssembly.RuntimeError, 409 /dereferencing null pointer/); 410 411 // Check that a wrapped object cannot be passed as an eqref even if the wrapper 412 // points to the right type. This is a temporary restriction, until we're able 413 // to avoid dealing with wrappers inside the engine. 414 415 { 416 var ins = wasmEvalText( 417 `(module 418 (type $Node (struct (field i32))) 419 (func (export "mk") (result eqref) 420 (struct.new $Node (i32.const 37))) 421 (func (export "f") (param $n eqref) (result eqref) 422 (ref.cast (ref null $Node) (local.get $n))))`).exports; 423 var n = ins.mk(); 424 assertEq(ins.f(n), n); 425 assertErrorMessage(() => ins.f(wrapWithProto(n, {})), TypeError, /can only pass a WebAssembly GC object/); 426 } 427 428 // Field names. 429 430 // Test that names map to the right fields. 431 432 { 433 let ins = new WebAssembly.Instance(new WebAssembly.Module(wasmTextToBinary( 434 `(module 435 (type $s (struct 436 (field $x i32) 437 (field $y i32))) 438 439 (func $f (param $p (ref null $s)) (result i32) 440 (struct.get $s $x (local.get $p))) 441 442 (func $g (param $p (ref null $s)) (result i32) 443 (struct.get $s $y (local.get $p))) 444 445 (func (export "testf") (param $n i32) (result i32) 446 (call $f (struct.new $s (local.get $n) (i32.mul (local.get $n) (i32.const 2))))) 447 448 (func (export "testg") (param $n i32) (result i32) 449 (call $g (struct.new $s (local.get $n) (i32.mul (local.get $n) (i32.const 2))))) 450 451 )`))).exports; 452 453 assertEq(ins.testf(10), 10); 454 assertEq(ins.testg(10), 20); 455 } 456 457 // negative tests 458 459 // Wrong type passed as initializer 460 461 assertErrorMessage(() => new WebAssembly.Module(wasmTextToBinary(` 462 (module 463 (type $r (struct (field i32))) 464 (func $f (param f64) (result eqref) 465 (struct.new $r (local.get 0))) 466 )`)), 467 WebAssembly.CompileError, /type mismatch/); 468 469 // Too few values passed for initializer 470 471 assertErrorMessage(() => new WebAssembly.Module(wasmTextToBinary(` 472 (module 473 (type $r (struct (field i32) (field i32))) 474 (func $f (result eqref) 475 (struct.new $r (i32.const 0))) 476 )`)), 477 WebAssembly.CompileError, /popping value from empty stack/); 478 479 // Too many values passed for initializer, sort of 480 481 assertErrorMessage(() => new WebAssembly.Module(wasmTextToBinary(` 482 (module 483 (type $r (struct (field i32) (field i32))) 484 (func $f (result eqref) 485 (i32.const 0) 486 (i32.const 1) 487 (i32.const 2) 488 struct.new $r) 489 )`)), 490 WebAssembly.CompileError, /unused values/); 491 492 // Not referencing a structure type 493 494 assertErrorMessage(() => new WebAssembly.Module(wasmTextToBinary(` 495 (module 496 (type (func (param i32) (result i32))) 497 (func $f (result eqref) 498 (struct.new 0)) 499 )`)), 500 WebAssembly.CompileError, /not a struct type/); 501 502 // Nominal type equivalence for structs, but the prefix rule allows this 503 // conversion to succeed. 504 505 wasmEvalText(` 506 (module 507 (type $p (struct (field i32))) 508 (type $q (struct (field i32))) 509 (func $f (result (ref null $p)) 510 (struct.new $q (i32.const 0)))) 511 `); 512 513 // The field name is optional, so this should work. 514 515 wasmEvalText(` 516 (module 517 (type $s (struct (field i32)))) 518 `) 519 520 // Empty structs are OK. 521 522 wasmEvalText(` 523 (module 524 (type $s (struct))) 525 `) 526 527 // Multiply defined structures. 528 529 assertErrorMessage(() => wasmEvalText(` 530 (module 531 (type $s (struct (field $x i32))) 532 (type $s (struct (field $y i32)))) 533 `), 534 SyntaxError, /duplicate type identifier/); 535 536 // Bogus type definition syntax. 537 538 assertErrorMessage(() => wasmEvalText(` 539 (module 540 (type $s)) 541 `), 542 SyntaxError, /wasm text error/); 543 544 assertErrorMessage(() => wasmEvalText(` 545 (module 546 (type $s (field $x i32))) 547 `), 548 SyntaxError, /expected one of: `func`, `struct`, `array`/); 549 550 assertErrorMessage(() => wasmEvalText(` 551 (module 552 (type $s (struct (field $x i31)))) 553 `), 554 SyntaxError, /wasm text error/); 555 556 assertErrorMessage(() => wasmEvalText(` 557 (module 558 (type $s (struct (fjeld $x i32)))) 559 `), 560 SyntaxError, /wasm text error/); 561 562 assertErrorMessage(() => wasmEvalText(` 563 (module 564 (type $s (struct abracadabra))) 565 `), 566 SyntaxError, /wasm text error/); 567 568 // Function should not reference struct type: syntactic test 569 570 assertErrorMessage(() => wasmEvalText(` 571 (module 572 (type $s (struct)) 573 (type $f (func (param i32) (result i32))) 574 (func (type 0) (param i32) (result i32) (unreachable))) 575 `), 576 SyntaxError, /not a function type/); 577 578 // Can't set immutable fields from JS 579 580 { 581 let ins = wasmEvalText( 582 `(module 583 (type $s (struct 584 (field i32) 585 (field (mut i64)))) 586 (func (export "make") (result eqref) 587 (struct.new $s (i32.const 37) (i64.const 42))))`).exports; 588 let v = ins.make(); 589 assertErrorMessage(() => v[0] = 12, TypeError, /can't modify/); 590 assertErrorMessage(() => v[1] = 12, TypeError, /can't modify/); 591 } 592 593 // Function should not reference struct type: binary test 594 595 var bad = new Uint8Array([0x00, 0x61, 0x73, 0x6d, 596 0x01, 0x00, 0x00, 0x00, 597 598 0x01, // Type section 599 0x03, // Section size 600 0x01, // One type 601 0x5f, // Struct 602 0x00, // Zero fields 603 604 0x03, // Function section 605 0x02, // Section size 606 0x01, // One function 607 0x00, // Type of function 608 609 0x0a, // Code section 610 0x05, // Section size 611 0x01, // One body 612 0x03, // Body size 613 0x00, // Zero locals 614 0x00, // UNREACHABLE 615 0x0b]); // END 616 617 assertErrorMessage(() => new WebAssembly.Module(bad), 618 WebAssembly.CompileError, /signature index references non-signature/); 619 620 // Exercise alias-analysis code for struct access 621 { 622 let txt = 623 `(module 624 (type $meh (struct)) 625 (type $hasOOL (struct 626 ;; In-line storage 627 (field i64) (field i64) 628 (field $ILnonref (mut i64)) (field $ILref (mut eqref)) 629 (field i64) (field i64) (field i64) (field i64) 630 (field i64) (field i64) (field i64) (field i64) 631 (field i64) (field i64) (field i64) (field i64) 632 ;; Out-of-line storage (or maybe it starts earlier, but 633 ;; definitely not after this point). 634 (field $OOLnonref (mut i64)) (field $OOLref (mut eqref))) 635 ) 636 (func (export "create") (result eqref) 637 (struct.new $hasOOL 638 (i64.const 1) (i64.const 2) 639 (i64.const 9876) (ref.null $meh) 640 (i64.const 3) (i64.const 4) (i64.const 5) (i64.const 6) 641 (i64.const 7) (i64.const 8) (i64.const 9) (i64.const 10) 642 (i64.const 11) (i64.const 12) (i64.const 13) (i64.const 14) 643 (i64.const 4321) (ref.null $meh)) 644 ) 645 ;; Write to an OOL field, then an IL field, then to an OOL field, so 646 ;; that we can at least check (from inspection of the optimised MIR) 647 ;; that the GVN+alias analysis causes the OOL block pointer not to be 648 ;; reloaded for the second OOL write. First for non-ref fields .. 649 (func (export "threeSetsNonReffy") (param eqref) 650 (local (ref $hasOOL)) 651 (local.set 1 (ref.as_non_null (ref.cast (ref null $hasOOL) (local.get 0)))) 652 (struct.set $hasOOL 16 (local.get 1) (i64.const 1337)) ;; set $OOLnonref 653 (struct.set $hasOOL 2 (local.get 1) (i64.const 7331)) ;; set $ILnonref 654 (struct.set $hasOOL 16 (local.get 1) (i64.const 9009)) ;; set $OOLnonref 655 ) 656 ;; and the same for ref fields. 657 (func (export "threeSetsReffy") (param eqref) 658 (local (ref $hasOOL)) 659 (local.set 1 (ref.as_non_null (ref.cast (ref null $hasOOL) (local.get 0)))) 660 (struct.set $hasOOL 17 (local.get 1) (ref.null $meh)) ;; set $OOLref 661 (struct.set $hasOOL 3 (local.get 1) (ref.null $meh)) ;; set $ILref 662 (struct.set $hasOOL 17 (local.get 1) (ref.null $meh)) ;; set $OOLref 663 ) 664 )`; 665 let exports = wasmEvalText(txt).exports; 666 } 667 668 // Exercise stack maps and GC 669 { 670 // Zeal will cause us to allocate structs via instance call, requiring live registers 671 // to be spilled, and then GC values traced while on the stack. 672 gczeal(2, 1); 673 674 { 675 const { test } = wasmEvalText(`(module 676 (type $s (struct (field i32))) 677 (func (export "test") (param i32) (result i32) 678 local.get 0 679 (struct.new $s (i32.const 234)) 680 (struct.new $s (i32.const 345)) 681 drop 682 drop 683 ) 684 )`).exports; 685 assertEq(test(123), 123); 686 } 687 { 688 const { test } = wasmEvalText(`(module 689 (type $s (struct (field f32))) 690 (func (export "test") (param f32) (result f32) 691 local.get 0 692 (struct.new $s (f32.const 234)) 693 (struct.new $s (f32.const 345)) 694 drop 695 drop 696 ) 697 )`).exports; 698 assertEq(test(123), 123); 699 } 700 701 gczeal(0); 702 }