5b0285866d
This reverts commit415b040d6f. Reason for revert: breaks riscv https://github.com/dart-lang/sdk/issues/63479 Original change's description: > Reland "[vm] Recognize int.trailingZeroBitCount/oneBitCount as graph-inlinable" > > The previous attempt was reverted because it broke unoptimized JIT > on ARM 32. This reland force-optimizes the two getters. > > Stacks on top of the int.{trailingZeroBitCount,oneBitCount} API CL > (commit754239b077). Both getters route through OTHER_RECOGNIZED_LIST > when a hardware fast path is available; otherwise the newly added > Dart bodies inline at call sites via vm:prefer-inline. The C++ > natives are removed. > > Backend codegen > --------------- > ARM64: NEON CNT + UADDLV (popcount); RBIT + CLZ (ctz). > ARM: NEON CNT + VPADDL chain (popcount); RBIT + CLZ on the > register pair (ctz). > x64: popcntq when TargetCPUFeatures::popcnt_supported(); > LoadImmediate(64) + rep_bsfq for ctz (decodes as tzcnt > on BMI1+, preserves dest on zero otherwise). > RISC-V 64: cpop / ctz when RV_baseline includes Zbb. > > Per-arch availability is encapsulated in > UnaryInt64OpInstr::IsSupported(Token::Kind). > > Apple M-series ARM64, AOT (us/iter, lower is better): > cardinality.swar 371 > cardinality.accelerated 154 (2.4x) > forEachSetBit.swar 19031 > forEachSetBit.accelerated 4988 (3.8x) > select.swar 199 > select.accelerated 77 (2.6x) > complementCardinality.swar 399 > complementCardinality.accel 152 (2.6x) > > Work towards https://github.com/dart-lang/sdk/issues/6486 (popcount > and ctz intrinsification). > > Work towards https://github.com/dart-lang/sdk/issues/1053 (efficient > BitSet implementation). > > Fixes https://github.com/dart-lang/sdk/issues/52673 > Fixes https://github.com/dart-lang/sdk/issues/38346 > Fixes https://github.com/dart-lang/sdk/issues/63436 > Issue https://github.com/dart-lang/sdk/issues/10212 > Issue https://github.com/dart-lang/sdk/issues/5798 > TEST=tests/corelib/int_bit_count_test > > Cq-Include-Trybots: luci.dart.try:vm-linux-release-simarm-try,vm-ffi-qemu-linux-release-arm-try,vm-aot-linux-release-simarm_x64-try,vm-aot-linux-debug-simarm_x64-try,dart-sdk-linux-riscv64-try > Change-Id: Ib812cbaec6e371b9720df7a543411f78e524cac1 > Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/506060 > Reviewed-by: Martin Kustermann <kustermann@google.com> > Auto-Submit: Modestas Valauskas <valauskasmodestas@gmail.com> > Reviewed-by: Slava Egorov <vegorov@google.com> > Commit-Queue: Martin Kustermann <kustermann@google.com> Cq-Include-Trybots: luci.dart.try:vm-linux-release-simarm-try,vm-ffi-qemu-linux-release-arm-try,vm-aot-linux-release-simarm_x64-try,vm-aot-linux-debug-simarm_x64-try,dart-sdk-linux-riscv64-try No-Presubmit: true No-Tree-Checks: true No-Try: true Change-Id: Iaf11d03d394fa615098bed8fcdea38ba40c7e45f Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/507520 Bot-Commit: rubber-stamper@appspot.gserviceaccount.com <rubber-stamper@appspot.gserviceaccount.com> Commit-Queue: Alexander Aprelev <aam@google.com> Reviewed-by: Kevin Moore <kevmoo@google.com>
6569 lines
183 KiB
C++
6569 lines
183 KiB
C++
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
|
|
// for details. All rights reserved. Use of this source code is governed by a
|
|
// BSD-style license that can be found in the LICENSE file.
|
|
|
|
#include "vm/globals.h"
|
|
#if defined(TARGET_ARCH_X64)
|
|
|
|
#include "vm/compiler/assembler/assembler.h"
|
|
#include "vm/compiler/assembler/assembler_test.h"
|
|
#include "vm/compiler/backend/locations.h"
|
|
#include "vm/cpu.h"
|
|
#include "vm/os.h"
|
|
#include "vm/unit_test.h"
|
|
#include "vm/virtual_memory.h"
|
|
#include "vm/zone_text_buffer.h"
|
|
|
|
namespace dart {
|
|
namespace compiler {
|
|
|
|
#define __ assembler->
|
|
|
|
#if defined(PRODUCT)
|
|
#define EXPECT_DISASSEMBLY(expected)
|
|
#define EXPECT_DISASSEMBLY_ENDS_WITH(expected)
|
|
#define EXPECT_DISASSEMBLY_NOT_WINDOWS(expected)
|
|
#define EXPECT_DISASSEMBLY_NOT_WINDOWS_ENDS_WITH(expected)
|
|
#else
|
|
#define EXPECT_DISASSEMBLY(expected) \
|
|
EXPECT_STREQ(expected, test->RelativeDisassembly())
|
|
#define EXPECT_DISASSEMBLY_ENDS_WITH(expected_arg) \
|
|
char* disassembly = test->RelativeDisassembly(); \
|
|
const char* expected = expected_arg; \
|
|
intptr_t dis_len = strlen(disassembly); \
|
|
intptr_t exp_len = strlen(expected); \
|
|
EXPECT_GT(dis_len, exp_len); \
|
|
EXPECT_STREQ(expected, disassembly + dis_len - exp_len);
|
|
#if defined(DART_TARGET_OS_WINDOWS)
|
|
// Windows has different calling conventions on x64, which means the
|
|
// disassembly looks different on some tests. We skip testing the
|
|
// disassembly output for those tests on Windows.
|
|
#define EXPECT_DISASSEMBLY_NOT_WINDOWS(expected)
|
|
#define EXPECT_DISASSEMBLY_NOT_WINDOWS_ENDS_WITH(expected)
|
|
#else
|
|
#define EXPECT_DISASSEMBLY_NOT_WINDOWS(expected) EXPECT_DISASSEMBLY(expected)
|
|
#define EXPECT_DISASSEMBLY_NOT_WINDOWS_ENDS_WITH(expected) \
|
|
EXPECT_DISASSEMBLY_ENDS_WITH(expected)
|
|
#endif
|
|
#endif
|
|
|
|
ASSEMBLER_TEST_GENERATE(ReadArgument, assembler) {
|
|
__ pushq(CallingConventions::kArg1Reg);
|
|
__ movq(RAX, Address(RSP, 0));
|
|
__ popq(RDX);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(ReadArgument, test) {
|
|
typedef int64_t (*ReadArgumentCode)(int64_t n);
|
|
ReadArgumentCode id = reinterpret_cast<ReadArgumentCode>(test->entry());
|
|
EXPECT_EQ(42, id(42));
|
|
EXPECT_EQ(87, id(87));
|
|
const int64_t kLargeConstant = 0x1234567812345678LL;
|
|
EXPECT_EQ(kLargeConstant, id(kLargeConstant));
|
|
EXPECT_DISASSEMBLY_NOT_WINDOWS(
|
|
"push rdi\n"
|
|
"movq rax,[rsp]\n"
|
|
"pop rdx\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(AddressingModes, assembler) {
|
|
__ movq(RAX, Address(RSP, 0));
|
|
__ movq(RAX, Address(RBP, 0));
|
|
__ movq(RAX, Address(RAX, 0));
|
|
__ movq(RAX, Address(R10, 0));
|
|
__ movq(RAX, Address(R12, 0));
|
|
__ movq(RAX, Address(R13, 0));
|
|
__ movq(R10, Address(RAX, 0));
|
|
|
|
__ movq(RAX, Address(RSP, target::kWordSize));
|
|
__ movq(RAX, Address(RBP, target::kWordSize));
|
|
__ movq(RAX, Address(RAX, target::kWordSize));
|
|
__ movq(RAX, Address(R10, target::kWordSize));
|
|
__ movq(RAX, Address(R12, target::kWordSize));
|
|
__ movq(RAX, Address(R13, target::kWordSize));
|
|
|
|
__ movq(RAX, Address(RSP, -target::kWordSize));
|
|
__ movq(RAX, Address(RBP, -target::kWordSize));
|
|
__ movq(RAX, Address(RAX, -target::kWordSize));
|
|
__ movq(RAX, Address(R10, -target::kWordSize));
|
|
__ movq(RAX, Address(R12, -target::kWordSize));
|
|
__ movq(RAX, Address(R13, -target::kWordSize));
|
|
|
|
__ movq(RAX, Address(RSP, 256 * target::kWordSize));
|
|
__ movq(RAX, Address(RBP, 256 * target::kWordSize));
|
|
__ movq(RAX, Address(RAX, 256 * target::kWordSize));
|
|
__ movq(RAX, Address(R10, 256 * target::kWordSize));
|
|
__ movq(RAX, Address(R12, 256 * target::kWordSize));
|
|
__ movq(RAX, Address(R13, 256 * target::kWordSize));
|
|
|
|
__ movq(RAX, Address(RSP, -256 * target::kWordSize));
|
|
__ movq(RAX, Address(RBP, -256 * target::kWordSize));
|
|
__ movq(RAX, Address(RAX, -256 * target::kWordSize));
|
|
__ movq(RAX, Address(R10, -256 * target::kWordSize));
|
|
__ movq(RAX, Address(R12, -256 * target::kWordSize));
|
|
__ movq(RAX, Address(R13, -256 * target::kWordSize));
|
|
|
|
__ movq(RAX, Address(RAX, TIMES_1, 0));
|
|
__ movq(RAX, Address(RAX, TIMES_2, 0));
|
|
__ movq(RAX, Address(RAX, TIMES_4, 0));
|
|
__ movq(RAX, Address(RAX, TIMES_8, 0));
|
|
|
|
__ movq(RAX, Address(RBP, TIMES_2, 0));
|
|
__ movq(RAX, Address(RAX, TIMES_2, 0));
|
|
__ movq(RAX, Address(R10, TIMES_2, 0));
|
|
__ movq(RAX, Address(R12, TIMES_2, 0));
|
|
__ movq(RAX, Address(R13, TIMES_2, 0));
|
|
|
|
__ movq(RAX, Address(RBP, TIMES_2, target::kWordSize));
|
|
__ movq(RAX, Address(RAX, TIMES_2, target::kWordSize));
|
|
__ movq(RAX, Address(R10, TIMES_2, target::kWordSize));
|
|
__ movq(RAX, Address(R12, TIMES_2, target::kWordSize));
|
|
__ movq(RAX, Address(R13, TIMES_2, target::kWordSize));
|
|
|
|
__ movq(RAX, Address(RBP, TIMES_2, 256 * target::kWordSize));
|
|
__ movq(RAX, Address(RAX, TIMES_2, 256 * target::kWordSize));
|
|
__ movq(RAX, Address(R10, TIMES_2, 256 * target::kWordSize));
|
|
__ movq(RAX, Address(R12, TIMES_2, 256 * target::kWordSize));
|
|
__ movq(RAX, Address(R13, TIMES_2, 256 * target::kWordSize));
|
|
|
|
__ movq(RAX, Address(RAX, RBP, TIMES_2, 0));
|
|
__ movq(RAX, Address(RAX, RAX, TIMES_2, 0));
|
|
__ movq(RAX, Address(RAX, R10, TIMES_2, 0));
|
|
__ movq(RAX, Address(RAX, R12, TIMES_2, 0));
|
|
__ movq(RAX, Address(RAX, R13, TIMES_2, 0));
|
|
|
|
__ movq(RAX, Address(RBP, RBP, TIMES_2, 0));
|
|
__ movq(RAX, Address(RBP, RAX, TIMES_2, 0));
|
|
__ movq(RAX, Address(RBP, R10, TIMES_2, 0));
|
|
__ movq(RAX, Address(RBP, R12, TIMES_2, 0));
|
|
__ movq(RAX, Address(RBP, R13, TIMES_2, 0));
|
|
|
|
__ movq(RAX, Address(RSP, RBP, TIMES_2, 0));
|
|
__ movq(RAX, Address(RSP, RAX, TIMES_2, 0));
|
|
__ movq(RAX, Address(RSP, R10, TIMES_2, 0));
|
|
__ movq(RAX, Address(RSP, R12, TIMES_2, 0));
|
|
__ movq(RAX, Address(RSP, R13, TIMES_2, 0));
|
|
|
|
__ movq(RAX, Address(R10, RBP, TIMES_2, 0));
|
|
__ movq(RAX, Address(R10, RAX, TIMES_2, 0));
|
|
__ movq(RAX, Address(R10, R10, TIMES_2, 0));
|
|
__ movq(RAX, Address(R10, R12, TIMES_2, 0));
|
|
__ movq(RAX, Address(R10, R13, TIMES_2, 0));
|
|
|
|
__ movq(RAX, Address(R12, RBP, TIMES_2, 0));
|
|
__ movq(RAX, Address(R12, RAX, TIMES_2, 0));
|
|
__ movq(RAX, Address(R12, R10, TIMES_2, 0));
|
|
__ movq(RAX, Address(R12, R12, TIMES_2, 0));
|
|
__ movq(RAX, Address(R12, R13, TIMES_2, 0));
|
|
|
|
__ movq(RAX, Address(R13, RBP, TIMES_2, 0));
|
|
__ movq(RAX, Address(R13, RAX, TIMES_2, 0));
|
|
__ movq(RAX, Address(R13, R10, TIMES_2, 0));
|
|
__ movq(RAX, Address(R13, R12, TIMES_2, 0));
|
|
__ movq(RAX, Address(R13, R13, TIMES_2, 0));
|
|
|
|
__ movq(RAX, Address(RAX, RBP, TIMES_2, target::kWordSize));
|
|
__ movq(RAX, Address(RAX, RAX, TIMES_2, target::kWordSize));
|
|
__ movq(RAX, Address(RAX, R10, TIMES_2, target::kWordSize));
|
|
__ movq(RAX, Address(RAX, R12, TIMES_2, target::kWordSize));
|
|
__ movq(RAX, Address(RAX, R13, TIMES_2, target::kWordSize));
|
|
|
|
__ movq(RAX, Address(RBP, RBP, TIMES_2, target::kWordSize));
|
|
__ movq(RAX, Address(RBP, RAX, TIMES_2, target::kWordSize));
|
|
__ movq(RAX, Address(RBP, R10, TIMES_2, target::kWordSize));
|
|
__ movq(RAX, Address(RBP, R12, TIMES_2, target::kWordSize));
|
|
__ movq(RAX, Address(RBP, R13, TIMES_2, target::kWordSize));
|
|
|
|
__ movq(RAX, Address(RSP, RBP, TIMES_2, target::kWordSize));
|
|
__ movq(RAX, Address(RSP, RAX, TIMES_2, target::kWordSize));
|
|
__ movq(RAX, Address(RSP, R10, TIMES_2, target::kWordSize));
|
|
__ movq(RAX, Address(RSP, R12, TIMES_2, target::kWordSize));
|
|
__ movq(RAX, Address(RSP, R13, TIMES_2, target::kWordSize));
|
|
|
|
__ movq(RAX, Address(R10, RBP, TIMES_2, target::kWordSize));
|
|
__ movq(RAX, Address(R10, RAX, TIMES_2, target::kWordSize));
|
|
__ movq(RAX, Address(R10, R10, TIMES_2, target::kWordSize));
|
|
__ movq(RAX, Address(R10, R12, TIMES_2, target::kWordSize));
|
|
__ movq(RAX, Address(R10, R13, TIMES_2, target::kWordSize));
|
|
|
|
__ movq(RAX, Address(R12, RBP, TIMES_2, target::kWordSize));
|
|
__ movq(RAX, Address(R12, RAX, TIMES_2, target::kWordSize));
|
|
__ movq(RAX, Address(R12, R10, TIMES_2, target::kWordSize));
|
|
__ movq(RAX, Address(R12, R12, TIMES_2, target::kWordSize));
|
|
__ movq(RAX, Address(R12, R13, TIMES_2, target::kWordSize));
|
|
|
|
__ movq(RAX, Address(R13, RBP, TIMES_2, target::kWordSize));
|
|
__ movq(RAX, Address(R13, RAX, TIMES_2, target::kWordSize));
|
|
__ movq(RAX, Address(R13, R10, TIMES_2, target::kWordSize));
|
|
__ movq(RAX, Address(R13, R12, TIMES_2, target::kWordSize));
|
|
__ movq(RAX, Address(R13, R13, TIMES_2, target::kWordSize));
|
|
|
|
__ movq(RAX, Address(RAX, RBP, TIMES_2, 256 * target::kWordSize));
|
|
__ movq(RAX, Address(RAX, RAX, TIMES_2, 256 * target::kWordSize));
|
|
__ movq(RAX, Address(RAX, R10, TIMES_2, 256 * target::kWordSize));
|
|
__ movq(RAX, Address(RAX, R12, TIMES_2, 256 * target::kWordSize));
|
|
__ movq(RAX, Address(RAX, R13, TIMES_2, 256 * target::kWordSize));
|
|
|
|
__ movq(RAX, Address(RBP, RBP, TIMES_2, 256 * target::kWordSize));
|
|
__ movq(RAX, Address(RBP, RAX, TIMES_2, 256 * target::kWordSize));
|
|
__ movq(RAX, Address(RBP, R10, TIMES_2, 256 * target::kWordSize));
|
|
__ movq(RAX, Address(RBP, R12, TIMES_2, 256 * target::kWordSize));
|
|
__ movq(RAX, Address(RBP, R13, TIMES_2, 256 * target::kWordSize));
|
|
|
|
__ movq(RAX, Address(RSP, RBP, TIMES_2, 256 * target::kWordSize));
|
|
__ movq(RAX, Address(RSP, RAX, TIMES_2, 256 * target::kWordSize));
|
|
__ movq(RAX, Address(RSP, R10, TIMES_2, 256 * target::kWordSize));
|
|
__ movq(RAX, Address(RSP, R12, TIMES_2, 256 * target::kWordSize));
|
|
__ movq(RAX, Address(RSP, R13, TIMES_2, 256 * target::kWordSize));
|
|
|
|
__ movq(RAX, Address(R10, RBP, TIMES_2, 256 * target::kWordSize));
|
|
__ movq(RAX, Address(R10, RAX, TIMES_2, 256 * target::kWordSize));
|
|
__ movq(RAX, Address(R10, R10, TIMES_2, 256 * target::kWordSize));
|
|
__ movq(RAX, Address(R10, R12, TIMES_2, 256 * target::kWordSize));
|
|
__ movq(RAX, Address(R10, R13, TIMES_2, 256 * target::kWordSize));
|
|
|
|
__ movq(RAX, Address(R12, RBP, TIMES_2, 256 * target::kWordSize));
|
|
__ movq(RAX, Address(R12, RAX, TIMES_2, 256 * target::kWordSize));
|
|
__ movq(RAX, Address(R12, R10, TIMES_2, 256 * target::kWordSize));
|
|
__ movq(RAX, Address(R12, R12, TIMES_2, 256 * target::kWordSize));
|
|
__ movq(RAX, Address(R12, R13, TIMES_2, 256 * target::kWordSize));
|
|
|
|
__ movq(RAX, Address(R13, RBP, TIMES_2, 256 * target::kWordSize));
|
|
__ movq(RAX, Address(R13, RAX, TIMES_2, 256 * target::kWordSize));
|
|
__ movq(RAX, Address(R13, R10, TIMES_2, 256 * target::kWordSize));
|
|
__ movq(RAX, Address(R13, R12, TIMES_2, 256 * target::kWordSize));
|
|
__ movq(RAX, Address(R13, R13, TIMES_2, 256 * target::kWordSize));
|
|
|
|
__ movq(RAX, Address::AddressBaseImm32(RSP, 0));
|
|
__ movq(RAX, Address::AddressBaseImm32(RBP, 0));
|
|
__ movq(RAX, Address::AddressBaseImm32(RAX, 0));
|
|
__ movq(RAX, Address::AddressBaseImm32(R10, 0));
|
|
__ movq(RAX, Address::AddressBaseImm32(R12, 0));
|
|
__ movq(RAX, Address::AddressBaseImm32(R13, 0));
|
|
__ movq(R10, Address::AddressBaseImm32(RAX, 0));
|
|
|
|
__ movq(RAX, Address::AddressBaseImm32(RSP, target::kWordSize));
|
|
__ movq(RAX, Address::AddressBaseImm32(RBP, target::kWordSize));
|
|
__ movq(RAX, Address::AddressBaseImm32(RAX, target::kWordSize));
|
|
__ movq(RAX, Address::AddressBaseImm32(R10, target::kWordSize));
|
|
__ movq(RAX, Address::AddressBaseImm32(R12, target::kWordSize));
|
|
__ movq(RAX, Address::AddressBaseImm32(R13, target::kWordSize));
|
|
|
|
__ movq(RAX, Address::AddressBaseImm32(RSP, -target::kWordSize));
|
|
__ movq(RAX, Address::AddressBaseImm32(RBP, -target::kWordSize));
|
|
__ movq(RAX, Address::AddressBaseImm32(RAX, -target::kWordSize));
|
|
__ movq(RAX, Address::AddressBaseImm32(R10, -target::kWordSize));
|
|
__ movq(RAX, Address::AddressBaseImm32(R12, -target::kWordSize));
|
|
__ movq(RAX, Address::AddressBaseImm32(R13, -target::kWordSize));
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(AddressingModes, test) {
|
|
// Avoid running the code since it is constructed to lead to crashes.
|
|
EXPECT_DISASSEMBLY(
|
|
"movq rax,[rsp]\n"
|
|
"movq rax,[rbp+0]\n"
|
|
"movq rax,[rax]\n"
|
|
"movq rax,[r10]\n"
|
|
"movq rax,[r12]\n"
|
|
"movq rax,[r13+0]\n"
|
|
"movq r10,[rax]\n"
|
|
"movq rax,[rsp+0x8]\n"
|
|
"movq rax,[rbp+0x8]\n"
|
|
"movq rax,[rax+0x8]\n"
|
|
"movq rax,[r10+0x8]\n"
|
|
"movq rax,[r12+0x8]\n"
|
|
"movq rax,[r13+0x8]\n"
|
|
"movq rax,[rsp-0x8]\n"
|
|
"movq rax,[rbp-0x8]\n"
|
|
"movq rax,[rax-0x8]\n"
|
|
"movq rax,[r10-0x8]\n"
|
|
"movq rax,[r12-0x8]\n"
|
|
"movq rax,[r13-0x8]\n"
|
|
"movq rax,[rsp+0x800]\n"
|
|
"movq rax,[rbp+0x800]\n"
|
|
"movq rax,[rax+0x800]\n"
|
|
"movq rax,[r10+0x800]\n"
|
|
"movq rax,[r12+0x800]\n"
|
|
"movq rax,[r13+0x800]\n"
|
|
"movq rax,[rsp-0x800]\n"
|
|
"movq rax,[rbp-0x800]\n"
|
|
"movq rax,[rax-0x800]\n"
|
|
"movq rax,[r10-0x800]\n"
|
|
"movq rax,[r12-0x800]\n"
|
|
"movq rax,[r13-0x800]\n"
|
|
"movq rax,[rax*1+0]\n"
|
|
"movq rax,[rax*2+0]\n"
|
|
"movq rax,[rax*4+0]\n"
|
|
"movq rax,[rax*8+0]\n"
|
|
"movq rax,[rbp*2+0]\n"
|
|
"movq rax,[rax*2+0]\n"
|
|
"movq rax,[r10*2+0]\n"
|
|
"movq rax,[r12*2+0]\n"
|
|
"movq rax,[r13*2+0]\n"
|
|
"movq rax,[rbp*2+0x8]\n"
|
|
"movq rax,[rax*2+0x8]\n"
|
|
"movq rax,[r10*2+0x8]\n"
|
|
"movq rax,[r12*2+0x8]\n"
|
|
"movq rax,[r13*2+0x8]\n"
|
|
"movq rax,[rbp*2+0x800]\n"
|
|
"movq rax,[rax*2+0x800]\n"
|
|
"movq rax,[r10*2+0x800]\n"
|
|
"movq rax,[r12*2+0x800]\n"
|
|
"movq rax,[r13*2+0x800]\n"
|
|
"movq rax,[rax+rbp*2]\n"
|
|
"movq rax,[rax+rax*2]\n"
|
|
"movq rax,[rax+r10*2]\n"
|
|
"movq rax,[rax+r12*2]\n"
|
|
"movq rax,[rax+r13*2]\n"
|
|
"movq rax,[rbp+rbp*2+0]\n"
|
|
"movq rax,[rbp+rax*2+0]\n"
|
|
"movq rax,[rbp+r10*2+0]\n"
|
|
"movq rax,[rbp+r12*2+0]\n"
|
|
"movq rax,[rbp+r13*2+0]\n"
|
|
"movq rax,[rsp+rbp*2]\n"
|
|
"movq rax,[rsp+rax*2]\n"
|
|
"movq rax,[rsp+r10*2]\n"
|
|
"movq rax,[rsp+r12*2]\n"
|
|
"movq rax,[rsp+r13*2]\n"
|
|
"movq rax,[r10+rbp*2]\n"
|
|
"movq rax,[r10+rax*2]\n"
|
|
"movq rax,[r10+r10*2]\n"
|
|
"movq rax,[r10+r12*2]\n"
|
|
"movq rax,[r10+r13*2]\n"
|
|
"movq rax,[r12+rbp*2]\n"
|
|
"movq rax,[r12+rax*2]\n"
|
|
"movq rax,[r12+r10*2]\n"
|
|
"movq rax,[r12+r12*2]\n"
|
|
"movq rax,[r12+r13*2]\n"
|
|
"movq rax,[r13+rbp*2+0]\n"
|
|
"movq rax,[r13+rax*2+0]\n"
|
|
"movq rax,[r13+r10*2+0]\n"
|
|
"movq rax,[r13+r12*2+0]\n"
|
|
"movq rax,[r13+r13*2+0]\n"
|
|
"movq rax,[rax+rbp*2+0x8]\n"
|
|
"movq rax,[rax+rax*2+0x8]\n"
|
|
"movq rax,[rax+r10*2+0x8]\n"
|
|
"movq rax,[rax+r12*2+0x8]\n"
|
|
"movq rax,[rax+r13*2+0x8]\n"
|
|
"movq rax,[rbp+rbp*2+0x8]\n"
|
|
"movq rax,[rbp+rax*2+0x8]\n"
|
|
"movq rax,[rbp+r10*2+0x8]\n"
|
|
"movq rax,[rbp+r12*2+0x8]\n"
|
|
"movq rax,[rbp+r13*2+0x8]\n"
|
|
"movq rax,[rsp+rbp*2+0x8]\n"
|
|
"movq rax,[rsp+rax*2+0x8]\n"
|
|
"movq rax,[rsp+r10*2+0x8]\n"
|
|
"movq rax,[rsp+r12*2+0x8]\n"
|
|
"movq rax,[rsp+r13*2+0x8]\n"
|
|
"movq rax,[r10+rbp*2+0x8]\n"
|
|
"movq rax,[r10+rax*2+0x8]\n"
|
|
"movq rax,[r10+r10*2+0x8]\n"
|
|
"movq rax,[r10+r12*2+0x8]\n"
|
|
"movq rax,[r10+r13*2+0x8]\n"
|
|
"movq rax,[r12+rbp*2+0x8]\n"
|
|
"movq rax,[r12+rax*2+0x8]\n"
|
|
"movq rax,[r12+r10*2+0x8]\n"
|
|
"movq rax,[r12+r12*2+0x8]\n"
|
|
"movq rax,[r12+r13*2+0x8]\n"
|
|
"movq rax,[r13+rbp*2+0x8]\n"
|
|
"movq rax,[r13+rax*2+0x8]\n"
|
|
"movq rax,[r13+r10*2+0x8]\n"
|
|
"movq rax,[r13+r12*2+0x8]\n"
|
|
"movq rax,[r13+r13*2+0x8]\n"
|
|
"movq rax,[rax+rbp*2+0x800]\n"
|
|
"movq rax,[rax+rax*2+0x800]\n"
|
|
"movq rax,[rax+r10*2+0x800]\n"
|
|
"movq rax,[rax+r12*2+0x800]\n"
|
|
"movq rax,[rax+r13*2+0x800]\n"
|
|
"movq rax,[rbp+rbp*2+0x800]\n"
|
|
"movq rax,[rbp+rax*2+0x800]\n"
|
|
"movq rax,[rbp+r10*2+0x800]\n"
|
|
"movq rax,[rbp+r12*2+0x800]\n"
|
|
"movq rax,[rbp+r13*2+0x800]\n"
|
|
"movq rax,[rsp+rbp*2+0x800]\n"
|
|
"movq rax,[rsp+rax*2+0x800]\n"
|
|
"movq rax,[rsp+r10*2+0x800]\n"
|
|
"movq rax,[rsp+r12*2+0x800]\n"
|
|
"movq rax,[rsp+r13*2+0x800]\n"
|
|
"movq rax,[r10+rbp*2+0x800]\n"
|
|
"movq rax,[r10+rax*2+0x800]\n"
|
|
"movq rax,[r10+r10*2+0x800]\n"
|
|
"movq rax,[r10+r12*2+0x800]\n"
|
|
"movq rax,[r10+r13*2+0x800]\n"
|
|
"movq rax,[r12+rbp*2+0x800]\n"
|
|
"movq rax,[r12+rax*2+0x800]\n"
|
|
"movq rax,[r12+r10*2+0x800]\n"
|
|
"movq rax,[r12+r12*2+0x800]\n"
|
|
"movq rax,[r12+r13*2+0x800]\n"
|
|
"movq rax,[r13+rbp*2+0x800]\n"
|
|
"movq rax,[r13+rax*2+0x800]\n"
|
|
"movq rax,[r13+r10*2+0x800]\n"
|
|
"movq rax,[r13+r12*2+0x800]\n"
|
|
"movq rax,[r13+r13*2+0x800]\n"
|
|
"movq rax,[rsp+0]\n"
|
|
"movq rax,[rbp+0]\n"
|
|
"movq rax,[rax+0]\n"
|
|
"movq rax,[r10+0]\n"
|
|
"movq rax,[r12+0]\n"
|
|
"movq rax,[r13+0]\n"
|
|
"movq r10,[rax+0]\n"
|
|
"movq rax,[rsp+0x8]\n"
|
|
"movq rax,[rbp+0x8]\n"
|
|
"movq rax,[rax+0x8]\n"
|
|
"movq rax,[r10+0x8]\n"
|
|
"movq rax,[r12+0x8]\n"
|
|
"movq rax,[r13+0x8]\n"
|
|
"movq rax,[rsp-0x8]\n"
|
|
"movq rax,[rbp-0x8]\n"
|
|
"movq rax,[rax-0x8]\n"
|
|
"movq rax,[r10-0x8]\n"
|
|
"movq rax,[r12-0x8]\n"
|
|
"movq rax,[r13-0x8]\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(JumpAroundCrash, assembler) {
|
|
Label done;
|
|
// Make sure all the condition jumps work.
|
|
for (Condition condition = OVERFLOW; condition <= GREATER;
|
|
condition = static_cast<Condition>(condition + 1)) {
|
|
__ j(condition, &done);
|
|
}
|
|
// This isn't strictly necessary, but we do an unconditional
|
|
// jump around the crashing code anyway.
|
|
__ jmp(&done);
|
|
|
|
// Be sure to skip this crashing code.
|
|
__ movq(RAX, Immediate(0));
|
|
__ movq(Address(RAX, 0), RAX);
|
|
|
|
__ Bind(&done);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(JumpAroundCrash, test) {
|
|
Instr* instr = Instr::At(test->entry());
|
|
EXPECT(!instr->IsBreakPoint());
|
|
typedef void (*JumpAroundCrashCode)();
|
|
reinterpret_cast<JumpAroundCrashCode>(test->entry())();
|
|
EXPECT_DISASSEMBLY(
|
|
"jo +109\n"
|
|
"jno +103\n"
|
|
"jc +97\n"
|
|
"jnc +91\n"
|
|
"jz +85\n"
|
|
"jnz +79\n"
|
|
"jna +73\n"
|
|
"ja +67\n"
|
|
"js +61\n"
|
|
"jns +55\n"
|
|
"jpe +49\n"
|
|
"jpo +43\n"
|
|
"jl +37\n"
|
|
"jge +31\n"
|
|
"jle +25\n"
|
|
"jg +19\n"
|
|
"jmp +13\n"
|
|
"movl rax,0\n"
|
|
"movq [rax],rax\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(SimpleLoop, assembler) {
|
|
__ movq(RAX, Immediate(0));
|
|
__ movq(RCX, Immediate(0));
|
|
Label loop;
|
|
__ Bind(&loop);
|
|
__ addq(RAX, Immediate(2));
|
|
__ incq(RCX);
|
|
__ cmpq(RCX, Immediate(87));
|
|
__ j(LESS, &loop);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(SimpleLoop, test) {
|
|
typedef int (*SimpleLoopCode)();
|
|
EXPECT_EQ(2 * 87, reinterpret_cast<SimpleLoopCode>(test->entry())());
|
|
EXPECT_DISASSEMBLY(
|
|
"movl rax,0\n"
|
|
"movl rcx,0\n"
|
|
"addq rax,2\n"
|
|
"incq rcx\n"
|
|
"cmpq rcx,0x57\n"
|
|
"jl -11\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(Cmpb, assembler) {
|
|
Label done;
|
|
__ movq(RAX, Immediate(1));
|
|
__ pushq(Immediate(0xffffff11));
|
|
__ cmpb(Address(RSP, 0), Immediate(0x11));
|
|
__ j(EQUAL, &done, Assembler::kNearJump);
|
|
__ movq(RAX, Immediate(0));
|
|
__ Bind(&done);
|
|
__ popq(RCX);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(Cmpb, test) {
|
|
typedef int (*CmpbCode)();
|
|
EXPECT_EQ(1, reinterpret_cast<CmpbCode>(test->entry())());
|
|
EXPECT_DISASSEMBLY(
|
|
"movl rax,1\n"
|
|
"movl tmp,0xffffff11\n"
|
|
"push tmp\n"
|
|
"cmpb [rsp],0x11\n"
|
|
"jz +7\n"
|
|
"movl rax,0\n"
|
|
"pop rcx\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(Testb, assembler) {
|
|
Label done;
|
|
__ movq(RAX, Immediate(1));
|
|
__ movq(RCX, Immediate(0));
|
|
__ pushq(Immediate(0xffffff11));
|
|
__ testb(Address(RSP, 0), Immediate(0x10));
|
|
// Fail if zero flag set.
|
|
__ cmoveq(RAX, RCX);
|
|
__ testb(Address(RSP, 0), Immediate(0x20));
|
|
// Fail if zero flag not set.
|
|
__ j(ZERO, &done);
|
|
__ movq(RAX, Immediate(0));
|
|
__ Bind(&done);
|
|
__ popq(RCX);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(Testb, test) {
|
|
typedef int (*TestbCode)();
|
|
EXPECT_EQ(1, reinterpret_cast<TestbCode>(test->entry())());
|
|
EXPECT_DISASSEMBLY(
|
|
"movl rax,1\n"
|
|
"movl rcx,0\n"
|
|
"movl tmp,0xffffff11\n"
|
|
"push tmp\n"
|
|
"testb [rsp],0x10\n"
|
|
"cmovzq rax,rcx\n"
|
|
"testb [rsp],0x20\n"
|
|
"jz +11\n"
|
|
"movl rax,0\n"
|
|
"pop rcx\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(Testb2, assembler) {
|
|
Label done, ok1, ok2, ok3, ok4, ok5, ok6, ok7;
|
|
|
|
__ movq(RAX, Immediate(0xffffefff));
|
|
__ bsrq(RCX, RAX);
|
|
__ cmpq(RCX, Immediate(31));
|
|
__ j(EQUAL, &ok1);
|
|
__ int3();
|
|
__ Bind(&ok1);
|
|
|
|
__ sarq(RAX, Immediate(1));
|
|
__ cmpq(RAX, Immediate(0x7ffff7ff));
|
|
__ j(EQUAL, &ok2);
|
|
__ int3();
|
|
__ Bind(&ok2);
|
|
|
|
__ movq(RAX, Immediate(0x7fffffff));
|
|
__ bsrq(RCX, RAX);
|
|
__ cmpq(RCX, Immediate(30));
|
|
__ j(EQUAL, &ok3);
|
|
__ int3();
|
|
__ Bind(&ok3);
|
|
|
|
__ cmpq(RAX, Immediate(0x7fffffff));
|
|
__ j(EQUAL, &ok4);
|
|
__ int3();
|
|
__ Bind(&ok4);
|
|
|
|
__ movq(RAX, Immediate(0x101020408));
|
|
__ andq(RAX, Immediate(0xffffffff));
|
|
__ cmpq(RAX, Immediate(0x1020408));
|
|
__ j(EQUAL, &ok5);
|
|
__ int3();
|
|
__ Bind(&ok5);
|
|
|
|
__ movq(RCX, Immediate(0x101020408));
|
|
__ andq(RCX, Immediate(0xffffffff));
|
|
__ cmpq(RCX, Immediate(0x1020408));
|
|
__ j(EQUAL, &ok6);
|
|
__ int3();
|
|
__ Bind(&ok6);
|
|
|
|
__ movq(RAX, Immediate(0x0fffeff0));
|
|
__ bsfq(RCX, RAX);
|
|
__ cmpq(RCX, Immediate(4));
|
|
__ j(EQUAL, &ok7);
|
|
__ int3();
|
|
__ Bind(&ok7);
|
|
|
|
__ movq(RAX, Immediate(42));
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(Testb2, test) {
|
|
typedef int64_t (*Testb2Code)();
|
|
EXPECT_EQ(42, reinterpret_cast<Testb2Code>(test->entry())());
|
|
EXPECT_DISASSEMBLY(
|
|
"movl rax,0xffffefff\n"
|
|
"bsrq rcx,rax\n"
|
|
"cmpq rcx,0x1f\n"
|
|
"jz +7\n"
|
|
"int3\n"
|
|
|
|
"sarq rax,1\n"
|
|
"cmpq rax,0x7ffff7ff\n"
|
|
"jz +7\n"
|
|
"int3\n"
|
|
|
|
"movl rax,0x7fffffff\n"
|
|
"bsrq rcx,rax\n"
|
|
"cmpq rcx,0x1e\n"
|
|
"jz +7\n"
|
|
"int3\n"
|
|
|
|
"cmpq rax,0x7fffffff\n"
|
|
"jz +7\n"
|
|
"int3\n"
|
|
|
|
"movq rax,0x0000000101020408\n"
|
|
"andl rax,0xffffffff\n"
|
|
"cmpq rax,0x01020408\n"
|
|
"jz +7\n"
|
|
"int3\n"
|
|
|
|
"movq rcx,0x0000000101020408\n"
|
|
"andl rcx,0xffffffff\n"
|
|
"cmpq rcx,0x01020408\n"
|
|
"jz +7\n"
|
|
"int3\n"
|
|
|
|
"movl rax,0x0fffeff0\n"
|
|
"bsfq rcx,rax\n"
|
|
"cmpq rcx,4\n"
|
|
"jz +7\n"
|
|
"int3\n"
|
|
|
|
"movl rax,0x2a\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(Testb3, assembler) {
|
|
Label zero;
|
|
__ pushq(CallingConventions::kArg1Reg);
|
|
__ movq(RDX, Immediate(0x10));
|
|
__ testb(Address(RSP, 0), RDX);
|
|
__ j(ZERO, &zero);
|
|
__ movq(RAX, Immediate(1));
|
|
__ popq(RCX);
|
|
__ ret();
|
|
__ Bind(&zero);
|
|
__ movq(RAX, Immediate(0));
|
|
__ popq(RCX);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(Testb3, test) {
|
|
typedef int (*TestbCode)(int);
|
|
EXPECT_EQ(1, reinterpret_cast<TestbCode>(test->entry())(0x11));
|
|
EXPECT_EQ(0, reinterpret_cast<TestbCode>(test->entry())(0x101));
|
|
EXPECT_DISASSEMBLY_NOT_WINDOWS(
|
|
"push rdi\n"
|
|
"movl rdx,0x10\n"
|
|
"testb dl,[rsp]\n"
|
|
"jz +13\n"
|
|
"movl rax,1\n"
|
|
"pop rcx\n"
|
|
"ret\n"
|
|
"movl rax,0\n"
|
|
"pop rcx\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(Popcnt, assembler) {
|
|
__ movq(RCX, Immediate(-1));
|
|
__ popcntq(RAX, RCX);
|
|
__ movq(RCX, Immediate(0xf));
|
|
__ popcntq(RCX, RCX);
|
|
__ addq(RAX, RCX);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(Popcnt, test) {
|
|
if (!HostCPUFeatures::popcnt_supported()) {
|
|
return;
|
|
}
|
|
typedef int64_t (*PopcntCode)();
|
|
EXPECT_EQ(68, reinterpret_cast<PopcntCode>(test->entry())());
|
|
EXPECT_DISASSEMBLY(
|
|
"movq rcx,-1\n"
|
|
"popcntq rax,rcx\n"
|
|
"movl rcx,0xf\n"
|
|
"popcntq rcx,rcx\n"
|
|
"addq rax,rcx\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(Lzcnt, assembler) {
|
|
__ movq(RCX, Immediate(0x0f00));
|
|
__ lzcntq(RAX, RCX);
|
|
__ movq(RCX, Immediate(0x00f0));
|
|
__ lzcntq(RCX, RCX);
|
|
__ addq(RAX, RCX);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(Lzcnt, test) {
|
|
if (!HostCPUFeatures::abm_supported()) {
|
|
return;
|
|
}
|
|
typedef int64_t (*LzcntCode)();
|
|
EXPECT_EQ(108, reinterpret_cast<LzcntCode>(test->entry())());
|
|
EXPECT_DISASSEMBLY(
|
|
"movl rcx,0xf00\n"
|
|
"lzcntq rax,rcx\n"
|
|
"movl rcx,0xf0\n"
|
|
"lzcntq rcx,rcx\n"
|
|
"addq rax,rcx\n"
|
|
"ret\n");
|
|
}
|
|
|
|
struct JumpAddress {
|
|
uword filler1;
|
|
uword filler2;
|
|
uword filler3;
|
|
uword filler4;
|
|
uword filler5;
|
|
uword target;
|
|
uword filler6;
|
|
uword filler7;
|
|
uword filler8;
|
|
};
|
|
static JumpAddress jump_address;
|
|
static uword jump_address_offset;
|
|
|
|
ASSEMBLER_TEST_GENERATE(JumpAddress, assembler) {
|
|
__ jmp(Address(CallingConventions::kArg1Reg, OFFSET_OF(JumpAddress, target)));
|
|
__ int3();
|
|
__ int3();
|
|
__ int3();
|
|
__ int3();
|
|
__ int3();
|
|
jump_address_offset = __ CodeSize();
|
|
__ movl(RAX, Immediate(42));
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(JumpAddress, test) {
|
|
memset(&jump_address, 0, sizeof(jump_address));
|
|
jump_address.target = test->entry() + jump_address_offset;
|
|
|
|
typedef int (*TestCode)(void*);
|
|
EXPECT_EQ(42, reinterpret_cast<TestCode>(test->entry())(&jump_address));
|
|
EXPECT_DISASSEMBLY_NOT_WINDOWS(
|
|
"jmp [rdi+0x28]\n"
|
|
"int3\n"
|
|
"int3\n"
|
|
"int3\n"
|
|
"int3\n"
|
|
"int3\n"
|
|
"movl rax,0x2a\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(Increment, assembler) {
|
|
__ movq(RAX, Immediate(0));
|
|
__ pushq(RAX);
|
|
__ incl(Address(RSP, 0));
|
|
__ incq(Address(RSP, 0));
|
|
__ movq(RCX, Address(RSP, 0));
|
|
__ incq(RCX);
|
|
__ popq(RAX);
|
|
__ movq(RAX, RCX);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(Increment, test) {
|
|
typedef int (*IncrementCode)();
|
|
EXPECT_EQ(3, reinterpret_cast<IncrementCode>(test->entry())());
|
|
EXPECT_DISASSEMBLY(
|
|
"movl rax,0\n"
|
|
"push rax\n"
|
|
"incl [rsp]\n"
|
|
"incq [rsp]\n"
|
|
"movq rcx,[rsp]\n"
|
|
"incq rcx\n"
|
|
"pop rax\n"
|
|
"movq rax,rcx\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(IncrementLong, assembler) {
|
|
__ movq(RAX, Immediate(0xffffffff));
|
|
__ pushq(RAX);
|
|
__ incq(Address(RSP, 0));
|
|
__ movq(RCX, Address(RSP, 0));
|
|
__ incq(RCX);
|
|
__ popq(RAX);
|
|
__ movq(RAX, RCX);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(IncrementLong, test) {
|
|
typedef int64_t (*IncrementCodeLong)();
|
|
EXPECT_EQ(0x100000001, reinterpret_cast<IncrementCodeLong>(test->entry())());
|
|
EXPECT_DISASSEMBLY(
|
|
"movl rax,0xffffffff\n"
|
|
"push rax\n"
|
|
"incq [rsp]\n"
|
|
"movq rcx,[rsp]\n"
|
|
"incq rcx\n"
|
|
"pop rax\n"
|
|
"movq rax,rcx\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(Decrement, assembler) {
|
|
__ movq(RAX, Immediate(3));
|
|
__ pushq(RAX);
|
|
__ decl(Address(RSP, 0));
|
|
__ decq(Address(RSP, 0));
|
|
__ movq(RCX, Address(RSP, 0));
|
|
__ decq(RCX);
|
|
__ popq(RAX);
|
|
__ movq(RAX, RCX);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(Decrement, test) {
|
|
typedef int (*DecrementCode)();
|
|
EXPECT_EQ(0, reinterpret_cast<DecrementCode>(test->entry())());
|
|
EXPECT_DISASSEMBLY(
|
|
"movl rax,3\n"
|
|
"push rax\n"
|
|
"decl [rsp]\n"
|
|
"decq [rsp]\n"
|
|
"movq rcx,[rsp]\n"
|
|
"decq rcx\n"
|
|
"pop rax\n"
|
|
"movq rax,rcx\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(DecrementLong, assembler) {
|
|
__ movq(RAX, Immediate(0x100000001));
|
|
__ pushq(RAX);
|
|
__ decq(Address(RSP, 0));
|
|
__ movq(RCX, Address(RSP, 0));
|
|
__ decq(RCX);
|
|
__ popq(RAX);
|
|
__ movq(RAX, RCX);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(DecrementLong, test) {
|
|
typedef int64_t (*DecrementCodeLong)();
|
|
EXPECT_EQ(0xffffffff, reinterpret_cast<DecrementCodeLong>(test->entry())());
|
|
EXPECT_DISASSEMBLY(
|
|
"movq rax,0x0000000100000001\n"
|
|
"push rax\n"
|
|
"decq [rsp]\n"
|
|
"movq rcx,[rsp]\n"
|
|
"decq rcx\n"
|
|
"pop rax\n"
|
|
"movq rax,rcx\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(SignedMultiply, assembler) {
|
|
__ movl(RAX, Immediate(2));
|
|
__ movl(RCX, Immediate(4));
|
|
__ imull(RAX, RCX);
|
|
__ imull(RAX, Immediate(1000));
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(SignedMultiply, test) {
|
|
typedef int (*SignedMultiply)();
|
|
EXPECT_EQ(8000, reinterpret_cast<SignedMultiply>(test->entry())());
|
|
EXPECT_DISASSEMBLY(
|
|
"movl rax,2\n"
|
|
"movl rcx,4\n"
|
|
"imull rax,rcx\n"
|
|
"imull rax,rax,0x3e8\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(UnsignedMultiply, assembler) {
|
|
__ movl(RAX, Immediate(-1)); // RAX = 0xFFFFFFFF
|
|
__ movl(RCX, Immediate(16)); // RCX = 0x10
|
|
__ mull(RCX); // RDX:RAX = RAX * RCX = 0x0FFFFFFFF0
|
|
__ movq(RAX, RDX); // Return high32(0x0FFFFFFFF0) == 0x0F
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(UnsignedMultiply, test) {
|
|
typedef int (*UnsignedMultiply)();
|
|
EXPECT_EQ(15, reinterpret_cast<UnsignedMultiply>(test->entry())());
|
|
EXPECT_DISASSEMBLY(
|
|
"movl rax,-1\n"
|
|
"movl rcx,0x10\n"
|
|
"mull (rax,rdx),rcx\n"
|
|
"movq rax,rdx\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(SignedMultiply64Implicit, assembler) {
|
|
__ movq(RAX, Immediate(7));
|
|
__ movq(RDX, Immediate(-3));
|
|
__ imulq(RDX); // // RDX:RAX = -21
|
|
__ addq(RAX, RDX);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(SignedMultiply64Implicit, test) {
|
|
typedef int (*SignedMultiply64Implicit)();
|
|
EXPECT_EQ(-22, reinterpret_cast<SignedMultiply64Implicit>(test->entry())());
|
|
EXPECT_DISASSEMBLY(
|
|
"movl rax,7\n"
|
|
"movq rdx,-3\n"
|
|
"imulq (rax,rdx),rdx\n"
|
|
"addq rax,rdx\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(SignedMultiply64, assembler) {
|
|
__ pushq(R15); // Callee saved.
|
|
__ movq(RAX, Immediate(2));
|
|
__ movq(RCX, Immediate(4));
|
|
__ imulq(RAX, RCX);
|
|
|
|
__ movq(R8, Immediate(2));
|
|
__ movq(R9, Immediate(4));
|
|
__ pushq(R9);
|
|
__ imulq(R8, Address(RSP, 0));
|
|
__ popq(R9);
|
|
__ addq(RAX, R8);
|
|
|
|
__ movq(R10, Immediate(2));
|
|
__ movq(TMP, Immediate(4));
|
|
__ imulq(R10, TMP);
|
|
__ addq(RAX, R10);
|
|
|
|
__ movq(R15, Immediate(2));
|
|
__ imulq(R15, Immediate(4));
|
|
__ addq(RAX, R15);
|
|
__ popq(R15);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(SignedMultiply64, test) {
|
|
typedef int64_t (*SignedMultiply64)();
|
|
EXPECT_EQ(32, reinterpret_cast<SignedMultiply64>(test->entry())());
|
|
EXPECT_DISASSEMBLY(
|
|
"push pp\n"
|
|
"movl rax,2\n"
|
|
"movl rcx,4\n"
|
|
"imulq rax,rcx\n"
|
|
"movl r8,2\n"
|
|
"movl r9,4\n"
|
|
"push r9\n"
|
|
"imulq r8,[rsp]\n"
|
|
"pop r9\n"
|
|
"addq rax,r8\n"
|
|
"movl r10,2\n"
|
|
"movl tmp,4\n"
|
|
"imulq r10,tmp\n"
|
|
"addq rax,r10\n"
|
|
"movl pp,2\n"
|
|
"imulq pp,pp,4\n"
|
|
"addq rax,pp\n"
|
|
"pop pp\n"
|
|
"ret\n");
|
|
}
|
|
|
|
static constexpr int64_t kLargeConstant = 0x1234567887654321;
|
|
static constexpr int64_t kAnotherLargeConstant = 987654321987654321LL;
|
|
static constexpr int64_t kProductLargeConstants = 0x5bbb29a7f52fbbd1;
|
|
|
|
ASSEMBLER_TEST_GENERATE(SignedMultiplyLong, assembler) {
|
|
Label done;
|
|
__ movq(RAX, Immediate(kLargeConstant));
|
|
__ movq(RCX, Immediate(kAnotherLargeConstant));
|
|
__ imulq(RAX, RCX);
|
|
__ MulImmediate(RCX, Immediate(kLargeConstant));
|
|
__ cmpq(RAX, RCX);
|
|
__ j(EQUAL, &done);
|
|
__ int3();
|
|
__ Bind(&done);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(SignedMultiplyLong, test) {
|
|
typedef int64_t (*SignedMultiplyLong)();
|
|
EXPECT_EQ(kProductLargeConstants,
|
|
reinterpret_cast<SignedMultiplyLong>(test->entry())());
|
|
EXPECT_DISASSEMBLY(
|
|
"movq rax,0x1234567887654321\n"
|
|
"movq rcx,0x0db4da5f7ef412b1\n"
|
|
"imulq rax,rcx\n"
|
|
"movq tmp,0x1234567887654321\n"
|
|
"imulq rcx,tmp\n"
|
|
"cmpq rax,rcx\n"
|
|
"jz +7\n"
|
|
"int3\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(OverflowSignedMultiply, assembler) {
|
|
__ movl(RDX, Immediate(0));
|
|
__ movl(RAX, Immediate(0x0fffffff));
|
|
__ movl(RCX, Immediate(0x0fffffff));
|
|
__ imull(RAX, RCX);
|
|
__ imull(RAX, RDX);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(OverflowSignedMultiply, test) {
|
|
typedef int (*OverflowSignedMultiply)();
|
|
EXPECT_EQ(0, reinterpret_cast<OverflowSignedMultiply>(test->entry())());
|
|
EXPECT_DISASSEMBLY(
|
|
"movl rdx,0\n"
|
|
"movl rax,0x0fffffff\n"
|
|
"movl rcx,0x0fffffff\n"
|
|
"imull rax,rcx\n"
|
|
"imull rax,rdx\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(SignedMultiply1, assembler) {
|
|
__ movl(RDX, Immediate(2));
|
|
__ movl(RCX, Immediate(4));
|
|
__ imull(RDX, RCX);
|
|
__ imull(RDX, Immediate(1000));
|
|
__ movl(RAX, RDX);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(SignedMultiply1, test) {
|
|
typedef int (*SignedMultiply1)();
|
|
EXPECT_EQ(8000, reinterpret_cast<SignedMultiply1>(test->entry())());
|
|
EXPECT_DISASSEMBLY(
|
|
"movl rdx,2\n"
|
|
"movl rcx,4\n"
|
|
"imull rdx,rcx\n"
|
|
"imull rdx,rdx,0x3e8\n"
|
|
"movl rax,rdx\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(SignedMultiply2, assembler) {
|
|
__ pushq(R15); // Callee saved.
|
|
__ movl(R15, Immediate(2));
|
|
__ imull(R15, Immediate(1000));
|
|
__ movl(RAX, R15);
|
|
__ popq(R15);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(SignedMultiply2, test) {
|
|
typedef int (*SignedMultiply2)();
|
|
EXPECT_EQ(2000, reinterpret_cast<SignedMultiply2>(test->entry())());
|
|
EXPECT_DISASSEMBLY(
|
|
"push pp\n"
|
|
"movl pp,2\n"
|
|
"imull pp,pp,0x3e8\n"
|
|
"movl rax,pp\n"
|
|
"pop pp\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(UnsignedMultiplyLong, assembler) {
|
|
__ movq(RAX, Immediate(-1)); // RAX = 0xFFFFFFFFFFFFFFFF
|
|
__ movq(RCX, Immediate(16)); // RCX = 0x10
|
|
__ mulq(RCX); // RDX:RAX = RAX * RCX = 0x0FFFFFFFFFFFFFFFF0
|
|
__ movq(RAX, RDX); // Return high64(0x0FFFFFFFFFFFFFFFF0) == 0x0F
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(UnsignedMultiplyLong, test) {
|
|
typedef int64_t (*UnsignedMultiplyLong)();
|
|
EXPECT_EQ(15, reinterpret_cast<UnsignedMultiplyLong>(test->entry())());
|
|
EXPECT_DISASSEMBLY(
|
|
"movq rax,-1\n"
|
|
"movl rcx,0x10\n"
|
|
"mulq (rax,rdx),rcx\n"
|
|
"movq rax,rdx\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(SignedDivide, assembler) {
|
|
__ movl(RAX, Immediate(-87));
|
|
__ movl(RDX, Immediate(123));
|
|
__ cdq();
|
|
__ movl(RCX, Immediate(42));
|
|
__ idivl(RCX);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(SignedDivide, test) {
|
|
typedef int32_t (*SignedDivide)();
|
|
EXPECT_EQ(-87 / 42, reinterpret_cast<SignedDivide>(test->entry())());
|
|
EXPECT_DISASSEMBLY(
|
|
"movl rax,-0x00000057\n"
|
|
"movl rdx,0x7b\n"
|
|
"cdq\n"
|
|
"movl rcx,0x2a\n"
|
|
"idivl (rax,rdx),rcx\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(UnsignedDivide, assembler) {
|
|
const int32_t low = 0;
|
|
const int32_t high = 0xf0000000;
|
|
const int32_t divisor = 0xffffffff;
|
|
__ movl(RAX, Immediate(low));
|
|
__ movl(RDX, Immediate(high));
|
|
__ movl(RCX, Immediate(divisor));
|
|
__ divl(RCX); // RAX = RDX:RAX / RCX =
|
|
// = 0xf000000000000000 / 0xffffffff = 0xf0000000
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(UnsignedDivide, test) {
|
|
typedef uint32_t (*UnsignedDivide)();
|
|
EXPECT_EQ(0xf0000000, reinterpret_cast<UnsignedDivide>(test->entry())());
|
|
EXPECT_DISASSEMBLY(
|
|
"movl rax,0\n"
|
|
"movl rdx,-0x10000000\n"
|
|
"movl rcx,-1\n"
|
|
"divl (rax,rdx),rcx\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(SignedDivideLong, assembler) {
|
|
__ movq(RAX, Immediate(kLargeConstant));
|
|
__ movq(RDX, Immediate(123));
|
|
__ cqo(); // Clear RDX.
|
|
__ movq(RCX, Immediate(42));
|
|
__ idivq(RCX);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(SignedDivideLong, test) {
|
|
typedef int64_t (*SignedDivideLong)();
|
|
EXPECT_EQ(kLargeConstant / 42,
|
|
reinterpret_cast<SignedDivideLong>(test->entry())());
|
|
EXPECT_DISASSEMBLY(
|
|
"movq rax,0x1234567887654321\n"
|
|
"movl rdx,0x7b\n"
|
|
"cqo\n"
|
|
"movl rcx,0x2a\n"
|
|
"idivq (rax,rdx),rcx\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(UnsignedDivideLong, assembler) {
|
|
const int64_t low = 0;
|
|
const int64_t high = 0xf000000000000000;
|
|
const int64_t divisor = 0xffffffffffffffff;
|
|
__ movq(RAX, Immediate(low));
|
|
__ movq(RDX, Immediate(high));
|
|
__ movq(RCX, Immediate(divisor));
|
|
__ divq(RCX); // RAX = RDX:RAX / RCX =
|
|
// = 0xf0000000000000000000000000000000 /
|
|
// 0xffffffffffffffff = 0xf000000000000000
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(UnsignedDivideLong, test) {
|
|
typedef uint64_t (*UnsignedDivideLong)();
|
|
EXPECT_EQ(0xf000000000000000,
|
|
reinterpret_cast<UnsignedDivideLong>(test->entry())());
|
|
EXPECT_DISASSEMBLY(
|
|
"movl rax,0\n"
|
|
"movq rdx,0xf000000000000000\n"
|
|
"movq rcx,-1\n"
|
|
"divq (rax,rdx),rcx\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(Negate, assembler) {
|
|
__ movq(RCX, Immediate(42));
|
|
__ negq(RCX);
|
|
__ movq(RAX, RCX);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(Negate, test) {
|
|
typedef int (*Negate)();
|
|
EXPECT_EQ(-42, reinterpret_cast<Negate>(test->entry())());
|
|
EXPECT_DISASSEMBLY(
|
|
"movl rcx,0x2a\n"
|
|
"negq rcx\n"
|
|
"movq rax,rcx\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(BitScanReverseTest, assembler) {
|
|
__ pushq(CallingConventions::kArg1Reg);
|
|
__ movq(RCX, Address(RSP, 0));
|
|
__ movq(RAX, Immediate(666)); // Marker for conditional write.
|
|
__ bsrq(RAX, RCX);
|
|
__ popq(RCX);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(BitScanReverseTest, test) {
|
|
typedef int (*Bsr)(int input);
|
|
Bsr call = reinterpret_cast<Bsr>(test->entry());
|
|
EXPECT_EQ(666, call(0));
|
|
EXPECT_EQ(0, call(1));
|
|
EXPECT_EQ(1, call(2));
|
|
EXPECT_EQ(1, call(3));
|
|
EXPECT_EQ(2, call(4));
|
|
EXPECT_EQ(5, call(42));
|
|
EXPECT_EQ(31, call(-1));
|
|
EXPECT_DISASSEMBLY_NOT_WINDOWS(
|
|
"push rdi\n"
|
|
"movq rcx,[rsp]\n"
|
|
"movl rax,0x29a\n"
|
|
"bsrq rax,rcx\n"
|
|
"pop rcx\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(MoveExtend, assembler) {
|
|
__ movq(RDX, Immediate(0xffff));
|
|
__ movzxb(RAX, RDX); // RAX = 0xff
|
|
__ movsxw(R8, RDX); // R8 = -1
|
|
__ movzxw(RCX, RDX); // RCX = 0xffff
|
|
__ addq(R8, RCX);
|
|
__ addq(RAX, R8);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(MoveExtend, test) {
|
|
typedef int (*MoveExtend)();
|
|
EXPECT_EQ(0xff - 1 + 0xffff, reinterpret_cast<MoveExtend>(test->entry())());
|
|
EXPECT_DISASSEMBLY(
|
|
"movl rdx,0xffff\n"
|
|
"movzxbq rax,rdx\n"
|
|
"movsxwq r8,rdx\n"
|
|
"movzxwq rcx,rdx\n"
|
|
"addq r8,rcx\n"
|
|
"addq rax,r8\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(MoveExtend32, assembler) {
|
|
__ movq(RDX, Immediate(0xffffffff));
|
|
__ movsxd(RDX, RDX);
|
|
__ movq(RAX, Immediate(0x7fffffff));
|
|
__ movsxd(RAX, RAX);
|
|
__ addq(RAX, RDX);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(MoveExtend32, test) {
|
|
typedef intptr_t (*MoveExtend)();
|
|
EXPECT_EQ(0x7ffffffe, reinterpret_cast<MoveExtend>(test->entry())());
|
|
EXPECT_DISASSEMBLY(
|
|
"movl rdx,0xffffffff\n"
|
|
"movsxdq rdx,rdx\n"
|
|
"movl rax,0x7fffffff\n"
|
|
"movsxdq rax,rax\n"
|
|
"addq rax,rdx\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(MoveExtendMemory, assembler) {
|
|
__ movq(RDX, Immediate(0x123456781234ffff));
|
|
|
|
__ pushq(RDX);
|
|
__ movzxb(RAX, Address(RSP, 0)); // RAX = 0xff
|
|
__ movsxw(R8, Address(RSP, 0)); // R8 = -1
|
|
__ movzxw(RCX, Address(RSP, 0)); // RCX = 0xffff
|
|
__ addq(RSP, Immediate(target::kWordSize));
|
|
|
|
__ addq(R8, RCX);
|
|
__ addq(RAX, R8);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(MoveExtendMemory, test) {
|
|
typedef int (*MoveExtendMemory)();
|
|
EXPECT_EQ(0xff - 1 + 0xffff,
|
|
reinterpret_cast<MoveExtendMemory>(test->entry())());
|
|
EXPECT_DISASSEMBLY(
|
|
"movq rdx,0x123456781234ffff\n"
|
|
"push rdx\n"
|
|
"movzxbq rax,[rsp]\n"
|
|
"movsxwq r8,[rsp]\n"
|
|
"movzxwq rcx,[rsp]\n"
|
|
"addq rsp,8\n"
|
|
"addq r8,rcx\n"
|
|
"addq rax,r8\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(MoveExtend32Memory, assembler) {
|
|
__ pushq(Immediate(0xffffffff));
|
|
__ pushq(Immediate(0x7fffffff));
|
|
__ movsxd(RDX, Address(RSP, target::kWordSize));
|
|
__ movsxd(RAX, Address(RSP, 0));
|
|
__ addq(RSP, Immediate(target::kWordSize * 2));
|
|
|
|
__ addq(RAX, RDX);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(MoveExtend32Memory, test) {
|
|
typedef intptr_t (*MoveExtend)();
|
|
EXPECT_EQ(0x7ffffffe, reinterpret_cast<MoveExtend>(test->entry())());
|
|
EXPECT_DISASSEMBLY(
|
|
"movl tmp,0xffffffff\n"
|
|
"push tmp\n"
|
|
"push 0x7fffffff\n"
|
|
"movsxdq rdx,[rsp+0x8]\n"
|
|
"movsxdq rax,[rsp]\n"
|
|
"addq rsp,0x10\n"
|
|
"addq rax,rdx\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(MoveWord, assembler) {
|
|
__ xorq(RAX, RAX);
|
|
__ pushq(Immediate(0));
|
|
__ movq(RAX, RSP);
|
|
__ movq(RCX, Immediate(-1));
|
|
__ movw(Address(RAX, 0), RCX);
|
|
__ movzxw(RAX, Address(RAX, 0)); // RAX = 0xffff
|
|
__ addq(RSP, Immediate(target::kWordSize));
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(MoveWord, test) {
|
|
typedef int (*MoveWord)();
|
|
EXPECT_EQ(0xffff, reinterpret_cast<MoveWord>(test->entry())());
|
|
EXPECT_DISASSEMBLY(
|
|
"xorq rax,rax\n"
|
|
"push 0\n"
|
|
"movq rax,rsp\n"
|
|
"movq rcx,-1\n"
|
|
"movw [rax],rcx\n"
|
|
"movzxwq rax,[rax]\n"
|
|
"addq rsp,8\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(WordOps, assembler) {
|
|
__ movq(RAX, Immediate(0x0102030405060708));
|
|
__ pushq(RAX);
|
|
__ addw(Address(RSP, 0), Immediate(-0x201));
|
|
__ subw(Address(RSP, 2), Immediate(0x201));
|
|
__ xorw(Address(RSP, 4), Immediate(0x201));
|
|
__ andw(Address(RSP, 6), Immediate(0x301));
|
|
__ andw(Address(RSP, 0), Immediate(-1));
|
|
__ popq(RAX);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(WordOps, test) {
|
|
typedef int64_t (*WordOps)();
|
|
EXPECT_EQ(0x0100010503050507, reinterpret_cast<WordOps>(test->entry())());
|
|
EXPECT_DISASSEMBLY(
|
|
"movq rax,0x0102030405060708\n"
|
|
"push rax\n"
|
|
"addw [rsp],0xfdff\n"
|
|
"subw [rsp+0x2],0x201\n"
|
|
"xorw [rsp+0x4],0x201\n"
|
|
"andw [rsp+0x6],0x301\n"
|
|
"andw [rsp],-1\n"
|
|
"pop rax\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(ByteOps, assembler) {
|
|
__ movq(RAX, Immediate(0x0102030405060708));
|
|
__ pushq(RAX);
|
|
__ addb(Address(RSP, 0), Immediate(0xff));
|
|
__ subb(Address(RSP, 2), Immediate(1));
|
|
__ xorb(Address(RSP, 4), Immediate(1));
|
|
__ andb(Address(RSP, 6), Immediate(1));
|
|
__ andb(Address(RSP, 0), Immediate(-1));
|
|
__ popq(RAX);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(ByteOps, test) {
|
|
typedef int64_t (*ByteOps)();
|
|
EXPECT_EQ(0x0100030505050707, reinterpret_cast<ByteOps>(test->entry())());
|
|
EXPECT_DISASSEMBLY(
|
|
"movq rax,0x0102030405060708\n"
|
|
"push rax\n"
|
|
"addb [rsp],-1\n"
|
|
"subb [rsp+0x2],1\n"
|
|
"xorb [rsp+0x4],1\n"
|
|
"andb [rsp+0x6],1\n"
|
|
"andb [rsp],-1\n"
|
|
"pop rax\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(MoveWordRex, assembler) {
|
|
__ pushq(Immediate(0));
|
|
__ movq(R8, RSP);
|
|
__ movq(R9, Immediate(-1));
|
|
__ movw(Address(R8, 0), R9);
|
|
__ movzxw(R8, Address(R8, 0)); // 0xffff
|
|
__ xorq(RAX, RAX);
|
|
__ addq(RAX, R8); // RAX = 0xffff
|
|
__ addq(RSP, Immediate(target::kWordSize));
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(MoveWordRex, test) {
|
|
typedef int (*MoveWordRex)();
|
|
EXPECT_EQ(0xffff, reinterpret_cast<MoveWordRex>(test->entry())());
|
|
EXPECT_DISASSEMBLY(
|
|
"push 0\n"
|
|
"movq r8,rsp\n"
|
|
"movq r9,-1\n"
|
|
"movw [r8],r9\n"
|
|
"movzxwq r8,[r8]\n"
|
|
"xorq rax,rax\n"
|
|
"addq rax,r8\n"
|
|
"addq rsp,8\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(LongAddReg, assembler) {
|
|
__ pushq(CallingConventions::kArg2Reg);
|
|
__ pushq(CallingConventions::kArg1Reg);
|
|
__ movl(RAX, Address(RSP, 0)); // left low.
|
|
__ movl(RDX, Address(RSP, 4)); // left high.
|
|
__ movl(RCX, Address(RSP, 8)); // right low.
|
|
__ movl(R8, Address(RSP, 12)); // right high
|
|
__ addl(RAX, RCX);
|
|
__ adcl(RDX, R8);
|
|
// Result is in RAX/RDX.
|
|
__ movl(Address(RSP, 0), RAX); // result low.
|
|
__ movl(Address(RSP, 4), RDX); // result high.
|
|
__ popq(RAX);
|
|
__ popq(RDX);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(LongAddReg, test) {
|
|
typedef int64_t (*LongAddRegCode)(int64_t a, int64_t b);
|
|
int64_t a = 12;
|
|
int64_t b = 14;
|
|
int64_t res = reinterpret_cast<LongAddRegCode>(test->entry())(a, b);
|
|
EXPECT_EQ((a + b), res);
|
|
a = 2147483647;
|
|
b = 600000;
|
|
res = reinterpret_cast<LongAddRegCode>(test->entry())(a, b);
|
|
EXPECT_EQ((a + b), res);
|
|
EXPECT_DISASSEMBLY_NOT_WINDOWS(
|
|
"push rsi\n"
|
|
"push rdi\n"
|
|
"movl rax,[rsp]\n"
|
|
"movl rdx,[rsp+0x4]\n"
|
|
"movl rcx,[rsp+0x8]\n"
|
|
"movl r8,[rsp+0xc]\n"
|
|
"addl rax,rcx\n"
|
|
"adcl rdx,r8\n"
|
|
"movl [rsp],rax\n"
|
|
"movl [rsp+0x4],rdx\n"
|
|
"pop rax\n"
|
|
"pop rdx\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(LongAddImmediate, assembler) {
|
|
__ pushq(CallingConventions::kArg1Reg);
|
|
__ movl(RAX, Address(RSP, 0)); // left low.
|
|
__ movl(RDX, Address(RSP, 4)); // left high.
|
|
__ addl(RAX, Immediate(12)); // right low immediate.
|
|
__ adcl(RDX, Immediate(11)); // right high immediate.
|
|
// Result is in RAX/RDX.
|
|
__ movl(Address(RSP, 0), RAX); // result low.
|
|
__ movl(Address(RSP, 4), RDX); // result high.
|
|
__ popq(RAX);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(LongAddImmediate, test) {
|
|
typedef int64_t (*LongAddImmediateCode)(int64_t a);
|
|
int64_t a = (13LL << 32) + 14;
|
|
int64_t b = (11LL << 32) + 12;
|
|
int64_t res = reinterpret_cast<LongAddImmediateCode>(test->entry())(a);
|
|
EXPECT_EQ((a + b), res);
|
|
a = (13LL << 32) - 1;
|
|
res = reinterpret_cast<LongAddImmediateCode>(test->entry())(a);
|
|
EXPECT_EQ((a + b), res);
|
|
EXPECT_DISASSEMBLY_NOT_WINDOWS(
|
|
"push rdi\n"
|
|
"movl rax,[rsp]\n"
|
|
"movl rdx,[rsp+0x4]\n"
|
|
"addl rax,0xc\n"
|
|
"adcl rdx,0xb\n"
|
|
"movl [rsp],rax\n"
|
|
"movl [rsp+0x4],rdx\n"
|
|
"pop rax\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(LongAddAddress, assembler) {
|
|
__ pushq(CallingConventions::kArg2Reg);
|
|
__ pushq(CallingConventions::kArg1Reg);
|
|
__ movl(RAX, Address(RSP, 0)); // left low.
|
|
__ movl(RDX, Address(RSP, 4)); // left high.
|
|
__ addl(RAX, Address(RSP, 8)); // low.
|
|
__ adcl(RDX, Address(RSP, 12)); // high.
|
|
// Result is in RAX/RDX.
|
|
__ movl(Address(RSP, 0), RAX); // result low.
|
|
__ movl(Address(RSP, 4), RDX); // result high.
|
|
__ popq(RAX);
|
|
__ popq(RDX);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(LongAddAddress, test) {
|
|
typedef int64_t (*LongAddAddressCode)(int64_t a, int64_t b);
|
|
int64_t a = 12;
|
|
int64_t b = 14;
|
|
int64_t res = reinterpret_cast<LongAddAddressCode>(test->entry())(a, b);
|
|
EXPECT_EQ((a + b), res);
|
|
a = 2147483647;
|
|
b = 600000;
|
|
res = reinterpret_cast<LongAddAddressCode>(test->entry())(a, b);
|
|
EXPECT_EQ((a + b), res);
|
|
EXPECT_DISASSEMBLY_NOT_WINDOWS(
|
|
"push rsi\n"
|
|
"push rdi\n"
|
|
"movl rax,[rsp]\n"
|
|
"movl rdx,[rsp+0x4]\n"
|
|
"addl rax,[rsp+0x8]\n"
|
|
"adcl rdx,[rsp+0xc]\n"
|
|
"movl [rsp],rax\n"
|
|
"movl [rsp+0x4],rdx\n"
|
|
"pop rax\n"
|
|
"pop rdx\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(LongSubReg, assembler) {
|
|
__ pushq(CallingConventions::kArg2Reg);
|
|
__ pushq(CallingConventions::kArg1Reg);
|
|
__ movl(RAX, Address(RSP, 0)); // left low.
|
|
__ movl(RDX, Address(RSP, 4)); // left high.
|
|
__ movl(RCX, Address(RSP, 8)); // right low.
|
|
__ movl(R8, Address(RSP, 12)); // right high
|
|
__ subl(RAX, RCX);
|
|
__ sbbl(RDX, R8);
|
|
// Result is in RAX/RDX.
|
|
__ movl(Address(RSP, 0), RAX); // result low.
|
|
__ movl(Address(RSP, 4), RDX); // result high.
|
|
__ popq(RAX);
|
|
__ popq(RDX);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(LongSubReg, test) {
|
|
typedef int64_t (*LongSubRegCode)(int64_t a, int64_t b);
|
|
int64_t a = 12;
|
|
int64_t b = 14;
|
|
int64_t res = reinterpret_cast<LongSubRegCode>(test->entry())(a, b);
|
|
EXPECT_EQ((a - b), res);
|
|
a = 600000;
|
|
b = 2147483647;
|
|
res = reinterpret_cast<LongSubRegCode>(test->entry())(a, b);
|
|
EXPECT_EQ((a - b), res);
|
|
EXPECT_DISASSEMBLY_NOT_WINDOWS(
|
|
"push rsi\n"
|
|
"push rdi\n"
|
|
"movl rax,[rsp]\n"
|
|
"movl rdx,[rsp+0x4]\n"
|
|
"movl rcx,[rsp+0x8]\n"
|
|
"movl r8,[rsp+0xc]\n"
|
|
"subl rax,rcx\n"
|
|
"sbbl rdx,r8\n"
|
|
"movl [rsp],rax\n"
|
|
"movl [rsp+0x4],rdx\n"
|
|
"pop rax\n"
|
|
"pop rdx\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(LongSubImmediate, assembler) {
|
|
__ pushq(CallingConventions::kArg1Reg);
|
|
__ movl(RAX, Immediate(0));
|
|
__ subl(
|
|
RAX,
|
|
Immediate(1)); // Set the carry flag so we can test that subl ignores it.
|
|
__ movl(RAX, Address(RSP, 0)); // left low.
|
|
__ movl(RDX, Address(RSP, 4)); // left high.
|
|
__ subl(RAX, Immediate(12)); // right low immediate.
|
|
__ sbbl(RDX, Immediate(11)); // right high immediate.
|
|
// Result is in RAX/RDX.
|
|
__ movl(Address(RSP, 0), RAX); // result low.
|
|
__ movl(Address(RSP, 4), RDX); // result high.
|
|
__ popq(RAX);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(LongSubImmediate, test) {
|
|
typedef int64_t (*LongSubImmediateCode)(int64_t a);
|
|
int64_t a = (13LL << 32) + 14;
|
|
int64_t b = (11LL << 32) + 12;
|
|
int64_t res = reinterpret_cast<LongSubImmediateCode>(test->entry())(a);
|
|
EXPECT_EQ((a - b), res);
|
|
a = (13LL << 32) + 10;
|
|
res = reinterpret_cast<LongSubImmediateCode>(test->entry())(a);
|
|
EXPECT_EQ((a - b), res);
|
|
EXPECT_DISASSEMBLY_NOT_WINDOWS(
|
|
"push rdi\n"
|
|
"movl rax,0\n"
|
|
"subl rax,1\n"
|
|
"movl rax,[rsp]\n"
|
|
"movl rdx,[rsp+0x4]\n"
|
|
"subl rax,0xc\n"
|
|
"sbbl rdx,0xb\n"
|
|
"movl [rsp],rax\n"
|
|
"movl [rsp+0x4],rdx\n"
|
|
"pop rax\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(LongSubAddress, assembler) {
|
|
__ pushq(CallingConventions::kArg2Reg);
|
|
__ pushq(CallingConventions::kArg1Reg);
|
|
__ movl(RAX, Address(RSP, 0)); // left low.
|
|
__ movl(RDX, Address(RSP, 4)); // left high.
|
|
__ subl(RAX, Address(RSP, 8)); // low.
|
|
__ sbbl(RDX, Address(RSP, 12)); // high.
|
|
// Result is in RAX/RDX.
|
|
__ movl(Address(RSP, 0), RAX); // result low.
|
|
__ movl(Address(RSP, 4), RDX); // result high.
|
|
__ popq(RAX);
|
|
__ popq(RDX);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(LongSubAddress, test) {
|
|
typedef int64_t (*LongSubAddressCode)(int64_t a, int64_t b);
|
|
int64_t a = 12;
|
|
int64_t b = 14;
|
|
int64_t res = reinterpret_cast<LongSubAddressCode>(test->entry())(a, b);
|
|
EXPECT_EQ((a - b), res);
|
|
a = 600000;
|
|
b = 2147483647;
|
|
res = reinterpret_cast<LongSubAddressCode>(test->entry())(a, b);
|
|
EXPECT_EQ((a - b), res);
|
|
EXPECT_DISASSEMBLY_NOT_WINDOWS(
|
|
"push rsi\n"
|
|
"push rdi\n"
|
|
"movl rax,[rsp]\n"
|
|
"movl rdx,[rsp+0x4]\n"
|
|
"subl rax,[rsp+0x8]\n"
|
|
"sbbl rdx,[rsp+0xc]\n"
|
|
"movl [rsp],rax\n"
|
|
"movl [rsp+0x4],rdx\n"
|
|
"pop rax\n"
|
|
"pop rdx\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(AddReg, assembler) {
|
|
__ movq(R10, CallingConventions::kArg1Reg); // al.
|
|
__ addq(R10, CallingConventions::kArg3Reg); // bl.
|
|
__ movq(RAX, CallingConventions::kArg2Reg); // ah.
|
|
__ adcq(RAX, CallingConventions::kArg4Reg); // bh.
|
|
// RAX = high64(ah:al + bh:bl).
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(AddReg, test) {
|
|
typedef int64_t (*AddRegCode)(int64_t al, int64_t ah, int64_t bl, int64_t bh);
|
|
int64_t al = 11;
|
|
int64_t ah = 12;
|
|
int64_t bl = 13;
|
|
int64_t bh = 14;
|
|
int64_t res = reinterpret_cast<AddRegCode>(test->entry())(al, ah, bl, bh);
|
|
EXPECT_EQ((ah + bh), res);
|
|
al = -1;
|
|
res = reinterpret_cast<AddRegCode>(test->entry())(al, ah, bl, bh);
|
|
EXPECT_EQ((ah + bh + 1), res);
|
|
EXPECT_DISASSEMBLY_NOT_WINDOWS(
|
|
"movq r10,rdi\n"
|
|
"addq r10,rdx\n"
|
|
"movq rax,rsi\n"
|
|
"adcq rax,rcx\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(AddImmediate, assembler) {
|
|
__ movq(R10, CallingConventions::kArg1Reg); // al.
|
|
__ addq(R10, Immediate(13)); // bl.
|
|
__ movq(RAX, CallingConventions::kArg2Reg); // ah.
|
|
__ adcq(RAX, Immediate(14)); // bh.
|
|
// RAX = high64(ah:al + bh:bl).
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(AddImmediate, test) {
|
|
typedef int64_t (*AddImmediateCode)(int64_t al, int64_t ah);
|
|
int64_t al = 11;
|
|
int64_t ah = 12;
|
|
int64_t bh = 14;
|
|
int64_t res = reinterpret_cast<AddImmediateCode>(test->entry())(al, ah);
|
|
EXPECT_EQ((ah + bh), res);
|
|
al = -1;
|
|
res = reinterpret_cast<AddImmediateCode>(test->entry())(al, ah);
|
|
EXPECT_EQ((ah + bh + 1), res);
|
|
EXPECT_DISASSEMBLY_NOT_WINDOWS(
|
|
"movq r10,rdi\n"
|
|
"addq r10,0xd\n"
|
|
"movq rax,rsi\n"
|
|
"adcq rax,0xe\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(AddAddress, assembler) {
|
|
__ pushq(CallingConventions::kArg4Reg);
|
|
__ pushq(CallingConventions::kArg3Reg);
|
|
__ pushq(CallingConventions::kArg2Reg);
|
|
__ pushq(CallingConventions::kArg1Reg);
|
|
__ movq(R10, Address(RSP, 0 * target::kWordSize)); // al.
|
|
__ addq(R10, Address(RSP, 2 * target::kWordSize)); // bl.
|
|
__ movq(RAX, Address(RSP, 1 * target::kWordSize)); // ah.
|
|
__ adcq(RAX, Address(RSP, 3 * target::kWordSize)); // bh.
|
|
// RAX = high64(ah:al + bh:bl).
|
|
__ Drop(4);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(AddAddress, test) {
|
|
typedef int64_t (*AddCode)(int64_t al, int64_t ah, int64_t bl, int64_t bh);
|
|
int64_t al = 11;
|
|
int64_t ah = 12;
|
|
int64_t bl = 13;
|
|
int64_t bh = 14;
|
|
int64_t res = reinterpret_cast<AddCode>(test->entry())(al, ah, bl, bh);
|
|
EXPECT_EQ((ah + bh), res);
|
|
al = -1;
|
|
res = reinterpret_cast<AddCode>(test->entry())(al, ah, bl, bh);
|
|
EXPECT_EQ((ah + bh + 1), res);
|
|
EXPECT_DISASSEMBLY_NOT_WINDOWS(
|
|
"push rcx\n"
|
|
"push rdx\n"
|
|
"push rsi\n"
|
|
"push rdi\n"
|
|
"movq r10,[rsp]\n"
|
|
"addq r10,[rsp+0x10]\n"
|
|
"movq rax,[rsp+0x8]\n"
|
|
"adcq rax,[rsp+0x18]\n"
|
|
"pop tmp\n"
|
|
"pop tmp\n"
|
|
"pop tmp\n"
|
|
"pop tmp\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(SubReg, assembler) {
|
|
__ movq(R10, CallingConventions::kArg1Reg); // al.
|
|
__ subq(R10, CallingConventions::kArg3Reg); // bl.
|
|
__ movq(RAX, CallingConventions::kArg2Reg); // ah.
|
|
__ sbbq(RAX, CallingConventions::kArg4Reg); // bh.
|
|
// RAX = high64(ah:al - bh:bl).
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(SubReg, test) {
|
|
typedef int64_t (*SubRegCode)(int64_t al, int64_t ah, int64_t bl, int64_t bh);
|
|
int64_t al = 14;
|
|
int64_t ah = 13;
|
|
int64_t bl = 12;
|
|
int64_t bh = 11;
|
|
int64_t res = reinterpret_cast<SubRegCode>(test->entry())(al, ah, bl, bh);
|
|
EXPECT_EQ((ah - bh), res);
|
|
al = 10;
|
|
res = reinterpret_cast<SubRegCode>(test->entry())(al, ah, bl, bh);
|
|
EXPECT_EQ((ah - bh - 1), res);
|
|
EXPECT_DISASSEMBLY_NOT_WINDOWS(
|
|
"movq r10,rdi\n"
|
|
"subq r10,rdx\n"
|
|
"movq rax,rsi\n"
|
|
"sbbq rax,rcx\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(SubImmediate, assembler) {
|
|
__ movq(R10, CallingConventions::kArg1Reg); // al.
|
|
__ subq(R10, Immediate(12)); // bl.
|
|
__ movq(RAX, CallingConventions::kArg2Reg); // ah.
|
|
__ sbbq(RAX, Immediate(11)); // bh.
|
|
// RAX = high64(ah:al - bh:bl).
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(SubImmediate, test) {
|
|
typedef int64_t (*SubImmediateCode)(int64_t al, int64_t ah);
|
|
int64_t al = 14;
|
|
int64_t ah = 13;
|
|
int64_t bh = 11;
|
|
int64_t res = reinterpret_cast<SubImmediateCode>(test->entry())(al, ah);
|
|
EXPECT_EQ((ah - bh), res);
|
|
al = 10;
|
|
res = reinterpret_cast<SubImmediateCode>(test->entry())(al, ah);
|
|
EXPECT_EQ((ah - bh - 1), res);
|
|
EXPECT_DISASSEMBLY_NOT_WINDOWS(
|
|
"movq r10,rdi\n"
|
|
"subq r10,0xc\n"
|
|
"movq rax,rsi\n"
|
|
"sbbq rax,0xb\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(SubAddress, assembler) {
|
|
__ pushq(CallingConventions::kArg4Reg);
|
|
__ pushq(CallingConventions::kArg3Reg);
|
|
__ pushq(CallingConventions::kArg2Reg);
|
|
__ pushq(CallingConventions::kArg1Reg);
|
|
__ movq(R10, Address(RSP, 0 * target::kWordSize)); // al.
|
|
__ subq(R10, Address(RSP, 2 * target::kWordSize)); // bl.
|
|
__ movq(RAX, Address(RSP, 1 * target::kWordSize)); // ah.
|
|
__ sbbq(RAX, Address(RSP, 3 * target::kWordSize)); // bh.
|
|
// RAX = high64(ah:al - bh:bl).
|
|
__ Drop(4);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(SubAddress, test) {
|
|
typedef int64_t (*SubCode)(int64_t al, int64_t ah, int64_t bl, int64_t bh);
|
|
int64_t al = 14;
|
|
int64_t ah = 13;
|
|
int64_t bl = 12;
|
|
int64_t bh = 11;
|
|
int64_t res = reinterpret_cast<SubCode>(test->entry())(al, ah, bl, bh);
|
|
EXPECT_EQ((ah - bh), res);
|
|
al = 10;
|
|
res = reinterpret_cast<SubCode>(test->entry())(al, ah, bl, bh);
|
|
EXPECT_EQ((ah - bh - 1), res);
|
|
EXPECT_DISASSEMBLY_NOT_WINDOWS(
|
|
"push rcx\n"
|
|
"push rdx\n"
|
|
"push rsi\n"
|
|
"push rdi\n"
|
|
"movq r10,[rsp]\n"
|
|
"subq r10,[rsp+0x10]\n"
|
|
"movq rax,[rsp+0x8]\n"
|
|
"sbbq rax,[rsp+0x18]\n"
|
|
"pop tmp\n"
|
|
"pop tmp\n"
|
|
"pop tmp\n"
|
|
"pop tmp\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(Bitwise, assembler) {
|
|
__ movq(R10, Immediate(-1));
|
|
__ orl(Address(CallingConventions::kArg1Reg, 0), R10);
|
|
__ orl(Address(CallingConventions::kArg2Reg, 0), R10);
|
|
__ movl(RCX, Immediate(42));
|
|
__ xorl(RCX, RCX);
|
|
__ orl(RCX, Immediate(256));
|
|
__ movl(RAX, Immediate(4));
|
|
__ orl(RCX, RAX);
|
|
__ movl(RAX, Immediate(0xfff0));
|
|
__ andl(RCX, RAX);
|
|
__ movl(RAX, Immediate(1));
|
|
__ orl(RCX, RAX);
|
|
__ movl(RAX, RCX);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(Bitwise, test) {
|
|
uint64_t f1 = 0;
|
|
uint64_t f2 = 0;
|
|
typedef int (*Bitwise)(void*, void*);
|
|
int result = reinterpret_cast<Bitwise>(test->entry())(&f1, &f2);
|
|
EXPECT_EQ(256 + 1, result);
|
|
EXPECT_EQ(kMaxUint32, f1);
|
|
EXPECT_EQ(kMaxUint32, f2);
|
|
EXPECT_DISASSEMBLY_NOT_WINDOWS(
|
|
"movq r10,-1\n"
|
|
"orl [rdi],r10\n"
|
|
"orl [rsi],r10\n"
|
|
"movl rcx,0x2a\n"
|
|
"xorl rcx,rcx\n"
|
|
"orl rcx,0x100\n"
|
|
"movl rax,4\n"
|
|
"orl rcx,rax\n"
|
|
"movl rax,0xfff0\n"
|
|
"andl rcx,rax\n"
|
|
"movl rax,1\n"
|
|
"orl rcx,rax\n"
|
|
"movl rax,rcx\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(Bitwise64, assembler) {
|
|
Label error;
|
|
__ movq(RAX, Immediate(42));
|
|
__ pushq(RAX);
|
|
__ xorq(RAX, Address(RSP, 0));
|
|
__ popq(RCX);
|
|
__ cmpq(RAX, Immediate(0));
|
|
__ j(NOT_EQUAL, &error);
|
|
__ movq(RCX, Immediate(0xFF));
|
|
__ movq(RAX, Immediate(0x5));
|
|
__ xorq(RCX, RAX);
|
|
__ cmpq(RCX, Immediate(0xFF ^ 0x5));
|
|
__ j(NOT_EQUAL, &error);
|
|
__ pushq(Immediate(0xFF));
|
|
__ movq(RCX, Immediate(0x5));
|
|
__ xorq(Address(RSP, 0), RCX);
|
|
__ popq(RCX);
|
|
__ cmpq(RCX, Immediate(0xFF ^ 0x5));
|
|
__ j(NOT_EQUAL, &error);
|
|
__ xorq(RCX, RCX);
|
|
__ orq(RCX, Immediate(256));
|
|
__ movq(RAX, Immediate(4));
|
|
__ orq(RCX, RAX);
|
|
__ movq(RAX, Immediate(0xfff0));
|
|
__ andq(RCX, RAX);
|
|
__ movq(RAX, Immediate(1));
|
|
__ pushq(RAX);
|
|
__ orq(RCX, Address(RSP, 0));
|
|
__ xorq(RCX, Immediate(0));
|
|
__ popq(RAX);
|
|
__ movq(RAX, RCX);
|
|
__ ret();
|
|
__ Bind(&error);
|
|
__ movq(RAX, Immediate(-1));
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(Bitwise64, test) {
|
|
typedef int (*Bitwise64)();
|
|
EXPECT_EQ(256 + 1, reinterpret_cast<Bitwise64>(test->entry())());
|
|
EXPECT_DISASSEMBLY(
|
|
"movl rax,0x2a\n"
|
|
"push rax\n"
|
|
"xorq rax,[rsp]\n"
|
|
"pop rcx\n"
|
|
"cmpq rax,0\n"
|
|
"jnz +105\n"
|
|
"movl rcx,0xff\n"
|
|
"movl rax,5\n"
|
|
"xorq rcx,rax\n"
|
|
"cmpq rcx,0xfa\n"
|
|
"jnz +79\n"
|
|
"push 0xff\n"
|
|
"movl rcx,5\n"
|
|
"xorq [rsp],rcx\n"
|
|
"pop rcx\n"
|
|
"cmpq rcx,0xfa\n"
|
|
"jnz +51\n"
|
|
"xorq rcx,rcx\n"
|
|
"orq rcx,0x100\n"
|
|
"movl rax,4\n"
|
|
"orq rcx,rax\n"
|
|
"movl rax,0xfff0\n"
|
|
"andq rcx,rax\n"
|
|
"movl rax,1\n"
|
|
"push rax\n"
|
|
"orq rcx,[rsp]\n"
|
|
"xorq rcx,0\n"
|
|
"pop rax\n"
|
|
"movq rax,rcx\n"
|
|
"ret\n"
|
|
"movq rax,-1\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(LogicalOps, assembler) {
|
|
Label donetest1;
|
|
__ movl(RAX, Immediate(4));
|
|
__ andl(RAX, Immediate(2));
|
|
__ cmpl(RAX, Immediate(0));
|
|
__ j(EQUAL, &donetest1);
|
|
// Be sure to skip this crashing code.
|
|
__ movl(RAX, Immediate(0));
|
|
__ movl(Address(RAX, 0), RAX);
|
|
__ Bind(&donetest1);
|
|
|
|
Label donetest2;
|
|
__ movl(RCX, Immediate(4));
|
|
__ andl(RCX, Immediate(4));
|
|
__ cmpl(RCX, Immediate(0));
|
|
__ j(NOT_EQUAL, &donetest2);
|
|
// Be sure to skip this crashing code.
|
|
__ movl(RAX, Immediate(0));
|
|
__ movl(Address(RAX, 0), RAX);
|
|
__ Bind(&donetest2);
|
|
|
|
Label donetest3;
|
|
__ movl(RAX, Immediate(0));
|
|
__ orl(RAX, Immediate(0));
|
|
__ cmpl(RAX, Immediate(0));
|
|
__ j(EQUAL, &donetest3);
|
|
// Be sure to skip this crashing code.
|
|
__ movl(RAX, Immediate(0));
|
|
__ movl(Address(RAX, 0), RAX);
|
|
__ Bind(&donetest3);
|
|
|
|
Label donetest4;
|
|
__ movl(RAX, Immediate(4));
|
|
__ orl(RAX, Immediate(0));
|
|
__ cmpl(RAX, Immediate(0));
|
|
__ j(NOT_EQUAL, &donetest4);
|
|
// Be sure to skip this crashing code.
|
|
__ movl(RAX, Immediate(0));
|
|
__ movl(Address(RAX, 0), RAX);
|
|
__ Bind(&donetest4);
|
|
|
|
Label donetest5;
|
|
__ pushq(RAX);
|
|
__ movl(RAX, Immediate(0xff));
|
|
__ movl(Address(RSP, 0), RAX);
|
|
__ cmpl(Address(RSP, 0), Immediate(0xff));
|
|
__ j(EQUAL, &donetest5);
|
|
// Be sure to skip this crashing code.
|
|
__ movq(RAX, Immediate(0));
|
|
__ movq(Address(RAX, 0), RAX);
|
|
__ Bind(&donetest5);
|
|
__ popq(RAX);
|
|
|
|
Label donetest6;
|
|
__ movl(RAX, Immediate(1));
|
|
__ shll(RAX, Immediate(3));
|
|
__ cmpl(RAX, Immediate(8));
|
|
__ j(EQUAL, &donetest6);
|
|
// Be sure to skip this crashing code.
|
|
__ movl(RAX, Immediate(0));
|
|
__ movl(Address(RAX, 0), RAX);
|
|
__ Bind(&donetest6);
|
|
|
|
Label donetest7;
|
|
__ movl(RAX, Immediate(2));
|
|
__ shrl(RAX, Immediate(1));
|
|
__ cmpl(RAX, Immediate(1));
|
|
__ j(EQUAL, &donetest7);
|
|
// Be sure to skip this crashing code.
|
|
__ movl(RAX, Immediate(0));
|
|
__ movl(Address(RAX, 0), RAX);
|
|
__ Bind(&donetest7);
|
|
|
|
Label donetest8;
|
|
__ movl(RAX, Immediate(8));
|
|
__ shrl(RAX, Immediate(3));
|
|
__ cmpl(RAX, Immediate(1));
|
|
__ j(EQUAL, &donetest8);
|
|
// Be sure to skip this crashing code.
|
|
__ movl(RAX, Immediate(0));
|
|
__ movl(Address(RAX, 0), RAX);
|
|
__ Bind(&donetest8);
|
|
|
|
Label donetest9;
|
|
__ movl(RAX, Immediate(1));
|
|
__ movl(RCX, Immediate(3));
|
|
__ shll(RAX, RCX);
|
|
__ cmpl(RAX, Immediate(8));
|
|
__ j(EQUAL, &donetest9);
|
|
// Be sure to skip this crashing code.
|
|
__ movl(RAX, Immediate(0));
|
|
__ movl(Address(RAX, 0), RAX);
|
|
__ Bind(&donetest9);
|
|
|
|
Label donetest10;
|
|
__ movl(RAX, Immediate(8));
|
|
__ movl(RCX, Immediate(3));
|
|
__ shrl(RAX, RCX);
|
|
__ cmpl(RAX, Immediate(1));
|
|
__ j(EQUAL, &donetest10);
|
|
// Be sure to skip this crashing code.
|
|
__ movl(RAX, Immediate(0));
|
|
__ movl(Address(RAX, 0), RAX);
|
|
__ Bind(&donetest10);
|
|
|
|
Label donetest6a;
|
|
__ movl(RAX, Immediate(1));
|
|
__ shlq(RAX, Immediate(3));
|
|
__ cmpl(RAX, Immediate(8));
|
|
__ j(EQUAL, &donetest6a);
|
|
// Be sure to skip this crashing code.
|
|
__ movl(RAX, Immediate(0));
|
|
__ movl(Address(RAX, 0), RAX);
|
|
__ Bind(&donetest6a);
|
|
|
|
Label donetest7a;
|
|
__ movl(RAX, Immediate(2));
|
|
__ shrq(RAX, Immediate(1));
|
|
__ cmpl(RAX, Immediate(1));
|
|
__ j(EQUAL, &donetest7a);
|
|
// Be sure to skip this crashing code.
|
|
__ movl(RAX, Immediate(0));
|
|
__ movl(Address(RAX, 0), RAX);
|
|
__ Bind(&donetest7a);
|
|
|
|
Label donetest8a;
|
|
__ movl(RAX, Immediate(8));
|
|
__ shrq(RAX, Immediate(3));
|
|
__ cmpl(RAX, Immediate(1));
|
|
__ j(EQUAL, &donetest8a);
|
|
// Be sure to skip this crashing code.
|
|
__ movl(RAX, Immediate(0));
|
|
__ movl(Address(RAX, 0), RAX);
|
|
__ Bind(&donetest8a);
|
|
|
|
Label donetest9a;
|
|
__ movl(RAX, Immediate(1));
|
|
__ movl(RCX, Immediate(3));
|
|
__ shlq(RAX, RCX);
|
|
__ cmpl(RAX, Immediate(8));
|
|
__ j(EQUAL, &donetest9a);
|
|
// Be sure to skip this crashing code.
|
|
__ movl(RAX, Immediate(0));
|
|
__ movl(Address(RAX, 0), RAX);
|
|
__ Bind(&donetest9a);
|
|
|
|
Label donetest10a;
|
|
__ movl(RAX, Immediate(8));
|
|
__ movl(RCX, Immediate(3));
|
|
__ shrq(RAX, RCX);
|
|
__ cmpl(RAX, Immediate(1));
|
|
__ j(EQUAL, &donetest10a);
|
|
// Be sure to skip this crashing code.
|
|
__ movl(RAX, Immediate(0));
|
|
__ movl(Address(RAX, 0), RAX);
|
|
__ Bind(&donetest10a);
|
|
|
|
Label donetest11a;
|
|
__ movl(RAX, Immediate(1));
|
|
__ shlq(RAX, Immediate(31));
|
|
__ shrq(RAX, Immediate(3));
|
|
__ cmpq(RAX, Immediate(0x10000000));
|
|
__ j(EQUAL, &donetest11a);
|
|
// Be sure to skip this crashing code.
|
|
__ movl(RAX, Immediate(0));
|
|
__ movl(Address(RAX, 0), RAX);
|
|
__ Bind(&donetest11a);
|
|
|
|
Label donetest12a;
|
|
__ movl(RAX, Immediate(1));
|
|
__ shlq(RAX, Immediate(31));
|
|
__ sarl(RAX, Immediate(3));
|
|
__ cmpl(RAX, Immediate(0xfffffffff0000000));
|
|
__ j(EQUAL, &donetest12a);
|
|
// Be sure to skip this crashing code.
|
|
__ movl(RAX, Immediate(0));
|
|
__ movl(Address(RAX, 0), RAX);
|
|
__ Bind(&donetest12a);
|
|
|
|
Label donetest13a;
|
|
__ movl(RAX, Immediate(1));
|
|
__ movl(RCX, Immediate(3));
|
|
__ shlq(RAX, Immediate(31));
|
|
__ sarl(RAX, RCX);
|
|
__ cmpl(RAX, Immediate(0xfffffffff0000000));
|
|
__ j(EQUAL, &donetest13a);
|
|
// Be sure to skip this crashing code.
|
|
__ movl(RAX, Immediate(0));
|
|
__ movl(Address(RAX, 0), RAX);
|
|
__ Bind(&donetest13a);
|
|
|
|
{
|
|
Label donetest15a;
|
|
const int32_t left = 0xff000000;
|
|
const int32_t right = 0xffffffff;
|
|
const int32_t shifted = 0xf0000003;
|
|
__ movl(RDX, Immediate(left));
|
|
__ movl(R8, Immediate(right));
|
|
__ movl(RCX, Immediate(2));
|
|
__ shll(RDX, RCX); // RDX = 0xff000000 << 2 == 0xfc000000
|
|
__ shldl(RDX, R8, Immediate(2));
|
|
// RDX = high32(0xfc000000:0xffffffff << 2) == 0xf0000003
|
|
__ cmpl(RDX, Immediate(shifted));
|
|
__ j(EQUAL, &donetest15a);
|
|
__ int3();
|
|
__ Bind(&donetest15a);
|
|
}
|
|
|
|
{
|
|
Label donetest15b;
|
|
const int64_t left = 0xff00000000000000;
|
|
const int64_t right = 0xffffffffffffffff;
|
|
const int64_t shifted = 0xf000000000000003;
|
|
__ movq(RDX, Immediate(left));
|
|
__ movq(R8, Immediate(right));
|
|
__ movq(RCX, Immediate(2));
|
|
__ shlq(RDX, RCX); // RDX = 0xff00000000000000 << 2 == 0xfc00000000000000
|
|
__ shldq(RDX, R8, Immediate(2));
|
|
// RDX = high64(0xfc00000000000000:0xffffffffffffffff << 2)
|
|
// == 0xf000000000000003
|
|
__ cmpq(RDX, Immediate(shifted));
|
|
__ j(EQUAL, &donetest15b);
|
|
__ int3();
|
|
__ Bind(&donetest15b);
|
|
}
|
|
|
|
{
|
|
Label donetest15c;
|
|
const int64_t left = 0xff00000000000000;
|
|
const int64_t right = 0xffffffffffffffff;
|
|
const int64_t shifted = 0xf000000000000003;
|
|
__ movq(RDX, Immediate(left));
|
|
__ movq(R8, Immediate(right));
|
|
__ movq(RCX, Immediate(2));
|
|
__ shlq(RDX, RCX); // RDX = 0xff00000000000000 << 2 == 0xfc00000000000000
|
|
__ shldq(RDX, R8, RCX);
|
|
// RDX = high64(0xfc00000000000000:0xffffffffffffffff << 2)
|
|
// == 0xf000000000000003
|
|
__ cmpq(RDX, Immediate(shifted));
|
|
__ j(EQUAL, &donetest15c);
|
|
__ int3();
|
|
__ Bind(&donetest15c);
|
|
}
|
|
|
|
{
|
|
Label donetest15d;
|
|
const int64_t left = 0xff00000000000000;
|
|
const int64_t right = 0xffffffffffffffff;
|
|
const int64_t shifted = 0xcff0000000000000;
|
|
__ movq(RDX, Immediate(left));
|
|
__ movq(R8, Immediate(right));
|
|
__ movq(RCX, Immediate(2));
|
|
__ shrq(RDX, RCX); // RDX = 0xff00000000000000 >> 2 == 0x3fc0000000000000
|
|
__ shrdq(RDX, R8, RCX);
|
|
// RDX = low64(0xffffffffffffffff:0x3fc0000000000000 >> 2)
|
|
// == 0xcff0000000000000
|
|
__ cmpq(RDX, Immediate(shifted));
|
|
__ j(EQUAL, &donetest15d);
|
|
__ int3();
|
|
__ Bind(&donetest15d);
|
|
}
|
|
|
|
__ movl(RAX, Immediate(0));
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(LogicalOps, test) {
|
|
typedef int (*LogicalOpsCode)();
|
|
EXPECT_EQ(0, reinterpret_cast<LogicalOpsCode>(test->entry())());
|
|
EXPECT_DISASSEMBLY(
|
|
"movl rax,4\n"
|
|
"andl rax,2\n"
|
|
"cmpl rax,0\n"
|
|
"jz +14\n"
|
|
"movl rax,0\n"
|
|
"movl [rax],rax\n"
|
|
"movl rcx,4\n"
|
|
"andl rcx,4\n"
|
|
"cmpl rcx,0\n"
|
|
"jnz +14\n"
|
|
"movl rax,0\n"
|
|
"movl [rax],rax\n"
|
|
"movl rax,0\n"
|
|
"orl rax,0\n"
|
|
"cmpl rax,0\n"
|
|
"jz +14\n"
|
|
"movl rax,0\n"
|
|
"movl [rax],rax\n"
|
|
"movl rax,4\n"
|
|
"orl rax,0\n"
|
|
"cmpl rax,0\n"
|
|
"jnz +14\n"
|
|
"movl rax,0\n"
|
|
"movl [rax],rax\n"
|
|
"push rax\n"
|
|
"movl rax,0xff\n"
|
|
"movl [rsp],rax\n"
|
|
"cmpl [rsp],0xff\n"
|
|
"jz +14\n"
|
|
"movl rax,0\n"
|
|
"movq [rax],rax\n"
|
|
"pop rax\n"
|
|
"movl rax,1\n"
|
|
"shll rax,3\n"
|
|
"cmpl rax,8\n"
|
|
"jz +14\n"
|
|
"movl rax,0\n"
|
|
"movl [rax],rax\n"
|
|
"movl rax,2\n"
|
|
"shrl rax,1\n"
|
|
"cmpl rax,1\n"
|
|
"jz +14\n"
|
|
"movl rax,0\n"
|
|
"movl [rax],rax\n"
|
|
"movl rax,8\n"
|
|
"shrl rax,3\n"
|
|
"cmpl rax,1\n"
|
|
"jz +14\n"
|
|
"movl rax,0\n"
|
|
"movl [rax],rax\n"
|
|
"movl rax,1\n"
|
|
"movl rcx,3\n"
|
|
"shll rax,cl\n"
|
|
"cmpl rax,8\n"
|
|
"jz +14\n"
|
|
"movl rax,0\n"
|
|
"movl [rax],rax\n"
|
|
"movl rax,8\n"
|
|
"movl rcx,3\n"
|
|
"shrl rax,cl\n"
|
|
"cmpl rax,1\n"
|
|
"jz +14\n"
|
|
"movl rax,0\n"
|
|
"movl [rax],rax\n"
|
|
"movl rax,1\n"
|
|
"shlq rax,3\n"
|
|
"cmpl rax,8\n"
|
|
"jz +14\n"
|
|
"movl rax,0\n"
|
|
"movl [rax],rax\n"
|
|
"movl rax,2\n"
|
|
"shrq rax,1\n"
|
|
"cmpl rax,1\n"
|
|
"jz +14\n"
|
|
"movl rax,0\n"
|
|
"movl [rax],rax\n"
|
|
"movl rax,8\n"
|
|
"shrq rax,3\n"
|
|
"cmpl rax,1\n"
|
|
"jz +14\n"
|
|
"movl rax,0\n"
|
|
"movl [rax],rax\n"
|
|
"movl rax,1\n"
|
|
"movl rcx,3\n"
|
|
"shlq rax,cl\n"
|
|
"cmpl rax,8\n"
|
|
"jz +14\n"
|
|
"movl rax,0\n"
|
|
"movl [rax],rax\n"
|
|
"movl rax,8\n"
|
|
"movl rcx,3\n"
|
|
"shrq rax,cl\n"
|
|
"cmpl rax,1\n"
|
|
"jz +14\n"
|
|
"movl rax,0\n"
|
|
"movl [rax],rax\n"
|
|
"movl rax,1\n"
|
|
"shlq rax,31\n"
|
|
"shrq rax,3\n"
|
|
"cmpq rax,0x10000000\n"
|
|
"jz +14\n"
|
|
"movl rax,0\n"
|
|
"movl [rax],rax\n"
|
|
"movl rax,1\n"
|
|
"shlq rax,31\n"
|
|
"sarl rax,3\n"
|
|
"cmpl rax,0xf0000000\n"
|
|
"jz +14\n"
|
|
"movl rax,0\n"
|
|
"movl [rax],rax\n"
|
|
"movl rax,1\n"
|
|
"movl rcx,3\n"
|
|
"shlq rax,31\n"
|
|
"sarl rax,cl\n"
|
|
"cmpl rax,0xf0000000\n"
|
|
"jz +14\n"
|
|
"movl rax,0\n"
|
|
"movl [rax],rax\n"
|
|
"movl rdx,-0x01000000\n"
|
|
"movl r8,-1\n"
|
|
"movl rcx,2\n"
|
|
"shll rdx,cl\n"
|
|
"shldl rdx,r8,2\n"
|
|
"cmpl rdx,0xf0000003\n"
|
|
"jz +7\n"
|
|
"int3\n"
|
|
"movq rdx,0xff00000000000000\n"
|
|
"movq r8,-1\n"
|
|
"movl rcx,2\n"
|
|
"shlq rdx,cl\n"
|
|
"shldq rdx,r8,2\n"
|
|
"movq tmp,0xf000000000000003\n"
|
|
"cmpq rdx,tmp\n"
|
|
"jz +7\n"
|
|
"int3\n"
|
|
"movq rdx,0xff00000000000000\n"
|
|
"movq r8,-1\n"
|
|
"movl rcx,2\n"
|
|
"shlq rdx,cl\n"
|
|
"shldq rdx,r8,cl\n"
|
|
"movq tmp,0xf000000000000003\n"
|
|
"cmpq rdx,tmp\n"
|
|
"jz +7\n"
|
|
"int3\n"
|
|
"movq rdx,0xff00000000000000\n"
|
|
"movq r8,-1\n"
|
|
"movl rcx,2\n"
|
|
"shrq rdx,cl\n"
|
|
"shrdq rdx,r8,cl\n"
|
|
"movq tmp,0xcff0000000000000\n"
|
|
"cmpq rdx,tmp\n"
|
|
"jz +7\n"
|
|
"int3\n"
|
|
"movl rax,0\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(LogicalOps64, assembler) {
|
|
Label donetest1;
|
|
__ movq(RAX, Immediate(4));
|
|
__ andq(RAX, Immediate(2));
|
|
__ cmpq(RAX, Immediate(0));
|
|
__ j(EQUAL, &donetest1);
|
|
__ int3();
|
|
__ Bind(&donetest1);
|
|
|
|
Label donetest2;
|
|
__ movq(RCX, Immediate(4));
|
|
__ pushq(RCX);
|
|
__ andq(RCX, Address(RSP, 0));
|
|
__ popq(RAX);
|
|
__ cmpq(RCX, Immediate(0));
|
|
__ j(NOT_EQUAL, &donetest2);
|
|
__ int3();
|
|
__ Bind(&donetest2);
|
|
|
|
Label donetest3;
|
|
__ movq(RAX, Immediate(0));
|
|
__ orq(RAX, Immediate(0));
|
|
__ cmpq(RAX, Immediate(0));
|
|
__ j(EQUAL, &donetest3);
|
|
__ int3();
|
|
__ Bind(&donetest3);
|
|
|
|
Label donetest4;
|
|
__ movq(RAX, Immediate(4));
|
|
__ orq(RAX, Immediate(0));
|
|
__ cmpq(RAX, Immediate(0));
|
|
__ j(NOT_EQUAL, &donetest4);
|
|
__ int3();
|
|
__ Bind(&donetest4);
|
|
|
|
Label donetest5;
|
|
__ pushq(RAX);
|
|
__ movq(RAX, Immediate(0xff));
|
|
__ movq(Address(RSP, 0), RAX);
|
|
__ cmpq(Address(RSP, 0), Immediate(0xff));
|
|
__ j(EQUAL, &donetest5);
|
|
__ int3();
|
|
__ Bind(&donetest5);
|
|
__ popq(RAX);
|
|
|
|
Label donetest6;
|
|
__ movq(RAX, Immediate(1));
|
|
__ shlq(RAX, Immediate(3));
|
|
__ cmpq(RAX, Immediate(8));
|
|
__ j(EQUAL, &donetest6);
|
|
__ int3();
|
|
__ Bind(&donetest6);
|
|
|
|
Label donetest7;
|
|
__ movq(RAX, Immediate(2));
|
|
__ shrq(RAX, Immediate(1));
|
|
__ cmpq(RAX, Immediate(1));
|
|
__ j(EQUAL, &donetest7);
|
|
__ int3();
|
|
__ Bind(&donetest7);
|
|
|
|
Label donetest8;
|
|
__ movq(RAX, Immediate(8));
|
|
__ shrq(RAX, Immediate(3));
|
|
__ cmpq(RAX, Immediate(1));
|
|
__ j(EQUAL, &donetest8);
|
|
__ int3();
|
|
__ Bind(&donetest8);
|
|
|
|
Label donetest9;
|
|
__ movq(RAX, Immediate(1));
|
|
__ movq(RCX, Immediate(3));
|
|
__ shlq(RAX, RCX);
|
|
__ cmpq(RAX, Immediate(8));
|
|
__ j(EQUAL, &donetest9);
|
|
__ int3();
|
|
__ Bind(&donetest9);
|
|
|
|
Label donetest10;
|
|
__ movq(RAX, Immediate(8));
|
|
__ movq(RCX, Immediate(3));
|
|
__ shrq(RAX, RCX);
|
|
__ cmpq(RAX, Immediate(1));
|
|
__ j(EQUAL, &donetest10);
|
|
__ int3();
|
|
__ Bind(&donetest10);
|
|
|
|
Label donetest6a;
|
|
__ movq(RAX, Immediate(1));
|
|
__ shlq(RAX, Immediate(3));
|
|
__ cmpq(RAX, Immediate(8));
|
|
__ j(EQUAL, &donetest6a);
|
|
// Be sure to skip this crashing code.
|
|
__ movq(RAX, Immediate(0));
|
|
__ movq(Address(RAX, 0), RAX);
|
|
__ Bind(&donetest6a);
|
|
|
|
Label donetest7a;
|
|
__ movq(RAX, Immediate(2));
|
|
__ shrq(RAX, Immediate(1));
|
|
__ cmpq(RAX, Immediate(1));
|
|
__ j(EQUAL, &donetest7a);
|
|
__ int3();
|
|
__ Bind(&donetest7a);
|
|
|
|
Label donetest8a;
|
|
__ movq(RAX, Immediate(8));
|
|
__ shrq(RAX, Immediate(3));
|
|
__ cmpq(RAX, Immediate(1));
|
|
__ j(EQUAL, &donetest8a);
|
|
__ int3();
|
|
__ Bind(&donetest8a);
|
|
|
|
Label donetest9a;
|
|
__ movq(RAX, Immediate(1));
|
|
__ movq(RCX, Immediate(3));
|
|
__ shlq(RAX, RCX);
|
|
__ cmpq(RAX, Immediate(8));
|
|
__ j(EQUAL, &donetest9a);
|
|
__ int3();
|
|
__ Bind(&donetest9a);
|
|
|
|
Label donetest10a;
|
|
__ movq(RAX, Immediate(8));
|
|
__ movq(RCX, Immediate(3));
|
|
__ shrq(RAX, RCX);
|
|
__ cmpq(RAX, Immediate(1));
|
|
__ j(EQUAL, &donetest10a);
|
|
__ int3();
|
|
__ Bind(&donetest10a);
|
|
|
|
Label donetest11a;
|
|
__ movq(RAX, Immediate(1));
|
|
__ shlq(RAX, Immediate(31));
|
|
__ shrq(RAX, Immediate(3));
|
|
__ cmpq(RAX, Immediate(0x10000000));
|
|
__ j(EQUAL, &donetest11a);
|
|
__ int3();
|
|
__ Bind(&donetest11a);
|
|
|
|
Label donetest12a;
|
|
__ movq(RAX, Immediate(1));
|
|
__ shlq(RAX, Immediate(63));
|
|
__ sarq(RAX, Immediate(3));
|
|
__ cmpq(RAX, Immediate(0xf000000000000000));
|
|
__ j(EQUAL, &donetest12a);
|
|
__ int3();
|
|
__ Bind(&donetest12a);
|
|
|
|
Label donetest13a;
|
|
__ movq(RAX, Immediate(1));
|
|
__ movq(RCX, Immediate(3));
|
|
__ shlq(RAX, Immediate(63));
|
|
__ sarq(RAX, RCX);
|
|
__ cmpq(RAX, Immediate(0xf000000000000000));
|
|
__ j(EQUAL, &donetest13a);
|
|
__ int3();
|
|
__ Bind(&donetest13a);
|
|
|
|
Label donetest14, donetest15;
|
|
__ pushq(R15); // Callee saved.
|
|
__ movq(R15, Immediate(0xf000000000000001));
|
|
__ andq(R15, Immediate(-1));
|
|
__ andq(R15, Immediate(0x8000000000000001));
|
|
__ orq(R15, Immediate(2));
|
|
__ orq(R15, Immediate(0xf800000000000000));
|
|
__ xorq(R15, Immediate(1));
|
|
__ xorq(R15, Immediate(0x0800000000000000));
|
|
__ cmpq(R15, Immediate(0xf000000000000002));
|
|
__ j(EQUAL, &donetest14);
|
|
__ int3();
|
|
__ Bind(&donetest14);
|
|
__ andq(R15, Immediate(2));
|
|
__ cmpq(R15, Immediate(2));
|
|
__ j(EQUAL, &donetest15);
|
|
__ int3();
|
|
__ Bind(&donetest15);
|
|
__ popq(R15); // Callee saved.
|
|
|
|
__ movq(RAX, Immediate(0));
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(LogicalOps64, test) {
|
|
typedef int (*LogicalOpsCode)();
|
|
EXPECT_EQ(0, reinterpret_cast<LogicalOpsCode>(test->entry())());
|
|
EXPECT_DISASSEMBLY(
|
|
"movl rax,4\n"
|
|
"andl rax,2\n"
|
|
"cmpq rax,0\n"
|
|
"jz +7\n"
|
|
"int3\n"
|
|
"movl rcx,4\n"
|
|
"push rcx\n"
|
|
"andq rcx,[rsp]\n"
|
|
"pop rax\n"
|
|
"cmpq rcx,0\n"
|
|
"jnz +7\n"
|
|
"int3\n"
|
|
"movl rax,0\n"
|
|
"orq rax,0\n"
|
|
"cmpq rax,0\n"
|
|
"jz +7\n"
|
|
"int3\n"
|
|
"movl rax,4\n"
|
|
"orq rax,0\n"
|
|
"cmpq rax,0\n"
|
|
"jnz +7\n"
|
|
"int3\n"
|
|
"push rax\n"
|
|
"movl rax,0xff\n"
|
|
"movq [rsp],rax\n"
|
|
"cmpq [rsp],0xff\n"
|
|
"jz +7\n"
|
|
"int3\n"
|
|
"pop rax\n"
|
|
"movl rax,1\n"
|
|
"shlq rax,3\n"
|
|
"cmpq rax,8\n"
|
|
"jz +7\n"
|
|
"int3\n"
|
|
"movl rax,2\n"
|
|
"shrq rax,1\n"
|
|
"cmpq rax,1\n"
|
|
"jz +7\n"
|
|
"int3\n"
|
|
"movl rax,8\n"
|
|
"shrq rax,3\n"
|
|
"cmpq rax,1\n"
|
|
"jz +7\n"
|
|
"int3\n"
|
|
"movl rax,1\n"
|
|
"movl rcx,3\n"
|
|
"shlq rax,cl\n"
|
|
"cmpq rax,8\n"
|
|
"jz +7\n"
|
|
"int3\n"
|
|
"movl rax,8\n"
|
|
"movl rcx,3\n"
|
|
"shrq rax,cl\n"
|
|
"cmpq rax,1\n"
|
|
"jz +7\n"
|
|
"int3\n"
|
|
"movl rax,1\n"
|
|
"shlq rax,3\n"
|
|
"cmpq rax,8\n"
|
|
"jz +14\n"
|
|
"movl rax,0\n"
|
|
"movq [rax],rax\n"
|
|
"movl rax,2\n"
|
|
"shrq rax,1\n"
|
|
"cmpq rax,1\n"
|
|
"jz +7\n"
|
|
"int3\n"
|
|
"movl rax,8\n"
|
|
"shrq rax,3\n"
|
|
"cmpq rax,1\n"
|
|
"jz +7\n"
|
|
"int3\n"
|
|
"movl rax,1\n"
|
|
"movl rcx,3\n"
|
|
"shlq rax,cl\n"
|
|
"cmpq rax,8\n"
|
|
"jz +7\n"
|
|
"int3\n"
|
|
"movl rax,8\n"
|
|
"movl rcx,3\n"
|
|
"shrq rax,cl\n"
|
|
"cmpq rax,1\n"
|
|
"jz +7\n"
|
|
"int3\n"
|
|
"movl rax,1\n"
|
|
"shlq rax,31\n"
|
|
"shrq rax,3\n"
|
|
"cmpq rax,0x10000000\n"
|
|
"jz +7\n"
|
|
"int3\n"
|
|
"movl rax,1\n"
|
|
"shlq rax,63\n"
|
|
"sarq rax,3\n"
|
|
"movq tmp,0xf000000000000000\n"
|
|
"cmpq rax,tmp\n"
|
|
"jz +7\n"
|
|
"int3\n"
|
|
"movl rax,1\n"
|
|
"movl rcx,3\n"
|
|
"shlq rax,63\n"
|
|
"sarq rax,cl\n"
|
|
"movq tmp,0xf000000000000000\n"
|
|
"cmpq rax,tmp\n"
|
|
"jz +7\n"
|
|
"int3\n"
|
|
"push pp\n"
|
|
"movq pp,0xf000000000000001\n"
|
|
"andq pp,-1\n"
|
|
"movq tmp,0x8000000000000001\n"
|
|
"andq pp,tmp\n"
|
|
"orq pp,2\n"
|
|
"movq tmp,0xf800000000000000\n"
|
|
"orq pp,tmp\n"
|
|
"xorq pp,1\n"
|
|
"movq tmp,0x0800000000000000\n"
|
|
"xorq pp,tmp\n"
|
|
"movq tmp,0xf000000000000002\n"
|
|
"cmpq pp,tmp\n"
|
|
"jz +7\n"
|
|
"int3\n"
|
|
"andl pp,2\n"
|
|
"cmpq pp,2\n"
|
|
"jz +7\n"
|
|
"int3\n"
|
|
"pop pp\n"
|
|
"movl rax,0\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(LogicalTestL, assembler) {
|
|
Label donetest1;
|
|
__ movl(RAX, Immediate(4));
|
|
__ movl(RCX, Immediate(2));
|
|
__ testl(RAX, RCX);
|
|
__ j(EQUAL, &donetest1);
|
|
// Be sure to skip this crashing code.
|
|
__ movl(RAX, Immediate(0));
|
|
__ movl(Address(RAX, 0), RAX);
|
|
__ Bind(&donetest1);
|
|
|
|
Label donetest2;
|
|
__ movl(RDX, Immediate(4));
|
|
__ movl(RCX, Immediate(4));
|
|
__ testl(RDX, RCX);
|
|
__ j(NOT_EQUAL, &donetest2);
|
|
// Be sure to skip this crashing code.
|
|
__ movl(RAX, Immediate(0));
|
|
__ movl(Address(RAX, 0), RAX);
|
|
__ Bind(&donetest2);
|
|
|
|
Label donetest3;
|
|
__ movl(RAX, Immediate(0));
|
|
__ testl(RAX, Immediate(0));
|
|
__ j(EQUAL, &donetest3);
|
|
// Be sure to skip this crashing code.
|
|
__ movl(RAX, Immediate(0));
|
|
__ movl(Address(RAX, 0), RAX);
|
|
__ Bind(&donetest3);
|
|
|
|
Label donetest4;
|
|
__ movl(RCX, Immediate(4));
|
|
__ testl(RCX, Immediate(4));
|
|
__ j(NOT_EQUAL, &donetest4);
|
|
// Be sure to skip this crashing code.
|
|
__ movl(RAX, Immediate(0));
|
|
__ movl(Address(RAX, 0), RAX);
|
|
__ Bind(&donetest4);
|
|
|
|
__ movl(RAX, Immediate(0));
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(LogicalTestL, test) {
|
|
typedef int (*LogicalTestCode)();
|
|
EXPECT_EQ(0, reinterpret_cast<LogicalTestCode>(test->entry())());
|
|
EXPECT_DISASSEMBLY(
|
|
"movl rax,4\n"
|
|
"movl rcx,2\n"
|
|
"testl rax,rcx\n"
|
|
"jz +14\n"
|
|
"movl rax,0\n"
|
|
"movl [rax],rax\n"
|
|
"movl rdx,4\n"
|
|
"movl rcx,4\n"
|
|
"testl rdx,rcx\n"
|
|
"jnz +14\n"
|
|
"movl rax,0\n"
|
|
"movl [rax],rax\n"
|
|
"movl rax,0\n"
|
|
"test al,0\n"
|
|
"jz +14\n"
|
|
"movl rax,0\n"
|
|
"movl [rax],rax\n"
|
|
"movl rcx,4\n"
|
|
"testb rcx,4\n"
|
|
"jnz +14\n"
|
|
"movl rax,0\n"
|
|
"movl [rax],rax\n"
|
|
"movl rax,0\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(LogicalTestQ, assembler) {
|
|
Label donetest1;
|
|
__ movq(RAX, Immediate(4));
|
|
__ movq(RCX, Immediate(2));
|
|
__ testq(RAX, RCX);
|
|
__ j(EQUAL, &donetest1);
|
|
// Be sure to skip this crashing code.
|
|
__ movq(RAX, Immediate(0));
|
|
__ movq(Address(RAX, 0), RAX);
|
|
__ Bind(&donetest1);
|
|
|
|
Label donetest2;
|
|
__ movq(RDX, Immediate(4));
|
|
__ movq(RCX, Immediate(4));
|
|
__ testq(RDX, RCX);
|
|
__ j(NOT_EQUAL, &donetest2);
|
|
// Be sure to skip this crashing code.
|
|
__ movq(RAX, Immediate(0));
|
|
__ movq(Address(RAX, 0), RAX);
|
|
__ Bind(&donetest2);
|
|
|
|
Label donetest3;
|
|
__ movq(RAX, Immediate(0));
|
|
__ testq(RAX, Immediate(0));
|
|
__ j(EQUAL, &donetest3);
|
|
// Be sure to skip this crashing code.
|
|
__ movq(RAX, Immediate(0));
|
|
__ movq(Address(RAX, 0), RAX);
|
|
__ Bind(&donetest3);
|
|
|
|
Label donetest4;
|
|
__ movq(RCX, Immediate(4));
|
|
__ testq(RCX, Immediate(4));
|
|
__ j(NOT_EQUAL, &donetest4);
|
|
// Be sure to skip this crashing code.
|
|
__ movq(RAX, Immediate(0));
|
|
__ movq(Address(RAX, 0), RAX);
|
|
__ Bind(&donetest4);
|
|
|
|
Label donetest5;
|
|
__ movq(RCX, Immediate(0xff));
|
|
__ testq(RCX, Immediate(0xff));
|
|
__ j(NOT_EQUAL, &donetest5);
|
|
// Be sure to skip this crashing code.
|
|
__ movq(RAX, Immediate(0));
|
|
__ movq(Address(RAX, 0), RAX);
|
|
__ Bind(&donetest5);
|
|
|
|
Label donetest6;
|
|
__ movq(RAX, Immediate(0xff));
|
|
__ testq(RAX, Immediate(0xff));
|
|
__ j(NOT_EQUAL, &donetest6);
|
|
// Be sure to skip this crashing code.
|
|
__ movq(RAX, Immediate(0));
|
|
__ movq(Address(RAX, 0), RAX);
|
|
__ Bind(&donetest6);
|
|
|
|
__ movq(RAX, Immediate(0));
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(LogicalTestQ, test) {
|
|
typedef int (*LogicalTestCode)();
|
|
EXPECT_EQ(0, reinterpret_cast<LogicalTestCode>(test->entry())());
|
|
EXPECT_DISASSEMBLY(
|
|
"movl rax,4\n"
|
|
"movl rcx,2\n"
|
|
"testq rax,rcx\n"
|
|
"jz +14\n"
|
|
"movl rax,0\n"
|
|
"movq [rax],rax\n"
|
|
"movl rdx,4\n"
|
|
"movl rcx,4\n"
|
|
"testq rdx,rcx\n"
|
|
"jnz +14\n"
|
|
"movl rax,0\n"
|
|
"movq [rax],rax\n"
|
|
"movl rax,0\n"
|
|
"test al,0\n"
|
|
"jz +14\n"
|
|
"movl rax,0\n"
|
|
"movq [rax],rax\n"
|
|
"movl rcx,4\n"
|
|
"testb rcx,4\n"
|
|
"jnz +14\n"
|
|
"movl rax,0\n"
|
|
"movq [rax],rax\n"
|
|
"movl rcx,0xff\n"
|
|
"testb rcx,0xff\n"
|
|
"jnz +14\n"
|
|
"movl rax,0\n"
|
|
"movq [rax],rax\n"
|
|
"movl rax,0xff\n"
|
|
"test al,0xff\n"
|
|
"jnz +14\n"
|
|
"movl rax,0\n"
|
|
"movq [rax],rax\n"
|
|
"movl rax,0\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(CompareSwapEQ, assembler) {
|
|
__ movq(RAX, Immediate(0));
|
|
__ pushq(RAX);
|
|
__ movq(RAX, Immediate(4));
|
|
__ movq(RCX, Immediate(0));
|
|
__ movq(Address(RSP, 0), RAX);
|
|
__ LockCmpxchgq(Address(RSP, 0), RCX);
|
|
__ popq(RAX);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(CompareSwapEQ, test) {
|
|
typedef int (*CompareSwapEQCode)();
|
|
EXPECT_EQ(0, reinterpret_cast<CompareSwapEQCode>(test->entry())());
|
|
EXPECT_DISASSEMBLY(
|
|
"movl rax,0\n"
|
|
"push rax\n"
|
|
"movl rax,4\n"
|
|
"movl rcx,0\n"
|
|
"movq [rsp],rax\n"
|
|
"lock cmpxchgq rcx,[rsp]\n"
|
|
"pop rax\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(CompareSwapNEQ, assembler) {
|
|
__ movq(RAX, Immediate(0));
|
|
__ pushq(RAX);
|
|
__ movq(RAX, Immediate(2));
|
|
__ movq(RCX, Immediate(4));
|
|
__ movq(Address(RSP, 0), RCX);
|
|
__ LockCmpxchgq(Address(RSP, 0), RCX);
|
|
__ popq(RAX);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(CompareSwapNEQ, test) {
|
|
typedef int (*CompareSwapNEQCode)();
|
|
EXPECT_EQ(4, reinterpret_cast<CompareSwapNEQCode>(test->entry())());
|
|
EXPECT_DISASSEMBLY(
|
|
"movl rax,0\n"
|
|
"push rax\n"
|
|
"movl rax,2\n"
|
|
"movl rcx,4\n"
|
|
"movq [rsp],rcx\n"
|
|
"lock cmpxchgq rcx,[rsp]\n"
|
|
"pop rax\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(CompareSwapEQ32, assembler) {
|
|
__ movq(RAX, Immediate(0x100000000));
|
|
__ pushq(RAX);
|
|
__ movq(RAX, Immediate(4));
|
|
__ movq(RCX, Immediate(0));
|
|
// 32 bit store of 4.
|
|
__ movl(Address(RSP, 0), RAX);
|
|
// Compare 32 bit memory location with RAX (4) and write 0.
|
|
__ LockCmpxchgl(Address(RSP, 0), RCX);
|
|
// Pop unchanged high word and zeroed out low word.
|
|
__ popq(RAX);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(CompareSwapEQ32, test) {
|
|
typedef intptr_t (*CompareSwapEQ32Code)();
|
|
EXPECT_EQ(0x100000000,
|
|
reinterpret_cast<CompareSwapEQ32Code>(test->entry())());
|
|
EXPECT_DISASSEMBLY(
|
|
"movq rax,0x0000000100000000\n"
|
|
"push rax\n"
|
|
"movl rax,4\n"
|
|
"movl rcx,0\n"
|
|
"movl [rsp],rax\n"
|
|
"lock cmpxchgl rcx,[rsp]\n"
|
|
"pop rax\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(CompareSwapNEQ32, assembler) {
|
|
__ movq(RAX, Immediate(0x100000000));
|
|
__ pushq(RAX);
|
|
__ movq(RAX, Immediate(2));
|
|
__ movq(RCX, Immediate(4));
|
|
__ movl(Address(RSP, 0), RCX);
|
|
__ LockCmpxchgl(Address(RSP, 0), RCX);
|
|
__ popq(RAX);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(CompareSwapNEQ32, test) {
|
|
typedef intptr_t (*CompareSwapNEQ32Code)();
|
|
EXPECT_EQ(0x100000004l,
|
|
reinterpret_cast<CompareSwapNEQ32Code>(test->entry())());
|
|
EXPECT_DISASSEMBLY(
|
|
"movq rax,0x0000000100000000\n"
|
|
"push rax\n"
|
|
"movl rax,2\n"
|
|
"movl rcx,4\n"
|
|
"movl [rsp],rcx\n"
|
|
"lock cmpxchgl rcx,[rsp]\n"
|
|
"pop rax\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(Exchange, assembler) {
|
|
__ movq(RAX, Immediate(kLargeConstant));
|
|
__ movq(RDX, Immediate(kAnotherLargeConstant));
|
|
__ xchgq(RAX, RDX);
|
|
__ subq(RAX, RDX);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(Exchange, test) {
|
|
typedef int64_t (*Exchange)();
|
|
EXPECT_EQ(kAnotherLargeConstant - kLargeConstant,
|
|
reinterpret_cast<Exchange>(test->entry())());
|
|
EXPECT_DISASSEMBLY(
|
|
"movq rax,0x1234567887654321\n"
|
|
"movq rdx,0x0db4da5f7ef412b1\n"
|
|
"xchgq rax,rdx\n"
|
|
"subq rax,rdx\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(LargeConstant, assembler) {
|
|
__ movq(RAX, Immediate(kLargeConstant));
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(LargeConstant, test) {
|
|
typedef int64_t (*LargeConstantCode)();
|
|
EXPECT_EQ(kLargeConstant,
|
|
reinterpret_cast<LargeConstantCode>(test->entry())());
|
|
EXPECT_DISASSEMBLY(
|
|
"movq rax,0x1234567887654321\n"
|
|
"ret\n");
|
|
}
|
|
|
|
static int ComputeStackSpaceReservation(int needed, int fixed) {
|
|
return (OS::ActivationFrameAlignment() > 1)
|
|
? Utils::RoundUp(needed + fixed, OS::ActivationFrameAlignment()) -
|
|
fixed
|
|
: needed;
|
|
}
|
|
|
|
static int LeafReturn42() {
|
|
return 42;
|
|
}
|
|
|
|
static int LeafReturnArgument(int x) {
|
|
return x + 87;
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(CallSimpleLeaf, assembler) {
|
|
ExternalLabel call1(reinterpret_cast<uword>(LeafReturn42));
|
|
ExternalLabel call2(reinterpret_cast<uword>(LeafReturnArgument));
|
|
int space = ComputeStackSpaceReservation(0, 8);
|
|
__ subq(RSP, Immediate(space));
|
|
__ call(&call1);
|
|
__ addq(RSP, Immediate(space));
|
|
space = ComputeStackSpaceReservation(0, 8);
|
|
__ subq(RSP, Immediate(space));
|
|
__ movl(CallingConventions::kArg1Reg, RAX);
|
|
__ call(&call2);
|
|
__ addq(RSP, Immediate(space));
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(CallSimpleLeaf, test) {
|
|
typedef int (*CallSimpleLeafCode)();
|
|
EXPECT_EQ(42 + 87, reinterpret_cast<CallSimpleLeafCode>(test->entry())());
|
|
EXPECT_DISASSEMBLY_ENDS_WITH(
|
|
"call tmp\n"
|
|
"addq rsp,8\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(JumpSimpleLeaf, assembler) {
|
|
ExternalLabel call1(reinterpret_cast<uword>(LeafReturn42));
|
|
Label L;
|
|
int space = ComputeStackSpaceReservation(0, 8);
|
|
__ subq(RSP, Immediate(space));
|
|
__ call(&L);
|
|
__ addq(RSP, Immediate(space));
|
|
__ ret();
|
|
__ Bind(&L);
|
|
__ jmp(&call1);
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(JumpSimpleLeaf, test) {
|
|
typedef int (*JumpSimpleLeafCode)();
|
|
EXPECT_EQ(42, reinterpret_cast<JumpSimpleLeafCode>(test->entry())());
|
|
EXPECT_DISASSEMBLY_ENDS_WITH("jmp tmp\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(JumpIndirect, assembler) {
|
|
ExternalLabel call1(reinterpret_cast<uword>(LeafReturn42));
|
|
__ movq(Address(CallingConventions::kArg1Reg, 0), Immediate(call1.address()));
|
|
__ jmp(Address(CallingConventions::kArg1Reg, 0));
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(JumpIndirect, test) {
|
|
uword temp = 0;
|
|
typedef int (*JumpIndirect)(uword*);
|
|
EXPECT_EQ(42, reinterpret_cast<JumpIndirect>(test->entry())(&temp));
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(SingleFPMoves, assembler) {
|
|
__ movq(RAX, Immediate(bit_cast<int32_t, float>(234.0f)));
|
|
__ movd(XMM0, RAX);
|
|
__ movss(XMM1, XMM0);
|
|
__ movss(XMM2, XMM1);
|
|
__ movss(XMM3, XMM2);
|
|
__ movss(XMM4, XMM3);
|
|
__ movss(XMM5, XMM4);
|
|
__ movss(XMM6, XMM5);
|
|
__ movss(XMM7, XMM6);
|
|
__ movss(XMM8, XMM7);
|
|
__ movss(XMM9, XMM8);
|
|
__ movss(XMM10, XMM9);
|
|
__ movss(XMM11, XMM10);
|
|
__ movss(XMM12, XMM11);
|
|
__ movss(XMM13, XMM12);
|
|
__ movss(XMM14, XMM13);
|
|
__ movss(XMM15, XMM14);
|
|
__ pushq(R15); // Callee saved.
|
|
__ pushq(RAX);
|
|
__ movq(Address(RSP, 0), Immediate(0));
|
|
__ movss(XMM0, Address(RSP, 0));
|
|
__ movss(Address(RSP, 0), XMM7);
|
|
__ movss(XMM1, Address(RSP, 0));
|
|
__ movq(R10, RSP);
|
|
__ movss(Address(R10, 0), XMM1);
|
|
__ movss(XMM2, Address(R10, 0));
|
|
__ movq(R15, RSP);
|
|
__ movss(Address(R15, 0), XMM2);
|
|
__ movss(XMM3, Address(R15, 0));
|
|
__ movq(RAX, RSP);
|
|
__ movss(Address(RAX, 0), XMM3);
|
|
__ movss(XMM1, Address(RAX, 0));
|
|
__ movss(XMM15, Address(RAX, 0));
|
|
__ movss(XMM14, XMM15);
|
|
__ movss(XMM13, XMM14);
|
|
__ movss(XMM12, XMM13);
|
|
__ movss(XMM11, XMM12);
|
|
__ movss(XMM10, XMM11);
|
|
__ movss(XMM9, XMM10);
|
|
__ movss(XMM8, XMM9);
|
|
__ movss(XMM7, XMM8);
|
|
__ movss(XMM6, XMM7);
|
|
__ movss(XMM5, XMM6);
|
|
__ movss(XMM4, XMM5);
|
|
__ movss(XMM3, XMM4);
|
|
__ movss(XMM2, XMM3);
|
|
__ movss(XMM1, XMM2);
|
|
__ movss(XMM0, XMM1);
|
|
__ popq(RAX);
|
|
__ popq(R15); // Callee saved.
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(SingleFPMoves, test) {
|
|
typedef float (*SingleFPMovesCode)();
|
|
EXPECT_EQ(234, reinterpret_cast<SingleFPMovesCode>(test->entry())());
|
|
EXPECT_DISASSEMBLY(
|
|
"movl rax,0x436a0000\n"
|
|
"movd xmm0,rax\n"
|
|
"movss rcx,xmm0\n"
|
|
"movss rdx,xmm1\n"
|
|
"movss rbx,xmm2\n"
|
|
"movss rsp,xmm3\n"
|
|
"movss rbp,xmm4\n"
|
|
"movss rsi,xmm5\n"
|
|
"movss rdi,xmm6\n"
|
|
"movss r8,xmm7\n"
|
|
"movss r9,xmm8\n"
|
|
"movss r10,xmm9\n"
|
|
"movss tmp,xmm10\n"
|
|
"movss r12,xmm11\n"
|
|
"movss r13,xmm12\n"
|
|
"movss thr,xmm13\n"
|
|
"movss pp,xmm14\n"
|
|
"push pp\n"
|
|
"push rax\n"
|
|
"movq [rsp],0\n"
|
|
"movss xmm0,[rsp]\n"
|
|
"movss [rsp],xmm7\n"
|
|
"movss xmm1,[rsp]\n"
|
|
"movq r10,rsp\n"
|
|
"movss [r10],xmm1\n"
|
|
"movss xmm2,[r10]\n"
|
|
"movq pp,rsp\n"
|
|
"movss [pp],xmm2\n"
|
|
"movss xmm3,[pp]\n"
|
|
"movq rax,rsp\n"
|
|
"movss [rax],xmm3\n"
|
|
"movss xmm1,[rax]\n"
|
|
"movss xmm15,[rax]\n"
|
|
"movss thr,xmm15\n"
|
|
"movss r13,xmm14\n"
|
|
"movss r12,xmm13\n"
|
|
"movss tmp,xmm12\n"
|
|
"movss r10,xmm11\n"
|
|
"movss r9,xmm10\n"
|
|
"movss r8,xmm9\n"
|
|
"movss rdi,xmm8\n"
|
|
"movss rsi,xmm7\n"
|
|
"movss rbp,xmm6\n"
|
|
"movss rsp,xmm5\n"
|
|
"movss rbx,xmm4\n"
|
|
"movss rdx,xmm3\n"
|
|
"movss rcx,xmm2\n"
|
|
"movss rax,xmm1\n"
|
|
"pop rax\n"
|
|
"pop pp\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(SingleFPMoves2, assembler) {
|
|
__ movq(RAX, Immediate(bit_cast<int32_t, float>(234.0f)));
|
|
__ movd(XMM0, RAX);
|
|
__ movd(XMM8, RAX);
|
|
__ movss(XMM1, XMM8);
|
|
__ pushq(RAX);
|
|
__ movq(Address(RSP, 0), Immediate(0));
|
|
__ movss(XMM0, Address(RSP, 0));
|
|
__ movss(Address(RSP, 0), XMM1);
|
|
__ movss(XMM0, Address(RSP, 0));
|
|
__ movq(Address(RSP, 0), Immediate(0));
|
|
__ movss(XMM9, XMM8);
|
|
__ movss(Address(RSP, 0), XMM9);
|
|
__ movss(XMM8, Address(RSP, 0));
|
|
__ movss(XMM0, XMM8);
|
|
__ popq(RAX);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(SingleFPMoves2, test) {
|
|
typedef float (*SingleFPMoves2Code)();
|
|
EXPECT_EQ(234, reinterpret_cast<SingleFPMoves2Code>(test->entry())());
|
|
EXPECT_DISASSEMBLY(
|
|
"movl rax,0x436a0000\n"
|
|
"movd xmm0,rax\n"
|
|
"movd xmm8,rax\n"
|
|
"movss rcx,xmm8\n"
|
|
"push rax\n"
|
|
"movq [rsp],0\n"
|
|
"movss xmm0,[rsp]\n"
|
|
"movss [rsp],xmm1\n"
|
|
"movss xmm0,[rsp]\n"
|
|
"movq [rsp],0\n"
|
|
"movss r9,xmm8\n"
|
|
"movss [rsp],xmm9\n"
|
|
"movss xmm8,[rsp]\n"
|
|
"movss rax,xmm8\n"
|
|
"pop rax\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(MovqXmmToCpu, assembler) {
|
|
__ movq(RAX, Immediate(bit_cast<int32_t, float>(234.5f)));
|
|
__ movd(XMM0, RAX);
|
|
__ cvtss2sd(XMM0, XMM0);
|
|
__ movq(RAX, XMM0);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(MovqXmmToCpu, test) {
|
|
typedef uint64_t (*MovqXmmToCpuCode)();
|
|
EXPECT_EQ((bit_cast<uint64_t, double>(234.5f)),
|
|
reinterpret_cast<MovqXmmToCpuCode>(test->entry())());
|
|
EXPECT_DISASSEMBLY(
|
|
"movl rax,0x436a8000\n"
|
|
"movd xmm0,rax\n"
|
|
"cvtss2sd xmm0,xmm0\n"
|
|
"movq rax,xmm0\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(PackedDoubleAdd, assembler) {
|
|
static const struct ALIGN16 {
|
|
double a;
|
|
double b;
|
|
} constant0 = {1.0, 2.0};
|
|
static const struct ALIGN16 {
|
|
double a;
|
|
double b;
|
|
} constant1 = {3.0, 4.0};
|
|
__ movq(RAX, Immediate(reinterpret_cast<uword>(&constant0)));
|
|
__ movups(XMM10, Address(RAX, 0));
|
|
__ movq(RAX, Immediate(reinterpret_cast<uword>(&constant1)));
|
|
__ movups(XMM11, Address(RAX, 0));
|
|
__ addpd(XMM10, XMM11);
|
|
__ movaps(XMM0, XMM10);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(PackedDoubleAdd, test) {
|
|
typedef double (*PackedDoubleAdd)();
|
|
double res = reinterpret_cast<PackedDoubleAdd>(test->entry())();
|
|
EXPECT_FLOAT_EQ(4.0, res, 0.000001f);
|
|
EXPECT_DISASSEMBLY_ENDS_WITH(
|
|
"movups xmm11,[rax]\n"
|
|
"addpd xmm10,xmm11\n"
|
|
"movaps xmm0,xmm10\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(PackedDoubleSub, assembler) {
|
|
static const struct ALIGN16 {
|
|
double a;
|
|
double b;
|
|
} constant0 = {1.0, 2.0};
|
|
static const struct ALIGN16 {
|
|
double a;
|
|
double b;
|
|
} constant1 = {3.0, 4.0};
|
|
__ movq(RAX, Immediate(reinterpret_cast<uword>(&constant0)));
|
|
__ movups(XMM10, Address(RAX, 0));
|
|
__ movq(RAX, Immediate(reinterpret_cast<uword>(&constant1)));
|
|
__ movups(XMM11, Address(RAX, 0));
|
|
__ subpd(XMM10, XMM11);
|
|
__ movaps(XMM0, XMM10);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(PackedDoubleSub, test) {
|
|
typedef double (*PackedDoubleSub)();
|
|
double res = reinterpret_cast<PackedDoubleSub>(test->entry())();
|
|
EXPECT_FLOAT_EQ(-2.0, res, 0.000001f);
|
|
EXPECT_DISASSEMBLY_ENDS_WITH(
|
|
"movups xmm11,[rax]\n"
|
|
"subpd xmm10,xmm11\n"
|
|
"movaps xmm0,xmm10\n"
|
|
"ret\n");
|
|
}
|
|
|
|
void EnterTestFrame(Assembler* assembler) {
|
|
COMPILE_ASSERT(THR != CallingConventions::kArg1Reg);
|
|
COMPILE_ASSERT(CODE_REG != CallingConventions::kArg2Reg);
|
|
__ EnterFrame(0);
|
|
__ pushq(CODE_REG);
|
|
__ pushq(PP);
|
|
__ pushq(THR);
|
|
__ movq(CODE_REG, CallingConventions::kArg1Reg);
|
|
__ movq(THR, CallingConventions::kArg2Reg);
|
|
__ LoadPoolPointer(PP);
|
|
}
|
|
|
|
void LeaveTestFrame(Assembler* assembler) {
|
|
__ popq(THR);
|
|
__ popq(PP);
|
|
__ popq(CODE_REG);
|
|
__ LeaveFrame();
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(PackedDoubleNegate, assembler) {
|
|
static const struct ALIGN16 {
|
|
double a;
|
|
double b;
|
|
} constant0 = {1.0, 2.0};
|
|
EnterTestFrame(assembler);
|
|
__ movq(RAX, Immediate(reinterpret_cast<uword>(&constant0)));
|
|
__ movups(XMM10, Address(RAX, 0));
|
|
__ negatepd(XMM10, XMM10);
|
|
__ movaps(XMM0, XMM10);
|
|
LeaveTestFrame(assembler);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(PackedDoubleNegate, test) {
|
|
double res = test->InvokeWithCodeAndThread<double>();
|
|
EXPECT_FLOAT_EQ(-1.0, res, 0.000001f);
|
|
EXPECT_DISASSEMBLY_NOT_WINDOWS_ENDS_WITH(
|
|
"movups xmm10,[rax]\n"
|
|
"movq tmp,[thr+0x...]\n"
|
|
"xorpd xmm10,[tmp]\n"
|
|
"movaps xmm0,xmm10\n"
|
|
"pop thr\n"
|
|
"pop pp\n"
|
|
"pop r12\n"
|
|
"movq rsp,rbp\n"
|
|
"pop rbp\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(PackedDoubleAbsolute, assembler) {
|
|
static const struct ALIGN16 {
|
|
double a;
|
|
double b;
|
|
} constant0 = {-1.0, 2.0};
|
|
EnterTestFrame(assembler);
|
|
__ movq(RAX, Immediate(reinterpret_cast<uword>(&constant0)));
|
|
__ movups(XMM10, Address(RAX, 0));
|
|
__ abspd(XMM0, XMM10);
|
|
LeaveTestFrame(assembler);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(PackedDoubleAbsolute, test) {
|
|
double res = test->InvokeWithCodeAndThread<double>();
|
|
EXPECT_FLOAT_EQ(1.0, res, 0.000001f);
|
|
EXPECT_DISASSEMBLY_NOT_WINDOWS_ENDS_WITH(
|
|
"movups xmm10,[rax]\n"
|
|
"movq tmp,[thr+0x...]\n"
|
|
"movups xmm0,[tmp]\n"
|
|
"andpd xmm0,xmm10\n"
|
|
"pop thr\n"
|
|
"pop pp\n"
|
|
"pop r12\n"
|
|
"movq rsp,rbp\n"
|
|
"pop rbp\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(PackedDoubleMul, assembler) {
|
|
static const struct ALIGN16 {
|
|
double a;
|
|
double b;
|
|
} constant0 = {3.0, 2.0};
|
|
static const struct ALIGN16 {
|
|
double a;
|
|
double b;
|
|
} constant1 = {3.0, 4.0};
|
|
__ movq(RAX, Immediate(reinterpret_cast<uword>(&constant0)));
|
|
__ movups(XMM10, Address(RAX, 0));
|
|
__ movq(RAX, Immediate(reinterpret_cast<uword>(&constant1)));
|
|
__ movups(XMM11, Address(RAX, 0));
|
|
__ mulpd(XMM10, XMM11);
|
|
__ movaps(XMM0, XMM10);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(PackedDoubleMul, test) {
|
|
typedef double (*PackedDoubleMul)();
|
|
double res = reinterpret_cast<PackedDoubleMul>(test->entry())();
|
|
EXPECT_FLOAT_EQ(9.0, res, 0.000001f);
|
|
EXPECT_DISASSEMBLY_ENDS_WITH(
|
|
"movups xmm11,[rax]\n"
|
|
"mulpd xmm10,xmm11\n"
|
|
"movaps xmm0,xmm10\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(PackedDoubleDiv, assembler) {
|
|
static const struct ALIGN16 {
|
|
double a;
|
|
double b;
|
|
} constant0 = {9.0, 2.0};
|
|
static const struct ALIGN16 {
|
|
double a;
|
|
double b;
|
|
} constant1 = {3.0, 4.0};
|
|
__ movq(RAX, Immediate(reinterpret_cast<uword>(&constant0)));
|
|
__ movups(XMM10, Address(RAX, 0));
|
|
__ movq(RAX, Immediate(reinterpret_cast<uword>(&constant1)));
|
|
__ movups(XMM11, Address(RAX, 0));
|
|
__ divpd(XMM10, XMM11);
|
|
__ movaps(XMM0, XMM10);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(PackedDoubleDiv, test) {
|
|
typedef double (*PackedDoubleDiv)();
|
|
double res = reinterpret_cast<PackedDoubleDiv>(test->entry())();
|
|
EXPECT_FLOAT_EQ(3.0, res, 0.000001f);
|
|
EXPECT_DISASSEMBLY_ENDS_WITH(
|
|
"movups xmm11,[rax]\n"
|
|
"divpd xmm10,xmm11\n"
|
|
"movaps xmm0,xmm10\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(PackedDoubleSqrt, assembler) {
|
|
static const struct ALIGN16 {
|
|
double a;
|
|
double b;
|
|
} constant0 = {16.0, 2.0};
|
|
__ movq(RAX, Immediate(reinterpret_cast<uword>(&constant0)));
|
|
__ movups(XMM10, Address(RAX, 0));
|
|
__ sqrtpd(XMM10, XMM10);
|
|
__ movaps(XMM0, XMM10);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(PackedDoubleSqrt, test) {
|
|
typedef double (*PackedDoubleSqrt)();
|
|
double res = reinterpret_cast<PackedDoubleSqrt>(test->entry())();
|
|
EXPECT_FLOAT_EQ(4.0, res, 0.000001f);
|
|
EXPECT_DISASSEMBLY_ENDS_WITH(
|
|
"movups xmm10,[rax]\n"
|
|
"sqrtpd xmm10,xmm10\n"
|
|
"movaps xmm0,xmm10\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(PackedDoubleMin, assembler) {
|
|
static const struct ALIGN16 {
|
|
double a;
|
|
double b;
|
|
} constant0 = {9.0, 2.0};
|
|
static const struct ALIGN16 {
|
|
double a;
|
|
double b;
|
|
} constant1 = {3.0, 4.0};
|
|
__ movq(RAX, Immediate(reinterpret_cast<uword>(&constant0)));
|
|
__ movups(XMM10, Address(RAX, 0));
|
|
__ movq(RAX, Immediate(reinterpret_cast<uword>(&constant1)));
|
|
__ movups(XMM11, Address(RAX, 0));
|
|
__ minpd(XMM10, XMM11);
|
|
__ movaps(XMM0, XMM10);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(PackedDoubleMin, test) {
|
|
typedef double (*PackedDoubleMin)();
|
|
double res = reinterpret_cast<PackedDoubleMin>(test->entry())();
|
|
EXPECT_FLOAT_EQ(3.0, res, 0.000001f);
|
|
EXPECT_DISASSEMBLY_ENDS_WITH(
|
|
"movups xmm11,[rax]\n"
|
|
"minpd xmm10,xmm11\n"
|
|
"movaps xmm0,xmm10\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(PackedDoubleMax, assembler) {
|
|
static const struct ALIGN16 {
|
|
double a;
|
|
double b;
|
|
} constant0 = {9.0, 2.0};
|
|
static const struct ALIGN16 {
|
|
double a;
|
|
double b;
|
|
} constant1 = {3.0, 4.0};
|
|
__ movq(RAX, Immediate(reinterpret_cast<uword>(&constant0)));
|
|
__ movups(XMM10, Address(RAX, 0));
|
|
__ movq(RAX, Immediate(reinterpret_cast<uword>(&constant1)));
|
|
__ movups(XMM11, Address(RAX, 0));
|
|
__ maxpd(XMM10, XMM11);
|
|
__ movaps(XMM0, XMM10);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(PackedDoubleMax, test) {
|
|
typedef double (*PackedDoubleMax)();
|
|
double res = reinterpret_cast<PackedDoubleMax>(test->entry())();
|
|
EXPECT_FLOAT_EQ(9.0, res, 0.000001f);
|
|
EXPECT_DISASSEMBLY_ENDS_WITH(
|
|
"movups xmm11,[rax]\n"
|
|
"maxpd xmm10,xmm11\n"
|
|
"movaps xmm0,xmm10\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(PackedDoubleShuffle, assembler) {
|
|
static const struct ALIGN16 {
|
|
double a;
|
|
double b;
|
|
} constant0 = {2.0, 9.0};
|
|
__ movq(RAX, Immediate(reinterpret_cast<uword>(&constant0)));
|
|
__ movups(XMM10, Address(RAX, 0));
|
|
// Splat Y across all lanes.
|
|
__ shufpd(XMM10, XMM10, Immediate(0x33));
|
|
// Splat X across all lanes.
|
|
__ shufpd(XMM10, XMM10, Immediate(0x0));
|
|
// Set return value.
|
|
__ movaps(XMM0, XMM10);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(PackedDoubleShuffle, test) {
|
|
typedef double (*PackedDoubleShuffle)();
|
|
double res = reinterpret_cast<PackedDoubleShuffle>(test->entry())();
|
|
EXPECT_FLOAT_EQ(9.0, res, 0.000001f);
|
|
EXPECT_DISASSEMBLY_ENDS_WITH(
|
|
"movups xmm10,[rax]\n"
|
|
"shufpd xmm10, xmm10 [33]\n"
|
|
"shufpd xmm10, xmm10 [0]\n"
|
|
"movaps xmm0,xmm10\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(PackedDoubleToSingle, assembler) {
|
|
static const struct ALIGN16 {
|
|
double a;
|
|
double b;
|
|
} constant0 = {9.0, 2.0};
|
|
__ movq(RAX, Immediate(reinterpret_cast<uword>(&constant0)));
|
|
__ movups(XMM11, Address(RAX, 0));
|
|
__ cvtpd2ps(XMM10, XMM11);
|
|
__ movaps(XMM0, XMM10);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(PackedDoubleToSingle, test) {
|
|
typedef float (*PackedDoubleToSingle)();
|
|
float res = reinterpret_cast<PackedDoubleToSingle>(test->entry())();
|
|
EXPECT_FLOAT_EQ(9.0f, res, 0.000001f);
|
|
EXPECT_DISASSEMBLY_ENDS_WITH(
|
|
"movups xmm11,[rax]\n"
|
|
"cvtpd2ps xmm10,xmm11\n"
|
|
"movaps xmm0,xmm10\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(PackedSingleToDouble, assembler) {
|
|
static const struct ALIGN16 {
|
|
float a;
|
|
float b;
|
|
float c;
|
|
float d;
|
|
} constant0 = {9.0f, 2.0f, 3.0f, 4.0f};
|
|
__ movq(RAX, Immediate(reinterpret_cast<uword>(&constant0)));
|
|
__ movups(XMM11, Address(RAX, 0));
|
|
__ cvtps2pd(XMM10, XMM11);
|
|
__ movaps(XMM0, XMM10);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(PackedSingleToDouble, test) {
|
|
typedef double (*PackedSingleToDouble)();
|
|
double res = reinterpret_cast<PackedSingleToDouble>(test->entry())();
|
|
EXPECT_FLOAT_EQ(9.0f, res, 0.000001f);
|
|
EXPECT_DISASSEMBLY_ENDS_WITH(
|
|
"movups xmm11,[rax]\n"
|
|
"cvtps2pd xmm10,xmm11\n"
|
|
"movaps xmm0,xmm10\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(SingleFPOperations, assembler) {
|
|
__ pushq(RBX);
|
|
__ pushq(RCX);
|
|
__ movq(RBX, Immediate(bit_cast<int32_t, float>(12.3f)));
|
|
__ movd(XMM0, RBX);
|
|
__ movd(XMM8, RBX);
|
|
__ movq(RCX, Immediate(bit_cast<int32_t, float>(3.4f)));
|
|
__ movd(XMM1, RCX);
|
|
__ movd(XMM9, RCX);
|
|
__ addss(XMM0, XMM1); // 15.7f
|
|
__ mulss(XMM0, XMM1); // 53.38f
|
|
__ subss(XMM0, XMM1); // 49.98f
|
|
__ divss(XMM0, XMM1); // 14.7f
|
|
__ addss(XMM8, XMM9); // 15.7f
|
|
__ mulss(XMM8, XMM9); // 53.38f
|
|
__ subss(XMM8, XMM9); // 49.98f
|
|
__ divss(XMM8, XMM9); // 14.7f
|
|
__ subss(XMM0, XMM8); // 0.0f
|
|
__ popq(RCX);
|
|
__ popq(RBX);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(SingleFPOperations, test) {
|
|
typedef float (*SingleFPOperationsCode)();
|
|
float res = reinterpret_cast<SingleFPOperationsCode>(test->entry())();
|
|
EXPECT_FLOAT_EQ(0.0f, res, 0.001f);
|
|
EXPECT_DISASSEMBLY(
|
|
"push rbx\n"
|
|
"push rcx\n"
|
|
"movl rbx,0x4144cccd\n"
|
|
"movd xmm0,rbx\n"
|
|
"movd xmm8,rbx\n"
|
|
"movl rcx,0x4059999a\n"
|
|
"movd xmm1,rcx\n"
|
|
"movd xmm9,rcx\n"
|
|
"addss xmm0,xmm1\n"
|
|
"mulss xmm0,xmm1\n"
|
|
"subss xmm0,xmm1\n"
|
|
"divss xmm0,xmm1\n"
|
|
"addss xmm8,xmm9\n"
|
|
"mulss xmm8,xmm9\n"
|
|
"subss xmm8,xmm9\n"
|
|
"divss xmm8,xmm9\n"
|
|
"subss xmm0,xmm8\n"
|
|
"pop rcx\n"
|
|
"pop rbx\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(PackedFPOperations, assembler) {
|
|
__ movq(RAX, Immediate(bit_cast<int32_t, float>(12.3f)));
|
|
__ movd(XMM10, RAX);
|
|
__ shufps(XMM10, XMM10, Immediate(0x0));
|
|
__ movq(RAX, Immediate(bit_cast<int32_t, float>(3.4f)));
|
|
__ movd(XMM9, RAX);
|
|
__ shufps(XMM9, XMM9, Immediate(0x0));
|
|
__ addps(XMM10, XMM9); // 15.7f
|
|
__ mulps(XMM10, XMM9); // 53.38f
|
|
__ subps(XMM10, XMM9); // 49.98f
|
|
__ divps(XMM10, XMM9); // 14.7f
|
|
__ movaps(XMM0, XMM10);
|
|
__ shufps(XMM0, XMM0, Immediate(0x55)); // Copy second lane into all 4 lanes.
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(PackedFPOperations, test) {
|
|
typedef float (*PackedFPOperationsCode)();
|
|
float res = reinterpret_cast<PackedFPOperationsCode>(test->entry())();
|
|
EXPECT_FLOAT_EQ(14.7f, res, 0.001f);
|
|
EXPECT_DISASSEMBLY(
|
|
"movl rax,0x4144cccd\n"
|
|
"movd xmm10,rax\n"
|
|
"shufps xmm10,xmm10 [0]\n"
|
|
"movl rax,0x4059999a\n"
|
|
"movd xmm9,rax\n"
|
|
"shufps xmm9,xmm9 [0]\n"
|
|
"addps xmm10,xmm9\n"
|
|
"mulps xmm10,xmm9\n"
|
|
"subps xmm10,xmm9\n"
|
|
"divps xmm10,xmm9\n"
|
|
"movaps xmm0,xmm10\n"
|
|
"shufps xmm0,xmm0 [55]\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(PackedIntOperations, assembler) {
|
|
__ movl(RAX, Immediate(0x2));
|
|
__ movd(XMM0, RAX);
|
|
__ shufps(XMM0, XMM0, Immediate(0x0));
|
|
__ movl(RAX, Immediate(0x1));
|
|
__ movd(XMM1, RAX);
|
|
__ shufps(XMM1, XMM1, Immediate(0x0));
|
|
__ addpl(XMM0, XMM1); // 0x3
|
|
__ addpl(XMM0, XMM0); // 0x6
|
|
__ subpl(XMM0, XMM1); // 0x5
|
|
__ pushq(RAX);
|
|
__ movss(Address(RSP, 0), XMM0);
|
|
__ popq(RAX);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(PackedIntOperations, test) {
|
|
typedef uint32_t (*PackedIntOperationsCode)();
|
|
uint32_t res = reinterpret_cast<PackedIntOperationsCode>(test->entry())();
|
|
EXPECT_EQ(static_cast<uword>(0x5), res);
|
|
EXPECT_DISASSEMBLY(
|
|
"movl rax,2\n"
|
|
"movd xmm0,rax\n"
|
|
"shufps xmm0,xmm0 [0]\n"
|
|
"movl rax,1\n"
|
|
"movd xmm1,rax\n"
|
|
"shufps xmm1,xmm1 [0]\n"
|
|
"paddd xmm0,xmm1\n"
|
|
"paddd xmm0,xmm0\n"
|
|
"psubd xmm0,xmm1\n"
|
|
"push rax\n"
|
|
"movss [rsp],xmm0\n"
|
|
"pop rax\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(PackedIntOperations2, assembler) {
|
|
// Note: on Windows 64 XMM6-XMM15 are callee save.
|
|
const intptr_t cpu_register_set = 0;
|
|
const intptr_t fpu_register_set =
|
|
((1 << XMM10) | (1 << XMM11)) & CallingConventions::kVolatileXmmRegisters;
|
|
const RegisterSet register_set(cpu_register_set, fpu_register_set);
|
|
__ PushRegisters(register_set);
|
|
__ movl(RAX, Immediate(0x2));
|
|
__ movd(XMM10, RAX);
|
|
__ shufps(XMM10, XMM10, Immediate(0x0));
|
|
__ movl(RAX, Immediate(0x1));
|
|
__ movd(XMM11, RAX);
|
|
__ shufps(XMM11, XMM11, Immediate(0x0));
|
|
__ addpl(XMM10, XMM11); // 0x3
|
|
__ addpl(XMM10, XMM10); // 0x6
|
|
__ subpl(XMM10, XMM11); // 0x5
|
|
__ pushq(RAX);
|
|
__ movss(Address(RSP, 0), XMM10);
|
|
__ popq(RAX);
|
|
__ PopRegisters(register_set);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(PackedIntOperations2, test) {
|
|
typedef uint32_t (*PackedIntOperationsCode)();
|
|
uint32_t res = reinterpret_cast<PackedIntOperationsCode>(test->entry())();
|
|
EXPECT_EQ(static_cast<uword>(0x5), res);
|
|
EXPECT_DISASSEMBLY_NOT_WINDOWS(
|
|
"subq rsp,0x20\n"
|
|
"movups [rsp],xmm10\n"
|
|
"movups [rsp+0x10],xmm11\n"
|
|
"movl rax,2\n"
|
|
"movd xmm10,rax\n"
|
|
"shufps xmm10,xmm10 [0]\n"
|
|
"movl rax,1\n"
|
|
"movd xmm11,rax\n"
|
|
"shufps xmm11,xmm11 [0]\n"
|
|
"paddd xmm10,xmm11\n"
|
|
"paddd xmm10,xmm10\n"
|
|
"psubd xmm10,xmm11\n"
|
|
"push rax\n"
|
|
"movss [rsp],xmm10\n"
|
|
"pop rax\n"
|
|
"movups xmm10,[rsp]\n"
|
|
"movups xmm11,[rsp+0x10]\n"
|
|
"addq rsp,0x20\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(PackedFPOperations2, assembler) {
|
|
__ movq(RAX, Immediate(bit_cast<int32_t, float>(4.0f)));
|
|
__ movd(XMM0, RAX);
|
|
__ shufps(XMM0, XMM0, Immediate(0x0));
|
|
|
|
__ movaps(XMM11, XMM0); // Copy XMM0
|
|
__ rcpps(XMM11, XMM11); // 0.25
|
|
__ sqrtps(XMM11, XMM11); // 0.5
|
|
__ rsqrtps(XMM0, XMM0); // ~0.5
|
|
__ subps(XMM0, XMM11); // ~0.0
|
|
__ shufps(XMM0, XMM0, Immediate(0x00)); // Copy second lane into all 4 lanes.
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(PackedFPOperations2, test) {
|
|
typedef float (*PackedFPOperations2Code)();
|
|
float res = reinterpret_cast<PackedFPOperations2Code>(test->entry())();
|
|
EXPECT_FLOAT_EQ(0.0f, res, 0.001f);
|
|
EXPECT_DISASSEMBLY(
|
|
"movl rax,0x40800000\n"
|
|
"movd xmm0,rax\n"
|
|
"shufps xmm0,xmm0 [0]\n"
|
|
"movaps xmm11,xmm0\n"
|
|
"rcpps xmm11,xmm11\n"
|
|
"sqrtps xmm11,xmm11\n"
|
|
"rsqrtps xmm0,xmm0\n"
|
|
"subps xmm0,xmm11\n"
|
|
"shufps xmm0,xmm0 [0]\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(PackedCompareEQ, assembler) {
|
|
__ set1ps(XMM0, RAX, Immediate(bit_cast<int32_t, float>(2.0f)));
|
|
__ set1ps(XMM1, RAX, Immediate(bit_cast<int32_t, float>(4.0f)));
|
|
__ cmppseq(XMM0, XMM1);
|
|
__ pushq(RAX);
|
|
__ movss(Address(RSP, 0), XMM0);
|
|
__ popq(RAX);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(PackedCompareEQ, test) {
|
|
typedef uint32_t (*PackedCompareEQCode)();
|
|
uint32_t res = reinterpret_cast<PackedCompareEQCode>(test->entry())();
|
|
EXPECT_EQ(static_cast<uword>(0x0), res);
|
|
EXPECT_DISASSEMBLY(
|
|
"movl rax,0x40000000\n"
|
|
"movd xmm0,rax\n"
|
|
"shufps xmm0,xmm0 [0]\n"
|
|
"movl rax,0x40800000\n"
|
|
"movd xmm1,rax\n"
|
|
"shufps xmm1,xmm1 [0]\n"
|
|
"cmpps xmm0,xmm1 [eq]\n"
|
|
"push rax\n"
|
|
"movss [rsp],xmm0\n"
|
|
"pop rax\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(XmmAlu, assembler) {
|
|
// Test the disassembler.
|
|
__ addss(XMM0, XMM0);
|
|
__ addsd(XMM0, XMM0);
|
|
__ addps(XMM0, XMM0);
|
|
__ addpd(XMM0, XMM0);
|
|
__ cvtss2sd(XMM0, XMM0);
|
|
__ cvtsd2ss(XMM0, XMM0);
|
|
__ cvtps2pd(XMM0, XMM0);
|
|
__ cvtpd2ps(XMM0, XMM0);
|
|
__ movl(RAX, Immediate(0));
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(XmmAlu, test) {
|
|
typedef intptr_t (*XmmAluTest)();
|
|
intptr_t res = reinterpret_cast<XmmAluTest>(test->entry())();
|
|
EXPECT_EQ(res, 0);
|
|
EXPECT_DISASSEMBLY(
|
|
"addss xmm0,xmm0\n"
|
|
"addsd xmm0,xmm0\n"
|
|
"addps xmm0,xmm0\n"
|
|
"addpd xmm0,xmm0\n"
|
|
"cvtss2sd xmm0,xmm0\n"
|
|
"cvtsd2ss xmm0,xmm0\n"
|
|
"cvtps2pd xmm0,xmm0\n"
|
|
"cvtpd2ps xmm0,xmm0\n"
|
|
"movl rax,0\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(PackedCompareNEQ, assembler) {
|
|
__ set1ps(XMM0, RAX, Immediate(bit_cast<int32_t, float>(2.0f)));
|
|
__ set1ps(XMM1, RAX, Immediate(bit_cast<int32_t, float>(4.0f)));
|
|
__ cmppsneq(XMM0, XMM1);
|
|
__ pushq(RAX);
|
|
__ movss(Address(RSP, 0), XMM0);
|
|
__ popq(RAX);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(PackedCompareNEQ, test) {
|
|
typedef uint32_t (*PackedCompareNEQCode)();
|
|
uint32_t res = reinterpret_cast<PackedCompareNEQCode>(test->entry())();
|
|
EXPECT_EQ(static_cast<uword>(0xFFFFFFFF), res);
|
|
EXPECT_DISASSEMBLY(
|
|
"movl rax,0x40000000\n"
|
|
"movd xmm0,rax\n"
|
|
"shufps xmm0,xmm0 [0]\n"
|
|
"movl rax,0x40800000\n"
|
|
"movd xmm1,rax\n"
|
|
"shufps xmm1,xmm1 [0]\n"
|
|
"cmpps xmm0,xmm1 [neq]\n"
|
|
"push rax\n"
|
|
"movss [rsp],xmm0\n"
|
|
"pop rax\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(PackedCompareLT, assembler) {
|
|
__ set1ps(XMM0, RAX, Immediate(bit_cast<int32_t, float>(2.0f)));
|
|
__ set1ps(XMM1, RAX, Immediate(bit_cast<int32_t, float>(4.0f)));
|
|
__ cmppslt(XMM0, XMM1);
|
|
__ pushq(RAX);
|
|
__ movss(Address(RSP, 0), XMM0);
|
|
__ popq(RAX);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(PackedCompareLT, test) {
|
|
typedef uint32_t (*PackedCompareLTCode)();
|
|
uint32_t res = reinterpret_cast<PackedCompareLTCode>(test->entry())();
|
|
EXPECT_EQ(static_cast<uword>(0xFFFFFFFF), res);
|
|
EXPECT_DISASSEMBLY(
|
|
"movl rax,0x40000000\n"
|
|
"movd xmm0,rax\n"
|
|
"shufps xmm0,xmm0 [0]\n"
|
|
"movl rax,0x40800000\n"
|
|
"movd xmm1,rax\n"
|
|
"shufps xmm1,xmm1 [0]\n"
|
|
"cmpps xmm0,xmm1 [lt]\n"
|
|
"push rax\n"
|
|
"movss [rsp],xmm0\n"
|
|
"pop rax\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(PackedCompareLE, assembler) {
|
|
__ set1ps(XMM0, RAX, Immediate(bit_cast<int32_t, float>(2.0f)));
|
|
__ set1ps(XMM1, RAX, Immediate(bit_cast<int32_t, float>(4.0f)));
|
|
__ cmppsle(XMM0, XMM1);
|
|
__ pushq(RAX);
|
|
__ movss(Address(RSP, 0), XMM0);
|
|
__ popq(RAX);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(PackedCompareLE, test) {
|
|
typedef uint32_t (*PackedCompareLECode)();
|
|
uint32_t res = reinterpret_cast<PackedCompareLECode>(test->entry())();
|
|
EXPECT_EQ(static_cast<uword>(0xFFFFFFFF), res);
|
|
EXPECT_DISASSEMBLY(
|
|
"movl rax,0x40000000\n"
|
|
"movd xmm0,rax\n"
|
|
"shufps xmm0,xmm0 [0]\n"
|
|
"movl rax,0x40800000\n"
|
|
"movd xmm1,rax\n"
|
|
"shufps xmm1,xmm1 [0]\n"
|
|
"cmpps xmm0,xmm1 [le]\n"
|
|
"push rax\n"
|
|
"movss [rsp],xmm0\n"
|
|
"pop rax\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(PackedCompareNLT, assembler) {
|
|
__ set1ps(XMM0, RAX, Immediate(bit_cast<int32_t, float>(2.0f)));
|
|
__ set1ps(XMM1, RAX, Immediate(bit_cast<int32_t, float>(4.0f)));
|
|
__ cmppsnlt(XMM0, XMM1);
|
|
__ pushq(RAX);
|
|
__ movss(Address(RSP, 0), XMM0);
|
|
__ popq(RAX);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(PackedCompareNLT, test) {
|
|
typedef uint32_t (*PackedCompareNLTCode)();
|
|
uint32_t res = reinterpret_cast<PackedCompareNLTCode>(test->entry())();
|
|
EXPECT_EQ(static_cast<uword>(0x0), res);
|
|
EXPECT_DISASSEMBLY(
|
|
"movl rax,0x40000000\n"
|
|
"movd xmm0,rax\n"
|
|
"shufps xmm0,xmm0 [0]\n"
|
|
"movl rax,0x40800000\n"
|
|
"movd xmm1,rax\n"
|
|
"shufps xmm1,xmm1 [0]\n"
|
|
"cmpps xmm0,xmm1 [nlt]\n"
|
|
"push rax\n"
|
|
"movss [rsp],xmm0\n"
|
|
"pop rax\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(PackedCompareNLE, assembler) {
|
|
__ set1ps(XMM0, RAX, Immediate(bit_cast<int32_t, float>(2.0f)));
|
|
__ set1ps(XMM1, RAX, Immediate(bit_cast<int32_t, float>(4.0f)));
|
|
__ cmppsnle(XMM0, XMM1);
|
|
__ pushq(RAX);
|
|
__ movss(Address(RSP, 0), XMM0);
|
|
__ popq(RAX);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(PackedCompareNLE, test) {
|
|
typedef uint32_t (*PackedCompareNLECode)();
|
|
uint32_t res = reinterpret_cast<PackedCompareNLECode>(test->entry())();
|
|
EXPECT_EQ(static_cast<uword>(0x0), res);
|
|
EXPECT_DISASSEMBLY(
|
|
"movl rax,0x40000000\n"
|
|
"movd xmm0,rax\n"
|
|
"shufps xmm0,xmm0 [0]\n"
|
|
"movl rax,0x40800000\n"
|
|
"movd xmm1,rax\n"
|
|
"shufps xmm1,xmm1 [0]\n"
|
|
"cmpps xmm0,xmm1 [nle]\n"
|
|
"push rax\n"
|
|
"movss [rsp],xmm0\n"
|
|
"pop rax\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(PackedNegate, assembler) {
|
|
EnterTestFrame(assembler);
|
|
__ movl(RAX, Immediate(bit_cast<int32_t, float>(12.3f)));
|
|
__ movd(XMM0, RAX);
|
|
__ shufps(XMM0, XMM0, Immediate(0x0));
|
|
__ negateps(XMM0, XMM0);
|
|
__ shufps(XMM0, XMM0, Immediate(0xAA)); // Copy third lane into all 4 lanes.
|
|
LeaveTestFrame(assembler);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(PackedNegate, test) {
|
|
float res = test->InvokeWithCodeAndThread<float>();
|
|
EXPECT_FLOAT_EQ(-12.3f, res, 0.001f);
|
|
EXPECT_DISASSEMBLY_NOT_WINDOWS(
|
|
"push rbp\n"
|
|
"movq rbp,rsp\n"
|
|
"push r12\n"
|
|
"push pp\n"
|
|
"push thr\n"
|
|
"movq r12,rdi\n"
|
|
"movq thr,rsi\n"
|
|
"movq pp,[r12+0x27]\n"
|
|
"movl rax,0x4144cccd\n"
|
|
"movd xmm0,rax\n"
|
|
"shufps xmm0,xmm0 [0]\n"
|
|
"movq tmp,[thr+0x...]\n"
|
|
"xorps xmm0,[tmp]\n"
|
|
"shufps xmm0,xmm0 [aa]\n"
|
|
"pop thr\n"
|
|
"pop pp\n"
|
|
"pop r12\n"
|
|
"movq rsp,rbp\n"
|
|
"pop rbp\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(PackedAbsolute, assembler) {
|
|
EnterTestFrame(assembler);
|
|
__ movl(RAX, Immediate(bit_cast<int32_t, float>(-15.3f)));
|
|
__ movd(XMM0, RAX);
|
|
__ shufps(XMM0, XMM0, Immediate(0x0));
|
|
__ absps(XMM0, XMM0);
|
|
__ shufps(XMM0, XMM0, Immediate(0xAA)); // Copy third lane into all 4 lanes.
|
|
LeaveTestFrame(assembler);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(PackedAbsolute, test) {
|
|
float res = test->InvokeWithCodeAndThread<float>();
|
|
EXPECT_FLOAT_EQ(15.3f, res, 0.001f);
|
|
EXPECT_DISASSEMBLY_NOT_WINDOWS(
|
|
"push rbp\n"
|
|
"movq rbp,rsp\n"
|
|
"push r12\n"
|
|
"push pp\n"
|
|
"push thr\n"
|
|
"movq r12,rdi\n"
|
|
"movq thr,rsi\n"
|
|
"movq pp,[r12+0x27]\n"
|
|
"movl rax,-0x3e8b3333\n"
|
|
"movd xmm0,rax\n"
|
|
"shufps xmm0,xmm0 [0]\n"
|
|
"movq tmp,[thr+0x...]\n"
|
|
"andps xmm0,[tmp]\n"
|
|
"shufps xmm0,xmm0 [aa]\n"
|
|
"pop thr\n"
|
|
"pop pp\n"
|
|
"pop r12\n"
|
|
"movq rsp,rbp\n"
|
|
"pop rbp\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(PackedSetWZero, assembler) {
|
|
EnterTestFrame(assembler);
|
|
__ set1ps(XMM0, RAX, Immediate(bit_cast<int32_t, float>(12.3f)));
|
|
__ zerowps(XMM0, XMM0);
|
|
__ shufps(XMM0, XMM0, Immediate(0xFF)); // Copy the W lane which is now 0.0.
|
|
LeaveTestFrame(assembler);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(PackedSetWZero, test) {
|
|
float res = test->InvokeWithCodeAndThread<float>();
|
|
EXPECT_FLOAT_EQ(0.0f, res, 0.001f);
|
|
EXPECT_DISASSEMBLY_NOT_WINDOWS(
|
|
"push rbp\n"
|
|
"movq rbp,rsp\n"
|
|
"push r12\n"
|
|
"push pp\n"
|
|
"push thr\n"
|
|
"movq r12,rdi\n"
|
|
"movq thr,rsi\n"
|
|
"movq pp,[r12+0x27]\n"
|
|
"movl rax,0x4144cccd\n"
|
|
"movd xmm0,rax\n"
|
|
"shufps xmm0,xmm0 [0]\n"
|
|
"movq tmp,[thr+0x...]\n"
|
|
"andps xmm0,[tmp]\n"
|
|
"shufps xmm0,xmm0 [ff]\n"
|
|
"pop thr\n"
|
|
"pop pp\n"
|
|
"pop r12\n"
|
|
"movq rsp,rbp\n"
|
|
"pop rbp\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(PackedMin, assembler) {
|
|
__ set1ps(XMM0, RAX, Immediate(bit_cast<int32_t, float>(2.0f)));
|
|
__ set1ps(XMM1, RAX, Immediate(bit_cast<int32_t, float>(4.0f)));
|
|
__ minps(XMM0, XMM1);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(PackedMin, test) {
|
|
typedef float (*PackedMinCode)();
|
|
float res = reinterpret_cast<PackedMinCode>(test->entry())();
|
|
EXPECT_FLOAT_EQ(2.0f, res, 0.001f);
|
|
EXPECT_DISASSEMBLY(
|
|
"movl rax,0x40000000\n"
|
|
"movd xmm0,rax\n"
|
|
"shufps xmm0,xmm0 [0]\n"
|
|
"movl rax,0x40800000\n"
|
|
"movd xmm1,rax\n"
|
|
"shufps xmm1,xmm1 [0]\n"
|
|
"minps xmm0,xmm1\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(PackedMax, assembler) {
|
|
__ set1ps(XMM0, RAX, Immediate(bit_cast<int32_t, float>(2.0f)));
|
|
__ set1ps(XMM1, RAX, Immediate(bit_cast<int32_t, float>(4.0f)));
|
|
__ maxps(XMM0, XMM1);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(PackedMax, test) {
|
|
typedef float (*PackedMaxCode)();
|
|
float res = reinterpret_cast<PackedMaxCode>(test->entry())();
|
|
EXPECT_FLOAT_EQ(4.0f, res, 0.001f);
|
|
EXPECT_DISASSEMBLY(
|
|
"movl rax,0x40000000\n"
|
|
"movd xmm0,rax\n"
|
|
"shufps xmm0,xmm0 [0]\n"
|
|
"movl rax,0x40800000\n"
|
|
"movd xmm1,rax\n"
|
|
"shufps xmm1,xmm1 [0]\n"
|
|
"maxps xmm0,xmm1\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(PackedLogicalOr, assembler) {
|
|
static const struct ALIGN16 {
|
|
uint32_t a;
|
|
uint32_t b;
|
|
uint32_t c;
|
|
uint32_t d;
|
|
} constant1 = {0xF0F0F0F0, 0xF0F0F0F0, 0xF0F0F0F0, 0xF0F0F0F0};
|
|
static const struct ALIGN16 {
|
|
uint32_t a;
|
|
uint32_t b;
|
|
uint32_t c;
|
|
uint32_t d;
|
|
} constant2 = {0x0F0F0F0F, 0x0F0F0F0F, 0x0F0F0F0F, 0x0F0F0F0F};
|
|
__ movq(RAX, Immediate(reinterpret_cast<intptr_t>(&constant1)));
|
|
__ movups(XMM0, Address(RAX, 0));
|
|
__ movq(RAX, Immediate(reinterpret_cast<intptr_t>(&constant2)));
|
|
__ movups(XMM1, Address(RAX, 0));
|
|
__ orps(XMM0, XMM1);
|
|
__ pushq(RAX);
|
|
__ movss(Address(RSP, 0), XMM0);
|
|
__ popq(RAX);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(PackedLogicalOr, test) {
|
|
typedef uint32_t (*PackedLogicalOrCode)();
|
|
uint32_t res = reinterpret_cast<PackedLogicalOrCode>(test->entry())();
|
|
EXPECT_EQ(0xFFFFFFFF, res);
|
|
EXPECT_DISASSEMBLY_ENDS_WITH(
|
|
"movups xmm1,[rax]\n"
|
|
"orps xmm0,xmm1\n"
|
|
"push rax\n"
|
|
"movss [rsp],xmm0\n"
|
|
"pop rax\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(PackedLogicalAnd, assembler) {
|
|
static const struct ALIGN16 {
|
|
uint32_t a;
|
|
uint32_t b;
|
|
uint32_t c;
|
|
uint32_t d;
|
|
} constant1 = {0xF0F0F0F0, 0xF0F0F0F0, 0xF0F0F0F0, 0xF0F0F0F0};
|
|
static const struct ALIGN16 {
|
|
uint32_t a;
|
|
uint32_t b;
|
|
uint32_t c;
|
|
uint32_t d;
|
|
} constant2 = {0x0F0FFF0F, 0x0F0F0F0F, 0x0F0F0F0F, 0x0F0F0F0F};
|
|
__ movq(RAX, Immediate(reinterpret_cast<intptr_t>(&constant1)));
|
|
__ movups(XMM0, Address(RAX, 0));
|
|
__ movq(RAX, Immediate(reinterpret_cast<intptr_t>(&constant2)));
|
|
__ andps(XMM0, Address(RAX, 0));
|
|
__ pushq(RAX);
|
|
__ movss(Address(RSP, 0), XMM0);
|
|
__ popq(RAX);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(PackedLogicalAnd, test) {
|
|
typedef uint32_t (*PackedLogicalAndCode)();
|
|
uint32_t res = reinterpret_cast<PackedLogicalAndCode>(test->entry())();
|
|
EXPECT_EQ(static_cast<uword>(0x0000F000), res);
|
|
EXPECT_DISASSEMBLY_ENDS_WITH(
|
|
"andps xmm0,[rax]\n"
|
|
"push rax\n"
|
|
"movss [rsp],xmm0\n"
|
|
"pop rax\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(PackedLogicalNot, assembler) {
|
|
static const struct ALIGN16 {
|
|
uint32_t a;
|
|
uint32_t b;
|
|
uint32_t c;
|
|
uint32_t d;
|
|
} constant1 = {0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF};
|
|
EnterTestFrame(assembler);
|
|
__ LoadImmediate(RAX, Immediate(reinterpret_cast<intptr_t>(&constant1)));
|
|
__ movups(XMM9, Address(RAX, 0));
|
|
__ notps(XMM0, XMM9);
|
|
__ pushq(RAX);
|
|
__ movss(Address(RSP, 0), XMM0);
|
|
__ popq(RAX);
|
|
LeaveTestFrame(assembler);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(PackedLogicalNot, test) {
|
|
uint32_t res = test->InvokeWithCodeAndThread<uint32_t>();
|
|
EXPECT_EQ(static_cast<uword>(0x0), res);
|
|
EXPECT_DISASSEMBLY_NOT_WINDOWS_ENDS_WITH(
|
|
"movups xmm9,[rax]\n"
|
|
"movq tmp,[thr+0x...]\n"
|
|
"movups xmm0,[tmp]\n"
|
|
"xorps xmm0,xmm9\n"
|
|
"push rax\n"
|
|
"movss [rsp],xmm0\n"
|
|
"pop rax\n"
|
|
"pop thr\n"
|
|
"pop pp\n"
|
|
"pop r12\n"
|
|
"movq rsp,rbp\n"
|
|
"pop rbp\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(PackedMoveHighLow, assembler) {
|
|
static const struct ALIGN16 {
|
|
float a;
|
|
float b;
|
|
float c;
|
|
float d;
|
|
} constant0 = {1.0, 2.0, 3.0, 4.0};
|
|
static const struct ALIGN16 {
|
|
float a;
|
|
float b;
|
|
float c;
|
|
float d;
|
|
} constant1 = {5.0, 6.0, 7.0, 8.0};
|
|
// XMM9 = 1.0f, 2.0f, 3.0f, 4.0f.
|
|
__ movq(RAX, Immediate(reinterpret_cast<intptr_t>(&constant0)));
|
|
__ movups(XMM9, Address(RAX, 0));
|
|
// XMM1 = 5.0f, 6.0f, 7.0f, 8.0f.
|
|
__ movq(RAX, Immediate(reinterpret_cast<intptr_t>(&constant1)));
|
|
__ movups(XMM1, Address(RAX, 0));
|
|
// XMM9 = 7.0f, 8.0f, 3.0f, 4.0f.
|
|
__ movhlps(XMM9, XMM1);
|
|
__ xorps(XMM1, XMM1);
|
|
// XMM1 = 7.0f, 8.0f, 3.0f, 4.0f.
|
|
__ movaps(XMM1, XMM9);
|
|
__ shufps(XMM9, XMM9, Immediate(0x00)); // 7.0f.
|
|
__ shufps(XMM1, XMM1, Immediate(0x55)); // 8.0f.
|
|
__ addss(XMM9, XMM1); // 15.0f.
|
|
__ movaps(XMM0, XMM9);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(PackedMoveHighLow, test) {
|
|
typedef float (*PackedMoveHighLow)();
|
|
float res = reinterpret_cast<PackedMoveHighLow>(test->entry())();
|
|
EXPECT_FLOAT_EQ(15.0f, res, 0.001f);
|
|
EXPECT_DISASSEMBLY_ENDS_WITH(
|
|
"movups xmm1,[rax]\n"
|
|
"movhlps xmm9,xmm1\n"
|
|
"xorps xmm1,xmm1\n"
|
|
"movaps xmm1,xmm9\n"
|
|
"shufps xmm9,xmm9 [0]\n"
|
|
"shufps xmm1,xmm1 [55]\n"
|
|
"addss xmm9,xmm1\n"
|
|
"movaps xmm0,xmm9\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(PackedMoveLowHigh, assembler) {
|
|
static const struct ALIGN16 {
|
|
float a;
|
|
float b;
|
|
float c;
|
|
float d;
|
|
} constant0 = {1.0, 2.0, 3.0, 4.0};
|
|
static const struct ALIGN16 {
|
|
float a;
|
|
float b;
|
|
float c;
|
|
float d;
|
|
} constant1 = {5.0, 6.0, 7.0, 8.0};
|
|
// XMM9 = 1.0f, 2.0f, 3.0f, 4.0f.
|
|
__ movq(RAX, Immediate(reinterpret_cast<intptr_t>(&constant0)));
|
|
__ movups(XMM9, Address(RAX, 0));
|
|
// XMM1 = 5.0f, 6.0f, 7.0f, 8.0f.
|
|
__ movq(RAX, Immediate(reinterpret_cast<intptr_t>(&constant1)));
|
|
__ movups(XMM1, Address(RAX, 0));
|
|
// XMM9 = 1.0f, 2.0f, 5.0f, 6.0f
|
|
__ movlhps(XMM9, XMM1);
|
|
__ xorps(XMM1, XMM1);
|
|
// XMM1 = 1.0f, 2.0f, 5.0f, 6.0f
|
|
__ movaps(XMM1, XMM9);
|
|
__ shufps(XMM9, XMM9, Immediate(0xAA)); // 5.0f.
|
|
__ shufps(XMM1, XMM1, Immediate(0xFF)); // 6.0f.
|
|
__ addss(XMM9, XMM1); // 11.0f.
|
|
__ movaps(XMM0, XMM9);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(PackedMoveLowHigh, test) {
|
|
typedef float (*PackedMoveLowHigh)();
|
|
float res = reinterpret_cast<PackedMoveLowHigh>(test->entry())();
|
|
EXPECT_FLOAT_EQ(11.0f, res, 0.001f);
|
|
EXPECT_DISASSEMBLY_ENDS_WITH(
|
|
"movups xmm1,[rax]\n"
|
|
"movlhps xmm9,xmm1\n"
|
|
"xorps xmm1,xmm1\n"
|
|
"movaps xmm1,xmm9\n"
|
|
"shufps xmm9,xmm9 [aa]\n"
|
|
"shufps xmm1,xmm1 [ff]\n"
|
|
"addss xmm9,xmm1\n"
|
|
"movaps xmm0,xmm9\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(PackedUnpackLow, assembler) {
|
|
static const struct ALIGN16 {
|
|
float a;
|
|
float b;
|
|
float c;
|
|
float d;
|
|
} constant0 = {1.0, 2.0, 3.0, 4.0};
|
|
static const struct ALIGN16 {
|
|
float a;
|
|
float b;
|
|
float c;
|
|
float d;
|
|
} constant1 = {5.0, 6.0, 7.0, 8.0};
|
|
// XMM9 = 1.0f, 2.0f, 3.0f, 4.0f.
|
|
__ movq(RAX, Immediate(reinterpret_cast<intptr_t>(&constant0)));
|
|
__ movups(XMM9, Address(RAX, 0));
|
|
// XMM1 = 5.0f, 6.0f, 7.0f, 8.0f.
|
|
__ movq(RAX, Immediate(reinterpret_cast<intptr_t>(&constant1)));
|
|
__ movups(XMM1, Address(RAX, 0));
|
|
// XMM9 = 1.0f, 5.0f, 2.0f, 6.0f.
|
|
__ unpcklps(XMM9, XMM1);
|
|
// XMM1 = 1.0f, 5.0f, 2.0f, 6.0f.
|
|
__ movaps(XMM1, XMM9);
|
|
__ shufps(XMM9, XMM9, Immediate(0x55));
|
|
__ shufps(XMM1, XMM1, Immediate(0xFF));
|
|
__ addss(XMM9, XMM1); // 11.0f.
|
|
__ movaps(XMM0, XMM9);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(PackedUnpackLow, test) {
|
|
typedef float (*PackedUnpackLow)();
|
|
float res = reinterpret_cast<PackedUnpackLow>(test->entry())();
|
|
EXPECT_FLOAT_EQ(11.0f, res, 0.001f);
|
|
EXPECT_DISASSEMBLY_ENDS_WITH(
|
|
"movups xmm1,[rax]\n"
|
|
"unpcklps xmm9,xmm1\n"
|
|
"movaps xmm1,xmm9\n"
|
|
"shufps xmm9,xmm9 [55]\n"
|
|
"shufps xmm1,xmm1 [ff]\n"
|
|
"addss xmm9,xmm1\n"
|
|
"movaps xmm0,xmm9\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(PackedUnpackHigh, assembler) {
|
|
static const struct ALIGN16 {
|
|
float a;
|
|
float b;
|
|
float c;
|
|
float d;
|
|
} constant0 = {1.0, 2.0, 3.0, 4.0};
|
|
static const struct ALIGN16 {
|
|
float a;
|
|
float b;
|
|
float c;
|
|
float d;
|
|
} constant1 = {5.0, 6.0, 7.0, 8.0};
|
|
// XMM9 = 1.0f, 2.0f, 3.0f, 4.0f.
|
|
__ movq(RAX, Immediate(reinterpret_cast<intptr_t>(&constant0)));
|
|
__ movups(XMM9, Address(RAX, 0));
|
|
// XMM1 = 5.0f, 6.0f, 7.0f, 8.0f.
|
|
__ movq(RAX, Immediate(reinterpret_cast<intptr_t>(&constant1)));
|
|
__ movups(XMM1, Address(RAX, 0));
|
|
// XMM9 = 3.0f, 7.0f, 4.0f, 8.0f.
|
|
__ unpckhps(XMM9, XMM1);
|
|
// XMM1 = 3.0f, 7.0f, 4.0f, 8.0f.
|
|
__ movaps(XMM1, XMM9);
|
|
__ shufps(XMM9, XMM9, Immediate(0x00));
|
|
__ shufps(XMM1, XMM1, Immediate(0xAA));
|
|
__ addss(XMM9, XMM1); // 7.0f.
|
|
__ movaps(XMM0, XMM9);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(PackedUnpackHigh, test) {
|
|
typedef float (*PackedUnpackHigh)();
|
|
float res = reinterpret_cast<PackedUnpackHigh>(test->entry())();
|
|
EXPECT_FLOAT_EQ(7.0f, res, 0.001f);
|
|
EXPECT_DISASSEMBLY_ENDS_WITH(
|
|
"movups xmm1,[rax]\n"
|
|
"unpckhps xmm9,xmm1\n"
|
|
"movaps xmm1,xmm9\n"
|
|
"shufps xmm9,xmm9 [0]\n"
|
|
"shufps xmm1,xmm1 [aa]\n"
|
|
"addss xmm9,xmm1\n"
|
|
"movaps xmm0,xmm9\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(PackedUnpackLowPair, assembler) {
|
|
static const struct ALIGN16 {
|
|
float a;
|
|
float b;
|
|
float c;
|
|
float d;
|
|
} constant0 = {1.0, 2.0, 3.0, 4.0};
|
|
static const struct ALIGN16 {
|
|
float a;
|
|
float b;
|
|
float c;
|
|
float d;
|
|
} constant1 = {5.0, 6.0, 7.0, 8.0};
|
|
// XMM9 = 1.0f, 2.0f, 3.0f, 4.0f.
|
|
__ movq(RAX, Immediate(reinterpret_cast<intptr_t>(&constant0)));
|
|
__ movups(XMM9, Address(RAX, 0));
|
|
// XMM1 = 5.0f, 6.0f, 7.0f, 8.0f.
|
|
__ movq(RAX, Immediate(reinterpret_cast<intptr_t>(&constant1)));
|
|
__ movups(XMM1, Address(RAX, 0));
|
|
// XMM9 = 1.0f, 2.0f, 5.0f, 6.0f.
|
|
__ unpcklpd(XMM9, XMM1);
|
|
// XMM1 = 1.0f, 2.0f, 5.0f, 6.0f.
|
|
__ movaps(XMM1, XMM9);
|
|
__ shufps(XMM9, XMM9, Immediate(0x00));
|
|
__ shufps(XMM1, XMM1, Immediate(0xAA));
|
|
__ addss(XMM9, XMM1); // 6.0f.
|
|
__ movaps(XMM0, XMM9);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(PackedUnpackLowPair, test) {
|
|
typedef float (*PackedUnpackLowPair)();
|
|
float res = reinterpret_cast<PackedUnpackLowPair>(test->entry())();
|
|
EXPECT_FLOAT_EQ(6.0f, res, 0.001f);
|
|
EXPECT_DISASSEMBLY_ENDS_WITH(
|
|
"movups xmm1,[rax]\n"
|
|
"unpcklpd xmm9,xmm1\n"
|
|
"movaps xmm1,xmm9\n"
|
|
"shufps xmm9,xmm9 [0]\n"
|
|
"shufps xmm1,xmm1 [aa]\n"
|
|
"addss xmm9,xmm1\n"
|
|
"movaps xmm0,xmm9\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(PackedUnpackHighPair, assembler) {
|
|
static const struct ALIGN16 {
|
|
float a;
|
|
float b;
|
|
float c;
|
|
float d;
|
|
} constant0 = {1.0, 2.0, 3.0, 4.0};
|
|
static const struct ALIGN16 {
|
|
float a;
|
|
float b;
|
|
float c;
|
|
float d;
|
|
} constant1 = {5.0, 6.0, 7.0, 8.0};
|
|
// XMM9 = 1.0f, 2.0f, 3.0f, 4.0f.
|
|
__ movq(RAX, Immediate(reinterpret_cast<intptr_t>(&constant0)));
|
|
__ movups(XMM9, Address(RAX, 0));
|
|
// XMM1 = 5.0f, 6.0f, 7.0f, 8.0f.
|
|
__ movq(RAX, Immediate(reinterpret_cast<intptr_t>(&constant1)));
|
|
__ movups(XMM1, Address(RAX, 0));
|
|
// XMM9 = 3.0f, 4.0f, 7.0f, 8.0f.
|
|
__ unpckhpd(XMM9, XMM1);
|
|
// XMM1 = 3.0f, 4.0f, 7.0f, 8.0f.
|
|
__ movaps(XMM1, XMM9);
|
|
__ shufps(XMM9, XMM9, Immediate(0x55));
|
|
__ shufps(XMM1, XMM1, Immediate(0xFF));
|
|
__ addss(XMM9, XMM1); // 12.0f.
|
|
__ movaps(XMM0, XMM9);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(PackedUnpackHighPair, test) {
|
|
typedef float (*PackedUnpackHighPair)();
|
|
float res = reinterpret_cast<PackedUnpackHighPair>(test->entry())();
|
|
EXPECT_FLOAT_EQ(12.0f, res, 0.001f);
|
|
EXPECT_DISASSEMBLY_ENDS_WITH(
|
|
"movups xmm1,[rax]\n"
|
|
"unpckhpd xmm9,xmm1\n"
|
|
"movaps xmm1,xmm9\n"
|
|
"shufps xmm9,xmm9 [55]\n"
|
|
"shufps xmm1,xmm1 [ff]\n"
|
|
"addss xmm9,xmm1\n"
|
|
"movaps xmm0,xmm9\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(DoubleFPMoves, assembler) {
|
|
__ movq(RAX, Immediate(bit_cast<int64_t, double>(1024.67)));
|
|
__ pushq(R15); // Callee saved.
|
|
__ pushq(RAX);
|
|
__ movsd(XMM0, Address(RSP, 0));
|
|
__ movsd(XMM1, XMM0);
|
|
__ movsd(XMM2, XMM1);
|
|
__ movsd(XMM3, XMM2);
|
|
__ movsd(XMM4, XMM3);
|
|
__ movsd(XMM5, XMM4);
|
|
__ movsd(XMM6, XMM5);
|
|
__ movsd(XMM7, XMM6);
|
|
__ movsd(XMM8, XMM7);
|
|
__ movsd(XMM9, XMM8);
|
|
__ movsd(XMM10, XMM9);
|
|
__ movsd(XMM11, XMM10);
|
|
__ movsd(XMM12, XMM11);
|
|
__ movsd(XMM13, XMM12);
|
|
__ movsd(XMM14, XMM13);
|
|
__ movsd(XMM15, XMM14);
|
|
__ movq(Address(RSP, 0), Immediate(0));
|
|
__ movsd(XMM0, Address(RSP, 0));
|
|
__ movsd(Address(RSP, 0), XMM15);
|
|
__ movsd(XMM1, Address(RSP, 0));
|
|
__ movq(R10, RSP);
|
|
__ movsd(Address(R10, 0), XMM1);
|
|
__ movsd(XMM2, Address(R10, 0));
|
|
__ movq(R15, RSP);
|
|
__ movsd(Address(R15, 0), XMM2);
|
|
__ movsd(XMM3, Address(R15, 0));
|
|
__ movq(RAX, RSP);
|
|
__ movsd(Address(RAX, 0), XMM3);
|
|
__ movsd(XMM4, Address(RAX, 0));
|
|
__ movsd(XMM15, Address(RSP, 0));
|
|
__ movaps(XMM14, XMM15);
|
|
__ movaps(XMM13, XMM14);
|
|
__ movaps(XMM12, XMM13);
|
|
__ movaps(XMM11, XMM12);
|
|
__ movaps(XMM10, XMM11);
|
|
__ movaps(XMM9, XMM10);
|
|
__ movaps(XMM8, XMM9);
|
|
__ movaps(XMM7, XMM8);
|
|
__ movaps(XMM6, XMM7);
|
|
__ movaps(XMM5, XMM6);
|
|
__ movaps(XMM4, XMM5);
|
|
__ movaps(XMM3, XMM4);
|
|
__ movaps(XMM2, XMM3);
|
|
__ movaps(XMM1, XMM2);
|
|
__ movaps(XMM0, XMM1);
|
|
__ popq(RAX);
|
|
__ popq(R15); // Callee saved.
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(DoubleFPMoves, test) {
|
|
typedef double (*DoubleFPMovesCode)();
|
|
EXPECT_FLOAT_EQ(1024.67, reinterpret_cast<DoubleFPMovesCode>(test->entry())(),
|
|
0.001);
|
|
EXPECT_DISASSEMBLY(
|
|
"movq rax,0x409002ae147ae148\n"
|
|
"push pp\n"
|
|
"push rax\n"
|
|
"movsd xmm0,[rsp]\n"
|
|
"movsd xmm1,xmm0\n"
|
|
"movsd xmm2,xmm1\n"
|
|
"movsd xmm3,xmm2\n"
|
|
"movsd xmm4,xmm3\n"
|
|
"movsd xmm5,xmm4\n"
|
|
"movsd xmm6,xmm5\n"
|
|
"movsd xmm7,xmm6\n"
|
|
"movsd xmm8,xmm7\n"
|
|
"movsd xmm9,xmm8\n"
|
|
"movsd xmm10,xmm9\n"
|
|
"movsd xmm11,xmm10\n"
|
|
"movsd xmm12,xmm11\n"
|
|
"movsd xmm13,xmm12\n"
|
|
"movsd xmm14,xmm13\n"
|
|
"movsd xmm15,xmm14\n"
|
|
"movq [rsp],0\n"
|
|
"movsd xmm0,[rsp]\n"
|
|
"movsd [rsp],xmm15\n"
|
|
"movsd xmm1,[rsp]\n"
|
|
"movq r10,rsp\n"
|
|
"movsd [r10],xmm1\n"
|
|
"movsd xmm2,[r10]\n"
|
|
"movq pp,rsp\n"
|
|
"movsd [pp],xmm2\n"
|
|
"movsd xmm3,[pp]\n"
|
|
"movq rax,rsp\n"
|
|
"movsd [rax],xmm3\n"
|
|
"movsd xmm4,[rax]\n"
|
|
"movsd xmm15,[rsp]\n"
|
|
"movaps xmm14,xmm15\n"
|
|
"movaps xmm13,xmm14\n"
|
|
"movaps xmm12,xmm13\n"
|
|
"movaps xmm11,xmm12\n"
|
|
"movaps xmm10,xmm11\n"
|
|
"movaps xmm9,xmm10\n"
|
|
"movaps xmm8,xmm9\n"
|
|
"movaps xmm7,xmm8\n"
|
|
"movaps xmm6,xmm7\n"
|
|
"movaps xmm5,xmm6\n"
|
|
"movaps xmm4,xmm5\n"
|
|
"movaps xmm3,xmm4\n"
|
|
"movaps xmm2,xmm3\n"
|
|
"movaps xmm1,xmm2\n"
|
|
"movaps xmm0,xmm1\n"
|
|
"pop rax\n"
|
|
"pop pp\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(DoubleFPOperations, assembler) {
|
|
__ movq(RAX, Immediate(bit_cast<int64_t, double>(12.3)));
|
|
__ pushq(RAX);
|
|
__ movsd(XMM0, Address(RSP, 0));
|
|
__ movsd(XMM8, Address(RSP, 0));
|
|
__ movq(RAX, Immediate(bit_cast<int64_t, double>(3.4)));
|
|
__ movq(Address(RSP, 0), RAX);
|
|
__ movsd(XMM12, Address(RSP, 0));
|
|
__ addsd(XMM8, XMM12); // 15.7
|
|
__ mulsd(XMM8, XMM12); // 53.38
|
|
__ subsd(XMM8, XMM12); // 49.98
|
|
__ divsd(XMM8, XMM12); // 14.7
|
|
__ sqrtsd(XMM8, XMM8); // 3.834
|
|
__ movsd(XMM1, Address(RSP, 0));
|
|
__ addsd(XMM0, XMM1); // 15.7
|
|
__ mulsd(XMM0, XMM1); // 53.38
|
|
__ subsd(XMM0, XMM1); // 49.98
|
|
__ divsd(XMM0, XMM1); // 14.7
|
|
__ sqrtsd(XMM0, XMM0); // 3.834057902
|
|
__ addsd(XMM0, XMM8); // 7.6681
|
|
__ popq(RAX);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(DoubleFPOperations, test) {
|
|
typedef double (*SingleFPOperationsCode)();
|
|
double res = reinterpret_cast<SingleFPOperationsCode>(test->entry())();
|
|
EXPECT_FLOAT_EQ(7.668, res, 0.001);
|
|
EXPECT_DISASSEMBLY(
|
|
"movq rax,0x402899999999999a\n"
|
|
"push rax\n"
|
|
"movsd xmm0,[rsp]\n"
|
|
"movsd xmm8,[rsp]\n"
|
|
"movq rax,0x400b333333333333\n"
|
|
"movq [rsp],rax\n"
|
|
"movsd xmm12,[rsp]\n"
|
|
"addsd xmm8,xmm12\n"
|
|
"mulsd xmm8,xmm12\n"
|
|
"subsd xmm8,xmm12\n"
|
|
"divsd xmm8,xmm12\n"
|
|
"sqrtsd xmm8,xmm8\n"
|
|
"movsd xmm1,[rsp]\n"
|
|
"addsd xmm0,xmm1\n"
|
|
"mulsd xmm0,xmm1\n"
|
|
"subsd xmm0,xmm1\n"
|
|
"divsd xmm0,xmm1\n"
|
|
"sqrtsd xmm0,xmm0\n"
|
|
"addsd xmm0,xmm8\n"
|
|
"pop rax\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(Int32ToDoubleConversion, assembler) {
|
|
// Fill upper bits with garbage.
|
|
__ movq(R11, Immediate(0x1111111100000006));
|
|
__ cvtsi2sdl(XMM0, R11);
|
|
// Fill upper bits with garbage.
|
|
__ movq(R11, Immediate(0x2222222200000008));
|
|
__ cvtsi2sdl(XMM8, R11);
|
|
__ subsd(XMM0, XMM8);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(Int32ToDoubleConversion, test) {
|
|
typedef double (*Int32ToDoubleConversion)();
|
|
double res = reinterpret_cast<Int32ToDoubleConversion>(test->entry())();
|
|
EXPECT_FLOAT_EQ(-2.0, res, 0.001);
|
|
EXPECT_DISASSEMBLY(
|
|
"movq tmp,0x1111111100000006\n"
|
|
"cvtsi2sd xmm0,tmp\n"
|
|
"movq tmp,0x2222222200000008\n"
|
|
"cvtsi2sd xmm8,tmp\n"
|
|
"subsd xmm0,xmm8\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(Int64ToDoubleConversion, assembler) {
|
|
__ movq(RDX, Immediate(12LL << 32));
|
|
__ cvtsi2sdq(XMM0, RDX);
|
|
__ movsd(XMM15, XMM0); // Move to high register
|
|
__ addsd(XMM0, XMM0); // Stomp XMM0
|
|
__ movsd(XMM0, XMM15); // Move back to XMM0
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(Int64ToDoubleConversion, test) {
|
|
typedef double (*Int64ToDoubleConversionCode)();
|
|
double res = reinterpret_cast<Int64ToDoubleConversionCode>(test->entry())();
|
|
EXPECT_FLOAT_EQ(static_cast<double>(12LL << 32), res, 0.001);
|
|
EXPECT_DISASSEMBLY(
|
|
"movq rdx,0x0000000c00000000\n"
|
|
"cvtsi2sd xmm0,rdx\n"
|
|
"movsd xmm15,xmm0\n"
|
|
"addsd xmm0,xmm0\n"
|
|
"movsd xmm0,xmm15\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(DoubleToInt64Conversion, assembler) {
|
|
__ movq(RAX, Immediate(bit_cast<int64_t, double>(4.2e22)));
|
|
__ pushq(RAX);
|
|
__ movsd(XMM9, Address(RSP, 0));
|
|
__ popq(RAX);
|
|
__ cvttsd2siq(RAX, XMM9);
|
|
__ CompareImmediate(RAX, Immediate(0x8000000000000000ll));
|
|
Label ok;
|
|
__ j(EQUAL, &ok);
|
|
__ int3(); // cvttsd2siq overflow not detected
|
|
__ Bind(&ok);
|
|
__ movq(RAX, Immediate(bit_cast<int64_t, double>(4.2e11)));
|
|
__ pushq(RAX);
|
|
__ movsd(XMM9, Address(RSP, 0));
|
|
__ movsd(XMM6, Address(RSP, 0));
|
|
__ popq(RAX);
|
|
__ cvttsd2siq(R10, XMM6);
|
|
__ cvttsd2siq(RDX, XMM6);
|
|
__ cvttsd2siq(R10, XMM9);
|
|
__ cvttsd2siq(RDX, XMM9);
|
|
__ subq(RDX, R10);
|
|
__ addq(RDX, RDX);
|
|
__ addq(RDX, R10);
|
|
__ movq(RAX, RDX);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(DoubleToInt64Conversion, test) {
|
|
typedef int64_t (*DoubleToInt64ConversionCode)();
|
|
int64_t res = reinterpret_cast<DoubleToInt64ConversionCode>(test->entry())();
|
|
EXPECT_EQ(420000000000l, res);
|
|
EXPECT_DISASSEMBLY(
|
|
"movq rax,0x44a1c9a62d04ed0c\n"
|
|
"push rax\n"
|
|
"movsd xmm9,[rsp]\n"
|
|
"pop rax\n"
|
|
"cvttsd2siq rax,xmm9\n"
|
|
"movq tmp,0x8000000000000000\n"
|
|
"cmpq rax,tmp\n"
|
|
"jz +7\n"
|
|
"int3\n"
|
|
"movq rax,0x4258727cda000000\n"
|
|
"push rax\n"
|
|
"movsd xmm9,[rsp]\n"
|
|
"movsd xmm6,[rsp]\n"
|
|
"pop rax\n"
|
|
"cvttsd2siq r10,xmm6\n"
|
|
"cvttsd2siq rdx,xmm6\n"
|
|
"cvttsd2siq r10,xmm9\n"
|
|
"cvttsd2siq rdx,xmm9\n"
|
|
"subq rdx,r10\n"
|
|
"addq rdx,rdx\n"
|
|
"addq rdx,r10\n"
|
|
"movq rax,rdx\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(DoubleToInt32Conversion, assembler) {
|
|
// Check that a too big double results in the overflow value for a conversion
|
|
// to signed 32 bit.
|
|
__ movq(RAX, Immediate(bit_cast<int64_t, double>(4.2e11)));
|
|
__ pushq(RAX);
|
|
__ movsd(XMM9, Address(RSP, 0));
|
|
__ popq(RAX);
|
|
__ cvttsd2sil(RAX, XMM9);
|
|
__ CompareImmediate(RAX, Immediate(0x80000000ll));
|
|
{
|
|
Label ok;
|
|
__ j(EQUAL, &ok);
|
|
__ int3(); // cvttsd2sil overflow not detected.
|
|
__ Bind(&ok);
|
|
}
|
|
|
|
// Check that negative floats result in signed 32 bit results with the top
|
|
// bits zeroed.
|
|
__ movq(RAX, Immediate(bit_cast<int64_t, double>(-42.0)));
|
|
__ pushq(RAX);
|
|
__ movsd(XMM9, Address(RSP, 0));
|
|
__ popq(RAX);
|
|
// These high 1-bits will be zeroed in the next insn.
|
|
__ movq(R10, Immediate(-1));
|
|
// Put -42 in r10d, zeroing the high bits of r10.
|
|
__ cvttsd2sil(R10, XMM9);
|
|
__ CompareImmediate(R10, Immediate(-42 & 0xffffffffll));
|
|
{
|
|
Label ok;
|
|
__ j(EQUAL, &ok);
|
|
__ int3(); // cvttsd2sil negative result error
|
|
__ Bind(&ok);
|
|
}
|
|
|
|
// Check for correct result for positive in-range input.
|
|
__ movq(RAX, Immediate(bit_cast<int64_t, double>(42.0)));
|
|
__ pushq(RAX);
|
|
__ movsd(XMM9, Address(RSP, 0));
|
|
__ movsd(XMM6, Address(RSP, 0));
|
|
__ popq(RAX);
|
|
__ cvttsd2sil(R10, XMM6);
|
|
__ cvttsd2sil(RDX, XMM6);
|
|
__ cvttsd2sil(R10, XMM9);
|
|
__ cvttsd2sil(RDX, XMM9);
|
|
__ subq(RDX, R10);
|
|
__ addq(RDX, RDX);
|
|
__ addq(RDX, R10);
|
|
__ movq(RAX, RDX);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(DoubleToInt32Conversion, test) {
|
|
typedef int64_t (*DoubleToInt32ConversionCode)();
|
|
int64_t res = reinterpret_cast<DoubleToInt32ConversionCode>(test->entry())();
|
|
EXPECT_EQ(42, res);
|
|
EXPECT_DISASSEMBLY(
|
|
"movq rax,0x4258727cda000000\n"
|
|
"push rax\n"
|
|
"movsd xmm9,[rsp]\n"
|
|
"pop rax\n"
|
|
"cvttsd2sil rax,xmm9\n"
|
|
"movl tmp,0x80000000\n"
|
|
"cmpq rax,tmp\n"
|
|
"jz +7\n"
|
|
"int3\n"
|
|
"movq rax,0xc045000000000000\n"
|
|
"push rax\n"
|
|
"movsd xmm9,[rsp]\n"
|
|
"pop rax\n"
|
|
"movq r10,-1\n"
|
|
"cvttsd2sil r10,xmm9\n"
|
|
"movl tmp,0xffffffd6\n"
|
|
"cmpq r10,tmp\n"
|
|
"jz +7\n"
|
|
"int3\n"
|
|
"movq rax,0x4045000000000000\n"
|
|
"push rax\n"
|
|
"movsd xmm9,[rsp]\n"
|
|
"movsd xmm6,[rsp]\n"
|
|
"pop rax\n"
|
|
"cvttsd2sil r10,xmm6\n"
|
|
"cvttsd2sil rdx,xmm6\n"
|
|
"cvttsd2sil r10,xmm9\n"
|
|
"cvttsd2sil rdx,xmm9\n"
|
|
"subq rdx,r10\n"
|
|
"addq rdx,rdx\n"
|
|
"addq rdx,r10\n"
|
|
"movq rax,rdx\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(TestObjectCompare, assembler) {
|
|
ObjectStore* object_store = IsolateGroup::Current()->object_store();
|
|
const Object& obj = Object::ZoneHandle(object_store->smi_class());
|
|
Label fail;
|
|
EnterTestFrame(assembler);
|
|
__ LoadObject(RAX, obj);
|
|
__ CompareObject(RAX, obj);
|
|
__ j(NOT_EQUAL, &fail);
|
|
__ LoadObject(RCX, obj);
|
|
__ CompareObject(RCX, obj);
|
|
__ j(NOT_EQUAL, &fail);
|
|
const Smi& smi = Smi::ZoneHandle(Smi::New(15));
|
|
__ LoadObject(RCX, smi);
|
|
__ CompareObject(RCX, smi);
|
|
__ j(NOT_EQUAL, &fail);
|
|
__ pushq(RAX);
|
|
__ StoreObject(Address(RSP, 0), obj);
|
|
__ popq(RCX);
|
|
__ CompareObject(RCX, obj);
|
|
__ j(NOT_EQUAL, &fail);
|
|
__ pushq(RAX);
|
|
__ StoreObject(Address(RSP, 0), smi);
|
|
__ popq(RCX);
|
|
__ CompareObject(RCX, smi);
|
|
__ j(NOT_EQUAL, &fail);
|
|
__ movl(RAX, Immediate(1)); // OK
|
|
LeaveTestFrame(assembler);
|
|
__ ret();
|
|
__ Bind(&fail);
|
|
__ movl(RAX, Immediate(0)); // Fail.
|
|
LeaveTestFrame(assembler);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(TestObjectCompare, test) {
|
|
bool res = test->InvokeWithCodeAndThread<bool>();
|
|
EXPECT_EQ(true, res);
|
|
#if !defined(DART_COMPRESSED_POINTERS)
|
|
EXPECT_DISASSEMBLY_NOT_WINDOWS(
|
|
"push rbp\n"
|
|
"movq rbp,rsp\n"
|
|
"push r12\n"
|
|
"push pp\n"
|
|
"push thr\n"
|
|
"movq r12,rdi\n"
|
|
"movq thr,rsi\n"
|
|
"movq pp,[r12+0x27]\n"
|
|
"movq rax,[pp+0xf]\n"
|
|
"cmpq rax,[pp+0xf]\n"
|
|
"jnz +92\n"
|
|
"movq rcx,[pp+0xf]\n"
|
|
"cmpq rcx,[pp+0xf]\n"
|
|
"jnz +78\n"
|
|
"movl rcx,0x1e\n"
|
|
"cmpq rcx,0x1e\n"
|
|
"jnz +63\n"
|
|
"push rax\n"
|
|
"movq tmp,[pp+0xf]\n"
|
|
"movq [rsp],tmp\n"
|
|
"pop rcx\n"
|
|
"cmpq rcx,[pp+0xf]\n"
|
|
"jnz +43\n"
|
|
"push rax\n"
|
|
"movq [rsp],0x1e\n"
|
|
"pop rcx\n"
|
|
"cmpq rcx,0x1e\n"
|
|
"jnz +23\n"
|
|
"movl rax,1\n"
|
|
"pop thr\n"
|
|
"pop pp\n"
|
|
"pop r12\n"
|
|
"movq rsp,rbp\n"
|
|
"pop rbp\n"
|
|
"ret\n"
|
|
"movl rax,0\n"
|
|
"pop thr\n"
|
|
"pop pp\n"
|
|
"pop r12\n"
|
|
"movq rsp,rbp\n"
|
|
"pop rbp\n"
|
|
"ret\n");
|
|
#else
|
|
EXPECT_DISASSEMBLY_NOT_WINDOWS(
|
|
"push rbp\n"
|
|
"movq rbp,rsp\n"
|
|
"push r12\n"
|
|
"push pp\n"
|
|
"push thr\n"
|
|
"movq r12,rdi\n"
|
|
"movq thr,rsi\n"
|
|
"movq pp,[r12+0x27]\n"
|
|
"movq rax,[pp+0xf]\n"
|
|
"cmpl rax,[pp+0xf]\n"
|
|
"jnz +90\n"
|
|
"movq rcx,[pp+0xf]\n"
|
|
"cmpl rcx,[pp+0xf]\n"
|
|
"jnz +76\n"
|
|
"movl rcx,0x1e\n"
|
|
"cmpl rcx,0x1e\n"
|
|
"jnz +62\n"
|
|
"push rax\n"
|
|
"movq tmp,[pp+0xf]\n"
|
|
"movq [rsp],tmp\n"
|
|
"pop rcx\n"
|
|
"cmpl rcx,[pp+0xf]\n"
|
|
"jnz +42\n"
|
|
"push rax\n"
|
|
"movq [rsp],0x1e\n"
|
|
"pop rcx\n"
|
|
"cmpl rcx,0x1e\n"
|
|
"jnz +23\n"
|
|
"movl rax,1\n"
|
|
"pop thr\n"
|
|
"pop pp\n"
|
|
"pop r12\n"
|
|
"movq rsp,rbp\n"
|
|
"pop rbp\n"
|
|
"ret\n"
|
|
"movl rax,0\n"
|
|
"pop thr\n"
|
|
"pop pp\n"
|
|
"pop r12\n"
|
|
"movq rsp,rbp\n"
|
|
"pop rbp\n"
|
|
"ret\n");
|
|
#endif
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(TestNop, assembler) {
|
|
__ nop(1);
|
|
__ nop(2);
|
|
__ nop(3);
|
|
__ nop(4);
|
|
__ nop(5);
|
|
__ nop(6);
|
|
__ nop(7);
|
|
__ nop(8);
|
|
__ movq(RAX, Immediate(assembler->CodeSize())); // Return code size.
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(TestNop, test) {
|
|
typedef int (*TestNop)();
|
|
int res = reinterpret_cast<TestNop>(test->payload_start())();
|
|
EXPECT_EQ(36, res); // 36 nop bytes emitted.
|
|
EXPECT_DISASSEMBLY(
|
|
"nop\n"
|
|
"nop\n"
|
|
"nop\n"
|
|
"nop\n"
|
|
"nop\n"
|
|
"nop\n"
|
|
"nop\n"
|
|
"nop\n"
|
|
"movl rax,0x24\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(TestAlign0, assembler) {
|
|
__ Align(4, 0);
|
|
__ movq(RAX, Immediate(assembler->CodeSize())); // Return code size.
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(TestAlign0, test) {
|
|
typedef int (*TestAlign0)();
|
|
int res = reinterpret_cast<TestAlign0>(test->payload_start())();
|
|
EXPECT_EQ(0, res); // 0 bytes emitted.
|
|
EXPECT_DISASSEMBLY(
|
|
"movl rax,0\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(TestAlign1, assembler) {
|
|
__ nop(1);
|
|
__ Align(4, 0);
|
|
__ movq(RAX, Immediate(assembler->CodeSize())); // Return code size.
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(TestAlign1, test) {
|
|
typedef int (*TestAlign1)();
|
|
int res = reinterpret_cast<TestAlign1>(test->payload_start())();
|
|
EXPECT_EQ(4, res); // 4 bytes emitted.
|
|
EXPECT_DISASSEMBLY(
|
|
"nop\n"
|
|
"nop\n"
|
|
"movl rax,4\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(TestAlign1Offset1, assembler) {
|
|
__ nop(1);
|
|
__ Align(4, 1);
|
|
__ movq(RAX, Immediate(assembler->CodeSize())); // Return code size.
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(TestAlign1Offset1, test) {
|
|
typedef int (*TestAlign1Offset1)();
|
|
int res = reinterpret_cast<TestAlign1Offset1>(test->payload_start())();
|
|
EXPECT_EQ(3, res); // 3 bytes emitted.
|
|
EXPECT_DISASSEMBLY(
|
|
"nop\n"
|
|
"nop\n"
|
|
"movl rax,3\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(TestAlignLarge, assembler) {
|
|
__ nop(1);
|
|
__ Align(16, 0);
|
|
__ movq(RAX, Immediate(assembler->CodeSize())); // Return code size.
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(TestAlignLarge, test) {
|
|
typedef int (*TestAlignLarge)();
|
|
int res = reinterpret_cast<TestAlignLarge>(test->payload_start())();
|
|
EXPECT_EQ(16, res); // 16 bytes emitted.
|
|
EXPECT_DISASSEMBLY(
|
|
"nop\n"
|
|
"nop\n"
|
|
"nop\n"
|
|
"movl rax,0x10\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(TestAdds, assembler) {
|
|
__ movq(RAX, Immediate(4));
|
|
__ pushq(RAX);
|
|
__ addq(Address(RSP, 0), Immediate(5));
|
|
// TOS: 9
|
|
__ addq(Address(RSP, 0), Immediate(-2));
|
|
// TOS: 7
|
|
__ movq(RCX, Immediate(3));
|
|
__ addq(Address(RSP, 0), RCX);
|
|
// TOS: 10
|
|
__ movq(RAX, Immediate(10));
|
|
__ addq(RAX, Address(RSP, 0));
|
|
// RAX: 20
|
|
__ popq(RCX);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(TestAdds, test) {
|
|
typedef int (*TestAdds)();
|
|
int res = reinterpret_cast<TestAdds>(test->entry())();
|
|
EXPECT_EQ(20, res);
|
|
EXPECT_DISASSEMBLY(
|
|
"movl rax,4\n"
|
|
"push rax\n"
|
|
"addq [rsp],5\n"
|
|
"addq [rsp],-2\n"
|
|
"movl rcx,3\n"
|
|
"addq [rsp],rcx\n"
|
|
"movl rax,0xa\n"
|
|
"addq rax,[rsp]\n"
|
|
"pop rcx\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(TestNot, assembler) {
|
|
__ movq(RAX, Immediate(0xFFFFFFFF00000000));
|
|
__ notq(RAX);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(TestNot, test) {
|
|
typedef int (*TestNot)();
|
|
unsigned int res = reinterpret_cast<TestNot>(test->entry())();
|
|
EXPECT_EQ(0xFFFFFFFF, res);
|
|
EXPECT_DISASSEMBLY(
|
|
"movq rax,0xffffffff00000000\n"
|
|
"notq rax\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(TestNotInt32, assembler) {
|
|
__ movq(RAX, Immediate(0x0));
|
|
__ notl(RAX);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(TestNotInt32, test) {
|
|
typedef int (*TestNot)();
|
|
unsigned int res = reinterpret_cast<TestNot>(test->entry())();
|
|
EXPECT_EQ(0xFFFFFFFF, res);
|
|
EXPECT_DISASSEMBLY(
|
|
"movl rax,0\n"
|
|
"notl rax\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(XorpdZeroing, assembler) {
|
|
__ pushq(RAX);
|
|
__ movsd(Address(RSP, 0), XMM0);
|
|
__ xorpd(XMM0, Address(RSP, 0));
|
|
__ popq(RAX);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(XorpdZeroing, test) {
|
|
typedef double (*XorpdZeroingCode)(double d);
|
|
double res = reinterpret_cast<XorpdZeroingCode>(test->entry())(12.56e3);
|
|
EXPECT_FLOAT_EQ(0.0, res, 0.0001);
|
|
EXPECT_DISASSEMBLY(
|
|
"push rax\n"
|
|
"movsd [rsp],xmm0\n"
|
|
"xorpd xmm0,[rsp]\n"
|
|
"pop rax\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(XorpdZeroing2, assembler) {
|
|
Label done;
|
|
__ xorpd(XMM15, XMM15);
|
|
__ xorpd(XMM0, XMM0);
|
|
__ xorpd(XMM0, XMM15);
|
|
__ comisd(XMM0, XMM15);
|
|
__ j(ZERO, &done);
|
|
__ int3();
|
|
__ Bind(&done);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(XorpdZeroing2, test) {
|
|
typedef double (*XorpdZeroing2Code)(double d);
|
|
double res = reinterpret_cast<XorpdZeroing2Code>(test->entry())(12.56e3);
|
|
EXPECT_FLOAT_EQ(0.0, res, 0.0001);
|
|
EXPECT_DISASSEMBLY(
|
|
"xorpd xmm15,xmm15\n"
|
|
"xorpd xmm0,xmm0\n"
|
|
"xorpd xmm0,xmm15\n"
|
|
"comisd xmm0,xmm15\n"
|
|
"jz +7\n"
|
|
"int3\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(Pxor, assembler) {
|
|
__ pxor(XMM0, XMM0);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(Pxor, test) {
|
|
typedef double (*PxorCode)(double d);
|
|
double res = reinterpret_cast<PxorCode>(test->entry())(12.3456e3);
|
|
EXPECT_FLOAT_EQ(0.0, res, 0.0);
|
|
EXPECT_DISASSEMBLY(
|
|
"pxor xmm0,xmm0\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(SquareRootDouble, assembler) {
|
|
__ sqrtsd(XMM0, XMM0);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(SquareRootDouble, test) {
|
|
typedef double (*SquareRootDoubleCode)(double d);
|
|
const double kDoubleConst = .7;
|
|
double res =
|
|
reinterpret_cast<SquareRootDoubleCode>(test->entry())(kDoubleConst);
|
|
EXPECT_FLOAT_EQ(sqrt(kDoubleConst), res, 0.0001);
|
|
EXPECT_DISASSEMBLY(
|
|
"sqrtsd xmm0,xmm0\n"
|
|
"ret\n");
|
|
}
|
|
|
|
// Called from assembler_test.cc.
|
|
ASSEMBLER_TEST_GENERATE(StoreIntoObject, assembler) {
|
|
__ pushq(CODE_REG);
|
|
__ pushq(THR);
|
|
__ movq(THR, CallingConventions::kArg3Reg);
|
|
__ StoreCompressedIntoObject(CallingConventions::kArg2Reg,
|
|
FieldAddress(CallingConventions::kArg2Reg,
|
|
GrowableObjectArray::data_offset()),
|
|
CallingConventions::kArg1Reg);
|
|
__ popq(THR);
|
|
__ popq(CODE_REG);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(DoubleFPUStackMoves, assembler) {
|
|
int64_t l = bit_cast<int64_t, double>(1024.67);
|
|
__ movq(RAX, Immediate(l));
|
|
__ pushq(RAX);
|
|
__ fldl(Address(RSP, 0));
|
|
__ movq(Address(RSP, 0), Immediate(0));
|
|
__ fstpl(Address(RSP, 0));
|
|
__ popq(RAX);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(DoubleFPUStackMoves, test) {
|
|
typedef int64_t (*DoubleFPUStackMovesCode)();
|
|
int64_t res = reinterpret_cast<DoubleFPUStackMovesCode>(test->entry())();
|
|
EXPECT_FLOAT_EQ(1024.67, (bit_cast<double, int64_t>(res)), 0.001);
|
|
EXPECT_DISASSEMBLY(
|
|
"movq rax,0x409002ae147ae148\n"
|
|
"push rax\n"
|
|
"fld_d [rsp]\n"
|
|
"movq [rsp],0\n"
|
|
"fstp_d [rsp]\n"
|
|
"pop rax\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(Sine, assembler) {
|
|
__ pushq(RAX);
|
|
__ movsd(Address(RSP, 0), XMM0);
|
|
__ fldl(Address(RSP, 0));
|
|
__ fsin();
|
|
__ fstpl(Address(RSP, 0));
|
|
__ movsd(XMM0, Address(RSP, 0));
|
|
__ popq(RAX);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(Sine, test) {
|
|
typedef double (*SineCode)(double d);
|
|
const double kDoubleConst = 0.7;
|
|
double res = reinterpret_cast<SineCode>(test->entry())(kDoubleConst);
|
|
EXPECT_FLOAT_EQ(sin(kDoubleConst), res, 0.0001);
|
|
EXPECT_DISASSEMBLY(
|
|
"push rax\n"
|
|
"movsd [rsp],xmm0\n"
|
|
"fld_d [rsp]\n"
|
|
"fsin\n"
|
|
"fstp_d [rsp]\n"
|
|
"movsd xmm0,[rsp]\n"
|
|
"pop rax\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(Cosine, assembler) {
|
|
__ pushq(RAX);
|
|
__ movsd(Address(RSP, 0), XMM0);
|
|
__ fldl(Address(RSP, 0));
|
|
__ fcos();
|
|
__ fstpl(Address(RSP, 0));
|
|
__ movsd(XMM0, Address(RSP, 0));
|
|
__ popq(RAX);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(Cosine, test) {
|
|
typedef double (*CosineCode)(double f);
|
|
const double kDoubleConst = 0.7;
|
|
double res = reinterpret_cast<CosineCode>(test->entry())(kDoubleConst);
|
|
EXPECT_FLOAT_EQ(cos(kDoubleConst), res, 0.0001);
|
|
EXPECT_DISASSEMBLY(
|
|
"push rax\n"
|
|
"movsd [rsp],xmm0\n"
|
|
"fld_d [rsp]\n"
|
|
"fcos\n"
|
|
"fstp_d [rsp]\n"
|
|
"movsd xmm0,[rsp]\n"
|
|
"pop rax\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(IntToDoubleConversion, assembler) {
|
|
__ movq(RDX, Immediate(6));
|
|
__ cvtsi2sdq(XMM0, RDX);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(IntToDoubleConversion, test) {
|
|
typedef double (*IntToDoubleConversionCode)();
|
|
double res = reinterpret_cast<IntToDoubleConversionCode>(test->entry())();
|
|
EXPECT_FLOAT_EQ(6.0, res, 0.001);
|
|
EXPECT_DISASSEMBLY(
|
|
"movl rdx,6\n"
|
|
"cvtsi2sd xmm0,rdx\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(DoubleToDoubleTrunc, assembler) {
|
|
__ roundsd(XMM0, XMM0, Assembler::kRoundToZero);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(DoubleToDoubleTrunc, test) {
|
|
typedef double (*DoubleToDoubleTruncCode)(double d);
|
|
double res = reinterpret_cast<DoubleToDoubleTruncCode>(test->entry())(12.3);
|
|
EXPECT_EQ(12.0, res);
|
|
res = reinterpret_cast<DoubleToDoubleTruncCode>(test->entry())(12.8);
|
|
EXPECT_EQ(12.0, res);
|
|
res = reinterpret_cast<DoubleToDoubleTruncCode>(test->entry())(-12.3);
|
|
EXPECT_EQ(-12.0, res);
|
|
res = reinterpret_cast<DoubleToDoubleTruncCode>(test->entry())(-12.8);
|
|
EXPECT_EQ(-12.0, res);
|
|
EXPECT_DISASSEMBLY(
|
|
"roundsd rax, rax, 3\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(DoubleAbs, assembler) {
|
|
EnterTestFrame(assembler);
|
|
#if defined(DART_HOST_OS_WINDOWS)
|
|
// First argument is code object, second argument is thread. MSVC passes
|
|
// third argument in XMM2.
|
|
__ DoubleAbs(XMM0, XMM2);
|
|
#else
|
|
// SysV ABI allocates integral and double registers for arguments
|
|
// independently.
|
|
__ DoubleAbs(XMM0, XMM0);
|
|
#endif
|
|
LeaveTestFrame(assembler);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(DoubleAbs, test) {
|
|
double val = -12.45;
|
|
double res = test->InvokeWithCodeAndThread<double, double>(val);
|
|
EXPECT_FLOAT_EQ(-val, res, 0.001);
|
|
val = 12.45;
|
|
res = test->InvokeWithCodeAndThread<double, double>(val);
|
|
EXPECT_FLOAT_EQ(val, res, 0.001);
|
|
EXPECT_DISASSEMBLY_NOT_WINDOWS(
|
|
"push rbp\n"
|
|
"movq rbp,rsp\n"
|
|
"push r12\n"
|
|
"push pp\n"
|
|
"push thr\n"
|
|
"movq r12,rdi\n"
|
|
"movq thr,rsi\n"
|
|
"movq pp,[r12+0x27]\n"
|
|
"movq tmp,[thr+0x...]\n"
|
|
"andpd xmm0,[tmp]\n"
|
|
"pop thr\n"
|
|
"pop pp\n"
|
|
"pop r12\n"
|
|
"movq rsp,rbp\n"
|
|
"pop rbp\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(ExtractSignBits, assembler) {
|
|
__ movmskpd(RAX, XMM0);
|
|
__ andq(RAX, Immediate(0x1));
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(ExtractSignBits, test) {
|
|
typedef int (*ExtractSignBits)(double d);
|
|
int res = reinterpret_cast<ExtractSignBits>(test->entry())(1.0);
|
|
EXPECT_EQ(0, res);
|
|
res = reinterpret_cast<ExtractSignBits>(test->entry())(-1.0);
|
|
EXPECT_EQ(1, res);
|
|
res = reinterpret_cast<ExtractSignBits>(test->entry())(-0.0);
|
|
EXPECT_EQ(1, res);
|
|
EXPECT_DISASSEMBLY(
|
|
"movmskpd rax,xmm0\n"
|
|
"andl rax,1\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(TestSetCC, assembler) {
|
|
__ movq(RAX, Immediate(0xFFFFFFFF));
|
|
__ cmpq(RAX, RAX);
|
|
__ setcc(NOT_EQUAL, AL);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(TestSetCC, test) {
|
|
typedef uword (*TestSetCC)();
|
|
uword res = reinterpret_cast<TestSetCC>(test->entry())();
|
|
EXPECT_EQ(0xFFFFFF00, res);
|
|
EXPECT_DISASSEMBLY(
|
|
"movl rax,0xffffffff\n"
|
|
"cmpq rax,rax\n"
|
|
"setnz al\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(TestSetCC2, assembler) {
|
|
__ pushq(RBX);
|
|
__ movq(RBX, Immediate(0xFFFFFFFF));
|
|
__ cmpq(RBX, RBX);
|
|
__ setcc(EQUAL, BH);
|
|
__ movq(RAX, RBX);
|
|
__ popq(RBX);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(TestSetCC2, test) {
|
|
typedef uword (*TestSetCC)();
|
|
uword res = reinterpret_cast<TestSetCC>(test->entry())();
|
|
EXPECT_EQ(0xFFFF01FF, res);
|
|
EXPECT_DISASSEMBLY(
|
|
"push rbx\n"
|
|
"movl rbx,0xffffffff\n"
|
|
"cmpq rbx,rbx\n"
|
|
"setz bh\n"
|
|
"movq rax,rbx\n"
|
|
"pop rbx\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(TestSetCC3, assembler) {
|
|
__ pushq(R10);
|
|
__ movq(R10, Immediate(0xFFFFFFFF));
|
|
__ cmpq(R10, R10);
|
|
__ setcc(NOT_EQUAL, R10B);
|
|
__ movq(RAX, R10);
|
|
__ popq(R10);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(TestSetCC3, test) {
|
|
typedef uword (*TestSetCC)();
|
|
uword res = reinterpret_cast<TestSetCC>(test->entry())();
|
|
EXPECT_EQ(0xFFFFFF00, res);
|
|
EXPECT_DISASSEMBLY(
|
|
"push r10\n"
|
|
"movl r10,0xffffffff\n"
|
|
"cmpq r10,r10\n"
|
|
"setnz r10b\n"
|
|
"movq rax,r10\n"
|
|
"pop r10\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(TestSetCC4, assembler) {
|
|
__ pushq(RSI);
|
|
__ movq(RSI, Immediate(0xFFFFFFFF));
|
|
__ cmpq(RSI, RSI);
|
|
__ setcc(EQUAL, SIL);
|
|
__ movq(RAX, RSI);
|
|
__ popq(RSI);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(TestSetCC4, test) {
|
|
typedef uword (*TestSetCC)();
|
|
uword res = reinterpret_cast<TestSetCC>(test->entry())();
|
|
EXPECT_EQ(0xFFFFFF01, res);
|
|
EXPECT_DISASSEMBLY(
|
|
"push rsi\n"
|
|
"movl rsi,0xffffffff\n"
|
|
"cmpq rsi,rsi\n"
|
|
"setz dh\n"
|
|
"movq rax,rsi\n"
|
|
"pop rsi\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(TestRepMovsBytes, assembler) {
|
|
__ pushq(RSI);
|
|
__ pushq(RDI);
|
|
__ pushq(CallingConventions::kArg1Reg); // from.
|
|
__ pushq(CallingConventions::kArg2Reg); // to.
|
|
__ pushq(CallingConventions::kArg3Reg); // count.
|
|
__ movq(RSI, Address(RSP, 2 * target::kWordSize)); // from.
|
|
__ movq(RDI, Address(RSP, 1 * target::kWordSize)); // to.
|
|
__ movq(RCX, Address(RSP, 0 * target::kWordSize)); // count.
|
|
__ rep_movsb();
|
|
// Remove saved arguments.
|
|
__ popq(RAX);
|
|
__ popq(RAX);
|
|
__ popq(RAX);
|
|
__ popq(RDI);
|
|
__ popq(RSI);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(TestRepMovsBytes, test) {
|
|
const char* from = "0123456789x";
|
|
char* to = new char[11]{0};
|
|
to[10] = 'y';
|
|
typedef void (*TestRepMovsBytes)(const char* from, char* to, int count);
|
|
reinterpret_cast<TestRepMovsBytes>(test->entry())(from, to, 10);
|
|
EXPECT_EQ(to[0], '0');
|
|
for (int i = 0; i < 10; i++) {
|
|
EXPECT_EQ(from[i], to[i]);
|
|
}
|
|
EXPECT_EQ(to[10], 'y');
|
|
delete[] to;
|
|
EXPECT_DISASSEMBLY_NOT_WINDOWS(
|
|
"push rsi\n"
|
|
"push rdi\n"
|
|
"push rdi\n"
|
|
"push rsi\n"
|
|
"push rdx\n"
|
|
"movq rsi,[rsp+0x10]\n"
|
|
"movq rdi,[rsp+0x8]\n"
|
|
"movq rcx,[rsp]\n"
|
|
"rep movsb\n"
|
|
"pop rax\n"
|
|
"pop rax\n"
|
|
"pop rax\n"
|
|
"pop rdi\n"
|
|
"pop rsi\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(TestRepMovsWords, assembler) {
|
|
__ pushq(RSI);
|
|
__ pushq(RDI);
|
|
__ pushq(CallingConventions::kArg1Reg); // from.
|
|
__ pushq(CallingConventions::kArg2Reg); // to.
|
|
__ pushq(CallingConventions::kArg3Reg); // count.
|
|
__ movq(RSI, Address(RSP, 2 * target::kWordSize)); // from.
|
|
__ movq(RDI, Address(RSP, 1 * target::kWordSize)); // to.
|
|
__ movq(RCX, Address(RSP, 0 * target::kWordSize)); // count.
|
|
__ rep_movsw();
|
|
// Remove saved arguments.
|
|
__ popq(RAX);
|
|
__ popq(RAX);
|
|
__ popq(RAX);
|
|
__ popq(RDI);
|
|
__ popq(RSI);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(TestRepMovsWords, test) {
|
|
const uint16_t from[11] = {0x0123, 0x1234, 0x2345, 0x3456, 0x4567, 0x5678,
|
|
0x6789, 0x789A, 0x89AB, 0x9ABC, 0xABCD};
|
|
uint16_t* to = new uint16_t[11]{0};
|
|
to[10] = 0xFEFE;
|
|
typedef void (*TestRepMovsWords)(const uint16_t* from, uint16_t* to,
|
|
int count);
|
|
reinterpret_cast<TestRepMovsWords>(test->entry())(from, to, 10);
|
|
EXPECT_EQ(to[0], 0x0123u);
|
|
for (int i = 0; i < 10; i++) {
|
|
EXPECT_EQ(from[i], to[i]);
|
|
}
|
|
EXPECT_EQ(to[10], 0xFEFEu);
|
|
delete[] to;
|
|
EXPECT_DISASSEMBLY_NOT_WINDOWS(
|
|
"push rsi\n"
|
|
"push rdi\n"
|
|
"push rdi\n"
|
|
"push rsi\n"
|
|
"push rdx\n"
|
|
"movq rsi,[rsp+0x10]\n"
|
|
"movq rdi,[rsp+0x8]\n"
|
|
"movq rcx,[rsp]\n"
|
|
"rep movsw\n"
|
|
"pop rax\n"
|
|
"pop rax\n"
|
|
"pop rax\n"
|
|
"pop rdi\n"
|
|
"pop rsi\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(TestRepMovsDwords, assembler) {
|
|
__ pushq(RSI);
|
|
__ pushq(RDI);
|
|
__ pushq(CallingConventions::kArg1Reg); // from.
|
|
__ pushq(CallingConventions::kArg2Reg); // to.
|
|
__ pushq(CallingConventions::kArg3Reg); // count.
|
|
__ movq(RSI, Address(RSP, 2 * target::kWordSize)); // from.
|
|
__ movq(RDI, Address(RSP, 1 * target::kWordSize)); // to.
|
|
__ movq(RCX, Address(RSP, 0 * target::kWordSize)); // count.
|
|
__ rep_movsd();
|
|
// Remove saved arguments.
|
|
__ popq(RAX);
|
|
__ popq(RAX);
|
|
__ popq(RAX);
|
|
__ popq(RDI);
|
|
__ popq(RSI);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(TestRepMovsDwords, test) {
|
|
const uint32_t from[11] = {0x01234567, 0x12345678, 0x23456789, 0x3456789A,
|
|
0x456789AB, 0x56789ABC, 0x6789ABCD, 0x789ABCDE,
|
|
0x89ABCDEF, 0x9ABCDEF0, 0xABCDEF01};
|
|
uint32_t* to = new uint32_t[11]{0};
|
|
to[10] = 0xFEFEFEFE;
|
|
typedef void (*TestRepMovsDwords)(const uint32_t* from, uint32_t* to,
|
|
int count);
|
|
reinterpret_cast<TestRepMovsDwords>(test->entry())(from, to, 10);
|
|
EXPECT_EQ(to[0], 0x01234567u);
|
|
for (int i = 0; i < 10; i++) {
|
|
EXPECT_EQ(from[i], to[i]);
|
|
}
|
|
EXPECT_EQ(to[10], 0xFEFEFEFEu);
|
|
delete[] to;
|
|
EXPECT_DISASSEMBLY_NOT_WINDOWS(
|
|
"push rsi\n"
|
|
"push rdi\n"
|
|
"push rdi\n"
|
|
"push rsi\n"
|
|
"push rdx\n"
|
|
"movq rsi,[rsp+0x10]\n"
|
|
"movq rdi,[rsp+0x8]\n"
|
|
"movq rcx,[rsp]\n"
|
|
"rep movsl\n"
|
|
"pop rax\n"
|
|
"pop rax\n"
|
|
"pop rax\n"
|
|
"pop rdi\n"
|
|
"pop rsi\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(TestRepMovsQwords, assembler) {
|
|
__ pushq(RSI);
|
|
__ pushq(RDI);
|
|
__ pushq(CallingConventions::kArg1Reg); // from.
|
|
__ pushq(CallingConventions::kArg2Reg); // to.
|
|
__ pushq(CallingConventions::kArg3Reg); // count.
|
|
__ movq(RSI, Address(RSP, 2 * target::kWordSize)); // from.
|
|
__ movq(RDI, Address(RSP, 1 * target::kWordSize)); // to.
|
|
__ movq(RCX, Address(RSP, 0 * target::kWordSize)); // count.
|
|
__ rep_movsq();
|
|
// Remove saved arguments.
|
|
__ popq(RAX);
|
|
__ popq(RAX);
|
|
__ popq(RAX);
|
|
__ popq(RDI);
|
|
__ popq(RSI);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(TestRepMovsQwords, test) {
|
|
const uint64_t from[11] = {
|
|
0x0123456789ABCDEF, 0x123456789ABCDEF0, 0x23456789ABCDEF01,
|
|
0x3456789ABCDEF012, 0x456789ABCDEF0123, 0x56789ABCDEF01234,
|
|
0x6789ABCDEF012345, 0x789ABCDEF0123456, 0x89ABCDEF01234567,
|
|
0x9ABCDEF012345678, 0xABCDEF0123456789};
|
|
uint64_t* to = new uint64_t[11]{0};
|
|
to[10] = 0xFEFEFEFEFEFEFEFE;
|
|
typedef void (*TestRepMovsQwords)(const uint64_t* from, uint64_t* to,
|
|
int count);
|
|
reinterpret_cast<TestRepMovsQwords>(test->entry())(from, to, 10);
|
|
EXPECT_EQ(to[0], 0x0123456789ABCDEFu);
|
|
for (int i = 0; i < 10; i++) {
|
|
EXPECT_EQ(from[i], to[i]);
|
|
}
|
|
EXPECT_EQ(to[10], 0xFEFEFEFEFEFEFEFEu);
|
|
delete[] to;
|
|
EXPECT_DISASSEMBLY_NOT_WINDOWS(
|
|
"push rsi\n"
|
|
"push rdi\n"
|
|
"push rdi\n"
|
|
"push rsi\n"
|
|
"push rdx\n"
|
|
"movq rsi,[rsp+0x10]\n"
|
|
"movq rdi,[rsp+0x8]\n"
|
|
"movq rcx,[rsp]\n"
|
|
"rep movsq\n"
|
|
"pop rax\n"
|
|
"pop rax\n"
|
|
"pop rax\n"
|
|
"pop rdi\n"
|
|
"pop rsi\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(ConditionalMovesCompare, assembler) {
|
|
__ cmpq(CallingConventions::kArg1Reg, CallingConventions::kArg2Reg);
|
|
__ movq(RDX, Immediate(1)); // Greater equal.
|
|
__ movq(RCX, Immediate(-1)); // Less
|
|
__ cmovlq(RAX, RCX);
|
|
__ cmovgeq(RAX, RDX);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(ConditionalMovesCompare, test) {
|
|
typedef int (*ConditionalMovesCompareCode)(int i, int j);
|
|
int res = reinterpret_cast<ConditionalMovesCompareCode>(test->entry())(10, 5);
|
|
EXPECT_EQ(1, res); // Greater equal.
|
|
res = reinterpret_cast<ConditionalMovesCompareCode>(test->entry())(5, 5);
|
|
EXPECT_EQ(1, res); // Greater equal.
|
|
res = reinterpret_cast<ConditionalMovesCompareCode>(test->entry())(2, 5);
|
|
EXPECT_EQ(-1, res); // Less.
|
|
EXPECT_DISASSEMBLY_ENDS_WITH(
|
|
"movl rdx,1\n"
|
|
"movq rcx,-1\n"
|
|
"cmovlq rax,rcx\n"
|
|
"cmovgeq rax,rdx\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(BitTestTest, assembler) {
|
|
__ movq(RAX, Immediate(4));
|
|
__ movq(R11, Immediate(2));
|
|
__ btq(RAX, R11);
|
|
Label ok;
|
|
__ j(CARRY, &ok);
|
|
__ int3();
|
|
__ Bind(&ok);
|
|
__ movq(RAX, Immediate(1));
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(BitTestTest, test) {
|
|
typedef int (*BitTest)();
|
|
EXPECT_EQ(1, reinterpret_cast<BitTest>(test->entry())());
|
|
EXPECT_DISASSEMBLY(
|
|
"movl rax,4\n"
|
|
"movl tmp,2\n"
|
|
"btq rax,tmp\n"
|
|
"jc +7\n"
|
|
"int3\n"
|
|
"movl rax,1\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(BitTestImmediate, assembler) {
|
|
__ movq(R11, Immediate(32));
|
|
__ btq(R11, 5);
|
|
Label ok;
|
|
__ j(CARRY, &ok);
|
|
__ int3();
|
|
__ Bind(&ok);
|
|
__ movq(RAX, Immediate(1));
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(BitTestImmediate, test) {
|
|
typedef int (*BitTestImmediate)();
|
|
EXPECT_EQ(1, reinterpret_cast<BitTestImmediate>(test->entry())());
|
|
EXPECT_DISASSEMBLY(
|
|
"movl tmp,0x20\n"
|
|
"btl tmp,5\n"
|
|
"jc +7\n"
|
|
"int3\n"
|
|
"movl rax,1\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(BitTestReset, assembler) {
|
|
__ xorq(RAX, RAX);
|
|
__ lock();
|
|
__ btrq(Address(CallingConventions::kArg1Reg, 8), 1);
|
|
__ setcc(CARRY, AL);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(BitTestReset, test) {
|
|
typedef int (*BitTestReset)(int64_t*);
|
|
int64_t value[2];
|
|
value[1] = 2;
|
|
EXPECT_EQ(1, reinterpret_cast<BitTestReset>(test->entry())(&value[0]));
|
|
EXPECT_EQ(value[1], 0);
|
|
value[1] = 4;
|
|
EXPECT_EQ(0, reinterpret_cast<BitTestReset>(test->entry())(&value[0]));
|
|
EXPECT_EQ(value[1], 4);
|
|
EXPECT_DISASSEMBLY_NOT_WINDOWS(
|
|
"xorq rax,rax\n"
|
|
"lock btrq [rdi+0x8],1\n"
|
|
"setc al\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(BitTestSet, assembler) {
|
|
__ xorq(RAX, RAX);
|
|
__ lock();
|
|
__ btsq(Address(CallingConventions::kArg1Reg, 8), 1);
|
|
__ setcc(CARRY, AL);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(BitTestSet, test) {
|
|
typedef int (*BitTestSet)(int64_t*);
|
|
int64_t value[2];
|
|
value[1] = 2;
|
|
EXPECT_EQ(1, reinterpret_cast<BitTestSet>(test->entry())(&value[0]));
|
|
EXPECT_EQ(value[1], 2);
|
|
value[1] = 4;
|
|
EXPECT_EQ(0, reinterpret_cast<BitTestSet>(test->entry())(&value[0]));
|
|
EXPECT_EQ(value[1], 6);
|
|
EXPECT_DISASSEMBLY_NOT_WINDOWS(
|
|
"xorq rax,rax\n"
|
|
"lock btsq [rdi+0x8],1\n"
|
|
"setc al\n"
|
|
"ret\n");
|
|
}
|
|
|
|
// Return 1 if equal, 0 if not equal.
|
|
ASSEMBLER_TEST_GENERATE(ConditionalMovesEqual, assembler) {
|
|
__ movq(RDX, CallingConventions::kArg1Reg);
|
|
__ xorq(RAX, RAX);
|
|
__ movq(RCX, Immediate(1));
|
|
__ cmpq(RDX, Immediate(785));
|
|
__ cmoveq(RAX, RCX);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(ConditionalMovesEqual, test) {
|
|
typedef int (*ConditionalMovesEqualCode)(int i);
|
|
int res = reinterpret_cast<ConditionalMovesEqualCode>(test->entry())(785);
|
|
EXPECT_EQ(1, res);
|
|
res = reinterpret_cast<ConditionalMovesEqualCode>(test->entry())(-12);
|
|
EXPECT_EQ(0, res);
|
|
EXPECT_DISASSEMBLY_ENDS_WITH(
|
|
"xorq rax,rax\n"
|
|
"movl rcx,1\n"
|
|
"cmpq rdx,0x311\n"
|
|
"cmovzq rax,rcx\n"
|
|
"ret\n");
|
|
}
|
|
|
|
// Return 1 if overflow, 0 if no overflow.
|
|
ASSEMBLER_TEST_GENERATE(ConditionalMovesNoOverflow, assembler) {
|
|
__ movq(RDX, CallingConventions::kArg1Reg);
|
|
__ addq(RDX, CallingConventions::kArg2Reg);
|
|
__ movq(RAX, Immediate(1));
|
|
__ movq(RCX, Immediate(0));
|
|
__ cmovnoq(RAX, RCX);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(ConditionalMovesNoOverflow, test) {
|
|
typedef int (*ConditionalMovesNoOverflowCode)(int64_t i, int64_t j);
|
|
int res = reinterpret_cast<ConditionalMovesNoOverflowCode>(test->entry())(
|
|
0x7fffffffffffffff, 2);
|
|
EXPECT_EQ(1, res);
|
|
res = reinterpret_cast<ConditionalMovesNoOverflowCode>(test->entry())(1, 1);
|
|
EXPECT_EQ(0, res);
|
|
EXPECT_DISASSEMBLY_NOT_WINDOWS(
|
|
"movq rdx,rdi\n"
|
|
"addq rdx,rsi\n"
|
|
"movl rax,1\n"
|
|
"movl rcx,0\n"
|
|
"cmovnoq rax,rcx\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(ImmediateMacros, assembler) {
|
|
const intptr_t kBillion = 1000 * 1000 * 1000;
|
|
{
|
|
__ LoadImmediate(RAX, Immediate(42));
|
|
__ MulImmediate(RAX, Immediate(kBillion));
|
|
Label ok;
|
|
__ CompareImmediate(RAX, Immediate(42 * kBillion));
|
|
__ j(EQUAL, &ok);
|
|
__ int3(); // MulImmediate 64 bit.
|
|
__ Bind(&ok);
|
|
}
|
|
{
|
|
__ LoadImmediate(RAX, Immediate(42));
|
|
__ MulImmediate(RAX, Immediate(kBillion), kFourBytes);
|
|
Label ok;
|
|
__ CompareImmediate(RAX, Immediate((42 * kBillion) & 0xffffffffll));
|
|
__ j(EQUAL, &ok);
|
|
__ int3(); // MulImmediate 32 bit.
|
|
__ Bind(&ok);
|
|
}
|
|
{
|
|
__ LoadImmediate(RAX, Immediate(kBillion));
|
|
__ AddImmediate(RAX, Immediate(41 * kBillion));
|
|
Label ok;
|
|
__ CompareImmediate(RAX, Immediate(42 * kBillion));
|
|
__ j(EQUAL, &ok);
|
|
__ int3(); // AddImmediate 64 bit.
|
|
__ Bind(&ok);
|
|
}
|
|
{
|
|
__ LoadImmediate(RAX, Immediate(kBillion));
|
|
__ AddImmediate(RAX, Immediate(kBillion), kFourBytes);
|
|
__ AddImmediate(RAX, Immediate(kBillion), kFourBytes);
|
|
__ AddImmediate(RAX, Immediate(kBillion), kFourBytes);
|
|
Label ok;
|
|
__ CompareImmediate(RAX, Immediate((4 * kBillion) & 0xffffffffll));
|
|
__ j(EQUAL, &ok);
|
|
__ int3(); // AddImmediate 32 bit.
|
|
__ Bind(&ok);
|
|
}
|
|
{
|
|
__ LoadImmediate(RAX, Immediate(kBillion));
|
|
__ AddImmediate(RAX, Immediate(static_cast<int32_t>(3 * kBillion)),
|
|
kFourBytes);
|
|
__ AddImmediate(RAX, Immediate(kBillion), kFourBytes);
|
|
__ AddImmediate(RAX, Immediate(-kBillion), kFourBytes);
|
|
Label ok;
|
|
__ CompareImmediate(RAX, Immediate((4 * kBillion) & 0xffffffffll));
|
|
__ j(EQUAL, &ok);
|
|
__ int3(); // AddImmediate negative 32 bit.
|
|
__ Bind(&ok);
|
|
}
|
|
{
|
|
__ LoadImmediate(RAX, Immediate(kBillion));
|
|
__ SubImmediate(RAX, Immediate(43 * kBillion));
|
|
Label ok;
|
|
__ CompareImmediate(RAX, Immediate(-42 * kBillion));
|
|
__ j(EQUAL, &ok);
|
|
__ int3(); // AddImmediate negative 64 bit.
|
|
__ Bind(&ok);
|
|
}
|
|
{
|
|
__ LoadImmediate(RAX, Immediate(-kBillion));
|
|
__ SubImmediate(RAX, Immediate(kBillion), kFourBytes);
|
|
__ SubImmediate(RAX, Immediate(kBillion), kFourBytes);
|
|
__ SubImmediate(RAX, Immediate(kBillion), kFourBytes);
|
|
Label ok;
|
|
__ CompareImmediate(RAX, Immediate((-4 * kBillion) & 0xffffffffll));
|
|
__ j(EQUAL, &ok);
|
|
__ int3(); // SubImmediate 32 bit.
|
|
__ Bind(&ok);
|
|
}
|
|
{
|
|
__ LoadImmediate(RAX, Immediate(kBillion));
|
|
__ SubImmediate(RAX, Immediate((-3 * kBillion) & 0xffffffffll), kFourBytes);
|
|
__ SubImmediate(RAX, Immediate(kBillion), kFourBytes);
|
|
__ SubImmediate(RAX, Immediate(-kBillion), kFourBytes);
|
|
Label ok;
|
|
__ CompareImmediate(RAX, Immediate((4 * kBillion) & 0xffffffffll));
|
|
__ j(EQUAL, &ok);
|
|
__ int3(); // SubImmediate 32 bit.
|
|
__ Bind(&ok);
|
|
}
|
|
__ LoadImmediate(RAX, Immediate(42));
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(ImmediateMacros, test) {
|
|
typedef int (*ImmediateMacrosCode)();
|
|
int res = reinterpret_cast<ImmediateMacrosCode>(test->entry())();
|
|
EXPECT_EQ(42, res);
|
|
EXPECT_DISASSEMBLY(
|
|
"movl rax,0x2a\n"
|
|
"imulq rax,rax,0x3b9aca00\n"
|
|
"movq tmp,0x00000009c7652400\n"
|
|
"cmpq rax,tmp\n"
|
|
"jz +7\n"
|
|
"int3\n"
|
|
"movl rax,0x2a\n"
|
|
"imull rax,rax,0x3b9aca00\n"
|
|
"movl tmp,0xc7652400\n"
|
|
"cmpq rax,tmp\n"
|
|
"jz +7\n"
|
|
"int3\n"
|
|
"movl rax,0x3b9aca00\n"
|
|
"movq tmp,0x000000098bca5a00\n"
|
|
"addq rax,tmp\n"
|
|
"movq tmp,0x00000009c7652400\n"
|
|
"cmpq rax,tmp\n"
|
|
"jz +7\n"
|
|
"int3\n"
|
|
"movl rax,0x3b9aca00\n"
|
|
"addl rax,0x3b9aca00\n"
|
|
"addl rax,0x3b9aca00\n"
|
|
"addl rax,0x3b9aca00\n"
|
|
"movl tmp,0xee6b2800\n"
|
|
"cmpq rax,tmp\n"
|
|
"jz +7\n"
|
|
"int3\n"
|
|
"movl rax,0x3b9aca00\n"
|
|
"subl rax,0x4d2fa200\n"
|
|
"addl rax,0x3b9aca00\n"
|
|
"subl rax,0x3b9aca00\n"
|
|
"movl tmp,0xee6b2800\n"
|
|
"cmpq rax,tmp\n"
|
|
"jz +7\n"
|
|
"int3\n"
|
|
"movl rax,0x3b9aca00\n"
|
|
"movq tmp,0x0000000a02ffee00\n"
|
|
"subq rax,tmp\n"
|
|
"movq tmp,0xfffffff6389adc00\n"
|
|
"cmpq rax,tmp\n"
|
|
"jz +7\n"
|
|
"int3\n"
|
|
"movq rax,-0x3b9aca00\n"
|
|
"subl rax,0x3b9aca00\n"
|
|
"subl rax,0x3b9aca00\n"
|
|
"subl rax,0x3b9aca00\n"
|
|
"cmpq rax,0x1194d800\n"
|
|
"jz +7\n"
|
|
"int3\n"
|
|
"movl rax,0x3b9aca00\n"
|
|
"subl rax,0x4d2fa200\n"
|
|
"subl rax,0x3b9aca00\n"
|
|
"addl rax,0x3b9aca00\n"
|
|
"movl tmp,0xee6b2800\n"
|
|
"cmpq rax,tmp\n"
|
|
"jz +7\n"
|
|
"int3\n"
|
|
"movl rax,0x2a\n"
|
|
"ret\n");
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(ImmediateMacros64, assembler) {
|
|
const intptr_t kTrillion = 1000000000000;
|
|
{
|
|
__ LoadImmediate(RAX, kTrillion);
|
|
Label ok;
|
|
__ CompareImmediate(RAX, kTrillion);
|
|
__ j(EQUAL, &ok);
|
|
__ int3();
|
|
__ Bind(&ok);
|
|
}
|
|
{
|
|
__ LoadImmediate(RAX, 3);
|
|
__ AddImmediate(RAX, kTrillion);
|
|
Label ok;
|
|
__ CompareImmediate(RAX, 3 + kTrillion);
|
|
__ j(EQUAL, &ok);
|
|
__ int3();
|
|
__ Bind(&ok);
|
|
}
|
|
{
|
|
__ LoadImmediate(RAX, 5);
|
|
__ AddImmediate(RCX, RAX, kTrillion);
|
|
Label ok;
|
|
__ CompareImmediate(RCX, 5 + kTrillion);
|
|
__ j(EQUAL, &ok);
|
|
__ int3();
|
|
__ Bind(&ok);
|
|
}
|
|
__ LoadImmediate(RAX, 42);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(ImmediateMacros64, test) {
|
|
typedef int (*ImmediateMacrosCode)();
|
|
int res = reinterpret_cast<ImmediateMacrosCode>(test->entry())();
|
|
EXPECT_EQ(42, res);
|
|
EXPECT_DISASSEMBLY(
|
|
"movq rax,0x000000e8d4a51000\n"
|
|
"movq tmp,0x000000e8d4a51000\n"
|
|
"cmpq rax,tmp\n"
|
|
"jz +7\n"
|
|
"int3\n"
|
|
"movl rax,3\n"
|
|
"movq tmp,0x000000e8d4a51000\n"
|
|
"addq rax,tmp\n"
|
|
"movq tmp,0x000000e8d4a51003\n"
|
|
"cmpq rax,tmp\n"
|
|
"jz +7\n"
|
|
"int3\n"
|
|
"movl rax,5\n"
|
|
"movq rcx,0x000000e8d4a51000\n"
|
|
"addq rcx,rax\n"
|
|
"movq tmp,0x000000e8d4a51005\n"
|
|
"cmpq rcx,tmp\n"
|
|
"jz +7\n"
|
|
"int3\n"
|
|
"movl rax,0x2a\n"
|
|
"ret\n");
|
|
}
|
|
|
|
// clang-format off
|
|
#define ALU_TEST(NAME, WIDTH, INTRO, LHS, RHS, OUTRO) \
|
|
ASSEMBLER_TEST_GENERATE(NAME, assembler) { \
|
|
int32_t input1_w = static_cast<int32_t>(0x87654321); \
|
|
int32_t input1_l = input1_w; \
|
|
int64_t input1_q = 0xfedcba987654321ll; \
|
|
input1_##WIDTH += input1_w * 0 + input1_l * 0 + input1_q * 0; \
|
|
int32_t input2_w = static_cast<int32_t>(0x12345678); \
|
|
int32_t input2_l = input2_w; \
|
|
int64_t input2_q = 0xabcdef912345678ll; \
|
|
input2_##WIDTH += input2_w * 0 + input2_l * 0 + input2_q * 0; \
|
|
\
|
|
__ movq(RAX, Immediate(input1_##WIDTH)); \
|
|
__ movq(RCX, Immediate(input2_##WIDTH)); \
|
|
\
|
|
INTRO; \
|
|
\
|
|
__ and##WIDTH(LHS, RHS); \
|
|
__ or##WIDTH(RHS, LHS); \
|
|
__ xor##WIDTH(LHS, RHS); \
|
|
__ add##WIDTH(RHS, LHS); \
|
|
__ cmp##WIDTH(LHS, RHS); \
|
|
__ adc##WIDTH(LHS, RHS); \
|
|
__ sub##WIDTH(RHS, LHS); \
|
|
__ sbb##WIDTH(LHS, RHS); \
|
|
\
|
|
OUTRO; \
|
|
/* A sort of movx(RAX, RAX) */ \
|
|
__ xorq(RCX, RCX); \
|
|
__ add##WIDTH(RCX, RAX); \
|
|
__ andq(RAX, RCX); \
|
|
__ ret(); \
|
|
} \
|
|
\
|
|
ASSEMBLER_TEST_RUN(NAME, test) { \
|
|
typedef uint64_t (*NAME)(); \
|
|
uint64_t expectation_q = 0xaed1be942649381ll; \
|
|
uint32_t expectation_l = expectation_q; \
|
|
uint16_t expectation_w = expectation_l; \
|
|
uint64_t expectation = expectation_##WIDTH | expectation_w; \
|
|
EXPECT_EQ(expectation, reinterpret_cast<NAME>(test->entry())()); \
|
|
}
|
|
// clang-format on
|
|
|
|
ALU_TEST(RegRegW, w, , RAX, RCX, )
|
|
ALU_TEST(RegAddrW1, w, __ pushq(RAX), Address(RSP, 0), RCX, __ popq(RAX))
|
|
ALU_TEST(RegAddrW2, w, __ pushq(RCX), RAX, Address(RSP, 0), __ popq(RCX))
|
|
ALU_TEST(RegRegL, l, , RAX, RCX, )
|
|
ALU_TEST(RegAddrL1, l, __ pushq(RAX), Address(RSP, 0), RCX, __ popq(RAX))
|
|
ALU_TEST(RegAddrL2, l, __ pushq(RCX), RAX, Address(RSP, 0), __ popq(RCX))
|
|
ALU_TEST(RegRegQ, q, , RAX, RCX, )
|
|
ALU_TEST(RegAddrQ1, q, __ pushq(RAX), Address(RSP, 0), RCX, __ popq(RAX))
|
|
ALU_TEST(RegAddrQ2, q, __ pushq(RCX), RAX, Address(RSP, 0), __ popq(RCX))
|
|
|
|
#define IMMEDIATE_TEST(NAME, REG, MASK, INTRO, VALUE, OUTRO) \
|
|
ASSEMBLER_TEST_GENERATE(NAME, assembler) { \
|
|
__ movl(REG, Immediate(static_cast<int32_t>(0x87654321))); \
|
|
\
|
|
INTRO; \
|
|
\
|
|
__ andl(VALUE, Immediate(static_cast<int32_t>(0xa8df51d3 & MASK))); \
|
|
__ orl(VALUE, Immediate(0x1582a681 & MASK)); \
|
|
__ xorl(VALUE, Immediate(static_cast<int32_t>(0xa5a5a5a5 & MASK))); \
|
|
__ addl(VALUE, Immediate(0x7fffffff & MASK)); \
|
|
__ cmpl(VALUE, Immediate(0x40404040 & MASK)); \
|
|
__ adcl(VALUE, Immediate(0x6eeeeeee & MASK)); \
|
|
__ subl(VALUE, Immediate(0x7eeeeeee & MASK)); \
|
|
__ sbbl(VALUE, Immediate(0x6fffffff & MASK)); \
|
|
\
|
|
OUTRO; \
|
|
\
|
|
__ movl(RAX, REG); \
|
|
__ ret(); \
|
|
} \
|
|
\
|
|
ASSEMBLER_TEST_RUN(NAME, test) { \
|
|
typedef uint64_t (*NAME)(); \
|
|
unsigned expectation = MASK < 0x100 ? 0x24 : 0x30624223; \
|
|
EXPECT_EQ(expectation, reinterpret_cast<NAME>(test->entry())()); \
|
|
}
|
|
|
|
// RAX-based instructions have different encodings so we test both RAX and RCX.
|
|
// If the immediate can be encoded as one byte there is also a different
|
|
// encoding, so test that too.
|
|
IMMEDIATE_TEST(RegImmRAX, RAX, 0xffffffff, , RAX, )
|
|
IMMEDIATE_TEST(RegImmRCX, RCX, 0xffffffff, , RCX, )
|
|
IMMEDIATE_TEST(RegImmRAXByte, RAX, 0x7f, , RAX, )
|
|
IMMEDIATE_TEST(RegImmRCXByte, RCX, 0x7f, , RCX, )
|
|
IMMEDIATE_TEST(AddrImmRAX,
|
|
RAX,
|
|
0xffffffff,
|
|
__ pushq(RAX),
|
|
Address(RSP, 0),
|
|
__ popq(RAX))
|
|
IMMEDIATE_TEST(AddrImmRAXByte,
|
|
RAX,
|
|
0x7f,
|
|
__ pushq(RAX),
|
|
Address(RSP, 0),
|
|
__ popq(RAX))
|
|
|
|
ASSEMBLER_TEST_GENERATE(StoreReleaseLoadAcquire, assembler) {
|
|
// On TSAN builds StoreRelease/LoadAcquire will do a runtime
|
|
// call to tell TSAN about our action.
|
|
__ pushq(THR);
|
|
__ MoveRegister(THR, CallingConventions::kArg2Reg);
|
|
__ pushq(RCX);
|
|
__ xorq(RCX, RCX);
|
|
__ pushq(RCX);
|
|
__ movq(RCX, RSP);
|
|
__ StoreReleaseToOffset(CallingConventions::kArg3Reg, RCX, 0);
|
|
__ LoadAcquireFromOffset(CallingConventions::kReturnReg, RCX, 0);
|
|
__ popq(RCX);
|
|
__ popq(RCX);
|
|
__ popq(THR);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(StoreReleaseLoadAcquire, test) {
|
|
const intptr_t res = test->InvokeWithCodeAndThread<intptr_t>(123);
|
|
EXPECT_EQ(123, res);
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(StoreReleaseLoadAcquire1024, assembler) {
|
|
// On TSAN builds StoreRelease/LoadAcquire will do a runtime
|
|
// call to tell TSAN about our action.
|
|
__ pushq(THR);
|
|
__ MoveRegister(THR, CallingConventions::kArg2Reg);
|
|
__ pushq(RCX);
|
|
__ xorq(RCX, RCX);
|
|
__ pushq(RCX);
|
|
__ subq(RSP, Immediate(1024));
|
|
__ movq(RCX, RSP);
|
|
__ StoreReleaseToOffset(CallingConventions::kArg3Reg, RCX, 1024);
|
|
__ LoadAcquireFromOffset(CallingConventions::kReturnReg, RCX, 1024);
|
|
__ addq(RSP, Immediate(1024));
|
|
__ popq(RCX);
|
|
__ popq(RCX);
|
|
__ popq(THR);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(StoreReleaseLoadAcquire1024, test) {
|
|
const intptr_t res = test->InvokeWithCodeAndThread<intptr_t>(123);
|
|
EXPECT_EQ(123, res);
|
|
if (!FLAG_target_thread_sanitizer) {
|
|
EXPECT_DISASSEMBLY_NOT_WINDOWS(
|
|
"push thr\n"
|
|
"movq thr,rsi\n"
|
|
"push rcx\n"
|
|
"xorq rcx,rcx\n"
|
|
"push rcx\n"
|
|
"subq rsp,0x400\n"
|
|
"movq rcx,rsp\n"
|
|
"movq [rcx+0x400],rdx\n"
|
|
"movq rax,[rcx+0x400]\n"
|
|
"addq rsp,0x400\n"
|
|
"pop rcx\n"
|
|
"pop rcx\n"
|
|
"pop thr\n"
|
|
"ret\n");
|
|
}
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(MoveByteRunTest, assembler) {
|
|
__ pushq(Immediate(0x1234567887654321));
|
|
// RAX / AL
|
|
__ xorq(CallingConventions::kReturnReg, CallingConventions::kReturnReg);
|
|
__ movb(ByteRegisterOf(CallingConventions::kReturnReg), Address(RSP, 0));
|
|
// RBX / BH
|
|
__ pushq(RBX);
|
|
__ xorq(RBX, RBX);
|
|
__ movb(BH, Address(RSP, target::kWordSize));
|
|
__ shrq(RBX, Immediate(8));
|
|
__ addq(CallingConventions::kReturnReg, RBX);
|
|
__ popq(RBX);
|
|
// R8 / R8B
|
|
ASSERT((CallingConventions::kVolatileCpuRegisters & (1 << R8)) != 0);
|
|
__ xorq(R8, R8);
|
|
__ movb(R8B, Address(RSP, 0));
|
|
__ addq(CallingConventions::kReturnReg, R8);
|
|
// RDI / DIL
|
|
__ pushq(RDI);
|
|
__ xorq(RDI, RDI);
|
|
__ movb(DIL, Address(RSP, target::kWordSize));
|
|
__ addq(CallingConventions::kReturnReg, RDI);
|
|
__ popq(RDI);
|
|
__ Drop(1);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(MoveByteRunTest, test) {
|
|
intptr_t res = test->InvokeWithCodeAndThread<intptr_t>();
|
|
EXPECT_EQ(0x21 + 0x21 + 0x21 + 0x21, res);
|
|
}
|
|
|
|
static void RangeCheck(Assembler* assembler, Register value, Register temp) {
|
|
const Register return_reg = CallingConventions::kReturnReg;
|
|
Label in_range;
|
|
__ RangeCheck(value, temp, kFirstErrorCid, kLastErrorCid,
|
|
AssemblerBase::kIfInRange, &in_range);
|
|
__ movq(return_reg, Immediate(0));
|
|
__ ret();
|
|
__ Bind(&in_range);
|
|
__ movq(return_reg, Immediate(1));
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(RangeCheckNoTemp, assembler) {
|
|
const Register value = CallingConventions::kArg1Reg;
|
|
const Register temp = kNoRegister;
|
|
RangeCheck(assembler, value, temp);
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(RangeCheckNoTemp, test) {
|
|
intptr_t result;
|
|
result = test->Invoke<intptr_t, intptr_t>(kErrorCid);
|
|
EXPECT_EQ(1, result);
|
|
result = test->Invoke<intptr_t, intptr_t>(kUnwindErrorCid);
|
|
EXPECT_EQ(1, result);
|
|
result = test->Invoke<intptr_t, intptr_t>(kFunctionCid);
|
|
EXPECT_EQ(0, result);
|
|
result = test->Invoke<intptr_t, intptr_t>(kMintCid);
|
|
EXPECT_EQ(0, result);
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(RangeCheckWithTemp, assembler) {
|
|
const Register value = CallingConventions::kArg1Reg;
|
|
const Register temp = CallingConventions::kArg2Reg;
|
|
RangeCheck(assembler, value, temp);
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(RangeCheckWithTemp, test) {
|
|
intptr_t result;
|
|
result = test->Invoke<intptr_t, intptr_t>(kErrorCid);
|
|
EXPECT_EQ(1, result);
|
|
result = test->Invoke<intptr_t, intptr_t>(kUnwindErrorCid);
|
|
EXPECT_EQ(1, result);
|
|
result = test->Invoke<intptr_t, intptr_t>(kFunctionCid);
|
|
EXPECT_EQ(0, result);
|
|
result = test->Invoke<intptr_t, intptr_t>(kMintCid);
|
|
EXPECT_EQ(0, result);
|
|
}
|
|
|
|
ASSEMBLER_TEST_GENERATE(RangeCheckWithTempReturnValue, assembler) {
|
|
const Register value = CallingConventions::kArg1Reg;
|
|
const Register temp = CallingConventions::kArg2Reg;
|
|
const Register return_reg = CallingConventions::kReturnReg;
|
|
Label in_range;
|
|
__ RangeCheck(value, temp, kFirstErrorCid, kLastErrorCid,
|
|
AssemblerBase::kIfInRange, &in_range);
|
|
__ Bind(&in_range);
|
|
__ movq(return_reg, value);
|
|
__ ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(RangeCheckWithTempReturnValue, test) {
|
|
intptr_t result;
|
|
result = test->Invoke<intptr_t, intptr_t>(kErrorCid);
|
|
EXPECT_EQ(kErrorCid, result);
|
|
result = test->Invoke<intptr_t, intptr_t>(kUnwindErrorCid);
|
|
EXPECT_EQ(kUnwindErrorCid, result);
|
|
result = test->Invoke<intptr_t, intptr_t>(kFunctionCid);
|
|
EXPECT_EQ(kFunctionCid, result);
|
|
result = test->Invoke<intptr_t, intptr_t>(kMintCid);
|
|
EXPECT_EQ(kMintCid, result);
|
|
}
|
|
|
|
// Tests that BranchLink only clobbers CODE_REG in JIT mode and does not
|
|
// clobber any allocatable registers in AOT mode.
|
|
ASSEMBLER_TEST_GENERATE(CallCodePreservesRegisters, assembler) {
|
|
const auto& do_nothing_just_return =
|
|
AssemblerTest::Generate("DoNothing", [](auto assembler) { __ ret(); });
|
|
|
|
EnterTestFrame(assembler);
|
|
|
|
const RegisterSet clobbered_regs(
|
|
kDartAvailableCpuRegs & ~(static_cast<RegList>(1) << RAX),
|
|
/*fpu_register_mask=*/0);
|
|
__ PushRegisters(clobbered_regs);
|
|
|
|
Label done;
|
|
|
|
const auto check_all_allocatable_registers_are_preserved_by_call = [&]() {
|
|
for (auto reg : RegisterRange(kDartAvailableCpuRegs)) {
|
|
__ LoadImmediate(reg, static_cast<int32_t>(reg));
|
|
}
|
|
__ Call(do_nothing_just_return);
|
|
for (auto reg : RegisterRange(kDartAvailableCpuRegs)) {
|
|
// We expect CODE_REG to be clobbered in JIT mode.
|
|
if (!FLAG_precompiled_mode && reg == CODE_REG) continue;
|
|
|
|
Label ok;
|
|
__ CompareImmediate(reg, static_cast<int32_t>(reg));
|
|
__ j(EQUAL, &ok);
|
|
__ LoadImmediate(RAX, reg);
|
|
__ jmp(&done);
|
|
__ Bind(&ok);
|
|
}
|
|
};
|
|
|
|
check_all_allocatable_registers_are_preserved_by_call();
|
|
|
|
FLAG_precompiled_mode = true;
|
|
check_all_allocatable_registers_are_preserved_by_call();
|
|
FLAG_precompiled_mode = false;
|
|
|
|
__ LoadImmediate(RAX, 42); // 42 is SUCCESS.
|
|
__ Bind(&done);
|
|
__ PopRegisters(clobbered_regs);
|
|
LeaveTestFrame(assembler);
|
|
__ Ret();
|
|
}
|
|
|
|
ASSEMBLER_TEST_RUN(CallCodePreservesRegisters, test) {
|
|
const intptr_t result = test->InvokeWithCodeAndThread<int64_t>();
|
|
EXPECT_EQ(42, result);
|
|
}
|
|
|
|
void CheckIsIdentity(Expect& expect, AssemblerTest* test) {
|
|
for (size_t i = 0; i < ARRAY_SIZE(kRegRegImmInputs); ++i) {
|
|
const intptr_t input = kRegRegImmInputs[i];
|
|
const intptr_t result = test->Invoke<intptr_t, intptr_t>(input);
|
|
expect.Equals(input, result);
|
|
}
|
|
}
|
|
|
|
void CheckMovesInputToReturnOnly(Expect& expect,
|
|
AssemblerTest* test,
|
|
bool is_direct) {
|
|
auto* const input_reg_name = cpu_reg_names[RegRegImmTests::kInputReg];
|
|
auto* const ret_reg_name = cpu_reg_names[RegRegImmTests::kReturnReg];
|
|
auto* const temp_reg_name = cpu_reg_names[TMP];
|
|
ZoneTextBuffer buf(Thread::Current()->zone());
|
|
if (!is_direct) {
|
|
RELEASE_ASSERT(RegRegImmTests::kInputReg != TMP);
|
|
buf.Printf("movq %s,%s\n", temp_reg_name, input_reg_name);
|
|
}
|
|
RELEASE_ASSERT(RegRegImmTests::kInputReg != RegRegImmTests::kReturnReg);
|
|
RELEASE_ASSERT(RegRegImmTests::kReturnReg != TMP);
|
|
buf.Printf("movq %s,%s\n", ret_reg_name,
|
|
is_direct ? input_reg_name : temp_reg_name);
|
|
buf.AddString("ret\n");
|
|
expect.StringEquals(buf.buffer(), test->RelativeDisassembly());
|
|
}
|
|
|
|
#define GENERATE_ASSEMBLER_IDENTITY_TEST(inst, rhs) \
|
|
ASSEMBLER_TEST_GENERATE(inst##_##rhs##_Identity_Direct, assembler) { \
|
|
__ inst(RegRegImmTests::kInputReg, RegRegImmTests::kInputReg, rhs, \
|
|
kWordBytes); \
|
|
__ MoveRegister(RegRegImmTests::kReturnReg, RegRegImmTests::kInputReg); \
|
|
__ Ret(); \
|
|
} \
|
|
ASSEMBLER_TEST_RUN(inst##_##rhs##_Identity_Direct, test) { \
|
|
Expect expect(__FILE__, __LINE__); \
|
|
CheckIsIdentity(expect, test); \
|
|
CheckMovesInputToReturnOnly(expect, test, /*is_direct=*/true); \
|
|
} \
|
|
ASSEMBLER_TEST_GENERATE(inst##_##rhs##_Identity_Indirect, assembler) { \
|
|
__ inst(TMP, RegRegImmTests::kInputReg, rhs, kWordBytes); \
|
|
__ MoveRegister(RegRegImmTests::kReturnReg, TMP); \
|
|
__ Ret(); \
|
|
} \
|
|
ASSEMBLER_TEST_RUN(inst##_##rhs##_Identity_Indirect, test) { \
|
|
Expect expect(__FILE__, __LINE__); \
|
|
CheckIsIdentity(expect, test); \
|
|
CheckMovesInputToReturnOnly(expect, test, /*is_direct=*/false); \
|
|
}
|
|
|
|
GENERATE_ASSEMBLER_IDENTITY_TEST(AndImmediate, kMaxUint64)
|
|
GENERATE_ASSEMBLER_IDENTITY_TEST(LslImmediate, 0)
|
|
GENERATE_ASSEMBLER_IDENTITY_TEST(ArithmeticShiftRightImmediate, 0)
|
|
|
|
#undef GENERATE_ASSEMBLER_IDENTITY_TEST
|
|
|
|
intptr_t RegRegImmTests::Lsl(intptr_t value, intptr_t shift, OperandSize sz) {
|
|
// For non-word sizes, the result is always zero extended to reduce
|
|
// instruction count.
|
|
return ZeroExtendValue(static_cast<uintptr_t>(value) << shift, sz);
|
|
}
|
|
|
|
intptr_t RegRegImmTests::Asr(intptr_t value, intptr_t shift, OperandSize sz) {
|
|
// For non-word sizes, the result is always zero extended to reduce
|
|
// instruction count.
|
|
return ZeroExtendValue(SignExtendValue(value, sz) >> shift, sz);
|
|
}
|
|
|
|
} // namespace compiler
|
|
} // namespace dart
|
|
|
|
#endif // defined TARGET_ARCH_X64
|