summaryrefslogtreecommitdiffstats
path: root/js/src/jit-test/tests/wasm/binop-x64-ion-codegen.js
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/jit-test/tests/wasm/binop-x64-ion-codegen.js')
-rw-r--r--js/src/jit-test/tests/wasm/binop-x64-ion-codegen.js335
1 files changed, 335 insertions, 0 deletions
diff --git a/js/src/jit-test/tests/wasm/binop-x64-ion-codegen.js b/js/src/jit-test/tests/wasm/binop-x64-ion-codegen.js
new file mode 100644
index 0000000000..93d0db1738
--- /dev/null
+++ b/js/src/jit-test/tests/wasm/binop-x64-ion-codegen.js
@@ -0,0 +1,335 @@
+// |jit-test| skip-if: !hasDisassembler() || wasmCompileMode() != "ion" || !getBuildConfiguration().x64 || getBuildConfiguration().simulator; include:codegen-x64-test.js
+
+// There may be some redundant moves to set some operations up on x64 (that's
+// bug 1701164) so we avoid those with no_prefix/no_suffix flags when the issue
+// comes up.
+
+// Test that multiplication by -1 yields negation.
+
+let neg32 =
+ `(module
+ (func (export "f") (param i32) (result i32)
+ (i32.mul (local.get 0) (i32.const -1))))`;
+codegenTestX64_adhoc(
+ neg32,
+ 'f',
+ 'f7 d8 neg %eax', {no_prefix:true});
+assertEq(wasmEvalText(neg32).exports.f(-37), 37)
+assertEq(wasmEvalText(neg32).exports.f(42), -42)
+
+let neg64 =
+ `(module
+ (func (export "f") (param i64) (result i64)
+ (i64.mul (local.get 0) (i64.const -1))))`;
+codegenTestX64_adhoc(
+ neg64,
+ 'f',
+ '48 f7 d8 neg %rax', {no_prefix:true});
+assertEq(wasmEvalText(neg64).exports.f(-37000000000n), 37000000000n)
+assertEq(wasmEvalText(neg64).exports.f(42000000000n), -42000000000n)
+
+// Test that multiplication by zero yields zero
+
+let zero32 =
+ `(module
+ (func (export "f") (param i32) (result i32)
+ (i32.mul (local.get 0) (i32.const 0))))`;
+codegenTestX64_adhoc(
+ zero32,
+ 'f',
+ '33 c0 xor %eax, %eax', {no_prefix:true});
+assertEq(wasmEvalText(zero32).exports.f(-37), 0)
+assertEq(wasmEvalText(zero32).exports.f(42), 0)
+
+let zero64 = `(module
+ (func (export "f") (param i64) (result i64)
+ (i64.mul (local.get 0) (i64.const 0))))`
+codegenTestX64_adhoc(
+ zero64,
+ 'f',
+ '48 33 c0 xor %rax, %rax', {no_prefix:true});
+assertEq(wasmEvalText(zero64).exports.f(-37000000000n), 0n)
+assertEq(wasmEvalText(zero64).exports.f(42000000000n), 0n)
+
+// Test that multiplication by one yields no code
+
+let one32 =
+ `(module
+ (func (export "f") (param i32) (result i32)
+ (i32.mul (local.get 0) (i32.const 1))))`;
+codegenTestX64_adhoc(
+ one32,
+ 'f',
+ '', {no_prefix:true});
+assertEq(wasmEvalText(one32).exports.f(-37), -37)
+assertEq(wasmEvalText(one32).exports.f(42), 42)
+
+let one64 = `(module
+ (func (export "f") (param i64) (result i64)
+ (i64.mul (local.get 0) (i64.const 1))))`
+codegenTestX64_adhoc(
+ one64,
+ 'f',
+ '', {no_prefix:true});
+assertEq(wasmEvalText(one64).exports.f(-37000000000n), -37000000000n)
+assertEq(wasmEvalText(one64).exports.f(42000000000n), 42000000000n)
+
+// Test that multiplication by two yields an add
+
+let double32 =
+ `(module
+ (func (export "f") (param i32) (result i32)
+ (i32.mul (local.get 0) (i32.const 2))))`;
+codegenTestX64_adhoc(
+ double32,
+ 'f',
+ '03 c0 add %eax, %eax', {no_prefix:true});
+assertEq(wasmEvalText(double32).exports.f(-37), -74)
+assertEq(wasmEvalText(double32).exports.f(42), 84)
+
+let double64 = `(module
+ (func (export "f") (param i64) (result i64)
+ (i64.mul (local.get 0) (i64.const 2))))`
+codegenTestX64_adhoc(
+ double64,
+ 'f',
+ '48 03 c0 add %rax, %rax', {no_prefix:true});
+assertEq(wasmEvalText(double64).exports.f(-37000000000n), -74000000000n)
+assertEq(wasmEvalText(double64).exports.f(42000000000n), 84000000000n)
+
+// Test that multiplication by four yields a shift
+
+let quad32 =
+ `(module
+ (func (export "f") (param i32) (result i32)
+ (i32.mul (local.get 0) (i32.const 4))))`;
+codegenTestX64_adhoc(
+ quad32,
+ 'f',
+ 'c1 e0 02 shl \\$0x02, %eax', {no_prefix:true});
+assertEq(wasmEvalText(quad32).exports.f(-37), -148)
+assertEq(wasmEvalText(quad32).exports.f(42), 168)
+
+let quad64 = `(module
+ (func (export "f") (param i64) (result i64)
+ (i64.mul (local.get 0) (i64.const 4))))`
+codegenTestX64_adhoc(
+ quad64,
+ 'f',
+ '48 c1 e0 02 shl \\$0x02, %rax', {no_prefix:true});
+assertEq(wasmEvalText(quad64).exports.f(-37000000000n), -148000000000n)
+assertEq(wasmEvalText(quad64).exports.f(42000000000n), 168000000000n)
+
+// Test that multiplication by five yields a multiply
+
+let quint32 =
+ `(module
+ (func (export "f") (param i32) (result i32)
+ (i32.mul (local.get 0) (i32.const 5))))`;
+codegenTestX64_adhoc(
+ quint32,
+ 'f',
+ '6b c0 05 imul \\$0x05, %eax, %eax', {no_prefix:true});
+assertEq(wasmEvalText(quint32).exports.f(-37), -37*5)
+assertEq(wasmEvalText(quint32).exports.f(42), 42*5)
+
+let quint64 = `(module
+ (func (export "f") (param i64) (result i64)
+ (i64.mul (local.get 0) (i64.const 5))))`
+codegenTestX64_adhoc(
+ quint64,
+ 'f',
+ `48 6b c0 05 imul \\$0x05, %rax, %rax`, {no_prefix:true})
+assertEq(wasmEvalText(quint64).exports.f(-37000000000n), -37000000000n*5n)
+assertEq(wasmEvalText(quint64).exports.f(42000000000n), 42000000000n*5n)
+
+// Test that 0-n yields negation.
+
+let subneg32 =
+ `(module
+ (func (export "f") (param i32) (result i32)
+ (i32.sub (i32.const 0) (local.get 0))))`
+codegenTestX64_adhoc(
+ subneg32,
+ 'f',
+ 'f7 d8 neg %eax', {no_prefix:true});
+assertEq(wasmEvalText(subneg32).exports.f(-37), 37)
+assertEq(wasmEvalText(subneg32).exports.f(42), -42)
+
+let subneg64 =
+ `(module
+ (func (export "f") (param i64) (result i64)
+ (i64.sub (i64.const 0) (local.get 0))))`
+codegenTestX64_adhoc(
+ subneg64,
+ 'f',
+ '48 f7 d8 neg %rax', {no_prefix:true});
+assertEq(wasmEvalText(subneg64).exports.f(-37000000000n), 37000000000n)
+assertEq(wasmEvalText(subneg64).exports.f(42000000000n), -42000000000n)
+
+// AND{32,64} followed by `== 0`: check the two operations are merged into a
+// single 'test' insn, and no 'and' insn. The merging isn't done for
+// {OR,XOR}{32,64}. This is for both arguments being non-constant.
+
+for ( [ty, expect_test] of
+ [['i32', '85 .. test %e.., %e..'],
+ ['i64', '48 85 .. test %r.., %r..']] ) {
+ codegenTestX64_adhoc(
+ `(module
+ (func (export "f") (param $p1 ${ty}) (param $p2 ${ty}) (result i32)
+ (local $x i32)
+ (set_local $x (i32.const 0x4D2))
+ (if (${ty}.eq (${ty}.and (local.get $p1) (local.get $p2))
+ (${ty}.const 0))
+ (set_local $x (i32.const 0x11D7))
+ )
+ (get_local $x)
+ )
+ )`,
+ 'f',
+ `${expect_test}
+ 0f 85 .. 00 00 00 jnz 0x00000000000000..
+ b8 d7 11 00 00 mov \\$0x11D7, %eax
+ e9 .. 00 00 00 jmp 0x00000000000000..
+ b8 d2 04 00 00 mov \\$0x4D2, %eax`
+ );
+}
+
+// AND64 followed by `== 0`, with one of the args being a constant. Depending
+// on the constant we can get one of three forms.
+
+for ( [imm, expect1, expect2] of
+ [ // in signed-32 range => imm in insn
+ ['0x17654321',
+ 'f7 c. 21 43 65 17 test \\$0x17654321, %e..', // edi or ecx
+ ''],
+ // in unsigned-32 range => imm in reg via movl
+ ['0x87654321',
+ '41 bb 21 43 65 87 mov \\$-0x789ABCDF, %r11d',
+ '4c 85 d. test %r11, %r..'], // rdi or rcx
+ // not in either range => imm in reg via mov(absq)
+ ['0x187654321',
+ '49 bb 21 43 65 87 01 00 00 00 mov \\$0x187654321, %r11',
+ '4c 85 d. test %r11, %r..']] // rdi or rcx
+ ) {
+ codegenTestX64_adhoc(
+ `(module
+ (func (export "f") (param $p1 i64) (result i32)
+ (local $x i32)
+ (set_local $x (i32.const 0x4D2))
+ (if (i64.eq (i64.and (i64.const ${imm}) (local.get $p1))
+ (i64.const 0))
+ (set_local $x (i32.const 0x11D7))
+ )
+ (get_local $x)
+ )
+ )`,
+ 'f',
+ `${expect1}
+ ${expect2}
+ 0f 85 .. 00 00 00 jnz 0x00000000000000..
+ b8 d7 11 00 00 mov \\$0x11D7, %eax
+ e9 .. 00 00 00 jmp 0x00000000000000..
+ b8 d2 04 00 00 mov \\$0x4D2, %eax`
+ );
+}
+
+// For integer comparison followed by select, check that the comparison result
+// isn't materialised into a register, for specific types.
+
+function cmpSel32vs64(cmpTy, cmpOp, selTy) {
+ return `(module
+ (func (export "f")
+ (param $p1 ${cmpTy}) (param $p2 ${cmpTy})
+ (param $p3 ${selTy}) (param $p4 ${selTy})
+ (result ${selTy})
+ (select (local.get $p3)
+ (local.get $p4)
+ (${cmpTy}.${cmpOp} (local.get $p1) (local.get $p2)))
+ )
+ )`;
+}
+if (getBuildConfiguration().windows) {
+ for ( [cmpTy, cmpOp, selTy, insn1, insn2, insn3] of
+ [ ['i32', 'le_s', 'i32', '8b c3 mov %ebx, %eax',
+ '3b ca cmp %edx, %ecx',
+ '41 0f 4f c1 cmovnle %r9d, %eax'],
+ ['i32', 'lt_u', 'i64', '48 89 d8 mov %rbx, %rax',
+ '3b ca cmp %edx, %ecx',
+ '49 0f 43 c1 cmovnb %r9, %rax'],
+ ['i64', 'le_s', 'i32', '8b c3 mov %ebx, %eax',
+ '48 3b ca cmp %rdx, %rcx',
+ '41 0f 4f c1 cmovnle %r9d, %eax'],
+ ['i64', 'lt_u', 'i64', '48 89 d8 mov %rbx, %rax',
+ '48 3b ca cmp %rdx, %rcx',
+ '49 0f 43 c1 cmovnb %r9, %rax']
+ ] ) {
+ codegenTestX64_adhoc(cmpSel32vs64(cmpTy, cmpOp, selTy), 'f',
+ `4. (89 c3|8b d8) mov %r8.*, %.bx
+ ${insn1}
+ ${insn2}
+ ${insn3}`
+ );
+ }
+} else {
+ for ( [cmpTy, cmpOp, selTy, insn1, insn2, insn3] of
+ [ ['i32', 'le_s', 'i32', '8b c2 mov %edx, %eax',
+ '3b fe cmp %esi, %edi',
+ '0f 4f c1 cmovnle %ecx, %eax'],
+ ['i32', 'lt_u', 'i64', '48 89 d0 mov %rdx, %rax',
+ '3b fe cmp %esi, %edi',
+ '48 0f 43 c1 cmovnb %rcx, %rax'],
+ ['i64', 'le_s', 'i32', '8b c2 mov %edx, %eax',
+ '48 3b fe cmp %rsi, %rdi',
+ '0f 4f c1 cmovnle %ecx, %eax'],
+ ['i64', 'lt_u', 'i64', '48 89 d0 mov %rdx, %rax',
+ '48 3b fe cmp %rsi, %rdi',
+ '48 0f 43 c1 cmovnb %rcx, %rax']
+ ] ) {
+ codegenTestX64_adhoc(cmpSel32vs64(cmpTy, cmpOp, selTy), 'f',
+ `${insn1}
+ ${insn2}
+ ${insn3}`
+ );
+ }
+}
+
+// For integer comparison followed by select, check correct use of operands in
+// registers vs memory. At least for the 64-bit-cmp/64-bit-sel case.
+
+for ( [pAnyCmp, pAnySel, cmpBytes, cmpArgL, cmovBytes, cmovArgL ] of
+ [ // r, r
+ ['$pReg1', '$pReg2',
+ '4. .. ..', '%r.+', '4. .. .. ..', '%r.+'],
+ // r, m
+ ['$pReg1', '$pMem2',
+ '4. .. ..', '%r.+', '4. .. .. .. ..', '0x..\\(%rbp\\)'],
+ // m, r
+ ['$pMem1', '$pReg2',
+ '4. .. .. ..', '0x..\\(%rbp\\)', '4. .. .. ..', '%r.+'],
+ // m, m
+ ['$pMem1', '$pMem2',
+ '4. .. .. ..', '0x..\\(%rbp\\)', '4. .. .. .. ..', '0x..\\(%rbp\\)']
+ ] ) {
+ codegenTestX64_adhoc(
+ `(module
+ (func (export "f")
+ (param $p1 i64) (param $p2 i64) ;; in regs for both ABIs
+ (param $pReg1 i64) (param $pReg2 i64) ;; in regs for both ABIs
+ (param i64) (param i64) ;; ignored
+ (param $pMem1 i64) (param $pMem2 i64) ;; in mem for both ABIs
+ (result i64)
+ (select (local.get $p1)
+ (local.get ${pAnySel})
+ (i64.eq (local.get $p2) (local.get ${pAnyCmp})))
+ )
+ )`,
+ 'f',
+ // On Linux we have an extra move
+ (getBuildConfiguration().windows ? '' : '48 89 .. mov %r.+, %r.+\n') +
+ // 'q*' because the disassembler shows 'q' only for the memory cases
+ `48 89 .. mov %r.+, %r.+
+ ${cmpBytes} cmpq* ${cmpArgL}, %r.+
+ ${cmovBytes} cmovnzq* ${cmovArgL}, %r.+`
+ );
+}