Commit caff0ddd authored by Albert Mingkun Yang's avatar Albert Mingkun Yang Committed by Commit Bot

Allow CSA stubs to restrict the set of allocatable registers.

This is useful for the RecordWrite stub that can now specify the set
of allocatable registers in its call descriptor interface. 
During register allocation a custom register configuration is used to
ensure that the register are allocated from the given set.

This makes calling RecordWrite stub less expensive as we need to save/restore
only the allocatable registers instead all registers.

Bug: chromium:749486
Change-Id: If4d73f1fd525e480970ea92600fb811e63677eb5
Reviewed-on: https://chromium-review.googlesource.com/624734Reviewed-by: 's avatarJaroslav Sevcik <jarin@chromium.org>
Reviewed-by: 's avatarUlan Degenbaev <ulan@chromium.org>
Commit-Queue: Albert Mingkun Yang <albertnetymk@google.com>
Cr-Commit-Position: refs/heads/master@{#47577}
parent f71d6a19
......@@ -22,6 +22,13 @@ void CallInterfaceDescriptor::DefaultInitializePlatformSpecific(
default_stub_registers);
}
void RecordWriteDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
// TODO(albertnetymk): Use default for now; should call
// RestrictAllocatableRegisters like src/x64/interface-descriptors-x64.cc
DefaultInitializePlatformSpecific(data, kParameterCount);
}
const Register FastNewFunctionContextDescriptor::FunctionRegister() {
return r1;
}
......
......@@ -22,6 +22,13 @@ void CallInterfaceDescriptor::DefaultInitializePlatformSpecific(
default_stub_registers);
}
void RecordWriteDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
// TODO(albertnetymk): Use default for now; should call
// RestrictAllocatableRegisters like src/x64/interface-descriptors-x64.cc
DefaultInitializePlatformSpecific(data, kParameterCount);
}
const Register FastNewFunctionContextDescriptor::FunctionRegister() {
return x1;
}
......
......@@ -397,7 +397,7 @@ CallDescriptor* Linkage::GetStubCallDescriptor(
kNoCalleeSaved, // callee-saved fp
CallDescriptor::kCanUseRoots | // flags
flags, // flags
descriptor.DebugName(isolate));
descriptor.DebugName(isolate), descriptor.allocatable_registers());
}
// static
......
......@@ -10,6 +10,7 @@
#include "src/compiler/frame.h"
#include "src/compiler/operator.h"
#include "src/globals.h"
#include "src/interface-descriptors.h"
#include "src/machine-type.h"
#include "src/reglist.h"
#include "src/runtime/runtime.h"
......@@ -198,7 +199,8 @@ class V8_EXPORT_PRIVATE CallDescriptor final
Operator::Properties properties,
RegList callee_saved_registers,
RegList callee_saved_fp_registers, Flags flags,
const char* debug_name = "")
const char* debug_name = "",
const RegList allocatable_registers = 0)
: kind_(kind),
target_type_(target_type),
target_loc_(target_loc),
......@@ -207,9 +209,9 @@ class V8_EXPORT_PRIVATE CallDescriptor final
properties_(properties),
callee_saved_registers_(callee_saved_registers),
callee_saved_fp_registers_(callee_saved_fp_registers),
allocatable_registers_(allocatable_registers),
flags_(flags),
debug_name_(debug_name) {
}
debug_name_(debug_name) {}
// Returns the kind of this call.
Kind kind() const { return kind_; }
......@@ -301,6 +303,12 @@ class V8_EXPORT_PRIVATE CallDescriptor final
int CalculateFixedFrameSize() const;
RegList AllocatableRegisters() const { return allocatable_registers_; }
bool HasRestrictedAllocatableRegisters() const {
return allocatable_registers_ != 0;
}
private:
friend class Linkage;
......@@ -312,6 +320,9 @@ class V8_EXPORT_PRIVATE CallDescriptor final
const Operator::Properties properties_;
const RegList callee_saved_registers_;
const RegList callee_saved_fp_registers_;
// Non-zero value means restricting the set of allocatable registers for
// register allocator to use.
const RegList allocatable_registers_;
const Flags flags_;
const char* const debug_name_;
......
......@@ -1974,8 +1974,17 @@ bool PipelineImpl::ScheduleAndSelectInstructions(Linkage* linkage,
bool run_verifier = FLAG_turbo_verify_allocation;
// Allocate registers.
AllocateRegisters(RegisterConfiguration::Default(), call_descriptor,
run_verifier);
if (call_descriptor->HasRestrictedAllocatableRegisters()) {
auto registers = call_descriptor->AllocatableRegisters();
DCHECK(NumRegs(registers) > 0);
std::unique_ptr<const RegisterConfiguration> config;
config.reset(RegisterConfiguration::RestrictGeneralRegisters(registers));
AllocateRegisters(config.get(), call_descriptor, run_verifier);
} else {
AllocateRegisters(RegisterConfiguration::Default(), call_descriptor,
run_verifier);
}
Run<FrameElisionPhase>();
if (data->compilation_failed()) {
info()->AbortOptimization(kNotEnoughVirtualRegistersRegalloc);
......
......@@ -1602,6 +1602,8 @@ InstructionOperand* ConstraintBuilder::AllocateFixed(
operand->fixed_slot_index());
} else if (operand->HasFixedRegisterPolicy()) {
DCHECK(!IsFloatingPoint(rep));
DCHECK(data()->config()->IsAllocatableGeneralCode(
operand->fixed_register_index()));
allocated = AllocatedOperand(AllocatedOperand::REGISTER, rep,
operand->fixed_register_index());
} else if (operand->HasFixedFPRegisterPolicy()) {
......
......@@ -242,6 +242,24 @@ class OutOfLineRecordWrite final : public OutOfLineCode {
mode_(mode),
zone_(gen->zone()) {}
void SaveRegisters(RegList registers) {
DCHECK(NumRegs(registers) > 0);
for (int i = 0; i < Register::kNumRegisters; ++i) {
if ((registers >> i) & 1u) {
__ pushq(Register::from_code(i));
}
}
}
void RestoreRegisters(RegList registers) {
DCHECK(NumRegs(registers) > 0);
for (int i = Register::kNumRegisters - 1; i >= 0; --i) {
if ((registers >> i) & 1u) {
__ popq(Register::from_code(i));
}
}
}
void Generate() final {
if (mode_ > RecordWriteMode::kValueIsPointer) {
__ JumpIfSmi(value_, exit());
......@@ -249,20 +267,15 @@ class OutOfLineRecordWrite final : public OutOfLineCode {
__ CheckPageFlag(value_, scratch0_,
MemoryChunk::kPointersToHereAreInterestingMask, zero,
exit());
RememberedSetAction const remembered_set_action =
mode_ > RecordWriteMode::kValueIsMap ? EMIT_REMEMBERED_SET
: OMIT_REMEMBERED_SET;
SaveFPRegsMode const save_fp_mode =
frame()->DidAllocateDoubleRegisters() ? kSaveFPRegs : kDontSaveFPRegs;
__ leap(scratch1_, operand_);
#ifdef V8_CSA_WRITE_BARRIER
(void)remembered_set_action;
// TODO(albertnetymk): Come up with a better way instead of blindly saving
// all registers.
__ PushCallerSaved(save_fp_mode);
Callable const callable =
Builtins::CallableFor(__ isolate(), Builtins::kRecordWrite);
RegList registers = callable.descriptor().allocatable_registers();
SaveRegisters(registers);
Register object_parameter(callable.descriptor().GetRegisterParameter(
RecordWriteDescriptor::kObject));
Register slot_parameter(callable.descriptor().GetRegisterParameter(
......@@ -280,8 +293,14 @@ class OutOfLineRecordWrite final : public OutOfLineCode {
ExternalReference::isolate_address(__ isolate()));
__ Call(callable.code(), RelocInfo::CODE_TARGET);
__ PopCallerSaved(save_fp_mode);
RestoreRegisters(registers);
#else
RememberedSetAction const remembered_set_action =
mode_ > RecordWriteMode::kValueIsMap ? EMIT_REMEMBERED_SET
: OMIT_REMEMBERED_SET;
SaveFPRegsMode const save_fp_mode =
frame()->DidAllocateDoubleRegisters() ? kSaveFPRegs : kDontSaveFPRegs;
__ CallStubDelayed(
new (zone_) RecordWriteStub(nullptr, object_, scratch0_, scratch1_,
remembered_set_action, save_fp_mode));
......
......@@ -20,6 +20,13 @@ void CallInterfaceDescriptor::DefaultInitializePlatformSpecific(
default_stub_registers);
}
void RecordWriteDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
// TODO(albertnetymk): Use default for now; should call
// RestrictAllocatableRegisters like src/x64/interface-descriptors-x64.cc
DefaultInitializePlatformSpecific(data, kParameterCount);
}
const Register FastNewFunctionContextDescriptor::FunctionRegister() {
return edi;
}
......
......@@ -107,11 +107,6 @@ void RecordWriteDescriptor::InitializePlatformIndependent(
machine_types);
}
void RecordWriteDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
DefaultInitializePlatformSpecific(data, kParameterCount);
}
void LoadDescriptor::InitializePlatformIndependent(
CallInterfaceDescriptorData* data) {
// kReceiver, kName, kSlot
......
......@@ -87,7 +87,10 @@ class PlatformInterfaceDescriptor;
class V8_EXPORT_PRIVATE CallInterfaceDescriptorData {
public:
CallInterfaceDescriptorData() : register_param_count_(-1), param_count_(-1) {}
CallInterfaceDescriptorData()
: register_param_count_(-1),
param_count_(-1),
allocatable_registers_(0) {}
// A copy of the passed in registers and param_representations is made
// and owned by the CallInterfaceDescriptorData.
......@@ -120,10 +123,24 @@ class V8_EXPORT_PRIVATE CallInterfaceDescriptorData {
return platform_specific_descriptor_;
}
void RestrictAllocatableRegisters(const Register* registers, int num) {
DCHECK(allocatable_registers_ == 0);
for (int i = 0; i < num; ++i) {
allocatable_registers_ |= registers[i].bit();
}
DCHECK(NumRegs(allocatable_registers_) > 0);
}
RegList allocatable_registers() const { return allocatable_registers_; }
private:
int register_param_count_;
int param_count_;
// Specifying the set of registers that could be used by the register
// allocator. Currently, it's only used by RecordWrite code stub.
RegList allocatable_registers_;
// The Register params are allocated dynamically by the
// InterfaceDescriptor, and freed on destruction. This is because static
// arrays of Registers cause creation of runtime static initializers
......@@ -179,6 +196,10 @@ class V8_EXPORT_PRIVATE CallInterfaceDescriptor {
return data()->platform_specific_descriptor();
}
RegList allocatable_registers() const {
return data()->allocatable_registers();
}
static const Register ContextRegister();
const char* DebugName(Isolate* isolate) const;
......
......@@ -20,6 +20,13 @@ void CallInterfaceDescriptor::DefaultInitializePlatformSpecific(
default_stub_registers);
}
void RecordWriteDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
// TODO(albertnetymk): Use default for now; should call
// RestrictAllocatableRegisters like src/x64/interface-descriptors-x64.cc
DefaultInitializePlatformSpecific(data, kParameterCount);
}
const Register FastNewFunctionContextDescriptor::FunctionRegister() {
return a1;
}
......
......@@ -20,6 +20,13 @@ void CallInterfaceDescriptor::DefaultInitializePlatformSpecific(
default_stub_registers);
}
void RecordWriteDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
// TODO(albertnetymk): Use default for now; should call
// RestrictAllocatableRegisters like src/x64/interface-descriptors-x64.cc
DefaultInitializePlatformSpecific(data, kParameterCount);
}
const Register FastNewFunctionContextDescriptor::FunctionRegister() {
return a1;
}
......
......@@ -20,6 +20,13 @@ void CallInterfaceDescriptor::DefaultInitializePlatformSpecific(
default_stub_registers);
}
void RecordWriteDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
// TODO(albertnetymk): Use default for now; should call
// RestrictAllocatableRegisters like src/x64/interface-descriptors-x64.cc
DefaultInitializePlatformSpecific(data, kParameterCount);
}
const Register FastNewFunctionContextDescriptor::FunctionRegister() {
return r4;
}
......
......@@ -64,52 +64,75 @@ STATIC_ASSERT(RegisterConfiguration::kMaxFPRegisters >=
STATIC_ASSERT(RegisterConfiguration::kMaxFPRegisters >=
Simd128Register::kMaxNumRegisters);
class ArchDefaultRegisterConfiguration : public RegisterConfiguration {
public:
ArchDefaultRegisterConfiguration()
: RegisterConfiguration(
Register::kNumRegisters, DoubleRegister::kMaxNumRegisters,
static int get_num_allocatable_general_registers() {
return
#if V8_TARGET_ARCH_IA32
kMaxAllocatableGeneralRegisterCount,
kMaxAllocatableDoubleRegisterCount,
kMaxAllocatableGeneralRegisterCount;
#elif V8_TARGET_ARCH_X64
kMaxAllocatableGeneralRegisterCount,
kMaxAllocatableDoubleRegisterCount,
kMaxAllocatableGeneralRegisterCount;
#elif V8_TARGET_ARCH_ARM
kMaxAllocatableGeneralRegisterCount,
CpuFeatures::IsSupported(VFP32DREGS)
? kMaxAllocatableDoubleRegisterCount
: (ALLOCATABLE_NO_VFP32_DOUBLE_REGISTERS(REGISTER_COUNT) 0),
kMaxAllocatableGeneralRegisterCount;
#elif V8_TARGET_ARCH_ARM64
kMaxAllocatableGeneralRegisterCount,
kMaxAllocatableDoubleRegisterCount,
kMaxAllocatableGeneralRegisterCount;
#elif V8_TARGET_ARCH_MIPS
kMaxAllocatableGeneralRegisterCount,
kMaxAllocatableDoubleRegisterCount,
kMaxAllocatableGeneralRegisterCount;
#elif V8_TARGET_ARCH_MIPS64
kMaxAllocatableGeneralRegisterCount,
kMaxAllocatableDoubleRegisterCount,
kMaxAllocatableGeneralRegisterCount;
#elif V8_TARGET_ARCH_PPC
kMaxAllocatableGeneralRegisterCount,
kMaxAllocatableDoubleRegisterCount,
kMaxAllocatableGeneralRegisterCount;
#elif V8_TARGET_ARCH_S390
kMaxAllocatableGeneralRegisterCount,
kMaxAllocatableDoubleRegisterCount,
kMaxAllocatableGeneralRegisterCount;
#else
#error Unsupported target architecture.
#endif
kAllocatableGeneralCodes,
}
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_S390
kMaxAllocatableDoubleRegisterCount;
#else
#error Unsupported target architecture.
#endif
}
static const int* get_allocatable_double_codes() {
return
#if V8_TARGET_ARCH_ARM
CpuFeatures::IsSupported(VFP32DREGS)
? kAllocatableDoubleCodes
: kAllocatableNoVFP32DoubleCodes,
CpuFeatures::IsSupported(VFP32DREGS) ? kAllocatableDoubleCodes
: kAllocatableNoVFP32DoubleCodes;
#else
kAllocatableDoubleCodes,
kAllocatableDoubleCodes;
#endif
}
class ArchDefaultRegisterConfiguration : public RegisterConfiguration {
public:
ArchDefaultRegisterConfiguration()
: RegisterConfiguration(
Register::kNumRegisters, DoubleRegister::kMaxNumRegisters,
get_num_allocatable_general_registers(),
get_num_allocatable_double_registers(), kAllocatableGeneralCodes,
get_allocatable_double_codes(),
kSimpleFPAliasing ? AliasingKind::OVERLAP : AliasingKind::COMBINE,
kGeneralRegisterNames, kFloatRegisterNames, kDoubleRegisterNames,
kSimd128RegisterNames) {
}
kSimd128RegisterNames) {}
};
struct RegisterConfigurationInitializer {
......@@ -122,12 +145,74 @@ static base::LazyInstance<ArchDefaultRegisterConfiguration,
RegisterConfigurationInitializer>::type
kDefaultRegisterConfiguration = LAZY_INSTANCE_INITIALIZER;
// 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::kMaxNumRegisters,
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_names.get(), kFloatRegisterNames,
kDoubleRegisterNames, kSimd128RegisterNames),
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 &kDefaultRegisterConfiguration.Get();
}
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] = Default()->GetGeneralRegisterName(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,
......
......@@ -8,6 +8,7 @@
#include "src/base/macros.h"
#include "src/globals.h"
#include "src/machine-type.h"
#include "src/reglist.h"
namespace v8 {
namespace internal {
......@@ -30,6 +31,9 @@ class V8_EXPORT_PRIVATE RegisterConfiguration {
// Default RegisterConfigurations for the target architecture.
static const RegisterConfiguration* Default();
static const RegisterConfiguration* RestrictGeneralRegisters(
RegList registers);
RegisterConfiguration(int num_general_registers, int num_double_registers,
int num_allocatable_general_registers,
int num_allocatable_double_registers,
......@@ -132,6 +136,8 @@ class V8_EXPORT_PRIVATE RegisterConfiguration {
bool AreAliases(MachineRepresentation rep, int index,
MachineRepresentation other_rep, int other_index) const;
virtual ~RegisterConfiguration() {}
private:
const int num_general_registers_;
int num_float_registers_;
......
......@@ -20,6 +20,13 @@ void CallInterfaceDescriptor::DefaultInitializePlatformSpecific(
default_stub_registers);
}
void RecordWriteDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
// TODO(albertnetymk): Use default for now; should call
// RestrictAllocatableRegisters like src/x64/interface-descriptors-x64.cc
DefaultInitializePlatformSpecific(data, kParameterCount);
}
const Register FastNewFunctionContextDescriptor::FunctionRegister() {
return r3;
}
......
......@@ -20,6 +20,19 @@ void CallInterfaceDescriptor::DefaultInitializePlatformSpecific(
default_stub_registers);
}
void RecordWriteDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
const Register default_stub_registers[] = {arg_reg_1, arg_reg_2, arg_reg_3,
arg_reg_4, kReturnRegister0};
data->RestrictAllocatableRegisters(default_stub_registers,
arraysize(default_stub_registers));
CHECK_LE(static_cast<size_t>(kParameterCount),
arraysize(default_stub_registers));
data->InitializePlatformSpecific(kParameterCount, default_stub_registers);
}
const Register FastNewFunctionContextDescriptor::FunctionRegister() {
return rdi;
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment