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