summaryrefslogtreecommitdiffstats
path: root/js/src/jit-test/tests/wasm/float-unaligned.js
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /js/src/jit-test/tests/wasm/float-unaligned.js
parentInitial commit. (diff)
downloadfirefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz
firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--js/src/jit-test/tests/wasm/float-unaligned.js118
1 files changed, 118 insertions, 0 deletions
diff --git a/js/src/jit-test/tests/wasm/float-unaligned.js b/js/src/jit-test/tests/wasm/float-unaligned.js
new file mode 100644
index 0000000000..a22ffe5fee
--- /dev/null
+++ b/js/src/jit-test/tests/wasm/float-unaligned.js
@@ -0,0 +1,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);
+ }
+ }
+}