Source code
Revision control
Copy as Markdown
Other Tools
// |jit-test| skip-if: !hasDisassembler() || wasmCompileMode() != "ion" || !getBuildConfiguration("arm64"); include:codegen-arm64-test.js
const WasmTrapIns = `dcps0 \\{#0x0\\} \\(Wasm Trap\\)`;
// Signed 32-bit division with constants.
const i32_div_s = [
// Division by zero.
{
divisor: 0,
expected: `mov w2, w0
mov w1, w2
${WasmTrapIns}`,
},
// Power of two divisor
{
divisor: 1,
expected: `mov w2, w0
mov w1, w2
mov w0, w1`,
},
{
divisor: 2,
expected: `mov w2, w0
mov w1, w2
lsr w0, w1, #31
add w0, w0, w1
asr w0, w0, #1`,
},
{
divisor: 4,
expected: `mov w2, w0
mov w1, w2
asr w0, w1, #31
lsr w0, w0, #30
add w0, w0, w1
asr w0, w0, #2`,
},
// Division by -1 needs an overflow check.
{
divisor: -1,
expected: `mov w2, w0
mov w1, w2
negs w0, w1
b.vc #\\+0x8 \\(addr 0x${HEX}+\\)
${WasmTrapIns}`,
},
// Other divisors.
{
divisor: 3,
expected: `mov w2, w0
mov w1, w2
mov w16, #0x5556
movk w16, #0x5555, lsl #16
smull x0, w16, w1
asr x0, x0, #32
sub w0, w0, w1, asr #31`,
},
{
divisor: 5,
expected: `mov w2, w0
mov w1, w2
mov w16, #0x6667
movk w16, #0x6666, lsl #16
smull x0, w16, w1
asr x0, x0, #33
sub w0, w0, w1, asr #31`,
},
{
divisor: 7,
expected: `mov w2, w0
mov w1, w2
mov w16, #0x2493
movk w16, #0x9249, lsl #16
lsl x0, x1, #32
smaddl x0, w16, w1, x0
asr x0, x0, #34
sub w0, w0, w1, asr #31`,
},
{
divisor: 9,
expected: `mov w2, w0
mov w1, w2
mov w16, #0x8e39
movk w16, #0x38e3, lsl #16
smull x0, w16, w1
asr x0, x0, #33
sub w0, w0, w1, asr #31`,
},
];
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}))))`
codegenTestARM64_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}))))`
codegenTestARM64_adhoc(divs32, 'f', expected + `
neg w0, w0`
);
}
}
// Unsigned 32-bit division with constants.
const i32_div_u = [
// Division by zero.
{
divisor: 0,
expected: `mov w2, w0
mov w1, w2
${WasmTrapIns}`,
},
// Power of two divisor
{
divisor: 1,
expected: `mov w2, w0
mov w1, w2
mov w0, w1`,
},
{
divisor: 2,
expected: `mov w2, w0
mov w1, w2
lsr w0, w1, #1`,
},
{
divisor: 4,
expected: `mov w2, w0
mov w1, w2
lsr w0, w1, #2`,
},
// Other divisors.
{
divisor: 3,
expected: `mov w2, w0
mov w1, w2
mov w16, #0xaaab
movk w16, #0xaaaa, lsl #16
umull x0, w16, w1
lsr x0, x0, #33`,
},
{
divisor: 5,
expected: `mov w2, w0
mov w1, w2
mov w16, #0xcccd
movk w16, #0xcccc, lsl #16
umull x0, w16, w1
lsr x0, x0, #34`,
},
{
divisor: 7,
expected: `mov w2, w0
mov w1, w2
mov w16, #0x4925
movk w16, #0x2492, lsl #16
umull x0, w16, w1
add x0, x1, x0, lsr #32
lsr x0, x0, #3`,
},
{
divisor: 9,
expected: `mov w2, w0
mov w1, w2
mov w16, #0x8e39
movk w16, #0x38e3, lsl #16
umull x0, w16, w1
lsr x0, x0, #33`,
},
// Special case: Zero (additional) shift amount.
{
divisor: 641,
expected: `mov w2, w0
mov w1, w2
mov w16, #0x3d81
movk w16, #0x66, lsl #16
umull x0, w16, w1
lsr x0, x0, #32`,
},
];
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}))))`
codegenTestARM64_adhoc(divu32, 'f', expected);
}
// Signed 64-bit division with constants.
const i64_div_s = [
// Division by zero.
{
divisor: 0,
expected: `mov x2, x0
mov x1, x2
${WasmTrapIns}`,
},
// Power of two divisor
{
divisor: 1,
expected: `mov x2, x0
mov x1, x2
mov x0, x1`,
},
{
divisor: 2,
expected: `mov x2, x0
mov x1, x2
lsr x0, x1, #63
add x0, x0, x1
asr x0, x0, #1`,
},
{
divisor: 4,
expected: `mov x2, x0
mov x1, x2
asr x0, x1, #63
lsr x0, x0, #62
add x0, x0, x1
asr x0, x0, #2`,
},
{
divisor: 0x1_0000_0000,
expected: `mov x2, x0
mov x1, x2
asr x0, x1, #63
lsr x0, x0, #32
add x0, x0, x1
asr x0, x0, #32`,
},
// Division by -1 needs an overflow check.
{
divisor: -1,
expected: `mov x2, x0
mov x1, x2
negs x0, x1
b.vc #\\+0x8 \\(addr 0x${HEX}+\\)
${WasmTrapIns}`,
},
// Other divisors.
{
divisor: 3,
expected: `mov x2, x0
mov x1, x2
mov x16, #0x5556
movk x16, #0x5555, lsl #16
movk x16, #0x5555, lsl #32
movk x16, #0x5555, lsl #48
smulh x0, x1, x16
sbfx x0, x0, #0, #64
sub x0, x0, x1, asr #63`,
},
{
divisor: 5,
expected: `mov x2, x0
mov x1, x2
mov x16, #0x6667
movk x16, #0x6666, lsl #16
movk x16, #0x6666, lsl #32
movk x16, #0x6666, lsl #48
smulh x0, x1, x16
asr x0, x0, #1
sub x0, x0, x1, asr #63`,
},
{
divisor: 7,
expected: `mov x2, x0
mov x1, x2
mov x16, #0x4925
movk x16, #0x2492, lsl #16
movk x16, #0x9249, lsl #32
movk x16, #0x4924, lsl #48
smulh x0, x1, x16
asr x0, x0, #1
sub x0, x0, x1, asr #63`,
},
{
divisor: 9,
expected: `mov x2, x0
mov x1, x2
mov x16, #0x1c72
movk x16, #0x71c7, lsl #16
movk x16, #0xc71c, lsl #32
movk x16, #0x1c71, lsl #48
smulh x0, x1, x16
sbfx x0, x0, #0, #64
sub x0, x0, x1, asr #63`,
},
];
for (let {divisor, expected} of i64_div_s) {
let divs64 =
`(module
(func (export "f") (param i64) (result i64)
(i64.div_s (local.get 0) (i64.const ${divisor}))))`
codegenTestARM64_adhoc(divs64, 'f', expected);
// 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}))))`
codegenTestARM64_adhoc(divs64, 'f', expected + `
neg x0, x0`);
}
}
// Unsigned 64-bit division with constants.
const i64_div_u = [
// Division by zero.
{
divisor: 0,
expected: `mov x2, x0
mov x1, x2
${WasmTrapIns}`,
},
// Power of two divisor
{
divisor: 1,
expected: `mov x2, x0
mov x1, x2
mov x0, x1`,
},
{
divisor: 2,
expected: `mov x2, x0
mov x1, x2
lsr x0, x1, #1`,
},
{
divisor: 4,
expected: `mov x2, x0
mov x1, x2
lsr x0, x1, #2`,
},
{
divisor: 0x1_0000_0000,
expected: `mov x2, x0
mov x1, x2
lsr x0, x1, #32`,
},
// Other divisors.
{
divisor: 3,
expected: `mov x2, x0
mov x1, x2
mov x16, #0xaaab
movk x16, #0xaaaa, lsl #16
movk x16, #0xaaaa, lsl #32
movk x16, #0xaaaa, lsl #48
umulh x0, x1, x16
lsr x0, x0, #1`,
},
{
divisor: 5,
expected: `mov x2, x0
mov x1, x2
mov x16, #0xcccd
movk x16, #0xcccc, lsl #16
movk x16, #0xcccc, lsl #32
movk x16, #0xcccc, lsl #48
umulh x0, x1, x16
lsr x0, x0, #2`,
},
{
divisor: 7,
expected: `mov x2, x0
mov x1, x2
mov x16, #0x2493
movk x16, #0x9249, lsl #16
movk x16, #0x4924, lsl #32
movk x16, #0x2492, lsl #48
umulh x0, x1, x16
sub x16, x1, x0
add x0, x0, x16, lsr #1
lsr x0, x0, #2`,
},
{
divisor: 9,
expected: `mov x2, x0
mov x1, x2
mov x16, #0xe38f
movk x16, #0x8e38, lsl #16
movk x16, #0x38e3, lsl #32
movk x16, #0xe38e, lsl #48
umulh x0, x1, x16
lsr x0, x0, #3`,
},
// Special case: Zero shift amount.
{
divisor: 274177,
expected: `mov x2, x0
mov x1, x2
mov x16, #0xd101
movk x16, #0xf19c, lsl #16
movk x16, #0x3d30, lsl #32
umulh x0, x1, x16
lsr x0, x0, #0`,
},
];
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}))))`
codegenTestARM64_adhoc(divu64, 'f', expected);
}
//////////////
// Signed 32-bit remainder with constants.
const i32_rem_s = [
// Division by zero.
{
divisor: 0,
expected: `mov w2, w0
mov w1, w2
${WasmTrapIns}`,
},
// Power of two divisor
{
divisor: 1,
expected: `mov w2, w0
mov w1, w2
mov w0, wzr`,
},
{
divisor: 2,
expected: `mov w2, w0
mov w1, w2
tst w1, w1
b.mi #\\+0xc \\(addr 0x${HEX}+\\)
and w0, w1, #0x1
b #\\+0x10 \\(addr 0x${HEX}+\\)
neg w0, w1
and w0, w0, #0x1
neg w0, w0`,
},
{
divisor: 4,
expected: `mov w2, w0
mov w1, w2
tst w1, w1
b.mi #\\+0xc \\(addr 0x${HEX}+\\)
and w0, w1, #0x3
b #\\+0x10 \\(addr 0x${HEX}+\\)
neg w0, w1
and w0, w0, #0x3
neg w0, w0`,
},
{
divisor: 0x100,
expected: `mov w2, w0
mov w1, w2
tst w1, w1
b.mi #\\+0xc \\(addr 0x${HEX}+\\)
and w0, w1, #0xff
b #\\+0x10 \\(addr 0x${HEX}+\\)
neg w0, w1
and w0, w0, #0xff
neg w0, w0`,
},
{
divisor: 0x10000,
expected: `mov w2, w0
mov w1, w2
tst w1, w1
b.mi #\\+0xc \\(addr 0x${HEX}+\\)
and w0, w1, #0xffff
b #\\+0x10 \\(addr 0x${HEX}+\\)
neg w0, w1
and w0, w0, #0xffff
neg w0, w0`,
},
{
divisor: 0x8000_0000,
expected: `mov w2, w0
mov w1, w2
tst w1, w1
b.mi #\\+0xc \\(addr 0x${HEX}+\\)
and w0, w1, #0x7fffffff
b #\\+0x10 \\(addr 0x${HEX}+\\)
neg w0, w1
and w0, w0, #0x7fffffff
neg w0, w0`,
},
];
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}))))`
codegenTestARM64_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}))))`
codegenTestARM64_adhoc(rems32, 'f', expected);
}
}
// Unigned 32-bit remainder with constants.
const u32_rem_s = [
// Division by zero.
{
divisor: 0,
expected: `mov w2, w0
mov w1, w2
${WasmTrapIns}`,
},
// Power of two divisor
{
divisor: 1,
expected: `mov w2, w0
mov w1, w2
mov w0, wzr`,
},
{
divisor: 2,
expected: `mov w2, w0
mov w1, w2
and w0, w1, #0x1`,
},
{
divisor: 4,
expected: `mov w2, w0
mov w1, w2
and w0, w1, #0x3`,
},
{
divisor: 0x100,
expected: `mov w2, w0
mov w1, w2
and w0, w1, #0xff`,
},
{
divisor: 0x10000,
expected: `mov w2, w0
mov w1, w2
and w0, w1, #0xffff`,
},
{
divisor: 0x8000_0000,
expected: `mov w2, w0
mov w1, w2
and w0, w1, #0x7fffffff`,
},
];
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}))))`
codegenTestARM64_adhoc(remu32, 'f', expected);
}
// Signed 64-bit remainder with constants.
const i64_rem_s = [
// Division by zero.
{
divisor: 0,
expected: `mov x2, x0
mov x1, x2
${WasmTrapIns}`,
},
// Power of two divisor
{
divisor: 1,
expected: `mov x2, x0
mov x1, x2
mov x0, xzr`,
},
{
divisor: 2,
expected: `mov x2, x0
mov x1, x2
tst x1, x1
b.mi #\\+0xc \\(addr 0x${HEX}+\\)
and x0, x1, #0x1
b #\\+0x10 \\(addr 0x${HEX}+\\)
neg x0, x1
and x0, x0, #0x1
neg x0, x0`,
},
{
divisor: 4,
expected: `mov x2, x0
mov x1, x2
tst x1, x1
b.mi #\\+0xc \\(addr 0x${HEX}+\\)
and x0, x1, #0x3
b #\\+0x10 \\(addr 0x${HEX}+\\)
neg x0, x1
and x0, x0, #0x3
neg x0, x0`,
},
{
divisor: 0x100,
expected: `mov x2, x0
mov x1, x2
tst x1, x1
b.mi #\\+0xc \\(addr 0x${HEX}+\\)
and x0, x1, #0xff
b #\\+0x10 \\(addr 0x${HEX}+\\)
neg x0, x1
and x0, x0, #0xff
neg x0, x0`,
},
{
divisor: 0x10000,
expected: `mov x2, x0
mov x1, x2
tst x1, x1
b.mi #\\+0xc \\(addr 0x${HEX}+\\)
and x0, x1, #0xffff
b #\\+0x10 \\(addr 0x${HEX}+\\)
neg x0, x1
and x0, x0, #0xffff
neg x0, x0`,
},
{
divisor: 0x8000_0000,
expected: `mov x2, x0
mov x1, x2
tst x1, x1
b.mi #\\+0xc \\(addr 0x${HEX}+\\)
and x0, x1, #0x7fffffff
b #\\+0x10 \\(addr 0x${HEX}+\\)
neg x0, x1
and x0, x0, #0x7fffffff
neg x0, x0`,
},
{
divisor: 0x1_0000_0000,
expected: `mov x2, x0
mov x1, x2
tst x1, x1
b.mi #\\+0xc \\(addr 0x${HEX}+\\)
mov w0, w1
b #\\+0x10 \\(addr 0x${HEX}+\\)
neg x0, x1
mov w0, w0
neg x0, x0`,
},
{
divisor: 0x8000_0000_0000_0000n,
expected: `mov x2, x0
mov x1, x2
tst x1, x1
b.mi #\\+0xc \\(addr 0x${HEX}+\\)
and x0, x1, #0x7fffffffffffffff
b #\\+0x10 \\(addr 0x${HEX}+\\)
neg x0, x1
and x0, x0, #0x7fffffffffffffff
neg x0, x0`,
},
];
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}))))`
codegenTestARM64_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}))))`
codegenTestARM64_adhoc(rems64, 'f', expected);
}
}
// Unsigned 64-bit remainder with constants.
const i64_rem_u = [
// Division by zero.
{
divisor: 0,
expected: `mov x2, x0
mov x1, x2
${WasmTrapIns}`,
},
// Power of two divisor
{
divisor: 1,
expected: `mov x2, x0
mov x1, x2
mov x0, xzr`,
},
{
divisor: 2,
expected: `mov x2, x0
mov x1, x2
and x0, x1, #0x1`,
},
{
divisor: 4,
expected: `mov x2, x0
mov x1, x2
and x0, x1, #0x3`,
},
{
divisor: 0x100,
expected: `mov x2, x0
mov x1, x2
and x0, x1, #0xff`,
},
{
divisor: 0x10000,
expected: `mov x2, x0
mov x1, x2
and x0, x1, #0xffff`,
},
{
divisor: 0x8000_0000,
expected: `mov x2, x0
mov x1, x2
and x0, x1, #0x7fffffff`,
},
{
divisor: 0x1_0000_0000,
expected: `mov x2, x0
mov x1, x2
mov w0, w1`,
},
{
divisor: 0x8000_0000_0000_0000n,
expected: `mov x2, x0
mov x1, x2
and x0, x1, #0x7fffffffffffffff`,
},
];
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}))))`
codegenTestARM64_adhoc(remu64, 'f', expected);
}