diff options
Diffstat (limited to 'js/src/jit-test/tests/wasm/bce.js')
-rw-r--r-- | js/src/jit-test/tests/wasm/bce.js | 190 |
1 files changed, 190 insertions, 0 deletions
diff --git a/js/src/jit-test/tests/wasm/bce.js b/js/src/jit-test/tests/wasm/bce.js new file mode 100644 index 0000000000..0097ad7f54 --- /dev/null +++ b/js/src/jit-test/tests/wasm/bce.js @@ -0,0 +1,190 @@ +mem='\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f'+ + '\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff'+ + '\x00'.repeat(65488) + + '\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff' + +let accessWidth = { + '8_s': 1, + '8_u': 1, + '16_s': 2, + '16_u': 2, + '': 4, + 'f32': 4, + 'f64': 8, +} + +let baseOp = { + '8_s': 'i32', + '8_u': 'i32', + '16_s': 'i32', + '16_u': 'i32', + '': 'i32', + 'f32': 'f32', + 'f64': 'f64', +} + +function toSigned(width, num) { + let unsignedMax = Math.pow(2, accessWidth[width] * 8) - 1; + let signedMax = Math.pow(2, accessWidth[width] * 8 - 1) - 1; + + return (num <= signedMax ? num : -(unsignedMax + 1 - num)); +} + +function fromLittleEndianNum(width, bytes) { + let base = 1; + var res = 0; + for (var i = 0; i < accessWidth[width]; i++) { + res += base * bytes[i]; + base *= 256; + } + return res; +} + +function getInt(width, offset, mem) { + var bytes = [ ]; + for (var i = offset; i < offset + accessWidth[width]; i++) { + if (i < mem.length) + bytes.push(mem.charCodeAt(i)); + else + bytes.push(0); + } + + var res = fromLittleEndianNum(width, bytes); + if (width == '8_s' || width == '16_s' || width == '') + res = toSigned(width, res); + return res; +} + +function loadTwiceModule(type, ext, offset, align) { + // TODO: Generate memory from byte string + return wasmEvalText( + `(module + (memory 1) + (data (i32.const 0) "\\00\\01\\02\\03\\04\\05\\06\\07\\08\\09\\0a\\0b\\0c\\0d\\0e\\0f") + (data (i32.const 16) "\\f0\\f1\\f2\\f3\\f4\\f5\\f6\\f7\\f8\\f9\\fa\\fb\\fc\\fd\\fe\\ff") + (data (i32.const 65520) "\\f0\\f1\\f2\\f3\\f4\\f5\\f6\\f7\\f8\\f9\\fa\\fb\\fc\\fd\\fe\\ff") + (func (param i32) (param i32) (result ${type}) + (drop (${type}.load${ext} + offset=${offset} + ${align != 0 ? 'align=' + align : ''} + (local.get 0) + )) + (${type}.load${ext} + offset=${offset} + ${align != 0 ? 'align=' + align : ''} + (local.get 1) + ) + ) (export "" (func 0)))` + ).exports[""]; +} + +function loadTwiceSameBasePlusConstModule(type, ext, offset, align, addConst) { + return wasmEvalText( + `(module + (memory 1) + (data (i32.const 0) "\\00\\01\\02\\03\\04\\05\\06\\07\\08\\09\\0a\\0b\\0c\\0d\\0e\\0f") + (data (i32.const 16) "\\f0\\f1\\f2\\f3\\f4\\f5\\f6\\f7\\f8\\f9\\fa\\fb\\fc\\fd\\fe\\ff") + (data (i32.const 65520) "\\f0\\f1\\f2\\f3\\f4\\f5\\f6\\f7\\f8\\f9\\fa\\fb\\fc\\fd\\fe\\ff") + (func (param i32) (result ${type}) + (drop (${type}.load${ext} + offset=${offset} + ${align != 0 ? 'align=' + align : ''} + (local.get 0) + )) + (${type}.load${ext} + offset=${offset} + ${align != 0 ? 'align=' + align : ''} + (i32.add (local.get 0) (i32.const ${addConst})) + ) + ) (export "" (func 0)))` + ).exports[""]; +} + +function loadTwiceSameBasePlusNonConstModule(type, ext, offset, align) { + return wasmEvalText( + `(module + (memory 1) + (data (i32.const 0) "\\00\\01\\02\\03\\04\\05\\06\\07\\08\\09\\0a\\0b\\0c\\0d\\0e\\0f") + (data (i32.const 16) "\\f0\\f1\\f2\\f3\\f4\\f5\\f6\\f7\\f8\\f9\\fa\\fb\\fc\\fd\\fe\\ff") + (data (i32.const 65520) "\\f0\\f1\\f2\\f3\\f4\\f5\\f6\\f7\\f8\\f9\\fa\\fb\\fc\\fd\\fe\\ff") + (func (param i32) (param i32) (result ${type}) + (drop (${type}.load${ext} + offset=${offset} + ${align != 0 ? 'align=' + align : ''} + (local.get 0) + )) + (${type}.load${ext} + offset=${offset} + ${align != 0 ? 'align=' + align : ''} + (i32.add (local.get 0) (local.get 1)) + ) + ) (export "" (func 0)))` + ).exports[""]; +} + +/* + * On x64 falsely removed bounds checks will be masked by the signal handlers. + * Thus it is important that these tests be run on x86. + */ + +function testOOB(mod, args) { + assertErrorMessage(() => mod(...args), WebAssembly.RuntimeError, /index out of bounds/); +} + +function testOk(mod, args, expected, expectedType) { + assertEq(mod(...args), expected); +} + +// TODO: It would be nice to verify how many BCs are eliminated on positive tests. + +const align = 0; +for (let offset of [0, 1, 2, 3, 4, 8, 16, 41, 0xfff8]) { + + var widths = ['8_s', '8_u', '16_s', '16_u', ''] + + for (let width of widths) { + // Accesses of 1 byte. + let lastValidIndex = 0x10000 - offset - accessWidth[width]; + let op = baseOp[width]; + + var mod = loadTwiceModule(op, width, offset, align); + + // Two consecutive loads from two different bases + testOk(mod, [lastValidIndex, lastValidIndex], getInt(width, lastValidIndex + offset, mem), op); + testOOB(mod, [lastValidIndex + 42, lastValidIndex + 42]); + testOOB(mod, [lastValidIndex, lastValidIndex + 42]); + + mod = loadTwiceSameBasePlusConstModule(op, width, offset, align, 1); + + testOk(mod, [lastValidIndex-1], getInt(width, lastValidIndex + offset, mem), op); + testOOB(mod, [lastValidIndex]); + + // Two consecutive loads from same base with different offsets + mod = loadTwiceSameBasePlusConstModule(op, width, offset, align, 2); + + testOk(mod, [lastValidIndex-2], getInt(width, lastValidIndex + offset, mem), op); + testOOB(mod, [lastValidIndex-1, 2]); + + mod = loadTwiceSameBasePlusConstModule(op, width, offset, align, lastValidIndex); + + testOk(mod, [0], getInt(width, lastValidIndex + offset, mem), op); + testOOB(mod, [1]); + + mod = loadTwiceSameBasePlusNonConstModule(op, width, offset, align); + testOk(mod, [0, 1], getInt(width, 1 + offset, mem), op); + testOk(mod, [0, lastValidIndex], getInt(width, lastValidIndex + offset, mem), op); + testOOB(mod, [1, lastValidIndex]) + + // TODO: All of the above with mixed loads and stores + + // TODO: Branching - what do we want? + + // TODO: Just loops + // - loop invariant checks + // - loop dependant checks remaining inbounds + // - loop dependant checks going out-of bounds. + // + // TODO: Loops + branching + // - loop invariant checks guarded by a loop invariant branch? + } +} |