commit 304c3faa920dfec0faa3b9c92b3ab145b9197b2c
parent 78054ddcf46cd0744dd2c18152a9be98ccf4514e
Author: André Bargull <andre.bargull@gmail.com>
Date: Sat, 15 Nov 2025 10:15:24 +0000
Bug 1998457 - Part 6: Add codegen tests for division/remainder with constants. r=spidermonkey-reviewers,iain
Add codegen tests to ensure division and modulus by constants generate the
expected code sequences.
Differential Revision: https://phabricator.services.mozilla.com/D271442
Diffstat:
2 files changed, 1493 insertions(+), 0 deletions(-)
diff --git a/js/src/jit-test/tests/wasm/binop-divrem-with-constant-arm64-ion-codegen.js b/js/src/jit-test/tests/wasm/binop-divrem-with-constant-arm64-ion-codegen.js
@@ -0,0 +1,774 @@
+// |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);
+}
diff --git a/js/src/jit-test/tests/wasm/binop-divrem-with-constant-x64-ion-codegen.js b/js/src/jit-test/tests/wasm/binop-divrem-with-constant-x64-ion-codegen.js
@@ -0,0 +1,719 @@
+// |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);
+}