tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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:
Mjs/src/jit-test/lib/adhoc-multiplatform-test.js | 28++++++++++++++++++++++++----
Mjs/src/jit-test/lib/codegen-arm64-test.js | 5++---
Mjs/src/jit-test/lib/codegen-test-common.js | 21+++++++--------------
Mjs/src/jit-test/lib/codegen-x64-test.js | 5++---
Mjs/src/jit-test/lib/codegen-x86-test.js | 5++---
Djs/src/jit-test/tests/wasm/binop-divrem-with-constant-arm64-ion-codegen.js | 772-------------------------------------------------------------------------------
Djs/src/jit-test/tests/wasm/binop-divrem-with-constant-x64-ion-codegen.js | 719-------------------------------------------------------------------------------
Mjs/src/jit-test/tests/wasm/binop-x64-ion-codegen.js | 2+-
Mjs/src/jit/LIR.h | 11+++++++----
Mjs/src/jit/LIROps.yaml | 211+++++++++----------------------------------------------------------------------
Mjs/src/jit/MacroAssembler.cpp | 3+--
Mjs/src/jit/ReciprocalMulConstants.cpp | 69+++++++++++----------------------------------------------------------
Mjs/src/jit/ReciprocalMulConstants.h | 32++++++--------------------------
Mjs/src/jit/arm64/CodeGenerator-arm64.cpp | 275+------------------------------------------------------------------------------
Mjs/src/jit/arm64/Lowering-arm64.cpp | 87++++++++-----------------------------------------------------------------------
Mjs/src/jit/x64/Assembler-x64.h | 6------
Mjs/src/jit/x64/BaseAssembler-x64.h | 10----------
Mjs/src/jit/x64/CodeGenerator-x64.cpp | 407++++++++-----------------------------------------------------------------------
Mjs/src/jit/x64/LIR-x64.h | 81+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mjs/src/jit/x64/Lowering-x64.cpp | 122++++++++++++-------------------------------------------------------------------
Mjs/src/jit/x64/MacroAssembler-x64-inl.h | 16+++-------------
Mjs/src/jit/x64/MacroAssembler-x64.cpp | 4++--
Mjs/src/jit/x86-shared/CodeGenerator-x86-shared.cpp | 595+++++++++++++++++++++++++++----------------------------------------------------
Mjs/src/jit/x86-shared/CodeGenerator-x86-shared.h | 5-----
Ajs/src/jit/x86-shared/LIR-x86-shared.h | 130+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mjs/src/jit/x86-shared/Lowering-x86-shared.cpp | 121+++++++++++++++++++++++++++++--------------------------------------------------
Mjs/src/jit/x86/CodeGenerator-x86.cpp | 2+-
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), &notOverflow); - masm.branchPtr(Assembler::NotEqual, rhs, ImmWord(-1), &notOverflow); - masm.wasmTrap(wasm::Trap::IntegerOverflow, mir->trapSiteDesc()); - masm.bind(&notOverflow); - } - - // 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), &notOverflow); masm.branchPtr(Assembler::NotEqual, rhs, ImmWord(-1), &notOverflow); - { + if (lir->mir()->isMod()) { masm.xorl(output, output); - masm.jump(&done); + } else { + masm.wasmTrap(wasm::Trap::IntegerOverflow, lir->trapSiteDesc()); } + masm.jump(&done); masm.bind(&notOverflow); } @@ -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), &notOverflow); + masm.cmp32(lhs, Imm32(INT32_MIN)); + masm.j(Assembler::NotEqual, &notOverflow); + masm.cmp32(rhs, Imm32(-1)); if (mir->trapOnError()) { - masm.branch32(Assembler::NotEqual, rhs, Imm32(-1), &notOverflow); + masm.j(Assembler::NotEqual, &notOverflow); 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(&notOverflow); } @@ -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), &notOverflow); if (mir->isWasmBuiltinModI64()) { masm.xor64(output, output); - masm.jump(&done); } else { masm.wasmTrap(wasm::Trap::IntegerOverflow, lir->trapSiteDesc()); } + masm.jump(&done); masm.bind(&notOverflow); }