Source code

Revision control

Copy as Markdown

Other Tools

// |jit-test| skip-if: !hasDisassembler() || wasmCompileMode() != "ion" || getBuildConfiguration("windows") || !getBuildConfiguration("x64") || getBuildConfiguration("simulator"); include:codegen-x64-test.js
// Windows is disallowed because the argument registers are different from on
// Linux, and matching both is both difficult and not of much value.
// Signed 32-bit division with constants.
const i32_div_s = [
// Division by zero.
{
divisor: 0,
expected: `ud2`,
},
// Power of two divisor
{
divisor: 1,
expected: `mov %edi, %eax`,
},
{
divisor: 2,
expected: `mov %edi, %eax
shr \\$0x1F, %eax
add %edi, %eax
sar \\$0x01, %eax`,
},
{
divisor: 4,
expected: `mov %edi, %eax
sar \\$0x1F, %eax
shr \\$0x1E, %eax
add %edi, %eax
sar \\$0x02, %eax`,
},
// Division by -1 needs an overflow check.
{
divisor: -1,
expected: `mov %edi, %eax
neg %eax
jno 0x${HEX}+
ud2`,
},
// Other divisors.
{
divisor: 3,
expected: `movsxd %edi, %rax
imul \\$0x55555556, %rax, %rax
shr \\$0x20, %rax
mov %edi, %ecx
sar \\$0x1F, %ecx
sub %ecx, %eax`,
},
{
divisor: 5,
expected: `movsxd %edi, %rax
imul \\$0x66666667, %rax, %rax
sar \\$0x21, %rax
mov %edi, %ecx
sar \\$0x1F, %ecx
sub %ecx, %eax`,
},
{
divisor: 7,
expected: `movsxd %edi, %rax
imul \\$-0x6DB6DB6D, %rax, %rax
shr \\$0x20, %rax
add %edi, %eax
sar \\$0x02, %eax
mov %edi, %ecx
sar \\$0x1F, %ecx
sub %ecx, %eax`,
},
{
divisor: 9,
expected: `movsxd %edi, %rax
imul \\$0x38E38E39, %rax, %rax
sar \\$0x21, %rax
mov %edi, %ecx
sar \\$0x1F, %ecx
sub %ecx, %eax`,
},
];
for (let {divisor, expected} of i32_div_s) {
let divs32 =
`(module
(func (export "f") (param i32) (result i32)
(i32.div_s (local.get 0) (i32.const ${divisor}))))`
codegenTestX64_adhoc(divs32, 'f', expected);
// Test negative divisors, too.
if (divisor > 1) {
let divs32 =
`(module
(func (export "f") (param i32) (result i32)
(i32.div_s (local.get 0) (i32.const -${divisor}))))`
codegenTestX64_adhoc(divs32, 'f', expected + `
neg %eax`
);
}
}
// Unsigned 32-bit division with constants.
const i32_div_u = [
// Division by zero.
{
divisor: 0,
expected: `ud2`,
},
// Power of two divisor
{
divisor: 1,
expected: `mov %edi, %ecx
mov %ecx, %eax`,
},
{
divisor: 2,
expected: `mov %edi, %ecx
mov %ecx, %eax
shr \\$0x01, %eax`,
},
{
divisor: 4,
expected: `mov %edi, %ecx
mov %ecx, %eax
shr \\$0x02, %eax`,
},
// Other divisors.
{
divisor: 3,
expected: `mov %edi, %eax
mov \\$-0x55555555, %ecx
imul %rcx, %rax
shr \\$0x21, %rax`,
},
{
divisor: 5,
expected: `mov %edi, %eax
mov \\$-0x33333333, %ecx
imul %rcx, %rax
shr \\$0x22, %rax`,
},
{
divisor: 7,
expected: `mov %edi, %eax
imul \\$0x24924925, %rax, %rax
shr \\$0x20, %rax
mov %edi, %ecx
sub %eax, %ecx
shr \\$0x01, %ecx
add %ecx, %eax
shr \\$0x02, %eax`,
},
{
divisor: 9,
expected: `mov %edi, %eax
imul \\$0x38E38E39, %rax, %rax
shr \\$0x21, %rax`,
},
// Special case: Zero (additional) shift amount.
{
divisor: 641,
expected: `mov %edi, %eax
imul \\$0x663D81, %rax, %rax
shr \\$0x20, %rax`,
},
];
for (let {divisor, expected} of i32_div_u) {
let divu32 =
`(module
(func (export "f") (param i32) (result i32)
(i32.div_u (local.get 0) (i32.const ${divisor}))))`
codegenTestX64_adhoc(divu32, 'f', expected);
}
// Signed 64-bit division with constants.
const i64_div_s = [
// Division by zero.
{
divisor: 0,
expected: `ud2`,
},
// Power of two divisor
{
divisor: 1,
expected: `mov %rdi, %rax`,
},
{
divisor: 2,
expected: `mov %rdi, %rax
shr \\$0x3F, %rax
add %rdi, %rax
sar \\$0x01, %rax`,
},
{
divisor: 4,
expected: `mov %rdi, %rax
sar \\$0x3F, %rax
shr \\$0x3E, %rax
add %rdi, %rax
sar \\$0x02, %rax`,
},
{
divisor: 0x1_0000_0000,
expected: `mov %rdi, %rax
sar \\$0x3F, %rax
shr \\$0x20, %rax
add %rdi, %rax
sar \\$0x20, %rax`,
},
// Division by -1 needs an overflow check.
{
divisor: -1,
expected: `mov %rdi, %rax
neg %rax
jno 0x${HEX}+
ud2`,
},
// Other divisors.
{
divisor: 3,
expected: `mov \\$0x5555555555555556, %rax
imul %rdi
mov %rdi, %rax
sar \\$0x3F, %rax
sub %rax, %rdx`,
},
{
divisor: 5,
expected: `mov \\$0x6666666666666667, %rax
imul %rdi
sar \\$0x01, %rdx
mov %rdi, %rax
sar \\$0x3F, %rax
sub %rax, %rdx`,
},
{
divisor: 7,
expected: `mov \\$0x4924924924924925, %rax
imul %rdi
sar \\$0x01, %rdx
mov %rdi, %rax
sar \\$0x3F, %rax
sub %rax, %rdx`,
},
{
divisor: 9,
expected: `mov \\$0x1C71C71C71C71C72, %rax
imul %rdi
mov %rdi, %rax
sar \\$0x3F, %rax
sub %rax, %rdx`,
},
];
for (let {divisor, expected} of i64_div_s) {
let result = IsPowerOfTwo(divisor) ? "rax" : "rdx";
let mov_rdx_to_rax = result !== "rax" ? `
mov %rdx, %rax` : ``;
let divs64 =
`(module
(func (export "f") (param i64) (result i64)
(i64.div_s (local.get 0) (i64.const ${divisor}))))`
codegenTestX64_adhoc(divs64, 'f', expected + mov_rdx_to_rax);
// Test negative divisors, too.
if (divisor > 1) {
let divs64 =
`(module
(func (export "f") (param i64) (result i64)
(i64.div_s (local.get 0) (i64.const -${divisor}))))`
codegenTestX64_adhoc(divs64, 'f', expected + `
neg %${result}` + mov_rdx_to_rax
);
}
}
function IsPowerOfTwo(x) {
x = BigInt(x);
if (x < 0) {
x = -x;
}
return x && (x & (x - 1n)) === 0n;
}
// Unsigned 64-bit division with constants.
const i64_div_u = [
// Division by zero.
{
divisor: 0,
expected: `ud2
mov %rdx, %rax`,
},
// Power of two divisor
{
divisor: 1,
expected: `mov %rdi, %rcx
mov %rcx, %rax`,
},
{
divisor: 2,
expected: `mov %rdi, %rcx
mov %rcx, %rax
shr \\$0x01, %rax`,
},
{
divisor: 4,
expected: `mov %rdi, %rcx
mov %rcx, %rax
shr \\$0x02, %rax`,
},
{
divisor: 0x1_0000_0000,
expected: `mov %rdi, %rcx
mov %rcx, %rax
shr \\$0x20, %rax`,
},
// Other divisors.
{
divisor: 3,
expected: `mov \\$-0x5555555555555555, %rax
mul %rdi
shr \\$0x01, %rdx
mov %rdx, %rax`,
},
{
divisor: 5,
expected: `mov \\$-0x3333333333333333, %rax
mul %rdi
shr \\$0x02, %rdx
mov %rdx, %rax`,
},
{
divisor: 7,
expected: `mov \\$0x2492492492492493, %rax
mul %rdi
mov %rdi, %rax
sub %rdx, %rax
shr \\$0x01, %rax
add %rax, %rdx
shr \\$0x02, %rdx
mov %rdx, %rax`,
},
{
divisor: 9,
expected: `mov \\$-0x1C71C71C71C71C71, %rax
mul %rdi
shr \\$0x03, %rdx
mov %rdx, %rax`,
},
// Special case: Zero shift amount.
{
divisor: 274177,
expected: `mov \\$0x3D30F19CD101, %rax
mul %rdi
mov %rdx, %rax`,
},
];
for (let {divisor, expected} of i64_div_u) {
let divu64 =
`(module
(func (export "f") (param i64) (result i64)
(i64.div_u (local.get 0) (i64.const ${divisor}))))`
codegenTestX64_adhoc(divu64, 'f', expected);
}
//////////////
// Signed 32-bit remainder with constants.
const i32_rem_s = [
// Division by zero.
{
divisor: 0,
expected: `ud2`,
},
// Power of two divisor
{
divisor: 1,
expected: `mov %edi, %ecx
mov %ecx, %eax
xor %eax, %eax`,
},
{
divisor: 2,
expected: `mov %edi, %ecx
mov %ecx, %eax
test %eax, %eax
js 0x${HEX}+
and \\$0x01, %eax
jmp 0x${HEX}+
neg %eax
and \\$0x01, %eax
neg %eax`,
},
{
divisor: 4,
expected: `mov %edi, %ecx
mov %ecx, %eax
test %eax, %eax
js 0x${HEX}+
and \\$0x03, %eax
jmp 0x${HEX}+
neg %eax
and \\$0x03, %eax
neg %eax`,
},
{
divisor: 0x100,
expected: `mov %edi, %ecx
mov %ecx, %eax
test %eax, %eax
js 0x${HEX}+
movzx %al, %eax
jmp 0x${HEX}+
neg %eax
movzx %al, %eax
neg %eax`,
},
{
divisor: 0x10000,
expected: `mov %edi, %ecx
mov %ecx, %eax
test %eax, %eax
js 0x${HEX}+
movzx %ax, %eax
jmp 0x${HEX}+
neg %eax
movzx %ax, %eax
neg %eax`,
},
{
divisor: 0x8000_0000,
expected: `mov %edi, %ecx
mov %ecx, %eax
test %eax, %eax
js 0x${HEX}+
and \\$0x7FFFFFFF, %eax
jmp 0x${HEX}+
neg %eax
and \\$0x7FFFFFFF, %eax
neg %eax`,
},
];
for (let {divisor, expected} of i32_rem_s) {
let rems32 =
`(module
(func (export "f") (param i32) (result i32)
(i32.rem_s (local.get 0) (i32.const ${divisor}))))`
codegenTestX64_adhoc(rems32, 'f', expected);
// Test negative divisors, too.
if (divisor > 0) {
let rems32 =
`(module
(func (export "f") (param i32) (result i32)
(i32.rem_s (local.get 0) (i32.const -${divisor}))))`
codegenTestX64_adhoc(rems32, 'f', expected);
}
}
// Unigned 32-bit remainder with constants.
const u32_rem_s = [
// Division by zero.
{
divisor: 0,
expected: `ud2`,
},
// Power of two divisor
{
divisor: 1,
expected: `mov %edi, %ecx
mov %ecx, %eax
xor %eax, %eax`,
},
{
divisor: 2,
expected: `mov %edi, %ecx
mov %ecx, %eax
and \\$0x01, %eax`,
},
{
divisor: 4,
expected: `mov %edi, %ecx
mov %ecx, %eax
and \\$0x03, %eax`,
},
{
divisor: 0x100,
expected: `mov %edi, %ecx
mov %ecx, %eax
movzx %al, %eax`,
},
{
divisor: 0x10000,
expected: `mov %edi, %ecx
mov %ecx, %eax
movzx %ax, %eax`,
},
{
divisor: 0x8000_0000,
expected: `mov %edi, %ecx
mov %ecx, %eax
and \\$0x7FFFFFFF, %eax`,
},
];
for (let {divisor, expected} of u32_rem_s) {
let remu32 =
`(module
(func (export "f") (param i32) (result i32)
(i32.rem_u (local.get 0) (i32.const ${divisor}))))`
codegenTestX64_adhoc(remu32, 'f', expected);
}
// Signed 64-bit remainder with constants.
const i64_rem_s = [
// Division by zero.
{
divisor: 0,
expected: `ud2`,
},
// Power of two divisor
{
divisor: 1,
expected: `mov %rdi, %rcx
mov %rcx, %rax
xor %eax, %eax`,
},
{
divisor: 2,
expected: `mov %rdi, %rcx
mov %rcx, %rax
test %rax, %rax
js 0x${HEX}+
and \\$0x01, %eax
jmp 0x${HEX}+
neg %rax
and \\$0x01, %eax
neg %rax`,
},
{
divisor: 4,
expected: `mov %rdi, %rcx
mov %rcx, %rax
test %rax, %rax
js 0x${HEX}+
and \\$0x03, %eax
jmp 0x${HEX}+
neg %rax
and \\$0x03, %eax
neg %rax`,
},
{
divisor: 0x100,
expected: `mov %rdi, %rcx
mov %rcx, %rax
test %rax, %rax
js 0x${HEX}+
movzx %al, %eax
jmp 0x${HEX}+
neg %rax
movzx %al, %eax
neg %rax`,
},
{
divisor: 0x10000,
expected: `mov %rdi, %rcx
mov %rcx, %rax
test %rax, %rax
js 0x${HEX}+
movzx %ax, %eax
jmp 0x${HEX}+
neg %rax
movzx %ax, %eax
neg %rax`,
},
{
divisor: 0x8000_0000,
expected: `mov %rdi, %rcx
mov %rcx, %rax
test %rax, %rax
js 0x${HEX}+
and \\$0x7FFFFFFF, %eax
jmp 0x${HEX}+
neg %rax
and \\$0x7FFFFFFF, %eax
neg %rax`,
},
{
divisor: 0x1_0000_0000,
expected: `mov %rdi, %rcx
mov %rcx, %rax
test %rax, %rax
js 0x${HEX}+
mov %eax, %eax
jmp 0x${HEX}+
neg %rax
mov %eax, %eax
neg %rax`,
},
{
divisor: 0x8000_0000_0000_0000n,
expected: `mov %rdi, %rcx
mov %rcx, %rax
test %rax, %rax
js 0x${HEX}+
mov \\$0x7FFFFFFFFFFFFFFF, %r11
and %r11, %rax
jmp 0x${HEX}+
neg %rax
mov \\$0x7FFFFFFFFFFFFFFF, %r11
and %r11, %rax
neg %rax`,
},
];
for (let {divisor, expected} of i64_rem_s) {
let rems64 =
`(module
(func (export "f") (param i64) (result i64)
(i64.rem_s (local.get 0) (i64.const ${divisor}))))`
codegenTestX64_adhoc(rems64, 'f', expected);
// Test negative divisors, too.
if (divisor > 0) {
let rems64 =
`(module
(func (export "f") (param i64) (result i64)
(i64.rem_s (local.get 0) (i64.const -${divisor}))))`
codegenTestX64_adhoc(rems64, 'f', expected);
}
}
// Unsigned 64-bit remainder with constants.
const i64_rem_u = [
// Division by zero.
{
divisor: 0,
expected: `ud2`,
},
// Power of two divisor
{
divisor: 1,
expected: `mov %rdi, %rcx
mov %rcx, %rax
xor %eax, %eax`,
},
{
divisor: 2,
expected: `mov %rdi, %rcx
mov %rcx, %rax
and \\$0x01, %eax`,
},
{
divisor: 4,
expected: `mov %rdi, %rcx
mov %rcx, %rax
and \\$0x03, %eax`,
},
{
divisor: 0x100,
expected: `mov %rdi, %rcx
mov %rcx, %rax
movzx %al, %eax`,
},
{
divisor: 0x10000,
expected: `mov %rdi, %rcx
mov %rcx, %rax
movzx %ax, %eax`,
},
{
divisor: 0x8000_0000,
expected: `mov %rdi, %rcx
mov %rcx, %rax
and \\$0x7FFFFFFF, %eax`,
},
{
divisor: 0x1_0000_0000,
expected: `mov %rdi, %rcx
mov %rcx, %rax
mov %eax, %eax`,
},
{
divisor: 0x8000_0000_0000_0000n,
expected: `mov %rdi, %rcx
mov %rcx, %rax
mov \\$0x7FFFFFFFFFFFFFFF, %r11
and %r11, %rax`,
},
];
for (let {divisor, expected} of i64_rem_u) {
let remu64 =
`(module
(func (export "f") (param i64) (result i64)
(i64.rem_u (local.get 0) (i64.const ${divisor}))))`
codegenTestX64_adhoc(remu64, 'f', expected);
}