| // 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/codegen/register-configuration.h" |
| #include "src/base/lazy-instance.h" |
| #include "src/codegen/cpu-features.h" |
| #include "src/codegen/register-arch.h" |
| #include "src/common/globals.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) kRegCode_##R, |
| ALLOCATABLE_GENERAL_REGISTERS(REGISTER_CODE)}; |
| #undef REGISTER_CODE |
| |
| #define REGISTER_CODE(R) kDoubleCode_##R, |
| static const int kAllocatableDoubleCodes[] = { |
| ALLOCATABLE_DOUBLE_REGISTERS(REGISTER_CODE)}; |
| #if V8_TARGET_ARCH_ARM |
| static const int kAllocatableNoVFP32DoubleCodes[] = { |
| ALLOCATABLE_NO_VFP32_DOUBLE_REGISTERS(REGISTER_CODE)}; |
| #endif // V8_TARGET_ARCH_ARM |
| #undef REGISTER_CODE |
| |
| STATIC_ASSERT(RegisterConfiguration::kMaxGeneralRegisters >= |
| Register::kNumRegisters); |
| STATIC_ASSERT(RegisterConfiguration::kMaxFPRegisters >= |
| FloatRegister::kNumRegisters); |
| STATIC_ASSERT(RegisterConfiguration::kMaxFPRegisters >= |
| DoubleRegister::kNumRegisters); |
| STATIC_ASSERT(RegisterConfiguration::kMaxFPRegisters >= |
| Simd128Register::kNumRegisters); |
| |
| // Callers on architectures other than Arm expect this to be be constant |
| // between build and runtime. Avoid adding variability on other platforms. |
| static int get_num_allocatable_double_registers() { |
| return |
| #if V8_TARGET_ARCH_IA32 |
| kMaxAllocatableDoubleRegisterCount; |
| #elif V8_TARGET_ARCH_X64 |
| kMaxAllocatableDoubleRegisterCount; |
| #elif V8_TARGET_ARCH_ARM |
| CpuFeatures::IsSupported(VFP32DREGS) |
| ? kMaxAllocatableDoubleRegisterCount |
| : (ALLOCATABLE_NO_VFP32_DOUBLE_REGISTERS(REGISTER_COUNT) 0); |
| #elif V8_TARGET_ARCH_ARM64 |
| kMaxAllocatableDoubleRegisterCount; |
| #elif V8_TARGET_ARCH_MIPS |
| kMaxAllocatableDoubleRegisterCount; |
| #elif V8_TARGET_ARCH_MIPS64 |
| kMaxAllocatableDoubleRegisterCount; |
| #elif V8_TARGET_ARCH_PPC |
| kMaxAllocatableDoubleRegisterCount; |
| #elif V8_TARGET_ARCH_PPC64 |
| kMaxAllocatableDoubleRegisterCount; |
| #elif V8_TARGET_ARCH_S390 |
| kMaxAllocatableDoubleRegisterCount; |
| #else |
| #error Unsupported target architecture. |
| #endif |
| } |
| |
| #undef REGISTER_COUNT |
| |
| // Callers on architectures other than Arm expect this to be be constant |
| // between build and runtime. Avoid adding variability on other platforms. |
| static const int* get_allocatable_double_codes() { |
| return |
| #if V8_TARGET_ARCH_ARM |
| CpuFeatures::IsSupported(VFP32DREGS) ? kAllocatableDoubleCodes |
| : kAllocatableNoVFP32DoubleCodes; |
| #else |
| kAllocatableDoubleCodes; |
| #endif |
| } |
| |
| class ArchDefaultRegisterConfiguration : public RegisterConfiguration { |
| public: |
| ArchDefaultRegisterConfiguration() |
| : RegisterConfiguration( |
| Register::kNumRegisters, DoubleRegister::kNumRegisters, |
| kMaxAllocatableGeneralRegisterCount, |
| get_num_allocatable_double_registers(), kAllocatableGeneralCodes, |
| get_allocatable_double_codes(), |
| kSimpleFPAliasing ? AliasingKind::OVERLAP : AliasingKind::COMBINE) { |
| } |
| }; |
| |
| DEFINE_LAZY_LEAKY_OBJECT_GETTER(ArchDefaultRegisterConfiguration, |
| GetDefaultRegisterConfiguration) |
| |
| // Allocatable registers with the masking register removed. |
| class ArchDefaultPoisoningRegisterConfiguration : public RegisterConfiguration { |
| public: |
| ArchDefaultPoisoningRegisterConfiguration() |
| : RegisterConfiguration( |
| Register::kNumRegisters, DoubleRegister::kNumRegisters, |
| kMaxAllocatableGeneralRegisterCount - 1, |
| get_num_allocatable_double_registers(), |
| InitializeGeneralRegisterCodes(), get_allocatable_double_codes(), |
| kSimpleFPAliasing ? AliasingKind::OVERLAP : AliasingKind::COMBINE) { |
| } |
| |
| private: |
| static const int* InitializeGeneralRegisterCodes() { |
| int filtered_index = 0; |
| for (int i = 0; i < kMaxAllocatableGeneralRegisterCount; ++i) { |
| if (kAllocatableGeneralCodes[i] != kSpeculationPoisonRegister.code()) { |
| allocatable_general_codes_[filtered_index] = |
| kAllocatableGeneralCodes[i]; |
| filtered_index++; |
| } |
| } |
| DCHECK_EQ(filtered_index, kMaxAllocatableGeneralRegisterCount - 1); |
| return allocatable_general_codes_; |
| } |
| |
| static int |
| allocatable_general_codes_[kMaxAllocatableGeneralRegisterCount - 1]; |
| }; |
| |
| int ArchDefaultPoisoningRegisterConfiguration::allocatable_general_codes_ |
| [kMaxAllocatableGeneralRegisterCount - 1]; |
| |
| DEFINE_LAZY_LEAKY_OBJECT_GETTER(ArchDefaultPoisoningRegisterConfiguration, |
| GetDefaultPoisoningRegisterConfiguration) |
| |
| // RestrictedRegisterConfiguration uses the subset of allocatable general |
| // registers the architecture support, which results into generating assembly |
| // to use less registers. Currently, it's only used by RecordWrite code stub. |
| class RestrictedRegisterConfiguration : public RegisterConfiguration { |
| public: |
| RestrictedRegisterConfiguration( |
| int num_allocatable_general_registers, |
| std::unique_ptr<int[]> allocatable_general_register_codes, |
| std::unique_ptr<char const*[]> allocatable_general_register_names) |
| : RegisterConfiguration( |
| Register::kNumRegisters, DoubleRegister::kNumRegisters, |
| num_allocatable_general_registers, |
| get_num_allocatable_double_registers(), |
| allocatable_general_register_codes.get(), |
| get_allocatable_double_codes(), |
| kSimpleFPAliasing ? AliasingKind::OVERLAP : AliasingKind::COMBINE), |
| allocatable_general_register_codes_( |
| std::move(allocatable_general_register_codes)), |
| allocatable_general_register_names_( |
| std::move(allocatable_general_register_names)) { |
| for (int i = 0; i < num_allocatable_general_registers; ++i) { |
| DCHECK( |
| IsAllocatableGeneralRegister(allocatable_general_register_codes_[i])); |
| } |
| } |
| |
| bool IsAllocatableGeneralRegister(int code) { |
| for (int i = 0; i < kMaxAllocatableGeneralRegisterCount; ++i) { |
| if (code == kAllocatableGeneralCodes[i]) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| private: |
| std::unique_ptr<int[]> allocatable_general_register_codes_; |
| std::unique_ptr<char const*[]> allocatable_general_register_names_; |
| }; |
| |
| } // namespace |
| |
| const RegisterConfiguration* RegisterConfiguration::Default() { |
| return GetDefaultRegisterConfiguration(); |
| } |
| |
| const RegisterConfiguration* RegisterConfiguration::Poisoning() { |
| return GetDefaultPoisoningRegisterConfiguration(); |
| } |
| |
| const RegisterConfiguration* RegisterConfiguration::RestrictGeneralRegisters( |
| RegList registers) { |
| int num = NumRegs(registers); |
| std::unique_ptr<int[]> codes{new int[num]}; |
| std::unique_ptr<char const* []> names { new char const*[num] }; |
| int counter = 0; |
| for (int i = 0; i < Default()->num_allocatable_general_registers(); ++i) { |
| auto reg = Register::from_code(Default()->GetAllocatableGeneralCode(i)); |
| if (reg.bit() & registers) { |
| DCHECK(counter < num); |
| codes[counter] = reg.code(); |
| names[counter] = RegisterName(Register::from_code(i)); |
| counter++; |
| } |
| } |
| |
| return new RestrictedRegisterConfiguration(num, std::move(codes), |
| std::move(names)); |
| } |
| |
| RegisterConfiguration::RegisterConfiguration( |
| int num_general_registers, int num_double_registers, |
| int num_allocatable_general_registers, int num_allocatable_double_registers, |
| const int* allocatable_general_codes, const int* allocatable_double_codes, |
| AliasingKind fp_aliasing_kind) |
| : num_general_registers_(num_general_registers), |
| num_float_registers_(0), |
| num_double_registers_(num_double_registers), |
| num_simd128_registers_(0), |
| num_allocatable_general_registers_(num_allocatable_general_registers), |
| num_allocatable_float_registers_(0), |
| num_allocatable_double_registers_(num_allocatable_double_registers), |
| num_allocatable_simd128_registers_(0), |
| allocatable_general_codes_mask_(0), |
| allocatable_float_codes_mask_(0), |
| allocatable_double_codes_mask_(0), |
| allocatable_simd128_codes_mask_(0), |
| allocatable_general_codes_(allocatable_general_codes), |
| allocatable_double_codes_(allocatable_double_codes), |
| fp_aliasing_kind_(fp_aliasing_kind) { |
| DCHECK_LE(num_general_registers_, |
| RegisterConfiguration::kMaxGeneralRegisters); |
| DCHECK_LE(num_double_registers_, RegisterConfiguration::kMaxFPRegisters); |
| 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]); |
| } |
| |
| if (fp_aliasing_kind_ == COMBINE) { |
| num_float_registers_ = num_double_registers_ * 2 <= kMaxFPRegisters |
| ? num_double_registers_ * 2 |
| : kMaxFPRegisters; |
| num_allocatable_float_registers_ = 0; |
| for (int i = 0; i < num_allocatable_double_registers_; i++) { |
| int base_code = allocatable_double_codes_[i] * 2; |
| if (base_code >= kMaxFPRegisters) continue; |
| allocatable_float_codes_[num_allocatable_float_registers_++] = base_code; |
| allocatable_float_codes_[num_allocatable_float_registers_++] = |
| base_code + 1; |
| allocatable_float_codes_mask_ |= (0x3 << base_code); |
| } |
| num_simd128_registers_ = num_double_registers_ / 2; |
| num_allocatable_simd128_registers_ = 0; |
| int last_simd128_code = allocatable_double_codes_[0] / 2; |
| for (int i = 1; i < num_allocatable_double_registers_; i++) { |
| int next_simd128_code = allocatable_double_codes_[i] / 2; |
| // This scheme assumes allocatable_double_codes_ are strictly increasing. |
| DCHECK_GE(next_simd128_code, last_simd128_code); |
| if (last_simd128_code == next_simd128_code) { |
| allocatable_simd128_codes_[num_allocatable_simd128_registers_++] = |
| next_simd128_code; |
| allocatable_simd128_codes_mask_ |= (0x1 << next_simd128_code); |
| } |
| last_simd128_code = next_simd128_code; |
| } |
| } else { |
| DCHECK(fp_aliasing_kind_ == OVERLAP); |
| num_float_registers_ = num_simd128_registers_ = num_double_registers_; |
| num_allocatable_float_registers_ = num_allocatable_simd128_registers_ = |
| num_allocatable_double_registers_; |
| for (int i = 0; i < num_allocatable_float_registers_; ++i) { |
| allocatable_float_codes_[i] = allocatable_simd128_codes_[i] = |
| allocatable_double_codes_[i]; |
| } |
| allocatable_float_codes_mask_ = allocatable_simd128_codes_mask_ = |
| allocatable_double_codes_mask_; |
| } |
| } |
| |
| // Assert that kFloat32, kFloat64, and kSimd128 are consecutive values. |
| STATIC_ASSERT(static_cast<int>(MachineRepresentation::kSimd128) == |
| static_cast<int>(MachineRepresentation::kFloat64) + 1); |
| STATIC_ASSERT(static_cast<int>(MachineRepresentation::kFloat64) == |
| static_cast<int>(MachineRepresentation::kFloat32) + 1); |
| |
| int RegisterConfiguration::GetAliases(MachineRepresentation rep, int index, |
| MachineRepresentation other_rep, |
| int* alias_base_index) const { |
| DCHECK(fp_aliasing_kind_ == COMBINE); |
| DCHECK(IsFloatingPoint(rep) && IsFloatingPoint(other_rep)); |
| if (rep == other_rep) { |
| *alias_base_index = index; |
| return 1; |
| } |
| int rep_int = static_cast<int>(rep); |
| int other_rep_int = static_cast<int>(other_rep); |
| if (rep_int > other_rep_int) { |
| int shift = rep_int - other_rep_int; |
| int base_index = index << shift; |
| if (base_index >= kMaxFPRegisters) { |
| // Alias indices would be out of FP register range. |
| return 0; |
| } |
| *alias_base_index = base_index; |
| return 1 << shift; |
| } |
| int shift = other_rep_int - rep_int; |
| *alias_base_index = index >> shift; |
| return 1; |
| } |
| |
| bool RegisterConfiguration::AreAliases(MachineRepresentation rep, int index, |
| MachineRepresentation other_rep, |
| int other_index) const { |
| DCHECK(fp_aliasing_kind_ == COMBINE); |
| DCHECK(IsFloatingPoint(rep) && IsFloatingPoint(other_rep)); |
| if (rep == other_rep) { |
| return index == other_index; |
| } |
| int rep_int = static_cast<int>(rep); |
| int other_rep_int = static_cast<int>(other_rep); |
| if (rep_int > other_rep_int) { |
| int shift = rep_int - other_rep_int; |
| return index == other_index >> shift; |
| } |
| int shift = other_rep_int - rep_int; |
| return index >> shift == other_index; |
| } |
| |
| } // namespace internal |
| } // namespace v8 |