758 lines
25 KiB
C
758 lines
25 KiB
C
|
// Copyright 2015, ARM Limited
|
||
|
// 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 ARM Limited 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 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 VIXL_A64_INSTRUCTIONS_A64_H_
|
||
|
#define VIXL_A64_INSTRUCTIONS_A64_H_
|
||
|
|
||
|
#include "vixl/globals.h"
|
||
|
#include "vixl/utils.h"
|
||
|
#include "vixl/a64/constants-a64.h"
|
||
|
|
||
|
namespace vixl {
|
||
|
// ISA constants. --------------------------------------------------------------
|
||
|
|
||
|
typedef uint32_t Instr;
|
||
|
const unsigned kInstructionSize = 4;
|
||
|
const unsigned kInstructionSizeLog2 = 2;
|
||
|
const unsigned kLiteralEntrySize = 4;
|
||
|
const unsigned kLiteralEntrySizeLog2 = 2;
|
||
|
const unsigned kMaxLoadLiteralRange = 1 * MBytes;
|
||
|
|
||
|
// This is the nominal page size (as used by the adrp instruction); the actual
|
||
|
// size of the memory pages allocated by the kernel is likely to differ.
|
||
|
const unsigned kPageSize = 4 * KBytes;
|
||
|
const unsigned kPageSizeLog2 = 12;
|
||
|
|
||
|
const unsigned kBRegSize = 8;
|
||
|
const unsigned kBRegSizeLog2 = 3;
|
||
|
const unsigned kBRegSizeInBytes = kBRegSize / 8;
|
||
|
const unsigned kBRegSizeInBytesLog2 = kBRegSizeLog2 - 3;
|
||
|
const unsigned kHRegSize = 16;
|
||
|
const unsigned kHRegSizeLog2 = 4;
|
||
|
const unsigned kHRegSizeInBytes = kHRegSize / 8;
|
||
|
const unsigned kHRegSizeInBytesLog2 = kHRegSizeLog2 - 3;
|
||
|
const unsigned kWRegSize = 32;
|
||
|
const unsigned kWRegSizeLog2 = 5;
|
||
|
const unsigned kWRegSizeInBytes = kWRegSize / 8;
|
||
|
const unsigned kWRegSizeInBytesLog2 = kWRegSizeLog2 - 3;
|
||
|
const unsigned kXRegSize = 64;
|
||
|
const unsigned kXRegSizeLog2 = 6;
|
||
|
const unsigned kXRegSizeInBytes = kXRegSize / 8;
|
||
|
const unsigned kXRegSizeInBytesLog2 = kXRegSizeLog2 - 3;
|
||
|
const unsigned kSRegSize = 32;
|
||
|
const unsigned kSRegSizeLog2 = 5;
|
||
|
const unsigned kSRegSizeInBytes = kSRegSize / 8;
|
||
|
const unsigned kSRegSizeInBytesLog2 = kSRegSizeLog2 - 3;
|
||
|
const unsigned kDRegSize = 64;
|
||
|
const unsigned kDRegSizeLog2 = 6;
|
||
|
const unsigned kDRegSizeInBytes = kDRegSize / 8;
|
||
|
const unsigned kDRegSizeInBytesLog2 = kDRegSizeLog2 - 3;
|
||
|
const unsigned kQRegSize = 128;
|
||
|
const unsigned kQRegSizeLog2 = 7;
|
||
|
const unsigned kQRegSizeInBytes = kQRegSize / 8;
|
||
|
const unsigned kQRegSizeInBytesLog2 = kQRegSizeLog2 - 3;
|
||
|
const uint64_t kWRegMask = UINT64_C(0xffffffff);
|
||
|
const uint64_t kXRegMask = UINT64_C(0xffffffffffffffff);
|
||
|
const uint64_t kSRegMask = UINT64_C(0xffffffff);
|
||
|
const uint64_t kDRegMask = UINT64_C(0xffffffffffffffff);
|
||
|
const uint64_t kSSignMask = UINT64_C(0x80000000);
|
||
|
const uint64_t kDSignMask = UINT64_C(0x8000000000000000);
|
||
|
const uint64_t kWSignMask = UINT64_C(0x80000000);
|
||
|
const uint64_t kXSignMask = UINT64_C(0x8000000000000000);
|
||
|
const uint64_t kByteMask = UINT64_C(0xff);
|
||
|
const uint64_t kHalfWordMask = UINT64_C(0xffff);
|
||
|
const uint64_t kWordMask = UINT64_C(0xffffffff);
|
||
|
const uint64_t kXMaxUInt = UINT64_C(0xffffffffffffffff);
|
||
|
const uint64_t kWMaxUInt = UINT64_C(0xffffffff);
|
||
|
const int64_t kXMaxInt = INT64_C(0x7fffffffffffffff);
|
||
|
const int64_t kXMinInt = INT64_C(0x8000000000000000);
|
||
|
const int32_t kWMaxInt = INT32_C(0x7fffffff);
|
||
|
const int32_t kWMinInt = INT32_C(0x80000000);
|
||
|
const unsigned kLinkRegCode = 30;
|
||
|
const unsigned kZeroRegCode = 31;
|
||
|
const unsigned kSPRegInternalCode = 63;
|
||
|
const unsigned kRegCodeMask = 0x1f;
|
||
|
|
||
|
const unsigned kAddressTagOffset = 56;
|
||
|
const unsigned kAddressTagWidth = 8;
|
||
|
const uint64_t kAddressTagMask =
|
||
|
((UINT64_C(1) << kAddressTagWidth) - 1) << kAddressTagOffset;
|
||
|
VIXL_STATIC_ASSERT(kAddressTagMask == UINT64_C(0xff00000000000000));
|
||
|
|
||
|
// AArch64 floating-point specifics. These match IEEE-754.
|
||
|
const unsigned kDoubleMantissaBits = 52;
|
||
|
const unsigned kDoubleExponentBits = 11;
|
||
|
const unsigned kFloatMantissaBits = 23;
|
||
|
const unsigned kFloatExponentBits = 8;
|
||
|
const unsigned kFloat16MantissaBits = 10;
|
||
|
const unsigned kFloat16ExponentBits = 5;
|
||
|
|
||
|
// Floating-point infinity values.
|
||
|
extern const float16 kFP16PositiveInfinity;
|
||
|
extern const float16 kFP16NegativeInfinity;
|
||
|
extern const float kFP32PositiveInfinity;
|
||
|
extern const float kFP32NegativeInfinity;
|
||
|
extern const double kFP64PositiveInfinity;
|
||
|
extern const double kFP64NegativeInfinity;
|
||
|
|
||
|
// The default NaN values (for FPCR.DN=1).
|
||
|
extern const float16 kFP16DefaultNaN;
|
||
|
extern const float kFP32DefaultNaN;
|
||
|
extern const double kFP64DefaultNaN;
|
||
|
|
||
|
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). It cannot be set in FPCR.
|
||
|
FPTieAway,
|
||
|
FPRoundOdd
|
||
|
};
|
||
|
|
||
|
enum Reg31Mode {
|
||
|
Reg31IsStackPointer,
|
||
|
Reg31IsZeroRegister
|
||
|
};
|
||
|
|
||
|
// Instructions. ---------------------------------------------------------------
|
||
|
|
||
|
class Instruction {
|
||
|
public:
|
||
|
Instr InstructionBits() const {
|
||
|
return *(reinterpret_cast<const Instr*>(this));
|
||
|
}
|
||
|
|
||
|
void SetInstructionBits(Instr new_instr) {
|
||
|
*(reinterpret_cast<Instr*>(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 {
|
||
|
int32_t bits = *(reinterpret_cast<const int32_t*>(this));
|
||
|
return signed_bitextract_32(msb, lsb, bits);
|
||
|
}
|
||
|
|
||
|
Instr Mask(uint32_t mask) const {
|
||
|
return InstructionBits() & mask;
|
||
|
}
|
||
|
|
||
|
#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 {
|
||
|
int offset =
|
||
|
static_cast<int>((ImmPCRelHi() << ImmPCRelLo_width) | ImmPCRelLo());
|
||
|
int width = ImmPCRelLo_width + ImmPCRelHi_width;
|
||
|
return signed_bitextract_32(width - 1, 0, offset);
|
||
|
}
|
||
|
|
||
|
uint64_t ImmLogical() const;
|
||
|
unsigned ImmNEONabcdefgh() const;
|
||
|
float ImmFP32() const;
|
||
|
double ImmFP64() const;
|
||
|
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 {
|
||
|
int64_t q = NEONQ();
|
||
|
int64_t s = NEONS();
|
||
|
int64_t size = NEONLSSize();
|
||
|
int64_t index = (q << 3) | (s << 2) | size;
|
||
|
return static_cast<int>(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;
|
||
|
}
|
||
|
|
||
|
bool IsPCRelAddressing() const {
|
||
|
return Mask(PCRelAddressingFMask) == PCRelAddressingFixed;
|
||
|
}
|
||
|
|
||
|
bool IsLogicalImmediate() const {
|
||
|
return Mask(LogicalImmediateFMask) == LogicalImmediateFixed;
|
||
|
}
|
||
|
|
||
|
bool IsAddSubImmediate() const {
|
||
|
return Mask(AddSubImmediateFMask) == AddSubImmediateFixed;
|
||
|
}
|
||
|
|
||
|
bool IsAddSubExtended() const {
|
||
|
return Mask(AddSubExtendedFMask) == AddSubExtendedFixed;
|
||
|
}
|
||
|
|
||
|
bool IsLoadOrStore() const {
|
||
|
return Mask(LoadStoreAnyFMask) == LoadStoreAnyFixed;
|
||
|
}
|
||
|
|
||
|
bool IsLoad() const;
|
||
|
bool IsStore() const;
|
||
|
|
||
|
bool IsLoadLiteral() const {
|
||
|
// This includes PRFM_lit.
|
||
|
return Mask(LoadLiteralFMask) == LoadLiteralFixed;
|
||
|
}
|
||
|
|
||
|
bool IsMovn() const {
|
||
|
return (Mask(MoveWideImmediateMask) == MOVN_x) ||
|
||
|
(Mask(MoveWideImmediateMask) == MOVN_w);
|
||
|
}
|
||
|
|
||
|
static int ImmBranchRangeBitwidth(ImmBranchType branch_type);
|
||
|
static int32_t ImmBranchForwardRange(ImmBranchType branch_type);
|
||
|
static bool IsValidImmPCOffset(ImmBranchType branch_type, int64_t offset);
|
||
|
|
||
|
// 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;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Find the target of this instruction. 'this' may be a branch or a
|
||
|
// PC-relative addressing instruction.
|
||
|
const Instruction* ImmPCOffsetTarget() const;
|
||
|
|
||
|
// Patch a PC-relative offset to refer to 'target'. 'this' may be a branch or
|
||
|
// a PC-relative addressing instruction.
|
||
|
void SetImmPCOffsetTarget(const Instruction* target);
|
||
|
// Patch a literal load instruction to load from 'source'.
|
||
|
void SetImmLLiteral(const Instruction* source);
|
||
|
|
||
|
// The range of a load literal instruction, expressed as 'instr +- range'.
|
||
|
// The range is actually the 'positive' range; the branch instruction can
|
||
|
// target [instr - range - kInstructionSize, instr + range].
|
||
|
static const int kLoadLiteralImmBitwidth = 19;
|
||
|
static const int kLoadLiteralRange =
|
||
|
(1 << kLoadLiteralImmBitwidth) / 2 - kInstructionSize;
|
||
|
|
||
|
// Calculate the address of a literal referred to by a load-literal
|
||
|
// instruction, and return it as the specified type.
|
||
|
//
|
||
|
// The literal itself is safely mutable only if the backing buffer is safely
|
||
|
// mutable.
|
||
|
template <typename T>
|
||
|
T LiteralAddress() const {
|
||
|
uint64_t base_raw = reinterpret_cast<uint64_t>(this);
|
||
|
int64_t offset = ImmLLiteral() << kLiteralEntrySizeLog2;
|
||
|
uint64_t address_raw = base_raw + offset;
|
||
|
|
||
|
// Cast the address using a C-style cast. A reinterpret_cast would be
|
||
|
// appropriate, but it can't cast one integral type to another.
|
||
|
T address = (T)(address_raw);
|
||
|
|
||
|
// Assert that the address can be represented by the specified type.
|
||
|
VIXL_ASSERT((uint64_t)(address) == address_raw);
|
||
|
|
||
|
return address;
|
||
|
}
|
||
|
|
||
|
uint32_t Literal32() const {
|
||
|
uint32_t literal;
|
||
|
memcpy(&literal, LiteralAddress<const void*>(), sizeof(literal));
|
||
|
return literal;
|
||
|
}
|
||
|
|
||
|
uint64_t Literal64() const {
|
||
|
uint64_t literal;
|
||
|
memcpy(&literal, LiteralAddress<const void*>(), sizeof(literal));
|
||
|
return literal;
|
||
|
}
|
||
|
|
||
|
float LiteralFP32() const {
|
||
|
return rawbits_to_float(Literal32());
|
||
|
}
|
||
|
|
||
|
double LiteralFP64() const {
|
||
|
return rawbits_to_double(Literal64());
|
||
|
}
|
||
|
|
||
|
const Instruction* NextInstruction() const {
|
||
|
return this + kInstructionSize;
|
||
|
}
|
||
|
|
||
|
const Instruction* InstructionAtOffset(int64_t offset) const {
|
||
|
VIXL_ASSERT(IsWordAligned(this + offset));
|
||
|
return this + offset;
|
||
|
}
|
||
|
|
||
|
template<typename T> static Instruction* Cast(T src) {
|
||
|
return reinterpret_cast<Instruction*>(src);
|
||
|
}
|
||
|
|
||
|
template<typename T> static const Instruction* CastConst(T src) {
|
||
|
return reinterpret_cast<const Instruction*>(src);
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
int ImmBranch() const;
|
||
|
|
||
|
static float Imm8ToFP32(uint32_t imm8);
|
||
|
static double Imm8ToFP64(uint32_t imm8);
|
||
|
|
||
|
void SetPCRelImmTarget(const Instruction* target);
|
||
|
void SetBranchImmTarget(const Instruction* target);
|
||
|
};
|
||
|
|
||
|
|
||
|
// Functions for handling NEON vector format information.
|
||
|
enum VectorFormat {
|
||
|
kFormatUndefined = 0xffffffff,
|
||
|
kFormat8B = NEON_8B,
|
||
|
kFormat16B = NEON_16B,
|
||
|
kFormat4H = NEON_4H,
|
||
|
kFormat8H = NEON_8H,
|
||
|
kFormat2S = NEON_2S,
|
||
|
kFormat4S = NEON_4S,
|
||
|
kFormat1D = NEON_1D,
|
||
|
kFormat2D = NEON_2D,
|
||
|
|
||
|
// Scalar formats. We add the scalar bit to distinguish between scalar and
|
||
|
// vector enumerations; the bit is always set in the encoding of scalar ops
|
||
|
// and always clear for vector ops. Although kFormatD and kFormat1D appear
|
||
|
// to be the same, their meaning is subtly different. The first is a scalar
|
||
|
// operation, the second a vector operation that only affects one lane.
|
||
|
kFormatB = NEON_B | NEONScalar,
|
||
|
kFormatH = NEON_H | NEONScalar,
|
||
|
kFormatS = NEON_S | NEONScalar,
|
||
|
kFormatD = NEON_D | NEONScalar
|
||
|
};
|
||
|
|
||
|
VectorFormat VectorFormatHalfWidth(const VectorFormat vform);
|
||
|
VectorFormat VectorFormatDoubleWidth(const VectorFormat vform);
|
||
|
VectorFormat VectorFormatDoubleLanes(const VectorFormat vform);
|
||
|
VectorFormat VectorFormatHalfLanes(const VectorFormat vform);
|
||
|
VectorFormat ScalarFormatFromLaneSize(int lanesize);
|
||
|
VectorFormat VectorFormatHalfWidthDoubleLanes(const VectorFormat vform);
|
||
|
VectorFormat VectorFormatFillQ(const VectorFormat vform);
|
||
|
unsigned RegisterSizeInBitsFromFormat(VectorFormat vform);
|
||
|
unsigned RegisterSizeInBytesFromFormat(VectorFormat vform);
|
||
|
// TODO: Make the return types of these functions consistent.
|
||
|
unsigned LaneSizeInBitsFromFormat(VectorFormat vform);
|
||
|
int LaneSizeInBytesFromFormat(VectorFormat vform);
|
||
|
int LaneSizeInBytesLog2FromFormat(VectorFormat vform);
|
||
|
int LaneCountFromFormat(VectorFormat vform);
|
||
|
int MaxLaneCountFromFormat(VectorFormat vform);
|
||
|
bool IsVectorFormat(VectorFormat vform);
|
||
|
int64_t MaxIntFromFormat(VectorFormat vform);
|
||
|
int64_t MinIntFromFormat(VectorFormat vform);
|
||
|
uint64_t MaxUintFromFormat(VectorFormat vform);
|
||
|
|
||
|
|
||
|
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
|
||
|
// subsitution. If no format map is specified, the default is the integer
|
||
|
// format map.
|
||
|
explicit NEONFormatDecoder(const Instruction* instr) {
|
||
|
instrbits_ = instr->InstructionBits();
|
||
|
SetFormatMaps(IntegerFormatMap());
|
||
|
}
|
||
|
NEONFormatDecoder(const Instruction* instr,
|
||
|
const NEONFormatMap* format) {
|
||
|
instrbits_ = instr->InstructionBits();
|
||
|
SetFormatMaps(format);
|
||
|
}
|
||
|
NEONFormatDecoder(const Instruction* instr,
|
||
|
const NEONFormatMap* format0,
|
||
|
const NEONFormatMap* format1) {
|
||
|
instrbits_ = instr->InstructionBits();
|
||
|
SetFormatMaps(format0, format1);
|
||
|
}
|
||
|
NEONFormatDecoder(const Instruction* instr,
|
||
|
const NEONFormatMap* format0,
|
||
|
const NEONFormatMap* format1,
|
||
|
const NEONFormatMap* format2) {
|
||
|
instrbits_ = instr->InstructionBits();
|
||
|
SetFormatMaps(format0, format1, format2);
|
||
|
}
|
||
|
|
||
|
// Set the format mapping for all or individual substitutions.
|
||
|
void SetFormatMaps(const NEONFormatMap* format0,
|
||
|
const NEONFormatMap* format1 = NULL,
|
||
|
const NEONFormatMap* format2 = NULL) {
|
||
|
VIXL_ASSERT(format0 != NULL);
|
||
|
formats_[0] = format0;
|
||
|
formats_[1] = (format1 == NULL) ? formats_[0] : format1;
|
||
|
formats_[2] = (format2 == NULL) ? formats_[1] : format2;
|
||
|
}
|
||
|
void SetFormatMap(unsigned index, const NEONFormatMap* format) {
|
||
|
VIXL_ASSERT(index <= (sizeof(formats_) / sizeof(formats_[0])));
|
||
|
VIXL_ASSERT(format != NULL);
|
||
|
formats_[index] = format;
|
||
|
}
|
||
|
|
||
|
// Substitute %s in the input string with the placeholder string for each
|
||
|
// register, ie. "'B", "'H", etc.
|
||
|
const char* SubstitutePlaceholders(const char* string) {
|
||
|
return Substitute(string, kPlaceholder, kPlaceholder, kPlaceholder);
|
||
|
}
|
||
|
|
||
|
// 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) {
|
||
|
snprintf(form_buffer_, sizeof(form_buffer_), string,
|
||
|
GetSubstitute(0, mode0),
|
||
|
GetSubstitute(1, mode1),
|
||
|
GetSubstitute(2, mode2));
|
||
|
return form_buffer_;
|
||
|
}
|
||
|
|
||
|
// Append a "2" to a mnemonic string based of the state of the Q bit.
|
||
|
const char* Mnemonic(const char* mnemonic) {
|
||
|
if ((instrbits_ & NEON_Q) != 0) {
|
||
|
snprintf(mne_buffer_, sizeof(mne_buffer_), "%s2", mnemonic);
|
||
|
return mne_buffer_;
|
||
|
}
|
||
|
return mnemonic;
|
||
|
}
|
||
|
|
||
|
VectorFormat GetVectorFormat(int format_index = 0) {
|
||
|
return GetVectorFormat(formats_[format_index]);
|
||
|
}
|
||
|
|
||
|
VectorFormat GetVectorFormat(const NEONFormatMap* format_map) {
|
||
|
static const VectorFormat vform[] = {
|
||
|
kFormatUndefined,
|
||
|
kFormat8B, kFormat16B, kFormat4H, kFormat8H,
|
||
|
kFormat2S, kFormat4S, kFormat1D, kFormat2D,
|
||
|
kFormatB, kFormatH, kFormatS, kFormatD
|
||
|
};
|
||
|
VIXL_ASSERT(GetNEONFormat(format_map) < (sizeof(vform) / sizeof(vform[0])));
|
||
|
return vform[GetNEONFormat(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) {
|
||
|
if (mode == kFormat) {
|
||
|
return NEONFormatAsString(GetNEONFormat(formats_[index]));
|
||
|
}
|
||
|
VIXL_ASSERT(mode == kPlaceholder);
|
||
|
return NEONFormatAsPlaceholder(GetNEONFormat(formats_[index]));
|
||
|
}
|
||
|
|
||
|
// Get the NEONFormat enumerated value for bits obtained from the
|
||
|
// instruction based on the specified format mapping.
|
||
|
NEONFormat GetNEONFormat(const NEONFormatMap* format_map) {
|
||
|
return format_map->map[PickBits(format_map->bits)];
|
||
|
}
|
||
|
|
||
|
// Convert a NEONFormat into a string.
|
||
|
static const char* NEONFormatAsString(NEONFormat format) {
|
||
|
static const char* formats[] = {
|
||
|
"undefined",
|
||
|
"8b", "16b", "4h", "8h", "2s", "4s", "1d", "2d",
|
||
|
"b", "h", "s", "d"
|
||
|
};
|
||
|
VIXL_ASSERT(format < (sizeof(formats) / sizeof(formats[0])));
|
||
|
return formats[format];
|
||
|
}
|
||
|
|
||
|
// Convert a NEONFormat into a register placeholder string.
|
||
|
static const char* NEONFormatAsPlaceholder(NEONFormat format) {
|
||
|
VIXL_ASSERT((format == NF_B) || (format == NF_H) ||
|
||
|
(format == NF_S) || (format == NF_D) ||
|
||
|
(format == NF_UNDEF));
|
||
|
static const char* formats[] = {
|
||
|
"undefined",
|
||
|
"undefined", "undefined", "undefined", "undefined",
|
||
|
"undefined", "undefined", "undefined", "undefined",
|
||
|
"'B", "'H", "'S", "'D"
|
||
|
};
|
||
|
return formats[format];
|
||
|
}
|
||
|
|
||
|
// Select bits from instrbits_ defined by the bits array, concatenate them,
|
||
|
// and return the value.
|
||
|
uint8_t PickBits(const uint8_t bits[]) {
|
||
|
uint8_t result = 0;
|
||
|
for (unsigned b = 0; b < kNEONFormatMaxBits; b++) {
|
||
|
if (bits[b] == 0) break;
|
||
|
result <<= 1;
|
||
|
result |= ((instrbits_ & (1 << bits[b])) == 0) ? 0 : 1;
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
Instr instrbits_;
|
||
|
const NEONFormatMap* formats_[3];
|
||
|
char form_buffer_[64];
|
||
|
char mne_buffer_[16];
|
||
|
};
|
||
|
} // namespace vixl
|
||
|
|
||
|
#endif // VIXL_A64_INSTRUCTIONS_A64_H_
|