binop-arm64-ion-codegen.js (12978B)
1 // |jit-test| skip-if: !hasDisassembler() || wasmCompileMode() != "ion" || !getBuildConfiguration("arm64"); include:codegen-arm64-test.js 2 3 // Basic constant folding tests 4 5 for ( [op, lhs, rhs, expect] of 6 [['add', 5, 8, 'mov x0, #0xd'], 7 ['sub', 4, 5, 'mov x0, #0xffffffffffffffff'], 8 ['mul', 8, 3, 'mov x0, #0x18'], 9 ['div_s', -8, 3, 'mov x0, #0xfffffffffffffffe'], 10 ['div_u', 8, 3, 'mov x0, #0x2'], 11 ['rem_s', 8, 5, 'mov x0, #0x3'], 12 ['rem_u', -7, 4, 'mov x0, #0x1'], 13 ['and', 0xfe, 0x77, 'mov x0, #0x76'], 14 ['or', 0xfe, 0x77, 'mov x0, #0xff'], 15 ['xor', 0xfe, 0x77, 'mov x0, #0x89'], 16 ['shl', 3, 4, 'mov x0, #0x30'], 17 ['shr_s', -8, 1, 'mov x0, #0xfffffffffffffffc'], 18 ['shr_u', -8, 1, 'mov x0, #0x7ffffffffffffffc']] ) { 19 codegenTestARM64_adhoc(` 20 (module 21 (func (export "f") (result i64) 22 (i64.${op} (i64.const ${lhs}) (i64.const ${rhs}))))`, 23 'f', 24 expect); 25 } 26 27 // Basic tests that addition and multiplication identities are collapsed, use 28 // arg 1 here to force an explicit move to be emitted. 29 30 for ( [op, args, expect] of 31 [['add', '(local.get 1) (i64.const 0)', 'mov x0, x1'], 32 ['add', '(i64.const 0) (local.get 1)', 'mov x0, x1'], 33 ['mul', '(local.get 1) (i64.const 1)', 'mov x0, x1'], 34 ['mul', '(i64.const 1) (local.get 1)', 'mov x0, x1']] ) { 35 codegenTestARM64_adhoc(` 36 (module 37 (func (export "f") (param i64) (param i64) (result i64) 38 (i64.${op} ${args})))`, 39 'f', 40 expect); 41 } 42 43 // Test that multiplication by -1 yields negation. 44 45 let neg32 = 46 `(module 47 (func (export "f") (param i32) (result i32) 48 (i32.mul (local.get 0) (i32.const -1))))`; 49 codegenTestARM64_adhoc( 50 neg32, 51 'f', 52 'neg w0, w0'); 53 assertEq(wasmEvalText(neg32).exports.f(-37), 37) 54 assertEq(wasmEvalText(neg32).exports.f(42), -42) 55 56 let neg64 = `(module 57 (func (export "f") (param i64) (result i64) 58 (i64.mul (local.get 0) (i64.const -1))))` 59 codegenTestARM64_adhoc( 60 neg64, 61 'f', 62 'neg x0, x0'); 63 assertEq(wasmEvalText(neg64).exports.f(-37000000000n), 37000000000n) 64 assertEq(wasmEvalText(neg64).exports.f(42000000000n), -42000000000n) 65 66 // Test that multiplication by zero yields zero 67 68 let zero32 = 69 `(module 70 (func (export "f") (param i32) (result i32) 71 (i32.mul (local.get 0) (i32.const 0))))`; 72 codegenTestARM64_adhoc( 73 zero32, 74 'f', 75 'mov w0, wzr'); 76 assertEq(wasmEvalText(zero32).exports.f(-37), 0) 77 assertEq(wasmEvalText(zero32).exports.f(42), 0) 78 79 let zero64 = `(module 80 (func (export "f") (param i64) (result i64) 81 (i64.mul (local.get 0) (i64.const 0))))` 82 codegenTestARM64_adhoc( 83 zero64, 84 'f', 85 'mov x0, xzr'); 86 assertEq(wasmEvalText(zero64).exports.f(-37000000000n), 0n) 87 assertEq(wasmEvalText(zero64).exports.f(42000000000n), 0n) 88 89 // Test that multiplication by one yields no code (this optimization currently 90 // exists both in constant folding and in lowering). 91 92 let one32 = 93 `(module 94 (func (export "f") (param i32) (result i32) 95 (i32.mul (local.get 0) (i32.const 1))))`; 96 codegenTestARM64_adhoc( 97 one32, 98 'f', 99 ''); 100 assertEq(wasmEvalText(one32).exports.f(-37), -37) 101 assertEq(wasmEvalText(one32).exports.f(42), 42) 102 103 let one64 = `(module 104 (func (export "f") (param i64) (result i64) 105 (i64.mul (local.get 0) (i64.const 1))))` 106 codegenTestARM64_adhoc( 107 one64, 108 'f', 109 ''); 110 assertEq(wasmEvalText(one64).exports.f(-37000000000n), -37000000000n) 111 assertEq(wasmEvalText(one64).exports.f(42000000000n), 42000000000n) 112 113 // Test that multiplication by two yields an add 114 115 let double32 = 116 `(module 117 (func (export "f") (param i32) (result i32) 118 (i32.mul (local.get 0) (i32.const 2))))`; 119 codegenTestARM64_adhoc( 120 double32, 121 'f', 122 'add w0, w0, w0'); 123 assertEq(wasmEvalText(double32).exports.f(-37), -74) 124 assertEq(wasmEvalText(double32).exports.f(42), 84) 125 126 let double64 = `(module 127 (func (export "f") (param i64) (result i64) 128 (i64.mul (local.get 0) (i64.const 2))))` 129 codegenTestARM64_adhoc( 130 double64, 131 'f', 132 'add x0, x0, x0'); 133 assertEq(wasmEvalText(double64).exports.f(-37000000000n), -74000000000n) 134 assertEq(wasmEvalText(double64).exports.f(42000000000n), 84000000000n) 135 136 // Test that multiplication by four yields a shift 137 138 let quad32 = 139 `(module 140 (func (export "f") (param i32) (result i32) 141 (i32.mul (local.get 0) (i32.const 4))))`; 142 codegenTestARM64_adhoc( 143 quad32, 144 'f', 145 'lsl w0, w0, #2'); 146 assertEq(wasmEvalText(quad32).exports.f(-37), -148) 147 assertEq(wasmEvalText(quad32).exports.f(42), 168) 148 149 let quad64 = `(module 150 (func (export "f") (param i64) (result i64) 151 (i64.mul (local.get 0) (i64.const 4))))` 152 codegenTestARM64_adhoc( 153 quad64, 154 'f', 155 'lsl x0, x0, #2'); 156 assertEq(wasmEvalText(quad64).exports.f(-37000000000n), -148000000000n) 157 assertEq(wasmEvalText(quad64).exports.f(42000000000n), 168000000000n) 158 159 // Test that multiplication by five yields a multiply 160 161 let quint32 = 162 `(module 163 (func (export "f") (param i32) (result i32) 164 (i32.mul (local.get 0) (i32.const 5))))`; 165 codegenTestARM64_adhoc( 166 quint32, 167 'f', 168 `mov w16, #0x5 169 mul w0, w0, w16`); 170 assertEq(wasmEvalText(quint32).exports.f(-37), -37*5) 171 assertEq(wasmEvalText(quint32).exports.f(42), 42*5) 172 173 let quint64 = `(module 174 (func (export "f") (param i64) (result i64) 175 (i64.mul (local.get 0) (i64.const 5))))` 176 codegenTestARM64_adhoc( 177 quint64, 178 'f', 179 `mov x16, #0x5 180 mul x0, x0, x16`); 181 assertEq(wasmEvalText(quint64).exports.f(-37000000000n), -37000000000n*5n) 182 assertEq(wasmEvalText(quint64).exports.f(42000000000n), 42000000000n*5n) 183 184 // Test that add/sub/and/or/xor don't need to reuse their input register. The 185 // proof here is that the destination register does not equal any of the input 186 // registers. 187 // 188 // We have adequate functionality tests for these elsewhere, so test only 189 // codegen here. 190 191 for ( [op, imm, expectVar, expectImm] of 192 [['and', 64, 193 'and x0, x1, x2', 194 'and x0, x1, #0x40'], 195 ['or', 64, 196 'orr x0, x1, x2', 197 'orr x0, x1, #0x40'], 198 ['xor', 64, 199 'eor x0, x1, x2', 200 'eor x0, x1, #0x40'], 201 ['add', 64, 202 'add x0, x1, x2', 203 'add x0, x1, #0x40 \\(64\\)'], 204 ['sub', 64, 205 'sub x0, x1, x2', 206 'sub x0, x1, #0x40 \\(64\\)']] ) { 207 codegenTestARM64_adhoc(` 208 (module 209 (func (export "f") (param i64) (param i64) (param i64) (result i64) 210 (i64.${op} (local.get 1) (local.get 2))))`, 211 'f', 212 expectVar); 213 codegenTestARM64_adhoc(` 214 (module 215 (func (export "f") (param i64) (param i64) (result i64) 216 (i64.${op} (local.get 1) (i64.const ${imm}))))`, 217 'f', 218 expectImm); 219 } 220 221 // Test that shifts and rotates with a constant don't need to reuse their input 222 // register. The proof here is that the destination register does not equal any 223 // of the input registers. 224 // 225 // We have adequate functionality tests for these elsewhere, so test only 226 // codegen here. 227 228 for ( [op, expect] of 229 [['shl', 'lsl x0, x1, #2'], 230 ['shr_s', 'asr x0, x1, #2'], 231 ['shr_u', 'lsr x0, x1, #2'], 232 ['rotl', 'ror x0, x1, #62'], 233 ['rotr', 'ror x0, x1, #2']] ) { 234 codegenTestARM64_adhoc(` 235 (module 236 (func (export "f") (param i64) (param i64) (result i64) 237 (i64.${op} (local.get 1) (i64.const 2))))`, 238 'f', 239 expect); 240 } 241 242 // Test that 0-n yields negation. 243 244 let subneg32 = 245 `(module 246 (func (export "f") (param i32) (result i32) 247 (i32.sub (i32.const 0) (local.get 0))))` 248 codegenTestARM64_adhoc( 249 subneg32, 250 'f', 251 'neg w0, w0'); 252 assertEq(wasmEvalText(subneg32).exports.f(-37), 37) 253 assertEq(wasmEvalText(subneg32).exports.f(42), -42) 254 255 let subneg64 = `(module 256 (func (export "f") (param i64) (result i64) 257 (i64.sub (i64.const 0) (local.get 0))))` 258 codegenTestARM64_adhoc( 259 subneg64, 260 'f', 261 'neg x0, x0'); 262 assertEq(wasmEvalText(subneg64).exports.f(-37000000000n), 37000000000n) 263 assertEq(wasmEvalText(subneg64).exports.f(42000000000n), -42000000000n) 264 265 // Test that select does something reasonable and does not tie its output to one 266 // of its inputs. 267 268 codegenTestARM64_adhoc( 269 `(module 270 (func (export "f") (param i64) (param i64) (param i64) (param i32) (result i64) 271 (select (local.get 1) (local.get 2) (local.get 3))))`, 272 'f', 273 `tst w3, w3 274 csel x0, x1, x2, ne`) 275 276 codegenTestARM64_adhoc( 277 `(module 278 (func (export "f") (param f64) (param f64) (param f64) (param i32) (result f64) 279 (select (local.get 1) (local.get 2) (local.get 3))))`, 280 'f', 281 `tst w0, w0 282 fcsel d0, d1, d2, ne`) 283 284 // Here we test that no boolean is generated and then re-tested, and that 285 // operands are swapped so that we can use an immediate constant, and that the 286 // input is not tied to the output. 287 288 codegenTestARM64_adhoc( 289 `(module 290 (func (export "f") (param $a i32) (param $b i32) (param $c i32) (param $d i32) (result i32) 291 (select (local.get $b) (local.get $d) (i32.lt_s (i32.const 0) (local.get $c)))))`, 292 'f', 293 `cmp w2, #0x0 \\(0\\) 294 csel w0, w1, w3, gt`) 295 296 codegenTestARM64_adhoc( 297 `(module 298 (func (export "f") (param $a f64) (param $b f64) (param $c f64) (param $d f64) (result f64) 299 (select (local.get $b) (local.get $d) (f64.lt (f64.const 0) (local.get $c)))))`, 300 'f', 301 `movi d0, #0x0 302 fcmp d0, d2 303 fcsel d0, d1, d3, lo`) 304 305 // FP ABS should not tie its input to its output. 306 307 codegenTestARM64_adhoc( 308 `(module 309 (func (export "f") (param f32) (param f32) (result f32) 310 (f32.abs (local.get 1))))`, 311 'f', 312 'fabs s0, s1'); 313 314 codegenTestARM64_adhoc( 315 `(module 316 (func (export "f") (param f64) (param f64) (result f64) 317 (f64.abs (local.get 1))))`, 318 'f', 319 'fabs d0, d1'); 320 321 // AND{32,64} followed by `== 0`: check the two operations are merged into a 322 // single 'tst' insn, and no 'and' insn. The merging isn't done for 323 // {OR,XOR}{32,64}. This is for both arguments being non-constant. 324 325 for ( [ty, expect_tst] of 326 [['i32', 'tst w0, w1'], 327 ['i64', 'tst x0, x1']] ) { 328 codegenTestARM64_adhoc( 329 `(module 330 (func (export "f") (param $p1 ${ty}) (param $p2 ${ty}) (result i32) 331 (local $x i32) 332 (local.set $x (i32.const 0x4D2)) 333 (if (${ty}.eq (${ty}.and (local.get $p1) (local.get $p2)) 334 (${ty}.const 0)) 335 (then (local.set $x (i32.const 0x11D7))) 336 ) 337 (local.get $x) 338 ) 339 )`, 340 'f', 341 `${expect_tst} 342 b\\.ne #\\+0xc \\(addr .*\\) 343 mov w0, #0x11d7 344 b #\\+0x8 \\(addr .*\\) 345 mov w0, #0x4d2` 346 ); 347 } 348 349 // AND64 followed by `== 0`, with one of the args being a constant. 350 351 for ( [imm, expect1, expect2] of 352 [ // as a valid logical-immediate => imm in insn 353 ['0x0F0F0F0F0F0F0F0F', 354 'tst x0, #0xf0f0f0f0f0f0f0f', 355 ''], 356 // anything else => imm synth'd into a reg 357 ['-0x4771', 358 'mov x16, #0xffffffffffffb88f', 359 'tst x0, x16']] 360 ) { 361 codegenTestARM64_adhoc( 362 `(module 363 (func (export "f") (param $p1 i64) (result i32) 364 (local $x i32) 365 (local.set $x (i32.const 0x4D2)) 366 (if (i64.eq (i64.and (i64.const ${imm}) (local.get $p1)) 367 (i64.const 0)) 368 (then (local.set $x (i32.const 0x11D7))) 369 ) 370 (local.get $x) 371 ) 372 )`, 373 'f', 374 `${expect1} 375 ${expect2} 376 b\\.ne #\\+0xc \\(addr .*\\) 377 mov w0, #0x11d7 378 b #\\+0x8 \\(addr .*\\) 379 mov w0, #0x4d2` 380 ); 381 } 382 383 // For integer comparison followed by select, check that the comparison result 384 // isn't materialised into a register, for specific types. 385 386 for ( [cmpTy, cmpOp, selTy, cmpRegPfx, cselRegPfx, armCC] of 387 [ ['i32', 'le_s', 'i32', 'w', 'w', 'le'], 388 ['i32', 'lt_u', 'i64', 'w', 'x', 'lo'], 389 ['i64', 'le_s', 'i32', 'x', 'w', 'le'], 390 ['i64', 'lt_u', 'i64', 'x', 'x', 'lo'], 391 ] ) { 392 codegenTestARM64_adhoc( 393 `(module 394 (func (export "f") 395 (param $p1 ${cmpTy}) (param $p2 ${cmpTy}) 396 (param $p3 ${selTy}) (param $p4 ${selTy}) 397 (result ${selTy}) 398 (select (local.get $p3) 399 (local.get $p4) 400 (${cmpTy}.${cmpOp} (local.get $p1) (local.get $p2))) 401 ) 402 )`, 403 'f', 404 `cmp ${cmpRegPfx}0, ${cmpRegPfx}1 405 csel ${cselRegPfx}0, ${cselRegPfx}2, ${cselRegPfx}3, ${armCC}` 406 ); 407 }