instructions-arm64.h 25.2 KB
Newer Older
1
// Copyright 2013 the V8 project authors. All rights reserved.
2 3
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
4

5 6
#ifndef V8_CODEGEN_ARM64_INSTRUCTIONS_ARM64_H_
#define V8_CODEGEN_ARM64_INSTRUCTIONS_ARM64_H_
7

8
#include "src/base/memory.h"
9 10 11
#include "src/codegen/arm64/constants-arm64.h"
#include "src/codegen/arm64/register-arm64.h"
#include "src/codegen/arm64/utils-arm64.h"
12
#include "src/common/globals.h"
13
#include "src/utils/utils.h"
14 15 16 17

namespace v8 {
namespace internal {

18 19
struct AssemblerOptions;

20 21
// ISA constants. --------------------------------------------------------------

22
using Instr = uint32_t;
23

24 25 26 27
#if defined(V8_OS_WIN)
extern "C" {
#endif

28 29
extern const float16 kFP16PositiveInfinity;
extern const float16 kFP16NegativeInfinity;
30 31 32 33
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;
34 35 36

// This value is a signalling NaN as both a double and as a float (taking the
// least-significant word).
37 38
V8_EXPORT_PRIVATE extern const double kFP64SignallingNaN;
V8_EXPORT_PRIVATE extern const float kFP32SignallingNaN;
39 40

// A similar value, but as a quiet NaN.
41 42
V8_EXPORT_PRIVATE extern const double kFP64QuietNaN;
V8_EXPORT_PRIVATE extern const float kFP32QuietNaN;
43

44
// The default NaN values (for FPCR.DN=1).
45 46
V8_EXPORT_PRIVATE extern const double kFP64DefaultNaN;
V8_EXPORT_PRIVATE extern const float kFP32DefaultNaN;
47
extern const float16 kFP16DefaultNaN;
48

49 50 51 52
#if defined(V8_OS_WIN)
}  // end of extern "C"
#endif

53 54
unsigned CalcLSDataSize(LoadStoreOp op);
unsigned CalcLSPairDataSize(LoadStorePairOp op);
55 56 57

enum ImmBranchType {
  UnknownBranchType = 0,
58 59
  CondBranchType = 1,
  UncondBranchType = 2,
60
  CompareBranchType = 3,
61
  TestBranchType = 4
62 63
};

64
enum AddrMode { Offset, PreIndex, PostIndex };
65 66 67 68 69 70 71 72

enum FPRounding {
  // The first four values are encodable directly by FPCR<RMode>.
  FPTieEven = 0x0,
  FPPositiveInfinity = 0x1,
  FPNegativeInfinity = 0x2,
  FPZero = 0x3,

73 74 75 76
  // 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
77 78
};

79
enum Reg31Mode { Reg31IsStackPointer, Reg31IsZeroRegister };
80 81 82 83 84

// Instructions. ---------------------------------------------------------------

class Instruction {
 public:
85
  V8_INLINE Instr InstructionBits() const {
86 87
    // Usually this is aligned, but when de/serializing that's not guaranteed.
    return base::ReadUnalignedValue<Instr>(reinterpret_cast<Address>(this));
88 89
  }

90
  V8_INLINE void SetInstructionBits(Instr new_instr) {
91 92
    // Usually this is aligned, but when de/serializing that's not guaranteed.
    base::WriteUnalignedValue(reinterpret_cast<Address>(this), new_instr);
93 94
  }

95
  int Bit(int pos) const { return (InstructionBits() >> pos) & 1; }
96 97 98 99 100 101

  uint32_t Bits(int msb, int lsb) const {
    return unsigned_bitextract_32(msb, lsb, InstructionBits());
  }

  int32_t SignedBits(int msb, int lsb) const {
102 103 104
    // Usually this is aligned, but when de/serializing that's not guaranteed.
    int32_t bits =
        base::ReadUnalignedValue<int32_t>(reinterpret_cast<Address>(this));
105 106 107
    return signed_bitextract_32(msb, lsb, bits);
  }

108
  Instr Mask(uint32_t mask) const { return InstructionBits() & mask; }
109

110
  V8_INLINE const Instruction* following(int count = 1) const {
111
    return InstructionAtOffset(count * static_cast<int>(kInstrSize));
112 113
  }

114
  V8_INLINE Instruction* following(int count = 1) {
115
    return InstructionAtOffset(count * static_cast<int>(kInstrSize));
116 117
  }

118 119 120 121
  V8_INLINE const Instruction* preceding(int count = 1) const {
    return following(-count);
  }

122
  V8_INLINE Instruction* preceding(int count = 1) { return following(-count); }
123

124 125
#define DEFINE_GETTER(Name, HighBit, LowBit, Func) \
  int32_t Name() const { return Func(HighBit, LowBit); }
126
  INSTRUCTION_FIELDS_LIST(DEFINE_GETTER)
127
#undef DEFINE_GETTER
128 129 130 131

  // ImmPCRel is a compound field (not present in INSTRUCTION_FIELDS_LIST),
  // formed from ImmPCRelLo and ImmPCRelHi.
  int ImmPCRel() const {
132
    DCHECK(IsPCRelAddressing());
133 134
    int offset = (static_cast<uint32_t>(ImmPCRelHi()) << ImmPCRelLo_width) |
                 ImmPCRelLo();
135
    int width = ImmPCRelLo_width + ImmPCRelHi_width;
136
    return signed_bitextract_32(width - 1, 0, offset);
137 138 139
  }

  uint64_t ImmLogical();
140
  unsigned ImmNEONabcdefgh() const;
141 142
  float ImmFP32();
  double ImmFP64();
143 144
  float ImmNEONFP32() const;
  double ImmNEONFP64() const;
145

146 147 148 149 150
  unsigned SizeLS() const {
    return CalcLSDataSize(static_cast<LoadStoreOp>(Mask(LoadStoreMask)));
  }

  unsigned SizeLSPair() const {
151 152 153 154
    return CalcLSPairDataSize(
        static_cast<LoadStorePairOp>(Mask(LoadStorePairMask)));
  }

155 156 157 158 159 160 161 162
  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;
  }

163 164 165 166 167 168 169 170 171 172 173 174 175
  // Helpers.
  bool IsCondBranchImm() const {
    return Mask(ConditionalBranchFMask) == ConditionalBranchFixed;
  }

  bool IsUncondBranchImm() const {
    return Mask(UnconditionalBranchFMask) == UnconditionalBranchFixed;
  }

  bool IsCompareBranch() const {
    return Mask(CompareBranchFMask) == CompareBranchFixed;
  }

176
  bool IsTestBranch() const { return Mask(TestBranchFMask) == TestBranchFixed; }
177

178
  bool IsImmBranch() const { return BranchType() != UnknownBranchType; }
179

180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206
  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);
  }

207 208 209 210
  bool IsLdrLiteral() const {
    return Mask(LoadLiteralFMask) == LoadLiteralFixed;
  }

211
  bool IsLdrLiteralX() const { return Mask(LoadLiteralMask) == LDR_x_lit; }
212
  bool IsLdrLiteralW() const { return Mask(LoadLiteralMask) == LDR_w_lit; }
213 214 215 216 217

  bool IsPCRelAddressing() const {
    return Mask(PCRelAddressingFMask) == PCRelAddressingFixed;
  }

218
  bool IsAdr() const { return Mask(PCRelAddressingMask) == ADR; }
219

220 221 222 223 224 225 226 227
  bool IsBrk() const { return Mask(ExceptionMask) == BRK; }

  bool IsUnresolvedInternalReference() const {
    // Unresolved internal references are encoded as two consecutive brk
    // instructions.
    return IsBrk() && following()->IsBrk();
  }

228 229 230 231 232 233 234 235
  bool IsLogicalImmediate() const {
    return Mask(LogicalImmediateFMask) == LogicalImmediateFixed;
  }

  bool IsAddSubImmediate() const {
    return Mask(AddSubImmediateFMask) == AddSubImmediateFixed;
  }

236 237 238 239
  bool IsAddSubShifted() const {
    return Mask(AddSubShiftedFMask) == AddSubShiftedFixed;
  }

240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256
  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 {
257
    // The following instructions use sp or wsp as Rd:
258 259 260 261 262 263 264 265 266 267 268 269 270
    //  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)
271
      // can set the flags. The others can all write into sp.
272 273 274 275 276 277 278 279 280 281 282 283 284 285
      // 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 {
286
    // The following instructions use sp or wsp as Rn:
287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310
    //  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;
    }
  }

311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327
  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) {
328 329
    return (1 << (ImmBranchRangeBitwidth(branch_type) + kInstrSizeLog2)) / 2 -
           kInstrSize;
330 331
  }

332 333
  int ImmBranch() const {
    switch (BranchType()) {
334 335 336 337 338 339 340 341 342 343
      case CondBranchType:
        return ImmCondBranch();
      case UncondBranchType:
        return ImmUncondBranch();
      case CompareBranchType:
        return ImmCmpBranch();
      case TestBranchType:
        return ImmTestBranch();
      default:
        UNREACHABLE();
344 345 346 347
    }
    return 0;
  }

348 349 350 351 352 353 354 355 356
  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;
  }

357 358 359 360 361 362
  bool IsUnconditionalBranch() const {
    return Mask(UnconditionalBranchMask) == B;
  }

  bool IsBranchAndLink() const { return Mask(UnconditionalBranchMask) == BL; }

363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381
  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);
  }

382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399
  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;
  }

400 401 402 403 404
  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>
405
    return (Mask(LogicalShiftedMask) == ORR_x) && (Rd() == Rm()) && (Rd() == n);
406 407 408 409 410
  }

  // Find the PC offset encoded in this instruction. 'this' may be a branch or
  // a PC-relative addressing instruction.
  // The offset returned is unscaled.
411
  V8_EXPORT_PRIVATE int64_t ImmPCOffset();
412 413 414

  // Find the target of this instruction. 'this' may be a branch or a
  // PC-relative addressing instruction.
415
  V8_EXPORT_PRIVATE Instruction* ImmPCOffsetTarget();
416

417 418
  // Check if the offset is in range of a given branch type. The offset is
  // a byte offset, unscaled.
419
  static bool IsValidImmPCOffset(ImmBranchType branch_type, ptrdiff_t offset);
420
  bool IsTargetInImmPCOffsetRange(Instruction* target);
421 422
  // Patch a PC-relative offset to refer to 'target'. 'this' may be a branch or
  // a PC-relative addressing instruction.
423
  void SetImmPCOffsetTarget(const AssemblerOptions& options,
424
                            Instruction* target);
425 426
  void SetUnresolvedInternalReferenceImmTarget(const AssemblerOptions& options,
                                               Instruction* target);
427 428 429
  // Patch a literal load instruction to load from 'source'.
  void SetImmLLiteral(Instruction* source);

430
  uintptr_t LiteralAddress() {
431
    int offset = ImmLLiteral() * kLoadLiteralScale;
432
    return reinterpret_cast<uintptr_t>(this) + offset;
433 434
  }

435
  enum CheckAlignment { NO_CHECK, CHECK_ALIGNMENT };
436

437 438 439
  V8_INLINE const Instruction* InstructionAtOffset(
      int64_t offset, CheckAlignment check = CHECK_ALIGNMENT) const {
    // The FUZZ_disasm test relies on no check being done.
440
    DCHECK(check == NO_CHECK || IsAligned(offset, kInstrSize));
441 442 443
    return this + offset;
  }

444
  V8_INLINE Instruction* InstructionAtOffset(
445
      int64_t offset, CheckAlignment check = CHECK_ALIGNMENT) {
446
    // The FUZZ_disasm test relies on no check being done.
447
    DCHECK(check == NO_CHECK || IsAligned(offset, kInstrSize));
448
    return this + offset;
449 450
  }

451 452
  template <typename T>
  V8_INLINE static Instruction* Cast(T src) {
453 454 455
    return reinterpret_cast<Instruction*>(src);
  }

456 457 458 459
  V8_INLINE ptrdiff_t DistanceTo(Instruction* target) {
    return reinterpret_cast<Address>(target) - reinterpret_cast<Address>(this);
  }

460
  static const int ImmPCRelRangeBitwidth = 21;
461
  static bool IsValidPCRelOffset(ptrdiff_t offset) { return is_int21(offset); }
462
  void SetPCRelImmTarget(const AssemblerOptions& options, Instruction* target);
463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480
  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;
481 482 483
// 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.
484 485 486
//   x1-x7: Optional arguments.
//   d0-d7: Optional arguments.
//
487 488 489 490 491 492 493 494 495 496
// 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.
497 498 499
const unsigned kPrintfArgCountOffset = 1 * kInstrSize;
const unsigned kPrintfArgPatternListOffset = 2 * kInstrSize;
const unsigned kPrintfLength = 3 * kInstrSize;
500 501 502 503 504 505 506 507 508 509 510 511 512

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;
513 514 515 516 517 518

// A pseudo 'debug' instruction.
const Instr kImmExceptionIsDebug = 0xdeb0;
// Parameters are inlined in the code after a debug pseudo-instruction:
// - Debug code.
// - Debug parameters.
519
// - Debug message string. This is a nullptr-terminated ASCII string, padded to
520
//   kInstrSize so that subsequent instructions are correctly aligned.
521 522
// - A kImmExceptionIsUnreachable marker, to catch accidental execution of the
//   string data.
523 524 525
const unsigned kDebugCodeOffset = 1 * kInstrSize;
const unsigned kDebugParamsOffset = 2 * kInstrSize;
const unsigned kDebugMessageOffset = 3 * kInstrSize;
526 527 528 529 530 531 532 533 534 535 536

// 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:
//
537
// __ debug("print registers and fp registers", 0, LOG_REGS | LOG_VREGS);
538 539 540 541 542 543 544 545 546 547 548 549
// 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 {
550 551 552 553 554 555 556 557 558 559 560
  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,
561 562

  // Trace control.
563 564
  TRACE_ENABLE = 1 << 6,
  TRACE_DISABLE = 2 << 6,
565 566 567
  TRACE_OVERRIDE = 3 << 6
};

568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584
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;
585

586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609
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,
610 611
                     const NEONFormatMap* format1 = nullptr,
                     const NEONFormatMap* format2 = nullptr);
612 613 614 615 616 617 618 619 620 621
  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,
622 623
                         SubstitutionMode mode2 = kFormat,
                         SubstitutionMode mode3 = kFormat);
624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741

  // 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 &map;
  }

  // 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 &map;
  }

  // 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 &map;
  }

  // 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 &map;
  }

  // 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 &map;
  }

  // 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 &map;
  }

  // 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 &map;
  }

  // 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 &map;
  }

  // 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 &map;
  }

  // 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 &map;
  }

 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_;
742
  const NEONFormatMap* formats_[4];
743 744 745
  char form_buffer_[64];
  char mne_buffer_[16];
};
746 747
}  // namespace internal
}  // namespace v8
748

749
#endif  // V8_CODEGEN_ARM64_INSTRUCTIONS_ARM64_H_