commit 3c4c4ac0ef31b40093c3b2d02dae2db2c5ec4f7d
parent fed32f6e6ec748efb052efdf7d8a1bc66aa68843
Author: Cristina Horotan <chorotan@mozilla.com>
Date: Mon, 10 Nov 2025 18:21:12 +0200
Revert "Bug 1998457, Bug 1998451, Bug 1998452, Bug 1998161 - Part 6: Add codegen tests for division/remainder with constants. r=spidermonkey-reviewers,iain" for causing SM failures on wasm.js
This reverts commit 6c4047cbb453260868cf6879dbd50cbf0bb871eb.
Revert "Bug 1998457 - Part 5: Don't generate unnecessary code for modulus by one. r=spidermonkey-reviewers,iain"
This reverts commit d48bfd57f35248fecc074c5cb20080ccf1085d65.
Revert "Bug 1998457 - Part 4: Optimise int64 divison and modulus with constants in arm64. r=spidermonkey-reviewers,iain"
This reverts commit 7768107fde9612324a6d1300c3592c1389198264.
Revert "Bug 1998457 - Part 3: Optimise int64 divison and modulus with constants in x64. r=spidermonkey-reviewers,iain"
This reverts commit 09e6a576a304ef061aac062e67b5b776653440b6.
Revert "Bug 1998457 - Part 2: Add imulq and umulq. r=spidermonkey-reviewers,iain"
This reverts commit 81a2dd4de1abc2b3fdfb87a2bb509cd7e32142ed.
Revert "Bug 1998457 - Part 1: Extend ReciprocalMulConstants for Int64 division. r=spidermonkey-reviewers,iain"
This reverts commit 34de8212a70815d504d03d80ef1b411e0bb9f881.
Revert "Bug 1998451: Improve performance of codegen tests. r=bvisness"
This reverts commit 1e220e42dc45ebb2161bba9fd8fda308e64ee3e4.
Revert "Bug 1998452: Avoid REX prefix for And with unsigned 32-bit immediate. r=spidermonkey-reviewers,iain"
This reverts commit 8e2181952db0848c3c58b2da236ffe2c772cc632.
Revert "Bug 1998161 - Part 4: Merge shifts for division by constants for x64. r=iain"
This reverts commit 7559329c7d447caf529e9b3745d40d53668e5b9b.
Revert "Bug 1998161 - Part 3: Remove register constraints for div/mod with constants on x64. r=spidermonkey-reviewers,iain"
This reverts commit de336ac8ca77b5fe259109d21772f25fca44d48d.
Revert "Bug 1998161 - Part 2: Use register allocator to place lhs operand for div/mod into eax. r=spidermonkey-reviewers,iain"
This reverts commit bc54bc3927d782af08d4f84a73dc417d99b52343.
Revert "Bug 1998161 - Part 1: Port arm64 changes to x86/x64. r=spidermonkey-reviewers,iain"
This reverts commit 92f6d9ca04e5c9e48c3c9b1eed3b2bdb2615be72.
Diffstat:
27 files changed, 620 insertions(+), 3124 deletions(-)
diff --git a/js/src/jit-test/lib/adhoc-multiplatform-test.js b/js/src/jit-test/lib/adhoc-multiplatform-test.js
@@ -94,7 +94,7 @@ const archOptions =
ldr x29, \\[sp\\]`
},
arm: {
- encoding: `${HEX}{8}\\s+${HEX}{8}`,
+ encoding: `${HEX}{8} ${HEX}{8}`,
// The move from r9 to fp is writing the callee's wasm instance into
// the frame for debug checks -- see WasmFrame.h.
prefix: `str fp, \\[sp, #-4\\]!
@@ -223,17 +223,37 @@ function codegenTestMultiplatform_adhoc(module_text, export_name,
if (!options.no_suffix) {
expected = expected + '\n' + suffix;
}
- expected = fixlines(expected);
+ if (genArm) {
+ // For obscure reasons, the arm(32) disassembler prints the
+ // instruction word twice. Rather than forcing all expected lines to
+ // do the same, we detect any line starting with 8 hex digits followed
+ // by a space, and duplicate them so as to match the
+ // disassembler's output.
+ let newExpected = "";
+ let pattern = /^[0-9a-fA-F]{8} /;
+ for (line of expected.split(/\n+/)) {
+ // Remove whitespace at the start of the line. This could happen
+ // for continuation lines in backtick-style expected strings.
+ while (line.match(/^\s/)) {
+ line = line.slice(1);
+ }
+ if (line.match(pattern)) {
+ line = line.slice(0,9) + line;
+ }
+ newExpected = newExpected + line + "\n";
+ }
+ expected = newExpected;
+ }
+ expected = fixlines(expected, encoding);
// Compile the test case and collect disassembly output.
let ins = wasmEvalText(module_text, {}, options.features);
if (options.instanceBox)
options.instanceBox.value = ins;
let output = wasmDis(ins.exports[export_name], {tier:"ion", asString:true});
- let output_simple = stripencoding(output, encoding);
// Check for success, print diagnostics
- let output_matches_expected = output_simple.match(new RegExp(expected)) != null;
+ let output_matches_expected = output.match(new RegExp(expected)) != null;
if (!output_matches_expected) {
print("---- adhoc-tier1-test.js: TEST FAILED ----");
}
diff --git a/js/src/jit-test/lib/codegen-arm64-test.js b/js/src/jit-test/lib/codegen-arm64-test.js
@@ -33,10 +33,9 @@ function codegenTestARM64_adhoc(module_text, export_name, expected, options = {}
expected = arm64_prefix + '\n' + expected;
if (!options.no_suffix)
expected = expected + '\n' + arm64_suffix;
- expected = fixlines(expected);
+ expected = fixlines(expected, `${HEX}{8}`);
- const output_simple = stripencoding(output, `${HEX}{8}`);
- const output_matches_expected = output_simple.match(new RegExp(expected)) != null;
+ const output_matches_expected = output.match(new RegExp(expected)) != null;
if (!output_matches_expected) {
print("---- codegen-arm64-test.js: TEST FAILED ----");
}
diff --git a/js/src/jit-test/lib/codegen-test-common.js b/js/src/jit-test/lib/codegen-test-common.js
@@ -11,28 +11,21 @@ function wrap(options, funcs) {
return `(module ${funcs})`;
}
-function fixlines(s) {
+function fixlines(s, insEncoding) {
return s.split(/\n+/)
.map(strip)
.filter(x => x.length > 0)
+ .map(x => `(?:0x)?${HEX}+ ${insEncoding} ${x}`)
.map(spaces)
.join('\n');
}
-function stripencoding(s, insEncoding) {
- var encoding = RegExp(`^(?:0x)?${HEX}+\\s+${insEncoding}\\s+(.*)$`);
- return s.split('\n')
- .map(x => x.match(encoding)?.[1] ?? x)
- .join('\n');
-}
-
function strip(s) {
- var start = 0, end = s.length;
- while (start < s.length && isspace(s.charAt(start)))
- start++;
- while (end > start && isspace(s.charAt(end - 1)))
- end--;
- return s.substring(start, end);
+ while (s.length > 0 && isspace(s.charAt(0)))
+ s = s.substring(1);
+ while (s.length > 0 && isspace(s.charAt(s.length-1)))
+ s = s.substring(0, s.length-1);
+ return s;
}
function striplines(s) {
diff --git a/js/src/jit-test/lib/codegen-x64-test.js b/js/src/jit-test/lib/codegen-x64-test.js
@@ -163,10 +163,9 @@ function codegenTestX64_adhoc(module_text, export_name, expected, options = {})
if (!options.no_suffix)
expected = expected + '\n' + x64_suffix;
const expected_pretty = striplines(expected);
- expected = fixlines(expected);
+ expected = fixlines(expected, `(?:${HEX}{2} )*`);
- const output_simple = stripencoding(output, `(?:${HEX}{2} )*`);
- const success = output_simple.match(new RegExp(expected)) != null;
+ const success = output.match(new RegExp(expected)) != null;
if (options.log || !success) {
print("Module text:")
print(module_text);
diff --git a/js/src/jit-test/lib/codegen-x86-test.js b/js/src/jit-test/lib/codegen-x86-test.js
@@ -57,10 +57,9 @@ function codegenTestX86_adhoc(module_text, export_name, expected, options = {})
expected = x86_prefix + '\n' + expected;
if (!options.no_suffix)
expected = expected + '\n' + x86_suffix;
- expected = fixlines(expected);
+ expected = fixlines(expected, `(?:${HEX}{2} )*`);
- const output_simple = stripencoding(output, `(?:${HEX}{2} )*`);
- const output_matches_expected = output_simple.match(new RegExp(expected)) != null;
+ const output_matches_expected = output.match(new RegExp(expected)) != null;
if (!output_matches_expected) {
print("---- codegen-x86-test.js: TEST FAILED ----");
}
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
@@ -1,772 +0,0 @@
-// |jit-test| skip-if: !hasDisassembler() || wasmCompileMode() != "ion" || !getBuildConfiguration("arm64"); include:codegen-arm64-test.js
-
-// Signed 32-bit division with constants.
-const i32_div_s = [
- // Division by zero.
- {
- divisor: 0,
- expected: `mov w2, w0
- mov w1, w2
- unimplemented \\(Exception\\)`,
- },
-
- // 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}+\\)
- unimplemented \\(Exception\\)`,
- },
-
- // 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
- unimplemented \\(Exception\\)`,
- },
-
- // 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
- unimplemented \\(Exception\\)`,
- },
-
- // 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}+\\)
- unimplemented \\(Exception\\)`,
- },
-
- // 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
- unimplemented \\(Exception\\)`,
- },
-
- // 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
- unimplemented \\(Exception\\)`,
- },
-
- // 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
- unimplemented \\(Exception\\)`,
- },
-
- // 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
- unimplemented \\(Exception\\)`,
- },
-
- // 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
- unimplemented \\(Exception\\)`,
- },
-
- // 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
@@ -1,719 +0,0 @@
-// |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);
-}
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
@@ -383,7 +383,7 @@ for ( [pAnyCmp, pAnySel, cmpArgL, cmovArgL ] of
)`,
'f',
// On Linux we have an extra move
- (getBuildConfiguration("windows") ? '' : 'mov %r.+, %r.+\n') +
+ (getBuildConfiguration("windows") ? '' : '48 89 .. mov %r.+, %r.+\n') +
// 'q*' because the disassembler shows 'q' only for the memory cases
`mov %r.+, %r.+
cmpq* ${cmpArgL}, %r.+
diff --git a/js/src/jit/LIR.h b/js/src/jit/LIR.h
@@ -2169,10 +2169,13 @@ AnyRegister LAllocation::toAnyRegister() const {
} // namespace js
#include "jit/shared/LIR-shared.h"
-#if defined(JS_CODEGEN_X86)
-# include "jit/x86/LIR-x86.h"
-#elif defined(JS_CODEGEN_X64)
-# include "jit/x64/LIR-x64.h"
+#if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
+# if defined(JS_CODEGEN_X86)
+# include "jit/x86/LIR-x86.h"
+# elif defined(JS_CODEGEN_X64)
+# include "jit/x64/LIR-x64.h"
+# endif
+# include "jit/x86-shared/LIR-x86-shared.h"
#elif defined(JS_CODEGEN_ARM)
# include "jit/arm/LIR-arm.h"
#elif defined(JS_CODEGEN_ARM64)
diff --git a/js/src/jit/LIROps.yaml b/js/src/jit/LIROps.yaml
@@ -4160,60 +4160,6 @@
defer_init: true
#endif
-#if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
-- name: DivConstantI
- result_type: WordSized
- operands:
- numerator: WordSized
- arguments:
- denominator: int32_t
- num_temps: 1
- mir_op: Div
-
-- name: ModConstantI
- result_type: WordSized
- operands:
- numerator: WordSized
- arguments:
- denominator: int32_t
- num_temps: 1
- mir_op: Mod
-
-- name: UDivConstant
- result_type: WordSized
- operands:
- numerator: WordSized
- arguments:
- denominator: uint32_t
- num_temps: 1
- mir_op: Div
-
-- name: UModConstant
- result_type: WordSized
- operands:
- numerator: WordSized
- arguments:
- denominator: uint32_t
- num_temps: 1
- mir_op: Mod
-
-- name: UDiv
- result_type: WordSized
- operands:
- lhs: WordSized
- rhs: WordSized
- num_temps: 1
- mir_op: Div
-
-- name: UMod
- result_type: WordSized
- operands:
- lhs: WordSized
- rhs: WordSized
- num_temps: 1
- mir_op: Mod
-#endif
-
#ifdef JS_CODEGEN_X86
- name: BoxFloatingPoint
result_type: BoxedValue
@@ -4230,6 +4176,15 @@
- name: UDivOrModI64
gen_boilerplate: false
+- name: DivOrModConstantI
+ gen_boilerplate: false
+
+- name: UDivOrMod
+ gen_boilerplate: false
+
+- name: UDivOrModConstant
+ gen_boilerplate: false
+
- name: WasmTruncateToInt64
result_type: Int64
operands:
@@ -4290,91 +4245,20 @@
#endif
#ifdef JS_CODEGEN_X64
-- name: DivI64
- result_type: Int64
- operands:
- lhs: WordSized
- rhs: WordSized
- num_temps: 1
- mir_op: Div
-
-- name: ModI64
- result_type: Int64
- operands:
- lhs: WordSized
- rhs: WordSized
- num_temps: 1
- mir_op: Mod
-
-- name: UDivI64
- result_type: Int64
- operands:
- lhs: WordSized
- rhs: WordSized
- num_temps: 1
- mir_op: Div
-
-- name: UModI64
- result_type: Int64
- operands:
- lhs: WordSized
- rhs: WordSized
- num_temps: 1
- mir_op: Mod
-
-- name: DivPowTwoI64
- result_type: WordSized
- operands:
- numerator: WordSized
- numeratorCopy: WordSized
- arguments:
- shift: int32_t
- negativeDivisor: bool
- mir_op: Div
-
-- name: ModPowTwoI64
- result_type: WordSized
- operands:
- input: WordSized
- arguments:
- shift: int32_t
- mir_op: Mod
+- name: DivOrModI64
+ gen_boilerplate: false
-- name: DivConstantI64
- result_type: WordSized
- operands:
- numerator: WordSized
- arguments:
- denominator: int64_t
- num_temps: 1
- mir_op: Div
+- name: UDivOrModI64
+ gen_boilerplate: false
-- name: ModConstantI64
- result_type: WordSized
- operands:
- numerator: WordSized
- arguments:
- denominator: int64_t
- num_temps: 1
- mir_op: Mod
+- name: DivOrModConstantI
+ gen_boilerplate: false
-- name: UDivConstantI64
- result_type: WordSized
- operands:
- numerator: WordSized
- arguments:
- denominator: uint64_t
- num_temps: 1
- mir_op: Div
+- name: UDivOrMod
+ gen_boilerplate: false
-- name: UModConstantI64
- result_type: WordSized
- operands:
- numerator: WordSized
- arguments:
- denominator: uint64_t
- num_temps: 1
- mir_op: Mod
+- name: UDivOrModConstant
+ gen_boilerplate: false
- name: WasmTruncateToInt64
result_type: Int64
@@ -4520,28 +4404,28 @@
#ifdef JS_CODEGEN_ARM64
- name: DivI64
- result_type: WordSized
+ result_type: Int64
operands:
lhs: WordSized
rhs: WordSized
mir_op: Div
- name: ModI64
- result_type: WordSized
+ result_type: Int64
operands:
lhs: WordSized
rhs: WordSized
mir_op: Mod
- name: UDivI64
- result_type: WordSized
+ result_type: Int64
operands:
lhs: WordSized
rhs: WordSized
mir_op: Div
- name: UModI64
- result_type: WordSized
+ result_type: Int64
operands:
lhs: WordSized
rhs: WordSized
@@ -4579,55 +4463,6 @@
denominator: uint32_t
mir_op: Mod
-- name: DivPowTwoI64
- result_type: WordSized
- operands:
- numerator: WordSized
- arguments:
- shift: int32_t
- negativeDivisor: bool
- mir_op: Div
-
-- name: ModPowTwoI64
- result_type: WordSized
- operands:
- input: WordSized
- arguments:
- shift: int32_t
- mir_op: Mod
-
-- name: DivConstantI64
- result_type: WordSized
- operands:
- numerator: WordSized
- arguments:
- denominator: int64_t
- mir_op: Div
-
-- name: ModConstantI64
- result_type: WordSized
- operands:
- numerator: WordSized
- arguments:
- denominator: int64_t
- mir_op: Mod
-
-- name: UDivConstantI64
- result_type: WordSized
- operands:
- numerator: WordSized
- arguments:
- denominator: uint64_t
- mir_op: Div
-
-- name: UModConstantI64
- result_type: WordSized
- operands:
- numerator: WordSized
- arguments:
- denominator: uint64_t
- mir_op: Mod
-
- name: UDiv
result_type: WordSized
operands:
diff --git a/js/src/jit/MacroAssembler.cpp b/js/src/jit/MacroAssembler.cpp
@@ -2062,8 +2062,7 @@ void MacroAssembler::loadInt32ToStringWithBase(
// "Unsigned Division by 7" for the case when |rmc.multiplier| exceeds
// UINT32_MAX and we need to adjust the shift amount.
- auto rmc = ReciprocalMulConstants::computeUnsignedDivisionConstants(
- uint32_t(base));
+ auto rmc = ReciprocalMulConstants::computeUnsignedDivisionConstants(base);
// We first compute |q = (M * n) >> 32), where M = rmc.multiplier.
mulHighUnsigned32(Imm32(rmc.multiplier), input, scratch1);
diff --git a/js/src/jit/ReciprocalMulConstants.cpp b/js/src/jit/ReciprocalMulConstants.cpp
@@ -8,37 +8,14 @@
#include "mozilla/Assertions.h"
-#include <limits>
-
using namespace js::jit;
-template <typename UintT>
-struct TwiceLarger;
-
-template <>
-struct TwiceLarger<uint32_t> {
- using Type = uint64_t;
-};
-
-#if defined(__SIZEOF_INT128__)
-template <>
-struct TwiceLarger<uint64_t> {
- using Type = __uint128_t;
-};
-#endif
-
-template <typename DivConstants, typename UintT>
-static auto ComputeDivisionConstants(UintT d, int maxLog) {
- using UintT_Twice = typename TwiceLarger<UintT>::Type;
-
- MOZ_ASSERT(maxLog >= 2 && maxLog <= std::numeric_limits<UintT>::digits);
-
+ReciprocalMulConstants ReciprocalMulConstants::computeDivisionConstants(
+ uint32_t d, int maxLog) {
+ MOZ_ASSERT(maxLog >= 2 && maxLog <= 32);
// In what follows, 0 < d < 2^maxLog and d is not a power of 2.
- MOZ_ASSERT(d < (UintT_Twice(1) << maxLog) && !mozilla::IsPowerOfTwo(d));
+ MOZ_ASSERT(d < (uint64_t(1) << maxLog) && (d & (d - 1)) != 0);
- // NOTE: The following explanation assumes T = uint32_t, but
- // T = uint64_t works similar.
- //
// Speeding up division by non power-of-2 constants is possible by
// calculating, during compilation, a value M such that high-order
// bits of M*n correspond to the result of the division of n by d.
@@ -99,43 +76,19 @@ static auto ComputeDivisionConstants(UintT d, int maxLog) {
// compute it as (2^p-1) % d + 1, where 2^p-1 can then be computed
// without overflow as UINT64_MAX >> (64-p).
- static constexpr auto UINT_BITS = std::numeric_limits<UintT>::digits;
- static constexpr auto UINT_TWICE_BITS =
- std::numeric_limits<UintT_Twice>::digits;
- static constexpr auto UINT_TWICE_MAX =
- std::numeric_limits<UintT_Twice>::max();
-
- // We now compute the least p >= UINT_BITS with the property above...
- int32_t p = UINT_BITS;
- while (true) {
- auto u = (UintT_Twice(1) << (p - maxLog));
- auto v = (UINT_TWICE_MAX >> (UINT_TWICE_BITS - p));
- if (u + (v % d) + 1 < d) {
- p++;
- } else {
- break;
- }
+ // We now compute the least p >= 32 with the property above...
+ int32_t p = 32;
+ while ((uint64_t(1) << (p - maxLog)) + (UINT64_MAX >> (64 - p)) % d + 1 < d) {
+ p++;
}
// ...and the corresponding M. For either the signed (L=31) or the
// unsigned (L=32) case, this value can be too large (cf. item a).
// Codegen can still multiply by M by multiplying by (M - 2^L) and
// adjusting the value afterwards, if this is the case.
- DivConstants rmc;
- rmc.multiplier = (UINT_TWICE_MAX >> (UINT_TWICE_BITS - p)) / d + 1;
- rmc.shiftAmount = p - UINT_BITS;
+ ReciprocalMulConstants rmc;
+ rmc.multiplier = (UINT64_MAX >> (64 - p)) / d + 1;
+ rmc.shiftAmount = p - 32;
return rmc;
}
-
-ReciprocalMulConstants::Div32Constants
-ReciprocalMulConstants::computeDivisionConstants(uint32_t d, int maxLog) {
- return ComputeDivisionConstants<Div32Constants>(d, maxLog);
-}
-
-#if defined(__SIZEOF_INT128__)
-ReciprocalMulConstants::Div64Constants
-ReciprocalMulConstants::computeDivisionConstants(uint64_t d, int maxLog) {
- return ComputeDivisionConstants<Div64Constants>(d, maxLog);
-}
-#endif
diff --git a/js/src/jit/ReciprocalMulConstants.h b/js/src/jit/ReciprocalMulConstants.h
@@ -14,40 +14,20 @@
namespace js::jit {
struct ReciprocalMulConstants {
- template <typename Multiplier, typename ShiftAmount>
- struct DivConstants {
- Multiplier multiplier;
- ShiftAmount shiftAmount;
- };
+ int64_t multiplier;
+ int32_t shiftAmount;
- using Div32Constants = DivConstants<int64_t, int32_t>;
-
- static auto computeSignedDivisionConstants(int32_t d) {
+ static ReciprocalMulConstants computeSignedDivisionConstants(int32_t d) {
return computeDivisionConstants(mozilla::Abs(d), 31);
}
- static auto computeUnsignedDivisionConstants(uint32_t d) {
+ static ReciprocalMulConstants computeUnsignedDivisionConstants(uint32_t d) {
return computeDivisionConstants(d, 32);
}
-#if defined(__SIZEOF_INT128__)
- using Div64Constants = DivConstants<__int128_t, int32_t>;
-
- static auto computeSignedDivisionConstants(int64_t d) {
- return computeDivisionConstants(mozilla::Abs(d), 63);
- }
-
- static auto computeUnsignedDivisionConstants(uint64_t d) {
- return computeDivisionConstants(d, 64);
- }
-#endif
-
private:
- static Div32Constants computeDivisionConstants(uint32_t d, int maxLog);
-
-#if defined(__SIZEOF_INT128__)
- static Div64Constants computeDivisionConstants(uint64_t d, int maxLog);
-#endif
+ static ReciprocalMulConstants computeDivisionConstants(uint32_t d,
+ int maxLog);
};
} // namespace js::jit
diff --git a/js/src/jit/arm64/CodeGenerator-arm64.cpp b/js/src/jit/arm64/CodeGenerator-arm64.cpp
@@ -506,61 +506,6 @@ void CodeGenerator::visitDivPowTwoI(LDivPowTwoI* ins) {
}
}
-void CodeGenerator::visitDivPowTwoI64(LDivPowTwoI64* ins) {
- ARMRegister numerator64 = toXRegister(ins->numerator());
- ARMRegister output64 = toXRegister(ins->output());
-
- int32_t shift = ins->shift();
- bool negativeDivisor = ins->negativeDivisor();
- MDiv* mir = ins->mir();
-
- if (shift) {
- if (mir->isUnsigned()) {
- // shift right
- masm.Lsr(output64, numerator64, shift);
- } else {
- ARMRegister temp64 = numerator64;
- // Adjust the value so that shifting produces a correctly
- // rounded result when the numerator is negative. See 10-1
- // "Signed Division by a Known Power of 2" in Henry
- // S. Warren, Jr.'s Hacker's Delight.
- if (mir->canBeNegativeDividend()) {
- if (shift > 1) {
- // Copy the sign bit of the numerator. (= (2^64 - 1) or 0)
- masm.Asr(output64, numerator64, 63);
- temp64 = output64;
- }
- // Divide by 2^(64 - shift)
- // i.e. (= (2^64 - 1) / 2^(64 - shift) or 0)
- // i.e. (= (2^shift - 1) or 0)
- masm.Lsr(output64, temp64, 64 - shift);
- // If signed, make any 1 bit below the shifted bits to bubble up, such
- // that once shifted the value would be rounded towards 0.
- masm.Add(output64, output64, numerator64);
- temp64 = output64;
- }
- masm.Asr(output64, temp64, shift);
-
- if (negativeDivisor) {
- masm.Neg(output64, output64);
- }
- }
- return;
- }
-
- if (negativeDivisor) {
- // INT64_MIN / -1 overflows.
- Label ok;
- masm.Negs(output64, numerator64);
- masm.branch(Assembler::NoOverflow, &ok);
- masm.wasmTrap(wasm::Trap::IntegerOverflow, mir->trapSiteDesc());
- masm.bind(&ok);
- } else {
- // Copy the result.
- masm.Mov(output64, numerator64);
- }
-}
-
template <class LDivOrMod>
static void DivideWithConstant(MacroAssembler& masm, LDivOrMod* ins) {
ARMRegister lhs32 = toWRegister(ins->numerator());
@@ -608,7 +553,8 @@ static void DivideWithConstant(MacroAssembler& masm, LDivOrMod* ins) {
// We'll subtract -1 instead of adding 1, because (n < 0 ? -1 : 0) can be
// computed with just a sign-extending shift of 31 bits.
if (mir->canBeNegativeDividend()) {
- masm.Sub(output32, output32, Operand(lhs32, vixl::ASR, 31));
+ masm.Asr(const32, lhs32, 31);
+ masm.Sub(output32, output32, const32);
}
// After this, output32 contains the correct truncated division result.
@@ -754,119 +700,6 @@ void CodeGenerator::visitUDivConstant(LUDivConstant* ins) {
}
}
-template <class LDivOrMod>
-static void Divide64WithConstant(MacroAssembler& masm, LDivOrMod* ins) {
- ARMRegister lhs64 = toXRegister(ins->numerator());
- ARMRegister output64 = toXRegister(ins->output());
- int64_t d = ins->denominator();
-
- vixl::UseScratchRegisterScope temps(&masm.asVIXL());
- ARMRegister const64 = temps.AcquireX();
-
- // The absolute value of the denominator isn't a power of 2.
- MOZ_ASSERT(!mozilla::IsPowerOfTwo(mozilla::Abs(d)));
-
- auto* mir = ins->mir();
-
- // We will first divide by Abs(d), and negate the answer if d is negative.
- // If desired, this can be avoided by generalizing computeDivisionConstants.
- auto rmc = ReciprocalMulConstants::computeSignedDivisionConstants(d);
-
- // We first compute (M * n) >> 64, where M = rmc.multiplier.
- masm.Mov(const64, uint64_t(rmc.multiplier));
- masm.Smulh(output64, lhs64, const64);
- if (rmc.multiplier > __int128_t(INT64_MAX)) {
- MOZ_ASSERT(rmc.multiplier < (__int128_t(1) << 64));
-
- // We actually computed output = ((int64_t(M) * n) >> 64) instead. Since
- // (M * n) >> 64 is the same as (output + n), we can correct for the
- // overflow. (output + n) can't overflow, as n and output have opposite
- // signs because int64_t(M) is negative.
- masm.Add(output64, output64, lhs64);
- }
-
- // (M * n) >> (64 + shift) is the truncated division answer if n is
- // non-negative, as proved in the comments of computeDivisionConstants. We
- // must add 1 later if n is negative to get the right answer in all cases.
- masm.Asr(output64, output64, rmc.shiftAmount);
-
- // We'll subtract -1 instead of adding 1, because (n < 0 ? -1 : 0) can be
- // computed with just a sign-extending shift of 63 bits.
- if (mir->canBeNegativeDividend()) {
- masm.Sub(output64, output64, Operand(lhs64, vixl::ASR, 63));
- }
-
- // After this, output64 contains the correct truncated division result.
- if (d < 0) {
- masm.Neg(output64, output64);
- }
-}
-
-void CodeGenerator::visitDivConstantI64(LDivConstantI64* ins) {
- int64_t d = ins->denominator();
-
- if (d == 0) {
- masm.wasmTrap(wasm::Trap::IntegerDivideByZero, ins->mir()->trapSiteDesc());
- return;
- }
-
- // Compute the truncated division result.
- Divide64WithConstant(masm, ins);
-}
-
-template <class LUDivOrUMod>
-static void UnsignedDivide64WithConstant(MacroAssembler& masm,
- LUDivOrUMod* ins) {
- ARMRegister lhs64 = toXRegister(ins->numerator());
- ARMRegister output64 = toXRegister(ins->output());
- uint64_t d = ins->denominator();
-
- vixl::UseScratchRegisterScope temps(&masm.asVIXL());
- ARMRegister const64 = temps.AcquireX();
-
- // The denominator isn't a power of 2 (see LDivPowTwoI).
- MOZ_ASSERT(!mozilla::IsPowerOfTwo(d));
-
- auto rmc = ReciprocalMulConstants::computeUnsignedDivisionConstants(d);
-
- // We first compute (M * n) >> 64, where M = rmc.multiplier.
- masm.Mov(const64, uint64_t(rmc.multiplier));
- masm.Umulh(output64, lhs64, const64);
- if (rmc.multiplier > __int128_t(UINT64_MAX)) {
- // M >= 2^64 and shift == 0 is impossible, as d >= 2 implies that
- // ((M * n) >> (64 + shift)) >= n > floor(n/d) whenever n >= d,
- // contradicting the proof of correctness in computeDivisionConstants.
- MOZ_ASSERT(rmc.shiftAmount > 0);
- MOZ_ASSERT(rmc.multiplier < (__int128_t(1) << 65));
-
- // We actually computed output = ((uint64_t(M) * n) >> 64) instead. Since
- // (M * n) >> (64 + shift) is the same as (output + n) >> shift, we can
- // correct for the overflow. This case is a bit trickier than the signed
- // case, though, as the (output + n) addition itself can overflow; however,
- // note that
- // (output + n) >> shift == (((n - output) >> 1) + output) >> (shift - 1),
- // which is overflow-free. See Hacker's Delight, section 10-8 for details.
-
- masm.Sub(const64, lhs64, output64);
- masm.Add(output64, output64, Operand(const64, vixl::LSR, 1));
- masm.Lsr(output64, output64, rmc.shiftAmount - 1);
- } else {
- masm.Lsr(output64, output64, rmc.shiftAmount);
- }
-}
-
-void CodeGenerator::visitUDivConstantI64(LUDivConstantI64* ins) {
- uint64_t d = ins->denominator();
-
- if (d == 0) {
- masm.wasmTrap(wasm::Trap::IntegerDivideByZero, ins->mir()->trapSiteDesc());
- return;
- }
-
- // Compute the truncated division result.
- UnsignedDivide64WithConstant(masm, ins);
-}
-
void CodeGenerator::visitModI(LModI* ins) {
Register lhs = ToRegister(ins->lhs());
Register rhs = ToRegister(ins->rhs());
@@ -923,14 +756,6 @@ void CodeGenerator::visitModPowTwoI(LModPowTwoI* ins) {
bool canBeNegative =
!ins->mir()->isUnsigned() && ins->mir()->canBeNegativeDividend();
- if (shift == 0) {
- if (canBeNegative && !ins->mir()->isTruncated()) {
- bailoutTest32(Assembler::Signed, lhs, lhs, ins->snapshot());
- }
- masm.Mov(outw, wzr);
- return;
- }
-
Label negative;
if (canBeNegative) {
// Switch based on sign of the lhs.
@@ -962,54 +787,6 @@ void CodeGenerator::visitModPowTwoI(LModPowTwoI* ins) {
}
}
-void CodeGenerator::visitModPowTwoI64(LModPowTwoI64* ins) {
- Register lhs = ToRegister(ins->input());
- ARMRegister lhs64 = toXRegister(ins->input());
- ARMRegister out64 = toXRegister(ins->output());
-
- int32_t shift = ins->shift();
- bool canBeNegative =
- !ins->mir()->isUnsigned() && ins->mir()->canBeNegativeDividend();
-
- if (shift == 0) {
- masm.Mov(out64, xzr);
- return;
- }
-
- auto clearHighBits = [&](ARMRegister reg) {
- switch (shift) {
- case 32:
- masm.Mov(out64.W(), reg.W());
- break;
- default:
- masm.And(out64, reg, Operand((uint64_t(1) << shift) - 1));
- break;
- }
- };
-
- Label negative;
- if (canBeNegative) {
- // Switch based on sign of the lhs.
- // Positive numbers are just a bitmask.
- masm.branchTestPtr(Assembler::Signed, lhs, lhs, &negative);
- }
-
- clearHighBits(lhs64);
-
- if (canBeNegative) {
- Label done;
- masm.jump(&done);
-
- // Negative numbers need a negate, bitmask, negate.
- masm.bind(&negative);
- masm.Neg(out64, Operand(lhs64));
- clearHighBits(out64);
- masm.Neg(out64, Operand(out64));
-
- masm.bind(&done);
- }
-}
-
void CodeGenerator::visitModConstantI(LModConstantI* ins) {
Register lhs = ToRegister(ins->numerator());
ARMRegister lhs32 = toWRegister(ins->numerator());
@@ -1089,54 +866,6 @@ void CodeGenerator::visitUModConstant(LUModConstant* ins) {
}
}
-void CodeGenerator::visitModConstantI64(LModConstantI64* ins) {
- ARMRegister lhs64 = toXRegister(ins->numerator());
- ARMRegister output64 = toXRegister(ins->output());
-
- int64_t d = ins->denominator();
-
- if (d == 0) {
- masm.wasmTrap(wasm::Trap::IntegerDivideByZero, ins->mir()->trapSiteDesc());
- return;
- }
-
- // Compute the truncated division result in output64.
- Divide64WithConstant(masm, ins);
-
- // Compute the remainder: output = lhs - (output * rhs).
- {
- vixl::UseScratchRegisterScope temps(&masm.asVIXL());
- ARMRegister rhs64 = temps.AcquireX();
-
- masm.Mov(rhs64, d);
- masm.Msub(output64, output64, rhs64, lhs64);
- }
-}
-
-void CodeGenerator::visitUModConstantI64(LUModConstantI64* ins) {
- ARMRegister lhs64 = toXRegister(ins->numerator());
- ARMRegister output64 = toXRegister(ins->output());
-
- uint64_t d = ins->denominator();
-
- if (d == 0) {
- masm.wasmTrap(wasm::Trap::IntegerDivideByZero, ins->mir()->trapSiteDesc());
- return;
- }
-
- // Compute the truncated division result in output64.
- UnsignedDivide64WithConstant(masm, ins);
-
- // Compute the remainder: output = lhs - (output * rhs).
- {
- vixl::UseScratchRegisterScope temps(&masm.asVIXL());
- ARMRegister rhs64 = temps.AcquireX();
-
- masm.Mov(rhs64, d);
- masm.Msub(output64, output64, rhs64, lhs64);
- }
-}
-
void CodeGeneratorARM64::emitBigIntPtrDiv(LBigIntPtrDiv* ins, Register dividend,
Register divisor, Register output) {
// Callers handle division by zero and integer overflow.
diff --git a/js/src/jit/arm64/Lowering-arm64.cpp b/js/src/jit/arm64/Lowering-arm64.cpp
@@ -294,98 +294,27 @@ void LIRGeneratorARM64::lowerModI(MMod* mod) {
}
void LIRGeneratorARM64::lowerDivI64(MDiv* div) {
- if (div->rhs()->isConstant()) {
- LAllocation lhs = useRegister(div->lhs());
- int64_t rhs = div->rhs()->toConstant()->toInt64();
-
- if (mozilla::IsPowerOfTwo(mozilla::Abs(rhs))) {
- int32_t shift = mozilla::FloorLog2(mozilla::Abs(rhs));
-
- auto* lir = new (alloc()) LDivPowTwoI64(lhs, shift, rhs < 0);
- define(lir, div);
- return;
- }
-
- auto* lir = new (alloc()) LDivConstantI64(lhs, rhs);
- define(lir, div);
- return;
- }
-
auto* lir = new (alloc())
LDivI64(useRegisterAtStart(div->lhs()), useRegisterAtStart(div->rhs()));
- define(lir, div);
+ defineInt64(lir, div);
}
void LIRGeneratorARM64::lowerModI64(MMod* mod) {
- LAllocation lhs = useRegister(mod->lhs());
-
- if (mod->rhs()->isConstant()) {
- int64_t rhs = mod->rhs()->toConstant()->toInt64();
-
- if (mozilla::IsPowerOfTwo(mozilla::Abs(rhs))) {
- int32_t shift = mozilla::FloorLog2(mozilla::Abs(rhs));
-
- auto* lir = new (alloc()) LModPowTwoI64(lhs, shift);
- define(lir, mod);
- return;
- }
-
- auto* lir = new (alloc()) LModConstantI64(lhs, rhs);
- define(lir, mod);
- return;
- }
-
- auto* lir = new (alloc()) LModI64(lhs, useRegister(mod->rhs()));
- define(lir, mod);
+ auto* lir =
+ new (alloc()) LModI64(useRegister(mod->lhs()), useRegister(mod->rhs()));
+ defineInt64(lir, mod);
}
void LIRGeneratorARM64::lowerUDivI64(MDiv* div) {
- if (div->rhs()->isConstant()) {
- LAllocation lhs = useRegister(div->lhs());
-
- // NOTE: the result of toInt64 is coerced to uint64_t.
- uint64_t rhs = div->rhs()->toConstant()->toInt64();
-
- if (mozilla::IsPowerOfTwo(rhs)) {
- int32_t shift = mozilla::FloorLog2(rhs);
-
- auto* lir = new (alloc()) LDivPowTwoI64(lhs, shift, false);
- define(lir, div);
- return;
- }
-
- auto* lir = new (alloc()) LUDivConstantI64(lhs, rhs);
- define(lir, div);
- return;
- }
-
auto* lir = new (alloc())
LUDivI64(useRegisterAtStart(div->lhs()), useRegisterAtStart(div->rhs()));
- define(lir, div);
+ defineInt64(lir, div);
}
void LIRGeneratorARM64::lowerUModI64(MMod* mod) {
- LAllocation lhs = useRegister(mod->lhs());
-
- if (mod->rhs()->isConstant()) {
- // NOTE: the result of toInt64 is coerced to uint64_t.
- uint64_t rhs = mod->rhs()->toConstant()->toInt64();
-
- if (mozilla::IsPowerOfTwo(rhs)) {
- int32_t shift = mozilla::FloorLog2(rhs);
-
- auto* lir = new (alloc()) LModPowTwoI64(lhs, shift);
- define(lir, mod);
- return;
- }
-
- auto* lir = new (alloc()) LUModConstantI64(lhs, rhs);
- define(lir, mod);
- return;
- }
-
- auto* lir = new (alloc()) LUModI64(lhs, useRegister(mod->rhs()));
- define(lir, mod);
+ auto* lir =
+ new (alloc()) LUModI64(useRegister(mod->lhs()), useRegister(mod->rhs()));
+ defineInt64(lir, mod);
}
void LIRGeneratorARM64::lowerWasmBuiltinDivI64(MWasmBuiltinDivI64* div) {
diff --git a/js/src/jit/x64/Assembler-x64.h b/js/src/jit/x64/Assembler-x64.h
@@ -962,12 +962,6 @@ class Assembler : public AssemblerX86Shared {
masm.popcntq_rr(src.encoding(), dest.encoding());
}
- void imulq(Register multiplier) {
- // Consumes rax as the other argument and clobbers rdx, as the result is in
- // rdx:rax.
- masm.imulq_r(multiplier.encoding());
- }
- void umulq(Register multiplier) { masm.mulq_r(multiplier.encoding()); }
void imulq(Imm32 imm, Register src, Register dest) {
masm.imulq_ir(imm.value, src.encoding(), dest.encoding());
}
diff --git a/js/src/jit/x64/BaseAssembler-x64.h b/js/src/jit/x64/BaseAssembler-x64.h
@@ -408,11 +408,6 @@ class BaseAssemblerX64 : public BaseAssembler {
m_formatter.twoByteOp64(OP2_IMUL_GvEv, src, dst);
}
- void imulq_r(RegisterID multiplier) {
- spew("imulq %s", GPReg64Name(multiplier));
- m_formatter.oneByteOp64(OP_GROUP3_Ev, multiplier, GROUP3_OP_IMUL);
- }
-
void imulq_mr(int32_t offset, RegisterID base, RegisterID dst) {
spew("imulq " MEM_ob ", %s", ADDR_ob(offset, base), GPReg64Name(dst));
m_formatter.twoByteOp64(OP2_IMUL_GvEv, offset, base, dst);
@@ -429,11 +424,6 @@ class BaseAssemblerX64 : public BaseAssembler {
}
}
- void mulq_r(RegisterID multiplier) {
- spew("mulq %s", GPReg64Name(multiplier));
- m_formatter.oneByteOp64(OP_GROUP3_Ev, multiplier, GROUP3_OP_MUL);
- }
-
void cqo() {
spew("cqo ");
m_formatter.oneByteOp64(OP_CDQ);
diff --git a/js/src/jit/x64/CodeGenerator-x64.cpp b/js/src/jit/x64/CodeGenerator-x64.cpp
@@ -12,7 +12,6 @@
#include "jit/CodeGenerator.h"
#include "jit/MIR-wasm.h"
#include "jit/MIR.h"
-#include "jit/ReciprocalMulConstants.h"
#include "js/ScalarType.h" // js::Scalar::Type
#include "jit/MacroAssembler-inl.h"
@@ -21,6 +20,8 @@
using namespace js;
using namespace js::jit;
+using mozilla::DebugOnly;
+
CodeGeneratorX64::CodeGeneratorX64(MIRGenerator* gen, LIRGraph* graph,
MacroAssembler* masm,
const wasm::CodeMetadata* wasmCodeMeta)
@@ -196,75 +197,42 @@ void CodeGenerator::visitMulI64(LMulI64* lir) {
}
}
-template <class LIR>
-static void TrapIfDivideByZero(MacroAssembler& masm, LIR* lir, Register rhs) {
- auto* mir = lir->mir();
- MOZ_ASSERT(mir->trapOnError());
-
- if (mir->canBeDivideByZero()) {
- Label nonZero;
- masm.branchTestPtr(Assembler::NonZero, rhs, rhs, &nonZero);
- masm.wasmTrap(wasm::Trap::IntegerDivideByZero, mir->trapSiteDesc());
- masm.bind(&nonZero);
- }
-}
-
-void CodeGenerator::visitDivI64(LDivI64* lir) {
- Register lhs = ToRegister(lir->lhs());
- Register rhs = ToRegister(lir->rhs());
-
- MOZ_ASSERT(lhs == rax);
- MOZ_ASSERT(rhs != rax);
- MOZ_ASSERT(rhs != rdx);
- MOZ_ASSERT(ToRegister(lir->output()) == rax);
- MOZ_ASSERT(ToRegister(lir->temp0()) == rdx);
-
- MDiv* mir = lir->mir();
-
- // Handle divide by zero.
- TrapIfDivideByZero(masm, lir, rhs);
-
- // Handle an integer overflow exception from INT64_MIN / -1.
- if (mir->canBeNegativeOverflow()) {
- Label notOverflow;
- masm.branchPtr(Assembler::NotEqual, lhs, ImmWord(INT64_MIN), ¬Overflow);
- masm.branchPtr(Assembler::NotEqual, rhs, ImmWord(-1), ¬Overflow);
- masm.wasmTrap(wasm::Trap::IntegerOverflow, mir->trapSiteDesc());
- masm.bind(¬Overflow);
- }
-
- // Sign extend the lhs into rdx to make rdx:rax.
- masm.cqo();
- masm.idivq(rhs);
-}
-
-void CodeGenerator::visitModI64(LModI64* lir) {
+void CodeGenerator::visitDivOrModI64(LDivOrModI64* lir) {
Register lhs = ToRegister(lir->lhs());
Register rhs = ToRegister(lir->rhs());
Register output = ToRegister(lir->output());
- MOZ_ASSERT(lhs == rax);
- MOZ_ASSERT(rhs != rax);
+ MOZ_ASSERT_IF(lhs != rhs, rhs != rax);
MOZ_ASSERT(rhs != rdx);
- MOZ_ASSERT(ToRegister(lir->output()) == rdx);
- MOZ_ASSERT(ToRegister(lir->temp0()) == rax);
-
- MMod* mir = lir->mir();
+ MOZ_ASSERT_IF(output == rax, ToRegister(lir->remainder()) == rdx);
+ MOZ_ASSERT_IF(output == rdx, ToRegister(lir->remainder()) == rax);
Label done;
+ // Put the lhs in rax.
+ if (lhs != rax) {
+ masm.mov(lhs, rax);
+ }
+
// Handle divide by zero.
- TrapIfDivideByZero(masm, lir, rhs);
+ if (lir->canBeDivideByZero()) {
+ Label nonZero;
+ masm.branchTestPtr(Assembler::NonZero, rhs, rhs, &nonZero);
+ masm.wasmTrap(wasm::Trap::IntegerDivideByZero, lir->trapSiteDesc());
+ masm.bind(&nonZero);
+ }
// Handle an integer overflow exception from INT64_MIN / -1.
- if (mir->canBeNegativeDividend()) {
+ if (lir->canBeNegativeOverflow()) {
Label notOverflow;
masm.branchPtr(Assembler::NotEqual, lhs, ImmWord(INT64_MIN), ¬Overflow);
masm.branchPtr(Assembler::NotEqual, rhs, ImmWord(-1), ¬Overflow);
- {
+ if (lir->mir()->isMod()) {
masm.xorl(output, output);
- masm.jump(&done);
+ } else {
+ masm.wasmTrap(wasm::Trap::IntegerOverflow, lir->trapSiteDesc());
}
+ masm.jump(&done);
masm.bind(¬Overflow);
}
@@ -275,331 +243,36 @@ void CodeGenerator::visitModI64(LModI64* lir) {
masm.bind(&done);
}
-void CodeGenerator::visitUDivI64(LUDivI64* lir) {
+void CodeGenerator::visitUDivOrModI64(LUDivOrModI64* lir) {
+ Register lhs = ToRegister(lir->lhs());
Register rhs = ToRegister(lir->rhs());
- MOZ_ASSERT(ToRegister(lir->lhs()) == rax);
- MOZ_ASSERT(rhs != rax);
+ DebugOnly<Register> output = ToRegister(lir->output());
+ MOZ_ASSERT_IF(lhs != rhs, rhs != rax);
MOZ_ASSERT(rhs != rdx);
- MOZ_ASSERT(ToRegister(lir->output()) == rax);
- MOZ_ASSERT(ToRegister(lir->temp0()) == rdx);
-
- // Prevent divide by zero.
- TrapIfDivideByZero(masm, lir, rhs);
+ MOZ_ASSERT_IF(output.value == rax, ToRegister(lir->remainder()) == rdx);
+ MOZ_ASSERT_IF(output.value == rdx, ToRegister(lir->remainder()) == rax);
- // Zero extend the lhs into rdx to make (rdx:rax).
- masm.xorl(rdx, rdx);
- masm.udivq(rhs);
-}
-
-void CodeGenerator::visitUModI64(LUModI64* lir) {
- Register rhs = ToRegister(lir->rhs());
+ // Put the lhs in rax.
+ if (lhs != rax) {
+ masm.mov(lhs, rax);
+ }
- MOZ_ASSERT(ToRegister(lir->lhs()) == rax);
- MOZ_ASSERT(rhs != rax);
- MOZ_ASSERT(rhs != rdx);
- MOZ_ASSERT(ToRegister(lir->output()) == rdx);
- MOZ_ASSERT(ToRegister(lir->temp0()) == rax);
+ Label done;
// Prevent divide by zero.
- TrapIfDivideByZero(masm, lir, rhs);
+ if (lir->canBeDivideByZero()) {
+ Label nonZero;
+ masm.branchTestPtr(Assembler::NonZero, rhs, rhs, &nonZero);
+ masm.wasmTrap(wasm::Trap::IntegerDivideByZero, lir->trapSiteDesc());
+ masm.bind(&nonZero);
+ }
// Zero extend the lhs into rdx to make (rdx:rax).
masm.xorl(rdx, rdx);
masm.udivq(rhs);
-}
-
-void CodeGenerator::visitDivPowTwoI64(LDivPowTwoI64* ins) {
- Register lhs = ToRegister(ins->numerator());
-
- int32_t shift = ins->shift();
- bool negativeDivisor = ins->negativeDivisor();
- MDiv* mir = ins->mir();
-
- // We use defineReuseInput so these should always be the same, which is
- // convenient since all of our instructions here are two-address.
- MOZ_ASSERT(lhs == ToRegister(ins->output()));
-
- // Unsigned division is just a right-shift.
- if (mir->isUnsigned()) {
- if (shift != 0) {
- masm.shrq(Imm32(shift), lhs);
- }
- return;
- }
-
- if (shift != 0) {
- // Adjust the value so that shifting produces a correctly rounded result
- // when the numerator is negative.
- // See 10-1 "Signed Division by a Known Power of 2" in Henry S. Warren,
- // Jr.'s Hacker's Delight.
- if (mir->canBeNegativeDividend()) {
- Register lhsCopy = ToRegister(ins->numeratorCopy());
- MOZ_ASSERT(lhsCopy != lhs);
- if (shift > 1) {
- // Copy the sign bit of the numerator. (= (2^63 - 1) or 0)
- masm.sarq(Imm32(63), lhs);
- }
- // Divide by 2^(64 - shift)
- // i.e. (= (2^64 - 1) / 2^(64 - shift) or 0)
- // i.e. (= (2^shift - 1) or 0)
- masm.shrq(Imm32(64 - shift), lhs);
- // If signed, make any 1 bit below the shifted bits to bubble up, such
- // that once shifted the value would be rounded towards 0.
- masm.addq(lhsCopy, lhs);
- }
- masm.sarq(Imm32(shift), lhs);
- }
-
- if (negativeDivisor) {
- masm.negq(lhs);
- }
-
- if (shift == 0 && negativeDivisor) {
- // INT64_MIN / -1 overflows.
- Label ok;
- masm.j(Assembler::NoOverflow, &ok);
- masm.wasmTrap(wasm::Trap::IntegerOverflow, mir->trapSiteDesc());
- masm.bind(&ok);
- }
-}
-
-void CodeGenerator::visitModPowTwoI64(LModPowTwoI64* ins) {
- Register64 lhs = Register64(ToRegister(ins->input()));
- int32_t shift = ins->shift();
- bool canBeNegative =
- !ins->mir()->isUnsigned() && ins->mir()->canBeNegativeDividend();
-
- MOZ_ASSERT(lhs.reg == ToRegister(ins->output()));
-
- if (shift == 0) {
- masm.xorl(lhs.reg, lhs.reg);
- return;
- }
-
- auto clearHighBits = [&]() {
- switch (shift) {
- case 8:
- masm.movzbl(lhs.reg, lhs.reg);
- break;
- case 16:
- masm.movzwl(lhs.reg, lhs.reg);
- break;
- case 32:
- masm.movl(lhs.reg, lhs.reg);
- break;
- default:
- masm.and64(Imm64((uint64_t(1) << shift) - 1), lhs);
- break;
- }
- };
-
- Label negative;
-
- if (canBeNegative) {
- // Switch based on sign of the lhs.
- // Positive numbers are just a bitmask
- masm.branchTest64(Assembler::Signed, lhs, lhs, &negative);
- }
-
- clearHighBits();
-
- if (canBeNegative) {
- Label done;
- masm.jump(&done);
-
- // Negative numbers need a negate, bitmask, negate
- masm.bind(&negative);
-
- // Unlike in the visitModI64 case, we are not computing the mod by means of
- // a division. Therefore, the divisor = -1 case isn't problematic (the andq
- // always returns 0, which is what we expect).
- //
- // The negq instruction overflows if lhs == INT64_MIN, but this is also not
- // a problem: shift is at most 63, and so the andq also always returns 0.
- masm.neg64(lhs);
- clearHighBits();
- masm.neg64(lhs);
-
- masm.bind(&done);
- }
-}
-
-template <class LDivOrMod>
-static void Divide64WithConstant(MacroAssembler& masm, LDivOrMod* ins) {
- Register lhs = ToRegister(ins->numerator());
- [[maybe_unused]] Register output = ToRegister(ins->output());
- [[maybe_unused]] Register temp = ToRegister(ins->temp0());
- int64_t d = ins->denominator();
-
- MOZ_ASSERT(lhs != rax && lhs != rdx);
- MOZ_ASSERT((output == rax && temp == rdx) || (output == rdx && temp == rax));
-
- // The absolute value of the denominator isn't a power of 2 (see LDivPowTwoI64
- // and LModPowTwoI64).
- MOZ_ASSERT(!mozilla::IsPowerOfTwo(mozilla::Abs(d)));
-
- auto* mir = ins->mir();
-
- // We will first divide by Abs(d), and negate the answer if d is negative.
- // If desired, this can be avoided by generalizing computeDivisionConstants.
- auto rmc = ReciprocalMulConstants::computeSignedDivisionConstants(d);
-
- // We first compute (M * n) >> 64, where M = rmc.multiplier.
- masm.movq(ImmWord(uint64_t(rmc.multiplier)), rax);
- masm.imulq(lhs);
- if (rmc.multiplier > __int128_t(INT64_MAX)) {
- MOZ_ASSERT(rmc.multiplier < (__int128_t(1) << 64));
-
- // We actually computed rdx = ((int64_t(M) * n) >> 64) instead. Since
- // (M * n) >> 64 is the same as (rdx + n), we can correct for the overflow.
- // (rdx + n) can't overflow, as n and rdx have opposite signs because
- // int64_t(M) is negative.
- masm.addq(lhs, rdx);
- }
- // (M * n) >> (64 + shift) is the truncated division answer if n is
- // non-negative, as proved in the comments of computeDivisionConstants. We
- // must add 1 later if n is negative to get the right answer in all cases.
- if (rmc.shiftAmount > 0) {
- masm.sarq(Imm32(rmc.shiftAmount), rdx);
- }
-
- // We'll subtract -1 instead of adding 1, because (n < 0 ? -1 : 0) can be
- // computed with just a sign-extending shift of 63 bits.
- if (mir->canBeNegativeDividend()) {
- masm.movq(lhs, rax);
- masm.sarq(Imm32(63), rax);
- masm.subq(rax, rdx);
- }
-
- // After this, rdx contains the correct truncated division result.
- if (d < 0) {
- masm.negq(rdx);
- }
-}
-
-void CodeGenerator::visitDivConstantI64(LDivConstantI64* ins) {
- int32_t d = ins->denominator();
-
- // This emits the division answer into rdx.
- MOZ_ASSERT(ToRegister(ins->output()) == rdx);
- MOZ_ASSERT(ToRegister(ins->temp0()) == rax);
- if (d == 0) {
- masm.wasmTrap(wasm::Trap::IntegerDivideByZero, ins->mir()->trapSiteDesc());
- return;
- }
-
- // Compute the truncated division result in rdx.
- Divide64WithConstant(masm, ins);
-}
-
-void CodeGenerator::visitModConstantI64(LModConstantI64* ins) {
- Register lhs = ToRegister(ins->numerator());
- int64_t d = ins->denominator();
-
- // This emits the modulus answer into rax.
- MOZ_ASSERT(ToRegister(ins->output()) == rax);
- MOZ_ASSERT(ToRegister(ins->temp0()) == rdx);
-
- if (d == 0) {
- masm.wasmTrap(wasm::Trap::IntegerDivideByZero, ins->mir()->trapSiteDesc());
- return;
- }
-
- // Compute the truncated division result in rdx.
- Divide64WithConstant(masm, ins);
-
- // Compute the remainder in |rax|: rax = lhs - d * rdx
- masm.mul64(Imm64(d), Register64(rdx));
- masm.movq(lhs, rax);
- masm.subq(rdx, rax);
-}
-
-template <class LUDivOrUMod>
-static void UnsignedDivide64WithConstant(MacroAssembler& masm,
- LUDivOrUMod* ins) {
- Register lhs = ToRegister(ins->numerator());
- [[maybe_unused]] Register output = ToRegister(ins->output());
- [[maybe_unused]] Register temp = ToRegister(ins->temp0());
- uint64_t d = ins->denominator();
-
- MOZ_ASSERT(lhs != rax && lhs != rdx);
- MOZ_ASSERT((output == rax && temp == rdx) || (output == rdx && temp == rax));
-
- // The denominator isn't a power of 2 (see LDivPowTwoI and LModPowTwoI).
- MOZ_ASSERT(!mozilla::IsPowerOfTwo(d));
-
- auto rmc = ReciprocalMulConstants::computeUnsignedDivisionConstants(d);
-
- // We first compute (M * n) >> 64, where M = rmc.multiplier.
- masm.movq(ImmWord(uint64_t(rmc.multiplier)), rax);
- masm.umulq(lhs);
- if (rmc.multiplier > __int128_t(UINT64_MAX)) {
- // M >= 2^64 and shift == 0 is impossible, as d >= 2 implies that
- // ((M * n) >> (64 + shift)) >= n > floor(n/d) whenever n >= d,
- // contradicting the proof of correctness in computeDivisionConstants.
- MOZ_ASSERT(rmc.shiftAmount > 0);
- MOZ_ASSERT(rmc.multiplier < (__int128_t(1) << 65));
-
- // We actually computed rdx = ((uint64_t(M) * n) >> 64) instead. Since
- // (M * n) >> (64 + shift) is the same as (rdx + n) >> shift, we can correct
- // for the overflow. This case is a bit trickier than the signed case,
- // though, as the (rdx + n) addition itself can overflow; however, note that
- // (rdx + n) >> shift == (((n - rdx) >> 1) + rdx) >> (shift - 1),
- // which is overflow-free. See Hacker's Delight, section 10-8 for details.
-
- // Compute (n - rdx) >> 1 into temp.
- masm.movq(lhs, rax);
- masm.subq(rdx, rax);
- masm.shrq(Imm32(1), rax);
-
- // Finish the computation.
- masm.addq(rax, rdx);
- masm.shrq(Imm32(rmc.shiftAmount - 1), rdx);
- } else {
- if (rmc.shiftAmount > 0) {
- masm.shrq(Imm32(rmc.shiftAmount), rdx);
- }
- }
-}
-
-void CodeGenerator::visitUDivConstantI64(LUDivConstantI64* ins) {
- uint64_t d = ins->denominator();
-
- // This emits the division answer into rdx.
- MOZ_ASSERT(ToRegister(ins->output()) == rdx);
- MOZ_ASSERT(ToRegister(ins->temp0()) == rax);
-
- if (d == 0) {
- masm.wasmTrap(wasm::Trap::IntegerDivideByZero, ins->mir()->trapSiteDesc());
- return;
- }
-
- // Compute the truncated division result in rdx.
- UnsignedDivide64WithConstant(masm, ins);
-}
-
-void CodeGenerator::visitUModConstantI64(LUModConstantI64* ins) {
- Register lhs = ToRegister(ins->numerator());
- uint64_t d = ins->denominator();
-
- // This emits the modulus answer into rax.
- MOZ_ASSERT(ToRegister(ins->output()) == rax);
- MOZ_ASSERT(ToRegister(ins->temp0()) == rdx);
-
- if (d == 0) {
- masm.wasmTrap(wasm::Trap::IntegerDivideByZero, ins->mir()->trapSiteDesc());
- return;
- }
-
- // Compute the truncated division result in rdx.
- UnsignedDivide64WithConstant(masm, ins);
-
- // Compute the remainder in |rax|: rax = lhs - d * rdx
- masm.mul64(Imm64(d), Register64(rdx));
- masm.movq(lhs, rax);
- masm.subq(rdx, rax);
+ masm.bind(&done);
}
void CodeGeneratorX64::emitBigIntPtrDiv(LBigIntPtrDiv* ins, Register dividend,
diff --git a/js/src/jit/x64/LIR-x64.h b/js/src/jit/x64/LIR-x64.h
@@ -28,6 +28,87 @@ class LUnbox : public LInstructionHelper<1, BOX_PIECES, 0> {
const char* extraName() const { return StringFromMIRType(mir()->type()); }
};
+class LDivOrModI64 : public LBinaryMath<1> {
+ public:
+ LIR_HEADER(DivOrModI64)
+
+ LDivOrModI64(const LAllocation& lhs, const LAllocation& rhs,
+ const LDefinition& temp)
+ : LBinaryMath(classOpcode) {
+ setOperand(0, lhs);
+ setOperand(1, rhs);
+ setTemp(0, temp);
+ }
+
+ const LDefinition* remainder() { return getTemp(0); }
+
+ MBinaryArithInstruction* mir() const {
+ MOZ_ASSERT(mir_->isDiv() || mir_->isMod());
+ return static_cast<MBinaryArithInstruction*>(mir_);
+ }
+ bool canBeDivideByZero() const {
+ if (mir_->isMod()) {
+ return mir_->toMod()->canBeDivideByZero();
+ }
+ return mir_->toDiv()->canBeDivideByZero();
+ }
+ bool canBeNegativeOverflow() const {
+ if (mir_->isMod()) {
+ return mir_->toMod()->canBeNegativeDividend();
+ }
+ return mir_->toDiv()->canBeNegativeOverflow();
+ }
+ const wasm::TrapSiteDesc& trapSiteDesc() const {
+ MOZ_ASSERT(mir_->isDiv() || mir_->isMod());
+ if (mir_->isMod()) {
+ return mir_->toMod()->trapSiteDesc();
+ }
+ return mir_->toDiv()->trapSiteDesc();
+ }
+};
+
+// This class performs a simple x86 'div', yielding either a quotient or
+// remainder depending on whether this instruction is defined to output
+// rax (quotient) or rdx (remainder).
+class LUDivOrModI64 : public LBinaryMath<1> {
+ public:
+ LIR_HEADER(UDivOrModI64);
+
+ LUDivOrModI64(const LAllocation& lhs, const LAllocation& rhs,
+ const LDefinition& temp)
+ : LBinaryMath(classOpcode) {
+ setOperand(0, lhs);
+ setOperand(1, rhs);
+ setTemp(0, temp);
+ }
+
+ const LDefinition* remainder() { return getTemp(0); }
+
+ const char* extraName() const {
+ return mir()->isTruncated() ? "Truncated" : nullptr;
+ }
+
+ MBinaryArithInstruction* mir() const {
+ MOZ_ASSERT(mir_->isDiv() || mir_->isMod());
+ return static_cast<MBinaryArithInstruction*>(mir_);
+ }
+
+ bool canBeDivideByZero() const {
+ if (mir_->isMod()) {
+ return mir_->toMod()->canBeDivideByZero();
+ }
+ return mir_->toDiv()->canBeDivideByZero();
+ }
+
+ const wasm::TrapSiteDesc& trapSiteDesc() const {
+ MOZ_ASSERT(mir_->isDiv() || mir_->isMod());
+ if (mir_->isMod()) {
+ return mir_->toMod()->trapSiteDesc();
+ }
+ return mir_->toDiv()->trapSiteDesc();
+ }
+};
+
} // namespace jit
} // namespace js
diff --git a/js/src/jit/x64/Lowering-x64.cpp b/js/src/jit/x64/Lowering-x64.cpp
@@ -7,7 +7,6 @@
#include "jit/x64/Lowering-x64.h"
#include "mozilla/CheckedInt.h"
-#include "mozilla/MathAlgorithms.h"
#include "jit/Lowering.h"
#include "jit/MIR-wasm.h"
@@ -527,120 +526,35 @@ void LIRGenerator::visitSubstr(MSubstr* ins) {
}
void LIRGeneratorX64::lowerDivI64(MDiv* div) {
- // Division instructions are slow. Division by constant denominators can be
- // rewritten to use other instructions.
- if (div->rhs()->isConstant()) {
- int64_t rhs = div->rhs()->toConstant()->toInt64();
-
- // Division by powers of two can be done by shifting, and division by
- // other numbers can be done by a reciprocal multiplication technique.
- if (mozilla::IsPowerOfTwo(mozilla::Abs(rhs))) {
- int32_t shift = mozilla::FloorLog2(mozilla::Abs(rhs));
- LAllocation lhs = useRegisterAtStart(div->lhs());
-
- // We have to round the result toward 0 when the remainder is non-zero.
- // This requires an extra register to round up/down when the left-hand
- // side is signed.
- LAllocation lhsCopy = div->canBeNegativeDividend()
- ? useRegister(div->lhs())
- : LAllocation();
-
- auto* lir = new (alloc()) LDivPowTwoI64(lhs, lhsCopy, shift, rhs < 0);
- defineReuseInput(lir, div, 0);
- return;
- }
-
- auto* lir = new (alloc())
- LDivConstantI64(useRegister(div->lhs()), tempFixed(rax), rhs);
- defineFixed(lir, div, LAllocation(AnyRegister(rdx)));
- return;
- }
+ LDivOrModI64* lir = new (alloc()) LDivOrModI64(
+ useRegister(div->lhs()), useRegister(div->rhs()), tempFixed(rdx));
+ defineInt64Fixed(lir, div, LInt64Allocation(LAllocation(AnyRegister(rax))));
+}
- auto* lir = new (alloc()) LDivI64(useFixedAtStart(div->lhs(), rax),
- useRegister(div->rhs()), tempFixed(rdx));
- defineFixed(lir, div, LAllocation(AnyRegister(rax)));
+void LIRGeneratorX64::lowerWasmBuiltinDivI64(MWasmBuiltinDivI64* div) {
+ MOZ_CRASH("We don't use runtime div for this architecture");
}
void LIRGeneratorX64::lowerModI64(MMod* mod) {
- if (mod->rhs()->isConstant()) {
- int64_t rhs = mod->rhs()->toConstant()->toInt64();
-
- if (mozilla::IsPowerOfTwo(mozilla::Abs(rhs))) {
- int32_t shift = mozilla::FloorLog2(mozilla::Abs(rhs));
-
- auto* lir =
- new (alloc()) LModPowTwoI64(useRegisterAtStart(mod->lhs()), shift);
- defineReuseInput(lir, mod, 0);
- return;
- }
-
- auto* lir = new (alloc())
- LModConstantI64(useRegister(mod->lhs()), tempFixed(rdx), rhs);
- defineFixed(lir, mod, LAllocation(AnyRegister(rax)));
- return;
- }
+ LDivOrModI64* lir = new (alloc()) LDivOrModI64(
+ useRegister(mod->lhs()), useRegister(mod->rhs()), tempFixed(rax));
+ defineInt64Fixed(lir, mod, LInt64Allocation(LAllocation(AnyRegister(rdx))));
+}
- auto* lir = new (alloc()) LModI64(useFixedAtStart(mod->lhs(), rax),
- useRegister(mod->rhs()), tempFixed(rax));
- defineFixed(lir, mod, LAllocation(AnyRegister(rdx)));
+void LIRGeneratorX64::lowerWasmBuiltinModI64(MWasmBuiltinModI64* mod) {
+ MOZ_CRASH("We don't use runtime mod for this architecture");
}
void LIRGeneratorX64::lowerUDivI64(MDiv* div) {
- if (div->rhs()->isConstant()) {
- // NOTE: the result of toInt64 is coerced to uint64_t.
- uint64_t rhs = div->rhs()->toConstant()->toInt64();
-
- if (mozilla::IsPowerOfTwo(rhs)) {
- int32_t shift = mozilla::FloorLog2(rhs);
-
- auto* lir = new (alloc()) LDivPowTwoI64(useRegisterAtStart(div->lhs()),
- LAllocation(), shift, false);
- defineReuseInput(lir, div, 0);
- return;
- }
-
- auto* lir = new (alloc())
- LUDivConstantI64(useRegister(div->lhs()), tempFixed(rax), rhs);
- defineFixed(lir, div, LAllocation(AnyRegister(rdx)));
- return;
- }
-
- auto* lir = new (alloc()) LUDivI64(useFixedAtStart(div->lhs(), rax),
- useRegister(div->rhs()), tempFixed(rdx));
- defineFixed(lir, div, LAllocation(AnyRegister(rax)));
+ LUDivOrModI64* lir = new (alloc()) LUDivOrModI64(
+ useRegister(div->lhs()), useRegister(div->rhs()), tempFixed(rdx));
+ defineInt64Fixed(lir, div, LInt64Allocation(LAllocation(AnyRegister(rax))));
}
void LIRGeneratorX64::lowerUModI64(MMod* mod) {
- if (mod->rhs()->isConstant()) {
- // NOTE: the result of toInt64 is coerced to uint64_t.
- uint64_t rhs = mod->rhs()->toConstant()->toInt64();
-
- if (mozilla::IsPowerOfTwo(rhs)) {
- int32_t shift = mozilla::FloorLog2(rhs);
-
- auto* lir =
- new (alloc()) LModPowTwoI64(useRegisterAtStart(mod->lhs()), shift);
- defineReuseInput(lir, mod, 0);
- return;
- }
-
- auto* lir = new (alloc())
- LUModConstantI64(useRegister(mod->lhs()), tempFixed(rdx), rhs);
- defineFixed(lir, mod, LAllocation(AnyRegister(rax)));
- return;
- }
-
- auto* lir = new (alloc()) LUModI64(useFixedAtStart(mod->lhs(), rax),
- useRegister(mod->rhs()), tempFixed(rax));
- defineFixed(lir, mod, LAllocation(AnyRegister(rdx)));
-}
-
-void LIRGeneratorX64::lowerWasmBuiltinDivI64(MWasmBuiltinDivI64* div) {
- MOZ_CRASH("We don't use runtime div for this architecture");
-}
-
-void LIRGeneratorX64::lowerWasmBuiltinModI64(MWasmBuiltinModI64* mod) {
- MOZ_CRASH("We don't use runtime mod for this architecture");
+ LUDivOrModI64* lir = new (alloc()) LUDivOrModI64(
+ useRegister(mod->lhs()), useRegister(mod->rhs()), tempFixed(rax));
+ defineInt64Fixed(lir, mod, LInt64Allocation(LAllocation(AnyRegister(rdx))));
}
void LIRGeneratorX64::lowerBigIntPtrDiv(MBigIntPtrDiv* ins) {
diff --git a/js/src/jit/x64/MacroAssembler-x64-inl.h b/js/src/jit/x64/MacroAssembler-x64-inl.h
@@ -84,28 +84,18 @@ void MacroAssembler::notPtr(Register reg) { notq(reg); }
void MacroAssembler::andPtr(Register src, Register dest) { andq(src, dest); }
-void MacroAssembler::andPtr(Imm32 imm, Register dest) {
- if (imm.value >= 0) {
- andl(imm, dest);
- } else {
- andq(imm, dest);
- }
-}
+void MacroAssembler::andPtr(Imm32 imm, Register dest) { andq(imm, dest); }
void MacroAssembler::andPtr(Imm32 imm, Register src, Register dest) {
if (src != dest) {
movq(src, dest);
}
- andPtr(imm, dest);
+ andq(imm, dest);
}
void MacroAssembler::and64(Imm64 imm, Register64 dest) {
if (INT32_MIN <= int64_t(imm.value) && int64_t(imm.value) <= INT32_MAX) {
- if (int32_t(imm.value) >= 0) {
- andl(Imm32(imm.value), dest.reg);
- } else {
- andq(Imm32(imm.value), dest.reg);
- }
+ andq(Imm32(imm.value), dest.reg);
} else {
ScratchRegisterScope scratch(*this);
movq(ImmWord(uintptr_t(imm.value)), scratch);
diff --git a/js/src/jit/x64/MacroAssembler-x64.cpp b/js/src/jit/x64/MacroAssembler-x64.cpp
@@ -1578,7 +1578,7 @@ void MacroAssembler::convertUInt64ToDouble(Register64 input,
mov(input.reg, scratch);
mov(input.reg, temp);
shrq(Imm32(1), scratch);
- andl(Imm32(1), temp);
+ andq(Imm32(1), temp);
orq(temp, scratch);
vcvtsq2sd(scratch, output, output);
@@ -1608,7 +1608,7 @@ void MacroAssembler::convertUInt64ToFloat32(Register64 input,
mov(input.reg, scratch);
mov(input.reg, temp);
shrq(Imm32(1), scratch);
- andl(Imm32(1), temp);
+ andq(Imm32(1), temp);
orq(temp, scratch);
vcvtsq2ss(scratch, output, output);
diff --git a/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp b/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp
@@ -872,54 +872,40 @@ void CodeGenerator::visitMulI(LMulI* ins) {
}
}
-template <class LIR>
-static void TrapIfDivideByZero(MacroAssembler& masm, LIR* lir, Register rhs) {
- auto* mir = lir->mir();
- MOZ_ASSERT(mir->trapOnError());
- MOZ_ASSERT(mir->canBeDivideByZero());
-
- Label nonZero;
- masm.branchTest32(Assembler::NonZero, rhs, rhs, &nonZero);
- masm.wasmTrap(wasm::Trap::IntegerDivideByZero, mir->trapSiteDesc());
- masm.bind(&nonZero);
-}
-
-OutOfLineCode* CodeGeneratorX86Shared::emitOutOfLineZeroForDivideByZero(
- Register rhs, Register output) {
- // Truncated division by zero is zero: (±Infinity|0 == 0) and (NaN|0 == 0).
- auto* ool = new (alloc()) LambdaOutOfLineCode([=](OutOfLineCode& ool) {
- masm.mov(ImmWord(0), output);
- masm.jmp(ool.rejoin());
- });
- masm.branchTest32(Assembler::Zero, rhs, rhs, ool->entry());
-
- return ool;
-}
-
-void CodeGenerator::visitUDiv(LUDiv* ins) {
+void CodeGenerator::visitUDivOrMod(LUDivOrMod* ins) {
+ Register lhs = ToRegister(ins->lhs());
Register rhs = ToRegister(ins->rhs());
Register output = ToRegister(ins->output());
- Register remainder = ToRegister(ins->temp0());
- MOZ_ASSERT(ToRegister(ins->lhs()) == eax);
- MOZ_ASSERT(rhs != eax);
+ MOZ_ASSERT_IF(lhs != rhs, rhs != eax);
MOZ_ASSERT(rhs != edx);
- MOZ_ASSERT(output == eax);
- MOZ_ASSERT(remainder == edx);
-
- MDiv* mir = ins->mir();
+ MOZ_ASSERT_IF(output == eax, ToRegister(ins->remainder()) == edx);
OutOfLineCode* ool = nullptr;
+ // Put the lhs in eax.
+ if (lhs != eax) {
+ masm.mov(lhs, eax);
+ }
+
// Prevent divide by zero.
- if (mir->canBeDivideByZero()) {
- if (mir->trapOnError()) {
- TrapIfDivideByZero(masm, ins, rhs);
- } else if (mir->isTruncated()) {
- ool = emitOutOfLineZeroForDivideByZero(rhs, output);
+ if (ins->canBeDivideByZero()) {
+ masm.test32(rhs, rhs);
+ if (ins->mir()->isTruncated()) {
+ if (ins->trapOnError()) {
+ Label nonZero;
+ masm.j(Assembler::NonZero, &nonZero);
+ masm.wasmTrap(wasm::Trap::IntegerDivideByZero, ins->trapSiteDesc());
+ masm.bind(&nonZero);
+ } else {
+ ool = new (alloc()) LambdaOutOfLineCode([=](OutOfLineCode& ool) {
+ masm.mov(ImmWord(0), output);
+ masm.jmp(ool.rejoin());
+ });
+ masm.j(Assembler::Zero, ool->entry());
+ }
} else {
- MOZ_ASSERT(mir->fallible());
- bailoutTest32(Assembler::Zero, rhs, rhs, ins->snapshot());
+ bailoutIf(Assembler::Zero, ins->snapshot());
}
}
@@ -928,102 +914,56 @@ void CodeGenerator::visitUDiv(LUDiv* ins) {
masm.udiv(rhs);
// If the remainder is > 0, bailout since this must be a double.
- if (!mir->canTruncateRemainder()) {
- bailoutTest32(Assembler::NonZero, remainder, remainder, ins->snapshot());
+ if (ins->mir()->isDiv() && !ins->mir()->toDiv()->canTruncateRemainder()) {
+ Register remainder = ToRegister(ins->remainder());
+ masm.test32(remainder, remainder);
+ bailoutIf(Assembler::NonZero, ins->snapshot());
}
- // Unsigned div can return a value that's not a signed int32.
+ // Unsigned div or mod can return a value that's not a signed int32.
// If our users aren't expecting that, bail.
- if (!mir->isTruncated()) {
- bailoutTest32(Assembler::Signed, output, output, ins->snapshot());
+ if (!ins->mir()->isTruncated()) {
+ masm.test32(output, output);
+ bailoutIf(Assembler::Signed, ins->snapshot());
}
if (ool) {
- addOutOfLineCode(ool, mir);
+ addOutOfLineCode(ool, ins->mir());
masm.bind(ool->rejoin());
}
}
-void CodeGenerator::visitUMod(LUMod* ins) {
- Register rhs = ToRegister(ins->rhs());
+void CodeGenerator::visitUDivOrModConstant(LUDivOrModConstant* ins) {
+ Register lhs = ToRegister(ins->numerator());
Register output = ToRegister(ins->output());
+ uint32_t d = ins->denominator();
- MOZ_ASSERT(ToRegister(ins->lhs()) == eax);
- MOZ_ASSERT(rhs != eax);
- MOZ_ASSERT(rhs != edx);
- MOZ_ASSERT(output == edx);
- MOZ_ASSERT(ToRegister(ins->temp0()) == eax);
-
- MMod* mir = ins->mir();
-
- OutOfLineCode* ool = nullptr;
+ // This emits the division answer into edx or the modulus answer into eax.
+ MOZ_ASSERT(output == eax || output == edx);
+ MOZ_ASSERT(lhs != eax && lhs != edx);
+ bool isDiv = (output == edx);
- // Prevent divide by zero.
- if (mir->canBeDivideByZero()) {
- if (mir->trapOnError()) {
- TrapIfDivideByZero(masm, ins, rhs);
- } else if (mir->isTruncated()) {
- ool = emitOutOfLineZeroForDivideByZero(rhs, output);
+ if (d == 0) {
+ if (ins->mir()->isTruncated()) {
+ if (ins->trapOnError()) {
+ masm.wasmTrap(wasm::Trap::IntegerDivideByZero, ins->trapSiteDesc());
+ } else {
+ masm.xorl(output, output);
+ }
} else {
- MOZ_ASSERT(mir->fallible());
- bailoutTest32(Assembler::Zero, rhs, rhs, ins->snapshot());
+ bailout(ins->snapshot());
}
+ return;
}
- // Zero extend the lhs into edx to make (edx:eax), since udiv is 64-bit.
- masm.mov(ImmWord(0), edx);
- masm.udiv(rhs);
-
- // Unsigned mod can return a value that's not a signed int32.
- // If our users aren't expecting that, bail.
- if (!mir->isTruncated()) {
- bailoutTest32(Assembler::Signed, output, output, ins->snapshot());
- }
-
- if (ool) {
- addOutOfLineCode(ool, mir);
- masm.bind(ool->rejoin());
- }
-}
-
-template <class LUDivOrUMod>
-static void UnsignedDivideWithConstant(MacroAssembler& masm, LUDivOrUMod* ins,
- Register result, Register temp) {
- Register lhs = ToRegister(ins->numerator());
- uint32_t d = ins->denominator();
-
- MOZ_ASSERT(lhs != result && lhs != temp);
-#ifdef JS_CODEGEN_X86
- MOZ_ASSERT(result == edx && temp == eax);
-#else
- MOZ_ASSERT(result != temp);
-#endif
-
// The denominator isn't a power of 2 (see LDivPowTwoI and LModPowTwoI).
- MOZ_ASSERT(!mozilla::IsPowerOfTwo(d));
+ MOZ_ASSERT((d & (d - 1)) != 0);
auto rmc = ReciprocalMulConstants::computeUnsignedDivisionConstants(d);
// We first compute (M * n) >> 32, where M = rmc.multiplier.
-#ifdef JS_CODEGEN_X86
masm.movl(Imm32(rmc.multiplier), eax);
masm.umull(lhs);
-#else
- // Zero-extend |lhs| in preparation for a 64-bit multiplication.
- masm.movl(lhs, result);
-
- // Note that imul sign-extends its 32-bit immediate, but we need an unsigned
- // multiplication.
- if (int32_t(rmc.multiplier) >= 0) {
- masm.imulq(Imm32(rmc.multiplier), result, result);
- } else {
- masm.movl(Imm32(rmc.multiplier), temp);
- masm.imulq(temp, result);
- }
- if (rmc.multiplier > UINT32_MAX || rmc.shiftAmount == 0) {
- masm.shrq(Imm32(32), result);
- }
-#endif
if (rmc.multiplier > UINT32_MAX) {
// M >= 2^32 and shift == 0 is impossible, as d >= 2 implies that
// ((M * n) >> (32 + shift)) >= n > floor(n/d) whenever n >= d,
@@ -1031,112 +971,45 @@ static void UnsignedDivideWithConstant(MacroAssembler& masm, LUDivOrUMod* ins,
MOZ_ASSERT(rmc.shiftAmount > 0);
MOZ_ASSERT(rmc.multiplier < (int64_t(1) << 33));
- // We actually computed result = ((uint32_t(M) * n) >> 32) instead. Since
- // (M * n) >> (32 + shift) is the same as (result + n) >> shift, we can
+ // We actually computed edx = ((uint32_t(M) * n) >> 32) instead. Since
+ // (M * n) >> (32 + shift) is the same as (edx + n) >> shift, we can
// correct for the overflow. This case is a bit trickier than the signed
- // case, though, as the (result + n) addition itself can overflow; however,
- // note that
- // (result + n) >> shift == (((n - result) >> 1) + result) >> (shift - 1),
+ // case, though, as the (edx + n) addition itself can overflow; however,
+ // note that (edx + n) >> shift == (((n - edx) >> 1) + edx) >> (shift - 1),
// which is overflow-free. See Hacker's Delight, section 10-8 for details.
- // Compute (n - result) >> 1 into temp.
- masm.movl(lhs, temp);
- masm.subl(result, temp);
- masm.shrl(Imm32(1), temp);
+ // Compute (n - edx) >> 1 into eax.
+ masm.movl(lhs, eax);
+ masm.subl(edx, eax);
+ masm.shrl(Imm32(1), eax);
// Finish the computation.
- masm.addl(temp, result);
- if (rmc.shiftAmount > 1) {
- masm.shrl(Imm32(rmc.shiftAmount - 1), result);
- }
+ masm.addl(eax, edx);
+ masm.shrl(Imm32(rmc.shiftAmount - 1), edx);
} else {
- if (rmc.shiftAmount > 0) {
-#ifdef JS_CODEGEN_X86
- masm.shrl(Imm32(rmc.shiftAmount), result);
-#else
- masm.shrq(Imm32(32 + rmc.shiftAmount), result);
-#endif
- }
- }
-}
-
-void CodeGenerator::visitUDivConstant(LUDivConstant* ins) {
- Register lhs = ToRegister(ins->numerator());
- Register output = ToRegister(ins->output());
- Register temp = ToRegister(ins->temp0());
- uint32_t d = ins->denominator();
-
- MDiv* mir = ins->mir();
-
-#ifdef JS_CODEGEN_X86
- // This emits the division answer into edx.
- MOZ_ASSERT(output == edx);
- MOZ_ASSERT(temp == eax);
-#endif
-
- if (d == 0) {
- if (mir->trapOnError()) {
- masm.wasmTrap(wasm::Trap::IntegerDivideByZero, mir->trapSiteDesc());
- } else if (mir->isTruncated()) {
- masm.xorl(output, output);
- } else {
- bailout(ins->snapshot());
- }
- return;
- }
-
- // Compute the truncated division result in |output|.
- UnsignedDivideWithConstant(masm, ins, output, temp);
-
- if (!mir->isTruncated()) {
- masm.imull(Imm32(d), output, temp);
- bailoutCmp32(Assembler::NotEqual, lhs, temp, ins->snapshot());
- }
-}
-
-void CodeGenerator::visitUModConstant(LUModConstant* ins) {
- Register lhs = ToRegister(ins->numerator());
- Register output = ToRegister(ins->output());
- Register temp = ToRegister(ins->temp0());
- uint32_t d = ins->denominator();
-
- MMod* mir = ins->mir();
-
-#ifdef JS_CODEGEN_X86
- // This emits the modulus answer into eax.
- MOZ_ASSERT(output == eax);
- MOZ_ASSERT(temp == edx);
-#endif
-
- if (d == 0) {
- if (mir->trapOnError()) {
- masm.wasmTrap(wasm::Trap::IntegerDivideByZero, mir->trapSiteDesc());
- } else if (mir->isTruncated()) {
- masm.xorl(output, output);
- } else {
- bailout(ins->snapshot());
+ masm.shrl(Imm32(rmc.shiftAmount), edx);
+ }
+
+ // We now have the truncated division value in edx. If we're
+ // computing a modulus or checking whether the division resulted
+ // in an integer, we need to multiply the obtained value by d and
+ // finish the computation/check.
+ if (!isDiv) {
+ masm.imull(Imm32(d), edx, edx);
+ masm.movl(lhs, eax);
+ masm.subl(edx, eax);
+
+ // The final result of the modulus op, just computed above by the
+ // sub instruction, can be a number in the range [2^31, 2^32). If
+ // this is the case and the modulus is not truncated, we must bail
+ // out.
+ if (!ins->mir()->isTruncated()) {
+ bailoutIf(Assembler::Signed, ins->snapshot());
}
- return;
- }
-
- // Compute the truncated division result in |temp|.
- UnsignedDivideWithConstant(masm, ins, temp, output);
-
- // We now have the truncated division value in |temp|. If we're computing a
- // modulus or checking whether the division resulted in an integer, we need
- // to multiply the obtained value by d and finish the computation/check.
- //
- // output = lhs - d * temp
- masm.imull(Imm32(d), temp, temp);
- masm.movl(lhs, output);
- masm.subl(temp, output);
-
- // The final result of the modulus op, just computed above by the
- // sub instruction, can be a number in the range [2^31, 2^32). If
- // this is the case and the modulus is not truncated, we must bail
- // out.
- if (!mir->isTruncated()) {
- bailoutIf(Assembler::Signed, ins->snapshot());
+ } else if (!ins->mir()->isTruncated()) {
+ masm.imull(Imm32(d), edx, eax);
+ masm.cmpl(lhs, eax);
+ bailoutIf(Assembler::NotEqual, ins->snapshot());
}
}
@@ -1154,14 +1027,15 @@ void CodeGenerator::visitDivPowTwoI(LDivPowTwoI* ins) {
if (!mir->isTruncated() && negativeDivisor) {
// 0 divided by a negative number must return a double.
- bailoutTest32(Assembler::Zero, lhs, lhs, ins->snapshot());
+ masm.test32(lhs, lhs);
+ bailoutIf(Assembler::Zero, ins->snapshot());
}
if (shift) {
if (!mir->isTruncated()) {
// If the remainder is != 0, bailout since this must be a double.
- bailoutTest32(Assembler::NonZero, lhs, Imm32(UINT32_MAX >> (32 - shift)),
- ins->snapshot());
+ masm.test32(lhs, Imm32(UINT32_MAX >> (32 - shift)));
+ bailoutIf(Assembler::NonZero, ins->snapshot());
}
if (mir->isUnsigned()) {
@@ -1210,165 +1084,93 @@ void CodeGenerator::visitDivPowTwoI(LDivPowTwoI* ins) {
masm.bind(&ok);
}
} else if (mir->isUnsigned() && !mir->isTruncated()) {
- // Unsigned division by 1 can overflow if output is not truncated.
- bailoutTest32(Assembler::Signed, lhs, lhs, ins->snapshot());
+ // Unsigned division by 1 can overflow if output is not
+ // truncated.
+ masm.test32(lhs, lhs);
+ bailoutIf(Assembler::Signed, ins->snapshot());
}
}
-template <class LDivOrMod>
-static void DivideWithConstant(MacroAssembler& masm, LDivOrMod* ins,
- Register result, Register temp) {
+void CodeGenerator::visitDivOrModConstantI(LDivOrModConstantI* ins) {
Register lhs = ToRegister(ins->numerator());
+ Register output = ToRegister(ins->output());
int32_t d = ins->denominator();
- MOZ_ASSERT(lhs != result && lhs != temp);
-#ifdef JS_CODEGEN_X86
- MOZ_ASSERT(result == edx && temp == eax);
-#else
- MOZ_ASSERT(result != temp);
-#endif
+ // This emits the division answer into edx or the modulus answer into eax.
+ MOZ_ASSERT(output == eax || output == edx);
+ MOZ_ASSERT(lhs != eax && lhs != edx);
+ bool isDiv = (output == edx);
// The absolute value of the denominator isn't a power of 2 (see LDivPowTwoI
// and LModPowTwoI).
MOZ_ASSERT(!mozilla::IsPowerOfTwo(mozilla::Abs(d)));
- auto* mir = ins->mir();
-
// We will first divide by Abs(d), and negate the answer if d is negative.
// If desired, this can be avoided by generalizing computeDivisionConstants.
auto rmc = ReciprocalMulConstants::computeSignedDivisionConstants(d);
// We first compute (M * n) >> 32, where M = rmc.multiplier.
-#ifdef JS_CODEGEN_X86
masm.movl(Imm32(rmc.multiplier), eax);
masm.imull(lhs);
-#else
- // Sign-extend |lhs| in preparation for a 64-bit multiplication.
- masm.movslq(lhs, result);
- masm.imulq(Imm32(rmc.multiplier), result, result);
- if (rmc.multiplier > INT32_MAX || rmc.shiftAmount == 0) {
- masm.shrq(Imm32(32), result);
- }
-#endif
if (rmc.multiplier > INT32_MAX) {
MOZ_ASSERT(rmc.multiplier < (int64_t(1) << 32));
- // We actually computed result = ((int32_t(M) * n) >> 32) instead. Since
- // (M * n) >> 32 is the same as (result + n), we can correct for the
- // overflow. (result + n) can't overflow, as n and |result| have opposite
- // signs because int32_t(M) is negative.
- masm.addl(lhs, result);
+ // We actually computed edx = ((int32_t(M) * n) >> 32) instead. Since
+ // (M * n) >> 32 is the same as (edx + n), we can correct for the overflow.
+ // (edx + n) can't overflow, as n and edx have opposite signs because
+ // int32_t(M) is negative.
+ masm.addl(lhs, edx);
}
// (M * n) >> (32 + shift) is the truncated division answer if n is
// non-negative, as proved in the comments of computeDivisionConstants. We
// must add 1 later if n is negative to get the right answer in all cases.
- if (rmc.shiftAmount > 0) {
-#ifdef JS_CODEGEN_X86
- masm.sarl(Imm32(rmc.shiftAmount), result);
-#else
- if (rmc.multiplier > INT32_MAX) {
- masm.sarl(Imm32(rmc.shiftAmount), result);
- } else {
- masm.sarq(Imm32(32 + rmc.shiftAmount), result);
- }
-#endif
- }
+ masm.sarl(Imm32(rmc.shiftAmount), edx);
// We'll subtract -1 instead of adding 1, because (n < 0 ? -1 : 0) can be
// computed with just a sign-extending shift of 31 bits.
- if (mir->canBeNegativeDividend()) {
- masm.movl(lhs, temp);
- masm.sarl(Imm32(31), temp);
- masm.subl(temp, result);
+ if (ins->canBeNegativeDividend()) {
+ masm.movl(lhs, eax);
+ masm.sarl(Imm32(31), eax);
+ masm.subl(eax, edx);
}
- // After this, |result| contains the correct truncated division result.
+ // After this, edx contains the correct truncated division result.
if (d < 0) {
- masm.negl(result);
+ masm.negl(edx);
}
-}
-
-void CodeGenerator::visitDivConstantI(LDivConstantI* ins) {
- Register lhs = ToRegister(ins->numerator());
- Register output = ToRegister(ins->output());
- Register temp = ToRegister(ins->temp0());
- int32_t d = ins->denominator();
- MDiv* mir = ins->mir();
-
-#ifdef JS_CODEGEN_X86
- // This emits the division answer into edx.
- MOZ_ASSERT(output == edx);
- MOZ_ASSERT(temp == eax);
-#endif
-
- if (d == 0) {
- if (mir->trapOnError()) {
- masm.wasmTrap(wasm::Trap::IntegerDivideByZero, mir->trapSiteDesc());
- } else if (mir->isTruncated()) {
- masm.xorl(output, output);
- } else {
- bailout(ins->snapshot());
- }
- return;
+ if (!isDiv) {
+ masm.imull(Imm32(-d), edx, eax);
+ masm.addl(lhs, eax);
}
- // Compute the truncated division result in |output|.
- DivideWithConstant(masm, ins, output, temp);
-
- if (!mir->isTruncated()) {
- // This is a division op. Multiply the obtained value by d to check if
- // the correct answer is an integer. This cannot overflow, since |d| > 1.
- masm.imull(Imm32(d), output, temp);
- bailoutCmp32(Assembler::NotEqual, lhs, temp, ins->snapshot());
-
- // If lhs is zero and the divisor is negative, the answer should have
- // been -0.
- if (d < 0) {
- bailoutTest32(Assembler::Zero, lhs, lhs, ins->snapshot());
- }
- }
-}
+ if (!ins->mir()->isTruncated()) {
+ if (isDiv) {
+ // This is a division op. Multiply the obtained value by d to check if
+ // the correct answer is an integer. This cannot overflow, since |d| > 1.
+ masm.imull(Imm32(d), edx, eax);
+ masm.cmp32(lhs, eax);
+ bailoutIf(Assembler::NotEqual, ins->snapshot());
-void CodeGenerator::visitModConstantI(LModConstantI* ins) {
- Register lhs = ToRegister(ins->numerator());
- Register output = ToRegister(ins->output());
- Register temp = ToRegister(ins->temp0());
- int32_t d = ins->denominator();
+ // If lhs is zero and the divisor is negative, the answer should have
+ // been -0.
+ if (d < 0) {
+ masm.test32(lhs, lhs);
+ bailoutIf(Assembler::Zero, ins->snapshot());
+ }
+ } else if (ins->canBeNegativeDividend()) {
+ // This is a mod op. If the computed value is zero and lhs
+ // is negative, the answer should have been -0.
+ Label done;
- MMod* mir = ins->mir();
+ masm.cmp32(lhs, Imm32(0));
+ masm.j(Assembler::GreaterThanOrEqual, &done);
-#ifdef JS_CODEGEN_X86
- // This emits the modulus answer into eax.
- MOZ_ASSERT(output == eax);
- MOZ_ASSERT(temp == edx);
-#endif
+ masm.test32(eax, eax);
+ bailoutIf(Assembler::Zero, ins->snapshot());
- if (d == 0) {
- if (mir->trapOnError()) {
- masm.wasmTrap(wasm::Trap::IntegerDivideByZero, mir->trapSiteDesc());
- } else if (mir->isTruncated()) {
- masm.xorl(output, output);
- } else {
- bailout(ins->snapshot());
+ masm.bind(&done);
}
- return;
- }
-
- // Compute the truncated division result in |temp|.
- DivideWithConstant(masm, ins, temp, output);
-
- // Compute the remainder in |output|: output = lhs - d * temp
- masm.imull(Imm32(-d), temp, output);
- masm.addl(lhs, output);
-
- if (!mir->isTruncated() && mir->canBeNegativeDividend()) {
- // This is a mod op. If the computed value is zero and lhs
- // is negative, the answer should have been -0.
- Label done;
- masm.branch32(Assembler::GreaterThanOrEqual, lhs, Imm32(0), &done);
- bailoutTest32(Assembler::Zero, output, output, ins->snapshot());
- masm.bind(&done);
}
}
@@ -1378,43 +1180,61 @@ void CodeGenerator::visitDivI(LDivI* ins) {
Register rhs = ToRegister(ins->rhs());
Register output = ToRegister(ins->output());
- MOZ_ASSERT(lhs == eax);
- MOZ_ASSERT(rhs != eax);
+ MDiv* mir = ins->mir();
+
+ MOZ_ASSERT_IF(lhs != rhs, rhs != eax);
MOZ_ASSERT(rhs != edx);
MOZ_ASSERT(remainder == edx);
MOZ_ASSERT(output == eax);
- MDiv* mir = ins->mir();
-
Label done;
OutOfLineCode* ool = nullptr;
+ // Put the lhs in eax, for either the negative overflow case or the regular
+ // divide case.
+ if (lhs != eax) {
+ masm.mov(lhs, eax);
+ }
+
// Handle divide by zero.
if (mir->canBeDivideByZero()) {
+ masm.test32(rhs, rhs);
if (mir->trapOnError()) {
- TrapIfDivideByZero(masm, ins, rhs);
+ Label nonZero;
+ masm.j(Assembler::NonZero, &nonZero);
+ masm.wasmTrap(wasm::Trap::IntegerDivideByZero, mir->trapSiteDesc());
+ masm.bind(&nonZero);
} else if (mir->canTruncateInfinities()) {
- ool = emitOutOfLineZeroForDivideByZero(rhs, output);
+ // Truncated division by zero is zero (Infinity|0 == 0)
+ if (!ool) {
+ ool = new (alloc()) LambdaOutOfLineCode([=](OutOfLineCode& ool) {
+ masm.mov(ImmWord(0), output);
+ masm.jmp(ool.rejoin());
+ });
+ }
+ masm.j(Assembler::Zero, ool->entry());
} else {
MOZ_ASSERT(mir->fallible());
- bailoutTest32(Assembler::Zero, rhs, rhs, ins->snapshot());
+ bailoutIf(Assembler::Zero, ins->snapshot());
}
}
// Handle an integer overflow exception from -2147483648 / -1.
if (mir->canBeNegativeOverflow()) {
Label notOverflow;
- masm.branch32(Assembler::NotEqual, lhs, Imm32(INT32_MIN), ¬Overflow);
+ masm.cmp32(lhs, Imm32(INT32_MIN));
+ masm.j(Assembler::NotEqual, ¬Overflow);
+ masm.cmp32(rhs, Imm32(-1));
if (mir->trapOnError()) {
- masm.branch32(Assembler::NotEqual, rhs, Imm32(-1), ¬Overflow);
+ masm.j(Assembler::NotEqual, ¬Overflow);
masm.wasmTrap(wasm::Trap::IntegerOverflow, mir->trapSiteDesc());
} else if (mir->canTruncateOverflow()) {
// (-INT32_MIN)|0 == INT32_MIN and INT32_MIN is already in the
// output register (lhs == eax).
- masm.branch32(Assembler::Equal, rhs, Imm32(-1), &done);
+ masm.j(Assembler::Equal, &done);
} else {
MOZ_ASSERT(mir->fallible());
- bailoutCmp32(Assembler::Equal, rhs, Imm32(-1), ins->snapshot());
+ bailoutIf(Assembler::Equal, ins->snapshot());
}
masm.bind(¬Overflow);
}
@@ -1422,18 +1242,24 @@ void CodeGenerator::visitDivI(LDivI* ins) {
// Handle negative 0.
if (!mir->canTruncateNegativeZero() && mir->canBeNegativeZero()) {
Label nonzero;
- masm.branchTest32(Assembler::NonZero, lhs, lhs, &nonzero);
- bailoutCmp32(Assembler::LessThan, rhs, Imm32(0), ins->snapshot());
+ masm.test32(lhs, lhs);
+ masm.j(Assembler::NonZero, &nonzero);
+ masm.cmp32(rhs, Imm32(0));
+ bailoutIf(Assembler::LessThan, ins->snapshot());
masm.bind(&nonzero);
}
// Sign extend the lhs into edx to make (edx:eax), since idiv is 64-bit.
+ if (lhs != eax) {
+ masm.mov(lhs, eax);
+ }
masm.cdq();
masm.idiv(rhs);
if (!mir->canTruncateRemainder()) {
// If the remainder is > 0, bailout since this must be a double.
- bailoutTest32(Assembler::NonZero, remainder, remainder, ins->snapshot());
+ masm.test32(remainder, remainder);
+ bailoutIf(Assembler::NonZero, ins->snapshot());
}
masm.bind(&done);
@@ -1447,45 +1273,18 @@ void CodeGenerator::visitDivI(LDivI* ins) {
void CodeGenerator::visitModPowTwoI(LModPowTwoI* ins) {
Register lhs = ToRegister(ins->input());
int32_t shift = ins->shift();
- bool canBeNegative =
- !ins->mir()->isUnsigned() && ins->mir()->canBeNegativeDividend();
-
- if (shift == 0) {
- if (canBeNegative && !ins->mir()->isTruncated()) {
- bailoutTest32(Assembler::Signed, lhs, lhs, ins->snapshot());
- }
- masm.xorl(lhs, lhs);
- return;
- }
-
- auto clearHighBits = [&]() {
- switch (shift) {
- case 16:
- masm.movzwl(lhs, lhs);
- break;
- case 8:
- if (AllocatableGeneralRegisterSet(Registers::SingleByteRegs).has(lhs)) {
- masm.movzbl(lhs, lhs);
- break;
- }
- [[fallthrough]];
- default:
- masm.andl(Imm32((uint32_t(1) << shift) - 1), lhs);
- break;
- }
- };
Label negative;
- if (canBeNegative) {
+ if (!ins->mir()->isUnsigned() && ins->mir()->canBeNegativeDividend()) {
// Switch based on sign of the lhs.
// Positive numbers are just a bitmask
masm.branchTest32(Assembler::Signed, lhs, lhs, &negative);
}
- clearHighBits();
+ masm.andl(Imm32((uint32_t(1) << shift) - 1), lhs);
- if (canBeNegative) {
+ if (!ins->mir()->isUnsigned() && ins->mir()->canBeNegativeDividend()) {
Label done;
masm.jump(&done);
@@ -1499,7 +1298,7 @@ void CodeGenerator::visitModPowTwoI(LModPowTwoI* ins) {
// The negl instruction overflows if lhs == INT32_MIN, but this is also not
// a problem: shift is at most 31, and so the andl also always returns 0.
masm.negl(lhs);
- clearHighBits();
+ masm.andl(Imm32((uint32_t(1) << shift) - 1), lhs);
masm.negl(lhs);
// Since a%b has the same sign as b, and a is negative in this branch,
@@ -1545,27 +1344,42 @@ void CodeGenerator::visitModI(LModI* ins) {
Register rhs = ToRegister(ins->rhs());
// Required to use idiv.
- MOZ_ASSERT(lhs == eax);
- MOZ_ASSERT(rhs != eax);
+ MOZ_ASSERT_IF(lhs != rhs, rhs != eax);
MOZ_ASSERT(rhs != edx);
MOZ_ASSERT(remainder == edx);
MOZ_ASSERT(ToRegister(ins->temp0()) == eax);
- MMod* mir = ins->mir();
-
Label done;
OutOfLineCode* ool = nullptr;
ModOverflowCheck* overflow = nullptr;
+ // Set up eax in preparation for doing a div.
+ if (lhs != eax) {
+ masm.mov(lhs, eax);
+ }
+
+ MMod* mir = ins->mir();
+
// Prevent divide by zero.
if (mir->canBeDivideByZero()) {
- if (mir->trapOnError()) {
- TrapIfDivideByZero(masm, ins, rhs);
- } else if (mir->isTruncated()) {
- ool = emitOutOfLineZeroForDivideByZero(rhs, remainder);
+ masm.test32(rhs, rhs);
+ if (mir->isTruncated()) {
+ if (mir->trapOnError()) {
+ Label nonZero;
+ masm.j(Assembler::NonZero, &nonZero);
+ masm.wasmTrap(wasm::Trap::IntegerDivideByZero, mir->trapSiteDesc());
+ masm.bind(&nonZero);
+ } else {
+ if (!ool) {
+ ool = new (alloc()) LambdaOutOfLineCode([=](OutOfLineCode& ool) {
+ masm.mov(ImmWord(0), edx);
+ masm.jmp(ool.rejoin());
+ });
+ }
+ masm.j(Assembler::Zero, ool->entry());
+ }
} else {
- MOZ_ASSERT(mir->fallible());
- bailoutTest32(Assembler::Zero, rhs, rhs, ins->snapshot());
+ bailoutIf(Assembler::Zero, ins->snapshot());
}
}
@@ -1611,16 +1425,17 @@ void CodeGenerator::visitModI(LModI* ins) {
masm.bind(&negative);
// Prevent an integer overflow exception from -2147483648 % -1
+ masm.cmp32(lhs, Imm32(INT32_MIN));
overflow = new (alloc()) ModOverflowCheck(ins, rhs);
- masm.branch32(Assembler::Equal, lhs, Imm32(INT32_MIN), overflow->entry());
+ masm.j(Assembler::Equal, overflow->entry());
masm.bind(overflow->rejoin());
-
masm.cdq();
masm.idiv(rhs);
if (!mir->isTruncated()) {
// A remainder of 0 means that the rval must be -0, which is a double.
- bailoutTest32(Assembler::Zero, remainder, remainder, ins->snapshot());
+ masm.test32(remainder, remainder);
+ bailoutIf(Assembler::Zero, ins->snapshot());
}
}
diff --git a/js/src/jit/x86-shared/CodeGenerator-x86-shared.h b/js/src/jit/x86-shared/CodeGenerator-x86-shared.h
@@ -84,11 +84,6 @@ class CodeGeneratorX86Shared : public CodeGeneratorShared {
void emitTableSwitchDispatch(MTableSwitch* mir, Register index,
Register base);
- // Emit out-of-line code to zero |output| if |rhs| is zero. Used for truncated
- // division and modulus instructions.
- OutOfLineCode* emitOutOfLineZeroForDivideByZero(Register rhs,
- Register output);
-
void generateInvalidateEpilogue();
template <typename T>
diff --git a/js/src/jit/x86-shared/LIR-x86-shared.h b/js/src/jit/x86-shared/LIR-x86-shared.h
@@ -0,0 +1,130 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=8 sts=2 et sw=2 tw=80:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef jit_x86_shared_LIR_x86_shared_h
+#define jit_x86_shared_LIR_x86_shared_h
+
+namespace js {
+namespace jit {
+
+class LDivOrModConstantI : public LInstructionHelper<1, 1, 1> {
+ const int32_t denominator_;
+
+ public:
+ LIR_HEADER(DivOrModConstantI)
+
+ LDivOrModConstantI(const LAllocation& lhs, int32_t denominator,
+ const LDefinition& temp)
+ : LInstructionHelper(classOpcode), denominator_(denominator) {
+ setOperand(0, lhs);
+ setTemp(0, temp);
+ }
+
+ const LAllocation* numerator() { return getOperand(0); }
+ int32_t denominator() const { return denominator_; }
+ MBinaryArithInstruction* mir() const {
+ MOZ_ASSERT(mir_->isDiv() || mir_->isMod());
+ return static_cast<MBinaryArithInstruction*>(mir_);
+ }
+ bool canBeNegativeDividend() const {
+ if (mir_->isMod()) {
+ return mir_->toMod()->canBeNegativeDividend();
+ }
+ return mir_->toDiv()->canBeNegativeDividend();
+ }
+};
+
+// This class performs a simple x86 'div', yielding either a quotient or
+// remainder depending on whether this instruction is defined to output eax
+// (quotient) or edx (remainder).
+class LUDivOrMod : public LBinaryMath<1> {
+ public:
+ LIR_HEADER(UDivOrMod);
+
+ LUDivOrMod(const LAllocation& lhs, const LAllocation& rhs,
+ const LDefinition& temp)
+ : LBinaryMath(classOpcode) {
+ setOperand(0, lhs);
+ setOperand(1, rhs);
+ setTemp(0, temp);
+ }
+
+ const LDefinition* remainder() { return getTemp(0); }
+
+ const char* extraName() const {
+ return mir()->isTruncated() ? "Truncated" : nullptr;
+ }
+
+ MBinaryArithInstruction* mir() const {
+ MOZ_ASSERT(mir_->isDiv() || mir_->isMod());
+ return static_cast<MBinaryArithInstruction*>(mir_);
+ }
+
+ bool canBeDivideByZero() const {
+ if (mir_->isMod()) {
+ return mir_->toMod()->canBeDivideByZero();
+ }
+ return mir_->toDiv()->canBeDivideByZero();
+ }
+
+ bool trapOnError() const {
+ if (mir_->isMod()) {
+ return mir_->toMod()->trapOnError();
+ }
+ return mir_->toDiv()->trapOnError();
+ }
+
+ wasm::TrapSiteDesc trapSiteDesc() const {
+ if (mir_->isMod()) {
+ return mir_->toMod()->trapSiteDesc();
+ }
+ return mir_->toDiv()->trapSiteDesc();
+ }
+};
+
+class LUDivOrModConstant : public LInstructionHelper<1, 1, 1> {
+ const uint32_t denominator_;
+
+ public:
+ LIR_HEADER(UDivOrModConstant)
+
+ LUDivOrModConstant(const LAllocation& lhs, uint32_t denominator,
+ const LDefinition& temp)
+ : LInstructionHelper(classOpcode), denominator_(denominator) {
+ setOperand(0, lhs);
+ setTemp(0, temp);
+ }
+
+ const LAllocation* numerator() { return getOperand(0); }
+ uint32_t denominator() const { return denominator_; }
+ MBinaryArithInstruction* mir() const {
+ MOZ_ASSERT(mir_->isDiv() || mir_->isMod());
+ return static_cast<MBinaryArithInstruction*>(mir_);
+ }
+ bool canBeNegativeDividend() const {
+ if (mir_->isMod()) {
+ return mir_->toMod()->canBeNegativeDividend();
+ }
+ return mir_->toDiv()->canBeNegativeDividend();
+ }
+ bool trapOnError() const {
+ if (mir_->isMod()) {
+ return mir_->toMod()->trapOnError();
+ }
+ return mir_->toDiv()->trapOnError();
+ }
+ wasm::TrapSiteDesc trapSiteDesc() const {
+ if (mir_->isMod()) {
+ return mir_->toMod()->trapSiteDesc();
+ }
+ return mir_->toDiv()->trapSiteDesc();
+ }
+};
+
+} // namespace jit
+} // namespace js
+
+#endif /* jit_x86_shared_LIR_x86_shared_h */
diff --git a/js/src/jit/x86-shared/Lowering-x86-shared.cpp b/js/src/jit/x86-shared/Lowering-x86-shared.cpp
@@ -179,48 +179,40 @@ void LIRGeneratorX86Shared::lowerDivI(MDiv* div) {
int32_t shift = FloorLog2(Abs(rhs));
if (rhs != 0 && uint32_t(1) << shift == Abs(rhs)) {
LAllocation lhs = useRegisterAtStart(div->lhs());
-
+ LDivPowTwoI* lir;
// When truncated with maybe a non-zero remainder, we have to round the
// result toward 0. This requires an extra register to round up/down
// whether the left-hand-side is signed.
- //
- // If the numerator might be signed, and needs adjusting, then an extra
- // lhs copy is needed to round the result of the integer division towards
- // zero.
- //
- // Otherwise the numerator is unsigned, so does not need adjusting.
bool needRoundNeg = div->canBeNegativeDividend() && div->isTruncated();
- LAllocation lhsCopy =
- needRoundNeg ? useRegister(div->lhs()) : LAllocation();
-
- auto* lir = new (alloc()) LDivPowTwoI(lhs, lhsCopy, shift, rhs < 0);
+ if (!needRoundNeg) {
+ // Numerator is unsigned, so does not need adjusting.
+ lir = new (alloc()) LDivPowTwoI(lhs, lhs, shift, rhs < 0);
+ } else {
+ // Numerator might be signed, and needs adjusting, and an extra lhs copy
+ // is needed to round the result of the integer division towards zero.
+ lir = new (alloc())
+ LDivPowTwoI(lhs, useRegister(div->lhs()), shift, rhs < 0);
+ }
if (div->fallible()) {
assignSnapshot(lir, div->bailoutKind());
}
defineReuseInput(lir, div, 0);
return;
}
-
-#ifdef JS_CODEGEN_X86
- auto* lir = new (alloc())
- LDivConstantI(useRegister(div->lhs()), tempFixed(eax), rhs);
- if (div->fallible()) {
- assignSnapshot(lir, div->bailoutKind());
- }
- defineFixed(lir, div, LAllocation(AnyRegister(edx)));
-#else
- auto* lir =
- new (alloc()) LDivConstantI(useRegister(div->lhs()), temp(), rhs);
- if (div->fallible()) {
- assignSnapshot(lir, div->bailoutKind());
+ if (rhs != 0) {
+ LDivOrModConstantI* lir;
+ lir = new (alloc())
+ LDivOrModConstantI(useRegister(div->lhs()), rhs, tempFixed(eax));
+ if (div->fallible()) {
+ assignSnapshot(lir, div->bailoutKind());
+ }
+ defineFixed(lir, div, LAllocation(AnyRegister(edx)));
+ return;
}
- define(lir, div);
-#endif
- return;
}
- auto* lir = new (alloc()) LDivI(useFixedAtStart(div->lhs(), eax),
- useRegister(div->rhs()), tempFixed(edx));
+ LDivI* lir = new (alloc())
+ LDivI(useRegister(div->lhs()), useRegister(div->rhs()), tempFixed(edx));
if (div->fallible()) {
assignSnapshot(lir, div->bailoutKind());
}
@@ -232,7 +224,7 @@ void LIRGeneratorX86Shared::lowerModI(MMod* mod) {
int32_t rhs = mod->rhs()->toConstant()->toInt32();
int32_t shift = FloorLog2(Abs(rhs));
if (rhs != 0 && uint32_t(1) << shift == Abs(rhs)) {
- auto* lir =
+ LModPowTwoI* lir =
new (alloc()) LModPowTwoI(useRegisterAtStart(mod->lhs()), shift);
if (mod->fallible()) {
assignSnapshot(lir, mod->bailoutKind());
@@ -240,27 +232,20 @@ void LIRGeneratorX86Shared::lowerModI(MMod* mod) {
defineReuseInput(lir, mod, 0);
return;
}
-
-#ifdef JS_CODEGEN_X86
- auto* lir = new (alloc())
- LModConstantI(useRegister(mod->lhs()), tempFixed(edx), rhs);
- if (mod->fallible()) {
- assignSnapshot(lir, mod->bailoutKind());
- }
- defineFixed(lir, mod, LAllocation(AnyRegister(eax)));
-#else
- auto* lir =
- new (alloc()) LModConstantI(useRegister(mod->lhs()), temp(), rhs);
- if (mod->fallible()) {
- assignSnapshot(lir, mod->bailoutKind());
+ if (rhs != 0) {
+ LDivOrModConstantI* lir;
+ lir = new (alloc())
+ LDivOrModConstantI(useRegister(mod->lhs()), rhs, tempFixed(edx));
+ if (mod->fallible()) {
+ assignSnapshot(lir, mod->bailoutKind());
+ }
+ defineFixed(lir, mod, LAllocation(AnyRegister(eax)));
+ return;
}
- define(lir, mod);
-#endif
- return;
}
- auto* lir = new (alloc()) LModI(useFixedAtStart(mod->lhs(), eax),
- useRegister(mod->rhs()), tempFixed(eax));
+ LModI* lir = new (alloc())
+ LModI(useRegister(mod->lhs()), useRegister(mod->rhs()), tempFixed(eax));
if (mod->fallible()) {
assignSnapshot(lir, mod->bailoutKind());
}
@@ -373,35 +358,26 @@ void LIRGeneratorX86Shared::lowerUDiv(MDiv* div) {
uint32_t rhs = div->rhs()->toConstant()->toInt32();
int32_t shift = FloorLog2(rhs);
+ LAllocation lhs = useRegisterAtStart(div->lhs());
if (rhs != 0 && uint32_t(1) << shift == rhs) {
- auto* lir = new (alloc()) LDivPowTwoI(useRegisterAtStart(div->lhs()),
- LAllocation(), shift, false);
+ LDivPowTwoI* lir = new (alloc()) LDivPowTwoI(lhs, lhs, shift, false);
if (div->fallible()) {
assignSnapshot(lir, div->bailoutKind());
}
defineReuseInput(lir, div, 0);
} else {
-#ifdef JS_CODEGEN_X86
- auto* lir = new (alloc())
- LUDivConstant(useRegister(div->lhs()), tempFixed(eax), rhs);
+ LUDivOrModConstant* lir = new (alloc())
+ LUDivOrModConstant(useRegister(div->lhs()), rhs, tempFixed(eax));
if (div->fallible()) {
assignSnapshot(lir, div->bailoutKind());
}
defineFixed(lir, div, LAllocation(AnyRegister(edx)));
-#else
- auto* lir =
- new (alloc()) LUDivConstant(useRegister(div->lhs()), temp(), rhs);
- if (div->fallible()) {
- assignSnapshot(lir, div->bailoutKind());
- }
- define(lir, div);
-#endif
}
return;
}
- auto* lir = new (alloc()) LUDiv(useFixedAtStart(div->lhs(), eax),
- useRegister(div->rhs()), tempFixed(edx));
+ LUDivOrMod* lir = new (alloc()) LUDivOrMod(
+ useRegister(div->lhs()), useRegister(div->rhs()), tempFixed(edx));
if (div->fallible()) {
assignSnapshot(lir, div->bailoutKind());
}
@@ -414,34 +390,25 @@ void LIRGeneratorX86Shared::lowerUMod(MMod* mod) {
int32_t shift = FloorLog2(rhs);
if (rhs != 0 && uint32_t(1) << shift == rhs) {
- auto* lir =
+ LModPowTwoI* lir =
new (alloc()) LModPowTwoI(useRegisterAtStart(mod->lhs()), shift);
if (mod->fallible()) {
assignSnapshot(lir, mod->bailoutKind());
}
defineReuseInput(lir, mod, 0);
} else {
-#ifdef JS_CODEGEN_X86
- auto* lir = new (alloc())
- LUModConstant(useRegister(mod->lhs()), tempFixed(edx), rhs);
+ LUDivOrModConstant* lir = new (alloc())
+ LUDivOrModConstant(useRegister(mod->lhs()), rhs, tempFixed(edx));
if (mod->fallible()) {
assignSnapshot(lir, mod->bailoutKind());
}
defineFixed(lir, mod, LAllocation(AnyRegister(eax)));
-#else
- auto* lir =
- new (alloc()) LUModConstant(useRegister(mod->lhs()), temp(), rhs);
- if (mod->fallible()) {
- assignSnapshot(lir, mod->bailoutKind());
- }
- define(lir, mod);
-#endif
}
return;
}
- auto* lir = new (alloc()) LUMod(useFixedAtStart(mod->lhs(), eax),
- useRegister(mod->rhs()), tempFixed(eax));
+ LUDivOrMod* lir = new (alloc()) LUDivOrMod(
+ useRegister(mod->lhs()), useRegister(mod->rhs()), tempFixed(eax));
if (mod->fallible()) {
assignSnapshot(lir, mod->bailoutKind());
}
diff --git a/js/src/jit/x86/CodeGenerator-x86.cpp b/js/src/jit/x86/CodeGenerator-x86.cpp
@@ -886,10 +886,10 @@ void CodeGenerator::visitDivOrModI64(LDivOrModI64* lir) {
masm.branch64(Assembler::NotEqual, rhs, Imm64(-1), ¬Overflow);
if (mir->isWasmBuiltinModI64()) {
masm.xor64(output, output);
- masm.jump(&done);
} else {
masm.wasmTrap(wasm::Trap::IntegerOverflow, lir->trapSiteDesc());
}
+ masm.jump(&done);
masm.bind(¬Overflow);
}