// Copyright 2014 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "src/register-configuration.h"
#include "src/globals.h"
#include "src/macro-assembler.h"

namespace v8 {
namespace internal {

namespace {

#define REGISTER_COUNT(R) 1 +
static const int kMaxAllocatableGeneralRegisterCount =
    ALLOCATABLE_GENERAL_REGISTERS(REGISTER_COUNT)0;
static const int kMaxAllocatableDoubleRegisterCount =
    ALLOCATABLE_DOUBLE_REGISTERS(REGISTER_COUNT)0;

static const int kAllocatableGeneralCodes[] = {
#define REGISTER_CODE(R) Register::kCode_##R,
    ALLOCATABLE_GENERAL_REGISTERS(REGISTER_CODE)};
#undef REGISTER_CODE

static const int kAllocatableDoubleCodes[] = {
#define REGISTER_CODE(R) DoubleRegister::kCode_##R,
    ALLOCATABLE_DOUBLE_REGISTERS(REGISTER_CODE)};
#undef REGISTER_CODE

static const char* const kGeneralRegisterNames[] = {
#define REGISTER_NAME(R) #R,
    GENERAL_REGISTERS(REGISTER_NAME)
#undef REGISTER_NAME
};

static const char* const kDoubleRegisterNames[] = {
#define REGISTER_NAME(R) #R,
    DOUBLE_REGISTERS(REGISTER_NAME)
#undef REGISTER_NAME
};

STATIC_ASSERT(RegisterConfiguration::kMaxGeneralRegisters >=
              Register::kNumRegisters);
STATIC_ASSERT(RegisterConfiguration::kMaxDoubleRegisters >=
              DoubleRegister::kMaxNumRegisters);

class ArchDefaultRegisterConfiguration : public RegisterConfiguration {
 public:
  explicit ArchDefaultRegisterConfiguration(CompilerSelector compiler)
      : RegisterConfiguration(Register::kNumRegisters,
                              DoubleRegister::kMaxNumRegisters,
#if V8_TARGET_ARCH_IA32
                              kMaxAllocatableGeneralRegisterCount,
                              kMaxAllocatableDoubleRegisterCount,
                              kMaxAllocatableDoubleRegisterCount,
#elif V8_TARGET_ARCH_X87
                              kMaxAllocatableGeneralRegisterCount,
                              compiler == TURBOFAN
                                  ? 1
                                  : kMaxAllocatableDoubleRegisterCount,
                              compiler == TURBOFAN
                                  ? 1
                                  : kMaxAllocatableDoubleRegisterCount,
#elif V8_TARGET_ARCH_X64
                              kMaxAllocatableGeneralRegisterCount,
                              kMaxAllocatableDoubleRegisterCount,
                              kMaxAllocatableDoubleRegisterCount,
#elif V8_TARGET_ARCH_ARM
                              FLAG_enable_embedded_constant_pool
                                  ? (kMaxAllocatableGeneralRegisterCount - 1)
                                  : kMaxAllocatableGeneralRegisterCount,
                              CpuFeatures::IsSupported(VFP32DREGS)
                                  ? kMaxAllocatableDoubleRegisterCount
                                  : (ALLOCATABLE_NO_VFP32_DOUBLE_REGISTERS(
                                        REGISTER_COUNT)0),
                              ALLOCATABLE_NO_VFP32_DOUBLE_REGISTERS(
                                  REGISTER_COUNT)0,
#elif V8_TARGET_ARCH_ARM64
                              kMaxAllocatableGeneralRegisterCount,
                              kMaxAllocatableDoubleRegisterCount,
                              kMaxAllocatableDoubleRegisterCount,
#elif V8_TARGET_ARCH_MIPS
                              kMaxAllocatableGeneralRegisterCount,
                              kMaxAllocatableDoubleRegisterCount,
                              kMaxAllocatableDoubleRegisterCount,
#elif V8_TARGET_ARCH_MIPS64
                              kMaxAllocatableGeneralRegisterCount,
                              kMaxAllocatableDoubleRegisterCount,
                              kMaxAllocatableDoubleRegisterCount,
#elif V8_TARGET_ARCH_PPC
                              kMaxAllocatableGeneralRegisterCount,
                              kMaxAllocatableDoubleRegisterCount,
                              kMaxAllocatableDoubleRegisterCount,
#elif V8_TARGET_ARCH_S390
                              kMaxAllocatableGeneralRegisterCount,
                              kMaxAllocatableDoubleRegisterCount,
                              kMaxAllocatableDoubleRegisterCount,
#else
#error Unsupported target architecture.
#endif
                              kAllocatableGeneralCodes, kAllocatableDoubleCodes,
                              kGeneralRegisterNames, kDoubleRegisterNames) {
  }
};


template <RegisterConfiguration::CompilerSelector compiler>
struct RegisterConfigurationInitializer {
  static void Construct(ArchDefaultRegisterConfiguration* config) {
    new (config) ArchDefaultRegisterConfiguration(compiler);
  }
};

static base::LazyInstance<
    ArchDefaultRegisterConfiguration,
    RegisterConfigurationInitializer<RegisterConfiguration::CRANKSHAFT>>::type
    kDefaultRegisterConfigurationForCrankshaft = LAZY_INSTANCE_INITIALIZER;


static base::LazyInstance<
    ArchDefaultRegisterConfiguration,
    RegisterConfigurationInitializer<RegisterConfiguration::TURBOFAN>>::type
    kDefaultRegisterConfigurationForTurboFan = LAZY_INSTANCE_INITIALIZER;

}  // namespace


const RegisterConfiguration* RegisterConfiguration::ArchDefault(
    CompilerSelector compiler) {
  return compiler == TURBOFAN
             ? &kDefaultRegisterConfigurationForTurboFan.Get()
             : &kDefaultRegisterConfigurationForCrankshaft.Get();
}


RegisterConfiguration::RegisterConfiguration(
    int num_general_registers, int num_double_registers,
    int num_allocatable_general_registers, int num_allocatable_double_registers,
    int num_allocatable_aliased_double_registers,
    const int* allocatable_general_codes, const int* allocatable_double_codes,
    const char* const* general_register_names,
    const char* const* double_register_names)
    : num_general_registers_(num_general_registers),
      num_double_registers_(num_double_registers),
      num_allocatable_general_registers_(num_allocatable_general_registers),
      num_allocatable_double_registers_(num_allocatable_double_registers),
      num_allocatable_aliased_double_registers_(
          num_allocatable_aliased_double_registers),
      allocatable_general_codes_mask_(0),
      allocatable_double_codes_mask_(0),
      allocatable_general_codes_(allocatable_general_codes),
      allocatable_double_codes_(allocatable_double_codes),
      general_register_names_(general_register_names),
      double_register_names_(double_register_names) {
  for (int i = 0; i < num_allocatable_general_registers_; ++i) {
    allocatable_general_codes_mask_ |= (1 << allocatable_general_codes_[i]);
  }
  for (int i = 0; i < num_allocatable_double_registers_; ++i) {
    allocatable_double_codes_mask_ |= (1 << allocatable_double_codes_[i]);
  }
}

#undef REGISTER_COUNT

}  // namespace internal
}  // namespace v8
