| Index: src/a64/assembler-a64.h
|
| diff --git a/src/a64/assembler-a64.h b/src/a64/assembler-a64.h
|
| deleted file mode 100644
|
| index 3da4382b79aa8dfc9f028e6671e01c03e6bb3716..0000000000000000000000000000000000000000
|
| --- a/src/a64/assembler-a64.h
|
| +++ /dev/null
|
| @@ -1,2223 +0,0 @@
|
| -// Copyright 2013 the V8 project authors. All rights reserved.
|
| -// Redistribution and use in source and binary forms, with or without
|
| -// modification, are permitted provided that the following conditions are
|
| -// met:
|
| -//
|
| -// * Redistributions of source code must retain the above copyright
|
| -// notice, this list of conditions and the following disclaimer.
|
| -// * Redistributions in binary form must reproduce the above
|
| -// copyright notice, this list of conditions and the following
|
| -// disclaimer in the documentation and/or other materials provided
|
| -// with the distribution.
|
| -// * Neither the name of Google Inc. nor the names of its
|
| -// contributors may be used to endorse or promote products derived
|
| -// from this software without specific prior written permission.
|
| -//
|
| -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
| -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
| -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
| -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
| -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
| -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
| -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
| -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
| -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
| -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
| -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
| -
|
| -#ifndef V8_A64_ASSEMBLER_A64_H_
|
| -#define V8_A64_ASSEMBLER_A64_H_
|
| -
|
| -#include <list>
|
| -#include <map>
|
| -
|
| -#include "globals.h"
|
| -#include "utils.h"
|
| -#include "assembler.h"
|
| -#include "serialize.h"
|
| -#include "a64/instructions-a64.h"
|
| -#include "a64/cpu-a64.h"
|
| -
|
| -
|
| -namespace v8 {
|
| -namespace internal {
|
| -
|
| -
|
| -// -----------------------------------------------------------------------------
|
| -// Registers.
|
| -#define REGISTER_CODE_LIST(R) \
|
| -R(0) R(1) R(2) R(3) R(4) R(5) R(6) R(7) \
|
| -R(8) R(9) R(10) R(11) R(12) R(13) R(14) R(15) \
|
| -R(16) R(17) R(18) R(19) R(20) R(21) R(22) R(23) \
|
| -R(24) R(25) R(26) R(27) R(28) R(29) R(30) R(31)
|
| -
|
| -
|
| -static const int kRegListSizeInBits = sizeof(RegList) * kBitsPerByte;
|
| -
|
| -
|
| -// Some CPURegister methods can return Register and FPRegister types, so we
|
| -// need to declare them in advance.
|
| -struct Register;
|
| -struct FPRegister;
|
| -
|
| -
|
| -struct CPURegister {
|
| - enum RegisterType {
|
| - // The kInvalid value is used to detect uninitialized static instances,
|
| - // which are always zero-initialized before any constructors are called.
|
| - kInvalid = 0,
|
| - kRegister,
|
| - kFPRegister,
|
| - kNoRegister
|
| - };
|
| -
|
| - static CPURegister Create(unsigned code, unsigned size, RegisterType type) {
|
| - CPURegister r = {code, size, type};
|
| - return r;
|
| - }
|
| -
|
| - unsigned code() const;
|
| - RegisterType type() const;
|
| - RegList Bit() const;
|
| - unsigned SizeInBits() const;
|
| - int SizeInBytes() const;
|
| - bool Is32Bits() const;
|
| - bool Is64Bits() const;
|
| - bool IsValid() const;
|
| - bool IsValidOrNone() const;
|
| - bool IsValidRegister() const;
|
| - bool IsValidFPRegister() const;
|
| - bool IsNone() const;
|
| - bool Is(const CPURegister& other) const;
|
| -
|
| - bool IsZero() const;
|
| - bool IsSP() const;
|
| -
|
| - bool IsRegister() const;
|
| - bool IsFPRegister() const;
|
| -
|
| - Register X() const;
|
| - Register W() const;
|
| - FPRegister D() const;
|
| - FPRegister S() const;
|
| -
|
| - bool IsSameSizeAndType(const CPURegister& other) const;
|
| -
|
| - // V8 compatibility.
|
| - bool is(const CPURegister& other) const { return Is(other); }
|
| - bool is_valid() const { return IsValid(); }
|
| -
|
| - unsigned reg_code;
|
| - unsigned reg_size;
|
| - RegisterType reg_type;
|
| -};
|
| -
|
| -
|
| -struct Register : public CPURegister {
|
| - static Register Create(unsigned code, unsigned size) {
|
| - return CPURegister::Create(code, size, CPURegister::kRegister);
|
| - }
|
| -
|
| - Register() {
|
| - reg_code = 0;
|
| - reg_size = 0;
|
| - reg_type = CPURegister::kNoRegister;
|
| - }
|
| -
|
| - Register(const CPURegister& r) { // NOLINT(runtime/explicit)
|
| - reg_code = r.reg_code;
|
| - reg_size = r.reg_size;
|
| - reg_type = r.reg_type;
|
| - ASSERT(IsValidOrNone());
|
| - }
|
| -
|
| - bool IsValid() const {
|
| - ASSERT(IsRegister() || IsNone());
|
| - return IsValidRegister();
|
| - }
|
| -
|
| - static Register XRegFromCode(unsigned code);
|
| - static Register WRegFromCode(unsigned code);
|
| -
|
| - // Start of V8 compatibility section ---------------------
|
| - // These memebers are necessary for compilation.
|
| - // A few of them may be unused for now.
|
| -
|
| - static const int kNumRegisters = kNumberOfRegisters;
|
| - static int NumRegisters() { return kNumRegisters; }
|
| -
|
| - // We allow crankshaft to use the following registers:
|
| - // - x0 to x15
|
| - // - x18 to x24
|
| - // - x27 (also context)
|
| - //
|
| - // TODO(all): Register x25 is currently free and could be available for
|
| - // crankshaft, but we don't use it as we might use it as a per function
|
| - // literal pool pointer in the future.
|
| - //
|
| - // TODO(all): Consider storing cp in x25 to have only two ranges.
|
| - // We split allocatable registers in three ranges called
|
| - // - "low range"
|
| - // - "high range"
|
| - // - "context"
|
| - static const unsigned kAllocatableLowRangeBegin = 0;
|
| - static const unsigned kAllocatableLowRangeEnd = 15;
|
| - static const unsigned kAllocatableHighRangeBegin = 18;
|
| - static const unsigned kAllocatableHighRangeEnd = 24;
|
| - static const unsigned kAllocatableContext = 27;
|
| -
|
| - // Gap between low and high ranges.
|
| - static const int kAllocatableRangeGapSize =
|
| - (kAllocatableHighRangeBegin - kAllocatableLowRangeEnd) - 1;
|
| -
|
| - static const int kMaxNumAllocatableRegisters =
|
| - (kAllocatableLowRangeEnd - kAllocatableLowRangeBegin + 1) +
|
| - (kAllocatableHighRangeEnd - kAllocatableHighRangeBegin + 1) + 1; // cp
|
| - static int NumAllocatableRegisters() { return kMaxNumAllocatableRegisters; }
|
| -
|
| - // Return true if the register is one that crankshaft can allocate.
|
| - bool IsAllocatable() const {
|
| - return ((reg_code == kAllocatableContext) ||
|
| - (reg_code <= kAllocatableLowRangeEnd) ||
|
| - ((reg_code >= kAllocatableHighRangeBegin) &&
|
| - (reg_code <= kAllocatableHighRangeEnd)));
|
| - }
|
| -
|
| - static Register FromAllocationIndex(unsigned index) {
|
| - ASSERT(index < static_cast<unsigned>(NumAllocatableRegisters()));
|
| - // cp is the last allocatable register.
|
| - if (index == (static_cast<unsigned>(NumAllocatableRegisters() - 1))) {
|
| - return from_code(kAllocatableContext);
|
| - }
|
| -
|
| - // Handle low and high ranges.
|
| - return (index <= kAllocatableLowRangeEnd)
|
| - ? from_code(index)
|
| - : from_code(index + kAllocatableRangeGapSize);
|
| - }
|
| -
|
| - static const char* AllocationIndexToString(int index) {
|
| - ASSERT((index >= 0) && (index < NumAllocatableRegisters()));
|
| - ASSERT((kAllocatableLowRangeBegin == 0) &&
|
| - (kAllocatableLowRangeEnd == 15) &&
|
| - (kAllocatableHighRangeBegin == 18) &&
|
| - (kAllocatableHighRangeEnd == 24) &&
|
| - (kAllocatableContext == 27));
|
| - const char* const names[] = {
|
| - "x0", "x1", "x2", "x3", "x4",
|
| - "x5", "x6", "x7", "x8", "x9",
|
| - "x10", "x11", "x12", "x13", "x14",
|
| - "x15", "x18", "x19", "x20", "x21",
|
| - "x22", "x23", "x24", "x27",
|
| - };
|
| - return names[index];
|
| - }
|
| -
|
| - static int ToAllocationIndex(Register reg) {
|
| - ASSERT(reg.IsAllocatable());
|
| - unsigned code = reg.code();
|
| - if (code == kAllocatableContext) {
|
| - return NumAllocatableRegisters() - 1;
|
| - }
|
| -
|
| - return (code <= kAllocatableLowRangeEnd)
|
| - ? code
|
| - : code - kAllocatableRangeGapSize;
|
| - }
|
| -
|
| - static Register from_code(int code) {
|
| - // Always return an X register.
|
| - return Register::Create(code, kXRegSizeInBits);
|
| - }
|
| -
|
| - // End of V8 compatibility section -----------------------
|
| -};
|
| -
|
| -
|
| -struct FPRegister : public CPURegister {
|
| - static FPRegister Create(unsigned code, unsigned size) {
|
| - return CPURegister::Create(code, size, CPURegister::kFPRegister);
|
| - }
|
| -
|
| - FPRegister() {
|
| - reg_code = 0;
|
| - reg_size = 0;
|
| - reg_type = CPURegister::kNoRegister;
|
| - }
|
| -
|
| - FPRegister(const CPURegister& r) { // NOLINT(runtime/explicit)
|
| - reg_code = r.reg_code;
|
| - reg_size = r.reg_size;
|
| - reg_type = r.reg_type;
|
| - ASSERT(IsValidOrNone());
|
| - }
|
| -
|
| - bool IsValid() const {
|
| - ASSERT(IsFPRegister() || IsNone());
|
| - return IsValidFPRegister();
|
| - }
|
| -
|
| - static FPRegister SRegFromCode(unsigned code);
|
| - static FPRegister DRegFromCode(unsigned code);
|
| -
|
| - // Start of V8 compatibility section ---------------------
|
| - static const int kMaxNumRegisters = kNumberOfFPRegisters;
|
| -
|
| - // Crankshaft can use all the FP registers except:
|
| - // - d15 which is used to keep the 0 double value
|
| - // - d30 which is used in crankshaft as a double scratch register
|
| - // - d31 which is used in the MacroAssembler as a double scratch register
|
| - static const unsigned kAllocatableLowRangeBegin = 0;
|
| - static const unsigned kAllocatableLowRangeEnd = 14;
|
| - static const unsigned kAllocatableHighRangeBegin = 16;
|
| - static const unsigned kAllocatableHighRangeEnd = 29;
|
| -
|
| - static const RegList kAllocatableFPRegisters = 0x3fff7fff;
|
| -
|
| - // Gap between low and high ranges.
|
| - static const int kAllocatableRangeGapSize =
|
| - (kAllocatableHighRangeBegin - kAllocatableLowRangeEnd) - 1;
|
| -
|
| - static const int kMaxNumAllocatableRegisters =
|
| - (kAllocatableLowRangeEnd - kAllocatableLowRangeBegin + 1) +
|
| - (kAllocatableHighRangeEnd - kAllocatableHighRangeBegin + 1);
|
| - static int NumAllocatableRegisters() { return kMaxNumAllocatableRegisters; }
|
| -
|
| - // Return true if the register is one that crankshaft can allocate.
|
| - bool IsAllocatable() const {
|
| - return (Bit() & kAllocatableFPRegisters) != 0;
|
| - }
|
| -
|
| - static FPRegister FromAllocationIndex(unsigned int index) {
|
| - ASSERT(index < static_cast<unsigned>(NumAllocatableRegisters()));
|
| -
|
| - return (index <= kAllocatableLowRangeEnd)
|
| - ? from_code(index)
|
| - : from_code(index + kAllocatableRangeGapSize);
|
| - }
|
| -
|
| - static const char* AllocationIndexToString(int index) {
|
| - ASSERT((index >= 0) && (index < NumAllocatableRegisters()));
|
| - ASSERT((kAllocatableLowRangeBegin == 0) &&
|
| - (kAllocatableLowRangeEnd == 14) &&
|
| - (kAllocatableHighRangeBegin == 16) &&
|
| - (kAllocatableHighRangeEnd == 29));
|
| - const char* const names[] = {
|
| - "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7",
|
| - "d8", "d9", "d10", "d11", "d12", "d13", "d14",
|
| - "d16", "d17", "d18", "d19", "d20", "d21", "d22", "d23",
|
| - "d24", "d25", "d26", "d27", "d28", "d29"
|
| - };
|
| - return names[index];
|
| - }
|
| -
|
| - static int ToAllocationIndex(FPRegister reg) {
|
| - ASSERT(reg.IsAllocatable());
|
| - unsigned code = reg.code();
|
| -
|
| - return (code <= kAllocatableLowRangeEnd)
|
| - ? code
|
| - : code - kAllocatableRangeGapSize;
|
| - }
|
| -
|
| - static FPRegister from_code(int code) {
|
| - // Always return a D register.
|
| - return FPRegister::Create(code, kDRegSizeInBits);
|
| - }
|
| - // End of V8 compatibility section -----------------------
|
| -};
|
| -
|
| -
|
| -STATIC_ASSERT(sizeof(CPURegister) == sizeof(Register));
|
| -STATIC_ASSERT(sizeof(CPURegister) == sizeof(FPRegister));
|
| -
|
| -
|
| -#if defined(A64_DEFINE_REG_STATICS)
|
| -#define INITIALIZE_REGISTER(register_class, name, code, size, type) \
|
| - const CPURegister init_##register_class##_##name = {code, size, type}; \
|
| - const register_class& name = *reinterpret_cast<const register_class*>( \
|
| - &init_##register_class##_##name)
|
| -#define ALIAS_REGISTER(register_class, alias, name) \
|
| - const register_class& alias = *reinterpret_cast<const register_class*>( \
|
| - &init_##register_class##_##name)
|
| -#else
|
| -#define INITIALIZE_REGISTER(register_class, name, code, size, type) \
|
| - extern const register_class& name
|
| -#define ALIAS_REGISTER(register_class, alias, name) \
|
| - extern const register_class& alias
|
| -#endif // defined(A64_DEFINE_REG_STATICS)
|
| -
|
| -// No*Reg is used to indicate an unused argument, or an error case. Note that
|
| -// these all compare equal (using the Is() method). The Register and FPRegister
|
| -// variants are provided for convenience.
|
| -INITIALIZE_REGISTER(Register, NoReg, 0, 0, CPURegister::kNoRegister);
|
| -INITIALIZE_REGISTER(FPRegister, NoFPReg, 0, 0, CPURegister::kNoRegister);
|
| -INITIALIZE_REGISTER(CPURegister, NoCPUReg, 0, 0, CPURegister::kNoRegister);
|
| -
|
| -// v8 compatibility.
|
| -INITIALIZE_REGISTER(Register, no_reg, 0, 0, CPURegister::kNoRegister);
|
| -
|
| -#define DEFINE_REGISTERS(N) \
|
| - INITIALIZE_REGISTER(Register, w##N, N, \
|
| - kWRegSizeInBits, CPURegister::kRegister); \
|
| - INITIALIZE_REGISTER(Register, x##N, N, \
|
| - kXRegSizeInBits, CPURegister::kRegister);
|
| -REGISTER_CODE_LIST(DEFINE_REGISTERS)
|
| -#undef DEFINE_REGISTERS
|
| -
|
| -INITIALIZE_REGISTER(Register, wcsp, kSPRegInternalCode, kWRegSizeInBits,
|
| - CPURegister::kRegister);
|
| -INITIALIZE_REGISTER(Register, csp, kSPRegInternalCode, kXRegSizeInBits,
|
| - CPURegister::kRegister);
|
| -
|
| -#define DEFINE_FPREGISTERS(N) \
|
| - INITIALIZE_REGISTER(FPRegister, s##N, N, \
|
| - kSRegSizeInBits, CPURegister::kFPRegister); \
|
| - INITIALIZE_REGISTER(FPRegister, d##N, N, \
|
| - kDRegSizeInBits, CPURegister::kFPRegister);
|
| -REGISTER_CODE_LIST(DEFINE_FPREGISTERS)
|
| -#undef DEFINE_FPREGISTERS
|
| -
|
| -#undef INITIALIZE_REGISTER
|
| -
|
| -// Registers aliases.
|
| -ALIAS_REGISTER(Register, ip0, x16);
|
| -ALIAS_REGISTER(Register, ip1, x17);
|
| -ALIAS_REGISTER(Register, wip0, w16);
|
| -ALIAS_REGISTER(Register, wip1, w17);
|
| -// Root register.
|
| -ALIAS_REGISTER(Register, root, x26);
|
| -ALIAS_REGISTER(Register, rr, x26);
|
| -// Context pointer register.
|
| -ALIAS_REGISTER(Register, cp, x27);
|
| -// We use a register as a JS stack pointer to overcome the restriction on the
|
| -// architectural SP alignment.
|
| -// We chose x28 because it is contiguous with the other specific purpose
|
| -// registers.
|
| -STATIC_ASSERT(kJSSPCode == 28);
|
| -ALIAS_REGISTER(Register, jssp, x28);
|
| -ALIAS_REGISTER(Register, wjssp, w28);
|
| -ALIAS_REGISTER(Register, fp, x29);
|
| -ALIAS_REGISTER(Register, lr, x30);
|
| -ALIAS_REGISTER(Register, xzr, x31);
|
| -ALIAS_REGISTER(Register, wzr, w31);
|
| -
|
| -// Keeps the 0 double value.
|
| -ALIAS_REGISTER(FPRegister, fp_zero, d15);
|
| -// Crankshaft double scratch register.
|
| -ALIAS_REGISTER(FPRegister, crankshaft_fp_scratch, d30);
|
| -// MacroAssembler double scratch register.
|
| -ALIAS_REGISTER(FPRegister, fp_scratch, d31);
|
| -
|
| -#undef ALIAS_REGISTER
|
| -
|
| -
|
| -Register GetAllocatableRegisterThatIsNotOneOf(Register reg1,
|
| - Register reg2 = NoReg,
|
| - Register reg3 = NoReg,
|
| - Register reg4 = NoReg);
|
| -
|
| -
|
| -// AreAliased returns true if any of the named registers overlap. Arguments set
|
| -// to NoReg are ignored. The system stack pointer may be specified.
|
| -bool AreAliased(const CPURegister& reg1,
|
| - const CPURegister& reg2,
|
| - const CPURegister& reg3 = NoReg,
|
| - const CPURegister& reg4 = NoReg,
|
| - const CPURegister& reg5 = NoReg,
|
| - const CPURegister& reg6 = NoReg,
|
| - const CPURegister& reg7 = NoReg,
|
| - const CPURegister& reg8 = NoReg);
|
| -
|
| -// AreSameSizeAndType returns true if all of the specified registers have the
|
| -// same size, and are of the same type. The system stack pointer may be
|
| -// specified. Arguments set to NoReg are ignored, as are any subsequent
|
| -// arguments. At least one argument (reg1) must be valid (not NoCPUReg).
|
| -bool AreSameSizeAndType(const CPURegister& reg1,
|
| - const CPURegister& reg2,
|
| - const CPURegister& reg3 = NoCPUReg,
|
| - const CPURegister& reg4 = NoCPUReg,
|
| - const CPURegister& reg5 = NoCPUReg,
|
| - const CPURegister& reg6 = NoCPUReg,
|
| - const CPURegister& reg7 = NoCPUReg,
|
| - const CPURegister& reg8 = NoCPUReg);
|
| -
|
| -
|
| -typedef FPRegister DoubleRegister;
|
| -
|
| -
|
| -// -----------------------------------------------------------------------------
|
| -// Lists of registers.
|
| -class CPURegList {
|
| - public:
|
| - explicit CPURegList(CPURegister reg1,
|
| - CPURegister reg2 = NoCPUReg,
|
| - CPURegister reg3 = NoCPUReg,
|
| - CPURegister reg4 = NoCPUReg)
|
| - : list_(reg1.Bit() | reg2.Bit() | reg3.Bit() | reg4.Bit()),
|
| - size_(reg1.SizeInBits()), type_(reg1.type()) {
|
| - ASSERT(AreSameSizeAndType(reg1, reg2, reg3, reg4));
|
| - ASSERT(IsValid());
|
| - }
|
| -
|
| - CPURegList(CPURegister::RegisterType type, unsigned size, RegList list)
|
| - : list_(list), size_(size), type_(type) {
|
| - ASSERT(IsValid());
|
| - }
|
| -
|
| - CPURegList(CPURegister::RegisterType type, unsigned size,
|
| - unsigned first_reg, unsigned last_reg)
|
| - : size_(size), type_(type) {
|
| - ASSERT(((type == CPURegister::kRegister) &&
|
| - (last_reg < kNumberOfRegisters)) ||
|
| - ((type == CPURegister::kFPRegister) &&
|
| - (last_reg < kNumberOfFPRegisters)));
|
| - ASSERT(last_reg >= first_reg);
|
| - list_ = (1UL << (last_reg + 1)) - 1;
|
| - list_ &= ~((1UL << first_reg) - 1);
|
| - ASSERT(IsValid());
|
| - }
|
| -
|
| - CPURegister::RegisterType type() const {
|
| - ASSERT(IsValid());
|
| - return type_;
|
| - }
|
| -
|
| - RegList list() const {
|
| - ASSERT(IsValid());
|
| - return list_;
|
| - }
|
| -
|
| - inline void set_list(RegList new_list) {
|
| - ASSERT(IsValid());
|
| - list_ = new_list;
|
| - }
|
| -
|
| - // Combine another CPURegList into this one. Registers that already exist in
|
| - // this list are left unchanged. The type and size of the registers in the
|
| - // 'other' list must match those in this list.
|
| - void Combine(const CPURegList& other);
|
| -
|
| - // Remove every register in the other CPURegList from this one. Registers that
|
| - // do not exist in this list are ignored. The type and size of the registers
|
| - // in the 'other' list must match those in this list.
|
| - void Remove(const CPURegList& other);
|
| -
|
| - // Variants of Combine and Remove which take CPURegisters.
|
| - void Combine(const CPURegister& other);
|
| - void Remove(const CPURegister& other1,
|
| - const CPURegister& other2 = NoCPUReg,
|
| - const CPURegister& other3 = NoCPUReg,
|
| - const CPURegister& other4 = NoCPUReg);
|
| -
|
| - // Variants of Combine and Remove which take a single register by its code;
|
| - // the type and size of the register is inferred from this list.
|
| - void Combine(int code);
|
| - void Remove(int code);
|
| -
|
| - // Remove all callee-saved registers from the list. This can be useful when
|
| - // preparing registers for an AAPCS64 function call, for example.
|
| - void RemoveCalleeSaved();
|
| -
|
| - CPURegister PopLowestIndex();
|
| - CPURegister PopHighestIndex();
|
| -
|
| - // AAPCS64 callee-saved registers.
|
| - static CPURegList GetCalleeSaved(unsigned size = kXRegSizeInBits);
|
| - static CPURegList GetCalleeSavedFP(unsigned size = kDRegSizeInBits);
|
| -
|
| - // AAPCS64 caller-saved registers. Note that this includes lr.
|
| - static CPURegList GetCallerSaved(unsigned size = kXRegSizeInBits);
|
| - static CPURegList GetCallerSavedFP(unsigned size = kDRegSizeInBits);
|
| -
|
| - // Registers saved as safepoints.
|
| - static CPURegList GetSafepointSavedRegisters();
|
| -
|
| - bool IsEmpty() const {
|
| - ASSERT(IsValid());
|
| - return list_ == 0;
|
| - }
|
| -
|
| - bool IncludesAliasOf(const CPURegister& other1,
|
| - const CPURegister& other2 = NoCPUReg,
|
| - const CPURegister& other3 = NoCPUReg,
|
| - const CPURegister& other4 = NoCPUReg) const {
|
| - ASSERT(IsValid());
|
| - RegList list = 0;
|
| - if (!other1.IsNone() && (other1.type() == type_)) list |= other1.Bit();
|
| - if (!other2.IsNone() && (other2.type() == type_)) list |= other2.Bit();
|
| - if (!other3.IsNone() && (other3.type() == type_)) list |= other3.Bit();
|
| - if (!other4.IsNone() && (other4.type() == type_)) list |= other4.Bit();
|
| - return (list_ & list) != 0;
|
| - }
|
| -
|
| - int Count() const {
|
| - ASSERT(IsValid());
|
| - return CountSetBits(list_, kRegListSizeInBits);
|
| - }
|
| -
|
| - unsigned RegisterSizeInBits() const {
|
| - ASSERT(IsValid());
|
| - return size_;
|
| - }
|
| -
|
| - unsigned RegisterSizeInBytes() const {
|
| - int size_in_bits = RegisterSizeInBits();
|
| - ASSERT((size_in_bits % kBitsPerByte) == 0);
|
| - return size_in_bits / kBitsPerByte;
|
| - }
|
| -
|
| - private:
|
| - RegList list_;
|
| - unsigned size_;
|
| - CPURegister::RegisterType type_;
|
| -
|
| - bool IsValid() const {
|
| - if ((type_ == CPURegister::kRegister) ||
|
| - (type_ == CPURegister::kFPRegister)) {
|
| - bool is_valid = true;
|
| - // Try to create a CPURegister for each element in the list.
|
| - for (int i = 0; i < kRegListSizeInBits; i++) {
|
| - if (((list_ >> i) & 1) != 0) {
|
| - is_valid &= CPURegister::Create(i, size_, type_).IsValid();
|
| - }
|
| - }
|
| - return is_valid;
|
| - } else if (type_ == CPURegister::kNoRegister) {
|
| - // The kNoRegister type is valid only for empty lists.
|
| - // We can't use IsEmpty here because that asserts IsValid().
|
| - return list_ == 0;
|
| - } else {
|
| - return false;
|
| - }
|
| - }
|
| -};
|
| -
|
| -
|
| -// AAPCS64 callee-saved registers.
|
| -#define kCalleeSaved CPURegList::GetCalleeSaved()
|
| -#define kCalleeSavedFP CPURegList::GetCalleeSavedFP()
|
| -
|
| -
|
| -// AAPCS64 caller-saved registers. Note that this includes lr.
|
| -#define kCallerSaved CPURegList::GetCallerSaved()
|
| -#define kCallerSavedFP CPURegList::GetCallerSavedFP()
|
| -
|
| -
|
| -// -----------------------------------------------------------------------------
|
| -// Operands.
|
| -const int kSmiShift = kSmiTagSize + kSmiShiftSize;
|
| -const uint64_t kSmiShiftMask = (1UL << kSmiShift) - 1;
|
| -
|
| -// Represents an operand in a machine instruction.
|
| -class Operand {
|
| - // TODO(all): If necessary, study more in details which methods
|
| - // TODO(all): should be inlined or not.
|
| - public:
|
| - // rm, {<shift> {#<shift_amount>}}
|
| - // where <shift> is one of {LSL, LSR, ASR, ROR}.
|
| - // <shift_amount> is uint6_t.
|
| - // This is allowed to be an implicit constructor because Operand is
|
| - // a wrapper class that doesn't normally perform any type conversion.
|
| - inline Operand(Register reg,
|
| - Shift shift = LSL,
|
| - unsigned shift_amount = 0); // NOLINT(runtime/explicit)
|
| -
|
| - // rm, <extend> {#<shift_amount>}
|
| - // where <extend> is one of {UXTB, UXTH, UXTW, UXTX, SXTB, SXTH, SXTW, SXTX}.
|
| - // <shift_amount> is uint2_t.
|
| - inline Operand(Register reg,
|
| - Extend extend,
|
| - unsigned shift_amount = 0);
|
| -
|
| - template<typename T>
|
| - inline explicit Operand(Handle<T> handle);
|
| -
|
| - // Implicit constructor for all int types, ExternalReference, and Smi.
|
| - template<typename T>
|
| - inline Operand(T t); // NOLINT(runtime/explicit)
|
| -
|
| - // Implicit constructor for int types.
|
| - template<typename int_t>
|
| - inline Operand(int_t t, RelocInfo::Mode rmode);
|
| -
|
| - inline bool IsImmediate() const;
|
| - inline bool IsShiftedRegister() const;
|
| - inline bool IsExtendedRegister() const;
|
| - inline bool IsZero() const;
|
| -
|
| - // This returns an LSL shift (<= 4) operand as an equivalent extend operand,
|
| - // which helps in the encoding of instructions that use the stack pointer.
|
| - inline Operand ToExtendedRegister() const;
|
| -
|
| - inline int64_t immediate() const;
|
| - inline Register reg() const;
|
| - inline Shift shift() const;
|
| - inline Extend extend() const;
|
| - inline unsigned shift_amount() const;
|
| -
|
| - // Relocation information.
|
| - RelocInfo::Mode rmode() const { return rmode_; }
|
| - void set_rmode(RelocInfo::Mode rmode) { rmode_ = rmode; }
|
| - bool NeedsRelocation() const;
|
| -
|
| - // Helpers
|
| - inline static Operand UntagSmi(Register smi);
|
| - inline static Operand UntagSmiAndScale(Register smi, int scale);
|
| -
|
| - private:
|
| - void initialize_handle(Handle<Object> value);
|
| - int64_t immediate_;
|
| - Register reg_;
|
| - Shift shift_;
|
| - Extend extend_;
|
| - unsigned shift_amount_;
|
| - RelocInfo::Mode rmode_;
|
| -};
|
| -
|
| -
|
| -// MemOperand represents a memory operand in a load or store instruction.
|
| -class MemOperand {
|
| - public:
|
| - inline explicit MemOperand(Register base,
|
| - ptrdiff_t offset = 0,
|
| - AddrMode addrmode = Offset);
|
| - inline explicit MemOperand(Register base,
|
| - Register regoffset,
|
| - Shift shift = LSL,
|
| - unsigned shift_amount = 0);
|
| - inline explicit MemOperand(Register base,
|
| - Register regoffset,
|
| - Extend extend,
|
| - unsigned shift_amount = 0);
|
| - inline explicit MemOperand(Register base,
|
| - const Operand& offset,
|
| - AddrMode addrmode = Offset);
|
| -
|
| - const Register& base() const { return base_; }
|
| - const Register& regoffset() const { return regoffset_; }
|
| - ptrdiff_t offset() const { return offset_; }
|
| - AddrMode addrmode() const { return addrmode_; }
|
| - Shift shift() const { return shift_; }
|
| - Extend extend() const { return extend_; }
|
| - unsigned shift_amount() const { return shift_amount_; }
|
| - inline bool IsImmediateOffset() const;
|
| - inline bool IsRegisterOffset() const;
|
| - inline bool IsPreIndex() const;
|
| - inline bool IsPostIndex() const;
|
| -
|
| - // For offset modes, return the offset as an Operand. This helper cannot
|
| - // handle indexed modes.
|
| - inline Operand OffsetAsOperand() const;
|
| -
|
| - private:
|
| - Register base_;
|
| - Register regoffset_;
|
| - ptrdiff_t offset_;
|
| - AddrMode addrmode_;
|
| - Shift shift_;
|
| - Extend extend_;
|
| - unsigned shift_amount_;
|
| -};
|
| -
|
| -
|
| -// -----------------------------------------------------------------------------
|
| -// Assembler.
|
| -
|
| -class Assembler : public AssemblerBase {
|
| - public:
|
| - // Create an assembler. Instructions and relocation information are emitted
|
| - // into a buffer, with the instructions starting from the beginning and the
|
| - // relocation information starting from the end of the buffer. See CodeDesc
|
| - // for a detailed comment on the layout (globals.h).
|
| - //
|
| - // If the provided buffer is NULL, the assembler allocates and grows its own
|
| - // buffer, and buffer_size determines the initial buffer size. The buffer is
|
| - // owned by the assembler and deallocated upon destruction of the assembler.
|
| - //
|
| - // If the provided buffer is not NULL, the assembler uses the provided buffer
|
| - // for code generation and assumes its size to be buffer_size. If the buffer
|
| - // is too small, a fatal error occurs. No deallocation of the buffer is done
|
| - // upon destruction of the assembler.
|
| - Assembler(Isolate* arg_isolate, void* buffer, int buffer_size);
|
| -
|
| - virtual ~Assembler();
|
| -
|
| - virtual void AbortedCodeGeneration() {
|
| - num_pending_reloc_info_ = 0;
|
| - }
|
| -
|
| - // System functions ---------------------------------------------------------
|
| - // Start generating code from the beginning of the buffer, discarding any code
|
| - // and data that has already been emitted into the buffer.
|
| - //
|
| - // In order to avoid any accidental transfer of state, Reset ASSERTs that the
|
| - // constant pool is not blocked.
|
| - void Reset();
|
| -
|
| - // GetCode emits any pending (non-emitted) code and fills the descriptor
|
| - // desc. GetCode() is idempotent; it returns the same result if no other
|
| - // Assembler functions are invoked in between GetCode() calls.
|
| - //
|
| - // The descriptor (desc) can be NULL. In that case, the code is finalized as
|
| - // usual, but the descriptor is not populated.
|
| - void GetCode(CodeDesc* desc);
|
| -
|
| - // Insert the smallest number of nop instructions
|
| - // possible to align the pc offset to a multiple
|
| - // of m. m must be a power of 2 (>= 4).
|
| - void Align(int m);
|
| -
|
| - inline void Unreachable();
|
| -
|
| - // Label --------------------------------------------------------------------
|
| - // Bind a label to the current pc. Note that labels can only be bound once,
|
| - // and if labels are linked to other instructions, they _must_ be bound
|
| - // before they go out of scope.
|
| - void bind(Label* label);
|
| -
|
| -
|
| - // RelocInfo and pools ------------------------------------------------------
|
| -
|
| - // Record relocation information for current pc_.
|
| - void RecordRelocInfo(RelocInfo::Mode rmode, intptr_t data = 0);
|
| -
|
| - // Return the address in the constant pool of the code target address used by
|
| - // the branch/call instruction at pc.
|
| - inline static Address target_pointer_address_at(Address pc);
|
| -
|
| - // Read/Modify the code target address in the branch/call instruction at pc.
|
| - inline static Address target_address_at(Address pc,
|
| - ConstantPoolArray* constant_pool);
|
| - inline static void set_target_address_at(Address pc,
|
| - ConstantPoolArray* constant_pool,
|
| - Address target);
|
| - static inline Address target_address_at(Address pc, Code* code);
|
| - static inline void set_target_address_at(Address pc,
|
| - Code* code,
|
| - Address target);
|
| -
|
| - // Return the code target address at a call site from the return address of
|
| - // that call in the instruction stream.
|
| - inline static Address target_address_from_return_address(Address pc);
|
| -
|
| - // Given the address of the beginning of a call, return the address in the
|
| - // instruction stream that call will return from.
|
| - inline static Address return_address_from_call_start(Address pc);
|
| -
|
| - // This sets the branch destination (which is in the constant pool on ARM).
|
| - // This is for calls and branches within generated code.
|
| - inline static void deserialization_set_special_target_at(
|
| - Address constant_pool_entry, Code* code, Address target);
|
| -
|
| - // All addresses in the constant pool are the same size as pointers.
|
| - static const int kSpecialTargetSize = kPointerSize;
|
| -
|
| - // The sizes of the call sequences emitted by MacroAssembler::Call.
|
| - // Wherever possible, use MacroAssembler::CallSize instead of these constants,
|
| - // as it will choose the correct value for a given relocation mode.
|
| - //
|
| - // Without relocation:
|
| - // movz ip0, #(target & 0x000000000000ffff)
|
| - // movk ip0, #(target & 0x00000000ffff0000)
|
| - // movk ip0, #(target & 0x0000ffff00000000)
|
| - // movk ip0, #(target & 0xffff000000000000)
|
| - // blr ip0
|
| - //
|
| - // With relocation:
|
| - // ldr ip0, =target
|
| - // blr ip0
|
| - static const int kCallSizeWithoutRelocation = 5 * kInstructionSize;
|
| - static const int kCallSizeWithRelocation = 2 * kInstructionSize;
|
| -
|
| - // Size of the generated code in bytes
|
| - uint64_t SizeOfGeneratedCode() const {
|
| - ASSERT((pc_ >= buffer_) && (pc_ < (buffer_ + buffer_size_)));
|
| - return pc_ - buffer_;
|
| - }
|
| -
|
| - // Return the code size generated from label to the current position.
|
| - uint64_t SizeOfCodeGeneratedSince(const Label* label) {
|
| - ASSERT(label->is_bound());
|
| - ASSERT(pc_offset() >= label->pos());
|
| - ASSERT(pc_offset() < buffer_size_);
|
| - return pc_offset() - label->pos();
|
| - }
|
| -
|
| - // Check the size of the code generated since the given label. This function
|
| - // is used primarily to work around comparisons between signed and unsigned
|
| - // quantities, since V8 uses both.
|
| - // TODO(jbramley): Work out what sign to use for these things and if possible,
|
| - // change things to be consistent.
|
| - void AssertSizeOfCodeGeneratedSince(const Label* label, ptrdiff_t size) {
|
| - ASSERT(size >= 0);
|
| - ASSERT(static_cast<uint64_t>(size) == SizeOfCodeGeneratedSince(label));
|
| - }
|
| -
|
| - // Return the number of instructions generated from label to the
|
| - // current position.
|
| - int InstructionsGeneratedSince(const Label* label) {
|
| - return SizeOfCodeGeneratedSince(label) / kInstructionSize;
|
| - }
|
| -
|
| - // Number of instructions generated for the return sequence in
|
| - // FullCodeGenerator::EmitReturnSequence.
|
| - static const int kJSRetSequenceInstructions = 7;
|
| - // Distance between start of patched return sequence and the emitted address
|
| - // to jump to.
|
| - static const int kPatchReturnSequenceAddressOffset = 0;
|
| - static const int kPatchDebugBreakSlotAddressOffset = 0;
|
| -
|
| - // Number of instructions necessary to be able to later patch it to a call.
|
| - // See Debug::GenerateSlot() and BreakLocationIterator::SetDebugBreakAtSlot().
|
| - static const int kDebugBreakSlotInstructions = 4;
|
| - static const int kDebugBreakSlotLength =
|
| - kDebugBreakSlotInstructions * kInstructionSize;
|
| -
|
| - static const int kPatchDebugBreakSlotReturnOffset = 2 * kInstructionSize;
|
| -
|
| - // Prevent contant pool emission until EndBlockConstPool is called.
|
| - // Call to this function can be nested but must be followed by an equal
|
| - // number of call to EndBlockConstpool.
|
| - void StartBlockConstPool();
|
| -
|
| - // Resume constant pool emission. Need to be called as many time as
|
| - // StartBlockConstPool to have an effect.
|
| - void EndBlockConstPool();
|
| -
|
| - bool is_const_pool_blocked() const;
|
| - static bool IsConstantPoolAt(Instruction* instr);
|
| - static int ConstantPoolSizeAt(Instruction* instr);
|
| - // See Assembler::CheckConstPool for more info.
|
| - void ConstantPoolMarker(uint32_t size);
|
| - void EmitPoolGuard();
|
| - void ConstantPoolGuard();
|
| -
|
| - // Prevent veneer pool emission until EndBlockVeneerPool is called.
|
| - // Call to this function can be nested but must be followed by an equal
|
| - // number of call to EndBlockConstpool.
|
| - void StartBlockVeneerPool();
|
| -
|
| - // Resume constant pool emission. Need to be called as many time as
|
| - // StartBlockVeneerPool to have an effect.
|
| - void EndBlockVeneerPool();
|
| -
|
| - bool is_veneer_pool_blocked() const {
|
| - return veneer_pool_blocked_nesting_ > 0;
|
| - }
|
| -
|
| - // Block/resume emission of constant pools and veneer pools.
|
| - void StartBlockPools() {
|
| - StartBlockConstPool();
|
| - StartBlockVeneerPool();
|
| - }
|
| - void EndBlockPools() {
|
| - EndBlockConstPool();
|
| - EndBlockVeneerPool();
|
| - }
|
| -
|
| - // Debugging ----------------------------------------------------------------
|
| - PositionsRecorder* positions_recorder() { return &positions_recorder_; }
|
| - void RecordComment(const char* msg);
|
| - int buffer_space() const;
|
| -
|
| - // Mark address of the ExitJSFrame code.
|
| - void RecordJSReturn();
|
| -
|
| - // Mark address of a debug break slot.
|
| - void RecordDebugBreakSlot();
|
| -
|
| - // Record the emission of a constant pool.
|
| - //
|
| - // The emission of constant and veneer pools depends on the size of the code
|
| - // generated and the number of RelocInfo recorded.
|
| - // The Debug mechanism needs to map code offsets between two versions of a
|
| - // function, compiled with and without debugger support (see for example
|
| - // Debug::PrepareForBreakPoints()).
|
| - // Compiling functions with debugger support generates additional code
|
| - // (Debug::GenerateSlot()). This may affect the emission of the pools and
|
| - // cause the version of the code with debugger support to have pools generated
|
| - // in different places.
|
| - // Recording the position and size of emitted pools allows to correctly
|
| - // compute the offset mappings between the different versions of a function in
|
| - // all situations.
|
| - //
|
| - // The parameter indicates the size of the pool (in bytes), including
|
| - // the marker and branch over the data.
|
| - void RecordConstPool(int size);
|
| -
|
| -
|
| - // Instruction set functions ------------------------------------------------
|
| -
|
| - // Branch / Jump instructions.
|
| - // For branches offsets are scaled, i.e. they in instrcutions not in bytes.
|
| - // Branch to register.
|
| - void br(const Register& xn);
|
| -
|
| - // Branch-link to register.
|
| - void blr(const Register& xn);
|
| -
|
| - // Branch to register with return hint.
|
| - void ret(const Register& xn = lr);
|
| -
|
| - // Unconditional branch to label.
|
| - void b(Label* label);
|
| -
|
| - // Conditional branch to label.
|
| - void b(Label* label, Condition cond);
|
| -
|
| - // Unconditional branch to PC offset.
|
| - void b(int imm26);
|
| -
|
| - // Conditional branch to PC offset.
|
| - void b(int imm19, Condition cond);
|
| -
|
| - // Branch-link to label / pc offset.
|
| - void bl(Label* label);
|
| - void bl(int imm26);
|
| -
|
| - // Compare and branch to label / pc offset if zero.
|
| - void cbz(const Register& rt, Label* label);
|
| - void cbz(const Register& rt, int imm19);
|
| -
|
| - // Compare and branch to label / pc offset if not zero.
|
| - void cbnz(const Register& rt, Label* label);
|
| - void cbnz(const Register& rt, int imm19);
|
| -
|
| - // Test bit and branch to label / pc offset if zero.
|
| - void tbz(const Register& rt, unsigned bit_pos, Label* label);
|
| - void tbz(const Register& rt, unsigned bit_pos, int imm14);
|
| -
|
| - // Test bit and branch to label / pc offset if not zero.
|
| - void tbnz(const Register& rt, unsigned bit_pos, Label* label);
|
| - void tbnz(const Register& rt, unsigned bit_pos, int imm14);
|
| -
|
| - // Address calculation instructions.
|
| - // Calculate a PC-relative address. Unlike for branches the offset in adr is
|
| - // unscaled (i.e. the result can be unaligned).
|
| - void adr(const Register& rd, Label* label);
|
| - void adr(const Register& rd, int imm21);
|
| -
|
| - // Data Processing instructions.
|
| - // Add.
|
| - void add(const Register& rd,
|
| - const Register& rn,
|
| - const Operand& operand);
|
| -
|
| - // Add and update status flags.
|
| - void adds(const Register& rd,
|
| - const Register& rn,
|
| - const Operand& operand);
|
| -
|
| - // Compare negative.
|
| - void cmn(const Register& rn, const Operand& operand);
|
| -
|
| - // Subtract.
|
| - void sub(const Register& rd,
|
| - const Register& rn,
|
| - const Operand& operand);
|
| -
|
| - // Subtract and update status flags.
|
| - void subs(const Register& rd,
|
| - const Register& rn,
|
| - const Operand& operand);
|
| -
|
| - // Compare.
|
| - void cmp(const Register& rn, const Operand& operand);
|
| -
|
| - // Negate.
|
| - void neg(const Register& rd,
|
| - const Operand& operand);
|
| -
|
| - // Negate and update status flags.
|
| - void negs(const Register& rd,
|
| - const Operand& operand);
|
| -
|
| - // Add with carry bit.
|
| - void adc(const Register& rd,
|
| - const Register& rn,
|
| - const Operand& operand);
|
| -
|
| - // Add with carry bit and update status flags.
|
| - void adcs(const Register& rd,
|
| - const Register& rn,
|
| - const Operand& operand);
|
| -
|
| - // Subtract with carry bit.
|
| - void sbc(const Register& rd,
|
| - const Register& rn,
|
| - const Operand& operand);
|
| -
|
| - // Subtract with carry bit and update status flags.
|
| - void sbcs(const Register& rd,
|
| - const Register& rn,
|
| - const Operand& operand);
|
| -
|
| - // Negate with carry bit.
|
| - void ngc(const Register& rd,
|
| - const Operand& operand);
|
| -
|
| - // Negate with carry bit and update status flags.
|
| - void ngcs(const Register& rd,
|
| - const Operand& operand);
|
| -
|
| - // Logical instructions.
|
| - // Bitwise and (A & B).
|
| - void and_(const Register& rd,
|
| - const Register& rn,
|
| - const Operand& operand);
|
| -
|
| - // Bitwise and (A & B) and update status flags.
|
| - void ands(const Register& rd,
|
| - const Register& rn,
|
| - const Operand& operand);
|
| -
|
| - // Bit test, and set flags.
|
| - void tst(const Register& rn, const Operand& operand);
|
| -
|
| - // Bit clear (A & ~B).
|
| - void bic(const Register& rd,
|
| - const Register& rn,
|
| - const Operand& operand);
|
| -
|
| - // Bit clear (A & ~B) and update status flags.
|
| - void bics(const Register& rd,
|
| - const Register& rn,
|
| - const Operand& operand);
|
| -
|
| - // Bitwise or (A | B).
|
| - void orr(const Register& rd, const Register& rn, const Operand& operand);
|
| -
|
| - // Bitwise nor (A | ~B).
|
| - void orn(const Register& rd, const Register& rn, const Operand& operand);
|
| -
|
| - // Bitwise eor/xor (A ^ B).
|
| - void eor(const Register& rd, const Register& rn, const Operand& operand);
|
| -
|
| - // Bitwise enor/xnor (A ^ ~B).
|
| - void eon(const Register& rd, const Register& rn, const Operand& operand);
|
| -
|
| - // Logical shift left variable.
|
| - void lslv(const Register& rd, const Register& rn, const Register& rm);
|
| -
|
| - // Logical shift right variable.
|
| - void lsrv(const Register& rd, const Register& rn, const Register& rm);
|
| -
|
| - // Arithmetic shift right variable.
|
| - void asrv(const Register& rd, const Register& rn, const Register& rm);
|
| -
|
| - // Rotate right variable.
|
| - void rorv(const Register& rd, const Register& rn, const Register& rm);
|
| -
|
| - // Bitfield instructions.
|
| - // Bitfield move.
|
| - void bfm(const Register& rd,
|
| - const Register& rn,
|
| - unsigned immr,
|
| - unsigned imms);
|
| -
|
| - // Signed bitfield move.
|
| - void sbfm(const Register& rd,
|
| - const Register& rn,
|
| - unsigned immr,
|
| - unsigned imms);
|
| -
|
| - // Unsigned bitfield move.
|
| - void ubfm(const Register& rd,
|
| - const Register& rn,
|
| - unsigned immr,
|
| - unsigned imms);
|
| -
|
| - // Bfm aliases.
|
| - // Bitfield insert.
|
| - void bfi(const Register& rd,
|
| - const Register& rn,
|
| - unsigned lsb,
|
| - unsigned width) {
|
| - ASSERT(width >= 1);
|
| - ASSERT(lsb + width <= rn.SizeInBits());
|
| - bfm(rd, rn, (rd.SizeInBits() - lsb) & (rd.SizeInBits() - 1), width - 1);
|
| - }
|
| -
|
| - // Bitfield extract and insert low.
|
| - void bfxil(const Register& rd,
|
| - const Register& rn,
|
| - unsigned lsb,
|
| - unsigned width) {
|
| - ASSERT(width >= 1);
|
| - ASSERT(lsb + width <= rn.SizeInBits());
|
| - bfm(rd, rn, lsb, lsb + width - 1);
|
| - }
|
| -
|
| - // Sbfm aliases.
|
| - // Arithmetic shift right.
|
| - void asr(const Register& rd, const Register& rn, unsigned shift) {
|
| - ASSERT(shift < rd.SizeInBits());
|
| - sbfm(rd, rn, shift, rd.SizeInBits() - 1);
|
| - }
|
| -
|
| - // Signed bitfield insert in zero.
|
| - void sbfiz(const Register& rd,
|
| - const Register& rn,
|
| - unsigned lsb,
|
| - unsigned width) {
|
| - ASSERT(width >= 1);
|
| - ASSERT(lsb + width <= rn.SizeInBits());
|
| - sbfm(rd, rn, (rd.SizeInBits() - lsb) & (rd.SizeInBits() - 1), width - 1);
|
| - }
|
| -
|
| - // Signed bitfield extract.
|
| - void sbfx(const Register& rd,
|
| - const Register& rn,
|
| - unsigned lsb,
|
| - unsigned width) {
|
| - ASSERT(width >= 1);
|
| - ASSERT(lsb + width <= rn.SizeInBits());
|
| - sbfm(rd, rn, lsb, lsb + width - 1);
|
| - }
|
| -
|
| - // Signed extend byte.
|
| - void sxtb(const Register& rd, const Register& rn) {
|
| - sbfm(rd, rn, 0, 7);
|
| - }
|
| -
|
| - // Signed extend halfword.
|
| - void sxth(const Register& rd, const Register& rn) {
|
| - sbfm(rd, rn, 0, 15);
|
| - }
|
| -
|
| - // Signed extend word.
|
| - void sxtw(const Register& rd, const Register& rn) {
|
| - sbfm(rd, rn, 0, 31);
|
| - }
|
| -
|
| - // Ubfm aliases.
|
| - // Logical shift left.
|
| - void lsl(const Register& rd, const Register& rn, unsigned shift) {
|
| - unsigned reg_size = rd.SizeInBits();
|
| - ASSERT(shift < reg_size);
|
| - ubfm(rd, rn, (reg_size - shift) % reg_size, reg_size - shift - 1);
|
| - }
|
| -
|
| - // Logical shift right.
|
| - void lsr(const Register& rd, const Register& rn, unsigned shift) {
|
| - ASSERT(shift < rd.SizeInBits());
|
| - ubfm(rd, rn, shift, rd.SizeInBits() - 1);
|
| - }
|
| -
|
| - // Unsigned bitfield insert in zero.
|
| - void ubfiz(const Register& rd,
|
| - const Register& rn,
|
| - unsigned lsb,
|
| - unsigned width) {
|
| - ASSERT(width >= 1);
|
| - ASSERT(lsb + width <= rn.SizeInBits());
|
| - ubfm(rd, rn, (rd.SizeInBits() - lsb) & (rd.SizeInBits() - 1), width - 1);
|
| - }
|
| -
|
| - // Unsigned bitfield extract.
|
| - void ubfx(const Register& rd,
|
| - const Register& rn,
|
| - unsigned lsb,
|
| - unsigned width) {
|
| - ASSERT(width >= 1);
|
| - ASSERT(lsb + width <= rn.SizeInBits());
|
| - ubfm(rd, rn, lsb, lsb + width - 1);
|
| - }
|
| -
|
| - // Unsigned extend byte.
|
| - void uxtb(const Register& rd, const Register& rn) {
|
| - ubfm(rd, rn, 0, 7);
|
| - }
|
| -
|
| - // Unsigned extend halfword.
|
| - void uxth(const Register& rd, const Register& rn) {
|
| - ubfm(rd, rn, 0, 15);
|
| - }
|
| -
|
| - // Unsigned extend word.
|
| - void uxtw(const Register& rd, const Register& rn) {
|
| - ubfm(rd, rn, 0, 31);
|
| - }
|
| -
|
| - // Extract.
|
| - void extr(const Register& rd,
|
| - const Register& rn,
|
| - const Register& rm,
|
| - unsigned lsb);
|
| -
|
| - // Conditional select: rd = cond ? rn : rm.
|
| - void csel(const Register& rd,
|
| - const Register& rn,
|
| - const Register& rm,
|
| - Condition cond);
|
| -
|
| - // Conditional select increment: rd = cond ? rn : rm + 1.
|
| - void csinc(const Register& rd,
|
| - const Register& rn,
|
| - const Register& rm,
|
| - Condition cond);
|
| -
|
| - // Conditional select inversion: rd = cond ? rn : ~rm.
|
| - void csinv(const Register& rd,
|
| - const Register& rn,
|
| - const Register& rm,
|
| - Condition cond);
|
| -
|
| - // Conditional select negation: rd = cond ? rn : -rm.
|
| - void csneg(const Register& rd,
|
| - const Register& rn,
|
| - const Register& rm,
|
| - Condition cond);
|
| -
|
| - // Conditional set: rd = cond ? 1 : 0.
|
| - void cset(const Register& rd, Condition cond);
|
| -
|
| - // Conditional set minus: rd = cond ? -1 : 0.
|
| - void csetm(const Register& rd, Condition cond);
|
| -
|
| - // Conditional increment: rd = cond ? rn + 1 : rn.
|
| - void cinc(const Register& rd, const Register& rn, Condition cond);
|
| -
|
| - // Conditional invert: rd = cond ? ~rn : rn.
|
| - void cinv(const Register& rd, const Register& rn, Condition cond);
|
| -
|
| - // Conditional negate: rd = cond ? -rn : rn.
|
| - void cneg(const Register& rd, const Register& rn, Condition cond);
|
| -
|
| - // Extr aliases.
|
| - void ror(const Register& rd, const Register& rs, unsigned shift) {
|
| - extr(rd, rs, rs, shift);
|
| - }
|
| -
|
| - // Conditional comparison.
|
| - // Conditional compare negative.
|
| - void ccmn(const Register& rn,
|
| - const Operand& operand,
|
| - StatusFlags nzcv,
|
| - Condition cond);
|
| -
|
| - // Conditional compare.
|
| - void ccmp(const Register& rn,
|
| - const Operand& operand,
|
| - StatusFlags nzcv,
|
| - Condition cond);
|
| -
|
| - // Multiplication.
|
| - // 32 x 32 -> 32-bit and 64 x 64 -> 64-bit multiply.
|
| - void mul(const Register& rd, const Register& rn, const Register& rm);
|
| -
|
| - // 32 + 32 x 32 -> 32-bit and 64 + 64 x 64 -> 64-bit multiply accumulate.
|
| - void madd(const Register& rd,
|
| - const Register& rn,
|
| - const Register& rm,
|
| - const Register& ra);
|
| -
|
| - // -(32 x 32) -> 32-bit and -(64 x 64) -> 64-bit multiply.
|
| - void mneg(const Register& rd, const Register& rn, const Register& rm);
|
| -
|
| - // 32 - 32 x 32 -> 32-bit and 64 - 64 x 64 -> 64-bit multiply subtract.
|
| - void msub(const Register& rd,
|
| - const Register& rn,
|
| - const Register& rm,
|
| - const Register& ra);
|
| -
|
| - // 32 x 32 -> 64-bit multiply.
|
| - void smull(const Register& rd, const Register& rn, const Register& rm);
|
| -
|
| - // Xd = bits<127:64> of Xn * Xm.
|
| - void smulh(const Register& rd, const Register& rn, const Register& rm);
|
| -
|
| - // Signed 32 x 32 -> 64-bit multiply and accumulate.
|
| - void smaddl(const Register& rd,
|
| - const Register& rn,
|
| - const Register& rm,
|
| - const Register& ra);
|
| -
|
| - // Unsigned 32 x 32 -> 64-bit multiply and accumulate.
|
| - void umaddl(const Register& rd,
|
| - const Register& rn,
|
| - const Register& rm,
|
| - const Register& ra);
|
| -
|
| - // Signed 32 x 32 -> 64-bit multiply and subtract.
|
| - void smsubl(const Register& rd,
|
| - const Register& rn,
|
| - const Register& rm,
|
| - const Register& ra);
|
| -
|
| - // Unsigned 32 x 32 -> 64-bit multiply and subtract.
|
| - void umsubl(const Register& rd,
|
| - const Register& rn,
|
| - const Register& rm,
|
| - const Register& ra);
|
| -
|
| - // Signed integer divide.
|
| - void sdiv(const Register& rd, const Register& rn, const Register& rm);
|
| -
|
| - // Unsigned integer divide.
|
| - void udiv(const Register& rd, const Register& rn, const Register& rm);
|
| -
|
| - // Bit count, bit reverse and endian reverse.
|
| - void rbit(const Register& rd, const Register& rn);
|
| - void rev16(const Register& rd, const Register& rn);
|
| - void rev32(const Register& rd, const Register& rn);
|
| - void rev(const Register& rd, const Register& rn);
|
| - void clz(const Register& rd, const Register& rn);
|
| - void cls(const Register& rd, const Register& rn);
|
| -
|
| - // Memory instructions.
|
| -
|
| - // Load literal from pc + offset_from_pc.
|
| - void LoadLiteral(const CPURegister& rt, int offset_from_pc);
|
| -
|
| - // Load integer or FP register.
|
| - void ldr(const CPURegister& rt, const MemOperand& src);
|
| -
|
| - // Store integer or FP register.
|
| - void str(const CPURegister& rt, const MemOperand& dst);
|
| -
|
| - // Load word with sign extension.
|
| - void ldrsw(const Register& rt, const MemOperand& src);
|
| -
|
| - // Load byte.
|
| - void ldrb(const Register& rt, const MemOperand& src);
|
| -
|
| - // Store byte.
|
| - void strb(const Register& rt, const MemOperand& dst);
|
| -
|
| - // Load byte with sign extension.
|
| - void ldrsb(const Register& rt, const MemOperand& src);
|
| -
|
| - // Load half-word.
|
| - void ldrh(const Register& rt, const MemOperand& src);
|
| -
|
| - // Store half-word.
|
| - void strh(const Register& rt, const MemOperand& dst);
|
| -
|
| - // Load half-word with sign extension.
|
| - void ldrsh(const Register& rt, const MemOperand& src);
|
| -
|
| - // Load integer or FP register pair.
|
| - void ldp(const CPURegister& rt, const CPURegister& rt2,
|
| - const MemOperand& src);
|
| -
|
| - // Store integer or FP register pair.
|
| - void stp(const CPURegister& rt, const CPURegister& rt2,
|
| - const MemOperand& dst);
|
| -
|
| - // Load word pair with sign extension.
|
| - void ldpsw(const Register& rt, const Register& rt2, const MemOperand& src);
|
| -
|
| - // Load integer or FP register pair, non-temporal.
|
| - void ldnp(const CPURegister& rt, const CPURegister& rt2,
|
| - const MemOperand& src);
|
| -
|
| - // Store integer or FP register pair, non-temporal.
|
| - void stnp(const CPURegister& rt, const CPURegister& rt2,
|
| - const MemOperand& dst);
|
| -
|
| - // Load literal to register.
|
| - void ldr(const Register& rt, uint64_t imm);
|
| -
|
| - // Load literal to FP register.
|
| - void ldr(const FPRegister& ft, double imm);
|
| - void ldr(const FPRegister& ft, float imm);
|
| -
|
| - // Move instructions. The default shift of -1 indicates that the move
|
| - // instruction will calculate an appropriate 16-bit immediate and left shift
|
| - // that is equal to the 64-bit immediate argument. If an explicit left shift
|
| - // is specified (0, 16, 32 or 48), the immediate must be a 16-bit value.
|
| - //
|
| - // For movk, an explicit shift can be used to indicate which half word should
|
| - // be overwritten, eg. movk(x0, 0, 0) will overwrite the least-significant
|
| - // half word with zero, whereas movk(x0, 0, 48) will overwrite the
|
| - // most-significant.
|
| -
|
| - // Move and keep.
|
| - void movk(const Register& rd, uint64_t imm, int shift = -1) {
|
| - MoveWide(rd, imm, shift, MOVK);
|
| - }
|
| -
|
| - // Move with non-zero.
|
| - void movn(const Register& rd, uint64_t imm, int shift = -1) {
|
| - MoveWide(rd, imm, shift, MOVN);
|
| - }
|
| -
|
| - // Move with zero.
|
| - void movz(const Register& rd, uint64_t imm, int shift = -1) {
|
| - MoveWide(rd, imm, shift, MOVZ);
|
| - }
|
| -
|
| - // Misc instructions.
|
| - // Monitor debug-mode breakpoint.
|
| - void brk(int code);
|
| -
|
| - // Halting debug-mode breakpoint.
|
| - void hlt(int code);
|
| -
|
| - // Move register to register.
|
| - void mov(const Register& rd, const Register& rn);
|
| -
|
| - // Move NOT(operand) to register.
|
| - void mvn(const Register& rd, const Operand& operand);
|
| -
|
| - // System instructions.
|
| - // Move to register from system register.
|
| - void mrs(const Register& rt, SystemRegister sysreg);
|
| -
|
| - // Move from register to system register.
|
| - void msr(SystemRegister sysreg, const Register& rt);
|
| -
|
| - // System hint.
|
| - void hint(SystemHint code);
|
| -
|
| - // Data memory barrier
|
| - void dmb(BarrierDomain domain, BarrierType type);
|
| -
|
| - // Data synchronization barrier
|
| - void dsb(BarrierDomain domain, BarrierType type);
|
| -
|
| - // Instruction synchronization barrier
|
| - void isb();
|
| -
|
| - // Alias for system instructions.
|
| - void nop() { hint(NOP); }
|
| -
|
| - // Different nop operations are used by the code generator to detect certain
|
| - // states of the generated code.
|
| - enum NopMarkerTypes {
|
| - DEBUG_BREAK_NOP,
|
| - INTERRUPT_CODE_NOP,
|
| - FIRST_NOP_MARKER = DEBUG_BREAK_NOP,
|
| - LAST_NOP_MARKER = INTERRUPT_CODE_NOP
|
| - };
|
| -
|
| - void nop(NopMarkerTypes n) {
|
| - ASSERT((FIRST_NOP_MARKER <= n) && (n <= LAST_NOP_MARKER));
|
| - mov(Register::XRegFromCode(n), Register::XRegFromCode(n));
|
| - }
|
| -
|
| - // FP instructions.
|
| - // Move immediate to FP register.
|
| - void fmov(FPRegister fd, double imm);
|
| - void fmov(FPRegister fd, float imm);
|
| -
|
| - // Move FP register to register.
|
| - void fmov(Register rd, FPRegister fn);
|
| -
|
| - // Move register to FP register.
|
| - void fmov(FPRegister fd, Register rn);
|
| -
|
| - // Move FP register to FP register.
|
| - void fmov(FPRegister fd, FPRegister fn);
|
| -
|
| - // FP add.
|
| - void fadd(const FPRegister& fd, const FPRegister& fn, const FPRegister& fm);
|
| -
|
| - // FP subtract.
|
| - void fsub(const FPRegister& fd, const FPRegister& fn, const FPRegister& fm);
|
| -
|
| - // FP multiply.
|
| - void fmul(const FPRegister& fd, const FPRegister& fn, const FPRegister& fm);
|
| -
|
| - // FP fused multiply and add.
|
| - void fmadd(const FPRegister& fd,
|
| - const FPRegister& fn,
|
| - const FPRegister& fm,
|
| - const FPRegister& fa);
|
| -
|
| - // FP fused multiply and subtract.
|
| - void fmsub(const FPRegister& fd,
|
| - const FPRegister& fn,
|
| - const FPRegister& fm,
|
| - const FPRegister& fa);
|
| -
|
| - // FP fused multiply, add and negate.
|
| - void fnmadd(const FPRegister& fd,
|
| - const FPRegister& fn,
|
| - const FPRegister& fm,
|
| - const FPRegister& fa);
|
| -
|
| - // FP fused multiply, subtract and negate.
|
| - void fnmsub(const FPRegister& fd,
|
| - const FPRegister& fn,
|
| - const FPRegister& fm,
|
| - const FPRegister& fa);
|
| -
|
| - // FP divide.
|
| - void fdiv(const FPRegister& fd, const FPRegister& fn, const FPRegister& fm);
|
| -
|
| - // FP maximum.
|
| - void fmax(const FPRegister& fd, const FPRegister& fn, const FPRegister& fm);
|
| -
|
| - // FP minimum.
|
| - void fmin(const FPRegister& fd, const FPRegister& fn, const FPRegister& fm);
|
| -
|
| - // FP maximum.
|
| - void fmaxnm(const FPRegister& fd, const FPRegister& fn, const FPRegister& fm);
|
| -
|
| - // FP minimum.
|
| - void fminnm(const FPRegister& fd, const FPRegister& fn, const FPRegister& fm);
|
| -
|
| - // FP absolute.
|
| - void fabs(const FPRegister& fd, const FPRegister& fn);
|
| -
|
| - // FP negate.
|
| - void fneg(const FPRegister& fd, const FPRegister& fn);
|
| -
|
| - // FP square root.
|
| - void fsqrt(const FPRegister& fd, const FPRegister& fn);
|
| -
|
| - // FP round to integer (nearest with ties to away).
|
| - void frinta(const FPRegister& fd, const FPRegister& fn);
|
| -
|
| - // FP round to integer (nearest with ties to even).
|
| - void frintn(const FPRegister& fd, const FPRegister& fn);
|
| -
|
| - // FP round to integer (towards zero.)
|
| - void frintz(const FPRegister& fd, const FPRegister& fn);
|
| -
|
| - // FP compare registers.
|
| - void fcmp(const FPRegister& fn, const FPRegister& fm);
|
| -
|
| - // FP compare immediate.
|
| - void fcmp(const FPRegister& fn, double value);
|
| -
|
| - // FP conditional compare.
|
| - void fccmp(const FPRegister& fn,
|
| - const FPRegister& fm,
|
| - StatusFlags nzcv,
|
| - Condition cond);
|
| -
|
| - // FP conditional select.
|
| - void fcsel(const FPRegister& fd,
|
| - const FPRegister& fn,
|
| - const FPRegister& fm,
|
| - Condition cond);
|
| -
|
| - // Common FP Convert function
|
| - void FPConvertToInt(const Register& rd,
|
| - const FPRegister& fn,
|
| - FPIntegerConvertOp op);
|
| -
|
| - // FP convert between single and double precision.
|
| - void fcvt(const FPRegister& fd, const FPRegister& fn);
|
| -
|
| - // Convert FP to unsigned integer (nearest with ties to away).
|
| - void fcvtau(const Register& rd, const FPRegister& fn);
|
| -
|
| - // Convert FP to signed integer (nearest with ties to away).
|
| - void fcvtas(const Register& rd, const FPRegister& fn);
|
| -
|
| - // Convert FP to unsigned integer (round towards -infinity).
|
| - void fcvtmu(const Register& rd, const FPRegister& fn);
|
| -
|
| - // Convert FP to signed integer (round towards -infinity).
|
| - void fcvtms(const Register& rd, const FPRegister& fn);
|
| -
|
| - // Convert FP to unsigned integer (nearest with ties to even).
|
| - void fcvtnu(const Register& rd, const FPRegister& fn);
|
| -
|
| - // Convert FP to signed integer (nearest with ties to even).
|
| - void fcvtns(const Register& rd, const FPRegister& fn);
|
| -
|
| - // Convert FP to unsigned integer (round towards zero).
|
| - void fcvtzu(const Register& rd, const FPRegister& fn);
|
| -
|
| - // Convert FP to signed integer (rounf towards zero).
|
| - void fcvtzs(const Register& rd, const FPRegister& fn);
|
| -
|
| - // Convert signed integer or fixed point to FP.
|
| - void scvtf(const FPRegister& fd, const Register& rn, unsigned fbits = 0);
|
| -
|
| - // Convert unsigned integer or fixed point to FP.
|
| - void ucvtf(const FPRegister& fd, const Register& rn, unsigned fbits = 0);
|
| -
|
| - // Instruction functions used only for test, debug, and patching.
|
| - // Emit raw instructions in the instruction stream.
|
| - void dci(Instr raw_inst) { Emit(raw_inst); }
|
| -
|
| - // Emit 8 bits of data in the instruction stream.
|
| - void dc8(uint8_t data) { EmitData(&data, sizeof(data)); }
|
| -
|
| - // Emit 32 bits of data in the instruction stream.
|
| - void dc32(uint32_t data) { EmitData(&data, sizeof(data)); }
|
| -
|
| - // Emit 64 bits of data in the instruction stream.
|
| - void dc64(uint64_t data) { EmitData(&data, sizeof(data)); }
|
| -
|
| - // Copy a string into the instruction stream, including the terminating NULL
|
| - // character. The instruction pointer (pc_) is then aligned correctly for
|
| - // subsequent instructions.
|
| - void EmitStringData(const char * string) {
|
| - size_t len = strlen(string) + 1;
|
| - ASSERT(RoundUp(len, kInstructionSize) <= static_cast<size_t>(kGap));
|
| - EmitData(string, len);
|
| - // Pad with NULL characters until pc_ is aligned.
|
| - const char pad[] = {'\0', '\0', '\0', '\0'};
|
| - STATIC_ASSERT(sizeof(pad) == kInstructionSize);
|
| - byte* next_pc = AlignUp(pc_, kInstructionSize);
|
| - EmitData(&pad, next_pc - pc_);
|
| - }
|
| -
|
| - // Pseudo-instructions ------------------------------------------------------
|
| -
|
| - // Parameters are described in a64/instructions-a64.h.
|
| - void debug(const char* message, uint32_t code, Instr params = BREAK);
|
| -
|
| - // Required by V8.
|
| - void dd(uint32_t data) { dc32(data); }
|
| - void db(uint8_t data) { dc8(data); }
|
| -
|
| - // Code generation helpers --------------------------------------------------
|
| -
|
| - unsigned num_pending_reloc_info() const { return num_pending_reloc_info_; }
|
| -
|
| - Instruction* InstructionAt(int offset) const {
|
| - return reinterpret_cast<Instruction*>(buffer_ + offset);
|
| - }
|
| -
|
| - // Register encoding.
|
| - static Instr Rd(CPURegister rd) {
|
| - ASSERT(rd.code() != kSPRegInternalCode);
|
| - return rd.code() << Rd_offset;
|
| - }
|
| -
|
| - static Instr Rn(CPURegister rn) {
|
| - ASSERT(rn.code() != kSPRegInternalCode);
|
| - return rn.code() << Rn_offset;
|
| - }
|
| -
|
| - static Instr Rm(CPURegister rm) {
|
| - ASSERT(rm.code() != kSPRegInternalCode);
|
| - return rm.code() << Rm_offset;
|
| - }
|
| -
|
| - static Instr Ra(CPURegister ra) {
|
| - ASSERT(ra.code() != kSPRegInternalCode);
|
| - return ra.code() << Ra_offset;
|
| - }
|
| -
|
| - static Instr Rt(CPURegister rt) {
|
| - ASSERT(rt.code() != kSPRegInternalCode);
|
| - return rt.code() << Rt_offset;
|
| - }
|
| -
|
| - static Instr Rt2(CPURegister rt2) {
|
| - ASSERT(rt2.code() != kSPRegInternalCode);
|
| - return rt2.code() << Rt2_offset;
|
| - }
|
| -
|
| - // These encoding functions allow the stack pointer to be encoded, and
|
| - // disallow the zero register.
|
| - static Instr RdSP(Register rd) {
|
| - ASSERT(!rd.IsZero());
|
| - return (rd.code() & kRegCodeMask) << Rd_offset;
|
| - }
|
| -
|
| - static Instr RnSP(Register rn) {
|
| - ASSERT(!rn.IsZero());
|
| - return (rn.code() & kRegCodeMask) << Rn_offset;
|
| - }
|
| -
|
| - // Flags encoding.
|
| - inline static Instr Flags(FlagsUpdate S);
|
| - inline static Instr Cond(Condition cond);
|
| -
|
| - // PC-relative address encoding.
|
| - inline static Instr ImmPCRelAddress(int imm21);
|
| -
|
| - // Branch encoding.
|
| - inline static Instr ImmUncondBranch(int imm26);
|
| - inline static Instr ImmCondBranch(int imm19);
|
| - inline static Instr ImmCmpBranch(int imm19);
|
| - inline static Instr ImmTestBranch(int imm14);
|
| - inline static Instr ImmTestBranchBit(unsigned bit_pos);
|
| -
|
| - // Data Processing encoding.
|
| - inline static Instr SF(Register rd);
|
| - inline static Instr ImmAddSub(int64_t imm);
|
| - inline static Instr ImmS(unsigned imms, unsigned reg_size);
|
| - inline static Instr ImmR(unsigned immr, unsigned reg_size);
|
| - inline static Instr ImmSetBits(unsigned imms, unsigned reg_size);
|
| - inline static Instr ImmRotate(unsigned immr, unsigned reg_size);
|
| - inline static Instr ImmLLiteral(int imm19);
|
| - inline static Instr BitN(unsigned bitn, unsigned reg_size);
|
| - inline static Instr ShiftDP(Shift shift);
|
| - inline static Instr ImmDPShift(unsigned amount);
|
| - inline static Instr ExtendMode(Extend extend);
|
| - inline static Instr ImmExtendShift(unsigned left_shift);
|
| - inline static Instr ImmCondCmp(unsigned imm);
|
| - inline static Instr Nzcv(StatusFlags nzcv);
|
| -
|
| - // MemOperand offset encoding.
|
| - inline static Instr ImmLSUnsigned(int imm12);
|
| - inline static Instr ImmLS(int imm9);
|
| - inline static Instr ImmLSPair(int imm7, LSDataSize size);
|
| - inline static Instr ImmShiftLS(unsigned shift_amount);
|
| - inline static Instr ImmException(int imm16);
|
| - inline static Instr ImmSystemRegister(int imm15);
|
| - inline static Instr ImmHint(int imm7);
|
| - inline static Instr ImmBarrierDomain(int imm2);
|
| - inline static Instr ImmBarrierType(int imm2);
|
| - inline static LSDataSize CalcLSDataSize(LoadStoreOp op);
|
| -
|
| - // Move immediates encoding.
|
| - inline static Instr ImmMoveWide(uint64_t imm);
|
| - inline static Instr ShiftMoveWide(int64_t shift);
|
| -
|
| - // FP Immediates.
|
| - static Instr ImmFP32(float imm);
|
| - static Instr ImmFP64(double imm);
|
| - inline static Instr FPScale(unsigned scale);
|
| -
|
| - // FP register type.
|
| - inline static Instr FPType(FPRegister fd);
|
| -
|
| - // Class for scoping postponing the constant pool generation.
|
| - class BlockConstPoolScope {
|
| - public:
|
| - explicit BlockConstPoolScope(Assembler* assem) : assem_(assem) {
|
| - assem_->StartBlockConstPool();
|
| - }
|
| - ~BlockConstPoolScope() {
|
| - assem_->EndBlockConstPool();
|
| - }
|
| -
|
| - private:
|
| - Assembler* assem_;
|
| -
|
| - DISALLOW_IMPLICIT_CONSTRUCTORS(BlockConstPoolScope);
|
| - };
|
| -
|
| - // Check if is time to emit a constant pool.
|
| - void CheckConstPool(bool force_emit, bool require_jump);
|
| -
|
| - // Allocate a constant pool of the correct size for the generated code.
|
| - MaybeObject* AllocateConstantPool(Heap* heap);
|
| -
|
| - // Generate the constant pool for the generated code.
|
| - void PopulateConstantPool(ConstantPoolArray* constant_pool);
|
| -
|
| - // Returns true if we should emit a veneer as soon as possible for a branch
|
| - // which can at most reach to specified pc.
|
| - bool ShouldEmitVeneer(int max_reachable_pc,
|
| - int margin = kVeneerDistanceMargin);
|
| - bool ShouldEmitVeneers(int margin = kVeneerDistanceMargin) {
|
| - return ShouldEmitVeneer(unresolved_branches_first_limit(), margin);
|
| - }
|
| -
|
| - // The maximum code size generated for a veneer. Currently one branch
|
| - // instruction. This is for code size checking purposes, and can be extended
|
| - // in the future for example if we decide to add nops between the veneers.
|
| - static const int kMaxVeneerCodeSize = 1 * kInstructionSize;
|
| -
|
| - void RecordVeneerPool(int location_offset, int size);
|
| - // Emits veneers for branches that are approaching their maximum range.
|
| - // If need_protection is true, the veneers are protected by a branch jumping
|
| - // over the code.
|
| - void EmitVeneers(bool force_emit, bool need_protection,
|
| - int margin = kVeneerDistanceMargin);
|
| - void EmitVeneersGuard() { EmitPoolGuard(); }
|
| - // Checks whether veneers need to be emitted at this point.
|
| - // If force_emit is set, a veneer is generated for *all* unresolved branches.
|
| - void CheckVeneerPool(bool force_emit, bool require_jump,
|
| - int margin = kVeneerDistanceMargin);
|
| -
|
| -
|
| - class BlockPoolsScope {
|
| - public:
|
| - explicit BlockPoolsScope(Assembler* assem) : assem_(assem) {
|
| - assem_->StartBlockPools();
|
| - }
|
| - ~BlockPoolsScope() {
|
| - assem_->EndBlockPools();
|
| - }
|
| -
|
| - private:
|
| - Assembler* assem_;
|
| -
|
| - DISALLOW_IMPLICIT_CONSTRUCTORS(BlockPoolsScope);
|
| - };
|
| -
|
| - // Available for constrained code generation scopes. Prefer
|
| - // MacroAssembler::Mov() when possible.
|
| - inline void LoadRelocated(const CPURegister& rt, const Operand& operand);
|
| -
|
| - protected:
|
| - inline const Register& AppropriateZeroRegFor(const CPURegister& reg) const;
|
| -
|
| - void LoadStore(const CPURegister& rt,
|
| - const MemOperand& addr,
|
| - LoadStoreOp op);
|
| - static bool IsImmLSUnscaled(ptrdiff_t offset);
|
| - static bool IsImmLSScaled(ptrdiff_t offset, LSDataSize size);
|
| -
|
| - void Logical(const Register& rd,
|
| - const Register& rn,
|
| - const Operand& operand,
|
| - LogicalOp op);
|
| - void LogicalImmediate(const Register& rd,
|
| - const Register& rn,
|
| - unsigned n,
|
| - unsigned imm_s,
|
| - unsigned imm_r,
|
| - LogicalOp op);
|
| - static bool IsImmLogical(uint64_t value,
|
| - unsigned width,
|
| - unsigned* n,
|
| - unsigned* imm_s,
|
| - unsigned* imm_r);
|
| -
|
| - void ConditionalCompare(const Register& rn,
|
| - const Operand& operand,
|
| - StatusFlags nzcv,
|
| - Condition cond,
|
| - ConditionalCompareOp op);
|
| - static bool IsImmConditionalCompare(int64_t immediate);
|
| -
|
| - void AddSubWithCarry(const Register& rd,
|
| - const Register& rn,
|
| - const Operand& operand,
|
| - FlagsUpdate S,
|
| - AddSubWithCarryOp op);
|
| -
|
| - // Functions for emulating operands not directly supported by the instruction
|
| - // set.
|
| - void EmitShift(const Register& rd,
|
| - const Register& rn,
|
| - Shift shift,
|
| - unsigned amount);
|
| - void EmitExtendShift(const Register& rd,
|
| - const Register& rn,
|
| - Extend extend,
|
| - unsigned left_shift);
|
| -
|
| - void AddSub(const Register& rd,
|
| - const Register& rn,
|
| - const Operand& operand,
|
| - FlagsUpdate S,
|
| - AddSubOp op);
|
| - static bool IsImmAddSub(int64_t immediate);
|
| -
|
| - static bool IsImmFP32(float imm);
|
| - static bool IsImmFP64(double imm);
|
| -
|
| - // Find an appropriate LoadStoreOp or LoadStorePairOp for the specified
|
| - // registers. Only simple loads are supported; sign- and zero-extension (such
|
| - // as in LDPSW_x or LDRB_w) are not supported.
|
| - static inline LoadStoreOp LoadOpFor(const CPURegister& rt);
|
| - static inline LoadStorePairOp LoadPairOpFor(const CPURegister& rt,
|
| - const CPURegister& rt2);
|
| - static inline LoadStoreOp StoreOpFor(const CPURegister& rt);
|
| - static inline LoadStorePairOp StorePairOpFor(const CPURegister& rt,
|
| - const CPURegister& rt2);
|
| - static inline LoadStorePairNonTemporalOp LoadPairNonTemporalOpFor(
|
| - const CPURegister& rt, const CPURegister& rt2);
|
| - static inline LoadStorePairNonTemporalOp StorePairNonTemporalOpFor(
|
| - const CPURegister& rt, const CPURegister& rt2);
|
| -
|
| - // Remove the specified branch from the unbound label link chain.
|
| - // If available, a veneer for this label can be used for other branches in the
|
| - // chain if the link chain cannot be fixed up without this branch.
|
| - void RemoveBranchFromLabelLinkChain(Instruction* branch,
|
| - Label* label,
|
| - Instruction* label_veneer = NULL);
|
| -
|
| - private:
|
| - // Instruction helpers.
|
| - void MoveWide(const Register& rd,
|
| - uint64_t imm,
|
| - int shift,
|
| - MoveWideImmediateOp mov_op);
|
| - void DataProcShiftedRegister(const Register& rd,
|
| - const Register& rn,
|
| - const Operand& operand,
|
| - FlagsUpdate S,
|
| - Instr op);
|
| - void DataProcExtendedRegister(const Register& rd,
|
| - const Register& rn,
|
| - const Operand& operand,
|
| - FlagsUpdate S,
|
| - Instr op);
|
| - void LoadStorePair(const CPURegister& rt,
|
| - const CPURegister& rt2,
|
| - const MemOperand& addr,
|
| - LoadStorePairOp op);
|
| - void LoadStorePairNonTemporal(const CPURegister& rt,
|
| - const CPURegister& rt2,
|
| - const MemOperand& addr,
|
| - LoadStorePairNonTemporalOp op);
|
| - // Register the relocation information for the operand and load its value
|
| - // into rt.
|
| - void LoadRelocatedValue(const CPURegister& rt,
|
| - const Operand& operand,
|
| - LoadLiteralOp op);
|
| - void ConditionalSelect(const Register& rd,
|
| - const Register& rn,
|
| - const Register& rm,
|
| - Condition cond,
|
| - ConditionalSelectOp op);
|
| - void DataProcessing1Source(const Register& rd,
|
| - const Register& rn,
|
| - DataProcessing1SourceOp op);
|
| - void DataProcessing3Source(const Register& rd,
|
| - const Register& rn,
|
| - const Register& rm,
|
| - const Register& ra,
|
| - DataProcessing3SourceOp op);
|
| - void FPDataProcessing1Source(const FPRegister& fd,
|
| - const FPRegister& fn,
|
| - FPDataProcessing1SourceOp op);
|
| - void FPDataProcessing2Source(const FPRegister& fd,
|
| - const FPRegister& fn,
|
| - const FPRegister& fm,
|
| - FPDataProcessing2SourceOp op);
|
| - void FPDataProcessing3Source(const FPRegister& fd,
|
| - const FPRegister& fn,
|
| - const FPRegister& fm,
|
| - const FPRegister& fa,
|
| - FPDataProcessing3SourceOp op);
|
| -
|
| - // Label helpers.
|
| -
|
| - // Return an offset for a label-referencing instruction, typically a branch.
|
| - int LinkAndGetByteOffsetTo(Label* label);
|
| -
|
| - // This is the same as LinkAndGetByteOffsetTo, but return an offset
|
| - // suitable for fields that take instruction offsets.
|
| - inline int LinkAndGetInstructionOffsetTo(Label* label);
|
| -
|
| - static const int kStartOfLabelLinkChain = 0;
|
| -
|
| - // Verify that a label's link chain is intact.
|
| - void CheckLabelLinkChain(Label const * label);
|
| -
|
| - void RecordLiteral(int64_t imm, unsigned size);
|
| -
|
| - // Postpone the generation of the constant pool for the specified number of
|
| - // instructions.
|
| - void BlockConstPoolFor(int instructions);
|
| -
|
| - // Emit the instruction at pc_.
|
| - void Emit(Instr instruction) {
|
| - STATIC_ASSERT(sizeof(*pc_) == 1);
|
| - STATIC_ASSERT(sizeof(instruction) == kInstructionSize);
|
| - ASSERT((pc_ + sizeof(instruction)) <= (buffer_ + buffer_size_));
|
| -
|
| - memcpy(pc_, &instruction, sizeof(instruction));
|
| - pc_ += sizeof(instruction);
|
| - CheckBuffer();
|
| - }
|
| -
|
| - // Emit data inline in the instruction stream.
|
| - void EmitData(void const * data, unsigned size) {
|
| - ASSERT(sizeof(*pc_) == 1);
|
| - ASSERT((pc_ + size) <= (buffer_ + buffer_size_));
|
| -
|
| - // TODO(all): Somehow register we have some data here. Then we can
|
| - // disassemble it correctly.
|
| - memcpy(pc_, data, size);
|
| - pc_ += size;
|
| - CheckBuffer();
|
| - }
|
| -
|
| - void GrowBuffer();
|
| - void CheckBuffer();
|
| -
|
| - // Pc offset of the next constant pool check.
|
| - int next_constant_pool_check_;
|
| -
|
| - // Constant pool generation
|
| - // Pools are emitted in the instruction stream, preferably after unconditional
|
| - // jumps or after returns from functions (in dead code locations).
|
| - // If a long code sequence does not contain unconditional jumps, it is
|
| - // necessary to emit the constant pool before the pool gets too far from the
|
| - // location it is accessed from. In this case, we emit a jump over the emitted
|
| - // constant pool.
|
| - // Constants in the pool may be addresses of functions that gets relocated;
|
| - // if so, a relocation info entry is associated to the constant pool entry.
|
| -
|
| - // Repeated checking whether the constant pool should be emitted is rather
|
| - // expensive. By default we only check again once a number of instructions
|
| - // has been generated. That also means that the sizing of the buffers is not
|
| - // an exact science, and that we rely on some slop to not overrun buffers.
|
| - static const int kCheckConstPoolIntervalInst = 128;
|
| - static const int kCheckConstPoolInterval =
|
| - kCheckConstPoolIntervalInst * kInstructionSize;
|
| -
|
| - // Constants in pools are accessed via pc relative addressing, which can
|
| - // reach +/-4KB thereby defining a maximum distance between the instruction
|
| - // and the accessed constant.
|
| - static const int kMaxDistToConstPool = 4 * KB;
|
| - static const int kMaxNumPendingRelocInfo =
|
| - kMaxDistToConstPool / kInstructionSize;
|
| -
|
| -
|
| - // Average distance beetween a constant pool and the first instruction
|
| - // accessing the constant pool. Longer distance should result in less I-cache
|
| - // pollution.
|
| - // In practice the distance will be smaller since constant pool emission is
|
| - // forced after function return and sometimes after unconditional branches.
|
| - static const int kAvgDistToConstPool =
|
| - kMaxDistToConstPool - kCheckConstPoolInterval;
|
| -
|
| - // Emission of the constant pool may be blocked in some code sequences.
|
| - int const_pool_blocked_nesting_; // Block emission if this is not zero.
|
| - int no_const_pool_before_; // Block emission before this pc offset.
|
| -
|
| - // Keep track of the first instruction requiring a constant pool entry
|
| - // since the previous constant pool was emitted.
|
| - int first_const_pool_use_;
|
| -
|
| - // Emission of the veneer pools may be blocked in some code sequences.
|
| - int veneer_pool_blocked_nesting_; // Block emission if this is not zero.
|
| -
|
| - // Relocation info generation
|
| - // Each relocation is encoded as a variable size value
|
| - static const int kMaxRelocSize = RelocInfoWriter::kMaxSize;
|
| - RelocInfoWriter reloc_info_writer;
|
| -
|
| - // Relocation info records are also used during code generation as temporary
|
| - // containers for constants and code target addresses until they are emitted
|
| - // to the constant pool. These pending relocation info records are temporarily
|
| - // stored in a separate buffer until a constant pool is emitted.
|
| - // If every instruction in a long sequence is accessing the pool, we need one
|
| - // pending relocation entry per instruction.
|
| -
|
| - // the buffer of pending relocation info
|
| - RelocInfo pending_reloc_info_[kMaxNumPendingRelocInfo];
|
| - // number of pending reloc info entries in the buffer
|
| - int num_pending_reloc_info_;
|
| -
|
| - // Relocation for a type-recording IC has the AST id added to it. This
|
| - // member variable is a way to pass the information from the call site to
|
| - // the relocation info.
|
| - TypeFeedbackId recorded_ast_id_;
|
| -
|
| - inline TypeFeedbackId RecordedAstId();
|
| - inline void ClearRecordedAstId();
|
| -
|
| - protected:
|
| - // Record the AST id of the CallIC being compiled, so that it can be placed
|
| - // in the relocation information.
|
| - void SetRecordedAstId(TypeFeedbackId ast_id) {
|
| - ASSERT(recorded_ast_id_.IsNone());
|
| - recorded_ast_id_ = ast_id;
|
| - }
|
| -
|
| - // Code generation
|
| - // The relocation writer's position is at least kGap bytes below the end of
|
| - // the generated instructions. This is so that multi-instruction sequences do
|
| - // not have to check for overflow. The same is true for writes of large
|
| - // relocation info entries, and debug strings encoded in the instruction
|
| - // stream.
|
| - static const int kGap = 128;
|
| -
|
| - public:
|
| - class FarBranchInfo {
|
| - public:
|
| - FarBranchInfo(int offset, Label* label)
|
| - : pc_offset_(offset), label_(label) {}
|
| - // Offset of the branch in the code generation buffer.
|
| - int pc_offset_;
|
| - // The label branched to.
|
| - Label* label_;
|
| - };
|
| -
|
| - protected:
|
| - // Information about unresolved (forward) branches.
|
| - // The Assembler is only allowed to delete out-of-date information from here
|
| - // after a label is bound. The MacroAssembler uses this information to
|
| - // generate veneers.
|
| - //
|
| - // The second member gives information about the unresolved branch. The first
|
| - // member of the pair is the maximum offset that the branch can reach in the
|
| - // buffer. The map is sorted according to this reachable offset, allowing to
|
| - // easily check when veneers need to be emitted.
|
| - // Note that the maximum reachable offset (first member of the pairs) should
|
| - // always be positive but has the same type as the return value for
|
| - // pc_offset() for convenience.
|
| - std::multimap<int, FarBranchInfo> unresolved_branches_;
|
| -
|
| - // We generate a veneer for a branch if we reach within this distance of the
|
| - // limit of the range.
|
| - static const int kVeneerDistanceMargin = 1 * KB;
|
| - // The factor of 2 is a finger in the air guess. With a default margin of
|
| - // 1KB, that leaves us an addional 256 instructions to avoid generating a
|
| - // protective branch.
|
| - static const int kVeneerNoProtectionFactor = 2;
|
| - static const int kVeneerDistanceCheckMargin =
|
| - kVeneerNoProtectionFactor * kVeneerDistanceMargin;
|
| - int unresolved_branches_first_limit() const {
|
| - ASSERT(!unresolved_branches_.empty());
|
| - return unresolved_branches_.begin()->first;
|
| - }
|
| - // This is similar to next_constant_pool_check_ and helps reduce the overhead
|
| - // of checking for veneer pools.
|
| - // It is maintained to the closest unresolved branch limit minus the maximum
|
| - // veneer margin (or kMaxInt if there are no unresolved branches).
|
| - int next_veneer_pool_check_;
|
| -
|
| - private:
|
| - // If a veneer is emitted for a branch instruction, that instruction must be
|
| - // removed from the associated label's link chain so that the assembler does
|
| - // not later attempt (likely unsuccessfully) to patch it to branch directly to
|
| - // the label.
|
| - void DeleteUnresolvedBranchInfoForLabel(Label* label);
|
| -
|
| - private:
|
| - PositionsRecorder positions_recorder_;
|
| - friend class PositionsRecorder;
|
| - friend class EnsureSpace;
|
| -};
|
| -
|
| -class PatchingAssembler : public Assembler {
|
| - public:
|
| - // Create an Assembler with a buffer starting at 'start'.
|
| - // The buffer size is
|
| - // size of instructions to patch + kGap
|
| - // Where kGap is the distance from which the Assembler tries to grow the
|
| - // buffer.
|
| - // If more or fewer instructions than expected are generated or if some
|
| - // relocation information takes space in the buffer, the PatchingAssembler
|
| - // will crash trying to grow the buffer.
|
| - PatchingAssembler(Instruction* start, unsigned count)
|
| - : Assembler(NULL,
|
| - reinterpret_cast<byte*>(start),
|
| - count * kInstructionSize + kGap) {
|
| - StartBlockPools();
|
| - }
|
| -
|
| - PatchingAssembler(byte* start, unsigned count)
|
| - : Assembler(NULL, start, count * kInstructionSize + kGap) {
|
| - // Block constant pool emission.
|
| - StartBlockPools();
|
| - }
|
| -
|
| - ~PatchingAssembler() {
|
| - // Const pool should still be blocked.
|
| - ASSERT(is_const_pool_blocked());
|
| - EndBlockPools();
|
| - // Verify we have generated the number of instruction we expected.
|
| - ASSERT((pc_offset() + kGap) == buffer_size_);
|
| - // Verify no relocation information has been emitted.
|
| - ASSERT(num_pending_reloc_info() == 0);
|
| - // Flush the Instruction cache.
|
| - size_t length = buffer_size_ - kGap;
|
| - CPU::FlushICache(buffer_, length);
|
| - }
|
| -};
|
| -
|
| -
|
| -class EnsureSpace BASE_EMBEDDED {
|
| - public:
|
| - explicit EnsureSpace(Assembler* assembler) {
|
| - assembler->CheckBuffer();
|
| - }
|
| -};
|
| -
|
| -} } // namespace v8::internal
|
| -
|
| -#endif // V8_A64_ASSEMBLER_A64_H_
|
|
|