summaryrefslogtreecommitdiffstats
path: root/js/src/jit-test/tests/asm.js/testSIMD-load-store.js
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/jit-test/tests/asm.js/testSIMD-load-store.js')
-rw-r--r--js/src/jit-test/tests/asm.js/testSIMD-load-store.js457
1 files changed, 457 insertions, 0 deletions
diff --git a/js/src/jit-test/tests/asm.js/testSIMD-load-store.js b/js/src/jit-test/tests/asm.js/testSIMD-load-store.js
new file mode 100644
index 000000000..d826c106b
--- /dev/null
+++ b/js/src/jit-test/tests/asm.js/testSIMD-load-store.js
@@ -0,0 +1,457 @@
+// |jit-test|
+load(libdir + "asm.js");
+load(libdir + "simd.js");
+load(libdir + "asserts.js");
+
+// Avoid pathological --ion-eager compile times due to bails in loops
+setJitCompilerOption('ion.warmup.trigger', 1000000);
+
+// Set to true to see more JS debugging spew
+const DEBUG = false;
+
+if (!isSimdAvailable() || typeof SIMD === 'undefined' || !isAsmJSCompilationAvailable()) {
+ DEBUG && print("won't run tests as simd extensions aren't activated yet");
+ quit(0);
+}
+
+const RuntimeError = WebAssembly.RuntimeError;
+
+const INT32_MAX = Math.pow(2, 31) - 1;
+const INT32_MIN = INT32_MAX + 1 | 0;
+
+try {
+
+// Load / Store
+var IMPORTS = USE_ASM + 'var H=new glob.Uint8Array(heap); var i4=glob.SIMD.Int32x4; var ci4=i4.check; var load=i4.load; var store=i4.store;';
+
+// Bad number of args
+assertAsmTypeFail('glob', 'ffi', 'heap', IMPORTS + "function f(){load();} return f");
+assertAsmTypeFail('glob', 'ffi', 'heap', IMPORTS + "function f(){load(3);} return f");
+assertAsmTypeFail('glob', 'ffi', 'heap', IMPORTS + "function f(){load(3, 4, 5);} return f");
+
+// Bad type of args
+assertAsmTypeFail('glob', 'ffi', 'heap', IMPORTS + "function f(){load(3, 5);} return f");
+assertAsmTypeFail('glob', 'ffi', 'heap', IMPORTS + "function f(){load(H, 5.0);} return f");
+assertAsmTypeFail('glob', 'ffi', 'heap', IMPORTS + "function f(){var i=0.;load(H, i);} return f");
+assertAsmTypeFail('glob', 'ffi', 'heap', IMPORTS + "var H2=new glob.Int32Array(heap); function f(){var i=0;load(H2, i)} return f");
+assertAsmTypeFail('glob', 'ffi', 'heap', IMPORTS + "var H2=42; function f(){var i=0;load(H2, i)} return f");
+assertAsmTypeFail('glob', 'ffi', 'heap', IMPORTS + "function f(){var i=0;load(H2, i)} return f");
+assertAsmTypeFail('glob', 'ffi', 'heap', IMPORTS + "var f4=glob.SIMD.Float32x4; function f(){var i=0;var vec=f4(1,2,3,4); store(H, i, vec)} return f");
+
+// Bad coercions of returned values
+assertAsmTypeFail('glob', 'ffi', 'heap', IMPORTS + "function f(){var i=0;return load(H, i)|0;} return f");
+assertAsmTypeFail('glob', 'ffi', 'heap', IMPORTS + "function f(){var i=0;return +load(H, i);} return f");
+
+// Literal index constants
+var buf = new ArrayBuffer(BUF_MIN);
+var SIZE_TA = BUF_MIN >> 2
+var asI32 = new Int32Array(buf);
+asI32[SIZE_TA - 4] = 4;
+asI32[SIZE_TA - 3] = 3;
+asI32[SIZE_TA - 2] = 2;
+asI32[SIZE_TA - 1] = 1;
+
+assertAsmTypeFail('glob', 'ffi', 'heap', IMPORTS + "function f(){load(H, -1);} return f");
+assertAsmTypeFail('glob', 'ffi', 'heap', IMPORTS + "function f(){load(H, " + (INT32_MAX + 1) + ");} return f");
+assertAsmTypeFail('glob', 'ffi', 'heap', IMPORTS + "function f(){load(H, " + (INT32_MAX + 1 - 15) + ");} return f");
+asmCompile('glob', 'ffi', 'heap', IMPORTS + "function f(){load(H, " + (INT32_MAX + 1 - 16) + ");} return f");
+
+assertAsmLinkFail(asmCompile('glob', 'ffi', 'heap', IMPORTS + "function f() {return ci4(load(H, " + (BUF_MIN - 15) + "));} return f"), this, {}, buf);
+assertEqX4(asmLink(asmCompile('glob', 'ffi', 'heap', IMPORTS + "function f() {return ci4(load(H, " + (BUF_MIN - 16) + "));} return f"), this, {}, buf)(), [4, 3, 2, 1]);
+assertEqX4(asmLink(asmCompile('glob', 'ffi', 'heap', IMPORTS + "function f() {return ci4(load(H, " + BUF_MIN + " - 16 | 0));} return f"), this, {}, buf)(), [4, 3, 2, 1]);
+
+var CONSTANT_INDEX = 42;
+var CONSTANT_BYTE_INDEX = CONSTANT_INDEX << 2;
+
+var loadStoreCode = `
+ "use asm";
+
+ var H = new glob.Uint8Array(heap);
+
+ var i4 = glob.SIMD.Int32x4;
+ var i4load = i4.load;
+ var i4store = i4.store;
+ var ci4 = i4.check;
+
+ var f4 = glob.SIMD.Float32x4;
+ var f4load = f4.load;
+ var f4store = f4.store;
+ var cf4 = f4.check;
+
+ function f32l(i) { i=i|0; return cf4(f4load(H, i|0)); }
+ function f32lcst() { return cf4(f4load(H, ${CONSTANT_BYTE_INDEX})); }
+ function f32s(i, vec) { i=i|0; vec=cf4(vec); f4store(H, i|0, vec); }
+ function f32scst(vec) { vec=cf4(vec); f4store(H, ${CONSTANT_BYTE_INDEX}, vec); }
+
+ function i32l(i) { i=i|0; return ci4(i4load(H, i|0)); }
+ function i32lcst() { return ci4(i4load(H, ${CONSTANT_BYTE_INDEX})); }
+ function i32s(i, vec) { i=i|0; vec=ci4(vec); i4store(H, i|0, vec); }
+ function i32scst(vec) { vec=ci4(vec); i4store(H, ${CONSTANT_BYTE_INDEX}, vec); }
+
+ function f32lbndcheck(i) {
+ i=i|0;
+ if ((i|0) > ${CONSTANT_BYTE_INDEX}) i=${CONSTANT_BYTE_INDEX};
+ if ((i|0) < 0) i = 0;
+ return cf4(f4load(H, i|0));
+ }
+ function f32sbndcheck(i, vec) {
+ i=i|0;
+ vec=cf4(vec);
+ if ((i|0) > ${CONSTANT_BYTE_INDEX}) i=${CONSTANT_BYTE_INDEX};
+ if ((i|0) < 0) i = 0;
+ return cf4(f4store(H, i|0, vec));
+ }
+
+ return {
+ f32l: f32l,
+ f32lcst: f32lcst,
+ f32s: f32s,
+ f32scst: f32scst,
+ f32lbndcheck: f32lbndcheck,
+ f32sbndcheck: f32sbndcheck,
+ i32l: i32l,
+ i32lcst: i32lcst,
+ i32s: i32s,
+ i32scst: i32scst
+ }
+`;
+
+const SIZE = 0x8000;
+
+var F32 = new Float32Array(SIZE);
+var reset = function() {
+ for (var i = 0; i < SIZE; i++)
+ F32[i] = i + 1;
+};
+reset();
+
+var buf = F32.buffer;
+var m = asmLink(asmCompile('glob', 'ffi', 'heap', loadStoreCode), this, null, buf);
+
+function slice(TA, i, n) { return Array.prototype.slice.call(TA, i, i + n); }
+
+// Float32x4.load
+function f32l(n) { return m.f32l((n|0) << 2 | 0); };
+
+// Correct accesses
+assertEqX4(f32l(0), slice(F32, 0, 4));
+assertEqX4(f32l(1), slice(F32, 1, 4));
+assertEqX4(f32l(SIZE - 4), slice(F32, SIZE - 4, 4));
+
+assertEqX4(m.f32lcst(), slice(F32, CONSTANT_INDEX, 4));
+assertEqX4(m.f32lbndcheck(CONSTANT_BYTE_INDEX), slice(F32, CONSTANT_INDEX, 4));
+
+// OOB
+assertThrowsInstanceOf(() => f32l(-1), RuntimeError);
+assertThrowsInstanceOf(() => f32l(SIZE), RuntimeError);
+assertThrowsInstanceOf(() => f32l(SIZE - 1), RuntimeError);
+assertThrowsInstanceOf(() => f32l(SIZE - 2), RuntimeError);
+assertThrowsInstanceOf(() => f32l(SIZE - 3), RuntimeError);
+
+var code = `
+ "use asm";
+ var f4 = glob.SIMD.Float32x4;
+ var f4l = f4.load;
+ var u8 = new glob.Uint8Array(heap);
+
+ function g(x) {
+ x = x|0;
+ // set a constraint on the size of the heap
+ var ptr = 0;
+ ptr = u8[0xFFFF] | 0;
+ // give a precise range to x
+ x = (x>>0) > 5 ? 5 : x;
+ x = (x>>0) < 0 ? 0 : x;
+ // ptr value gets a precise range but the bounds check shouldn't get
+ // eliminated.
+ return f4l(u8, 0xFFFA + x | 0);
+ }
+
+ return g;
+`;
+assertThrowsInstanceOf(() => asmLink(asmCompile('glob', 'ffi', 'heap', code), this, {}, new ArrayBuffer(0x10000))(0), RuntimeError);
+
+// Float32x4.store
+function f32s(n, v) { return m.f32s((n|0) << 2 | 0, v); };
+
+var vec = SIMD.Float32x4(5,6,7,8);
+var vec2 = SIMD.Float32x4(0,1,2,3);
+var vecWithNaN = SIMD.Float32x4(NaN, 2, NaN, 4);
+
+reset();
+f32s(0, vec);
+assertEqX4(vec, slice(F32, 0, 4));
+
+reset();
+f32s(0, vec2);
+assertEqX4(vec2, slice(F32, 0, 4));
+
+reset();
+f32s(4, vec);
+assertEqX4(vec, slice(F32, 4, 4));
+
+reset();
+f32s(4, vecWithNaN);
+assertEqX4(vecWithNaN, slice(F32, 4, 4));
+
+reset();
+m.f32scst(vec2);
+assertEqX4(vec2, slice(F32, CONSTANT_INDEX, 4));
+
+reset();
+m.f32sbndcheck(CONSTANT_BYTE_INDEX, vec);
+assertEqX4(vec, slice(F32, CONSTANT_INDEX, 4));
+
+// OOB
+reset();
+assertThrowsInstanceOf(() => f32s(SIZE - 3, vec), RuntimeError);
+assertThrowsInstanceOf(() => f32s(SIZE - 2, vec), RuntimeError);
+assertThrowsInstanceOf(() => f32s(SIZE - 1, vec), RuntimeError);
+assertThrowsInstanceOf(() => f32s(SIZE, vec), RuntimeError);
+for (var i = 0; i < SIZE; i++)
+ assertEq(F32[i], i + 1);
+
+// Int32x4.load
+var I32 = new Int32Array(buf);
+reset = function () {
+ for (var i = 0; i < SIZE; i++)
+ I32[i] = i + 1;
+};
+reset();
+
+function i32(n) { return m.i32l((n|0) << 2 | 0); };
+
+// Correct accesses
+assertEqX4(i32(0), slice(I32, 0, 4));
+assertEqX4(i32(1), slice(I32, 1, 4));
+assertEqX4(i32(SIZE - 4), slice(I32, SIZE - 4, 4));
+
+assertEqX4(m.i32lcst(), slice(I32, CONSTANT_INDEX, 4));
+
+// OOB
+assertThrowsInstanceOf(() => i32(-1), RuntimeError);
+assertThrowsInstanceOf(() => i32(SIZE), RuntimeError);
+assertThrowsInstanceOf(() => i32(SIZE - 1), RuntimeError);
+assertThrowsInstanceOf(() => i32(SIZE - 2), RuntimeError);
+assertThrowsInstanceOf(() => i32(SIZE - 3), RuntimeError);
+
+// Int32x4.store
+function i32s(n, v) { return m.i32s((n|0) << 2 | 0, v); };
+
+var vec = SIMD.Int32x4(5,6,7,8);
+var vec2 = SIMD.Int32x4(0,1,2,3);
+
+reset();
+i32s(0, vec);
+assertEqX4(vec, slice(I32, 0, 4));
+
+reset();
+i32s(0, vec2);
+assertEqX4(vec2, slice(I32, 0, 4));
+
+reset();
+i32s(4, vec);
+assertEqX4(vec, slice(I32, 4, 4));
+
+reset();
+m.i32scst(vec2);
+assertEqX4(vec2, slice(I32, CONSTANT_INDEX, 4));
+
+// OOB
+reset();
+assertThrowsInstanceOf(() => i32s(SIZE - 3, vec), RuntimeError);
+assertThrowsInstanceOf(() => i32s(SIZE - 2, vec), RuntimeError);
+assertThrowsInstanceOf(() => i32s(SIZE - 1, vec), RuntimeError);
+assertThrowsInstanceOf(() => i32s(SIZE - 0, vec), RuntimeError);
+for (var i = 0; i < SIZE; i++)
+ assertEq(I32[i], i + 1);
+
+// Partial loads and stores
+(function() {
+
+// Variable indexes
+function MakeCodeFor(typeName) {
+ return `
+ "use asm";
+ var type = glob.SIMD.${typeName};
+ var c = type.check;
+
+ var l1 = type.load1;
+ var l2 = type.load2;
+
+ var s1 = type.store1;
+ var s2 = type.store2;
+
+ var u8 = new glob.Uint8Array(heap);
+
+ function load1(i) { i=i|0; return l1(u8, i); }
+ function load2(i) { i=i|0; return l2(u8, i); }
+
+ function loadCst1() { return l1(u8, 41 << 2); }
+ function loadCst2() { return l2(u8, 41 << 2); }
+
+ function store1(i, x) { i=i|0; x=c(x); return s1(u8, i, x); }
+ function store2(i, x) { i=i|0; x=c(x); return s2(u8, i, x); }
+
+ function storeCst1(x) { x=c(x); return s1(u8, 41 << 2, x); }
+ function storeCst2(x) { x=c(x); return s2(u8, 41 << 2, x); }
+
+ return {
+ load1: load1,
+ load2: load2,
+ loadCst1: loadCst1,
+ loadCst2: loadCst2,
+ store1: store1,
+ store2: store2,
+ storeCst1: storeCst1,
+ storeCst2: storeCst2,
+ }
+`;
+}
+
+var SIZE = 0x10000;
+
+function TestPartialLoads(m, typedArray, x, y, z, w) {
+ // Fill array with predictable values
+ for (var i = 0; i < SIZE; i += 4) {
+ typedArray[i] = x(i);
+ typedArray[i + 1] = y(i);
+ typedArray[i + 2] = z(i);
+ typedArray[i + 3] = w(i);
+ }
+
+ // Test correct loads
+ var i = 0, j = 0; // i in elems, j in bytes
+ assertEqX4(m.load1(j), [x(i), 0, 0, 0]);
+ assertEqX4(m.load2(j), [x(i), y(i), 0, 0]);
+
+ j += 4;
+ assertEqX4(m.load1(j), [y(i), 0, 0, 0]);
+ assertEqX4(m.load2(j), [y(i), z(i), 0, 0]);
+
+ j += 4;
+ assertEqX4(m.load1(j), [z(i), 0, 0, 0]);
+ assertEqX4(m.load2(j), [z(i), w(i), 0, 0]);
+
+ j += 4;
+ assertEqX4(m.load1(j), [w(i), 0, 0, 0]);
+ assertEqX4(m.load2(j), [w(i), x(i+4), 0, 0]);
+
+ j += 4;
+ i += 4;
+ assertEqX4(m.load1(j), [x(i), 0, 0, 0]);
+ assertEqX4(m.load2(j), [x(i), y(i), 0, 0]);
+
+ // Test loads with constant indexes (41)
+ assertEqX4(m.loadCst1(), [y(40), 0, 0, 0]);
+ assertEqX4(m.loadCst2(), [y(40), z(40), 0, 0]);
+
+ // Test limit and OOB accesses
+ assertEqX4(m.load1((SIZE - 1) << 2), [w(SIZE - 4), 0, 0, 0]);
+ assertThrowsInstanceOf(() => m.load1(((SIZE - 1) << 2) + 1), RuntimeError);
+
+ assertEqX4(m.load2((SIZE - 2) << 2), [z(SIZE - 4), w(SIZE - 4), 0, 0]);
+ assertThrowsInstanceOf(() => m.load2(((SIZE - 2) << 2) + 1), RuntimeError);
+}
+
+// Partial stores
+function TestPartialStores(m, typedArray, typeName, x, y, z, w) {
+ var val = SIMD[typeName](x, y, z, w);
+
+ function Reset() {
+ for (var i = 0; i < SIZE; i++)
+ typedArray[i] = i + 1;
+ }
+ function CheckNotModified(low, high) {
+ for (var i = low; i < high; i++)
+ assertEq(typedArray[i], i + 1);
+ }
+
+ function TestStore1(i) {
+ m.store1(i, val);
+ CheckNotModified(0, i >> 2);
+ assertEq(typedArray[i >> 2], x);
+ CheckNotModified((i >> 2) + 1, SIZE);
+ typedArray[i >> 2] = (i >> 2) + 1;
+ }
+
+ function TestStore2(i) {
+ m.store2(i, val);
+ CheckNotModified(0, i >> 2);
+ assertEq(typedArray[i >> 2], x);
+ assertEq(typedArray[(i >> 2) + 1], y);
+ CheckNotModified((i >> 2) + 2, SIZE);
+ typedArray[i >> 2] = (i >> 2) + 1;
+ typedArray[(i >> 2) + 1] = (i >> 2) + 2;
+ }
+
+ function TestOOBStore(f) {
+ assertThrowsInstanceOf(f, RuntimeError);
+ CheckNotModified(0, SIZE);
+ }
+
+ Reset();
+
+ TestStore1(0);
+ TestStore1(1 << 2);
+ TestStore1(2 << 2);
+ TestStore1(3 << 2);
+ TestStore1(1337 << 2);
+
+ var i = (SIZE - 1) << 2;
+ TestStore1(i);
+ TestOOBStore(() => m.store1(i + 1, val));
+ TestOOBStore(() => m.store1(-1, val));
+
+ TestStore2(0);
+ TestStore2(1 << 2);
+ TestStore2(2 << 2);
+ TestStore2(3 << 2);
+ TestStore2(1337 << 2);
+
+ var i = (SIZE - 2) << 2;
+ TestStore2(i);
+ TestOOBStore(() => m.store2(i + 1, val));
+ TestOOBStore(() => m.store2(-1, val));
+
+ // Constant indexes (41)
+ m.storeCst1(val);
+ CheckNotModified(0, 41);
+ assertEq(typedArray[41], x);
+ CheckNotModified(42, SIZE);
+ typedArray[41] = 42;
+
+ m.storeCst2(val);
+ CheckNotModified(0, 41);
+ assertEq(typedArray[41], x);
+ assertEq(typedArray[42], y);
+ CheckNotModified(43, SIZE);
+ typedArray[41] = 42;
+ typedArray[42] = 43;
+}
+
+var f32 = new Float32Array(SIZE);
+var mFloat32x4 = asmLink(asmCompile('glob', 'ffi', 'heap', MakeCodeFor('Float32x4')), this, null, f32.buffer);
+
+TestPartialLoads(mFloat32x4, f32,
+ (i) => i + 1,
+ (i) => Math.fround(13.37),
+ (i) => Math.fround(1/i),
+ (i) => Math.fround(Math.sqrt(0x2000 - i)));
+
+TestPartialStores(mFloat32x4, f32, 'Float32x4', 42, -0, NaN, 0.1337);
+
+var i32 = new Int32Array(f32.buffer);
+var mInt32x4 = asmLink(asmCompile('glob', 'ffi', 'heap', MakeCodeFor('Int32x4')), this, null, i32.buffer);
+
+TestPartialLoads(mInt32x4, i32,
+ (i) => i + 1 | 0,
+ (i) => -i | 0,
+ (i) => i * 2 | 0,
+ (i) => 42);
+
+TestPartialStores(mInt32x4, i32, 'Int32x4', 42, -3, 13, 37);
+
+})();
+
+} catch (e) { print('stack: ', e.stack); throw e }