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_ |