| // Copyright 2013 the V8 project authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #ifndef V8_CODEGEN_ARM64_INSTRUCTIONS_ARM64_H_ | 
 | #define V8_CODEGEN_ARM64_INSTRUCTIONS_ARM64_H_ | 
 |  | 
 | #include "src/base/memory.h" | 
 | #include "src/codegen/arm64/constants-arm64.h" | 
 | #include "src/codegen/arm64/register-arm64.h" | 
 | #include "src/codegen/arm64/utils-arm64.h" | 
 | #include "src/common/globals.h" | 
 | #include "src/utils/utils.h" | 
 |  | 
 | namespace v8 { | 
 | namespace internal { | 
 |  | 
 | struct AssemblerOptions; | 
 |  | 
 | // ISA constants. -------------------------------------------------------------- | 
 |  | 
 | using Instr = uint32_t; | 
 |  | 
 | #if defined(V8_OS_WIN) | 
 | extern "C" { | 
 | #endif | 
 |  | 
 | extern const float16 kFP16PositiveInfinity; | 
 | extern const float16 kFP16NegativeInfinity; | 
 | V8_EXPORT_PRIVATE extern const float kFP32PositiveInfinity; | 
 | V8_EXPORT_PRIVATE extern const float kFP32NegativeInfinity; | 
 | V8_EXPORT_PRIVATE extern const double kFP64PositiveInfinity; | 
 | V8_EXPORT_PRIVATE extern const double kFP64NegativeInfinity; | 
 |  | 
 | // This value is a signalling NaN as both a double and as a float (taking the | 
 | // least-significant word). | 
 | V8_EXPORT_PRIVATE extern const double kFP64SignallingNaN; | 
 | V8_EXPORT_PRIVATE extern const float kFP32SignallingNaN; | 
 |  | 
 | // A similar value, but as a quiet NaN. | 
 | V8_EXPORT_PRIVATE extern const double kFP64QuietNaN; | 
 | V8_EXPORT_PRIVATE extern const float kFP32QuietNaN; | 
 |  | 
 | // The default NaN values (for FPCR.DN=1). | 
 | V8_EXPORT_PRIVATE extern const double kFP64DefaultNaN; | 
 | V8_EXPORT_PRIVATE extern const float kFP32DefaultNaN; | 
 | extern const float16 kFP16DefaultNaN; | 
 |  | 
 | #if defined(V8_OS_WIN) | 
 | }  // end of extern "C" | 
 | #endif | 
 |  | 
 | unsigned CalcLSDataSize(LoadStoreOp op); | 
 | unsigned CalcLSPairDataSize(LoadStorePairOp op); | 
 |  | 
 | enum ImmBranchType { | 
 |   UnknownBranchType = 0, | 
 |   CondBranchType = 1, | 
 |   UncondBranchType = 2, | 
 |   CompareBranchType = 3, | 
 |   TestBranchType = 4 | 
 | }; | 
 |  | 
 | enum AddrMode { Offset, PreIndex, PostIndex }; | 
 |  | 
 | enum FPRounding { | 
 |   // The first four values are encodable directly by FPCR<RMode>. | 
 |   FPTieEven = 0x0, | 
 |   FPPositiveInfinity = 0x1, | 
 |   FPNegativeInfinity = 0x2, | 
 |   FPZero = 0x3, | 
 |  | 
 |   // The final rounding modes are only available when explicitly specified by | 
 |   // the instruction (such as with fcvta). They cannot be set in FPCR. | 
 |   FPTieAway, | 
 |   FPRoundOdd | 
 | }; | 
 |  | 
 | enum Reg31Mode { Reg31IsStackPointer, Reg31IsZeroRegister }; | 
 |  | 
 | // Instructions. --------------------------------------------------------------- | 
 |  | 
 | class Instruction { | 
 |  public: | 
 |   V8_INLINE Instr InstructionBits() const { | 
 |     // Usually this is aligned, but when de/serializing that's not guaranteed. | 
 |     return base::ReadUnalignedValue<Instr>(reinterpret_cast<Address>(this)); | 
 |   } | 
 |  | 
 |   V8_INLINE void SetInstructionBits(Instr new_instr) { | 
 |     // Usually this is aligned, but when de/serializing that's not guaranteed. | 
 |     base::WriteUnalignedValue(reinterpret_cast<Address>(this), new_instr); | 
 |   } | 
 |  | 
 |   int Bit(int pos) const { return (InstructionBits() >> pos) & 1; } | 
 |  | 
 |   uint32_t Bits(int msb, int lsb) const { | 
 |     return unsigned_bitextract_32(msb, lsb, InstructionBits()); | 
 |   } | 
 |  | 
 |   int32_t SignedBits(int msb, int lsb) const { | 
 |     // Usually this is aligned, but when de/serializing that's not guaranteed. | 
 |     int32_t bits = | 
 |         base::ReadUnalignedValue<int32_t>(reinterpret_cast<Address>(this)); | 
 |     return signed_bitextract_32(msb, lsb, bits); | 
 |   } | 
 |  | 
 |   Instr Mask(uint32_t mask) const { return InstructionBits() & mask; } | 
 |  | 
 |   V8_INLINE const Instruction* following(int count = 1) const { | 
 |     return InstructionAtOffset(count * static_cast<int>(kInstrSize)); | 
 |   } | 
 |  | 
 |   V8_INLINE Instruction* following(int count = 1) { | 
 |     return InstructionAtOffset(count * static_cast<int>(kInstrSize)); | 
 |   } | 
 |  | 
 |   V8_INLINE const Instruction* preceding(int count = 1) const { | 
 |     return following(-count); | 
 |   } | 
 |  | 
 |   V8_INLINE Instruction* preceding(int count = 1) { return following(-count); } | 
 |  | 
 | #define DEFINE_GETTER(Name, HighBit, LowBit, Func) \ | 
 |   int32_t Name() const { return Func(HighBit, LowBit); } | 
 |   INSTRUCTION_FIELDS_LIST(DEFINE_GETTER) | 
 | #undef DEFINE_GETTER | 
 |  | 
 |   // ImmPCRel is a compound field (not present in INSTRUCTION_FIELDS_LIST), | 
 |   // formed from ImmPCRelLo and ImmPCRelHi. | 
 |   int ImmPCRel() const { | 
 |     DCHECK(IsPCRelAddressing()); | 
 |     int offset = (static_cast<uint32_t>(ImmPCRelHi()) << ImmPCRelLo_width) | | 
 |                  ImmPCRelLo(); | 
 |     int width = ImmPCRelLo_width + ImmPCRelHi_width; | 
 |     return signed_bitextract_32(width - 1, 0, offset); | 
 |   } | 
 |  | 
 |   uint64_t ImmLogical(); | 
 |   unsigned ImmNEONabcdefgh() const; | 
 |   float ImmFP32(); | 
 |   double ImmFP64(); | 
 |   float ImmNEONFP32() const; | 
 |   double ImmNEONFP64() const; | 
 |  | 
 |   unsigned SizeLS() const { | 
 |     return CalcLSDataSize(static_cast<LoadStoreOp>(Mask(LoadStoreMask))); | 
 |   } | 
 |  | 
 |   unsigned SizeLSPair() const { | 
 |     return CalcLSPairDataSize( | 
 |         static_cast<LoadStorePairOp>(Mask(LoadStorePairMask))); | 
 |   } | 
 |  | 
 |   int NEONLSIndex(int access_size_shift) const { | 
 |     int q = NEONQ(); | 
 |     int s = NEONS(); | 
 |     int size = NEONLSSize(); | 
 |     int index = (q << 3) | (s << 2) | size; | 
 |     return index >> access_size_shift; | 
 |   } | 
 |  | 
 |   // Helpers. | 
 |   bool IsCondBranchImm() const { | 
 |     return Mask(ConditionalBranchFMask) == ConditionalBranchFixed; | 
 |   } | 
 |  | 
 |   bool IsUncondBranchImm() const { | 
 |     return Mask(UnconditionalBranchFMask) == UnconditionalBranchFixed; | 
 |   } | 
 |  | 
 |   bool IsCompareBranch() const { | 
 |     return Mask(CompareBranchFMask) == CompareBranchFixed; | 
 |   } | 
 |  | 
 |   bool IsTestBranch() const { return Mask(TestBranchFMask) == TestBranchFixed; } | 
 |  | 
 |   bool IsImmBranch() const { return BranchType() != UnknownBranchType; } | 
 |  | 
 |   static float Imm8ToFP32(uint32_t imm8) { | 
 |     //   Imm8: abcdefgh (8 bits) | 
 |     // Single: aBbb.bbbc.defg.h000.0000.0000.0000.0000 (32 bits) | 
 |     // where B is b ^ 1 | 
 |     uint32_t bits = imm8; | 
 |     uint32_t bit7 = (bits >> 7) & 0x1; | 
 |     uint32_t bit6 = (bits >> 6) & 0x1; | 
 |     uint32_t bit5_to_0 = bits & 0x3f; | 
 |     uint32_t result = (bit7 << 31) | ((32 - bit6) << 25) | (bit5_to_0 << 19); | 
 |  | 
 |     return bit_cast<float>(result); | 
 |   } | 
 |  | 
 |   static double Imm8ToFP64(uint32_t imm8) { | 
 |     //   Imm8: abcdefgh (8 bits) | 
 |     // Double: aBbb.bbbb.bbcd.efgh.0000.0000.0000.0000 | 
 |     //         0000.0000.0000.0000.0000.0000.0000.0000 (64 bits) | 
 |     // where B is b ^ 1 | 
 |     uint32_t bits = imm8; | 
 |     uint64_t bit7 = (bits >> 7) & 0x1; | 
 |     uint64_t bit6 = (bits >> 6) & 0x1; | 
 |     uint64_t bit5_to_0 = bits & 0x3f; | 
 |     uint64_t result = (bit7 << 63) | ((256 - bit6) << 54) | (bit5_to_0 << 48); | 
 |  | 
 |     return bit_cast<double>(result); | 
 |   } | 
 |  | 
 |   bool IsLdrLiteral() const { | 
 |     return Mask(LoadLiteralFMask) == LoadLiteralFixed; | 
 |   } | 
 |  | 
 |   bool IsLdrLiteralX() const { return Mask(LoadLiteralMask) == LDR_x_lit; } | 
 |   bool IsLdrLiteralW() const { return Mask(LoadLiteralMask) == LDR_w_lit; } | 
 |  | 
 |   bool IsPCRelAddressing() const { | 
 |     return Mask(PCRelAddressingFMask) == PCRelAddressingFixed; | 
 |   } | 
 |  | 
 |   bool IsAdr() const { return Mask(PCRelAddressingMask) == ADR; } | 
 |  | 
 |   bool IsBrk() const { return Mask(ExceptionMask) == BRK; } | 
 |  | 
 |   bool IsUnresolvedInternalReference() const { | 
 |     // Unresolved internal references are encoded as two consecutive brk | 
 |     // instructions. | 
 |     return IsBrk() && following()->IsBrk(); | 
 |   } | 
 |  | 
 |   bool IsLogicalImmediate() const { | 
 |     return Mask(LogicalImmediateFMask) == LogicalImmediateFixed; | 
 |   } | 
 |  | 
 |   bool IsAddSubImmediate() const { | 
 |     return Mask(AddSubImmediateFMask) == AddSubImmediateFixed; | 
 |   } | 
 |  | 
 |   bool IsAddSubShifted() const { | 
 |     return Mask(AddSubShiftedFMask) == AddSubShiftedFixed; | 
 |   } | 
 |  | 
 |   bool IsAddSubExtended() const { | 
 |     return Mask(AddSubExtendedFMask) == AddSubExtendedFixed; | 
 |   } | 
 |  | 
 |   // Match any loads or stores, including pairs. | 
 |   bool IsLoadOrStore() const { | 
 |     return Mask(LoadStoreAnyFMask) == LoadStoreAnyFixed; | 
 |   } | 
 |  | 
 |   // Match any loads, including pairs. | 
 |   bool IsLoad() const; | 
 |   // Match any stores, including pairs. | 
 |   bool IsStore() const; | 
 |  | 
 |   // Indicate whether Rd can be the stack pointer or the zero register. This | 
 |   // does not check that the instruction actually has an Rd field. | 
 |   Reg31Mode RdMode() const { | 
 |     // The following instructions use sp or wsp as Rd: | 
 |     //  Add/sub (immediate) when not setting the flags. | 
 |     //  Add/sub (extended) when not setting the flags. | 
 |     //  Logical (immediate) when not setting the flags. | 
 |     // Otherwise, r31 is the zero register. | 
 |     if (IsAddSubImmediate() || IsAddSubExtended()) { | 
 |       if (Mask(AddSubSetFlagsBit)) { | 
 |         return Reg31IsZeroRegister; | 
 |       } else { | 
 |         return Reg31IsStackPointer; | 
 |       } | 
 |     } | 
 |     if (IsLogicalImmediate()) { | 
 |       // Of the logical (immediate) instructions, only ANDS (and its aliases) | 
 |       // can set the flags. The others can all write into sp. | 
 |       // Note that some logical operations are not available to | 
 |       // immediate-operand instructions, so we have to combine two masks here. | 
 |       if (Mask(LogicalImmediateMask & LogicalOpMask) == ANDS) { | 
 |         return Reg31IsZeroRegister; | 
 |       } else { | 
 |         return Reg31IsStackPointer; | 
 |       } | 
 |     } | 
 |     return Reg31IsZeroRegister; | 
 |   } | 
 |  | 
 |   // Indicate whether Rn can be the stack pointer or the zero register. This | 
 |   // does not check that the instruction actually has an Rn field. | 
 |   Reg31Mode RnMode() const { | 
 |     // The following instructions use sp or wsp as Rn: | 
 |     //  All loads and stores. | 
 |     //  Add/sub (immediate). | 
 |     //  Add/sub (extended). | 
 |     // Otherwise, r31 is the zero register. | 
 |     if (IsLoadOrStore() || IsAddSubImmediate() || IsAddSubExtended()) { | 
 |       return Reg31IsStackPointer; | 
 |     } | 
 |     return Reg31IsZeroRegister; | 
 |   } | 
 |  | 
 |   ImmBranchType BranchType() const { | 
 |     if (IsCondBranchImm()) { | 
 |       return CondBranchType; | 
 |     } else if (IsUncondBranchImm()) { | 
 |       return UncondBranchType; | 
 |     } else if (IsCompareBranch()) { | 
 |       return CompareBranchType; | 
 |     } else if (IsTestBranch()) { | 
 |       return TestBranchType; | 
 |     } else { | 
 |       return UnknownBranchType; | 
 |     } | 
 |   } | 
 |  | 
 |   static int ImmBranchRangeBitwidth(ImmBranchType branch_type) { | 
 |     switch (branch_type) { | 
 |       case UncondBranchType: | 
 |         return ImmUncondBranch_width; | 
 |       case CondBranchType: | 
 |         return ImmCondBranch_width; | 
 |       case CompareBranchType: | 
 |         return ImmCmpBranch_width; | 
 |       case TestBranchType: | 
 |         return ImmTestBranch_width; | 
 |       default: | 
 |         UNREACHABLE(); | 
 |     } | 
 |   } | 
 |  | 
 |   // The range of the branch instruction, expressed as 'instr +- range'. | 
 |   static int32_t ImmBranchRange(ImmBranchType branch_type) { | 
 |     return (1 << (ImmBranchRangeBitwidth(branch_type) + kInstrSizeLog2)) / 2 - | 
 |            kInstrSize; | 
 |   } | 
 |  | 
 |   int ImmBranch() const { | 
 |     switch (BranchType()) { | 
 |       case CondBranchType: | 
 |         return ImmCondBranch(); | 
 |       case UncondBranchType: | 
 |         return ImmUncondBranch(); | 
 |       case CompareBranchType: | 
 |         return ImmCmpBranch(); | 
 |       case TestBranchType: | 
 |         return ImmTestBranch(); | 
 |       default: | 
 |         UNREACHABLE(); | 
 |     } | 
 |     return 0; | 
 |   } | 
 |  | 
 |   int ImmUnresolvedInternalReference() const { | 
 |     DCHECK(IsUnresolvedInternalReference()); | 
 |     // Unresolved references are encoded as two consecutive brk instructions. | 
 |     // The associated immediate is made of the two 16-bit payloads. | 
 |     int32_t high16 = ImmException(); | 
 |     int32_t low16 = following()->ImmException(); | 
 |     return (high16 << 16) | low16; | 
 |   } | 
 |  | 
 |   bool IsUnconditionalBranch() const { | 
 |     return Mask(UnconditionalBranchMask) == B; | 
 |   } | 
 |  | 
 |   bool IsBranchAndLink() const { return Mask(UnconditionalBranchMask) == BL; } | 
 |  | 
 |   bool IsBranchAndLinkToRegister() const { | 
 |     return Mask(UnconditionalBranchToRegisterMask) == BLR; | 
 |   } | 
 |  | 
 |   bool IsMovz() const { | 
 |     return (Mask(MoveWideImmediateMask) == MOVZ_x) || | 
 |            (Mask(MoveWideImmediateMask) == MOVZ_w); | 
 |   } | 
 |  | 
 |   bool IsMovk() const { | 
 |     return (Mask(MoveWideImmediateMask) == MOVK_x) || | 
 |            (Mask(MoveWideImmediateMask) == MOVK_w); | 
 |   } | 
 |  | 
 |   bool IsMovn() const { | 
 |     return (Mask(MoveWideImmediateMask) == MOVN_x) || | 
 |            (Mask(MoveWideImmediateMask) == MOVN_w); | 
 |   } | 
 |  | 
 |   bool IsException() const { return Mask(ExceptionFMask) == ExceptionFixed; } | 
 |  | 
 |   bool IsPAuth() const { return Mask(SystemPAuthFMask) == SystemPAuthFixed; } | 
 |  | 
 |   bool IsBti() const { | 
 |     if (Mask(SystemHintFMask) == SystemHintFixed) { | 
 |       int imm_hint = ImmHint(); | 
 |       switch (imm_hint) { | 
 |         case BTI: | 
 |         case BTI_c: | 
 |         case BTI_j: | 
 |         case BTI_jc: | 
 |           return true; | 
 |       } | 
 |     } | 
 |     return false; | 
 |   } | 
 |  | 
 |   bool IsNop(int n) { | 
 |     // A marking nop is an instruction | 
 |     //   mov r<n>,  r<n> | 
 |     // which is encoded as | 
 |     //   orr r<n>, xzr, r<n> | 
 |     return (Mask(LogicalShiftedMask) == ORR_x) && (Rd() == Rm()) && (Rd() == n); | 
 |   } | 
 |  | 
 |   // Find the PC offset encoded in this instruction. 'this' may be a branch or | 
 |   // a PC-relative addressing instruction. | 
 |   // The offset returned is unscaled. | 
 |   V8_EXPORT_PRIVATE int64_t ImmPCOffset(); | 
 |  | 
 |   // Find the target of this instruction. 'this' may be a branch or a | 
 |   // PC-relative addressing instruction. | 
 |   V8_EXPORT_PRIVATE Instruction* ImmPCOffsetTarget(); | 
 |  | 
 |   // Check if the offset is in range of a given branch type. The offset is | 
 |   // a byte offset, unscaled. | 
 |   static bool IsValidImmPCOffset(ImmBranchType branch_type, ptrdiff_t offset); | 
 |   bool IsTargetInImmPCOffsetRange(Instruction* target); | 
 |   // Patch a PC-relative offset to refer to 'target'. 'this' may be a branch or | 
 |   // a PC-relative addressing instruction. | 
 |   void SetImmPCOffsetTarget(const AssemblerOptions& options, | 
 |                             Instruction* target); | 
 |   void SetUnresolvedInternalReferenceImmTarget(const AssemblerOptions& options, | 
 |                                                Instruction* target); | 
 |   // Patch a literal load instruction to load from 'source'. | 
 |   void SetImmLLiteral(Instruction* source); | 
 |  | 
 |   uintptr_t LiteralAddress() { | 
 |     int offset = ImmLLiteral() * kLoadLiteralScale; | 
 |     return reinterpret_cast<uintptr_t>(this) + offset; | 
 |   } | 
 |  | 
 |   enum CheckAlignment { NO_CHECK, CHECK_ALIGNMENT }; | 
 |  | 
 |   V8_INLINE const Instruction* InstructionAtOffset( | 
 |       int64_t offset, CheckAlignment check = CHECK_ALIGNMENT) const { | 
 |     // The FUZZ_disasm test relies on no check being done. | 
 |     DCHECK(check == NO_CHECK || IsAligned(offset, kInstrSize)); | 
 |     return this + offset; | 
 |   } | 
 |  | 
 |   V8_INLINE Instruction* InstructionAtOffset( | 
 |       int64_t offset, CheckAlignment check = CHECK_ALIGNMENT) { | 
 |     // The FUZZ_disasm test relies on no check being done. | 
 |     DCHECK(check == NO_CHECK || IsAligned(offset, kInstrSize)); | 
 |     return this + offset; | 
 |   } | 
 |  | 
 |   template <typename T> | 
 |   V8_INLINE static Instruction* Cast(T src) { | 
 |     return reinterpret_cast<Instruction*>(src); | 
 |   } | 
 |  | 
 |   V8_INLINE ptrdiff_t DistanceTo(Instruction* target) { | 
 |     return reinterpret_cast<Address>(target) - reinterpret_cast<Address>(this); | 
 |   } | 
 |  | 
 |   static const int ImmPCRelRangeBitwidth = 21; | 
 |   static bool IsValidPCRelOffset(ptrdiff_t offset) { return is_int21(offset); } | 
 |   void SetPCRelImmTarget(const AssemblerOptions& options, Instruction* target); | 
 |   V8_EXPORT_PRIVATE void SetBranchImmTarget(Instruction* target); | 
 | }; | 
 |  | 
 | // Simulator/Debugger debug instructions --------------------------------------- | 
 | // Each debug marker is represented by a HLT instruction. The immediate comment | 
 | // field in the instruction is used to identify the type of debug marker. Each | 
 | // marker encodes arguments in a different way, as described below. | 
 |  | 
 | // Indicate to the Debugger that the instruction is a redirected call. | 
 | const Instr kImmExceptionIsRedirectedCall = 0xca11; | 
 |  | 
 | // Represent unreachable code. This is used as a guard in parts of the code that | 
 | // should not be reachable, such as in data encoded inline in the instructions. | 
 | const Instr kImmExceptionIsUnreachable = 0xdebf; | 
 |  | 
 | // A pseudo 'printf' instruction. The arguments will be passed to the platform | 
 | // printf method. | 
 | const Instr kImmExceptionIsPrintf = 0xdeb1; | 
 | // Most parameters are stored in ARM64 registers as if the printf | 
 | // pseudo-instruction was a call to the real printf method: | 
 | //      x0: The format string. | 
 | //   x1-x7: Optional arguments. | 
 | //   d0-d7: Optional arguments. | 
 | // | 
 | // Also, the argument layout is described inline in the instructions: | 
 | //  - arg_count: The number of arguments. | 
 | //  - arg_pattern: A set of PrintfArgPattern values, packed into two-bit fields. | 
 | // | 
 | // Floating-point and integer arguments are passed in separate sets of registers | 
 | // in AAPCS64 (even for varargs functions), so it is not possible to determine | 
 | // the type of each argument without some information about the values that were | 
 | // passed in. This information could be retrieved from the printf format string, | 
 | // but the format string is not trivial to parse so we encode the relevant | 
 | // information with the HLT instruction. | 
 | const unsigned kPrintfArgCountOffset = 1 * kInstrSize; | 
 | const unsigned kPrintfArgPatternListOffset = 2 * kInstrSize; | 
 | const unsigned kPrintfLength = 3 * kInstrSize; | 
 |  | 
 | const unsigned kPrintfMaxArgCount = 4; | 
 |  | 
 | // The argument pattern is a set of two-bit-fields, each with one of the | 
 | // following values: | 
 | enum PrintfArgPattern { | 
 |   kPrintfArgW = 1, | 
 |   kPrintfArgX = 2, | 
 |   // There is no kPrintfArgS because floats are always converted to doubles in C | 
 |   // varargs calls. | 
 |   kPrintfArgD = 3 | 
 | }; | 
 | static const unsigned kPrintfArgPatternBits = 2; | 
 |  | 
 | // A pseudo 'debug' instruction. | 
 | const Instr kImmExceptionIsDebug = 0xdeb0; | 
 | // Parameters are inlined in the code after a debug pseudo-instruction: | 
 | // - Debug code. | 
 | // - Debug parameters. | 
 | // - Debug message string. This is a nullptr-terminated ASCII string, padded to | 
 | //   kInstrSize so that subsequent instructions are correctly aligned. | 
 | // - A kImmExceptionIsUnreachable marker, to catch accidental execution of the | 
 | //   string data. | 
 | const unsigned kDebugCodeOffset = 1 * kInstrSize; | 
 | const unsigned kDebugParamsOffset = 2 * kInstrSize; | 
 | const unsigned kDebugMessageOffset = 3 * kInstrSize; | 
 |  | 
 | // Debug parameters. | 
 | // Used without a TRACE_ option, the Debugger will print the arguments only | 
 | // once. Otherwise TRACE_ENABLE and TRACE_DISABLE will enable or disable tracing | 
 | // before every instruction for the specified LOG_ parameters. | 
 | // | 
 | // TRACE_OVERRIDE enables the specified LOG_ parameters, and disabled any | 
 | // others that were not specified. | 
 | // | 
 | // For example: | 
 | // | 
 | // __ debug("print registers and fp registers", 0, LOG_REGS | LOG_VREGS); | 
 | // will print the registers and fp registers only once. | 
 | // | 
 | // __ debug("trace disasm", 1, TRACE_ENABLE | LOG_DISASM); | 
 | // starts disassembling the code. | 
 | // | 
 | // __ debug("trace rets", 2, TRACE_ENABLE | LOG_REGS); | 
 | // adds the general purpose registers to the trace. | 
 | // | 
 | // __ debug("stop regs", 3, TRACE_DISABLE | LOG_REGS); | 
 | // stops tracing the registers. | 
 | const unsigned kDebuggerTracingDirectivesMask = 3 << 6; | 
 | enum DebugParameters { | 
 |   NO_PARAM = 0, | 
 |   BREAK = 1 << 0, | 
 |   LOG_DISASM = 1 << 1,    // Use only with TRACE. Disassemble the code. | 
 |   LOG_REGS = 1 << 2,      // Log general purpose registers. | 
 |   LOG_VREGS = 1 << 3,     // Log NEON and floating-point registers. | 
 |   LOG_SYS_REGS = 1 << 4,  // Log the status flags. | 
 |   LOG_WRITE = 1 << 5,     // Log any memory write. | 
 |  | 
 |   LOG_NONE = 0, | 
 |   LOG_STATE = LOG_REGS | LOG_VREGS | LOG_SYS_REGS, | 
 |   LOG_ALL = LOG_DISASM | LOG_STATE | LOG_WRITE, | 
 |  | 
 |   // Trace control. | 
 |   TRACE_ENABLE = 1 << 6, | 
 |   TRACE_DISABLE = 2 << 6, | 
 |   TRACE_OVERRIDE = 3 << 6 | 
 | }; | 
 |  | 
 | enum NEONFormat { | 
 |   NF_UNDEF = 0, | 
 |   NF_8B = 1, | 
 |   NF_16B = 2, | 
 |   NF_4H = 3, | 
 |   NF_8H = 4, | 
 |   NF_2S = 5, | 
 |   NF_4S = 6, | 
 |   NF_1D = 7, | 
 |   NF_2D = 8, | 
 |   NF_B = 9, | 
 |   NF_H = 10, | 
 |   NF_S = 11, | 
 |   NF_D = 12 | 
 | }; | 
 |  | 
 | static const unsigned kNEONFormatMaxBits = 6; | 
 |  | 
 | struct NEONFormatMap { | 
 |   // The bit positions in the instruction to consider. | 
 |   uint8_t bits[kNEONFormatMaxBits]; | 
 |  | 
 |   // Mapping from concatenated bits to format. | 
 |   NEONFormat map[1 << kNEONFormatMaxBits]; | 
 | }; | 
 |  | 
 | class NEONFormatDecoder { | 
 |  public: | 
 |   enum SubstitutionMode { kPlaceholder, kFormat }; | 
 |  | 
 |   // Construct a format decoder with increasingly specific format maps for each | 
 |   // substitution. If no format map is specified, the default is the integer | 
 |   // format map. | 
 |   explicit NEONFormatDecoder(const Instruction* instr); | 
 |   NEONFormatDecoder(const Instruction* instr, const NEONFormatMap* format); | 
 |   NEONFormatDecoder(const Instruction* instr, const NEONFormatMap* format0, | 
 |                     const NEONFormatMap* format1); | 
 |   NEONFormatDecoder(const Instruction* instr, const NEONFormatMap* format0, | 
 |                     const NEONFormatMap* format1, const NEONFormatMap* format2); | 
 |  | 
 |   // Set the format mapping for all or individual substitutions. | 
 |   void SetFormatMaps(const NEONFormatMap* format0, | 
 |                      const NEONFormatMap* format1 = nullptr, | 
 |                      const NEONFormatMap* format2 = nullptr); | 
 |   void SetFormatMap(unsigned index, const NEONFormatMap* format); | 
 |  | 
 |   // Substitute %s in the input string with the placeholder string for each | 
 |   // register, ie. "'B", "'H", etc. | 
 |   const char* SubstitutePlaceholders(const char* string); | 
 |  | 
 |   // Substitute %s in the input string with a new string based on the | 
 |   // substitution mode. | 
 |   const char* Substitute(const char* string, SubstitutionMode mode0 = kFormat, | 
 |                          SubstitutionMode mode1 = kFormat, | 
 |                          SubstitutionMode mode2 = kFormat, | 
 |                          SubstitutionMode mode3 = kFormat); | 
 |  | 
 |   // Append a "2" to a mnemonic string based of the state of the Q bit. | 
 |   const char* Mnemonic(const char* mnemonic); | 
 |  | 
 |   VectorFormat GetVectorFormat(int format_index = 0); | 
 |   VectorFormat GetVectorFormat(const NEONFormatMap* format_map); | 
 |  | 
 |   // Built in mappings for common cases. | 
 |  | 
 |   // The integer format map uses three bits (Q, size<1:0>) to encode the | 
 |   // "standard" set of NEON integer vector formats. | 
 |   static const NEONFormatMap* IntegerFormatMap() { | 
 |     static const NEONFormatMap map = { | 
 |         {23, 22, 30}, | 
 |         {NF_8B, NF_16B, NF_4H, NF_8H, NF_2S, NF_4S, NF_UNDEF, NF_2D}}; | 
 |     return ↦ | 
 |   } | 
 |  | 
 |   // The long integer format map uses two bits (size<1:0>) to encode the | 
 |   // long set of NEON integer vector formats. These are used in narrow, wide | 
 |   // and long operations. | 
 |   static const NEONFormatMap* LongIntegerFormatMap() { | 
 |     static const NEONFormatMap map = {{23, 22}, {NF_8H, NF_4S, NF_2D}}; | 
 |     return ↦ | 
 |   } | 
 |  | 
 |   // The FP format map uses two bits (Q, size<0>) to encode the NEON FP vector | 
 |   // formats: NF_2S, NF_4S, NF_2D. | 
 |   static const NEONFormatMap* FPFormatMap() { | 
 |     // The FP format map assumes two bits (Q, size<0>) are used to encode the | 
 |     // NEON FP vector formats: NF_2S, NF_4S, NF_2D. | 
 |     static const NEONFormatMap map = {{22, 30}, | 
 |                                       {NF_2S, NF_4S, NF_UNDEF, NF_2D}}; | 
 |     return ↦ | 
 |   } | 
 |  | 
 |   // The load/store format map uses three bits (Q, 11, 10) to encode the | 
 |   // set of NEON vector formats. | 
 |   static const NEONFormatMap* LoadStoreFormatMap() { | 
 |     static const NEONFormatMap map = { | 
 |         {11, 10, 30}, | 
 |         {NF_8B, NF_16B, NF_4H, NF_8H, NF_2S, NF_4S, NF_1D, NF_2D}}; | 
 |     return ↦ | 
 |   } | 
 |  | 
 |   // The logical format map uses one bit (Q) to encode the NEON vector format: | 
 |   // NF_8B, NF_16B. | 
 |   static const NEONFormatMap* LogicalFormatMap() { | 
 |     static const NEONFormatMap map = {{30}, {NF_8B, NF_16B}}; | 
 |     return ↦ | 
 |   } | 
 |  | 
 |   // The triangular format map uses between two and five bits to encode the NEON | 
 |   // vector format: | 
 |   // xxx10->8B, xxx11->16B, xx100->4H, xx101->8H | 
 |   // x1000->2S, x1001->4S,  10001->2D, all others undefined. | 
 |   static const NEONFormatMap* TriangularFormatMap() { | 
 |     static const NEONFormatMap map = { | 
 |         {19, 18, 17, 16, 30}, | 
 |         {NF_UNDEF, NF_UNDEF, NF_8B, NF_16B, NF_4H, NF_8H, NF_8B, NF_16B, | 
 |          NF_2S,    NF_4S,    NF_8B, NF_16B, NF_4H, NF_8H, NF_8B, NF_16B, | 
 |          NF_UNDEF, NF_2D,    NF_8B, NF_16B, NF_4H, NF_8H, NF_8B, NF_16B, | 
 |          NF_2S,    NF_4S,    NF_8B, NF_16B, NF_4H, NF_8H, NF_8B, NF_16B}}; | 
 |     return ↦ | 
 |   } | 
 |  | 
 |   // The scalar format map uses two bits (size<1:0>) to encode the NEON scalar | 
 |   // formats: NF_B, NF_H, NF_S, NF_D. | 
 |   static const NEONFormatMap* ScalarFormatMap() { | 
 |     static const NEONFormatMap map = {{23, 22}, {NF_B, NF_H, NF_S, NF_D}}; | 
 |     return ↦ | 
 |   } | 
 |  | 
 |   // The long scalar format map uses two bits (size<1:0>) to encode the longer | 
 |   // NEON scalar formats: NF_H, NF_S, NF_D. | 
 |   static const NEONFormatMap* LongScalarFormatMap() { | 
 |     static const NEONFormatMap map = {{23, 22}, {NF_H, NF_S, NF_D}}; | 
 |     return ↦ | 
 |   } | 
 |  | 
 |   // The FP scalar format map assumes one bit (size<0>) is used to encode the | 
 |   // NEON FP scalar formats: NF_S, NF_D. | 
 |   static const NEONFormatMap* FPScalarFormatMap() { | 
 |     static const NEONFormatMap map = {{22}, {NF_S, NF_D}}; | 
 |     return ↦ | 
 |   } | 
 |  | 
 |   // The triangular scalar format map uses between one and four bits to encode | 
 |   // the NEON FP scalar formats: | 
 |   // xxx1->B, xx10->H, x100->S, 1000->D, all others undefined. | 
 |   static const NEONFormatMap* TriangularScalarFormatMap() { | 
 |     static const NEONFormatMap map = { | 
 |         {19, 18, 17, 16}, | 
 |         {NF_UNDEF, NF_B, NF_H, NF_B, NF_S, NF_B, NF_H, NF_B, NF_D, NF_B, NF_H, | 
 |          NF_B, NF_S, NF_B, NF_H, NF_B}}; | 
 |     return ↦ | 
 |   } | 
 |  | 
 |  private: | 
 |   // Get a pointer to a string that represents the format or placeholder for | 
 |   // the specified substitution index, based on the format map and instruction. | 
 |   const char* GetSubstitute(int index, SubstitutionMode mode); | 
 |  | 
 |   // Get the NEONFormat enumerated value for bits obtained from the | 
 |   // instruction based on the specified format mapping. | 
 |   NEONFormat GetNEONFormat(const NEONFormatMap* format_map); | 
 |  | 
 |   // Convert a NEONFormat into a string. | 
 |   static const char* NEONFormatAsString(NEONFormat format); | 
 |  | 
 |   // Convert a NEONFormat into a register placeholder string. | 
 |   static const char* NEONFormatAsPlaceholder(NEONFormat format); | 
 |  | 
 |   // Select bits from instrbits_ defined by the bits array, concatenate them, | 
 |   // and return the value. | 
 |   uint8_t PickBits(const uint8_t bits[]); | 
 |  | 
 |   Instr instrbits_; | 
 |   const NEONFormatMap* formats_[4]; | 
 |   char form_buffer_[64]; | 
 |   char mne_buffer_[16]; | 
 | }; | 
 | }  // namespace internal | 
 | }  // namespace v8 | 
 |  | 
 | #endif  // V8_CODEGEN_ARM64_INSTRUCTIONS_ARM64_H_ |