bce.js (6155B)
1 mem='\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f'+ 2 '\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff'+ 3 '\x00'.repeat(65488) + 4 '\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff' 5 6 let accessWidth = { 7 '8_s': 1, 8 '8_u': 1, 9 '16_s': 2, 10 '16_u': 2, 11 '': 4, 12 'f32': 4, 13 'f64': 8, 14 } 15 16 let baseOp = { 17 '8_s': 'i32', 18 '8_u': 'i32', 19 '16_s': 'i32', 20 '16_u': 'i32', 21 '': 'i32', 22 'f32': 'f32', 23 'f64': 'f64', 24 } 25 26 function toSigned(width, num) { 27 let unsignedMax = Math.pow(2, accessWidth[width] * 8) - 1; 28 let signedMax = Math.pow(2, accessWidth[width] * 8 - 1) - 1; 29 30 return (num <= signedMax ? num : -(unsignedMax + 1 - num)); 31 } 32 33 function fromLittleEndianNum(width, bytes) { 34 let base = 1; 35 var res = 0; 36 for (var i = 0; i < accessWidth[width]; i++) { 37 res += base * bytes[i]; 38 base *= 256; 39 } 40 return res; 41 } 42 43 function getInt(width, offset, mem) { 44 var bytes = [ ]; 45 for (var i = offset; i < offset + accessWidth[width]; i++) { 46 if (i < mem.length) 47 bytes.push(mem.charCodeAt(i)); 48 else 49 bytes.push(0); 50 } 51 52 var res = fromLittleEndianNum(width, bytes); 53 if (width == '8_s' || width == '16_s' || width == '') 54 res = toSigned(width, res); 55 return res; 56 } 57 58 function loadTwiceModule(type, ext, offset, align) { 59 // TODO: Generate memory from byte string 60 return wasmEvalText( 61 `(module 62 (memory 1) 63 (data (i32.const 0) "\\00\\01\\02\\03\\04\\05\\06\\07\\08\\09\\0a\\0b\\0c\\0d\\0e\\0f") 64 (data (i32.const 16) "\\f0\\f1\\f2\\f3\\f4\\f5\\f6\\f7\\f8\\f9\\fa\\fb\\fc\\fd\\fe\\ff") 65 (data (i32.const 65520) "\\f0\\f1\\f2\\f3\\f4\\f5\\f6\\f7\\f8\\f9\\fa\\fb\\fc\\fd\\fe\\ff") 66 (func (param i32) (param i32) (result ${type}) 67 (drop (${type}.load${ext} 68 offset=${offset} 69 ${align != 0 ? 'align=' + align : ''} 70 (local.get 0) 71 )) 72 (${type}.load${ext} 73 offset=${offset} 74 ${align != 0 ? 'align=' + align : ''} 75 (local.get 1) 76 ) 77 ) (export "" (func 0)))` 78 ).exports[""]; 79 } 80 81 function loadTwiceSameBasePlusConstModule(type, ext, offset, align, addConst) { 82 return wasmEvalText( 83 `(module 84 (memory 1) 85 (data (i32.const 0) "\\00\\01\\02\\03\\04\\05\\06\\07\\08\\09\\0a\\0b\\0c\\0d\\0e\\0f") 86 (data (i32.const 16) "\\f0\\f1\\f2\\f3\\f4\\f5\\f6\\f7\\f8\\f9\\fa\\fb\\fc\\fd\\fe\\ff") 87 (data (i32.const 65520) "\\f0\\f1\\f2\\f3\\f4\\f5\\f6\\f7\\f8\\f9\\fa\\fb\\fc\\fd\\fe\\ff") 88 (func (param i32) (result ${type}) 89 (drop (${type}.load${ext} 90 offset=${offset} 91 ${align != 0 ? 'align=' + align : ''} 92 (local.get 0) 93 )) 94 (${type}.load${ext} 95 offset=${offset} 96 ${align != 0 ? 'align=' + align : ''} 97 (i32.add (local.get 0) (i32.const ${addConst})) 98 ) 99 ) (export "" (func 0)))` 100 ).exports[""]; 101 } 102 103 function loadTwiceSameBasePlusNonConstModule(type, ext, offset, align) { 104 return wasmEvalText( 105 `(module 106 (memory 1) 107 (data (i32.const 0) "\\00\\01\\02\\03\\04\\05\\06\\07\\08\\09\\0a\\0b\\0c\\0d\\0e\\0f") 108 (data (i32.const 16) "\\f0\\f1\\f2\\f3\\f4\\f5\\f6\\f7\\f8\\f9\\fa\\fb\\fc\\fd\\fe\\ff") 109 (data (i32.const 65520) "\\f0\\f1\\f2\\f3\\f4\\f5\\f6\\f7\\f8\\f9\\fa\\fb\\fc\\fd\\fe\\ff") 110 (func (param i32) (param i32) (result ${type}) 111 (drop (${type}.load${ext} 112 offset=${offset} 113 ${align != 0 ? 'align=' + align : ''} 114 (local.get 0) 115 )) 116 (${type}.load${ext} 117 offset=${offset} 118 ${align != 0 ? 'align=' + align : ''} 119 (i32.add (local.get 0) (local.get 1)) 120 ) 121 ) (export "" (func 0)))` 122 ).exports[""]; 123 } 124 125 /* 126 * On x64 falsely removed bounds checks will be masked by the signal handlers. 127 * Thus it is important that these tests be run on x86. 128 */ 129 130 function testOOB(mod, args) { 131 assertErrorMessage(() => mod(...args), WebAssembly.RuntimeError, /index out of bounds/); 132 } 133 134 function testOk(mod, args, expected, expectedType) { 135 assertEq(mod(...args), expected); 136 } 137 138 // TODO: It would be nice to verify how many BCs are eliminated on positive tests. 139 140 const align = 0; 141 for (let offset of [0, 1, 2, 3, 4, 8, 16, 41, 0xfff8]) { 142 143 var widths = ['8_s', '8_u', '16_s', '16_u', ''] 144 145 for (let width of widths) { 146 // Accesses of 1 byte. 147 let lastValidIndex = 0x10000 - offset - accessWidth[width]; 148 let op = baseOp[width]; 149 150 var mod = loadTwiceModule(op, width, offset, align); 151 152 // Two consecutive loads from two different bases 153 testOk(mod, [lastValidIndex, lastValidIndex], getInt(width, lastValidIndex + offset, mem), op); 154 testOOB(mod, [lastValidIndex + 42, lastValidIndex + 42]); 155 testOOB(mod, [lastValidIndex, lastValidIndex + 42]); 156 157 mod = loadTwiceSameBasePlusConstModule(op, width, offset, align, 1); 158 159 testOk(mod, [lastValidIndex-1], getInt(width, lastValidIndex + offset, mem), op); 160 testOOB(mod, [lastValidIndex]); 161 162 // Two consecutive loads from same base with different offsets 163 mod = loadTwiceSameBasePlusConstModule(op, width, offset, align, 2); 164 165 testOk(mod, [lastValidIndex-2], getInt(width, lastValidIndex + offset, mem), op); 166 testOOB(mod, [lastValidIndex-1, 2]); 167 168 mod = loadTwiceSameBasePlusConstModule(op, width, offset, align, lastValidIndex); 169 170 testOk(mod, [0], getInt(width, lastValidIndex + offset, mem), op); 171 testOOB(mod, [1]); 172 173 mod = loadTwiceSameBasePlusNonConstModule(op, width, offset, align); 174 testOk(mod, [0, 1], getInt(width, 1 + offset, mem), op); 175 testOk(mod, [0, lastValidIndex], getInt(width, lastValidIndex + offset, mem), op); 176 testOOB(mod, [1, lastValidIndex]) 177 178 // TODO: All of the above with mixed loads and stores 179 180 // TODO: Branching - what do we want? 181 182 // TODO: Just loops 183 // - loop invariant checks 184 // - loop dependant checks remaining inbounds 185 // - loop dependant checks going out-of bounds. 186 // 187 // TODO: Loops + branching 188 // - loop invariant checks guarded by a loop invariant branch? 189 } 190 }