summaryrefslogtreecommitdiffstats
path: root/js/src/jit-test/tests/wasm/float-unaligned.js
blob: a22ffe5feecff0a17589a18921c83b4d09fe6b84 (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
// Various tests for unaligned float accesses.  These are specifically meant to
// test the SIGBUS handling on 32-bit ARM by exercising odd addresses and odd
// offsets.

// For a triple of (numBallast, ty, offset), create the text for a pair of
// functions "get_ty_offset" and "set_ty_offset" where each has numBallast live
// dummy values across the operation of interest to force the use of different
// register numbers.  (This is primarily for the FP registers as ARM code
// generation currently always uses the same scratch register for the base
// address of the access.)
//
// These must be augmented with a memory.  Memory addresses 0-255 are reserved
// for internal use by these functions.  The memory must start as zero.

function makeLoadStore(numBallast, ty, offset) {
    // The general idea of the ballast is that we occupy some FP registers and
    // some int registers with non-dead values before we perform an operation,
    // and then we consume the occupied registers after.
    //
    // In the case of load, the loaded result is stored back in memory before we
    // consume the ballast, thus the ion regalloc will not simply always load
    // the result into d0, but usually into some temp other than d0.  Thus the
    // amount of ballast affects the register.  (Ditto baseline though the
    // reasoning is simpler.)
    //
    // In the case of store, we keep the parameter value live until the end so
    // that the tmp that we compute for the store is moved into a different
    // register.  The tmp has the same value as the parameter value but a
    // non-JIT compiler can't know that.

    let loadtxt =
      `(func (export "get_${ty}_${offset}") (param $p i32) (result ${ty})
         ${ballast(() => `
              (i32.const 8)
              (i32.store (i32.const 8) (i32.add (i32.load (i32.const 8)) (i32.const 1)))
              (${ty}.load (i32.const 8))`)}

         (${ty}.store (i32.const 0) (${ty}.load offset=${offset} (local.get $p)))

         ${ballast(() => `
             ${ty}.store`)}

         (${ty}.load (i32.const 0)))`;

    // This will assume the value at mem[16] is zero.
    let storetxt =
      `(func (export "set_${ty}_${offset}") (param $p i32) (param $v ${ty})
         (local $tmp ${ty})
         ${ballast(() => `
              (i32.const 8)
              (i32.store (i32.const 8) (i32.add (i32.load (i32.const 8)) (i32.const 1)))
              (${ty}.load (i32.const 8))`)}

         (local.set $tmp (${ty}.add (local.get $v) (${ty}.load (i32.const 16))))
         (${ty}.store offset=${offset} (local.get $p) (local.get $tmp))

         ${ballast(() => `
             ${ty}.store`)}
         (${ty}.store (i32.const 8) (local.get $v)))`;

    return `${loadtxt}
            ${storetxt}`;

    function ballast(thunk) {
        let s = "";
        for ( let i=0 ; i < numBallast; i++ )
            s += thunk();
        return s;
    }
}

// The complexity here comes from trying to force the source/target FP registers
// in the FP access instruction to vary.  For Baseline this is not hard; for Ion
// trickier.

function makeInstance(numBallast, offset) {
    let txt =
        `(module
           (memory (export "memory") 1 1)
           ${makeLoadStore(numBallast, 'f64', offset)}
           ${makeLoadStore(numBallast, 'f32', offset)})`;
    return new WebAssembly.Instance(new WebAssembly.Module(wasmTextToBinary(txt)));
}

// `offset` corresponds to the "offset" directive in the instruction
for ( let offset=0 ; offset < 8; offset++ ) {

    // `numBallast` represents the amount of ballast registers we're trying to use,
    // see comments above.
    for ( let numBallast=0; numBallast < 16; numBallast++ ) {
        let ins = makeInstance(numBallast, offset);
        let mem = ins.exports.memory;
        let buf = new DataView(mem.buffer);

        // `i` represents the offset in the pointer from a proper boundary
        for ( let i=0; i < 9; i++ ) {
	    let offs = 256+i;
            let val = Math.PI+i;

            buf.setFloat64(offs + offset, val, true);
            assertEq(ins.exports["get_f64_" + offset](offs), val);

            ins.exports["set_f64_" + offset](offs + 32, val);
            assertEq(buf.getFloat64(offs + 32 + offset, true), val);
        }

        for ( let i=0; i < 9; i++ ) {
            let offs = 512+i;
            let val = Math.fround(Math.PI+i);

            buf.setFloat32(offs + offset, val, true);
            assertEq(ins.exports["get_f32_" + offset](offs), val);

            ins.exports["set_f32_" + offset](offs + 32, val);
            assertEq(buf.getFloat32(offs + 32 + offset, true), val);
        }
    }
}