float-unaligned.js (4655B)
1 // Various tests for unaligned float accesses. These are specifically meant to 2 // test the SIGBUS handling on 32-bit ARM by exercising odd addresses and odd 3 // offsets. 4 5 // For a triple of (numBallast, ty, offset), create the text for a pair of 6 // functions "get_ty_offset" and "set_ty_offset" where each has numBallast live 7 // dummy values across the operation of interest to force the use of different 8 // register numbers. (This is primarily for the FP registers as ARM code 9 // generation currently always uses the same scratch register for the base 10 // address of the access.) 11 // 12 // These must be augmented with a memory. Memory addresses 0-255 are reserved 13 // for internal use by these functions. The memory must start as zero. 14 15 function makeLoadStore(numBallast, ty, offset) { 16 // The general idea of the ballast is that we occupy some FP registers and 17 // some int registers with non-dead values before we perform an operation, 18 // and then we consume the occupied registers after. 19 // 20 // In the case of load, the loaded result is stored back in memory before we 21 // consume the ballast, thus the ion regalloc will not simply always load 22 // the result into d0, but usually into some temp other than d0. Thus the 23 // amount of ballast affects the register. (Ditto baseline though the 24 // reasoning is simpler.) 25 // 26 // In the case of store, we keep the parameter value live until the end so 27 // that the tmp that we compute for the store is moved into a different 28 // register. The tmp has the same value as the parameter value but a 29 // non-JIT compiler can't know that. 30 31 let loadtxt = 32 `(func (export "get_${ty}_${offset}") (param $p i32) (result ${ty}) 33 ${ballast(() => ` 34 (i32.const 8) 35 (i32.store (i32.const 8) (i32.add (i32.load (i32.const 8)) (i32.const 1))) 36 (${ty}.load (i32.const 8))`)} 37 38 (${ty}.store (i32.const 0) (${ty}.load offset=${offset} (local.get $p))) 39 40 ${ballast(() => ` 41 ${ty}.store`)} 42 43 (${ty}.load (i32.const 0)))`; 44 45 // This will assume the value at mem[16] is zero. 46 let storetxt = 47 `(func (export "set_${ty}_${offset}") (param $p i32) (param $v ${ty}) 48 (local $tmp ${ty}) 49 ${ballast(() => ` 50 (i32.const 8) 51 (i32.store (i32.const 8) (i32.add (i32.load (i32.const 8)) (i32.const 1))) 52 (${ty}.load (i32.const 8))`)} 53 54 (local.set $tmp (${ty}.add (local.get $v) (${ty}.load (i32.const 16)))) 55 (${ty}.store offset=${offset} (local.get $p) (local.get $tmp)) 56 57 ${ballast(() => ` 58 ${ty}.store`)} 59 (${ty}.store (i32.const 8) (local.get $v)))`; 60 61 return `${loadtxt} 62 ${storetxt}`; 63 64 function ballast(thunk) { 65 let s = ""; 66 for ( let i=0 ; i < numBallast; i++ ) 67 s += thunk(); 68 return s; 69 } 70 } 71 72 // The complexity here comes from trying to force the source/target FP registers 73 // in the FP access instruction to vary. For Baseline this is not hard; for Ion 74 // trickier. 75 76 function makeInstance(numBallast, offset) { 77 let txt = 78 `(module 79 (memory (export "memory") 1 1) 80 ${makeLoadStore(numBallast, 'f64', offset)} 81 ${makeLoadStore(numBallast, 'f32', offset)})`; 82 return new WebAssembly.Instance(new WebAssembly.Module(wasmTextToBinary(txt))); 83 } 84 85 // `offset` corresponds to the "offset" directive in the instruction 86 for ( let offset=0 ; offset < 8; offset++ ) { 87 88 // `numBallast` represents the amount of ballast registers we're trying to use, 89 // see comments above. 90 for ( let numBallast=0; numBallast < 16; numBallast++ ) { 91 let ins = makeInstance(numBallast, offset); 92 let mem = ins.exports.memory; 93 let buf = new DataView(mem.buffer); 94 95 // `i` represents the offset in the pointer from a proper boundary 96 for ( let i=0; i < 9; i++ ) { 97 let offs = 256+i; 98 let val = Math.PI+i; 99 100 buf.setFloat64(offs + offset, val, true); 101 assertEq(ins.exports["get_f64_" + offset](offs), val); 102 103 ins.exports["set_f64_" + offset](offs + 32, val); 104 assertEq(buf.getFloat64(offs + 32 + offset, true), val); 105 } 106 107 for ( let i=0; i < 9; i++ ) { 108 let offs = 512+i; 109 let val = Math.fround(Math.PI+i); 110 111 buf.setFloat32(offs + offset, val, true); 112 assertEq(ins.exports["get_f32_" + offset](offs), val); 113 114 ins.exports["set_f32_" + offset](offs + 32, val); 115 assertEq(buf.getFloat32(offs + 32 + offset, true), val); 116 } 117 } 118 }