Home | History | Annotate | Download | only in arm64
      1 // Copyright 2013 the V8 project authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #ifndef V8_ARM64_INSTRUCTIONS_ARM64_H_
      6 #define V8_ARM64_INSTRUCTIONS_ARM64_H_
      7 
      8 #include "src/arm64/constants-arm64.h"
      9 #include "src/arm64/utils-arm64.h"
     10 #include "src/globals.h"
     11 #include "src/utils.h"
     12 
     13 namespace v8 {
     14 namespace internal {
     15 
     16 
     17 // ISA constants. --------------------------------------------------------------
     18 
     19 typedef uint32_t Instr;
     20 
     21 // The following macros initialize a float/double variable with a bit pattern
     22 // without using static initializers: If ARM64_DEFINE_FP_STATICS is defined, the
     23 // symbol is defined as uint32_t/uint64_t initialized with the desired bit
     24 // pattern. Otherwise, the same symbol is declared as an external float/double.
     25 #if defined(ARM64_DEFINE_FP_STATICS)
     26 #define DEFINE_FLOAT(name, value) extern const uint32_t name = value
     27 #define DEFINE_DOUBLE(name, value) extern const uint64_t name = value
     28 #else
     29 #define DEFINE_FLOAT(name, value) extern const float name
     30 #define DEFINE_DOUBLE(name, value) extern const double name
     31 #endif  // defined(ARM64_DEFINE_FP_STATICS)
     32 
     33 DEFINE_FLOAT(kFP32PositiveInfinity, 0x7f800000);
     34 DEFINE_FLOAT(kFP32NegativeInfinity, 0xff800000);
     35 DEFINE_DOUBLE(kFP64PositiveInfinity, 0x7ff0000000000000UL);
     36 DEFINE_DOUBLE(kFP64NegativeInfinity, 0xfff0000000000000UL);
     37 
     38 // This value is a signalling NaN as both a double and as a float (taking the
     39 // least-significant word).
     40 DEFINE_DOUBLE(kFP64SignallingNaN, 0x7ff000007f800001);
     41 DEFINE_FLOAT(kFP32SignallingNaN, 0x7f800001);
     42 
     43 // A similar value, but as a quiet NaN.
     44 DEFINE_DOUBLE(kFP64QuietNaN, 0x7ff800007fc00001);
     45 DEFINE_FLOAT(kFP32QuietNaN, 0x7fc00001);
     46 
     47 // The default NaN values (for FPCR.DN=1).
     48 DEFINE_DOUBLE(kFP64DefaultNaN, 0x7ff8000000000000UL);
     49 DEFINE_FLOAT(kFP32DefaultNaN, 0x7fc00000);
     50 
     51 #undef DEFINE_FLOAT
     52 #undef DEFINE_DOUBLE
     53 
     54 
     55 enum LSDataSize {
     56   LSByte        = 0,
     57   LSHalfword    = 1,
     58   LSWord        = 2,
     59   LSDoubleWord  = 3
     60 };
     61 
     62 LSDataSize CalcLSPairDataSize(LoadStorePairOp op);
     63 
     64 enum ImmBranchType {
     65   UnknownBranchType = 0,
     66   CondBranchType    = 1,
     67   UncondBranchType  = 2,
     68   CompareBranchType = 3,
     69   TestBranchType    = 4
     70 };
     71 
     72 enum AddrMode {
     73   Offset,
     74   PreIndex,
     75   PostIndex
     76 };
     77 
     78 enum FPRounding {
     79   // The first four values are encodable directly by FPCR<RMode>.
     80   FPTieEven = 0x0,
     81   FPPositiveInfinity = 0x1,
     82   FPNegativeInfinity = 0x2,
     83   FPZero = 0x3,
     84 
     85   // The final rounding mode is only available when explicitly specified by the
     86   // instruction (such as with fcvta). It cannot be set in FPCR.
     87   FPTieAway
     88 };
     89 
     90 enum Reg31Mode {
     91   Reg31IsStackPointer,
     92   Reg31IsZeroRegister
     93 };
     94 
     95 // Instructions. ---------------------------------------------------------------
     96 
     97 class Instruction {
     98  public:
     99   V8_INLINE Instr InstructionBits() const {
    100     return *reinterpret_cast<const Instr*>(this);
    101   }
    102 
    103   V8_INLINE void SetInstructionBits(Instr new_instr) {
    104     *reinterpret_cast<Instr*>(this) = new_instr;
    105   }
    106 
    107   int Bit(int pos) const {
    108     return (InstructionBits() >> pos) & 1;
    109   }
    110 
    111   uint32_t Bits(int msb, int lsb) const {
    112     return unsigned_bitextract_32(msb, lsb, InstructionBits());
    113   }
    114 
    115   int32_t SignedBits(int msb, int lsb) const {
    116     int32_t bits = *(reinterpret_cast<const int32_t*>(this));
    117     return signed_bitextract_32(msb, lsb, bits);
    118   }
    119 
    120   Instr Mask(uint32_t mask) const {
    121     return InstructionBits() & mask;
    122   }
    123 
    124   V8_INLINE const Instruction* following(int count = 1) const {
    125     return InstructionAtOffset(count * static_cast<int>(kInstructionSize));
    126   }
    127 
    128   V8_INLINE Instruction* following(int count = 1) {
    129     return InstructionAtOffset(count * static_cast<int>(kInstructionSize));
    130   }
    131 
    132   V8_INLINE const Instruction* preceding(int count = 1) const {
    133     return following(-count);
    134   }
    135 
    136   V8_INLINE Instruction* preceding(int count = 1) {
    137     return following(-count);
    138   }
    139 
    140 #define DEFINE_GETTER(Name, HighBit, LowBit, Func) \
    141   int32_t Name() const { return Func(HighBit, LowBit); }
    142   INSTRUCTION_FIELDS_LIST(DEFINE_GETTER)
    143   #undef DEFINE_GETTER
    144 
    145   // ImmPCRel is a compound field (not present in INSTRUCTION_FIELDS_LIST),
    146   // formed from ImmPCRelLo and ImmPCRelHi.
    147   int ImmPCRel() const {
    148     DCHECK(IsPCRelAddressing());
    149     int offset = ((ImmPCRelHi() << ImmPCRelLo_width) | ImmPCRelLo());
    150     int width = ImmPCRelLo_width + ImmPCRelHi_width;
    151     return signed_bitextract_32(width - 1, 0, offset);
    152   }
    153 
    154   uint64_t ImmLogical();
    155   float ImmFP32();
    156   double ImmFP64();
    157 
    158   LSDataSize SizeLSPair() const {
    159     return CalcLSPairDataSize(
    160         static_cast<LoadStorePairOp>(Mask(LoadStorePairMask)));
    161   }
    162 
    163   // Helpers.
    164   bool IsCondBranchImm() const {
    165     return Mask(ConditionalBranchFMask) == ConditionalBranchFixed;
    166   }
    167 
    168   bool IsUncondBranchImm() const {
    169     return Mask(UnconditionalBranchFMask) == UnconditionalBranchFixed;
    170   }
    171 
    172   bool IsCompareBranch() const {
    173     return Mask(CompareBranchFMask) == CompareBranchFixed;
    174   }
    175 
    176   bool IsTestBranch() const {
    177     return Mask(TestBranchFMask) == TestBranchFixed;
    178   }
    179 
    180   bool IsImmBranch() const {
    181     return BranchType() != UnknownBranchType;
    182   }
    183 
    184   bool IsLdrLiteral() const {
    185     return Mask(LoadLiteralFMask) == LoadLiteralFixed;
    186   }
    187 
    188   bool IsLdrLiteralX() const {
    189     return Mask(LoadLiteralMask) == LDR_x_lit;
    190   }
    191 
    192   bool IsPCRelAddressing() const {
    193     return Mask(PCRelAddressingFMask) == PCRelAddressingFixed;
    194   }
    195 
    196   bool IsAdr() const {
    197     return Mask(PCRelAddressingMask) == ADR;
    198   }
    199 
    200   bool IsBrk() const { return Mask(ExceptionMask) == BRK; }
    201 
    202   bool IsUnresolvedInternalReference() const {
    203     // Unresolved internal references are encoded as two consecutive brk
    204     // instructions.
    205     return IsBrk() && following()->IsBrk();
    206   }
    207 
    208   bool IsLogicalImmediate() const {
    209     return Mask(LogicalImmediateFMask) == LogicalImmediateFixed;
    210   }
    211 
    212   bool IsAddSubImmediate() const {
    213     return Mask(AddSubImmediateFMask) == AddSubImmediateFixed;
    214   }
    215 
    216   bool IsAddSubShifted() const {
    217     return Mask(AddSubShiftedFMask) == AddSubShiftedFixed;
    218   }
    219 
    220   bool IsAddSubExtended() const {
    221     return Mask(AddSubExtendedFMask) == AddSubExtendedFixed;
    222   }
    223 
    224   // Match any loads or stores, including pairs.
    225   bool IsLoadOrStore() const {
    226     return Mask(LoadStoreAnyFMask) == LoadStoreAnyFixed;
    227   }
    228 
    229   // Match any loads, including pairs.
    230   bool IsLoad() const;
    231   // Match any stores, including pairs.
    232   bool IsStore() const;
    233 
    234   // Indicate whether Rd can be the stack pointer or the zero register. This
    235   // does not check that the instruction actually has an Rd field.
    236   Reg31Mode RdMode() const {
    237     // The following instructions use csp or wsp as Rd:
    238     //  Add/sub (immediate) when not setting the flags.
    239     //  Add/sub (extended) when not setting the flags.
    240     //  Logical (immediate) when not setting the flags.
    241     // Otherwise, r31 is the zero register.
    242     if (IsAddSubImmediate() || IsAddSubExtended()) {
    243       if (Mask(AddSubSetFlagsBit)) {
    244         return Reg31IsZeroRegister;
    245       } else {
    246         return Reg31IsStackPointer;
    247       }
    248     }
    249     if (IsLogicalImmediate()) {
    250       // Of the logical (immediate) instructions, only ANDS (and its aliases)
    251       // can set the flags. The others can all write into csp.
    252       // Note that some logical operations are not available to
    253       // immediate-operand instructions, so we have to combine two masks here.
    254       if (Mask(LogicalImmediateMask & LogicalOpMask) == ANDS) {
    255         return Reg31IsZeroRegister;
    256       } else {
    257         return Reg31IsStackPointer;
    258       }
    259     }
    260     return Reg31IsZeroRegister;
    261   }
    262 
    263   // Indicate whether Rn can be the stack pointer or the zero register. This
    264   // does not check that the instruction actually has an Rn field.
    265   Reg31Mode RnMode() const {
    266     // The following instructions use csp or wsp as Rn:
    267     //  All loads and stores.
    268     //  Add/sub (immediate).
    269     //  Add/sub (extended).
    270     // Otherwise, r31 is the zero register.
    271     if (IsLoadOrStore() || IsAddSubImmediate() || IsAddSubExtended()) {
    272       return Reg31IsStackPointer;
    273     }
    274     return Reg31IsZeroRegister;
    275   }
    276 
    277   ImmBranchType BranchType() const {
    278     if (IsCondBranchImm()) {
    279       return CondBranchType;
    280     } else if (IsUncondBranchImm()) {
    281       return UncondBranchType;
    282     } else if (IsCompareBranch()) {
    283       return CompareBranchType;
    284     } else if (IsTestBranch()) {
    285       return TestBranchType;
    286     } else {
    287       return UnknownBranchType;
    288     }
    289   }
    290 
    291   static int ImmBranchRangeBitwidth(ImmBranchType branch_type) {
    292     switch (branch_type) {
    293       case UncondBranchType:
    294         return ImmUncondBranch_width;
    295       case CondBranchType:
    296         return ImmCondBranch_width;
    297       case CompareBranchType:
    298         return ImmCmpBranch_width;
    299       case TestBranchType:
    300         return ImmTestBranch_width;
    301       default:
    302         UNREACHABLE();
    303         return 0;
    304     }
    305   }
    306 
    307   // The range of the branch instruction, expressed as 'instr +- range'.
    308   static int32_t ImmBranchRange(ImmBranchType branch_type) {
    309     return
    310       (1 << (ImmBranchRangeBitwidth(branch_type) + kInstructionSizeLog2)) / 2 -
    311       kInstructionSize;
    312   }
    313 
    314   int ImmBranch() const {
    315     switch (BranchType()) {
    316       case CondBranchType: return ImmCondBranch();
    317       case UncondBranchType: return ImmUncondBranch();
    318       case CompareBranchType: return ImmCmpBranch();
    319       case TestBranchType: return ImmTestBranch();
    320       default: UNREACHABLE();
    321     }
    322     return 0;
    323   }
    324 
    325   int ImmUnresolvedInternalReference() const {
    326     DCHECK(IsUnresolvedInternalReference());
    327     // Unresolved references are encoded as two consecutive brk instructions.
    328     // The associated immediate is made of the two 16-bit payloads.
    329     int32_t high16 = ImmException();
    330     int32_t low16 = following()->ImmException();
    331     return (high16 << 16) | low16;
    332   }
    333 
    334   bool IsBranchAndLinkToRegister() const {
    335     return Mask(UnconditionalBranchToRegisterMask) == BLR;
    336   }
    337 
    338   bool IsMovz() const {
    339     return (Mask(MoveWideImmediateMask) == MOVZ_x) ||
    340            (Mask(MoveWideImmediateMask) == MOVZ_w);
    341   }
    342 
    343   bool IsMovk() const {
    344     return (Mask(MoveWideImmediateMask) == MOVK_x) ||
    345            (Mask(MoveWideImmediateMask) == MOVK_w);
    346   }
    347 
    348   bool IsMovn() const {
    349     return (Mask(MoveWideImmediateMask) == MOVN_x) ||
    350            (Mask(MoveWideImmediateMask) == MOVN_w);
    351   }
    352 
    353   bool IsNop(int n) {
    354     // A marking nop is an instruction
    355     //   mov r<n>,  r<n>
    356     // which is encoded as
    357     //   orr r<n>, xzr, r<n>
    358     return (Mask(LogicalShiftedMask) == ORR_x) &&
    359            (Rd() == Rm()) &&
    360            (Rd() == n);
    361   }
    362 
    363   // Find the PC offset encoded in this instruction. 'this' may be a branch or
    364   // a PC-relative addressing instruction.
    365   // The offset returned is unscaled.
    366   int64_t ImmPCOffset();
    367 
    368   // Find the target of this instruction. 'this' may be a branch or a
    369   // PC-relative addressing instruction.
    370   Instruction* ImmPCOffsetTarget();
    371 
    372   static bool IsValidImmPCOffset(ImmBranchType branch_type, ptrdiff_t offset);
    373   bool IsTargetInImmPCOffsetRange(Instruction* target);
    374   // Patch a PC-relative offset to refer to 'target'. 'this' may be a branch or
    375   // a PC-relative addressing instruction.
    376   void SetImmPCOffsetTarget(Isolate* isolate, Instruction* target);
    377   void SetUnresolvedInternalReferenceImmTarget(Isolate* isolate,
    378                                                Instruction* target);
    379   // Patch a literal load instruction to load from 'source'.
    380   void SetImmLLiteral(Instruction* source);
    381 
    382   uintptr_t LiteralAddress() {
    383     int offset = ImmLLiteral() << kLoadLiteralScaleLog2;
    384     return reinterpret_cast<uintptr_t>(this) + offset;
    385   }
    386 
    387   enum CheckAlignment { NO_CHECK, CHECK_ALIGNMENT };
    388 
    389   V8_INLINE const Instruction* InstructionAtOffset(
    390       int64_t offset, CheckAlignment check = CHECK_ALIGNMENT) const {
    391     // The FUZZ_disasm test relies on no check being done.
    392     DCHECK(check == NO_CHECK || IsAligned(offset, kInstructionSize));
    393     return this + offset;
    394   }
    395 
    396   V8_INLINE Instruction* InstructionAtOffset(
    397       int64_t offset, CheckAlignment check = CHECK_ALIGNMENT) {
    398     // The FUZZ_disasm test relies on no check being done.
    399     DCHECK(check == NO_CHECK || IsAligned(offset, kInstructionSize));
    400     return this + offset;
    401   }
    402 
    403   template<typename T> V8_INLINE static Instruction* Cast(T src) {
    404     return reinterpret_cast<Instruction*>(src);
    405   }
    406 
    407   V8_INLINE ptrdiff_t DistanceTo(Instruction* target) {
    408     return reinterpret_cast<Address>(target) - reinterpret_cast<Address>(this);
    409   }
    410 
    411 
    412   static const int ImmPCRelRangeBitwidth = 21;
    413   static bool IsValidPCRelOffset(ptrdiff_t offset) { return is_int21(offset); }
    414   void SetPCRelImmTarget(Isolate* isolate, Instruction* target);
    415   void SetBranchImmTarget(Instruction* target);
    416 };
    417 
    418 
    419 // Where Instruction looks at instructions generated by the Assembler,
    420 // InstructionSequence looks at instructions sequences generated by the
    421 // MacroAssembler.
    422 class InstructionSequence : public Instruction {
    423  public:
    424   static InstructionSequence* At(Address address) {
    425     return reinterpret_cast<InstructionSequence*>(address);
    426   }
    427 
    428   // Sequences generated by MacroAssembler::InlineData().
    429   bool IsInlineData() const;
    430   uint64_t InlineData() const;
    431 };
    432 
    433 
    434 // Simulator/Debugger debug instructions ---------------------------------------
    435 // Each debug marker is represented by a HLT instruction. The immediate comment
    436 // field in the instruction is used to identify the type of debug marker. Each
    437 // marker encodes arguments in a different way, as described below.
    438 
    439 // Indicate to the Debugger that the instruction is a redirected call.
    440 const Instr kImmExceptionIsRedirectedCall = 0xca11;
    441 
    442 // Represent unreachable code. This is used as a guard in parts of the code that
    443 // should not be reachable, such as in data encoded inline in the instructions.
    444 const Instr kImmExceptionIsUnreachable = 0xdebf;
    445 
    446 // A pseudo 'printf' instruction. The arguments will be passed to the platform
    447 // printf method.
    448 const Instr kImmExceptionIsPrintf = 0xdeb1;
    449 // Most parameters are stored in ARM64 registers as if the printf
    450 // pseudo-instruction was a call to the real printf method:
    451 //      x0: The format string.
    452 //   x1-x7: Optional arguments.
    453 //   d0-d7: Optional arguments.
    454 //
    455 // Also, the argument layout is described inline in the instructions:
    456 //  - arg_count: The number of arguments.
    457 //  - arg_pattern: A set of PrintfArgPattern values, packed into two-bit fields.
    458 //
    459 // Floating-point and integer arguments are passed in separate sets of registers
    460 // in AAPCS64 (even for varargs functions), so it is not possible to determine
    461 // the type of each argument without some information about the values that were
    462 // passed in. This information could be retrieved from the printf format string,
    463 // but the format string is not trivial to parse so we encode the relevant
    464 // information with the HLT instruction.
    465 const unsigned kPrintfArgCountOffset = 1 * kInstructionSize;
    466 const unsigned kPrintfArgPatternListOffset = 2 * kInstructionSize;
    467 const unsigned kPrintfLength = 3 * kInstructionSize;
    468 
    469 const unsigned kPrintfMaxArgCount = 4;
    470 
    471 // The argument pattern is a set of two-bit-fields, each with one of the
    472 // following values:
    473 enum PrintfArgPattern {
    474   kPrintfArgW = 1,
    475   kPrintfArgX = 2,
    476   // There is no kPrintfArgS because floats are always converted to doubles in C
    477   // varargs calls.
    478   kPrintfArgD = 3
    479 };
    480 static const unsigned kPrintfArgPatternBits = 2;
    481 
    482 // A pseudo 'debug' instruction.
    483 const Instr kImmExceptionIsDebug = 0xdeb0;
    484 // Parameters are inlined in the code after a debug pseudo-instruction:
    485 // - Debug code.
    486 // - Debug parameters.
    487 // - Debug message string. This is a NULL-terminated ASCII string, padded to
    488 //   kInstructionSize so that subsequent instructions are correctly aligned.
    489 // - A kImmExceptionIsUnreachable marker, to catch accidental execution of the
    490 //   string data.
    491 const unsigned kDebugCodeOffset = 1 * kInstructionSize;
    492 const unsigned kDebugParamsOffset = 2 * kInstructionSize;
    493 const unsigned kDebugMessageOffset = 3 * kInstructionSize;
    494 
    495 // Debug parameters.
    496 // Used without a TRACE_ option, the Debugger will print the arguments only
    497 // once. Otherwise TRACE_ENABLE and TRACE_DISABLE will enable or disable tracing
    498 // before every instruction for the specified LOG_ parameters.
    499 //
    500 // TRACE_OVERRIDE enables the specified LOG_ parameters, and disabled any
    501 // others that were not specified.
    502 //
    503 // For example:
    504 //
    505 // __ debug("print registers and fp registers", 0, LOG_REGS | LOG_FP_REGS);
    506 // will print the registers and fp registers only once.
    507 //
    508 // __ debug("trace disasm", 1, TRACE_ENABLE | LOG_DISASM);
    509 // starts disassembling the code.
    510 //
    511 // __ debug("trace rets", 2, TRACE_ENABLE | LOG_REGS);
    512 // adds the general purpose registers to the trace.
    513 //
    514 // __ debug("stop regs", 3, TRACE_DISABLE | LOG_REGS);
    515 // stops tracing the registers.
    516 const unsigned kDebuggerTracingDirectivesMask = 3 << 6;
    517 enum DebugParameters {
    518   NO_PARAM       = 0,
    519   BREAK          = 1 << 0,
    520   LOG_DISASM     = 1 << 1,  // Use only with TRACE. Disassemble the code.
    521   LOG_REGS       = 1 << 2,  // Log general purpose registers.
    522   LOG_FP_REGS    = 1 << 3,  // Log floating-point registers.
    523   LOG_SYS_REGS   = 1 << 4,  // Log the status flags.
    524   LOG_WRITE      = 1 << 5,  // Log any memory write.
    525 
    526   LOG_STATE      = LOG_REGS | LOG_FP_REGS | LOG_SYS_REGS,
    527   LOG_ALL        = LOG_DISASM | LOG_STATE | LOG_WRITE,
    528 
    529   // Trace control.
    530   TRACE_ENABLE   = 1 << 6,
    531   TRACE_DISABLE  = 2 << 6,
    532   TRACE_OVERRIDE = 3 << 6
    533 };
    534 
    535 
    536 }  // namespace internal
    537 }  // namespace v8
    538 
    539 
    540 #endif  // V8_ARM64_INSTRUCTIONS_ARM64_H_
    541