| Index: src/a64/assembler-a64.h
|
| diff --git a/src/a64/assembler-a64.h b/src/a64/assembler-a64.h
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..18c5b70c76df38ce6a0fa3c7b8ea04384b9c3e27
|
| --- /dev/null
|
| +++ b/src/a64/assembler-a64.h
|
| @@ -0,0 +1,2045 @@
|
| +// 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 "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.
|
| +class Register;
|
| +class 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, kXRegSize);
|
| + }
|
| +
|
| + // 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:
|
| + // - d29 which is used in crankshaft as a double scratch register
|
| + // - d30 which is used to keep the 0 double value
|
| + // - d31 which is used in the MacroAssembler as a double scratch register
|
| + static const int kNumReservedRegisters = 3;
|
| + static const int kMaxNumAllocatableRegisters =
|
| + kNumberOfFPRegisters - kNumReservedRegisters;
|
| + static int NumAllocatableRegisters() { return kMaxNumAllocatableRegisters; }
|
| + static const RegList kAllocatableFPRegisters =
|
| + (1 << kMaxNumAllocatableRegisters) - 1;
|
| +
|
| + static FPRegister FromAllocationIndex(int index) {
|
| + ASSERT((index >= 0) && (index < NumAllocatableRegisters()));
|
| + return from_code(index);
|
| + }
|
| +
|
| + static const char* AllocationIndexToString(int index) {
|
| + ASSERT((index >= 0) && (index < NumAllocatableRegisters()));
|
| + const char* const names[] = {
|
| + "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7",
|
| + "d8", "d9", "d10", "d11", "d12", "d13", "d14", "d15",
|
| + "d16", "d17", "d18", "d19", "d20", "d21", "d22", "d23",
|
| + "d24", "d25", "d26", "d27", "d28",
|
| + };
|
| + return names[index];
|
| + }
|
| +
|
| + static int ToAllocationIndex(FPRegister reg) {
|
| + int code = reg.code();
|
| + ASSERT(code < NumAllocatableRegisters());
|
| + return code;
|
| + }
|
| +
|
| + static FPRegister from_code(int code) {
|
| + // Always return a D register.
|
| + return FPRegister::Create(code, kDRegSize);
|
| + }
|
| + // 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, kWRegSize, CPURegister::kRegister); \
|
| + INITIALIZE_REGISTER(Register, x##N, N, kXRegSize, CPURegister::kRegister);
|
| +REGISTER_CODE_LIST(DEFINE_REGISTERS)
|
| +#undef DEFINE_REGISTERS
|
| +
|
| +INITIALIZE_REGISTER(Register, wcsp, kSPRegInternalCode, kWRegSize,
|
| + CPURegister::kRegister);
|
| +INITIALIZE_REGISTER(Register, csp, kSPRegInternalCode, kXRegSize,
|
| + CPURegister::kRegister);
|
| +
|
| +#define DEFINE_FPREGISTERS(N) \
|
| + INITIALIZE_REGISTER(FPRegister, s##N, N, kSRegSize, \
|
| + CPURegister::kFPRegister); \
|
| + INITIALIZE_REGISTER(FPRegister, d##N, N, kDRegSize, 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);
|
| +
|
| +// Crankshaft double scratch register.
|
| +ALIAS_REGISTER(FPRegister, crankshaft_fp_scratch, d29);
|
| +// Keeps the 0 double value.
|
| +ALIAS_REGISTER(FPRegister, fp_zero, d30);
|
| +// MacroAssembler double scratch register.
|
| +ALIAS_REGISTER(FPRegister, fp_scratch, d31);
|
| +
|
| +#undef ALIAS_REGISTER
|
| +
|
| +// 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_;
|
| + }
|
| +
|
| + // 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 a single register.
|
| + void Combine(const CPURegister& other);
|
| + void Remove(const CPURegister& other);
|
| +
|
| + // 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 = kXRegSize);
|
| + static CPURegList GetCalleeSavedFP(unsigned size = kDRegSize);
|
| +
|
| + // AAPCS64 caller-saved registers. Note that this includes lr.
|
| + static CPURegList GetCallerSaved(unsigned size = kXRegSize);
|
| + static CPURegList GetCallerSavedFP(unsigned size = kDRegSize);
|
| +
|
| + // Registers saved as safepoints.
|
| + static CPURegList GetSafepointSavedRegisters();
|
| +
|
| + bool IsEmpty() const {
|
| + ASSERT(IsValid());
|
| + return list_ == 0;
|
| + }
|
| +
|
| + bool IncludesAliasOf(const CPURegister& other) const {
|
| + ASSERT(IsValid());
|
| + return (type_ == other.type()) && (other.Bit() & list_);
|
| + }
|
| +
|
| + 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:
|
| + // #<immediate>
|
| + // where <immediate> is int64_t.
|
| + // GCC complains about ambiguous aliasing if we don't explicitly declare the
|
| + // variants.
|
| + // The simple literal-value wrappers are allowed to be implicit constructors
|
| + // because Operand is a wrapper class that doesn't normally perform any type
|
| + // conversion.
|
| + inline Operand(int64_t immediate,
|
| + RelocInfo::Mode rmode = RelocInfo::NONE64); // NOLINT(runtime/explicit)
|
| + inline Operand(uint64_t immediate,
|
| + RelocInfo::Mode rmode = RelocInfo::NONE64); // NOLINT(runtime/explicit)
|
| + inline Operand(int32_t immediate,
|
| + RelocInfo::Mode rmode = RelocInfo::NONE32); // NOLINT(runtime/explicit)
|
| + inline Operand(uint32_t immediate,
|
| + RelocInfo::Mode rmode = RelocInfo::NONE32); // NOLINT(runtime/explicit)
|
| +
|
| +
|
| + // 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);
|
| +
|
| + inline explicit Operand(Smi* value);
|
| + explicit Operand(const ExternalReference& f);
|
| + explicit Operand(Handle<Object> handle);
|
| +
|
| + 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:
|
| + 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();
|
| +
|
| + // 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);
|
| +
|
| + // 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 constant pool ----------------------------------------------
|
| +
|
| + // 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);
|
| + inline static void set_target_address_at(Address pc, 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, 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;
|
| + }
|
| +
|
| + // TODO(all): Initialize these constants related with code patching.
|
| + // TODO(all): Set to -1 to hopefully crash if mistakenly used.
|
| +
|
| + // 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 ConstantPoolGuard();
|
| +
|
| +
|
| + // 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 pool 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 constant
|
| + // pools and cause the version of the code with debugger support to have
|
| + // constant pools generated in different places.
|
| + // Recording the position and size of emitted constant 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 constant 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);
|
| +
|
| + // 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);
|
| +
|
| + // 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);
|
| +
|
| + // 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);
|
| +
|
| +
|
| + 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 buffer check.
|
| + int next_buffer_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 kCheckPoolIntervalInst = 128;
|
| + static const int kCheckPoolInterval =
|
| + kCheckPoolIntervalInst * 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 kMaxDistToPool = 4 * KB;
|
| + static const int kMaxNumPendingRelocInfo = kMaxDistToPool / 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 kAvgDistToPool = kMaxDistToPool - kCheckPoolInterval;
|
| +
|
| + // 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_;
|
| +
|
| + // 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;
|
| +
|
| + private:
|
| + // TODO(jbramley): VIXL uses next_literal_pool_check_ and
|
| + // literal_pool_monitor_ to determine when to consider emitting a literal
|
| + // pool. V8 doesn't use them, so they should either not be here at all, or
|
| + // should replace or be merged with next_buffer_check_ and
|
| + // const_pool_blocked_nesting_.
|
| + Instruction* next_literal_pool_check_;
|
| + unsigned literal_pool_monitor_;
|
| +
|
| + 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) {
|
| + // Block constant pool emission.
|
| + StartBlockConstPool();
|
| + }
|
| +
|
| + PatchingAssembler(byte* start, unsigned count)
|
| + : Assembler(NULL, start, count * kInstructionSize + kGap) {
|
| + // Block constant pool emission.
|
| + StartBlockConstPool();
|
| + }
|
| +
|
| + ~PatchingAssembler() {
|
| + // Const pool should still be blocked.
|
| + ASSERT(is_const_pool_blocked());
|
| + EndBlockConstPool();
|
| + // 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_
|
|
|