// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

#ifndef RUNTIME_VM_CONSTANTS_X64_H_
#define RUNTIME_VM_CONSTANTS_X64_H_

#ifndef RUNTIME_VM_CONSTANTS_H_
#error Do not include constants_x64.h directly; use constants.h instead.
#endif

#include "platform/assert.h"
#include "platform/globals.h"
#include "platform/utils.h"

#include "vm/constants_base.h"

namespace dart {

#define R(reg) (static_cast<RegList>(1) << (reg))

enum Register {
  RAX = 0,
  RCX = 1,
  RDX = 2,
  RBX = 3,
  RSP = 4,  // SP
  RBP = 5,  // FP
  RSI = 6,
  RDI = 7,
  R8 = 8,
  R9 = 9,
  R10 = 10,
  R11 = 11,  // TMP
  R12 = 12,  // CODE_REG
  R13 = 13,
  R14 = 14,  // THR
  R15 = 15,  // PP
  kNumberOfCpuRegisters = 16,
  kNoRegister = -1,  // Signals an illegal register.
};

enum ByteRegister {
  AL = 0,
  CL = 1,
  DL = 2,
  BL = 3,
  AH = 4,
  CH = 5,
  DH = 6,
  BH = 7,
  SPL = 4 | 0x10,
  BPL = 5 | 0x10,
  SIL = 6 | 0x10,
  DIL = 7 | 0x10,
  R8B = 8,
  R9B = 9,
  R10B = 10,
  R11B = 11,
  R12B = 12,
  R13B = 13,
  R14B = 14,
  R15B = 15,
  kNumberOfByteRegisters = 16,
  kNoByteRegister = -1  // Signals an illegal register.
};

inline ByteRegister ByteRegisterOf(Register reg) {
  if (RSP <= reg && reg <= RDI) {
    return static_cast<ByteRegister>(reg | 0x10);
  } else {
    return static_cast<ByteRegister>(reg);
  }
}

enum XmmRegister {
  XMM0 = 0,
  XMM1 = 1,
  XMM2 = 2,
  XMM3 = 3,
  XMM4 = 4,
  XMM5 = 5,
  XMM6 = 6,
  XMM7 = 7,
  XMM8 = 8,
  XMM9 = 9,
  XMM10 = 10,
  XMM11 = 11,
  XMM12 = 12,
  XMM13 = 13,
  XMM14 = 14,
  XMM15 = 15,
  kNumberOfXmmRegisters = 16,
  kNoXmmRegister = -1  // Signals an illegal register.
};

// Architecture independent aliases.
typedef XmmRegister FpuRegister;
const FpuRegister FpuTMP = XMM15;
const int kFpuRegisterSize = 16;
typedef simd128_value_t fpu_register_t;
const int kNumberOfFpuRegisters = kNumberOfXmmRegisters;
const FpuRegister kNoFpuRegister = kNoXmmRegister;

extern const char* const cpu_reg_names[kNumberOfCpuRegisters];
extern const char* const cpu_reg_abi_names[kNumberOfCpuRegisters];
extern const char* const cpu_reg_byte_names[kNumberOfByteRegisters];
extern const char* const fpu_reg_names[kNumberOfXmmRegisters];

enum RexBits {
  REX_NONE = 0,
  REX_B = 1 << 0,
  REX_X = 1 << 1,
  REX_R = 1 << 2,
  REX_W = 1 << 3,
  REX_PREFIX = 1 << 6
};

// Register aliases.
const Register TMP = R11;  // Used as scratch register by the assembler.
const Register TMP2 = kNoRegister;  // No second assembler scratch register.
// Caches object pool pointer in generated code.
const Register PP = R15;
const Register SPREG = RSP;          // Stack pointer register.
const Register FPREG = RBP;          // Frame pointer register.
const Register IC_DATA_REG = RBX;    // ICData/MegamorphicCache register.
const Register ARGS_DESC_REG = R10;  // Arguments descriptor register.
const Register CODE_REG = R12;
// Set when calling Dart functions in JIT mode, used by LazyCompileStub.
const Register FUNCTION_REG = RAX;
const Register THR = R14;  // Caches current thread in generated code.
const Register CALLEE_SAVED_TEMP = RBX;

// ABI for catch-clause entry point.
const Register kExceptionObjectReg = RAX;
const Register kStackTraceObjectReg = RDX;

// ABI for write barrier stub.
const Register kWriteBarrierObjectReg = RDX;
const Register kWriteBarrierValueReg = RAX;
const Register kWriteBarrierSlotReg = R13;

// Common ABI for shared slow path stubs.
struct SharedSlowPathStubABI {
  static constexpr Register kResultReg = RAX;
};

// ABI for instantiation stubs.
struct InstantiationABI {
  static constexpr Register kUninstantiatedTypeArgumentsReg = RBX;
  static constexpr Register kInstantiatorTypeArgumentsReg = RDX;
  static constexpr Register kFunctionTypeArgumentsReg = RCX;
  static constexpr Register kResultTypeArgumentsReg = RAX;
  static constexpr Register kResultTypeReg = RAX;
  static constexpr Register kScratchReg = R9;
};

// Registers in addition to those listed in InstantiationABI used inside the
// implementation of the InstantiateTypeArguments stubs.
struct InstantiateTAVInternalRegs {
  // The set of registers that must be pushed/popped when probing a hash-based
  // cache due to overlap with the registers in InstantiationABI.
  static constexpr intptr_t kSavedRegisters = 0;

  // Additional registers used to probe hash-based caches.
  static constexpr Register kEntryStartReg = R10;
  static constexpr Register kProbeMaskReg = R13;
  static constexpr Register kProbeDistanceReg = R8;
  static constexpr Register kCurrentEntryIndexReg = RSI;
};

// Registers in addition to those listed in TypeTestABI used inside the
// implementation of type testing stubs that are _not_ preserved.
struct TTSInternalRegs {
  static constexpr Register kInstanceTypeArgumentsReg = RSI;
  static constexpr Register kScratchReg = R8;
  static constexpr Register kSubTypeArgumentReg = R10;
  static constexpr Register kSuperTypeArgumentReg = R13;

  // Must be pushed/popped whenever generic type arguments are being checked as
  // they overlap with registers in TypeTestABI.
  static constexpr intptr_t kSavedTypeArgumentRegisters = 0;

  static constexpr intptr_t kInternalRegisters =
      ((1 << kInstanceTypeArgumentsReg) | (1 << kScratchReg) |
       (1 << kSubTypeArgumentReg) | (1 << kSuperTypeArgumentReg)) &
      ~kSavedTypeArgumentRegisters;
};

// Registers in addition to those listed in TypeTestABI used inside the
// implementation of subtype test cache stubs that are _not_ preserved.
struct STCInternalRegs {
  static constexpr Register kCacheEntryReg = RDI;
  static constexpr Register kInstanceCidOrSignatureReg = R10;
  static constexpr Register kInstanceInstantiatorTypeArgumentsReg = R13;

  static constexpr intptr_t kInternalRegisters =
      (1 << kCacheEntryReg) | (1 << kInstanceCidOrSignatureReg) |
      (1 << kInstanceInstantiatorTypeArgumentsReg);
};

// Calling convention when calling TypeTestingStub and SubtypeTestCacheStub.
struct TypeTestABI {
  static constexpr Register kInstanceReg = RAX;
  static constexpr Register kDstTypeReg = RBX;
  static constexpr Register kInstantiatorTypeArgumentsReg = RDX;
  static constexpr Register kFunctionTypeArgumentsReg = RCX;
  static constexpr Register kSubtypeTestCacheReg = R9;
  static constexpr Register kScratchReg = RSI;

  // For calls to InstanceOfStub.
  static constexpr Register kInstanceOfResultReg = kInstanceReg;
  // For calls to SubtypeNTestCacheStub. Must not overlap with any other
  // registers above, for it is also used internally as kNullReg in those stubs.
  static constexpr Register kSubtypeTestCacheResultReg = R8;

  static constexpr intptr_t kPreservedAbiRegisters =
      (1 << kInstanceReg) | (1 << kDstTypeReg) |
      (1 << kInstantiatorTypeArgumentsReg) | (1 << kFunctionTypeArgumentsReg);

  static constexpr intptr_t kNonPreservedAbiRegisters =
      TTSInternalRegs::kInternalRegisters |
      STCInternalRegs::kInternalRegisters | (1 << kSubtypeTestCacheReg) |
      (1 << kScratchReg) | (1 << kSubtypeTestCacheResultReg) | (1 << CODE_REG);

  static constexpr intptr_t kAbiRegisters =
      kPreservedAbiRegisters | kNonPreservedAbiRegisters;
};

// Calling convention when calling AssertSubtypeStub.
struct AssertSubtypeABI {
  static constexpr Register kSubTypeReg = RAX;
  static constexpr Register kSuperTypeReg = RBX;
  static constexpr Register kInstantiatorTypeArgumentsReg = RDX;
  static constexpr Register kFunctionTypeArgumentsReg = RCX;
  static constexpr Register kDstNameReg = R9;

  static constexpr intptr_t kAbiRegisters =
      (1 << kSubTypeReg) | (1 << kSuperTypeReg) |
      (1 << kInstantiatorTypeArgumentsReg) | (1 << kFunctionTypeArgumentsReg) |
      (1 << kDstNameReg);

  // No result register, as AssertSubtype is only run for side effect
  // (throws if the subtype check fails).
};

// ABI for InitStaticFieldStub.
struct InitStaticFieldABI {
  static constexpr Register kFieldReg = RDX;
  static constexpr Register kResultReg = RAX;
};

// Registers used inside the implementation of InitLateStaticFieldStub.
struct InitLateStaticFieldInternalRegs {
  static constexpr Register kAddressReg = RCX;
  static constexpr Register kScratchReg = RSI;
};

// ABI for InitInstanceFieldStub.
struct InitInstanceFieldABI {
  static constexpr Register kInstanceReg = RBX;
  static constexpr Register kFieldReg = RDX;
  static constexpr Register kResultReg = RAX;
};

// Registers used inside the implementation of InitLateInstanceFieldStub.
struct InitLateInstanceFieldInternalRegs {
  static constexpr Register kAddressReg = RCX;
  static constexpr Register kScratchReg = RSI;
};

// ABI for LateInitializationError stubs.
struct LateInitializationErrorABI {
  static constexpr Register kFieldReg = RSI;
};

// ABI for FieldAccessError stubs.
struct FieldAccessErrorABI {
  static constexpr Register kFieldReg = RSI;
};

// ABI for ThrowStub.
struct ThrowABI {
  static constexpr Register kExceptionReg = RAX;
};

// ABI for ReThrowStub.
struct ReThrowABI {
  static constexpr Register kExceptionReg = RAX;
  static constexpr Register kStackTraceReg = RBX;
};

// ABI for RangeErrorStub.
struct RangeErrorABI {
  static constexpr Register kLengthReg = RAX;
  static constexpr Register kIndexReg = RBX;
};

// ABI for AllocateObjectStub.
struct AllocateObjectABI {
  static constexpr Register kResultReg = RAX;
  static constexpr Register kTypeArgumentsReg = RDX;
  static constexpr Register kTagsReg = R8;
};

// ABI for AllocateClosureStub.
struct AllocateClosureABI {
  static constexpr Register kResultReg = AllocateObjectABI::kResultReg;
  static constexpr Register kFunctionReg = RBX;
  static constexpr Register kLengthAndFlagsReg = RDX;
  static constexpr Register kContextReg = RCX;
  static constexpr Register kScratchReg = R13;
};

// ABI for AllocateMintShared*Stub.
struct AllocateMintABI {
  static constexpr Register kResultReg = AllocateObjectABI::kResultReg;
  static constexpr Register kTempReg = RBX;
};

// ABI for Allocate{Mint,Double,Float32x4,Float64x2}Stub.
struct AllocateBoxABI {
  static constexpr Register kResultReg = AllocateObjectABI::kResultReg;
  static constexpr Register kTempReg = RBX;
};

// ABI for AllocateArrayStub.
struct AllocateArrayABI {
  static constexpr Register kResultReg = AllocateObjectABI::kResultReg;
  static constexpr Register kLengthReg = R10;
  static constexpr Register kTypeArgumentsReg = RBX;
};

// ABI for AllocateRecordStub.
struct AllocateRecordABI {
  static constexpr Register kResultReg = AllocateObjectABI::kResultReg;
  static constexpr Register kShapeReg = RBX;
  static constexpr Register kTemp1Reg = RDX;
  static constexpr Register kTemp2Reg = RCX;
};

// ABI for AllocateSmallRecordStub (AllocateRecord2, AllocateRecord2Named,
// AllocateRecord3, AllocateRecord3Named).
struct AllocateSmallRecordABI {
  static constexpr Register kResultReg = AllocateObjectABI::kResultReg;
  static constexpr Register kShapeReg = R10;
  static constexpr Register kValue0Reg = RBX;
  static constexpr Register kValue1Reg = RDX;
  static constexpr Register kValue2Reg = RCX;
  static constexpr Register kTempReg = RDI;
};

// ABI for AllocateTypedDataArrayStub.
struct AllocateTypedDataArrayABI {
  static constexpr Register kResultReg = AllocateObjectABI::kResultReg;
  static constexpr Register kLengthReg = kResultReg;
};

// ABI for BoxDoubleStub.
struct BoxDoubleStubABI {
  static constexpr FpuRegister kValueReg = XMM0;
  static constexpr Register kTempReg = RBX;
  static constexpr Register kResultReg = RAX;
};

// ABI for DoubleToIntegerStub.
struct DoubleToIntegerStubABI {
  static constexpr FpuRegister kInputReg = XMM0;
  static constexpr Register kRecognizedKindReg = RAX;
  static constexpr Register kResultReg = RAX;
};

// ABI for CheckedStoreIntoSharedStub.
struct CheckedStoreIntoSharedStubABI {
  static constexpr Register kFieldReg = RBX;
  static constexpr Register kValueReg = RDX;
  static constexpr Register kResultReg = RAX;
};

// ABI for EnsureDeeplyImmutableStub.
struct EnsureDeeplyImmutableStubABI {
  static constexpr Register kValueReg = RAX;
  static constexpr Register kTempReg = R10;
};

// ABI for SuspendStub (AwaitStub, AwaitWithTypeCheckStub, YieldAsyncStarStub,
// SuspendSyncStarAtStartStub, SuspendSyncStarAtYieldStub).
struct SuspendStubABI {
  static constexpr Register kArgumentReg = RAX;
  static constexpr Register kTypeArgsReg = RDX;  // Can be the same as kTempReg
  static constexpr Register kTempReg = RDX;
  static constexpr Register kFrameSizeReg = RCX;
  static constexpr Register kSuspendStateReg = RBX;
  static constexpr Register kFunctionDataReg = R8;
  static constexpr Register kSrcFrameReg = RSI;
  static constexpr Register kDstFrameReg = RDI;

  // Number of bytes to skip after
  // suspend stub return address in order to resume.
  // X64: mov rsp, rbp; pop rbp; ret
  static constexpr intptr_t kResumePcDistance = 5;
};

// ABI for InitSuspendableFunctionStub (InitAsyncStub, InitAsyncStarStub,
// InitSyncStarStub).
struct InitSuspendableFunctionStubABI {
  static constexpr Register kTypeArgsReg = RAX;
};

// ABI for ResumeStub
struct ResumeStubABI {
  static constexpr Register kSuspendStateReg = RBX;
  static constexpr Register kTempReg = RDX;
  // Registers for the frame copying (the 1st part).
  static constexpr Register kFrameSizeReg = RCX;
  static constexpr Register kSrcFrameReg = RSI;
  static constexpr Register kDstFrameReg = RDI;
  // Registers for control transfer.
  // (the 2nd part, can reuse registers from the 1st part)
  static constexpr Register kResumePcReg = RCX;
  // Can also reuse kSuspendStateReg but should not conflict with CODE_REG/PP.
  static constexpr Register kExceptionReg = RSI;
  static constexpr Register kStackTraceReg = RDI;
};

// ABI for ReturnStub (ReturnAsyncStub, ReturnAsyncNotFutureStub,
// ReturnAsyncStarStub).
struct ReturnStubABI {
  static constexpr Register kSuspendStateReg = RBX;
};

// ABI for AsyncExceptionHandlerStub.
struct AsyncExceptionHandlerStubABI {
  static constexpr Register kSuspendStateReg = RBX;
};

// ABI for CloneSuspendStateStub.
struct CloneSuspendStateStubABI {
  static constexpr Register kSourceReg = RAX;
  static constexpr Register kDestinationReg = RBX;
  static constexpr Register kTempReg = RDX;
  static constexpr Register kFrameSizeReg = RCX;
  static constexpr Register kSrcFrameReg = RSI;
  static constexpr Register kDstFrameReg = RDI;
};

// ABI for FfiAsyncCallbackSendStub.
struct FfiAsyncCallbackSendStubABI {
  static constexpr Register kArgsReg = RAX;
};

// ABI for DispatchTableNullErrorStub and consequently for all dispatch
// table calls (though normal functions will not expect or use this
// register). This ABI is added to distinguish memory corruption errors from
// null errors.
struct DispatchTableNullErrorABI {
  static constexpr Register kClassIdReg = RCX;
};

typedef uint32_t RegList;
const RegList kAllCpuRegistersList = 0xFFFF;
const RegList kAllFpuRegistersList = 0xFFFF;

const RegList kReservedCpuRegisters =
    R(SPREG) | R(FPREG) | R(TMP) | R(PP) | R(THR);
constexpr intptr_t kNumberOfReservedCpuRegisters =
    Utils::CountOneBits32(kReservedCpuRegisters);
// CPU registers available to Dart allocator.
const RegList kDartAvailableCpuRegs =
    kAllCpuRegistersList & ~kReservedCpuRegisters;
constexpr int kNumberOfDartAvailableCpuRegs =
    kNumberOfCpuRegisters - kNumberOfReservedCpuRegisters;
// Low numbered registers sometimes require fewer prefixes.
constexpr int kRegisterAllocationBias = 0;
constexpr int kStoreBufferWrapperSize = 13;

#if defined(DART_TARGET_OS_WINDOWS)
const RegList kAbiPreservedCpuRegs =
    R(RBX) | R(RSI) | R(RDI) | R(R12) | R(R13) | R(R14) | R(R15);
const RegList kAbiVolatileFpuRegs =
    R(XMM0) | R(XMM1) | R(XMM2) | R(XMM3) | R(XMM4) | R(XMM5);
#else
const RegList kAbiPreservedCpuRegs = R(RBX) | R(R12) | R(R13) | R(R14) | R(R15);
const RegList kAbiVolatileFpuRegs = kAllFpuRegistersList;
#endif

// Registers available to Dart that are not preserved by runtime calls.
const RegList kDartVolatileCpuRegs =
    kDartAvailableCpuRegs & ~kAbiPreservedCpuRegs;
const RegList kDartVolatileFpuRegs = kAbiVolatileFpuRegs & ~R(FpuTMP);

enum ScaleFactor {
  TIMES_1 = 0,
  TIMES_2 = 1,
  TIMES_4 = 2,
  TIMES_8 = 3,
  // Note that Intel addressing does not support this addressing.
  // > Scale factor — A value of 2, 4, or 8 that is multiplied by the index
  // > value.
  // https://software.intel.com/en-us/download/intel-64-and-ia-32-architectures-sdm-combined-volumes-1-2a-2b-2c-2d-3a-3b-3c-3d-and-4
  // 3.7.5 Specifying an Offset
  TIMES_16 = 4,
// We can't include vm/compiler/runtime_api.h, so just be explicit instead
// of using (dart::)kWordSizeLog2.
#if defined(TARGET_ARCH_IS_64_BIT)
  // Used for Smi-boxed indices.
  TIMES_HALF_WORD_SIZE = kInt64SizeLog2 - 1,
  // Used for unboxed indices.
  TIMES_WORD_SIZE = kInt64SizeLog2,
#else
#error "Unexpected word size"
#endif
#if !defined(DART_COMPRESSED_POINTERS)
  TIMES_COMPRESSED_WORD_SIZE = TIMES_WORD_SIZE,
#else
  TIMES_COMPRESSED_WORD_SIZE = TIMES_HALF_WORD_SIZE,
#endif
  // Used for Smi-boxed indices.
  TIMES_COMPRESSED_HALF_WORD_SIZE = TIMES_COMPRESSED_WORD_SIZE - 1,
};

class CallingConventions {
 public:
#if defined(DART_TARGET_OS_WINDOWS)
  static constexpr Register kArg1Reg = RCX;
  static constexpr Register kArg2Reg = RDX;
  static constexpr Register kArg3Reg = R8;
  static constexpr Register kArg4Reg = R9;
  static const Register ArgumentRegisters[];
  static constexpr intptr_t kArgumentRegisters =
      R(kArg1Reg) | R(kArg2Reg) | R(kArg3Reg) | R(kArg4Reg);
  static constexpr intptr_t kNumArgRegs = 4;
  static constexpr Register kPointerToReturnStructRegisterCall = kArg1Reg;

  static const XmmRegister FpuArgumentRegisters[];
  static constexpr intptr_t kFpuArgumentRegisters =
      R(XMM0) | R(XMM1) | R(XMM2) | R(XMM3);
  static constexpr intptr_t kNumFpuArgRegs = 4;

  // Whether ArgumentRegisters[i] prevents using XmmArgumentRegisters[i] at the
  // same time and vice versa.
  static constexpr bool kArgumentIntRegXorFpuReg = true;

  // AL not set on vararg calls in Windows.
  static constexpr Register kVarArgFpuRegisterCount = kNoRegister;

  // > The x64 Application Binary Interface (ABI) uses a four-register
  // > fast-call calling convention by default. Space is allocated on the call
  // > stack as a shadow store for callees to save those registers.
  // https://docs.microsoft.com/en-us/cpp/build/x64-calling-convention?view=msvc-160
  //
  // The caller allocates this space. The caller should also reclaim this space
  // after the call to restore the stack to its original state if needed.
  //
  // This is also known as home space.
  // https://devblogs.microsoft.com/oldnewthing/20160623-00/?p=93735
  static constexpr intptr_t kShadowSpaceBytes = 4 * kWordSize;

  static constexpr intptr_t kVolatileCpuRegisters =
      R(RAX) | R(RCX) | R(RDX) | R(R8) | R(R9) | R(R10) | R(R11);

  static constexpr RegList kVolatileXmmRegisters = kAbiVolatileFpuRegs;

  static constexpr intptr_t kCalleeSaveXmmRegisters =
      R(XMM6) | R(XMM7) | R(XMM8) | R(XMM9) | R(XMM10) | R(XMM11) | R(XMM12) |
      R(XMM13) | R(XMM14) | R(XMM15);

  static constexpr XmmRegister xmmFirstNonParameterReg = XMM4;

  // Windows x64 ABI specifies that small objects are passed in registers.
  // Otherwise they are passed by reference.
  static const size_t kRegisterTransferLimit = 16;

  static constexpr Register kReturnReg = RAX;
  static constexpr Register kSecondReturnReg = RDX;
  static constexpr FpuRegister kReturnFpuReg = XMM0;
  static constexpr FpuRegister kSecondReturnFpuReg = XMM1;
  static constexpr Register kPointerToReturnStructRegisterReturn = kReturnReg;

  // Whether larger than wordsize arguments are aligned to even registers.
  static constexpr AlignmentStrategy kArgumentRegisterAlignment =
      kAlignedToWordSize;
  static constexpr AlignmentStrategy kArgumentRegisterAlignmentVarArgs =
      kArgumentRegisterAlignment;

  // How stack arguments are aligned.
  static constexpr AlignmentStrategy kArgumentStackAlignment =
      kAlignedToWordSize;
  static constexpr AlignmentStrategy kArgumentStackAlignmentVarArgs =
      kArgumentStackAlignment;

  // How fields in compounds are aligned.
  static constexpr AlignmentStrategy kFieldAlignment = kAlignedToValueSize;

  // Whether 1 or 2 byte-sized arguments or return values are passed extended
  // to 4 bytes.
  static constexpr ExtensionStrategy kReturnRegisterExtension = kNotExtended;
  static constexpr ExtensionStrategy kArgumentRegisterExtension = kNotExtended;
  static constexpr ExtensionStrategy kArgumentStackExtension = kNotExtended;

#else
  static constexpr Register kArg1Reg = RDI;
  static constexpr Register kArg2Reg = RSI;
  static constexpr Register kArg3Reg = RDX;
  static constexpr Register kArg4Reg = RCX;
  static constexpr Register kArg5Reg = R8;
  static constexpr Register kArg6Reg = R9;
  static const Register ArgumentRegisters[];
  static constexpr intptr_t kArgumentRegisters = R(kArg1Reg) | R(kArg2Reg) |
                                                 R(kArg3Reg) | R(kArg4Reg) |
                                                 R(kArg5Reg) | R(kArg6Reg);
  static constexpr intptr_t kNumArgRegs = 6;
  static constexpr Register kPointerToReturnStructRegisterCall = kArg1Reg;

  static const XmmRegister FpuArgumentRegisters[];
  static constexpr intptr_t kFpuArgumentRegisters =
      R(XMM0) | R(XMM1) | R(XMM2) | R(XMM3) | R(XMM4) | R(XMM5) | R(XMM6) |
      R(XMM7);
  static constexpr intptr_t kNumFpuArgRegs = 8;

  // Whether ArgumentRegisters[i] prevents using XmmArgumentRegisters[i] at the
  // same time and vice versa.
  static constexpr bool kArgumentIntRegXorFpuReg = false;

  // > For calls that may call functions that use varargs or stdargs
  // > (prototype-less calls or calls to functions containing ellipsis (...) in
  // > the declaration) %al16 is used as hidden argument to specify the number
  // > of vector registers used. The contents of %al do not need to match
  // > exactly the number of registers, but must be an upper bound on the number
  // > of vector registers used and is in the range 0–8 inclusive.
  // System V ABI spec.
  static constexpr Register kVarArgFpuRegisterCount = RAX;

  static constexpr intptr_t kShadowSpaceBytes = 0;

  static constexpr intptr_t kVolatileCpuRegisters = R(RAX) | R(RCX) | R(RDX) |
                                                    R(RSI) | R(RDI) | R(R8) |
                                                    R(R9) | R(R10) | R(R11);

  static constexpr RegList kVolatileXmmRegisters = kAbiVolatileFpuRegs;

  static constexpr intptr_t kCalleeSaveXmmRegisters = 0;

  static constexpr XmmRegister xmmFirstNonParameterReg = XMM8;

  static constexpr Register kReturnReg = RAX;
  static constexpr Register kSecondReturnReg = RDX;
  static constexpr FpuRegister kReturnFpuReg = XMM0;
  static constexpr FpuRegister kSecondReturnFpuReg = XMM1;
  static constexpr Register kPointerToReturnStructRegisterReturn = kReturnReg;

  // Whether larger than wordsize arguments are aligned to even registers.
  static constexpr AlignmentStrategy kArgumentRegisterAlignment =
      kAlignedToWordSize;
  static constexpr AlignmentStrategy kArgumentRegisterAlignmentVarArgs =
      kArgumentRegisterAlignment;

  // How stack arguments are aligned.
  static constexpr AlignmentStrategy kArgumentStackAlignment =
      kAlignedToWordSize;
  static constexpr AlignmentStrategy kArgumentStackAlignmentVarArgs =
      kArgumentStackAlignment;

  // How fields in compounds are aligned.
  static constexpr AlignmentStrategy kFieldAlignment = kAlignedToValueSize;

  // Whether 1 or 2 byte-sized arguments or return values are passed extended
  // to 4 bytes.
  // Note that `kReturnRegisterExtension != kArgumentRegisterExtension`, which
  // effectively means that the caller is responsable for truncating and
  // extending both arguments and return value.
  static constexpr ExtensionStrategy kReturnRegisterExtension = kNotExtended;
  static constexpr ExtensionStrategy kArgumentRegisterExtension = kExtendedTo4;
  static constexpr ExtensionStrategy kArgumentStackExtension = kExtendedTo4;

#endif

  static constexpr intptr_t kCalleeSaveCpuRegisters = kAbiPreservedCpuRegs;

  COMPILE_ASSERT((kArgumentRegisters & kReservedCpuRegisters) == 0);

  static constexpr Register kFfiAnyNonAbiRegister = R12;
  static constexpr Register kFirstNonArgumentRegister = RAX;
  static constexpr Register kSecondNonArgumentRegister = RBX;
  static constexpr Register kStackPointerRegister = SPREG;

  COMPILE_ASSERT(((R(kFfiAnyNonAbiRegister)) & kCalleeSaveCpuRegisters) != 0);

  COMPILE_ASSERT(
      ((R(kFirstNonArgumentRegister) | R(kSecondNonArgumentRegister)) &
       (kArgumentRegisters | R(kPointerToReturnStructRegisterCall))) == 0);
};

// Register based calling convention used for Dart functions.
//
// See |compiler::ComputeCallingConvention| for more details.
struct DartCallingConvention {
  static constexpr Register kCpuRegistersForArgs[] = {RDI, RSI, RDX,
                                                      RBX, R8,  R9};
  static constexpr FpuRegister kFpuRegistersForArgs[] = {XMM1, XMM2, XMM3,
                                                         XMM4, XMM5, XMM6};
};

#undef R

class Instr {
 public:
  static constexpr uint8_t kHltInstruction = 0xF4;
  // We prefer not to use the int3 instruction since it conflicts with gdb.
  static constexpr uint8_t kBreakPointInstruction = kHltInstruction;
  static constexpr int kBreakPointInstructionSize = 1;
  static constexpr uint8_t kGdbBreakpointInstruction = 0xcc;

  bool IsBreakPoint() {
    ASSERT(kBreakPointInstructionSize == 1);
    return (*reinterpret_cast<const uint8_t*>(this)) == kBreakPointInstruction;
  }

  // Instructions are read out of a code stream. The only way to get a
  // reference to an instruction is to convert a pointer. There is no way
  // to allocate or create instances of class Instr.
  // Use the At(pc) function to create references to Instr.
  static Instr* At(uword pc) { return reinterpret_cast<Instr*>(pc); }

 private:
  DISALLOW_ALLOCATION();
  // We need to prevent the creation of instances of class Instr.
  DISALLOW_IMPLICIT_CONSTRUCTORS(Instr);
};

// The largest multibyte nop we will emit.  This could go up to 15 if it
// becomes important to us.
const int MAX_NOP_SIZE = 8;

const uint64_t kBreakInstructionFiller = 0xCCCCCCCCCCCCCCCCL;

// Based on presentation "Causes of Performance Instability due to Code
// Placement in X86 - Zia Ansari, Intel"[1].
//
// [1]: https://www.youtube.com/watch?v=IX16gcX4vDQ
const intptr_t kPreferredLoopAlignment = 32;

}  // namespace dart

#endif  // RUNTIME_VM_CONSTANTS_X64_H_
