зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1753972 - Generalise `codegenTest{X64,X32,ARM64}_adhoc` and add consistent folding tests. r=lth.
The wasm test machinery has functions `codegenTest{X64,X32,ARM64}_adhoc` to compile tiny wasm fragments and compare generated code against expected regexps. While useful these have the following limitations: * arm(32) isn't handled * results from the other 3 Tier-1 targets, given the same input, are potentially scattered across multiple files These make it difficult to systematically assess folding and codegen effects across the 4 Tier-1 platforms. The attached patch: * adds `codegenTestMultiplatform_adhoc`, which takes expected-result strings for all 4 platforms and uses the correct one. Debug printing is also improved. For convenience, the result string for 32-bit arm may be omitted, in which case the relevant test is not run. * adds tests for the folding rules shown below, for both 32- and 64-bit operations. mips64 is not supported, because it has no disassembly facility. Various missing folding rules and much regalloc badness is thereby exposed. The behaviour of the compilers is unaltered; this patch merely serves as a convenient way to assess and document the current folding behaviour. ``` 0 * x => 0 1 * x => x -1 * x => -x 2 * x => x + x 4 * x => x << 2 x * 0 => 0 x * 1 => x x * -1 => -x x * 2 => x + x x * 4 => x << 2 x >> 0 => x (any shift kind: shl, shrU, shrS) x + 0 => x 0 + x => x x + x => x << 1 x - 0 => x 0 - x => -x x - x => 0 ``` Differential Revision: https://phabricator.services.mozilla.com/D138129
This commit is contained in:
Родитель
fcebffe5e8
Коммит
db99d20ce6
|
@ -0,0 +1,259 @@
|
|||
|
||||
// This file provides a version of the functions
|
||||
//
|
||||
// codegenTestX64_adhoc (src/jit-test/lib/codegen-x64-test.js)
|
||||
// codegenTestX86_adhoc (src/jit-test/lib/codegen-x86-test.js)
|
||||
// codegenTestARM64_adhoc (src/jit-test/lib/codegen-arm64-test.js)
|
||||
// (and the equivalent arm(32) function)
|
||||
//
|
||||
// generalised so the output can be specified for all 4 targets in one place.
|
||||
//
|
||||
// Usage:
|
||||
// codegenTestMultiplatform_adhoc(module_text, export_name,
|
||||
// expectedAllTargets, options = {})
|
||||
//
|
||||
// where
|
||||
// `expectedAllTargets` states the expected-output regexps for each target,
|
||||
// thusly:
|
||||
//
|
||||
// {x64: 'text', x86: 'text', arm64: 'text', arm: 'text'}
|
||||
//
|
||||
// The arm(32) expected output is optional. The other 3 must be present.
|
||||
//
|
||||
// Each 'text' is a string that represents a regular expression, possibly
|
||||
// with newlines, representing the instruction or instructions we are looking
|
||||
// for for the operator. Spaces in the expected-pattern are arbitrary, we
|
||||
// preprocess the pattern to replace any space string with \s+. Lines are
|
||||
// separated by newlines and leading and trailing spaces are stripped.
|
||||
// Pattern strings may be empty, denoting "no instruction(s)".
|
||||
//
|
||||
// options specifies options thusly:
|
||||
//
|
||||
// instanceBox: if present, an object with a `value` property that will
|
||||
// receive the constructed instance
|
||||
//
|
||||
// log: for debugging -- print the disassembly and other info helpful to
|
||||
// resolving test failures. This is also printed on a test failure
|
||||
// regardless of the log setting.
|
||||
//
|
||||
// features: this is passed on verbatim to wasmEvalText,
|
||||
// as its third argument.
|
||||
//
|
||||
// no_prefix: by default, the required pattern must be immediately preceded
|
||||
// by `<target>_prefix`, and this is checked. Setting this to
|
||||
// true skips the check. Try not to use this.
|
||||
//
|
||||
// no_suffix: by default, the required pattern must be immediately followed
|
||||
// by `<target>_suffix`, and this is checked. Setting this to
|
||||
// true skips the check. Try not to use this.
|
||||
//
|
||||
// no_prefix/no_suffix apply to all 4 targets. Per-target overrides are
|
||||
// supported, by putting them in a suitably tagged sub-object, eg:
|
||||
// options = {x86: {no_prefix: true}}
|
||||
|
||||
load(libdir + "codegen-test-common.js");
|
||||
|
||||
// Architectures supported by this script.
|
||||
const knownArchs = ["x64", "x86", "arm64", "arm"];
|
||||
|
||||
// Architectures for which `expectedAllTargets` must supply an expected result.
|
||||
const requiredArchs = ["x64", "x86", "arm64"];
|
||||
|
||||
// These define the end-of-prologue ("prefix") and start-of-epilogue
|
||||
// ("suffix") to be matched.
|
||||
const prefixAndSuffix =
|
||||
{x64: {
|
||||
prefix: `48 8b ec mov %rsp, %rbp`,
|
||||
suffix: `5d pop %rbp`
|
||||
},
|
||||
x86: {
|
||||
// The mov to e[ac]x is debug code, inserted by the register
|
||||
// allocator to clobber e[ac]x before a move group. But it is only
|
||||
// present if there is a move group there.
|
||||
prefix: `8b ec mov %esp, %ebp(
|
||||
b. ef be ad de mov \\$0xDEADBEEF, %e.x)?`,
|
||||
// `.bp` because zydis chooses `rbp` even on 32-bit systems.
|
||||
suffix: `5d pop %.bp`
|
||||
},
|
||||
arm64: {
|
||||
prefix: `910003fd mov x29, sp
|
||||
910003fc mov x28, sp`,
|
||||
suffix: `f94003fd ldr x29, \\[sp\\]`
|
||||
},
|
||||
arm: {
|
||||
prefix: `e52db004 str fp, \\[sp, #-4\\]!
|
||||
e1a0b00d mov fp, sp`,
|
||||
suffix: `e49db004 ldr fp, \\[sp\\], #\\+4`
|
||||
}
|
||||
};
|
||||
|
||||
// The options object may a mix of generic (all-targets) options and contain
|
||||
// sub-objects containing arch-specific options, for example:
|
||||
//
|
||||
// {a_generic_option: 1337, x86: {no_prefix:true}, arm64: {foo:4771}}
|
||||
//
|
||||
// promoteArchSpecificOptions lifts options for `archName` to the top level
|
||||
// and deletes *all* arch-specific subobjects, hence producing the final
|
||||
// to-be-used option set. For the above example, if `archName` is "x86" we
|
||||
// get:
|
||||
//
|
||||
// {a_generic_option: 1337, no_prefix: true}
|
||||
//
|
||||
function promoteArchSpecificOptions(options, archName) {
|
||||
assertEq(true, knownArchs.some(a => archName == a));
|
||||
if (options.hasOwnProperty(archName)) {
|
||||
let archOptions = options[archName];
|
||||
for (optName in archOptions) {
|
||||
options[optName] = archOptions[optName];
|
||||
if (options.log) {
|
||||
print("---- adding " + archName + "-specific option {"
|
||||
+ optName + ":" + archOptions[optName] + "}");
|
||||
}
|
||||
}
|
||||
}
|
||||
for (a of knownArchs) {
|
||||
delete options[a];
|
||||
}
|
||||
if (options.log) {
|
||||
print("---- final options");
|
||||
for (optName in options) {
|
||||
print("{" + optName + ":" + options[optName] + "}");
|
||||
}
|
||||
}
|
||||
return options;
|
||||
}
|
||||
|
||||
// Main test function. See comments at top of this file.
|
||||
function codegenTestMultiplatform_adhoc(module_text, export_name,
|
||||
expectedAllTargets, options = {}) {
|
||||
assertEq(hasDisassembler(), true);
|
||||
|
||||
// Check that we've been provided with an expected result for at least
|
||||
// x64, x86 and arm64.
|
||||
assertEq(true,
|
||||
requiredArchs.every(a => expectedAllTargets.hasOwnProperty(a)));
|
||||
|
||||
// Poke the build-configuration object to find out what target we're
|
||||
// generating code for.
|
||||
let conf = getBuildConfiguration();
|
||||
let genX64 = conf.x64;
|
||||
let genX86 = conf.x86;
|
||||
let genArm64 = conf.arm64;
|
||||
let genArm = conf.arm;
|
||||
// So far so good, except .. X64 or X86 might be emulating something else.
|
||||
if (genX64 && genArm64 && conf['arm64-simulator']) {
|
||||
genX64 = false;
|
||||
}
|
||||
if (genX86 && genArm && conf['arm-simulator']) {
|
||||
genX86 = false;
|
||||
}
|
||||
|
||||
// Check we've definitively identified exactly one architecture to test.
|
||||
assertEq(1, [genX64, genX86, genArm64, genArm].map(x => x ? 1 : 0)
|
||||
.reduce((a,b) => a+b, 0));
|
||||
|
||||
// Decide on the arch name for which we're testing. Everything is keyed
|
||||
// off this.
|
||||
let archName = "";
|
||||
if (genX64) {
|
||||
archName = "x64";
|
||||
} else if (genX86) {
|
||||
archName = "x86";
|
||||
} else if (genArm64) {
|
||||
archName = "arm64";
|
||||
} else if (genArm) {
|
||||
archName = "arm";
|
||||
}
|
||||
if (options.log) {
|
||||
print("---- testing for architecture \"" + archName + "\"");
|
||||
}
|
||||
// If this fails, it means we're running on an "unknown" architecture.
|
||||
assertEq(true, archName.length > 0);
|
||||
|
||||
// Finalise options, by promoting arch-specific ones to the top level of
|
||||
// the options object.
|
||||
options = promoteArchSpecificOptions(options, archName);
|
||||
|
||||
// Get the prefix and suffix strings for the target.
|
||||
assertEq(true, prefixAndSuffix.hasOwnProperty(archName));
|
||||
let prefix = prefixAndSuffix[archName].prefix;
|
||||
let suffix = prefixAndSuffix[archName].suffix;
|
||||
assertEq(true, prefix.length >= 10);
|
||||
assertEq(true, suffix.length >= 10);
|
||||
|
||||
// Get the expected output string, or skip the test if no expected output
|
||||
// has been provided. Note, because of the assertion near the top of this
|
||||
// file, this will currently only allow arm(32) tests to be skipped.
|
||||
let expected = "";
|
||||
if (expectedAllTargets.hasOwnProperty(archName)) {
|
||||
expected = expectedAllTargets[archName];
|
||||
} else {
|
||||
// Paranoia. Don't want to silently skip tests due to logic bugs above.
|
||||
assertEq(archName, "arm");
|
||||
if (options.log) {
|
||||
print("---- !! no expected output for target, skipping !!");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Finalise the expected-result string, and stash the original for
|
||||
// debug-printing.
|
||||
expectedInitial = expected;
|
||||
if (!options.no_prefix) {
|
||||
expected = prefix + '\n' + expected;
|
||||
}
|
||||
if (!options.no_suffix) {
|
||||
expected = expected + '\n' + suffix;
|
||||
}
|
||||
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);
|
||||
|
||||
// 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});
|
||||
|
||||
// Check for success, print diagnostics
|
||||
let output_matches_expected = output.match(new RegExp(expected)) != null;
|
||||
if (!output_matches_expected) {
|
||||
print("---- adhoc-tier1-test.js: TEST FAILED ----");
|
||||
}
|
||||
if (options.log && output_matches_expected) {
|
||||
print("---- adhoc-tier1-test.js: TEST PASSED ----");
|
||||
}
|
||||
if (options.log || !output_matches_expected) {
|
||||
print("---- module text");
|
||||
print(module_text);
|
||||
print("---- actual");
|
||||
print(output);
|
||||
print("---- expected (initial)");
|
||||
print(expectedInitial);
|
||||
print("---- expected (as used)");
|
||||
print(expected);
|
||||
print("----");
|
||||
}
|
||||
|
||||
// Finally, the whole point of this:
|
||||
assertEq(output_matches_expected, true);
|
||||
}
|
|
@ -0,0 +1,630 @@
|
|||
// |jit-test| skip-if: !hasDisassembler() || wasmCompileMode() != "ion" || getBuildConfiguration().windows || (!getBuildConfiguration().x64 && !getBuildConfiguration().x86 && !getBuildConfiguration().arm64 && !getBuildConfiguration().arm); include:adhoc-multiplatform-test.js
|
||||
//
|
||||
// These tests push wasm functions through the ion pipe and specify an expected
|
||||
// disassembly output on all 4 primary targets, x64 / x86 / arm64 / arm(32).
|
||||
// Results must be provided for the first 3, but can optionally be skipped
|
||||
// for arm(32).
|
||||
//
|
||||
// Hence: disassembler is needed, compiler must be ion.
|
||||
//
|
||||
// Windows is disallowed because the argument registers are different from on
|
||||
// Linux, and matching both is both difficult and not of much value.
|
||||
|
||||
// Tests are "end-to-end" in the sense that we don't care whether the
|
||||
// tested-for code improvement is done by MIR optimisation, or later in the
|
||||
// pipe. Observed defects are marked with FIXMEs for future easy finding.
|
||||
|
||||
|
||||
// Note that identities involving AND, OR and XOR are tested by
|
||||
// binop-x64-ion-folding.js
|
||||
|
||||
// Multiplication with magic constant on the left
|
||||
//
|
||||
// 0 * x => 0
|
||||
// 1 * x => x
|
||||
// -1 * x => -x
|
||||
// 2 * x => x + x
|
||||
// 4 * x => x << 2
|
||||
|
||||
codegenTestMultiplatform_adhoc(
|
||||
`(module (func (export "mul32_zeroL") (param $p1 i32) (result i32)
|
||||
(i32.mul (i32.const 0) (local.get $p1))))`,
|
||||
"mul32_zeroL",
|
||||
{x64: // FIXME move folding to MIR level
|
||||
// First we move edi to eax unnecessarily via ecx (bug 1752520),
|
||||
// then we overwrite eax. Presumably because the folding
|
||||
// 0 * x => 0 is done at the LIR level, not the MIR level, hence
|
||||
// the now-pointless WasmParameter node is not DCE'd away, since
|
||||
// DCE only happens at the MIR level. In fact all targets suffer
|
||||
// from the latter problem, but on x86 no_prefix_x86:true
|
||||
// hides it, and on arm32/64 the pointless move is correctly
|
||||
// transformed by RA into a no-op.
|
||||
`8b cf mov %edi, %ecx
|
||||
8b c1 mov %ecx, %eax
|
||||
33 c0 xor %eax, %eax`,
|
||||
x86: `33 c0 xor %eax, %eax`,
|
||||
arm64: `2a1f03e0 mov w0, wzr`,
|
||||
arm: `e3a00000 mov r0, #0`},
|
||||
{x86: {no_prefix:true}}
|
||||
);
|
||||
codegenTestMultiplatform_adhoc(
|
||||
`(module (func (export "mul64_zeroL") (param $p1 i64) (result i64)
|
||||
(i64.mul (i64.const 0) (local.get $p1))))`,
|
||||
"mul64_zeroL",
|
||||
// FIXME folding happened, zero-creation insns could be improved
|
||||
{x64: // Same shenanigans as above. Also, on xor, REX.W is redundant.
|
||||
`48 8b cf mov %rdi, %rcx
|
||||
48 8b c1 mov %rcx, %rax
|
||||
48 33 c0 xor %rax, %rax`,
|
||||
x86: `33 c0 xor %eax, %eax
|
||||
33 d2 xor %edx, %edx`,
|
||||
arm64: `aa1f03e0 mov x0, xzr`,
|
||||
arm: // bizarrely inconsistent with the 32-bit case
|
||||
`e0200000 eor r0, r0, r0
|
||||
e0211001 eor r1, r1, r1` },
|
||||
{x86: {no_prefix:true}}
|
||||
);
|
||||
|
||||
codegenTestMultiplatform_adhoc(
|
||||
`(module (func (export "mul32_oneL") (param $p1 i32) (result i32)
|
||||
(i32.mul (i32.const 1) (local.get $p1))))`,
|
||||
"mul32_oneL",
|
||||
{x64: `8b cf mov %edi, %ecx
|
||||
8b c1 mov %ecx, %eax`,
|
||||
x86: `8b 45 10 movl 0x10\\(%rbp\\), %eax`,
|
||||
arm64: ``,
|
||||
arm: ``},
|
||||
{x86: {no_prefix:true}}
|
||||
);
|
||||
codegenTestMultiplatform_adhoc(
|
||||
`(module (func (export "mul64_oneL") (param $p1 i64) (result i64)
|
||||
(i64.mul (i64.const 1) (local.get $p1))))`,
|
||||
"mul64_oneL",
|
||||
{x64: `48 8b cf mov %rdi, %rcx
|
||||
48 8b c1 mov %rcx, %rax`,
|
||||
x86: `8b 55 14 movl 0x14\\(%rbp\\), %edx
|
||||
8b 45 10 movl 0x10\\(%rbp\\), %eax`,
|
||||
arm64: ``,
|
||||
arm: ``},
|
||||
{x86: {no_prefix:true}}
|
||||
);
|
||||
|
||||
codegenTestMultiplatform_adhoc(
|
||||
`(module (func (export "mul32_minusOneL") (param $p1 i32) (result i32)
|
||||
(i32.mul (i32.const -1) (local.get $p1))))`,
|
||||
"mul32_minusOneL",
|
||||
{x64: `f7 d8 neg %eax`,
|
||||
x86: `f7 d8 neg %eax`,
|
||||
arm64: `4b0003e0 neg w0, w0`,
|
||||
arm: `e2600000 rsb r0, r0, #0`},
|
||||
{x86: {no_prefix:true}, x64: {no_prefix:true}}
|
||||
);
|
||||
codegenTestMultiplatform_adhoc(
|
||||
`(module (func (export "mul64_minusOneL") (param $p1 i64) (result i64)
|
||||
(i64.mul (i64.const -1) (local.get $p1))))`,
|
||||
"mul64_minusOneL",
|
||||
{x64: `48 8b cf mov %rdi, %rcx
|
||||
48 8b c1 mov %rcx, %rax
|
||||
48 f7 d8 neg %rax`,
|
||||
x86: `f7 d8 neg %eax
|
||||
83 d2 00 adc \\$0x00, %edx
|
||||
f7 da neg %edx`,
|
||||
arm64: `cb0003e0 neg x0, x0`,
|
||||
arm: `e2700000 rsbs r0, r0, #0
|
||||
e2e11000 rsc r1, r1, #0`},
|
||||
{x86: {no_prefix:true}}
|
||||
);
|
||||
|
||||
codegenTestMultiplatform_adhoc(
|
||||
`(module (func (export "mul32_twoL") (param $p1 i32) (result i32)
|
||||
(i32.mul (i32.const 2) (local.get $p1))))`,
|
||||
"mul32_twoL",
|
||||
{x64: `8b cf mov %edi, %ecx
|
||||
8b c1 mov %ecx, %eax
|
||||
03 c0 add %eax, %eax`,
|
||||
x86: `8b 45 10 movl 0x10\\(%rbp\\), %eax
|
||||
03 c0 add %eax, %eax`,
|
||||
arm64: `2b000000 adds w0, w0, w0`,
|
||||
arm: `e0900000 adds r0, r0, r0`},
|
||||
{x86: {no_prefix:true}}
|
||||
);
|
||||
codegenTestMultiplatform_adhoc(
|
||||
`(module (func (export "mul64_twoL") (param $p1 i64) (result i64)
|
||||
(i64.mul (i64.const 2) (local.get $p1))))`,
|
||||
"mul64_twoL",
|
||||
{x64: `48 8b cf mov %rdi, %rcx
|
||||
48 8b c1 mov %rcx, %rax
|
||||
48 03 c0 add %rax, %rax`,
|
||||
x86: `8b 55 14 movl 0x14\\(%rbp\\), %edx
|
||||
8b 45 10 movl 0x10\\(%rbp\\), %eax
|
||||
03 c0 add %eax, %eax
|
||||
13 d2 adc %edx, %edx`,
|
||||
arm64: `8b000000 add x0, x0, x0`,
|
||||
arm: `e0900000 adds r0, r0, r0
|
||||
e0a11001 adc r1, r1, r1`},
|
||||
{x86: {no_prefix:true}}
|
||||
);
|
||||
|
||||
codegenTestMultiplatform_adhoc(
|
||||
`(module (func (export "mul32_fourL") (param $p1 i32) (result i32)
|
||||
(i32.mul (i32.const 4) (local.get $p1))))`,
|
||||
"mul32_fourL",
|
||||
{x64: `8b cf mov %edi, %ecx
|
||||
8b c1 mov %ecx, %eax
|
||||
c1 e0 02 shl \\$0x02, %eax`,
|
||||
x86: `8b 45 10 movl 0x10\\(%rbp\\), %eax
|
||||
c1 e0 02 shl \\$0x02, %eax`,
|
||||
arm64: `531e7400 lsl w0, w0, #2`,
|
||||
arm: `e1a00100 mov r0, r0, lsl #2`},
|
||||
{x86: {no_prefix:true}}
|
||||
);
|
||||
codegenTestMultiplatform_adhoc(
|
||||
`(module (func (export "mul64_fourL") (param $p1 i64) (result i64)
|
||||
(i64.mul (i64.const 4) (local.get $p1))))`,
|
||||
"mul64_fourL",
|
||||
{x64: `48 8b cf mov %rdi, %rcx
|
||||
48 8b c1 mov %rcx, %rax
|
||||
48 c1 e0 02 shl \\$0x02, %rax`,
|
||||
x86: `8b 55 14 movl 0x14\\(%rbp\\), %edx
|
||||
8b 45 10 movl 0x10\\(%rbp\\), %eax
|
||||
0f a4 c2 02 shld \\$0x02, %eax, %edx
|
||||
c1 e0 02 shl \\$0x02, %eax`,
|
||||
arm64: `d37ef400 lsl x0, x0, #2`,
|
||||
arm: `e1a01101 mov r1, r1, lsl #2
|
||||
e1811f20 orr r1, r1, r0, lsr #30
|
||||
e1a00100 mov r0, r0, lsl #2`},
|
||||
{x86: {no_prefix:true}}
|
||||
);
|
||||
|
||||
// Multiplication with magic constant on the right
|
||||
//
|
||||
// x * 0 => 0
|
||||
// x * 1 => x
|
||||
// x * -1 => -x
|
||||
// x * 2 => x + x
|
||||
// x * 4 => x << 2
|
||||
|
||||
codegenTestMultiplatform_adhoc(
|
||||
`(module (func (export "mul32_zeroR") (param $p1 i32) (result i32)
|
||||
(i32.mul (local.get $p1) (i32.const 0))))`,
|
||||
"mul32_zeroR",
|
||||
{x64: `8b cf mov %edi, %ecx
|
||||
8b c1 mov %ecx, %eax
|
||||
33 c0 xor %eax, %eax`,
|
||||
x86: `33 c0 xor %eax, %eax`,
|
||||
arm64: `2a1f03e0 mov w0, wzr`,
|
||||
arm: `e3a00000 mov r0, #0`},
|
||||
{x86: {no_prefix:true}}
|
||||
);
|
||||
codegenTestMultiplatform_adhoc(
|
||||
`(module (func (export "mul64_zeroR") (param $p1 i64) (result i64)
|
||||
(i64.mul (local.get $p1) (i64.const 0))))`,
|
||||
"mul64_zeroR",
|
||||
{x64: `48 8b cf mov %rdi, %rcx
|
||||
48 8b c1 mov %rcx, %rax
|
||||
48 33 c0 xor %rax, %rax`, // REX.W is redundant
|
||||
x86: `33 c0 xor %eax, %eax
|
||||
33 d2 xor %edx, %edx`,
|
||||
arm64: `aa1f03e0 mov x0, xzr`,
|
||||
arm: `e0200000 eor r0, r0, r0
|
||||
e0211001 eor r1, r1, r1` },
|
||||
{x86: {no_prefix:true}}
|
||||
);
|
||||
|
||||
codegenTestMultiplatform_adhoc(
|
||||
`(module (func (export "mul32_oneR") (param $p1 i32) (result i32)
|
||||
(i32.mul (local.get $p1) (i32.const 1))))`,
|
||||
"mul32_oneR",
|
||||
{x64: `8b cf mov %edi, %ecx
|
||||
8b c1 mov %ecx, %eax`,
|
||||
x86: `8b 45 10 movl 0x10\\(%rbp\\), %eax`,
|
||||
arm64: ``,
|
||||
arm: ``},
|
||||
{x86: {no_prefix:true}}
|
||||
);
|
||||
codegenTestMultiplatform_adhoc(
|
||||
`(module (func (export "mul64_oneR") (param $p1 i64) (result i64)
|
||||
(i64.mul (local.get $p1) (i64.const 1))))`,
|
||||
"mul64_oneR",
|
||||
{x64: `48 8b cf mov %rdi, %rcx
|
||||
48 8b c1 mov %rcx, %rax`,
|
||||
x86: `8b 55 14 movl 0x14\\(%rbp\\), %edx
|
||||
8b 45 10 movl 0x10\\(%rbp\\), %eax`,
|
||||
arm64: ``,
|
||||
arm: ``},
|
||||
{x86: {no_prefix:true}}
|
||||
);
|
||||
|
||||
codegenTestMultiplatform_adhoc(
|
||||
`(module (func (export "mul32_minusOneR") (param $p1 i32) (result i32)
|
||||
(i32.mul (local.get $p1) (i32.const -1))))`,
|
||||
"mul32_minusOneR",
|
||||
{x64: `f7 d8 neg %eax`,
|
||||
x86: `f7 d8 neg %eax`,
|
||||
arm64: `4b0003e0 neg w0, w0`,
|
||||
arm: `e2600000 rsb r0, r0, #0`},
|
||||
{x86: {no_prefix:true}, x64: {no_prefix:true}}
|
||||
);
|
||||
codegenTestMultiplatform_adhoc(
|
||||
`(module (func (export "mul64_minusOneR") (param $p1 i64) (result i64)
|
||||
(i64.mul (local.get $p1) (i64.const -1))))`,
|
||||
"mul64_minusOneR",
|
||||
{x64: `48 8b cf mov %rdi, %rcx
|
||||
48 8b c1 mov %rcx, %rax
|
||||
48 f7 d8 neg %rax`,
|
||||
x86: `f7 d8 neg %eax
|
||||
83 d2 00 adc \\$0x00, %edx
|
||||
f7 da neg %edx`,
|
||||
arm64: `cb0003e0 neg x0, x0`,
|
||||
arm: `e2700000 rsbs r0, r0, #0
|
||||
e2e11000 rsc r1, r1, #0`},
|
||||
{x86: {no_prefix:true}}
|
||||
);
|
||||
|
||||
codegenTestMultiplatform_adhoc(
|
||||
`(module (func (export "mul32_twoR") (param $p1 i32) (result i32)
|
||||
(i32.mul (local.get $p1) (i32.const 2))))`,
|
||||
"mul32_twoR",
|
||||
{x64: `8b cf mov %edi, %ecx
|
||||
8b c1 mov %ecx, %eax
|
||||
03 c0 add %eax, %eax`,
|
||||
x86: `8b 45 10 movl 0x10\\(%rbp\\), %eax
|
||||
03 c0 add %eax, %eax`,
|
||||
arm64: `2b000000 adds w0, w0, w0`,
|
||||
arm: `e0900000 adds r0, r0, r0`},
|
||||
{x86: {no_prefix:true}}
|
||||
);
|
||||
codegenTestMultiplatform_adhoc(
|
||||
`(module (func (export "mul64_twoR") (param $p1 i64) (result i64)
|
||||
(i64.mul (local.get $p1) (i64.const 2))))`,
|
||||
"mul64_twoR",
|
||||
{x64: `48 8b cf mov %rdi, %rcx
|
||||
48 8b c1 mov %rcx, %rax
|
||||
48 03 c0 add %rax, %rax`,
|
||||
x86: `8b 55 14 movl 0x14\\(%rbp\\), %edx
|
||||
8b 45 10 movl 0x10\\(%rbp\\), %eax
|
||||
03 c0 add %eax, %eax
|
||||
13 d2 adc %edx, %edx`,
|
||||
arm64: `8b000000 add x0, x0, x0`,
|
||||
arm: `e0900000 adds r0, r0, r0
|
||||
e0a11001 adc r1, r1, r1`},
|
||||
{x86: {no_prefix:true}}
|
||||
);
|
||||
|
||||
codegenTestMultiplatform_adhoc(
|
||||
`(module (func (export "mul32_fourR") (param $p1 i32) (result i32)
|
||||
(i32.mul (local.get $p1) (i32.const 4))))`,
|
||||
"mul32_fourR",
|
||||
{x64: `8b cf mov %edi, %ecx
|
||||
8b c1 mov %ecx, %eax
|
||||
c1 e0 02 shl \\$0x02, %eax`,
|
||||
x86: `8b 45 10 movl 0x10\\(%rbp\\), %eax
|
||||
c1 e0 02 shl \\$0x02, %eax`,
|
||||
arm64: `531e7400 lsl w0, w0, #2`,
|
||||
arm: `e1a00100 mov r0, r0, lsl #2`},
|
||||
{x86: {no_prefix:true}}
|
||||
);
|
||||
codegenTestMultiplatform_adhoc(
|
||||
`(module (func (export "mul64_fourR") (param $p1 i64) (result i64)
|
||||
(i64.mul (local.get $p1) (i64.const 4))))`,
|
||||
"mul64_fourR",
|
||||
{x64: `48 8b cf mov %rdi, %rcx
|
||||
48 8b c1 mov %rcx, %rax
|
||||
48 c1 e0 02 shl \\$0x02, %rax`,
|
||||
x86: `8b 55 14 movl 0x14\\(%rbp\\), %edx
|
||||
8b 45 10 movl 0x10\\(%rbp\\), %eax
|
||||
0f a4 c2 02 shld \\$0x02, %eax, %edx
|
||||
c1 e0 02 shl \\$0x02, %eax`,
|
||||
arm64: `d37ef400 lsl x0, x0, #2`,
|
||||
arm: `e1a01101 mov r1, r1, lsl #2
|
||||
e1811f20 orr r1, r1, r0, lsr #30
|
||||
e1a00100 mov r0, r0, lsl #2`
|
||||
},
|
||||
{x86: {no_prefix:true}}
|
||||
);
|
||||
|
||||
// Shifts by zero (the right arg is zero)
|
||||
//
|
||||
// x >> 0 => x (any shift kind: shl, shrU, shrS)
|
||||
|
||||
codegenTestMultiplatform_adhoc(
|
||||
`(module (func (export "shl32_zeroR") (param $p1 i32) (result i32)
|
||||
(i32.shl (local.get $p1) (i32.const 0))))`,
|
||||
"shl32_zeroR",
|
||||
// FIXME check these are consistently folded out at the MIR level
|
||||
{x64: `8b cf mov %edi, %ecx
|
||||
8b c1 mov %ecx, %eax`,
|
||||
x86: `8b 45 10 movl 0x10\\(%rbp\\), %eax`,
|
||||
arm64: // Regalloc badness, plus not folded out at the MIR level
|
||||
`2a0003e2 mov w2, w0
|
||||
2a0203e1 mov w1, w2
|
||||
53007c20 lsr w0, w1, #0`, // Uhh. lsr ?!
|
||||
arm: `e1a02000 mov r2, r0
|
||||
e1a01002 mov r1, r2
|
||||
e1a00001 mov r0, r1`
|
||||
},
|
||||
{x86: {no_prefix:true}}
|
||||
);
|
||||
codegenTestMultiplatform_adhoc(
|
||||
`(module (func (export "shl64_zeroR") (param $p1 i64) (result i64)
|
||||
(i64.shl (local.get $p1) (i64.const 0))))`,
|
||||
"shl64_zeroR",
|
||||
// FIXME why is this code so much better than the 32-bit case?
|
||||
{x64: `48 8b cf mov %rdi, %rcx
|
||||
48 8b c1 mov %rcx, %rax`,
|
||||
x86: `8b 55 14 movl 0x14\\(%rbp\\), %edx
|
||||
8b 45 10 movl 0x10\\(%rbp\\), %eax`,
|
||||
arm64: ``, // no-op
|
||||
arm: `` // no-op
|
||||
},
|
||||
{x86: {no_prefix:true}}
|
||||
);
|
||||
|
||||
codegenTestMultiplatform_adhoc(
|
||||
`(module (func (export "shrU32_zeroR") (param $p1 i32) (result i32)
|
||||
(i32.shr_u (local.get $p1) (i32.const 0))))`,
|
||||
"shrU32_zeroR",
|
||||
{x64: `8b cf mov %edi, %ecx
|
||||
8b c1 mov %ecx, %eax`,
|
||||
x86: `8b 45 10 movl 0x10\\(%rbp\\), %eax`,
|
||||
arm64: `2a0003e2 mov w2, w0
|
||||
2a0203e1 mov w1, w2
|
||||
2a0103e0 mov w0, w1`,
|
||||
arm: `e1a02000 mov r2, r0
|
||||
e1a01002 mov r1, r2
|
||||
e1a00001 mov r0, r1`
|
||||
},
|
||||
{x86: {no_prefix:true}}
|
||||
);
|
||||
codegenTestMultiplatform_adhoc(
|
||||
`(module (func (export "shrU64_zeroR") (param $p1 i64) (result i64)
|
||||
(i64.shr_u (local.get $p1) (i64.const 0))))`,
|
||||
"shrU64_zeroR",
|
||||
{x64: `48 8b cf mov %rdi, %rcx
|
||||
48 8b c1 mov %rcx, %rax`,
|
||||
x86: `8b 55 14 movl 0x14\\(%rbp\\), %edx
|
||||
8b 45 10 movl 0x10\\(%rbp\\), %eax`,
|
||||
arm64: ``,
|
||||
arm: ``
|
||||
},
|
||||
{x86: {no_prefix:true}}
|
||||
);
|
||||
|
||||
codegenTestMultiplatform_adhoc(
|
||||
`(module (func (export "shrS32_zeroR") (param $p1 i32) (result i32)
|
||||
(i32.shr_s (local.get $p1) (i32.const 0))))`,
|
||||
"shrS32_zeroR",
|
||||
{x64: `8b cf mov %edi, %ecx
|
||||
8b c1 mov %ecx, %eax`,
|
||||
x86: `8b 45 10 movl 0x10\\(%rbp\\), %eax`,
|
||||
arm64: `2a0003e2 mov w2, w0
|
||||
2a0203e1 mov w1, w2
|
||||
13007c20 sbfx w0, w1, #0, #32`,
|
||||
arm: `e1a02000 mov r2, r0
|
||||
e1a01002 mov r1, r2
|
||||
e1a00001 mov r0, r1`
|
||||
},
|
||||
{x86: {no_prefix:true}}
|
||||
);
|
||||
codegenTestMultiplatform_adhoc(
|
||||
`(module (func (export "shrS64_zeroR") (param $p1 i64) (result i64)
|
||||
(i64.shr_s (local.get $p1) (i64.const 0))))`,
|
||||
"shrS64_zeroR",
|
||||
{x64: `48 8b cf mov %rdi, %rcx
|
||||
48 8b c1 mov %rcx, %rax`,
|
||||
x86: `8b 55 14 movl 0x14\\(%rbp\\), %edx
|
||||
8b 45 10 movl 0x10\\(%rbp\\), %eax`,
|
||||
arm64: ``,
|
||||
arm: ``
|
||||
},
|
||||
{x86: {no_prefix:true}}
|
||||
);
|
||||
|
||||
// Identities involving addition
|
||||
//
|
||||
// x + 0 => x
|
||||
// 0 + x => x
|
||||
// x + x => x << 1
|
||||
|
||||
codegenTestMultiplatform_adhoc(
|
||||
`(module (func (export "add32_zeroR") (param $p1 i32) (result i32)
|
||||
(i32.add (local.get $p1) (i32.const 0))))`,
|
||||
"add32_zeroR",
|
||||
{x64: `8b cf mov %edi, %ecx
|
||||
8b c1 mov %ecx, %eax`,
|
||||
x86: `8b 45 10 movl 0x10\\(%rbp\\), %eax`,
|
||||
arm64: ``,
|
||||
arm: ``
|
||||
},
|
||||
{x86: {no_prefix:true}}
|
||||
);
|
||||
codegenTestMultiplatform_adhoc(
|
||||
`(module (func (export "add64_zeroR") (param $p1 i64) (result i64)
|
||||
(i64.add (local.get $p1) (i64.const 0))))`,
|
||||
"add64_zeroR",
|
||||
{x64: `48 8b cf mov %rdi, %rcx
|
||||
48 8b c1 mov %rcx, %rax`,
|
||||
x86: `8b 55 14 movl 0x14\\(%rbp\\), %edx
|
||||
8b 45 10 movl 0x10\\(%rbp\\), %eax`,
|
||||
arm64: ``,
|
||||
arm: ``
|
||||
},
|
||||
{x86: {no_prefix:true}}
|
||||
);
|
||||
|
||||
codegenTestMultiplatform_adhoc(
|
||||
`(module (func (export "add32_zeroL") (param $p1 i32) (result i32)
|
||||
(i32.add (i32.const 0) (local.get $p1))))`,
|
||||
"add32_zeroL",
|
||||
{x64: `8b cf mov %edi, %ecx
|
||||
8b c1 mov %ecx, %eax`,
|
||||
x86: `8b 45 10 movl 0x10\\(%rbp\\), %eax`,
|
||||
arm64: ``,
|
||||
arm: ``
|
||||
},
|
||||
{x86: {no_prefix:true}}
|
||||
);
|
||||
codegenTestMultiplatform_adhoc(
|
||||
`(module (func (export "add64_zeroL") (param $p1 i64) (result i64)
|
||||
(i64.add (i64.const 0) (local.get $p1))))`,
|
||||
"add64_zeroL",
|
||||
{x64: `48 8b cf mov %rdi, %rcx
|
||||
48 8b c1 mov %rcx, %rax`,
|
||||
x86: `8b 55 14 movl 0x14\\(%rbp\\), %edx
|
||||
8b 45 10 movl 0x10\\(%rbp\\), %eax`,
|
||||
arm64: ``,
|
||||
arm: ``
|
||||
},
|
||||
{x86: {no_prefix:true}}
|
||||
);
|
||||
|
||||
codegenTestMultiplatform_adhoc(
|
||||
`(module (func (export "add32_self") (param $p1 i32) (result i32)
|
||||
(i32.add (local.get $p1) (local.get $p1))))`,
|
||||
"add32_self",
|
||||
{x64: `8b cf mov %edi, %ecx
|
||||
8b c1 mov %ecx, %eax
|
||||
03 c1 add %ecx, %eax`,
|
||||
x86: `8b 45 10 movl 0x10\\(%rbp\\), %eax
|
||||
03 45 10 addl 0x10\\(%rbp\\), %eax`,
|
||||
arm64: `0b000000 add w0, w0, w0`,
|
||||
arm: `e0900000 adds r0, r0, r0 `
|
||||
},
|
||||
{x86: {no_prefix:true}}
|
||||
);
|
||||
codegenTestMultiplatform_adhoc(
|
||||
`(module (func (export "add64_self") (param $p1 i64) (result i64)
|
||||
(i64.add (local.get $p1) (local.get $p1))))`,
|
||||
"add64_self",
|
||||
// FIXME outstandingly bad 32-bit sequences, probably due to the RA
|
||||
{x64: `48 8b cf mov %rdi, %rcx
|
||||
48 8b c1 mov %rcx, %rax
|
||||
48 03 c1 add %rcx, %rax`,
|
||||
x86: `8b 5d 14 movl 0x14\\(%rbp\\), %ebx
|
||||
8b 4d 10 movl 0x10\\(%rbp\\), %ecx
|
||||
bf ef be ad de mov \\$0xDEADBEEF, %edi
|
||||
8b 55 14 movl 0x14\\(%rbp\\), %edx
|
||||
8b 45 10 movl 0x10\\(%rbp\\), %eax
|
||||
03 c1 add %ecx, %eax
|
||||
13 d3 adc %ebx, %edx`,
|
||||
arm64: `8b000000 add x0, x0, x0`,
|
||||
arm: // play Musical Chairs for a while
|
||||
`e1a03001 mov r3, r1
|
||||
e1a02000 mov r2, r0
|
||||
e1a05003 mov r5, r3
|
||||
e1a04002 mov r4, r2
|
||||
e1a01003 mov r1, r3
|
||||
e1a00002 mov r0, r2
|
||||
e0900004 adds r0, r0, r4
|
||||
e0a11005 adc r1, r1, r5`
|
||||
},
|
||||
{x86: {no_prefix:true}}
|
||||
);
|
||||
|
||||
// Identities involving subtraction
|
||||
//
|
||||
// x - 0 => x
|
||||
// 0 - x => -x
|
||||
// x - x => 0
|
||||
|
||||
codegenTestMultiplatform_adhoc(
|
||||
`(module (func (export "sub32_zeroR") (param $p1 i32) (result i32)
|
||||
(i32.sub (local.get $p1) (i32.const 0))))`,
|
||||
"sub32_zeroR",
|
||||
{x64: `8b cf mov %edi, %ecx
|
||||
8b c1 mov %ecx, %eax`,
|
||||
x86: `8b 45 10 movl 0x10\\(%rbp\\), %eax`,
|
||||
arm64: ``,
|
||||
arm: ``
|
||||
},
|
||||
{x86: {no_prefix:true}}
|
||||
);
|
||||
codegenTestMultiplatform_adhoc(
|
||||
`(module (func (export "sub64_zeroR") (param $p1 i64) (result i64)
|
||||
(i64.sub (local.get $p1) (i64.const 0))))`,
|
||||
"sub64_zeroR",
|
||||
// FIXME folding missing for all 4 targets
|
||||
{x64: `48 8b cf mov %rdi, %rcx
|
||||
48 8b c1 mov %rcx, %rax
|
||||
48 83 e8 00 sub \\$0x00, %rax`,
|
||||
x86: `8b 55 14 movl 0x14\\(%rbp\\), %edx
|
||||
8b 45 10 movl 0x10\\(%rbp\\), %eax
|
||||
83 ea 00 sub \\$0x00, %edx`,
|
||||
arm64: ``,
|
||||
arm: `e2500000 subs r0, r0, #0
|
||||
e2c11000 sbc r1, r1, #0`
|
||||
},
|
||||
{x86: {no_prefix:true}}
|
||||
);
|
||||
|
||||
codegenTestMultiplatform_adhoc(
|
||||
`(module (func (export "sub32_zeroL") (param $p1 i32) (result i32)
|
||||
(i32.sub (i32.const 0) (local.get $p1))))`,
|
||||
"sub32_zeroL",
|
||||
{x64: `8b cf mov %edi, %ecx
|
||||
8b c1 mov %ecx, %eax
|
||||
f7 d8 neg %eax`,
|
||||
x86: `8b 45 10 movl 0x10\\(%rbp\\), %eax
|
||||
f7 d8 neg %eax`,
|
||||
arm64: `4b0003e0 neg w0, w0 `,
|
||||
arm: `e2600000 rsb r0, r0, #0`
|
||||
},
|
||||
{x86: {no_prefix:true}}
|
||||
);
|
||||
codegenTestMultiplatform_adhoc(
|
||||
`(module (func (export "sub64_zeroL") (param $p1 i64) (result i64)
|
||||
(i64.sub (i64.const 0) (local.get $p1))))`,
|
||||
"sub64_zeroL",
|
||||
{x64: `48 8b cf mov %rdi, %rcx
|
||||
48 8b c1 mov %rcx, %rax
|
||||
48 f7 d8 neg %rax`,
|
||||
x86: `8b 55 14 movl 0x14\\(%rbp\\), %edx
|
||||
8b 45 10 movl 0x10\\(%rbp\\), %eax
|
||||
f7 d8 neg %eax
|
||||
83 d2 00 adc \\$0x00, %edx
|
||||
f7 da neg %edx`,
|
||||
arm64: `cb0003e0 neg x0, x0`,
|
||||
arm: `e2700000 rsbs r0, r0, #0
|
||||
e2e11000 rsc r1, r1, #0`
|
||||
},
|
||||
{x86: {no_prefix:true}}
|
||||
);
|
||||
|
||||
codegenTestMultiplatform_adhoc(
|
||||
`(module (func (export "sub32_self") (param $p1 i32) (result i32)
|
||||
(i32.sub (local.get $p1) (local.get $p1))))`,
|
||||
"sub32_self",
|
||||
{x64: `33 c0 xor %eax, %eax`,
|
||||
x86: `33 c0 xor %eax, %eax`,
|
||||
arm64: `52800000 mov w0, #0x0`,
|
||||
arm: `e3a00000 mov r0, #0`
|
||||
},
|
||||
{x86: {no_prefix:true}}
|
||||
);
|
||||
codegenTestMultiplatform_adhoc(
|
||||
`(module (func (export "sub64_self") (param $p1 i64) (result i64)
|
||||
(i64.sub (local.get $p1) (local.get $p1))))`,
|
||||
"sub64_self",
|
||||
// FIXME folding missing for all 4 targets
|
||||
{x64: `48 8b cf mov %rdi, %rcx
|
||||
48 8b c1 mov %rcx, %rax
|
||||
48 2b c1 sub %rcx, %rax`,
|
||||
x86: `8b 5d 14 movl 0x14\\(%rbp\\), %ebx
|
||||
8b 4d 10 movl 0x10\\(%rbp\\), %ecx
|
||||
bf ef be ad de mov \\$0xDEADBEEF, %edi
|
||||
8b 55 14 movl 0x14\\(%rbp\\), %edx
|
||||
8b 45 10 movl 0x10\\(%rbp\\), %eax
|
||||
2b c1 sub %ecx, %eax
|
||||
1b d3 sbb %ebx, %edx`,
|
||||
arm64: `cb000000 sub x0, x0, x0`,
|
||||
arm: `e1a03001 mov r3, r1
|
||||
e1a02000 mov r2, r0
|
||||
e1a05003 mov r5, r3
|
||||
e1a04002 mov r4, r2
|
||||
e1a01003 mov r1, r3
|
||||
e1a00002 mov r0, r2
|
||||
e0500004 subs r0, r0, r4
|
||||
e0c11005 sbc r1, r1, r5`
|
||||
},
|
||||
{x86: {no_prefix:true}}
|
||||
);
|
Загрузка…
Ссылка в новой задаче