binop-x64-ion-codegen.js (13165B)
1 // |jit-test| skip-if: !hasDisassembler() || wasmCompileMode() != "ion" || !getBuildConfiguration("x64") || getBuildConfiguration("simulator"); include:codegen-x64-test.js 2 3 // There may be some redundant moves to set some operations up on x64 (that's 4 // bug 1701164) so we avoid those with no_prefix/no_suffix flags when the issue 5 // comes up. 6 7 // Handle calling convention differences. 8 function codegenTestX64_adhoc_call(module_text, export_name, expected, options = {}) { 9 // Microsoft x64 calling convention passes the first four arguments in 10 // registers rcx, rdx, r8, r9 (in that order). 11 if (getBuildConfiguration("windows")) { 12 // Arguments passed on the stack not yet supported. 13 assertEq( 14 expected.includes("%r8") || expected.includes("%r9"), 15 false, 16 "too many arguments" 17 ); 18 expected = expected.replaceAll("%rcx", "%r9") 19 .replaceAll("%rdx", "%r8") 20 .replaceAll("%rsi", "%rdx") 21 .replaceAll("%rdi", "%rcx") 22 .replaceAll("%esi", "%edx") 23 .replaceAll("%edi", "%ecx"); 24 } 25 codegenTestX64_adhoc(module_text, export_name, expected, options); 26 } 27 28 // Test that multiplication by -1 yields negation. 29 30 let neg32 = 31 `(module 32 (func (export "f") (param i32) (result i32) 33 (i32.mul (local.get 0) (i32.const -1))))`; 34 codegenTestX64_adhoc( 35 neg32, 36 'f', 37 'neg %eax', {no_prefix:true}); 38 assertEq(wasmEvalText(neg32).exports.f(-37), 37) 39 assertEq(wasmEvalText(neg32).exports.f(42), -42) 40 41 let neg64 = 42 `(module 43 (func (export "f") (param i64) (result i64) 44 (i64.mul (local.get 0) (i64.const -1))))`; 45 codegenTestX64_adhoc( 46 neg64, 47 'f', 48 'neg %rax', {no_prefix:true}); 49 assertEq(wasmEvalText(neg64).exports.f(-37000000000n), 37000000000n) 50 assertEq(wasmEvalText(neg64).exports.f(42000000000n), -42000000000n) 51 52 // Test that multiplication by zero yields zero 53 54 let zero32 = 55 `(module 56 (func (export "f") (param i32) (result i32) 57 (i32.mul (local.get 0) (i32.const 0))))`; 58 codegenTestX64_adhoc( 59 zero32, 60 'f', 61 'xor %eax, %eax'); 62 assertEq(wasmEvalText(zero32).exports.f(-37), 0) 63 assertEq(wasmEvalText(zero32).exports.f(42), 0) 64 65 let zero64 = `(module 66 (func (export "f") (param i64) (result i64) 67 (i64.mul (local.get 0) (i64.const 0))))` 68 codegenTestX64_adhoc( 69 zero64, 70 'f', 71 'xor %rax, %rax'); 72 assertEq(wasmEvalText(zero64).exports.f(-37000000000n), 0n) 73 assertEq(wasmEvalText(zero64).exports.f(42000000000n), 0n) 74 75 // Test that multiplication by one yields no code 76 77 let one32 = 78 `(module 79 (func (export "f") (param i32) (result i32) 80 (i32.mul (local.get 0) (i32.const 1))))`; 81 codegenTestX64_adhoc( 82 one32, 83 'f', 84 '', {no_prefix:true}); 85 assertEq(wasmEvalText(one32).exports.f(-37), -37) 86 assertEq(wasmEvalText(one32).exports.f(42), 42) 87 88 let one64 = `(module 89 (func (export "f") (param i64) (result i64) 90 (i64.mul (local.get 0) (i64.const 1))))` 91 codegenTestX64_adhoc( 92 one64, 93 'f', 94 '', {no_prefix:true}); 95 assertEq(wasmEvalText(one64).exports.f(-37000000000n), -37000000000n) 96 assertEq(wasmEvalText(one64).exports.f(42000000000n), 42000000000n) 97 98 // Test that multiplication by two yields lea 99 100 let double32 = 101 `(module 102 (func (export "f") (param i32) (result i32) 103 (i32.mul (local.get 0) (i32.const 2))))`; 104 codegenTestX64_adhoc_call( 105 double32, 106 'f', 107 'lea \\(%rdi,%rdi,1\\), %eax'); 108 assertEq(wasmEvalText(double32).exports.f(-37), -74) 109 assertEq(wasmEvalText(double32).exports.f(42), 84) 110 111 let double64 = `(module 112 (func (export "f") (param i64) (result i64) 113 (i64.mul (local.get 0) (i64.const 2))))` 114 codegenTestX64_adhoc_call( 115 double64, 116 'f', 117 'lea \\(%rdi,%rdi,1\\), %rax'); 118 assertEq(wasmEvalText(double64).exports.f(-37000000000n), -74000000000n) 119 assertEq(wasmEvalText(double64).exports.f(42000000000n), 84000000000n) 120 121 // Test that multiplication by four yields lea 122 123 let quad32 = 124 `(module 125 (func (export "f") (param i32) (result i32) 126 (i32.mul (local.get 0) (i32.const 4))))`; 127 codegenTestX64_adhoc_call( 128 quad32, 129 'f', 130 'lea \\(,%rdi,4\\), %eax'); 131 assertEq(wasmEvalText(quad32).exports.f(-37), -148) 132 assertEq(wasmEvalText(quad32).exports.f(42), 168) 133 134 let quad64 = `(module 135 (func (export "f") (param i64) (result i64) 136 (i64.mul (local.get 0) (i64.const 4))))` 137 codegenTestX64_adhoc_call( 138 quad64, 139 'f', 140 'lea \\(,%rdi,4\\), %rax'); 141 assertEq(wasmEvalText(quad64).exports.f(-37000000000n), -148000000000n) 142 assertEq(wasmEvalText(quad64).exports.f(42000000000n), 168000000000n) 143 144 // Test that multiplication by five yields lea 145 146 let quint32 = 147 `(module 148 (func (export "f") (param i32) (result i32) 149 (i32.mul (local.get 0) (i32.const 5))))`; 150 codegenTestX64_adhoc_call( 151 quint32, 152 'f', 153 'lea \\(%rdi,%rdi,4\\), %eax'); 154 assertEq(wasmEvalText(quint32).exports.f(-37), -37*5) 155 assertEq(wasmEvalText(quint32).exports.f(42), 42*5) 156 157 let quint64 = `(module 158 (func (export "f") (param i64) (result i64) 159 (i64.mul (local.get 0) (i64.const 5))))` 160 codegenTestX64_adhoc_call( 161 quint64, 162 'f', 163 `lea \\(%rdi,%rdi,4\\), %rax`) 164 assertEq(wasmEvalText(quint64).exports.f(-37000000000n), -37000000000n*5n) 165 assertEq(wasmEvalText(quint64).exports.f(42000000000n), 42000000000n*5n) 166 167 // Test that multiplication by six yields imul 168 169 let sext32 = 170 `(module 171 (func (export "f") (param i32) (result i32) 172 (i32.mul (local.get 0) (i32.const 6))))`; 173 codegenTestX64_adhoc_call( 174 sext32, 175 'f', 176 'imul \\$0x06, %edi, %eax'); 177 assertEq(wasmEvalText(sext32).exports.f(-37), -37*6) 178 assertEq(wasmEvalText(sext32).exports.f(42), 42*6) 179 180 let sext64 = `(module 181 (func (export "f") (param i64) (result i64) 182 (i64.mul (local.get 0) (i64.const 6))))` 183 codegenTestX64_adhoc_call( 184 sext64, 185 'f', 186 `imul \\$0x06, %rdi, %rax`) 187 assertEq(wasmEvalText(sext64).exports.f(-37000000000n), -37000000000n*6n) 188 assertEq(wasmEvalText(sext64).exports.f(42000000000n), 42000000000n*6n) 189 190 // Test that multiplication by UINT32_MAX yields imul 191 192 let uint32max64 = `(module 193 (func (export "f") (param i64) (result i64) 194 (i64.mul (local.get 0) (i64.const 0xffffffff))))` 195 codegenTestX64_adhoc( 196 uint32max64, 197 'f', 198 `mov \\$-0x01, %r11d 199 imul %r11, %rax`, {no_prefix:true}) 200 assertEq(wasmEvalText(uint32max64).exports.f(-37000000000n), BigInt.asIntN(64, -37000000000n*0xffffffffn)) 201 assertEq(wasmEvalText(uint32max64).exports.f(42000000000n), BigInt.asIntN(64, 42000000000n*0xffffffffn)) 202 203 // Test that 0-n yields negation. 204 205 let subneg32 = 206 `(module 207 (func (export "f") (param i32) (result i32) 208 (i32.sub (i32.const 0) (local.get 0))))` 209 codegenTestX64_adhoc( 210 subneg32, 211 'f', 212 'neg %eax', {no_prefix:true}); 213 assertEq(wasmEvalText(subneg32).exports.f(-37), 37) 214 assertEq(wasmEvalText(subneg32).exports.f(42), -42) 215 216 let subneg64 = 217 `(module 218 (func (export "f") (param i64) (result i64) 219 (i64.sub (i64.const 0) (local.get 0))))` 220 codegenTestX64_adhoc( 221 subneg64, 222 'f', 223 'neg %rax', {no_prefix:true}); 224 assertEq(wasmEvalText(subneg64).exports.f(-37000000000n), 37000000000n) 225 assertEq(wasmEvalText(subneg64).exports.f(42000000000n), -42000000000n) 226 227 // AND{32,64} followed by `== 0`: check the two operations are merged into a 228 // single 'test' insn, and no 'and' insn. The merging isn't done for 229 // {OR,XOR}{32,64}. This is for both arguments being non-constant. 230 231 for ( [ty, expect_test] of 232 [['i32', 'test %e.., %e..'], 233 ['i64', 'test %r.., %r..']] ) { 234 codegenTestX64_adhoc( 235 `(module 236 (func (export "f") (param $p1 ${ty}) (param $p2 ${ty}) (result i32) 237 (local $x i32) 238 (local.set $x (i32.const 0x4D2)) 239 (if (${ty}.eq (${ty}.and (local.get $p1) (local.get $p2)) 240 (${ty}.const 0)) 241 (then (local.set $x (i32.const 0x11D7))) 242 ) 243 (local.get $x) 244 ) 245 )`, 246 'f', 247 `${expect_test} 248 jnz 0x00000000000000.. 249 mov \\$0x11D7, %eax 250 jmp 0x00000000000000.. 251 mov \\$0x4D2, %eax` 252 ); 253 } 254 255 // AND64 followed by `== 0`, with one of the args being a constant. Depending 256 // on the constant we can get one of three forms. 257 258 for ( [imm, expect1, expect2] of 259 [ // in signed-32 range => imm in insn 260 ['0x17654321', 261 'test \\$0x17654321, %e..', // edi or ecx 262 ''], 263 // in unsigned-32 range => imm in reg via movl 264 ['0x87654321', 265 'mov \\$-0x789ABCDF, %r11d', 266 'test %r11, %r..'], // rdi or rcx 267 // not in either range => imm in reg via mov(absq) 268 ['0x187654321', 269 'mov \\$0x187654321, %r11', 270 'test %r11, %r..']] // rdi or rcx 271 ) { 272 codegenTestX64_adhoc( 273 `(module 274 (func (export "f") (param $p1 i64) (result i32) 275 (local $x i32) 276 (local.set $x (i32.const 0x4D2)) 277 (if (i64.eq (i64.and (i64.const ${imm}) (local.get $p1)) 278 (i64.const 0)) 279 (then (local.set $x (i32.const 0x11D7))) 280 ) 281 (local.get $x) 282 ) 283 )`, 284 'f', 285 `${expect1} 286 ${expect2} 287 jnz 0x00000000000000.. 288 mov \\$0x11D7, %eax 289 jmp 0x00000000000000.. 290 mov \\$0x4D2, %eax` 291 ); 292 } 293 294 // For integer comparison followed by select, check that the comparison result 295 // isn't materialised into a register, for specific types. 296 297 function cmpSel32vs64(cmpTy, cmpOp, selTy) { 298 return `(module 299 (func (export "f") 300 (param $p1 ${cmpTy}) (param $p2 ${cmpTy}) 301 (param $p3 ${selTy}) (param $p4 ${selTy}) 302 (result ${selTy}) 303 (select (local.get $p3) 304 (local.get $p4) 305 (${cmpTy}.${cmpOp} (local.get $p1) (local.get $p2))) 306 ) 307 )`; 308 } 309 if (getBuildConfiguration("windows")) { 310 for ( [cmpTy, cmpOp, selTy, insn1, insn2, insn3] of 311 [ ['i32', 'le_s', 'i32', 'mov %ebx, %eax', 312 'cmp %edx, %ecx', 313 'cmovnle %r9d, %eax'], 314 ['i32', 'lt_u', 'i64', 'mov %rbx, %rax', 315 'cmp %edx, %ecx', 316 'cmovnb %r9, %rax'], 317 ['i64', 'le_s', 'i32', 'mov %ebx, %eax', 318 'cmp %rdx, %rcx', 319 'cmovnle %r9d, %eax'], 320 ['i64', 'lt_u', 'i64', 'mov %rbx, %rax', 321 'cmp %rdx, %rcx', 322 'cmovnb %r9, %rax'] 323 ] ) { 324 codegenTestX64_adhoc(cmpSel32vs64(cmpTy, cmpOp, selTy), 'f', 325 `mov %r8.*, %.bx 326 ${insn1} 327 ${insn2} 328 ${insn3}` 329 ); 330 } 331 } else { 332 for ( [cmpTy, cmpOp, selTy, insn1, insn2, insn3] of 333 [ ['i32', 'le_s', 'i32', 'mov %edx, %eax', 334 'cmp %esi, %edi', 335 'cmovnle %ecx, %eax'], 336 ['i32', 'lt_u', 'i64', 'mov %rdx, %rax', 337 'cmp %esi, %edi', 338 'cmovnb %rcx, %rax'], 339 ['i64', 'le_s', 'i32', 'mov %edx, %eax', 340 'cmp %rsi, %rdi', 341 'cmovnle %ecx, %eax'], 342 ['i64', 'lt_u', 'i64', 'mov %rdx, %rax', 343 'cmp %rsi, %rdi', 344 'cmovnb %rcx, %rax'] 345 ] ) { 346 codegenTestX64_adhoc(cmpSel32vs64(cmpTy, cmpOp, selTy), 'f', 347 `${insn1} 348 ${insn2} 349 ${insn3}` 350 ); 351 } 352 } 353 354 // For integer comparison followed by select, check correct use of operands in 355 // registers vs memory. At least for the 64-bit-cmp/64-bit-sel case. 356 357 for ( [pAnyCmp, pAnySel, cmpArgL, cmovArgL ] of 358 [ // r, r 359 ['$pReg1', '$pReg2', 360 '%r.+', '%r.+'], 361 // r, m 362 ['$pReg1', '$pMem2', 363 '%r.+', '0x..\\(%rbp\\)'], 364 // m, r 365 ['$pMem1', '$pReg2', 366 '0x..\\(%rbp\\)', '%r.+'], 367 // m, m 368 ['$pMem1', '$pMem2', 369 '0x..\\(%rbp\\)', '0x..\\(%rbp\\)'] 370 ] ) { 371 codegenTestX64_adhoc( 372 `(module 373 (func (export "f") 374 (param $p1 i64) (param $p2 i64) ;; in regs for both ABIs 375 (param $pReg1 i64) (param $pReg2 i64) ;; in regs for both ABIs 376 (param i64) (param i64) ;; ignored 377 (param $pMem1 i64) (param $pMem2 i64) ;; in mem for both ABIs 378 (result i64) 379 (select (local.get $p1) 380 (local.get ${pAnySel}) 381 (i64.eq (local.get $p2) (local.get ${pAnyCmp}))) 382 ) 383 )`, 384 'f', 385 // On Linux we have an extra move 386 (getBuildConfiguration("windows") ? '' : 'mov %r.+, %r.+\n') + 387 // 'q*' because the disassembler shows 'q' only for the memory cases 388 `mov %r.+, %r.+ 389 cmpq* ${cmpArgL}, %r.+ 390 cmovnzq* ${cmovArgL}, %r.+` 391 ); 392 }