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