Commit 17915002 authored by Camillo Bruni's avatar Camillo Bruni Committed by V8 LUCI CQ

[builtins][x64] Use callee-saved registers for write barrier stubs

Calls to the record write stub are quite frequent and the caller has to
save all registers used by the builtin.

This CL moves the register saving to the builtin itself, reducing the
call-site code size significantly in many cases and thus improving
compilation speed of sparkplug.

Follow-up CLs with introduce the same behaviour to other platforms.

- CallRecordWriteStubSaveRegisters preserves the existing behaviour and
  saves clobbered registers.
- CallRecordWriteStub expects the registers to match the ones specified
  in the WriteBarrierDescriptor for more compact code.

Bug: v8:11420
Change-Id: Ib1260cf972712bb9ba879beacd34b06a7fa347f1
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2922103Reviewed-by: 's avatarSantiago Aboy Solanes <solanes@chromium.org>
Reviewed-by: 's avatarAndreas Haas <ahaas@chromium.org>
Commit-Queue: Camillo Bruni <cbruni@chromium.org>
Cr-Commit-Position: refs/heads/master@{#74831}
parent 88e5b8f5
...@@ -825,18 +825,21 @@ void Builtins::Generate_ResumeGeneratorTrampoline(MacroAssembler* masm) { ...@@ -825,18 +825,21 @@ void Builtins::Generate_ResumeGeneratorTrampoline(MacroAssembler* masm) {
} }
} }
// TODO(juliana): if we remove the code below then we don't need all
// the parameters.
static void ReplaceClosureCodeWithOptimizedCode(MacroAssembler* masm, static void ReplaceClosureCodeWithOptimizedCode(MacroAssembler* masm,
Register optimized_code, Register optimized_code,
Register closure, Register closure,
Register scratch1, Register scratch1,
Register scratch2) { Register slot_address) {
DCHECK(!AreAliased(optimized_code, closure, scratch1, slot_address));
DCHECK_EQ(closure, kJSFunctionRegister);
// Store the optimized code in the closure. // Store the optimized code in the closure.
__ StoreTaggedField(FieldOperand(closure, JSFunction::kCodeOffset), __ StoreTaggedField(FieldOperand(closure, JSFunction::kCodeOffset),
optimized_code); optimized_code);
__ movq(scratch1, optimized_code); // Write barrier clobbers scratch1 below. // Write barrier clobbers scratch1 below.
__ RecordWriteField(closure, JSFunction::kCodeOffset, scratch1, scratch2, Register value = scratch1;
__ movq(value, optimized_code);
__ RecordWriteField(closure, JSFunction::kCodeOffset, value, slot_address,
SaveFPRegsMode::kIgnore, RememberedSetAction::kOmit, SaveFPRegsMode::kIgnore, RememberedSetAction::kOmit,
SmiCheck::kOmit); SmiCheck::kOmit);
} }
...@@ -923,15 +926,17 @@ static void MaybeOptimizeCode(MacroAssembler* masm, Register feedback_vector, ...@@ -923,15 +926,17 @@ static void MaybeOptimizeCode(MacroAssembler* masm, Register feedback_vector,
static void TailCallOptimizedCodeSlot(MacroAssembler* masm, static void TailCallOptimizedCodeSlot(MacroAssembler* masm,
Register optimized_code_entry, Register optimized_code_entry,
Register scratch1, Register scratch2, Register closure, Register scratch1,
JumpMode jump_mode) { Register scratch2, JumpMode jump_mode) {
// ----------- S t a t e ------------- // ----------- S t a t e -------------
// -- rax : actual argument count // rax : actual argument count
// -- rdx : new target (preserved for callee if needed, and caller) // rdx : new target (preserved for callee if needed, and caller)
// -- rdi : target function (preserved for callee if needed, and caller) // rsi : current context, used for the runtime call
// rdi : target function (preserved for callee if needed, and caller)
// ----------------------------------- // -----------------------------------
DCHECK_EQ(closure, kJSFunctionRegister);
Register closure = rdi; DCHECK(!AreAliased(rax, rdx, closure, rsi, optimized_code_entry, scratch1,
scratch2));
Label heal_optimized_code_slot; Label heal_optimized_code_slot;
...@@ -1065,7 +1070,8 @@ static void LoadOptimizationStateAndJumpIfNeedsProcessing( ...@@ -1065,7 +1070,8 @@ static void LoadOptimizationStateAndJumpIfNeedsProcessing(
static void MaybeOptimizeCodeOrTailCallOptimizedCodeSlot( static void MaybeOptimizeCodeOrTailCallOptimizedCodeSlot(
MacroAssembler* masm, Register optimization_state, Register feedback_vector, MacroAssembler* masm, Register optimization_state, Register feedback_vector,
JumpMode jump_mode = JumpMode::kJump) { Register closure, JumpMode jump_mode = JumpMode::kJump) {
DCHECK(!AreAliased(optimization_state, feedback_vector, closure));
Label maybe_has_optimized_code; Label maybe_has_optimized_code;
__ testl( __ testl(
optimization_state, optimization_state,
...@@ -1081,7 +1087,8 @@ static void MaybeOptimizeCodeOrTailCallOptimizedCodeSlot( ...@@ -1081,7 +1087,8 @@ static void MaybeOptimizeCodeOrTailCallOptimizedCodeSlot(
__ LoadAnyTaggedField( __ LoadAnyTaggedField(
optimized_code_entry, optimized_code_entry,
FieldOperand(feedback_vector, FeedbackVector::kMaybeOptimizedCodeOffset)); FieldOperand(feedback_vector, FeedbackVector::kMaybeOptimizedCodeOffset));
TailCallOptimizedCodeSlot(masm, optimized_code_entry, r8, r15, jump_mode); TailCallOptimizedCodeSlot(masm, optimized_code_entry, closure, r8, r15,
jump_mode);
} }
// Generate code for entering a JS function with the interpreter. // Generate code for entering a JS function with the interpreter.
...@@ -1293,7 +1300,7 @@ void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) { ...@@ -1293,7 +1300,7 @@ void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) {
__ bind(&has_optimized_code_or_marker); __ bind(&has_optimized_code_or_marker);
MaybeOptimizeCodeOrTailCallOptimizedCodeSlot(masm, optimization_state, MaybeOptimizeCodeOrTailCallOptimizedCodeSlot(masm, optimization_state,
feedback_vector); feedback_vector, closure);
__ bind(&is_baseline); __ bind(&is_baseline);
{ {
...@@ -1729,8 +1736,9 @@ void Builtins::Generate_BaselineOutOfLinePrologue(MacroAssembler* masm) { ...@@ -1729,8 +1736,9 @@ void Builtins::Generate_BaselineOutOfLinePrologue(MacroAssembler* masm) {
// return since we may do a runtime call along the way that requires the // return since we may do a runtime call along the way that requires the
// stack to only contain valid frames. // stack to only contain valid frames.
__ Drop(1); __ Drop(1);
MaybeOptimizeCodeOrTailCallOptimizedCodeSlot( MaybeOptimizeCodeOrTailCallOptimizedCodeSlot(masm, optimization_state,
masm, optimization_state, feedback_vector, JumpMode::kPushAndReturn); feedback_vector, closure,
JumpMode::kPushAndReturn);
__ Trap(); __ Trap();
__ RecordComment("]"); __ RecordComment("]");
} }
...@@ -1846,7 +1854,8 @@ void Builtins::Generate_NotifyDeoptimized(MacroAssembler* masm) { ...@@ -1846,7 +1854,8 @@ void Builtins::Generate_NotifyDeoptimized(MacroAssembler* masm) {
void Builtins::Generate_TailCallOptimizedCodeSlot(MacroAssembler* masm) { void Builtins::Generate_TailCallOptimizedCodeSlot(MacroAssembler* masm) {
Register optimized_code_entry = kJavaScriptCallCodeStartRegister; Register optimized_code_entry = kJavaScriptCallCodeStartRegister;
TailCallOptimizedCodeSlot(masm, optimized_code_entry, r8, r15, Register closure = kJSFunctionRegister;
TailCallOptimizedCodeSlot(masm, optimized_code_entry, closure, r8, r15,
JumpMode::kJump); JumpMode::kJump);
} }
...@@ -4506,7 +4515,7 @@ void Builtins::Generate_DynamicCheckMapsTrampoline(MacroAssembler* masm) { ...@@ -4506,7 +4515,7 @@ void Builtins::Generate_DynamicCheckMapsTrampoline(MacroAssembler* masm) {
// FLAG_debug_code is enabled CSA checks will call C function and so we need // FLAG_debug_code is enabled CSA checks will call C function and so we need
// to save all CallerSaved registers too. // to save all CallerSaved registers too.
if (FLAG_debug_code) registers |= kCallerSaved; if (FLAG_debug_code) registers |= kCallerSaved;
__ SaveRegisters(registers); __ MaybeSaveRegisters(registers);
// Load the immediate arguments from the deopt exit to pass to the builtin. // Load the immediate arguments from the deopt exit to pass to the builtin.
Register slot_arg = Register slot_arg =
...@@ -4527,7 +4536,7 @@ void Builtins::Generate_DynamicCheckMapsTrampoline(MacroAssembler* masm) { ...@@ -4527,7 +4536,7 @@ void Builtins::Generate_DynamicCheckMapsTrampoline(MacroAssembler* masm) {
__ cmpq(rax, Immediate(static_cast<int>(DynamicCheckMapsStatus::kSuccess))); __ cmpq(rax, Immediate(static_cast<int>(DynamicCheckMapsStatus::kSuccess)));
__ j(not_equal, &deopt); __ j(not_equal, &deopt);
__ RestoreRegisters(registers); __ MaybeRestoreRegisters(registers);
__ LeaveFrame(StackFrame::INTERNAL); __ LeaveFrame(StackFrame::INTERNAL);
__ Ret(); __ Ret();
...@@ -4539,14 +4548,14 @@ void Builtins::Generate_DynamicCheckMapsTrampoline(MacroAssembler* masm) { ...@@ -4539,14 +4548,14 @@ void Builtins::Generate_DynamicCheckMapsTrampoline(MacroAssembler* masm) {
__ cmpq(rax, Immediate(static_cast<int>(DynamicCheckMapsStatus::kDeopt))); __ cmpq(rax, Immediate(static_cast<int>(DynamicCheckMapsStatus::kDeopt)));
__ Assert(equal, AbortReason::kUnexpectedDynamicCheckMapsStatus); __ Assert(equal, AbortReason::kUnexpectedDynamicCheckMapsStatus);
} }
__ RestoreRegisters(registers); __ MaybeRestoreRegisters(registers);
__ LeaveFrame(StackFrame::INTERNAL); __ LeaveFrame(StackFrame::INTERNAL);
Handle<Code> deopt_eager = masm->isolate()->builtins()->builtin_handle( Handle<Code> deopt_eager = masm->isolate()->builtins()->builtin_handle(
Deoptimizer::GetDeoptimizationEntry(DeoptimizeKind::kEager)); Deoptimizer::GetDeoptimizationEntry(DeoptimizeKind::kEager));
__ Jump(deopt_eager, RelocInfo::CODE_TARGET); __ Jump(deopt_eager, RelocInfo::CODE_TARGET);
__ bind(&bailout); __ bind(&bailout);
__ RestoreRegisters(registers); __ MaybeRestoreRegisters(registers);
__ LeaveFrame(StackFrame::INTERNAL); __ LeaveFrame(StackFrame::INTERNAL);
Handle<Code> deopt_bailout = masm->isolate()->builtins()->builtin_handle( Handle<Code> deopt_bailout = masm->isolate()->builtins()->builtin_handle(
Deoptimizer::GetDeoptimizationEntry(DeoptimizeKind::kBailout)); Deoptimizer::GetDeoptimizationEntry(DeoptimizeKind::kBailout));
......
...@@ -69,6 +69,8 @@ void StaticCallInterfaceDescriptor<DerivedDescriptor>::Initialize( ...@@ -69,6 +69,8 @@ void StaticCallInterfaceDescriptor<DerivedDescriptor>::Initialize(
if (DerivedDescriptor::kRestrictAllocatableRegisters) { if (DerivedDescriptor::kRestrictAllocatableRegisters) {
data->RestrictAllocatableRegisters(registers.data(), registers.size()); data->RestrictAllocatableRegisters(registers.data(), registers.size());
} else {
DCHECK(!DerivedDescriptor::kCalleeSaveRegisters);
} }
data->InitializeRegisters( data->InitializeRegisters(
...@@ -186,6 +188,33 @@ constexpr Register FastNewObjectDescriptor::NewTargetRegister() { ...@@ -186,6 +188,33 @@ constexpr Register FastNewObjectDescriptor::NewTargetRegister() {
return kJavaScriptCallNewTargetRegister; return kJavaScriptCallNewTargetRegister;
} }
// static
constexpr Register WriteBarrierDescriptor::ObjectRegister() {
return std::get<kObject>(registers());
}
// static
constexpr Register WriteBarrierDescriptor::SlotAddressRegister() {
return std::get<kSlotAddress>(registers());
}
// static
constexpr Register WriteBarrierDescriptor::ValueRegister() {
return std::get<kSlotAddress + 1>(registers());
}
// static
constexpr RegList WriteBarrierDescriptor::ComputeSavedRegisters(
Register object, Register slot_address) {
DCHECK(!AreAliased(object, slot_address));
// Only push clobbered registers.
RegList registers = 0;
if (object != ObjectRegister()) registers |= ObjectRegister().bit();
if (slot_address != SlotAddressRegister()) {
registers |= SlotAddressRegister().bit();
}
return registers;
}
// static // static
constexpr Register ApiGetterDescriptor::ReceiverRegister() { constexpr Register ApiGetterDescriptor::ReceiverRegister() {
return LoadDescriptor::ReceiverRegister(); return LoadDescriptor::ReceiverRegister();
......
...@@ -149,6 +149,8 @@ class V8_EXPORT_PRIVATE CallInterfaceDescriptorData { ...@@ -149,6 +149,8 @@ class V8_EXPORT_PRIVATE CallInterfaceDescriptorData {
// passed on the stack. // passed on the stack.
// This does not indicate if arguments adaption is used or not. // This does not indicate if arguments adaption is used or not.
kAllowVarArgs = 1u << 2, kAllowVarArgs = 1u << 2,
// Callee save allocatable_registers.
kCalleeSaveRegisters = 1u << 3,
}; };
using Flags = base::Flags<Flag>; using Flags = base::Flags<Flag>;
...@@ -321,6 +323,10 @@ class V8_EXPORT_PRIVATE CallInterfaceDescriptor { ...@@ -321,6 +323,10 @@ class V8_EXPORT_PRIVATE CallInterfaceDescriptor {
return flags() & CallInterfaceDescriptorData::kAllowVarArgs; return flags() & CallInterfaceDescriptorData::kAllowVarArgs;
} }
bool CalleeSaveRegisters() const {
return flags() & CallInterfaceDescriptorData::kCalleeSaveRegisters;
}
int GetReturnCount() const { return data()->return_count(); } int GetReturnCount() const { return data()->return_count(); }
MachineType GetReturnType(int index) const { MachineType GetReturnType(int index) const {
...@@ -431,6 +437,9 @@ class StaticCallInterfaceDescriptor : public CallInterfaceDescriptor { ...@@ -431,6 +437,9 @@ class StaticCallInterfaceDescriptor : public CallInterfaceDescriptor {
// the first kParameterCount registers() are the parameters of the builtin. // the first kParameterCount registers() are the parameters of the builtin.
static constexpr bool kRestrictAllocatableRegisters = false; static constexpr bool kRestrictAllocatableRegisters = false;
// If set to true, builtins will callee save the set returned by registers().
static constexpr bool kCalleeSaveRegisters = false;
// End of customization points. // End of customization points.
// =========================================================================== // ===========================================================================
...@@ -443,6 +452,9 @@ class StaticCallInterfaceDescriptor : public CallInterfaceDescriptor { ...@@ -443,6 +452,9 @@ class StaticCallInterfaceDescriptor : public CallInterfaceDescriptor {
: 0) | : 0) |
(DerivedDescriptor::kNoStackScan (DerivedDescriptor::kNoStackScan
? CallInterfaceDescriptorData::kNoStackScan ? CallInterfaceDescriptorData::kNoStackScan
: 0) |
(DerivedDescriptor::kCalleeSaveRegisters
? CallInterfaceDescriptorData::kCalleeSaveRegisters
: 0)); : 0));
} }
static constexpr inline bool AllowVarArgs() { static constexpr inline bool AllowVarArgs() {
...@@ -501,7 +513,7 @@ struct EmptyRegisterArray { ...@@ -501,7 +513,7 @@ struct EmptyRegisterArray {
Register operator[](size_t i) const { UNREACHABLE(); } Register operator[](size_t i) const { UNREACHABLE(); }
}; };
// Helper method for defining an array of registers for the various // Helper method for defining an array of unique registers for the various
// Descriptor::registers() methods. // Descriptor::registers() methods.
template <typename... Registers> template <typename... Registers>
constexpr std::array<Register, 1 + sizeof...(Registers)> RegisterArray( constexpr std::array<Register, 1 + sizeof...(Registers)> RegisterArray(
...@@ -1003,6 +1015,16 @@ class WriteBarrierDescriptor final ...@@ -1003,6 +1015,16 @@ class WriteBarrierDescriptor final
DECLARE_DESCRIPTOR(WriteBarrierDescriptor) DECLARE_DESCRIPTOR(WriteBarrierDescriptor)
static constexpr auto registers(); static constexpr auto registers();
static constexpr bool kRestrictAllocatableRegisters = true; static constexpr bool kRestrictAllocatableRegisters = true;
#if V8_TARGET_ARCH_X64
// TODO(cbruni): Extend to all platforms.
static constexpr bool kCalleeSaveRegisters = true;
#endif
static constexpr inline Register ObjectRegister();
static constexpr inline Register SlotAddressRegister();
// A temporary register used in helpers.
static constexpr inline Register ValueRegister();
static constexpr inline RegList ComputeSavedRegisters(Register object,
Register slot_address);
}; };
#ifdef V8_IS_TSAN #ifdef V8_IS_TSAN
......
...@@ -323,10 +323,11 @@ void TurboAssembler::DecompressAnyTagged(Register destination, ...@@ -323,10 +323,11 @@ void TurboAssembler::DecompressAnyTagged(Register destination,
} }
void MacroAssembler::RecordWriteField(Register object, int offset, void MacroAssembler::RecordWriteField(Register object, int offset,
Register value, Register dst, Register value, Register slot_address,
SaveFPRegsMode save_fp, SaveFPRegsMode save_fp,
RememberedSetAction remembered_set_action, RememberedSetAction remembered_set_action,
SmiCheck smi_check) { SmiCheck smi_check) {
DCHECK(!AreAliased(object, value, slot_address));
// First, check if a write barrier is even needed. The tests below // First, check if a write barrier is even needed. The tests below
// catch stores of Smis. // catch stores of Smis.
Label done; Label done;
...@@ -340,16 +341,16 @@ void MacroAssembler::RecordWriteField(Register object, int offset, ...@@ -340,16 +341,16 @@ void MacroAssembler::RecordWriteField(Register object, int offset,
// of the object, so the offset must be a multiple of kTaggedSize. // of the object, so the offset must be a multiple of kTaggedSize.
DCHECK(IsAligned(offset, kTaggedSize)); DCHECK(IsAligned(offset, kTaggedSize));
leaq(dst, FieldOperand(object, offset)); leaq(slot_address, FieldOperand(object, offset));
if (FLAG_debug_code) { if (FLAG_debug_code) {
Label ok; Label ok;
testb(dst, Immediate(kTaggedSize - 1)); testb(slot_address, Immediate(kTaggedSize - 1));
j(zero, &ok, Label::kNear); j(zero, &ok, Label::kNear);
int3(); int3();
bind(&ok); bind(&ok);
} }
RecordWrite(object, dst, value, save_fp, remembered_set_action, RecordWrite(object, slot_address, value, save_fp, remembered_set_action,
SmiCheck::kOmit); SmiCheck::kOmit);
bind(&done); bind(&done);
...@@ -358,22 +359,14 @@ void MacroAssembler::RecordWriteField(Register object, int offset, ...@@ -358,22 +359,14 @@ void MacroAssembler::RecordWriteField(Register object, int offset,
// turned on to provoke errors. // turned on to provoke errors.
if (FLAG_debug_code) { if (FLAG_debug_code) {
Move(value, kZapValue, RelocInfo::NONE); Move(value, kZapValue, RelocInfo::NONE);
Move(dst, kZapValue, RelocInfo::NONE); Move(slot_address, kZapValue, RelocInfo::NONE);
}
}
void TurboAssembler::SaveRegisters(RegList registers) {
DCHECK_GT(NumRegs(registers), 0);
for (int i = 0; i < Register::kNumRegisters; ++i) {
if ((registers >> i) & 1u) {
pushq(Register::from_code(i));
}
} }
} }
void TurboAssembler::LoadExternalPointerField( void TurboAssembler::LoadExternalPointerField(
Register destination, Operand field_operand, ExternalPointerTag tag, Register destination, Operand field_operand, ExternalPointerTag tag,
Register scratch, IsolateRootLocation isolateRootLocation) { Register scratch, IsolateRootLocation isolateRootLocation) {
DCHECK(!AreAliased(destination, scratch));
#ifdef V8_HEAP_SANDBOX #ifdef V8_HEAP_SANDBOX
DCHECK(!field_operand.AddressUsesRegister(scratch)); DCHECK(!field_operand.AddressUsesRegister(scratch));
if (isolateRootLocation == IsolateRootLocation::kInRootRegister) { if (isolateRootLocation == IsolateRootLocation::kInRootRegister) {
...@@ -398,7 +391,18 @@ void TurboAssembler::LoadExternalPointerField( ...@@ -398,7 +391,18 @@ void TurboAssembler::LoadExternalPointerField(
#endif // V8_HEAP_SANDBOX #endif // V8_HEAP_SANDBOX
} }
void TurboAssembler::RestoreRegisters(RegList registers) { void TurboAssembler::MaybeSaveRegisters(RegList registers) {
if (registers == 0) return;
DCHECK_GT(NumRegs(registers), 0);
for (int i = 0; i < Register::kNumRegisters; ++i) {
if ((registers >> i) & 1u) {
pushq(Register::from_code(i));
}
}
}
void TurboAssembler::MaybeRestoreRegisters(RegList registers) {
if (registers == 0) return;
DCHECK_GT(NumRegs(registers), 0); DCHECK_GT(NumRegs(registers), 0);
for (int i = Register::kNumRegisters - 1; i >= 0; --i) { for (int i = Register::kNumRegisters - 1; i >= 0; --i) {
if ((registers >> i) & 1u) { if ((registers >> i) & 1u) {
...@@ -410,42 +414,50 @@ void TurboAssembler::RestoreRegisters(RegList registers) { ...@@ -410,42 +414,50 @@ void TurboAssembler::RestoreRegisters(RegList registers) {
void TurboAssembler::CallEphemeronKeyBarrier(Register object, Register address, void TurboAssembler::CallEphemeronKeyBarrier(Register object, Register address,
SaveFPRegsMode fp_mode) { SaveFPRegsMode fp_mode) {
DCHECK(!AreAliased(object, address)); DCHECK(!AreAliased(object, address));
WriteBarrierDescriptor descriptor; RegList registers =
RegList registers = descriptor.allocatable_registers(); WriteBarrierDescriptor::ComputeSavedRegisters(object, address);
MaybeSaveRegisters(registers);
SaveRegisters(registers);
Register object_parameter( Register object_parameter = WriteBarrierDescriptor::ObjectRegister();
descriptor.GetRegisterParameter(WriteBarrierDescriptor::kObject)); Register slot_parameter = WriteBarrierDescriptor::SlotAddressRegister();
Register slot_parameter(
descriptor.GetRegisterParameter(WriteBarrierDescriptor::kSlotAddress));
MovePair(slot_parameter, address, object_parameter, object); MovePair(slot_parameter, address, object_parameter, object);
Call(isolate()->builtins()->builtin_handle( Call(isolate()->builtins()->builtin_handle(
Builtins::GetEphemeronKeyBarrierStub(fp_mode)), Builtins::GetEphemeronKeyBarrierStub(fp_mode)),
RelocInfo::CODE_TARGET); RelocInfo::CODE_TARGET);
RestoreRegisters(registers); MaybeRestoreRegisters(registers);
} }
void TurboAssembler::CallRecordWriteStub( void TurboAssembler::CallRecordWriteStubSaveRegisters(
Register object, Register address, Register object, Register slot_address,
RememberedSetAction remembered_set_action, SaveFPRegsMode fp_mode, RememberedSetAction remembered_set_action, SaveFPRegsMode fp_mode,
StubCallMode mode) { StubCallMode mode) {
DCHECK(!AreAliased(object, address)); DCHECK(!AreAliased(object, slot_address));
WriteBarrierDescriptor descriptor; RegList registers =
RegList registers = descriptor.allocatable_registers(); WriteBarrierDescriptor::ComputeSavedRegisters(object, slot_address);
SaveRegisters(registers); MaybeSaveRegisters(registers);
Register object_parameter(
descriptor.GetRegisterParameter(WriteBarrierDescriptor::kObject));
Register slot_parameter(
descriptor.GetRegisterParameter(WriteBarrierDescriptor::kSlotAddress));
// Prepare argument registers for calling RecordWrite // Prepare argument registers for calling RecordWrite
// slot_parameter <= address
// object_parameter <= object // object_parameter <= object
MovePair(slot_parameter, address, object_parameter, object); // slot_parameter <= address
Register object_parameter = WriteBarrierDescriptor::ObjectRegister();
Register slot_address_parameter =
WriteBarrierDescriptor::SlotAddressRegister();
MovePair(object_parameter, object, slot_address_parameter, slot_address);
CallRecordWriteStub(object_parameter, slot_address_parameter,
remembered_set_action, fp_mode, mode);
MaybeRestoreRegisters(registers);
}
void TurboAssembler::CallRecordWriteStub(
Register object, Register slot_address,
RememberedSetAction remembered_set_action, SaveFPRegsMode fp_mode,
StubCallMode mode) {
// Use CallRecordWriteStubSaveRegisters if the object and slot registers
// need to be caller saved.
DCHECK_EQ(WriteBarrierDescriptor::ObjectRegister(), object);
DCHECK_EQ(WriteBarrierDescriptor::SlotAddressRegister(), slot_address);
#if V8_ENABLE_WEBASSEMBLY #if V8_ENABLE_WEBASSEMBLY
if (mode == StubCallMode::kCallWasmRuntimeStub) { if (mode == StubCallMode::kCallWasmRuntimeStub) {
// Use {near_call} for direct Wasm call within a module. // Use {near_call} for direct Wasm call within a module.
...@@ -466,8 +478,6 @@ void TurboAssembler::CallRecordWriteStub( ...@@ -466,8 +478,6 @@ void TurboAssembler::CallRecordWriteStub(
Call(code_target, RelocInfo::CODE_TARGET); Call(code_target, RelocInfo::CODE_TARGET);
} }
} }
RestoreRegisters(registers);
} }
#ifdef V8_IS_TSAN #ifdef V8_IS_TSAN
...@@ -505,11 +515,11 @@ void TurboAssembler::CallTSANRelaxedStoreStub(Register address, Register value, ...@@ -505,11 +515,11 @@ void TurboAssembler::CallTSANRelaxedStoreStub(Register address, Register value,
} }
#endif // V8_IS_TSAN #endif // V8_IS_TSAN
void MacroAssembler::RecordWrite(Register object, Register address, void MacroAssembler::RecordWrite(Register object, Register slot_address,
Register value, SaveFPRegsMode fp_mode, Register value, SaveFPRegsMode fp_mode,
RememberedSetAction remembered_set_action, RememberedSetAction remembered_set_action,
SmiCheck smi_check) { SmiCheck smi_check) {
DCHECK(!AreAliased(object, value, address)); DCHECK(!AreAliased(object, slot_address, value));
AssertNotSmi(object); AssertNotSmi(object);
if ((remembered_set_action == RememberedSetAction::kOmit && if ((remembered_set_action == RememberedSetAction::kOmit &&
...@@ -520,7 +530,7 @@ void MacroAssembler::RecordWrite(Register object, Register address, ...@@ -520,7 +530,7 @@ void MacroAssembler::RecordWrite(Register object, Register address,
if (FLAG_debug_code) { if (FLAG_debug_code) {
Label ok; Label ok;
cmp_tagged(value, Operand(address, 0)); cmp_tagged(value, Operand(slot_address, 0));
j(equal, &ok, Label::kNear); j(equal, &ok, Label::kNear);
int3(); int3();
bind(&ok); bind(&ok);
...@@ -545,14 +555,15 @@ void MacroAssembler::RecordWrite(Register object, Register address, ...@@ -545,14 +555,15 @@ void MacroAssembler::RecordWrite(Register object, Register address,
MemoryChunk::kPointersFromHereAreInterestingMask, zero, &done, MemoryChunk::kPointersFromHereAreInterestingMask, zero, &done,
Label::kNear); Label::kNear);
CallRecordWriteStub(object, address, remembered_set_action, fp_mode); CallRecordWriteStubSaveRegisters(object, slot_address, remembered_set_action,
fp_mode);
bind(&done); bind(&done);
// Clobber clobbered registers when running with the debug-code flag // Clobber clobbered registers when running with the debug-code flag
// turned on to provoke errors. // turned on to provoke errors.
if (FLAG_debug_code) { if (FLAG_debug_code) {
Move(address, kZapValue, RelocInfo::NONE); Move(slot_address, kZapValue, RelocInfo::NONE);
Move(value, kZapValue, RelocInfo::NONE); Move(value, kZapValue, RelocInfo::NONE);
} }
} }
......
...@@ -501,12 +501,16 @@ class V8_EXPORT_PRIVATE TurboAssembler : public SharedTurboAssembler { ...@@ -501,12 +501,16 @@ class V8_EXPORT_PRIVATE TurboAssembler : public SharedTurboAssembler {
#endif #endif
} }
void SaveRegisters(RegList registers); void MaybeSaveRegisters(RegList registers);
void RestoreRegisters(RegList registers); void MaybeRestoreRegisters(RegList registers);
void CallEphemeronKeyBarrier(Register object, Register address, void CallEphemeronKeyBarrier(Register object, Register address,
SaveFPRegsMode fp_mode); SaveFPRegsMode fp_mode);
void CallRecordWriteStubSaveRegisters(
Register object, Register address,
RememberedSetAction remembered_set_action, SaveFPRegsMode fp_mode,
StubCallMode mode = StubCallMode::kCallBuiltinPointer);
void CallRecordWriteStub( void CallRecordWriteStub(
Register object, Register address, Register object, Register address,
RememberedSetAction remembered_set_action, SaveFPRegsMode fp_mode, RememberedSetAction remembered_set_action, SaveFPRegsMode fp_mode,
...@@ -674,7 +678,7 @@ class V8_EXPORT_PRIVATE MacroAssembler : public TurboAssembler { ...@@ -674,7 +678,7 @@ class V8_EXPORT_PRIVATE MacroAssembler : public TurboAssembler {
// The offset is the offset from the start of the object, not the offset from // The offset is the offset from the start of the object, not the offset from
// the tagged HeapObject pointer. For use with FieldOperand(reg, off). // the tagged HeapObject pointer. For use with FieldOperand(reg, off).
void RecordWriteField( void RecordWriteField(
Register object, int offset, Register value, Register scratch, Register object, int offset, Register value, Register slot_address,
SaveFPRegsMode save_fp, SaveFPRegsMode save_fp,
RememberedSetAction remembered_set_action = RememberedSetAction::kEmit, RememberedSetAction remembered_set_action = RememberedSetAction::kEmit,
SmiCheck smi_check = SmiCheck::kInline); SmiCheck smi_check = SmiCheck::kInline);
...@@ -685,7 +689,8 @@ class V8_EXPORT_PRIVATE MacroAssembler : public TurboAssembler { ...@@ -685,7 +689,8 @@ class V8_EXPORT_PRIVATE MacroAssembler : public TurboAssembler {
// operation. RecordWrite filters out smis so it does not update // operation. RecordWrite filters out smis so it does not update
// the write barrier if the value is a smi. // the write barrier if the value is a smi.
void RecordWrite( void RecordWrite(
Register object, Register address, Register value, SaveFPRegsMode save_fp, Register object, Register slot_address, Register value,
SaveFPRegsMode save_fp,
RememberedSetAction remembered_set_action = RememberedSetAction::kEmit, RememberedSetAction remembered_set_action = RememberedSetAction::kEmit,
SmiCheck smi_check = SmiCheck::kInline); SmiCheck smi_check = SmiCheck::kInline);
......
...@@ -300,12 +300,13 @@ class OutOfLineRecordWrite final : public OutOfLineCode { ...@@ -300,12 +300,13 @@ class OutOfLineRecordWrite final : public OutOfLineCode {
// A direct call to a wasm runtime stub defined in this module. // A direct call to a wasm runtime stub defined in this module.
// Just encode the stub index. This will be patched when the code // Just encode the stub index. This will be patched when the code
// is added to the native module and copied into wasm code space. // is added to the native module and copied into wasm code space.
__ CallRecordWriteStub(object_, scratch1_, remembered_set_action, __ CallRecordWriteStubSaveRegisters(object_, scratch1_,
save_fp_mode, StubCallMode::kCallWasmRuntimeStub); remembered_set_action, save_fp_mode,
StubCallMode::kCallWasmRuntimeStub);
#endif // V8_ENABLE_WEBASSEMBLY #endif // V8_ENABLE_WEBASSEMBLY
} else { } else {
__ CallRecordWriteStub(object_, scratch1_, remembered_set_action, __ CallRecordWriteStubSaveRegisters(object_, scratch1_,
save_fp_mode); remembered_set_action, save_fp_mode);
} }
} }
...@@ -4670,9 +4671,6 @@ void CodeGenerator::AssembleReturn(InstructionOperand* additional_pop_count) { ...@@ -4670,9 +4671,6 @@ void CodeGenerator::AssembleReturn(InstructionOperand* additional_pop_count) {
unwinding_info_writer_.MarkBlockWillExit(); unwinding_info_writer_.MarkBlockWillExit();
// We might need rcx and r10 for scratch.
DCHECK_EQ(0u, call_descriptor->CalleeSavedRegisters() & rcx.bit());
DCHECK_EQ(0u, call_descriptor->CalleeSavedRegisters() & r10.bit());
X64OperandConverter g(this, nullptr); X64OperandConverter g(this, nullptr);
int parameter_slots = static_cast<int>(call_descriptor->ParameterSlotCount()); int parameter_slots = static_cast<int>(call_descriptor->ParameterSlotCount());
...@@ -4692,9 +4690,9 @@ void CodeGenerator::AssembleReturn(InstructionOperand* additional_pop_count) { ...@@ -4692,9 +4690,9 @@ void CodeGenerator::AssembleReturn(InstructionOperand* additional_pop_count) {
// If {parameter_slots} == 0, it means it is a builtin with // If {parameter_slots} == 0, it means it is a builtin with
// kDontAdaptArgumentsSentinel, which takes care of JS arguments popping // kDontAdaptArgumentsSentinel, which takes care of JS arguments popping
// itself. // itself.
const bool drop_jsargs = frame_access_state()->has_frame() && const bool drop_jsargs = parameter_slots != 0 &&
call_descriptor->IsJSFunctionCall() && frame_access_state()->has_frame() &&
parameter_slots != 0; call_descriptor->IsJSFunctionCall();
if (call_descriptor->IsCFunctionCall()) { if (call_descriptor->IsCFunctionCall()) {
AssembleDeconstructFrame(); AssembleDeconstructFrame();
} else if (frame_access_state()->has_frame()) { } else if (frame_access_state()->has_frame()) {
...@@ -4710,6 +4708,7 @@ void CodeGenerator::AssembleReturn(InstructionOperand* additional_pop_count) { ...@@ -4710,6 +4708,7 @@ void CodeGenerator::AssembleReturn(InstructionOperand* additional_pop_count) {
} }
if (drop_jsargs) { if (drop_jsargs) {
// Get the actual argument count. // Get the actual argument count.
DCHECK_EQ(0u, call_descriptor->CalleeSavedRegisters() & argc_reg.bit());
__ movq(argc_reg, Operand(rbp, StandardFrameConstants::kArgCOffset)); __ movq(argc_reg, Operand(rbp, StandardFrameConstants::kArgCOffset));
} }
AssembleDeconstructFrame(); AssembleDeconstructFrame();
...@@ -4724,6 +4723,8 @@ void CodeGenerator::AssembleReturn(InstructionOperand* additional_pop_count) { ...@@ -4724,6 +4723,8 @@ void CodeGenerator::AssembleReturn(InstructionOperand* additional_pop_count) {
Label mismatch_return; Label mismatch_return;
Register scratch_reg = r10; Register scratch_reg = r10;
DCHECK_NE(argc_reg, scratch_reg); DCHECK_NE(argc_reg, scratch_reg);
DCHECK_EQ(0u, call_descriptor->CalleeSavedRegisters() & scratch_reg.bit());
DCHECK_EQ(0u, call_descriptor->CalleeSavedRegisters() & argc_reg.bit());
__ cmpq(argc_reg, Immediate(parameter_slots_without_receiver)); __ cmpq(argc_reg, Immediate(parameter_slots_without_receiver));
__ j(greater, &mismatch_return, Label::kNear); __ j(greater, &mismatch_return, Label::kNear);
__ Ret(parameter_slots * kSystemPointerSize, scratch_reg); __ Ret(parameter_slots * kSystemPointerSize, scratch_reg);
...@@ -4736,6 +4737,7 @@ void CodeGenerator::AssembleReturn(InstructionOperand* additional_pop_count) { ...@@ -4736,6 +4737,7 @@ void CodeGenerator::AssembleReturn(InstructionOperand* additional_pop_count) {
__ Ret(); __ Ret();
} else if (additional_pop_count->IsImmediate()) { } else if (additional_pop_count->IsImmediate()) {
Register scratch_reg = r10; Register scratch_reg = r10;
DCHECK_EQ(0u, call_descriptor->CalleeSavedRegisters() & scratch_reg.bit());
int additional_count = g.ToConstant(additional_pop_count).ToInt32(); int additional_count = g.ToConstant(additional_pop_count).ToInt32();
size_t pop_size = (parameter_slots + additional_count) * kSystemPointerSize; size_t pop_size = (parameter_slots + additional_count) * kSystemPointerSize;
CHECK_LE(pop_size, static_cast<size_t>(std::numeric_limits<int>::max())); CHECK_LE(pop_size, static_cast<size_t>(std::numeric_limits<int>::max()));
...@@ -4743,6 +4745,8 @@ void CodeGenerator::AssembleReturn(InstructionOperand* additional_pop_count) { ...@@ -4743,6 +4745,8 @@ void CodeGenerator::AssembleReturn(InstructionOperand* additional_pop_count) {
} else { } else {
Register pop_reg = g.ToRegister(additional_pop_count); Register pop_reg = g.ToRegister(additional_pop_count);
Register scratch_reg = pop_reg == r10 ? rcx : r10; Register scratch_reg = pop_reg == r10 ? rcx : r10;
DCHECK_EQ(0u, call_descriptor->CalleeSavedRegisters() & scratch_reg.bit());
DCHECK_EQ(0u, call_descriptor->CalleeSavedRegisters() & pop_reg.bit());
int pop_size = static_cast<int>(parameter_slots * kSystemPointerSize); int pop_size = static_cast<int>(parameter_slots * kSystemPointerSize);
__ PopReturnAddressTo(scratch_reg); __ PopReturnAddressTo(scratch_reg);
__ leaq(rsp, Operand(rsp, pop_reg, times_system_pointer_size, __ leaq(rsp, Operand(rsp, pop_reg, times_system_pointer_size,
......
...@@ -490,6 +490,17 @@ CallDescriptor* Linkage::GetStubCallDescriptor( ...@@ -490,6 +490,17 @@ CallDescriptor* Linkage::GetStubCallDescriptor(
break; break;
} }
RegList allocatable_registers = descriptor.allocatable_registers();
RegList callee_saved_registers = kNoCalleeSaved;
if (descriptor.CalleeSaveRegisters()) {
#if V8_TARGET_ARCH_X64
// TODO(cbruni): Extend to all architectures.
callee_saved_registers = allocatable_registers;
DCHECK(callee_saved_registers);
#else
UNREACHABLE();
#endif
}
LinkageLocation target_loc = LinkageLocation::ForAnyRegister(target_type); LinkageLocation target_loc = LinkageLocation::ForAnyRegister(target_type);
return zone->New<CallDescriptor>( // -- return zone->New<CallDescriptor>( // --
kind, // kind kind, // kind
...@@ -498,12 +509,12 @@ CallDescriptor* Linkage::GetStubCallDescriptor( ...@@ -498,12 +509,12 @@ CallDescriptor* Linkage::GetStubCallDescriptor(
locations.Build(), // location_sig locations.Build(), // location_sig
stack_parameter_count, // stack_parameter_count stack_parameter_count, // stack_parameter_count
properties, // properties properties, // properties
kNoCalleeSaved, // callee-saved registers callee_saved_registers, // callee-saved registers
kNoCalleeSaved, // callee-saved fp kNoCalleeSaved, // callee-saved fp
CallDescriptor::kCanUseRoots | flags, // flags CallDescriptor::kCanUseRoots | flags, // flags
descriptor.DebugName(), // debug name descriptor.DebugName(), // debug name
descriptor.GetStackArgumentOrder(), // stack order descriptor.GetStackArgumentOrder(), // stack order
descriptor.allocatable_registers()); allocatable_registers);
} }
// static // static
......
...@@ -500,7 +500,6 @@ class PipelineData { ...@@ -500,7 +500,6 @@ class PipelineData {
sequence_->instruction_blocks()[0]->mark_needs_frame(); sequence_->instruction_blocks()[0]->mark_needs_frame();
} else { } else {
DCHECK_EQ(0u, call_descriptor->CalleeSavedFPRegisters()); DCHECK_EQ(0u, call_descriptor->CalleeSavedFPRegisters());
DCHECK_EQ(0u, call_descriptor->CalleeSavedRegisters());
} }
} }
......
...@@ -381,9 +381,10 @@ void LiftoffAssembler::StoreTaggedPointer(Register dst_addr, ...@@ -381,9 +381,10 @@ void LiftoffAssembler::StoreTaggedPointer(Register dst_addr,
MemoryChunk::kPointersToHereAreInterestingMask, zero, &exit, MemoryChunk::kPointersToHereAreInterestingMask, zero, &exit,
Label::kNear); Label::kNear);
leaq(scratch, dst_op); leaq(scratch, dst_op);
CallRecordWriteStub(dst_addr, scratch, RememberedSetAction::kEmit,
SaveFPRegsMode::kSave, CallRecordWriteStubSaveRegisters(
StubCallMode::kCallWasmRuntimeStub); dst_addr, scratch, RememberedSetAction::kEmit, SaveFPRegsMode::kSave,
StubCallMode::kCallWasmRuntimeStub);
bind(&exit); bind(&exit);
} }
......
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