summaryrefslogtreecommitdiffstats
path: root/js/src/jit-test/tests/wasm/ref-types/stackmaps2.js
blob: 986d378a753a7f6330a2bd03fd1ead9171a1d8fc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
// Tests wasm frame tracing in the presence of interrupt handlers that perform
// allocation.  The structure is
//
//   test top level: call fn2
//   fn2: call fn1
//   fn1: repeat { call-direct fn0; call-indirect fn0; }
//   fn0: a 100-iteration loop that does nothing except waste time
//
// At the same time we are asynchronously runnning handler(), which does a lot
// of allocation.  At some point that will trigger a GC.  Assuming that
// handler() runs whilst fn0 is running (the most likely scenario, since fn0
// consumes the majority of the wasm running time), then the runtime will walk
// the stack from the wasm exit frame, through fn0, fn1 and finally fn2.  As
// with stackmaps1.js, there are some ref-typed args in use so as provide
// traceable stack slots to follow.
//
// The test runs until the loop in fn1 determines that handler() has allocated
// sufficient memory as to have caused at least three collections.  This helps
// keep the test effective in the face of wide variations in the rate of
// progress of the handler()'s loop (eg x86+native is fast, arm64+simulator is
// slow).

const {Module,Instance} = WebAssembly;

const DEBUG = false;

let t =
  `(module
     (type $typeOfFn0
           (func (param i32) (param externref) (param i32)
                 (param externref) (param externref) (param i32) (result i32)))

     (import "" "alloc" (func $alloc (result externref)))
     (import "" "quitp" (func $quitp (result i32)))
     (import "" "check3" (func $check3 (param externref) (param externref) (param externref)))

     (table 1 1 funcref)
     (elem (i32.const 0) $fn0)

     ;; -- fn 0
     (func $fn0 (export "fn0")
                 (param $arg1 i32) (param $arg2 externref) (param $arg3 i32)
                 (param $arg4 externref) (param $arg5 externref) (param $arg6 i32) (result i32)
       (local $i i32)

       ;; spinloop to waste time
       (loop
         (local.set $i (i32.add (local.get $i) (i32.const 1)))
         (br_if 0 (i32.lt_s (local.get $i) (i32.const 100)))
       )

       (i32.add (i32.add (local.get $arg1) (local.get $arg3)) (local.get $arg6))

       ;; Poke the ref-typed arguments, to be sure that they got kept alive
       ;; properly across any GC that might have happened.
       (call $check3 (local.get $arg2) (local.get $arg4) (local.get $arg5))
     )

     ;; -- fn 1
     (func $fn1 (export "fn1") (param $arg1 externref) (result i32)
       (loop (result i32)
         ;; call direct to $fn0
         (call $fn0 (i32.const 10) (local.get $arg1) (i32.const 12)
                    (local.get $arg1) (local.get $arg1) (i32.const 15))

         ;; call indirect to table index 0, which is $fn0
         (call_indirect (type $typeOfFn0)
                    (i32.const 10) (local.get $arg1) (i32.const 12)
                    (local.get $arg1) (local.get $arg1) (i32.const 15)
                    (i32.const 0)) ;; table index

         i32.add

         ;; Continue iterating until handler() has allocated enough
         (br_if 0 (i32.eq (call $quitp) (i32.const 0)))
       )
     )

     ;; -- fn 2
     (func $fn2 (export "fn2") (param $arg1 externref) (result i32)
       (call $fn1 (local.get $arg1))
     )
   )`;

function Croissant(chocolate, number) {
    this.chocolate = chocolate;
    this.number = number;
}

function allocates() {
    return new Croissant(true, 271828);
}

let totAllocs = 0;

function handler() {
    if (DEBUG) {
        print('XXXXXXXX icallback: START');
    }
    let q = allocates();
    let sum = 0;
    let iters = 15000;
    for (let i = 0; i < iters; i++) {
        let x = allocates();
        // Without this hoop jumping to create an apparent use of |x|, Ion
        // will remove the allocation call and make the test pointless.
        if (x == q) { sum++; }
    }
    totAllocs += iters;
    // Artificial use of |sum|.  See comment above.
    if (sum == 133713371337) { print("unlikely!"); }
    timeout(0.5, handler);
    if (DEBUG) {
        print('XXXXXXXX icallback: END');
    }
    return true;
}

function quitp() {
    return totAllocs > 200000 ? 1 : 0;
}

function check3(a1, a2, a3) {
    assertEq(a1.number, 31415927);
    assertEq(a2.number, 31415927);
    assertEq(a3.number, 31415927);
}

let i = wasmEvalText(t, {"":{alloc: allocates, quitp: quitp, check3: check3}});

timeout(0.5, handler);
print(i.exports.fn2( new Croissant(false, 31415927) ));