Commit 3f1a59f4 authored by Pierre Langlois's avatar Pierre Langlois Committed by Commit Bot

[arm][arm64] Do not allocate temp registers for the write barrier.

Improve code generation for stores with write barriers slightly by using the
assembler's dedicated scratch registers (x16 and x17 on Arm64, ip on Arm)
instead of allocating temporaries.

To do this, we've done two things:

  - Use ip as a scratch register when loading page flags.

  - TurboAssembler::CallRecordWriteStub() now takes the offset of the slot
    that's written to rather than its address, removing the need to allocate a
    temporary register for it.

In essence, we've gone from:

```
;; Do the store.
stur x19, [x9, #15]
;; Check *destination* object page flags and jump out-of-line.
and x4, x9, #0xfffffffffff80000
ldr x4, [x4, #8]
tbnz x4, #2, #+0x1e7c
|     ;; Check *source* object page flags.
| `-> and x4, x19, #0xfffffffffff80000
|     ldr x4, [xM, #8]
|,--- tbz x4, #1, #-0x1e80
|     ;; Compute address of slot.
|     add x5, x9, #0xf (15)
|     ;; Setup arguments to RecordWrite
|     stp x2, x3, [sp, #-32]!
|     stp x4, lr, [sp, #16]
|     stp x0, x1, [sp, #-16]!
|     mov x0, x9 ;; Object address in x9
|     mov x1, x5 ;; Slot address in x5
|     movz x2, #0x0
|     movz x3, #0x100000000
|     ;; Call RecordWrite
|     ldr x16, pc+2056
|     blr x16
```

Which allocates x4 and x5 as temporaries.

To:

```
stur x19, [x9, #15]
and x16, x9, #0xfffffffffff80000 ;; Using x16 instead of allocating x4.
ldr x16, [x16, #8]
tbnz x16, #2, #+0x1e7c
| `-> and x16, x19, #0xfffffffffff80000
|     ldr x16, [xM, #8]
|,--- tbz x16, #1, #-0x1e80
|     stp x2, x3, [sp, #-32]!
|     stp x4, lr, [sp, #16]
|     stp x0, x1, [sp, #-16]!
|     mov x0, x9            ;; Object address still in x9.
|     add x1, x9, #0xf (15) ;; Compute the slot address directly.
|     movz x2, #0x0
|     movz x3, #0x100000000
|     ldr x16, pc+2056
|     blr x16
```

Finally, `RecordWriteField()` does not need an extra scratch register anymore.

Change-Id: Icb71310e7b8ab1ca83ced250851456166b337d00
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1505793
Commit-Queue: Pierre Langlois <pierre.langlois@arm.com>
Reviewed-by: 's avatarSigurd Schneider <sigurds@chromium.org>
Reviewed-by: 's avatarUlan Degenbaev <ulan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#61153}
parent f88e4a3f
...@@ -610,9 +610,8 @@ void TurboAssembler::LoadRoot(Register destination, RootIndex index, ...@@ -610,9 +610,8 @@ void TurboAssembler::LoadRoot(Register destination, RootIndex index,
MemOperand(kRootRegister, RootRegisterOffsetForRootIndex(index)), cond); MemOperand(kRootRegister, RootRegisterOffsetForRootIndex(index)), cond);
} }
void MacroAssembler::RecordWriteField(Register object, int offset, void MacroAssembler::RecordWriteField(Register object, int offset,
Register value, Register dst, Register value,
LinkRegisterStatus lr_status, LinkRegisterStatus lr_status,
SaveFPRegsMode save_fp, SaveFPRegsMode save_fp,
RememberedSetAction remembered_set_action, RememberedSetAction remembered_set_action,
...@@ -630,26 +629,21 @@ void MacroAssembler::RecordWriteField(Register object, int offset, ...@@ -630,26 +629,21 @@ void MacroAssembler::RecordWriteField(Register object, int offset,
// of the object, so so offset must be a multiple of kPointerSize. // of the object, so so offset must be a multiple of kPointerSize.
DCHECK(IsAligned(offset, kPointerSize)); DCHECK(IsAligned(offset, kPointerSize));
add(dst, object, Operand(offset - kHeapObjectTag));
if (emit_debug_code()) { if (emit_debug_code()) {
Label ok; Label ok;
tst(dst, Operand(kPointerSize - 1)); UseScratchRegisterScope temps(this);
Register scratch = temps.Acquire();
add(scratch, object, Operand(offset - kHeapObjectTag));
tst(scratch, Operand(kPointerSize - 1));
b(eq, &ok); b(eq, &ok);
stop("Unaligned cell in write barrier"); stop("Unaligned cell in write barrier");
bind(&ok); bind(&ok);
} }
RecordWrite(object, dst, value, lr_status, save_fp, remembered_set_action, RecordWrite(object, Operand(offset - kHeapObjectTag), value, lr_status,
OMIT_SMI_CHECK); save_fp, remembered_set_action, OMIT_SMI_CHECK);
bind(&done); bind(&done);
// Clobber clobbered input registers when running with the debug-code flag
// turned on to provoke errors.
if (emit_debug_code()) {
mov(value, Operand(bit_cast<int32_t>(kZapValue + 4)));
mov(dst, Operand(bit_cast<int32_t>(kZapValue + 8)));
}
} }
void TurboAssembler::SaveRegisters(RegList registers) { void TurboAssembler::SaveRegisters(RegList registers) {
...@@ -675,7 +669,7 @@ void TurboAssembler::RestoreRegisters(RegList registers) { ...@@ -675,7 +669,7 @@ void TurboAssembler::RestoreRegisters(RegList registers) {
ldm(ia_w, sp, regs); ldm(ia_w, sp, regs);
} }
void TurboAssembler::CallEphemeronKeyBarrier(Register object, Register address, void TurboAssembler::CallEphemeronKeyBarrier(Register object, Operand offset,
SaveFPRegsMode fp_mode) { SaveFPRegsMode fp_mode) {
EphemeronKeyBarrierDescriptor descriptor; EphemeronKeyBarrierDescriptor descriptor;
RegList registers = descriptor.allocatable_registers(); RegList registers = descriptor.allocatable_registers();
...@@ -689,7 +683,7 @@ void TurboAssembler::CallEphemeronKeyBarrier(Register object, Register address, ...@@ -689,7 +683,7 @@ void TurboAssembler::CallEphemeronKeyBarrier(Register object, Register address,
Register fp_mode_parameter( Register fp_mode_parameter(
descriptor.GetRegisterParameter(EphemeronKeyBarrierDescriptor::kFPMode)); descriptor.GetRegisterParameter(EphemeronKeyBarrierDescriptor::kFPMode));
MovePair(object_parameter, object, slot_parameter, address); MoveObjectAndSlot(object_parameter, slot_parameter, object, offset);
Move(fp_mode_parameter, Smi::FromEnum(fp_mode)); Move(fp_mode_parameter, Smi::FromEnum(fp_mode));
Call(isolate()->builtins()->builtin_handle(Builtins::kEphemeronKeyBarrier), Call(isolate()->builtins()->builtin_handle(Builtins::kEphemeronKeyBarrier),
RelocInfo::CODE_TARGET); RelocInfo::CODE_TARGET);
...@@ -697,26 +691,24 @@ void TurboAssembler::CallEphemeronKeyBarrier(Register object, Register address, ...@@ -697,26 +691,24 @@ void TurboAssembler::CallEphemeronKeyBarrier(Register object, Register address,
} }
void TurboAssembler::CallRecordWriteStub( void TurboAssembler::CallRecordWriteStub(
Register object, Register address, Register object, Operand offset, RememberedSetAction remembered_set_action,
RememberedSetAction remembered_set_action, SaveFPRegsMode fp_mode) { SaveFPRegsMode fp_mode) {
CallRecordWriteStub( CallRecordWriteStub(
object, address, remembered_set_action, fp_mode, object, offset, remembered_set_action, fp_mode,
isolate()->builtins()->builtin_handle(Builtins::kRecordWrite), isolate()->builtins()->builtin_handle(Builtins::kRecordWrite),
kNullAddress); kNullAddress);
} }
void TurboAssembler::CallRecordWriteStub( void TurboAssembler::CallRecordWriteStub(
Register object, Register address, Register object, Operand offset, RememberedSetAction remembered_set_action,
RememberedSetAction remembered_set_action, SaveFPRegsMode fp_mode, SaveFPRegsMode fp_mode, Address wasm_target) {
Address wasm_target) { CallRecordWriteStub(object, offset, remembered_set_action, fp_mode,
CallRecordWriteStub(object, address, remembered_set_action, fp_mode,
Handle<Code>::null(), wasm_target); Handle<Code>::null(), wasm_target);
} }
void TurboAssembler::CallRecordWriteStub( void TurboAssembler::CallRecordWriteStub(
Register object, Register address, Register object, Operand offset, RememberedSetAction remembered_set_action,
RememberedSetAction remembered_set_action, SaveFPRegsMode fp_mode, SaveFPRegsMode fp_mode, Handle<Code> code_target, Address wasm_target) {
Handle<Code> code_target, Address wasm_target) {
DCHECK_NE(code_target.is_null(), wasm_target == kNullAddress); DCHECK_NE(code_target.is_null(), wasm_target == kNullAddress);
// TODO(albertnetymk): For now we ignore remembered_set_action and fp_mode, // TODO(albertnetymk): For now we ignore remembered_set_action and fp_mode,
// i.e. always emit remember set and save FP registers in RecordWriteStub. If // i.e. always emit remember set and save FP registers in RecordWriteStub. If
...@@ -737,7 +729,7 @@ void TurboAssembler::CallRecordWriteStub( ...@@ -737,7 +729,7 @@ void TurboAssembler::CallRecordWriteStub(
Register fp_mode_parameter( Register fp_mode_parameter(
descriptor.GetRegisterParameter(RecordWriteDescriptor::kFPMode)); descriptor.GetRegisterParameter(RecordWriteDescriptor::kFPMode));
MovePair(object_parameter, object, slot_parameter, address); MoveObjectAndSlot(object_parameter, slot_parameter, object, offset);
Move(remembered_set_parameter, Smi::FromEnum(remembered_set_action)); Move(remembered_set_parameter, Smi::FromEnum(remembered_set_action));
Move(fp_mode_parameter, Smi::FromEnum(fp_mode)); Move(fp_mode_parameter, Smi::FromEnum(fp_mode));
...@@ -750,20 +742,54 @@ void TurboAssembler::CallRecordWriteStub( ...@@ -750,20 +742,54 @@ void TurboAssembler::CallRecordWriteStub(
RestoreRegisters(registers); RestoreRegisters(registers);
} }
// Will clobber 3 registers: object, address, and value. The register 'object' void TurboAssembler::MoveObjectAndSlot(Register dst_object, Register dst_slot,
// contains a heap object pointer. The heap object tag is shifted away. Register object, Operand offset) {
// A scratch register also needs to be available. DCHECK_NE(dst_object, dst_slot);
void MacroAssembler::RecordWrite(Register object, Register address, DCHECK(offset.IsRegister() || offset.IsImmediate());
// If `offset` is a register, it cannot overlap with `object`.
DCHECK_IMPLIES(offset.IsRegister(), offset.rm() != object);
// If the slot register does not overlap with the object register, we can
// overwrite it.
if (dst_slot != object) {
add(dst_slot, object, offset);
Move(dst_object, object);
return;
}
DCHECK_EQ(dst_slot, object);
// If the destination object register does not overlap with the offset
// register, we can overwrite it.
if (!offset.IsRegister() || (offset.rm() != dst_object)) {
Move(dst_object, dst_slot);
add(dst_slot, dst_slot, offset);
return;
}
DCHECK_EQ(dst_object, offset.rm());
// We only have `dst_slot` and `dst_object` left as distinct registers so we
// have to swap them. We write this as a add+sub sequence to avoid using a
// scratch register.
add(dst_slot, dst_slot, dst_object);
sub(dst_object, dst_slot, dst_object);
}
// The register 'object' contains a heap object pointer. The heap object tag is
// shifted away. A scratch register also needs to be available.
void MacroAssembler::RecordWrite(Register object, Operand offset,
Register value, LinkRegisterStatus lr_status, Register value, LinkRegisterStatus lr_status,
SaveFPRegsMode fp_mode, SaveFPRegsMode fp_mode,
RememberedSetAction remembered_set_action, RememberedSetAction remembered_set_action,
SmiCheck smi_check) { SmiCheck smi_check) {
DCHECK(object != value); DCHECK_NE(object, value);
if (emit_debug_code()) { if (emit_debug_code()) {
{ {
UseScratchRegisterScope temps(this); UseScratchRegisterScope temps(this);
Register scratch = temps.Acquire(); Register scratch = temps.Acquire();
ldr(scratch, MemOperand(address)); add(scratch, object, offset);
ldr(scratch, MemOperand(scratch));
cmp(scratch, value); cmp(scratch, value);
} }
Check(eq, AbortReason::kWrongAddressOrValuePassedToRecordWrite); Check(eq, AbortReason::kWrongAddressOrValuePassedToRecordWrite);
...@@ -782,32 +808,21 @@ void MacroAssembler::RecordWrite(Register object, Register address, ...@@ -782,32 +808,21 @@ void MacroAssembler::RecordWrite(Register object, Register address,
JumpIfSmi(value, &done); JumpIfSmi(value, &done);
} }
CheckPageFlag(value, CheckPageFlag(value, MemoryChunk::kPointersToHereAreInterestingMask, eq,
value, // Used as scratch. &done);
MemoryChunk::kPointersToHereAreInterestingMask, eq, &done); CheckPageFlag(object, MemoryChunk::kPointersFromHereAreInterestingMask, eq,
CheckPageFlag(object,
value, // Used as scratch.
MemoryChunk::kPointersFromHereAreInterestingMask,
eq,
&done); &done);
// Record the actual write. // Record the actual write.
if (lr_status == kLRHasNotBeenSaved) { if (lr_status == kLRHasNotBeenSaved) {
push(lr); push(lr);
} }
CallRecordWriteStub(object, address, remembered_set_action, fp_mode); CallRecordWriteStub(object, offset, remembered_set_action, fp_mode);
if (lr_status == kLRHasNotBeenSaved) { if (lr_status == kLRHasNotBeenSaved) {
pop(lr); pop(lr);
} }
bind(&done); bind(&done);
// Clobber clobbered registers when running with the debug-code flag
// turned on to provoke errors.
if (emit_debug_code()) {
mov(address, Operand(bit_cast<int32_t>(kZapValue + 12)));
mov(value, Operand(bit_cast<int32_t>(kZapValue + 16)));
}
} }
void TurboAssembler::PushCommonFrame(Register marker_reg) { void TurboAssembler::PushCommonFrame(Register marker_reg) {
...@@ -2458,8 +2473,10 @@ void TurboAssembler::CallCFunctionHelper(Register function, ...@@ -2458,8 +2473,10 @@ void TurboAssembler::CallCFunctionHelper(Register function,
} }
} }
void TurboAssembler::CheckPageFlag(Register object, Register scratch, int mask, void TurboAssembler::CheckPageFlag(Register object, int mask, Condition cc,
Condition cc, Label* condition_met) { Label* condition_met) {
UseScratchRegisterScope temps(this);
Register scratch = temps.Acquire();
DCHECK(cc == eq || cc == ne); DCHECK(cc == eq || cc == ne);
Bfc(scratch, object, 0, kPageSizeBits); Bfc(scratch, object, 0, kPageSizeBits);
ldr(scratch, MemOperand(scratch, MemoryChunk::kFlagsOffset)); ldr(scratch, MemOperand(scratch, MemoryChunk::kFlagsOffset));
......
...@@ -337,7 +337,7 @@ class V8_EXPORT_PRIVATE TurboAssembler : public TurboAssemblerBase { ...@@ -337,7 +337,7 @@ class V8_EXPORT_PRIVATE TurboAssembler : public TurboAssemblerBase {
void VmovLow(Register dst, DwVfpRegister src); void VmovLow(Register dst, DwVfpRegister src);
void VmovLow(DwVfpRegister dst, Register src); void VmovLow(DwVfpRegister dst, Register src);
void CheckPageFlag(Register object, Register scratch, int mask, Condition cc, void CheckPageFlag(Register object, int mask, Condition cc,
Label* condition_met); Label* condition_met);
// Check whether d16-d31 are available on the CPU. The result is given by the // Check whether d16-d31 are available on the CPU. The result is given by the
...@@ -347,15 +347,25 @@ class V8_EXPORT_PRIVATE TurboAssembler : public TurboAssemblerBase { ...@@ -347,15 +347,25 @@ class V8_EXPORT_PRIVATE TurboAssembler : public TurboAssemblerBase {
void SaveRegisters(RegList registers); void SaveRegisters(RegList registers);
void RestoreRegisters(RegList registers); void RestoreRegisters(RegList registers);
void CallRecordWriteStub(Register object, Register address, void CallRecordWriteStub(Register object, Operand offset,
RememberedSetAction remembered_set_action, RememberedSetAction remembered_set_action,
SaveFPRegsMode fp_mode); SaveFPRegsMode fp_mode);
void CallRecordWriteStub(Register object, Register address, void CallRecordWriteStub(Register object, Operand offset,
RememberedSetAction remembered_set_action, RememberedSetAction remembered_set_action,
SaveFPRegsMode fp_mode, Address wasm_target); SaveFPRegsMode fp_mode, Address wasm_target);
void CallEphemeronKeyBarrier(Register object, Register address, void CallEphemeronKeyBarrier(Register object, Operand offset,
SaveFPRegsMode fp_mode); SaveFPRegsMode fp_mode);
// For a given |object| and |offset|:
// - Move |object| to |dst_object|.
// - Compute the address of the slot pointed to by |offset| in |object| and
// write it to |dst_slot|. |offset| can be either an immediate or a
// register.
// This method makes sure |object| and |offset| are allowed to overlap with
// the destination registers.
void MoveObjectAndSlot(Register dst_object, Register dst_slot,
Register object, Operand offset);
// Does a runtime check for 16/32 FP registers. Either way, pushes 32 double // Does a runtime check for 16/32 FP registers. Either way, pushes 32 double
// values to location, saving [d0..(d15|d31)]. // values to location, saving [d0..(d15|d31)].
void SaveFPRegs(Register location, Register scratch); void SaveFPRegs(Register location, Register scratch);
...@@ -541,7 +551,7 @@ class V8_EXPORT_PRIVATE TurboAssembler : public TurboAssemblerBase { ...@@ -541,7 +551,7 @@ class V8_EXPORT_PRIVATE TurboAssembler : public TurboAssemblerBase {
void CallCFunctionHelper(Register function, int num_reg_arguments, void CallCFunctionHelper(Register function, int num_reg_arguments,
int num_double_arguments); int num_double_arguments);
void CallRecordWriteStub(Register object, Register address, void CallRecordWriteStub(Register object, Operand offset,
RememberedSetAction remembered_set_action, RememberedSetAction remembered_set_action,
SaveFPRegsMode fp_mode, Handle<Code> code_target, SaveFPRegsMode fp_mode, Handle<Code> code_target,
Address wasm_target); Address wasm_target);
...@@ -566,20 +576,19 @@ class V8_EXPORT_PRIVATE MacroAssembler : public TurboAssembler { ...@@ -566,20 +576,19 @@ class V8_EXPORT_PRIVATE MacroAssembler : public TurboAssembler {
// Notify the garbage collector that we wrote a pointer into an object. // Notify the garbage collector that we wrote a pointer into an object.
// |object| is the object being stored into, |value| is the object being // |object| is the object being stored into, |value| is the object being
// stored. value and scratch registers are clobbered by the operation. // stored.
// 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 FieldMemOperand(reg, off). // the tagged HeapObject pointer. For use with FieldMemOperand(reg, off).
void RecordWriteField( void RecordWriteField(
Register object, int offset, Register value, Register scratch, Register object, int offset, Register value, LinkRegisterStatus lr_status,
LinkRegisterStatus lr_status, SaveFPRegsMode save_fp, SaveFPRegsMode save_fp,
RememberedSetAction remembered_set_action = EMIT_REMEMBERED_SET, RememberedSetAction remembered_set_action = EMIT_REMEMBERED_SET,
SmiCheck smi_check = INLINE_SMI_CHECK); SmiCheck smi_check = INLINE_SMI_CHECK);
// For a given |object| notify the garbage collector that the slot |address| // For a given |object| notify the garbage collector that the slot at |offset|
// has been written. |value| is the object being stored. The value and // has been written. |value| is the object being stored.
// address registers are clobbered by the operation.
void RecordWrite( void RecordWrite(
Register object, Register address, Register value, Register object, Operand offset, Register value,
LinkRegisterStatus lr_status, SaveFPRegsMode save_fp, LinkRegisterStatus lr_status, SaveFPRegsMode save_fp,
RememberedSetAction remembered_set_action = EMIT_REMEMBERED_SET, RememberedSetAction remembered_set_action = EMIT_REMEMBERED_SET,
SmiCheck smi_check = INLINE_SMI_CHECK); SmiCheck smi_check = INLINE_SMI_CHECK);
......
...@@ -2931,9 +2931,10 @@ int MacroAssembler::SafepointRegisterStackIndex(int reg_code) { ...@@ -2931,9 +2931,10 @@ int MacroAssembler::SafepointRegisterStackIndex(int reg_code) {
} }
} }
void TurboAssembler::CheckPageFlag(const Register& object, void TurboAssembler::CheckPageFlag(const Register& object, int mask,
const Register& scratch, int mask,
Condition cc, Label* condition_met) { Condition cc, Label* condition_met) {
UseScratchRegisterScope temps(this);
Register scratch = temps.AcquireX();
And(scratch, object, ~kPageAlignmentMask); And(scratch, object, ~kPageAlignmentMask);
Ldr(scratch, MemOperand(scratch, MemoryChunk::kFlagsOffset)); Ldr(scratch, MemOperand(scratch, MemoryChunk::kFlagsOffset));
if (cc == eq) { if (cc == eq) {
...@@ -2945,7 +2946,7 @@ void TurboAssembler::CheckPageFlag(const Register& object, ...@@ -2945,7 +2946,7 @@ void TurboAssembler::CheckPageFlag(const Register& object,
} }
void MacroAssembler::RecordWriteField(Register object, int offset, void MacroAssembler::RecordWriteField(Register object, int offset,
Register value, Register scratch, Register value,
LinkRegisterStatus lr_status, LinkRegisterStatus lr_status,
SaveFPRegsMode save_fp, SaveFPRegsMode save_fp,
RememberedSetAction remembered_set_action, RememberedSetAction remembered_set_action,
...@@ -2963,26 +2964,21 @@ void MacroAssembler::RecordWriteField(Register object, int offset, ...@@ -2963,26 +2964,21 @@ void MacroAssembler::RecordWriteField(Register object, int offset,
// of the object, so offset must be a multiple of kTaggedSize. // of the object, so offset must be a multiple of kTaggedSize.
DCHECK(IsAligned(offset, kTaggedSize)); DCHECK(IsAligned(offset, kTaggedSize));
Add(scratch, object, offset - kHeapObjectTag);
if (emit_debug_code()) { if (emit_debug_code()) {
Label ok; Label ok;
UseScratchRegisterScope temps(this);
Register scratch = temps.AcquireX();
Add(scratch, object, offset - kHeapObjectTag);
Tst(scratch, kTaggedSize - 1); Tst(scratch, kTaggedSize - 1);
B(eq, &ok); B(eq, &ok);
Abort(AbortReason::kUnalignedCellInWriteBarrier); Abort(AbortReason::kUnalignedCellInWriteBarrier);
Bind(&ok); Bind(&ok);
} }
RecordWrite(object, scratch, value, lr_status, save_fp, remembered_set_action, RecordWrite(object, Operand(offset - kHeapObjectTag), value, lr_status,
OMIT_SMI_CHECK); save_fp, remembered_set_action, OMIT_SMI_CHECK);
Bind(&done); Bind(&done);
// Clobber clobbered input registers when running with the debug-code flag
// turned on to provoke errors.
if (emit_debug_code()) {
Mov(value, Operand(bit_cast<int64_t>(kZapValue + 4)));
Mov(scratch, Operand(bit_cast<int64_t>(kZapValue + 8)));
}
} }
void TurboAssembler::SaveRegisters(RegList registers) { void TurboAssembler::SaveRegisters(RegList registers) {
...@@ -3009,7 +3005,7 @@ void TurboAssembler::RestoreRegisters(RegList registers) { ...@@ -3009,7 +3005,7 @@ void TurboAssembler::RestoreRegisters(RegList registers) {
PopCPURegList(regs); PopCPURegList(regs);
} }
void TurboAssembler::CallEphemeronKeyBarrier(Register object, Register address, void TurboAssembler::CallEphemeronKeyBarrier(Register object, Operand offset,
SaveFPRegsMode fp_mode) { SaveFPRegsMode fp_mode) {
EphemeronKeyBarrierDescriptor descriptor; EphemeronKeyBarrierDescriptor descriptor;
RegList registers = descriptor.allocatable_registers(); RegList registers = descriptor.allocatable_registers();
...@@ -3023,7 +3019,7 @@ void TurboAssembler::CallEphemeronKeyBarrier(Register object, Register address, ...@@ -3023,7 +3019,7 @@ void TurboAssembler::CallEphemeronKeyBarrier(Register object, Register address,
Register fp_mode_parameter( Register fp_mode_parameter(
descriptor.GetRegisterParameter(EphemeronKeyBarrierDescriptor::kFPMode)); descriptor.GetRegisterParameter(EphemeronKeyBarrierDescriptor::kFPMode));
MovePair(object_parameter, object, slot_parameter, address); MoveObjectAndSlot(object_parameter, slot_parameter, object, offset);
Mov(fp_mode_parameter, Smi::FromEnum(fp_mode)); Mov(fp_mode_parameter, Smi::FromEnum(fp_mode));
Call(isolate()->builtins()->builtin_handle(Builtins::kEphemeronKeyBarrier), Call(isolate()->builtins()->builtin_handle(Builtins::kEphemeronKeyBarrier),
...@@ -3032,26 +3028,24 @@ void TurboAssembler::CallEphemeronKeyBarrier(Register object, Register address, ...@@ -3032,26 +3028,24 @@ void TurboAssembler::CallEphemeronKeyBarrier(Register object, Register address,
} }
void TurboAssembler::CallRecordWriteStub( void TurboAssembler::CallRecordWriteStub(
Register object, Register address, Register object, Operand offset, RememberedSetAction remembered_set_action,
RememberedSetAction remembered_set_action, SaveFPRegsMode fp_mode) { SaveFPRegsMode fp_mode) {
CallRecordWriteStub( CallRecordWriteStub(
object, address, remembered_set_action, fp_mode, object, offset, remembered_set_action, fp_mode,
isolate()->builtins()->builtin_handle(Builtins::kRecordWrite), isolate()->builtins()->builtin_handle(Builtins::kRecordWrite),
kNullAddress); kNullAddress);
} }
void TurboAssembler::CallRecordWriteStub( void TurboAssembler::CallRecordWriteStub(
Register object, Register address, Register object, Operand offset, RememberedSetAction remembered_set_action,
RememberedSetAction remembered_set_action, SaveFPRegsMode fp_mode, SaveFPRegsMode fp_mode, Address wasm_target) {
Address wasm_target) { CallRecordWriteStub(object, offset, remembered_set_action, fp_mode,
CallRecordWriteStub(object, address, remembered_set_action, fp_mode,
Handle<Code>::null(), wasm_target); Handle<Code>::null(), wasm_target);
} }
void TurboAssembler::CallRecordWriteStub( void TurboAssembler::CallRecordWriteStub(
Register object, Register address, Register object, Operand offset, RememberedSetAction remembered_set_action,
RememberedSetAction remembered_set_action, SaveFPRegsMode fp_mode, SaveFPRegsMode fp_mode, Handle<Code> code_target, Address wasm_target) {
Handle<Code> code_target, Address wasm_target) {
DCHECK_NE(code_target.is_null(), wasm_target == kNullAddress); DCHECK_NE(code_target.is_null(), wasm_target == kNullAddress);
// TODO(albertnetymk): For now we ignore remembered_set_action and fp_mode, // TODO(albertnetymk): For now we ignore remembered_set_action and fp_mode,
// i.e. always emit remember set and save FP registers in RecordWriteStub. If // i.e. always emit remember set and save FP registers in RecordWriteStub. If
...@@ -3072,7 +3066,7 @@ void TurboAssembler::CallRecordWriteStub( ...@@ -3072,7 +3066,7 @@ void TurboAssembler::CallRecordWriteStub(
Register fp_mode_parameter( Register fp_mode_parameter(
descriptor.GetRegisterParameter(RecordWriteDescriptor::kFPMode)); descriptor.GetRegisterParameter(RecordWriteDescriptor::kFPMode));
MovePair(object_parameter, object, slot_parameter, address); MoveObjectAndSlot(object_parameter, slot_parameter, object, offset);
Mov(remembered_set_parameter, Smi::FromEnum(remembered_set_action)); Mov(remembered_set_parameter, Smi::FromEnum(remembered_set_action));
Mov(fp_mode_parameter, Smi::FromEnum(fp_mode)); Mov(fp_mode_parameter, Smi::FromEnum(fp_mode));
...@@ -3085,12 +3079,44 @@ void TurboAssembler::CallRecordWriteStub( ...@@ -3085,12 +3079,44 @@ void TurboAssembler::CallRecordWriteStub(
RestoreRegisters(registers); RestoreRegisters(registers);
} }
// Will clobber: object, address, value. void TurboAssembler::MoveObjectAndSlot(Register dst_object, Register dst_slot,
// If lr_status is kLRHasBeenSaved, lr will also be clobbered. Register object, Operand offset) {
DCHECK_NE(dst_object, dst_slot);
// If `offset` is a register, it cannot overlap with `object`.
DCHECK_IMPLIES(!offset.IsImmediate(), offset.reg() != object);
// If the slot register does not overlap with the object register, we can
// overwrite it.
if (dst_slot != object) {
Add(dst_slot, object, offset);
Mov(dst_object, object);
return;
}
DCHECK_EQ(dst_slot, object);
// If the destination object register does not overlap with the offset
// register, we can overwrite it.
if (offset.IsImmediate() || (offset.reg() != dst_object)) {
Mov(dst_object, dst_slot);
Add(dst_slot, dst_slot, offset);
return;
}
DCHECK_EQ(dst_object, offset.reg());
// We only have `dst_slot` and `dst_object` left as distinct registers so we
// have to swap them. We write this as a add+sub sequence to avoid using a
// scratch register.
Add(dst_slot, dst_slot, dst_object);
Sub(dst_object, dst_slot, dst_object);
}
// If lr_status is kLRHasBeenSaved, lr will be clobbered.
// //
// The register 'object' contains a heap object pointer. The heap object tag is // The register 'object' contains a heap object pointer. The heap object tag is
// shifted away. // shifted away.
void MacroAssembler::RecordWrite(Register object, Register address, void MacroAssembler::RecordWrite(Register object, Operand offset,
Register value, LinkRegisterStatus lr_status, Register value, LinkRegisterStatus lr_status,
SaveFPRegsMode fp_mode, SaveFPRegsMode fp_mode,
RememberedSetAction remembered_set_action, RememberedSetAction remembered_set_action,
...@@ -3102,7 +3128,8 @@ void MacroAssembler::RecordWrite(Register object, Register address, ...@@ -3102,7 +3128,8 @@ void MacroAssembler::RecordWrite(Register object, Register address,
UseScratchRegisterScope temps(this); UseScratchRegisterScope temps(this);
Register temp = temps.AcquireX(); Register temp = temps.AcquireX();
LoadTaggedPointerField(temp, MemOperand(address)); Add(temp, object, offset);
LoadTaggedPointerField(temp, MemOperand(temp));
Cmp(temp, value); Cmp(temp, value);
Check(eq, AbortReason::kWrongAddressOrValuePassedToRecordWrite); Check(eq, AbortReason::kWrongAddressOrValuePassedToRecordWrite);
} }
...@@ -3115,31 +3142,22 @@ void MacroAssembler::RecordWrite(Register object, Register address, ...@@ -3115,31 +3142,22 @@ void MacroAssembler::RecordWrite(Register object, Register address,
DCHECK_EQ(0, kSmiTag); DCHECK_EQ(0, kSmiTag);
JumpIfSmi(value, &done); JumpIfSmi(value, &done);
} }
CheckPageFlag(value, CheckPageFlag(value, MemoryChunk::kPointersToHereAreInterestingMask, ne,
value, // Used as scratch. &done);
MemoryChunk::kPointersToHereAreInterestingMask, ne, &done);
CheckPageFlag(object, CheckPageFlag(object, MemoryChunk::kPointersFromHereAreInterestingMask, ne,
value, // Used as scratch. &done);
MemoryChunk::kPointersFromHereAreInterestingMask, ne, &done);
// Record the actual write. // Record the actual write.
if (lr_status == kLRHasNotBeenSaved) { if (lr_status == kLRHasNotBeenSaved) {
Push(padreg, lr); Push(padreg, lr);
} }
CallRecordWriteStub(object, address, remembered_set_action, fp_mode); CallRecordWriteStub(object, offset, remembered_set_action, fp_mode);
if (lr_status == kLRHasNotBeenSaved) { if (lr_status == kLRHasNotBeenSaved) {
Pop(lr, padreg); Pop(lr, padreg);
} }
Bind(&done); Bind(&done);
// Clobber clobbered registers when running with the debug-code flag
// turned on to provoke errors.
if (emit_debug_code()) {
Mov(address, Operand(bit_cast<int64_t>(kZapValue + 12)));
Mov(value, Operand(bit_cast<int64_t>(kZapValue + 16)));
}
} }
void TurboAssembler::Assert(Condition cond, AbortReason reason) { void TurboAssembler::Assert(Condition cond, AbortReason reason) {
......
...@@ -740,15 +740,24 @@ class V8_EXPORT_PRIVATE TurboAssembler : public TurboAssemblerBase { ...@@ -740,15 +740,24 @@ class V8_EXPORT_PRIVATE TurboAssembler : public TurboAssemblerBase {
void SaveRegisters(RegList registers); void SaveRegisters(RegList registers);
void RestoreRegisters(RegList registers); void RestoreRegisters(RegList registers);
void CallRecordWriteStub(Register object, Register address, void CallRecordWriteStub(Register object, Operand offset,
RememberedSetAction remembered_set_action, RememberedSetAction remembered_set_action,
SaveFPRegsMode fp_mode); SaveFPRegsMode fp_mode);
void CallRecordWriteStub(Register object, Register address, void CallRecordWriteStub(Register object, Operand offset,
RememberedSetAction remembered_set_action, RememberedSetAction remembered_set_action,
SaveFPRegsMode fp_mode, Address wasm_target); SaveFPRegsMode fp_mode, Address wasm_target);
void CallEphemeronKeyBarrier(Register object, Register address, void CallEphemeronKeyBarrier(Register object, Operand offset,
SaveFPRegsMode fp_mode); SaveFPRegsMode fp_mode);
// For a given |object| and |offset|:
// - Move |object| to |dst_object|.
// - Compute the address of the slot pointed to by |offset| in |object| and
// write it to |dst_slot|.
// This method makes sure |object| and |offset| are allowed to overlap with
// the destination registers.
void MoveObjectAndSlot(Register dst_object, Register dst_slot,
Register object, Operand offset);
// Alternative forms of Push and Pop, taking a RegList or CPURegList that // Alternative forms of Push and Pop, taking a RegList or CPURegList that
// specifies the registers that are to be pushed or popped. Higher-numbered // specifies the registers that are to be pushed or popped. Higher-numbered
// registers are associated with higher memory addresses (as in the A32 push // registers are associated with higher memory addresses (as in the A32 push
...@@ -783,8 +792,8 @@ class V8_EXPORT_PRIVATE TurboAssembler : public TurboAssemblerBase { ...@@ -783,8 +792,8 @@ class V8_EXPORT_PRIVATE TurboAssembler : public TurboAssemblerBase {
Operand MoveImmediateForShiftedOp(const Register& dst, int64_t imm, Operand MoveImmediateForShiftedOp(const Register& dst, int64_t imm,
PreShiftImmMode mode); PreShiftImmMode mode);
void CheckPageFlag(const Register& object, const Register& scratch, int mask, void CheckPageFlag(const Register& object, int mask, Condition cc,
Condition cc, Label* condition_met); Label* condition_met);
// Test the bits of register defined by bit_pattern, and branch if ANY of // Test the bits of register defined by bit_pattern, and branch if ANY of
// those bits are set. May corrupt the status flags. // those bits are set. May corrupt the status flags.
...@@ -1273,7 +1282,7 @@ class V8_EXPORT_PRIVATE TurboAssembler : public TurboAssemblerBase { ...@@ -1273,7 +1282,7 @@ class V8_EXPORT_PRIVATE TurboAssembler : public TurboAssemblerBase {
void JumpHelper(int64_t offset, RelocInfo::Mode rmode, Condition cond = al); void JumpHelper(int64_t offset, RelocInfo::Mode rmode, Condition cond = al);
void CallRecordWriteStub(Register object, Register address, void CallRecordWriteStub(Register object, Operand offset,
RememberedSetAction remembered_set_action, RememberedSetAction remembered_set_action,
SaveFPRegsMode fp_mode, Handle<Code> code_target, SaveFPRegsMode fp_mode, Handle<Code> code_target,
Address wasm_target); Address wasm_target);
...@@ -1929,20 +1938,19 @@ class V8_EXPORT_PRIVATE MacroAssembler : public TurboAssembler { ...@@ -1929,20 +1938,19 @@ class V8_EXPORT_PRIVATE MacroAssembler : public TurboAssembler {
// Notify the garbage collector that we wrote a pointer into an object. // Notify the garbage collector that we wrote a pointer into an object.
// |object| is the object being stored into, |value| is the object being // |object| is the object being stored into, |value| is the object being
// stored. value and scratch registers are clobbered by the operation. // stored.
// 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 FieldMemOperand(reg, off). // the tagged HeapObject pointer. For use with FieldMemOperand(reg, off).
void RecordWriteField( void RecordWriteField(
Register object, int offset, Register value, Register scratch, Register object, int offset, Register value, LinkRegisterStatus lr_status,
LinkRegisterStatus lr_status, SaveFPRegsMode save_fp, SaveFPRegsMode save_fp,
RememberedSetAction remembered_set_action = EMIT_REMEMBERED_SET, RememberedSetAction remembered_set_action = EMIT_REMEMBERED_SET,
SmiCheck smi_check = INLINE_SMI_CHECK); SmiCheck smi_check = INLINE_SMI_CHECK);
// For a given |object| notify the garbage collector that the slot |address| // For a given |object| notify the garbage collector that the slot at |offset|
// has been written. |value| is the object being stored. The value and // has been written. |value| is the object being stored.
// address registers are clobbered by the operation.
void RecordWrite( void RecordWrite(
Register object, Register address, Register value, Register object, Operand offset, Register value,
LinkRegisterStatus lr_status, SaveFPRegsMode save_fp, LinkRegisterStatus lr_status, SaveFPRegsMode save_fp,
RememberedSetAction remembered_set_action = EMIT_REMEMBERED_SET, RememberedSetAction remembered_set_action = EMIT_REMEMBERED_SET,
SmiCheck smi_check = INLINE_SMI_CHECK); SmiCheck smi_check = INLINE_SMI_CHECK);
......
...@@ -403,7 +403,7 @@ void Builtins::Generate_ResumeGeneratorTrampoline(MacroAssembler* masm) { ...@@ -403,7 +403,7 @@ void Builtins::Generate_ResumeGeneratorTrampoline(MacroAssembler* masm) {
// Store input value into generator object. // Store input value into generator object.
__ str(r0, FieldMemOperand(r1, JSGeneratorObject::kInputOrDebugPosOffset)); __ str(r0, FieldMemOperand(r1, JSGeneratorObject::kInputOrDebugPosOffset));
__ RecordWriteField(r1, JSGeneratorObject::kInputOrDebugPosOffset, r0, r3, __ RecordWriteField(r1, JSGeneratorObject::kInputOrDebugPosOffset, r0,
kLRHasNotBeenSaved, kDontSaveFPRegs); kLRHasNotBeenSaved, kDontSaveFPRegs);
// Load suspended function and context. // Load suspended function and context.
...@@ -841,13 +841,12 @@ void Builtins::Generate_RunMicrotasksTrampoline(MacroAssembler* masm) { ...@@ -841,13 +841,12 @@ void Builtins::Generate_RunMicrotasksTrampoline(MacroAssembler* masm) {
__ Jump(BUILTIN_CODE(masm->isolate(), RunMicrotasks), RelocInfo::CODE_TARGET); __ Jump(BUILTIN_CODE(masm->isolate(), RunMicrotasks), RelocInfo::CODE_TARGET);
} }
static void ReplaceClosureCodeWithOptimizedCode( static void ReplaceClosureCodeWithOptimizedCode(MacroAssembler* masm,
MacroAssembler* masm, Register optimized_code, Register closure, Register optimized_code,
Register scratch1, Register scratch2, Register scratch3) { Register closure) {
// Store code entry in the closure. // Store code entry in the closure.
__ str(optimized_code, FieldMemOperand(closure, JSFunction::kCodeOffset)); __ str(optimized_code, FieldMemOperand(closure, JSFunction::kCodeOffset));
__ mov(scratch1, optimized_code); // Write barrier clobbers scratch1 below. __ RecordWriteField(closure, JSFunction::kCodeOffset, optimized_code,
__ RecordWriteField(closure, JSFunction::kCodeOffset, scratch1, scratch2,
kLRHasNotBeenSaved, kDontSaveFPRegs, OMIT_REMEMBERED_SET, kLRHasNotBeenSaved, kDontSaveFPRegs, OMIT_REMEMBERED_SET,
OMIT_SMI_CHECK); OMIT_SMI_CHECK);
} }
...@@ -882,14 +881,14 @@ static void TailCallRuntimeIfMarkerEquals(MacroAssembler* masm, ...@@ -882,14 +881,14 @@ static void TailCallRuntimeIfMarkerEquals(MacroAssembler* masm,
static void MaybeTailCallOptimizedCodeSlot(MacroAssembler* masm, static void MaybeTailCallOptimizedCodeSlot(MacroAssembler* masm,
Register feedback_vector, Register feedback_vector,
Register scratch1, Register scratch2, Register scratch1,
Register scratch3) { Register scratch2) {
// ----------- S t a t e ------------- // ----------- S t a t e -------------
// -- r3 : new target (preserved for callee if needed, and caller) // -- r3 : new target (preserved for callee if needed, and caller)
// -- r1 : target function (preserved for callee if needed, and caller) // -- r1 : target function (preserved for callee if needed, and caller)
// -- feedback vector (preserved for caller if needed) // -- feedback vector (preserved for caller if needed)
// ----------------------------------- // -----------------------------------
DCHECK(!AreAliased(feedback_vector, r1, r3, scratch1, scratch2, scratch3)); DCHECK(!AreAliased(feedback_vector, r1, r3, scratch1, scratch2));
Label optimized_code_slot_is_weak_ref, fallthrough; Label optimized_code_slot_is_weak_ref, fallthrough;
...@@ -962,8 +961,7 @@ static void MaybeTailCallOptimizedCodeSlot(MacroAssembler* masm, ...@@ -962,8 +961,7 @@ static void MaybeTailCallOptimizedCodeSlot(MacroAssembler* masm,
// the optimized functions list, then tail call the optimized code. // the optimized functions list, then tail call the optimized code.
// The feedback vector is no longer used, so re-use it as a scratch // The feedback vector is no longer used, so re-use it as a scratch
// register. // register.
ReplaceClosureCodeWithOptimizedCode(masm, optimized_code_entry, closure, ReplaceClosureCodeWithOptimizedCode(masm, optimized_code_entry, closure);
scratch2, scratch3, feedback_vector);
static_assert(kJavaScriptCallCodeStartRegister == r2, "ABI mismatch"); static_assert(kJavaScriptCallCodeStartRegister == r2, "ABI mismatch");
__ LoadCodeObjectEntry(r2, optimized_code_entry); __ LoadCodeObjectEntry(r2, optimized_code_entry);
__ Jump(r2); __ Jump(r2);
...@@ -1082,7 +1080,7 @@ void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) { ...@@ -1082,7 +1080,7 @@ void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) {
// Read off the optimized code slot in the feedback vector, and if there // Read off the optimized code slot in the feedback vector, and if there
// is optimized code or an optimization marker, call that instead. // is optimized code or an optimization marker, call that instead.
MaybeTailCallOptimizedCodeSlot(masm, feedback_vector, r4, r6, r5); MaybeTailCallOptimizedCodeSlot(masm, feedback_vector, r4, r6);
// Increment invocation count for the function. // Increment invocation count for the function.
__ ldr(r9, FieldMemOperand(feedback_vector, __ ldr(r9, FieldMemOperand(feedback_vector,
......
...@@ -473,7 +473,7 @@ void Builtins::Generate_ResumeGeneratorTrampoline(MacroAssembler* masm) { ...@@ -473,7 +473,7 @@ void Builtins::Generate_ResumeGeneratorTrampoline(MacroAssembler* masm) {
// Store input value into generator object. // Store input value into generator object.
__ StoreTaggedField( __ StoreTaggedField(
x0, FieldMemOperand(x1, JSGeneratorObject::kInputOrDebugPosOffset)); x0, FieldMemOperand(x1, JSGeneratorObject::kInputOrDebugPosOffset));
__ RecordWriteField(x1, JSGeneratorObject::kInputOrDebugPosOffset, x0, x3, __ RecordWriteField(x1, JSGeneratorObject::kInputOrDebugPosOffset, x0,
kLRHasNotBeenSaved, kDontSaveFPRegs); kLRHasNotBeenSaved, kDontSaveFPRegs);
// Load suspended function and context. // Load suspended function and context.
...@@ -957,14 +957,13 @@ void Builtins::Generate_RunMicrotasksTrampoline(MacroAssembler* masm) { ...@@ -957,14 +957,13 @@ void Builtins::Generate_RunMicrotasksTrampoline(MacroAssembler* masm) {
__ Jump(BUILTIN_CODE(masm->isolate(), RunMicrotasks), RelocInfo::CODE_TARGET); __ Jump(BUILTIN_CODE(masm->isolate(), RunMicrotasks), RelocInfo::CODE_TARGET);
} }
static void ReplaceClosureCodeWithOptimizedCode( static void ReplaceClosureCodeWithOptimizedCode(MacroAssembler* masm,
MacroAssembler* masm, Register optimized_code, Register closure, Register optimized_code,
Register scratch1, Register scratch2, Register scratch3) { Register closure) {
// Store code entry in the closure. // Store code entry in the closure.
__ StoreTaggedField(optimized_code, __ StoreTaggedField(optimized_code,
FieldMemOperand(closure, JSFunction::kCodeOffset)); FieldMemOperand(closure, JSFunction::kCodeOffset));
__ Mov(scratch1, optimized_code); // Write barrier clobbers scratch1 below. __ RecordWriteField(closure, JSFunction::kCodeOffset, optimized_code,
__ RecordWriteField(closure, JSFunction::kCodeOffset, scratch1, scratch2,
kLRHasNotBeenSaved, kDontSaveFPRegs, OMIT_REMEMBERED_SET, kLRHasNotBeenSaved, kDontSaveFPRegs, OMIT_REMEMBERED_SET,
OMIT_SMI_CHECK); OMIT_SMI_CHECK);
} }
...@@ -1003,14 +1002,14 @@ static void TailCallRuntimeIfMarkerEquals(MacroAssembler* masm, ...@@ -1003,14 +1002,14 @@ static void TailCallRuntimeIfMarkerEquals(MacroAssembler* masm,
static void MaybeTailCallOptimizedCodeSlot(MacroAssembler* masm, static void MaybeTailCallOptimizedCodeSlot(MacroAssembler* masm,
Register feedback_vector, Register feedback_vector,
Register scratch1, Register scratch2, Register scratch1,
Register scratch3) { Register scratch2) {
// ----------- S t a t e ------------- // ----------- S t a t e -------------
// -- x3 : new target (preserved for callee if needed, and caller) // -- x3 : new target (preserved for callee if needed, and caller)
// -- x1 : target function (preserved for callee if needed, and caller) // -- x1 : target function (preserved for callee if needed, and caller)
// -- feedback vector (preserved for caller if needed) // -- feedback vector (preserved for caller if needed)
// ----------------------------------- // -----------------------------------
DCHECK(!AreAliased(feedback_vector, x1, x3, scratch1, scratch2, scratch3)); DCHECK(!AreAliased(feedback_vector, x1, x3, scratch1, scratch2));
Label optimized_code_slot_is_weak_ref, fallthrough; Label optimized_code_slot_is_weak_ref, fallthrough;
...@@ -1084,8 +1083,7 @@ static void MaybeTailCallOptimizedCodeSlot(MacroAssembler* masm, ...@@ -1084,8 +1083,7 @@ static void MaybeTailCallOptimizedCodeSlot(MacroAssembler* masm,
// the optimized functions list, then tail call the optimized code. // the optimized functions list, then tail call the optimized code.
// The feedback vector is no longer used, so re-use it as a scratch // The feedback vector is no longer used, so re-use it as a scratch
// register. // register.
ReplaceClosureCodeWithOptimizedCode(masm, optimized_code_entry, closure, ReplaceClosureCodeWithOptimizedCode(masm, optimized_code_entry, closure);
scratch2, scratch3, feedback_vector);
static_assert(kJavaScriptCallCodeStartRegister == x2, "ABI mismatch"); static_assert(kJavaScriptCallCodeStartRegister == x2, "ABI mismatch");
__ LoadCodeObjectEntry(x2, optimized_code_entry); __ LoadCodeObjectEntry(x2, optimized_code_entry);
__ Jump(x2); __ Jump(x2);
...@@ -1207,7 +1205,7 @@ void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) { ...@@ -1207,7 +1205,7 @@ void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) {
// Read off the optimized code slot in the feedback vector, and if there // Read off the optimized code slot in the feedback vector, and if there
// is optimized code or an optimization marker, call that instead. // is optimized code or an optimization marker, call that instead.
MaybeTailCallOptimizedCodeSlot(masm, feedback_vector, x7, x4, x5); MaybeTailCallOptimizedCodeSlot(masm, feedback_vector, x7, x4);
// Increment invocation count for the function. // Increment invocation count for the function.
// MaybeTailCallOptimizedCodeSlot preserves feedback_vector, so safe to reuse // MaybeTailCallOptimizedCodeSlot preserves feedback_vector, so safe to reuse
......
...@@ -168,34 +168,14 @@ namespace { ...@@ -168,34 +168,14 @@ namespace {
class OutOfLineRecordWrite final : public OutOfLineCode { class OutOfLineRecordWrite final : public OutOfLineCode {
public: public:
OutOfLineRecordWrite(CodeGenerator* gen, Register object, Register index, OutOfLineRecordWrite(CodeGenerator* gen, Register object, Operand offset,
Register value, Register scratch0, Register scratch1, Register value, RecordWriteMode mode,
RecordWriteMode mode, StubCallMode stub_mode, StubCallMode stub_mode,
UnwindingInfoWriter* unwinding_info_writer) UnwindingInfoWriter* unwinding_info_writer)
: OutOfLineCode(gen), : OutOfLineCode(gen),
object_(object), object_(object),
index_(index), offset_(offset),
index_immediate_(0),
value_(value), value_(value),
scratch0_(scratch0),
scratch1_(scratch1),
mode_(mode),
stub_mode_(stub_mode),
must_save_lr_(!gen->frame_access_state()->has_frame()),
unwinding_info_writer_(unwinding_info_writer),
zone_(gen->zone()) {}
OutOfLineRecordWrite(CodeGenerator* gen, Register object, int32_t index,
Register value, Register scratch0, Register scratch1,
RecordWriteMode mode, StubCallMode stub_mode,
UnwindingInfoWriter* unwinding_info_writer)
: OutOfLineCode(gen),
object_(object),
index_(no_reg),
index_immediate_(index),
value_(value),
scratch0_(scratch0),
scratch1_(scratch1),
mode_(mode), mode_(mode),
stub_mode_(stub_mode), stub_mode_(stub_mode),
must_save_lr_(!gen->frame_access_state()->has_frame()), must_save_lr_(!gen->frame_access_state()->has_frame()),
...@@ -206,15 +186,8 @@ class OutOfLineRecordWrite final : public OutOfLineCode { ...@@ -206,15 +186,8 @@ class OutOfLineRecordWrite final : public OutOfLineCode {
if (mode_ > RecordWriteMode::kValueIsPointer) { if (mode_ > RecordWriteMode::kValueIsPointer) {
__ JumpIfSmi(value_, exit()); __ JumpIfSmi(value_, exit());
} }
__ CheckPageFlag(value_, scratch0_, __ CheckPageFlag(value_, MemoryChunk::kPointersToHereAreInterestingMask, eq,
MemoryChunk::kPointersToHereAreInterestingMask, eq,
exit()); exit());
if (index_ == no_reg) {
__ add(scratch1_, object_, Operand(index_immediate_));
} else {
DCHECK_EQ(0, index_immediate_);
__ add(scratch1_, object_, Operand(index_));
}
RememberedSetAction const remembered_set_action = RememberedSetAction const remembered_set_action =
mode_ > RecordWriteMode::kValueIsMap ? EMIT_REMEMBERED_SET mode_ > RecordWriteMode::kValueIsMap ? EMIT_REMEMBERED_SET
: OMIT_REMEMBERED_SET; : OMIT_REMEMBERED_SET;
...@@ -226,12 +199,12 @@ class OutOfLineRecordWrite final : public OutOfLineCode { ...@@ -226,12 +199,12 @@ class OutOfLineRecordWrite final : public OutOfLineCode {
unwinding_info_writer_->MarkLinkRegisterOnTopOfStack(__ pc_offset()); unwinding_info_writer_->MarkLinkRegisterOnTopOfStack(__ pc_offset());
} }
if (mode_ == RecordWriteMode::kValueIsEphemeronKey) { if (mode_ == RecordWriteMode::kValueIsEphemeronKey) {
__ CallEphemeronKeyBarrier(object_, scratch1_, save_fp_mode); __ CallEphemeronKeyBarrier(object_, offset_, save_fp_mode);
} else if (stub_mode_ == StubCallMode::kCallWasmRuntimeStub) { } else if (stub_mode_ == StubCallMode::kCallWasmRuntimeStub) {
__ CallRecordWriteStub(object_, scratch1_, remembered_set_action, __ CallRecordWriteStub(object_, offset_, remembered_set_action,
save_fp_mode, wasm::WasmCode::kWasmRecordWrite); save_fp_mode, wasm::WasmCode::kWasmRecordWrite);
} else { } else {
__ CallRecordWriteStub(object_, scratch1_, remembered_set_action, __ CallRecordWriteStub(object_, offset_, remembered_set_action,
save_fp_mode); save_fp_mode);
} }
if (must_save_lr_) { if (must_save_lr_) {
...@@ -242,11 +215,8 @@ class OutOfLineRecordWrite final : public OutOfLineCode { ...@@ -242,11 +215,8 @@ class OutOfLineRecordWrite final : public OutOfLineCode {
private: private:
Register const object_; Register const object_;
Register const index_; Operand const offset_;
int32_t const index_immediate_; // Valid if index_==no_reg.
Register const value_; Register const value_;
Register const scratch0_;
Register const scratch1_;
RecordWriteMode const mode_; RecordWriteMode const mode_;
StubCallMode stub_mode_; StubCallMode stub_mode_;
bool must_save_lr_; bool must_save_lr_;
...@@ -972,29 +942,25 @@ CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction( ...@@ -972,29 +942,25 @@ CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction(
static_cast<RecordWriteMode>(MiscField::decode(instr->opcode())); static_cast<RecordWriteMode>(MiscField::decode(instr->opcode()));
Register object = i.InputRegister(0); Register object = i.InputRegister(0);
Register value = i.InputRegister(2); Register value = i.InputRegister(2);
Register scratch0 = i.TempRegister(0);
Register scratch1 = i.TempRegister(1);
OutOfLineRecordWrite* ool;
AddressingMode addressing_mode = AddressingMode addressing_mode =
AddressingModeField::decode(instr->opcode()); AddressingModeField::decode(instr->opcode());
Operand offset(0);
if (addressing_mode == kMode_Offset_RI) { if (addressing_mode == kMode_Offset_RI) {
int32_t index = i.InputInt32(1); int32_t immediate = i.InputInt32(1);
ool = new (zone()) OutOfLineRecordWrite( offset = Operand(immediate);
this, object, index, value, scratch0, scratch1, mode, __ str(value, MemOperand(object, immediate));
DetermineStubCallMode(), &unwinding_info_writer_);
__ str(value, MemOperand(object, index));
} else { } else {
DCHECK_EQ(kMode_Offset_RR, addressing_mode); DCHECK_EQ(kMode_Offset_RR, addressing_mode);
Register index(i.InputRegister(1)); Register reg = i.InputRegister(1);
ool = new (zone()) OutOfLineRecordWrite( offset = Operand(reg);
this, object, index, value, scratch0, scratch1, mode, __ str(value, MemOperand(object, reg));
DetermineStubCallMode(), &unwinding_info_writer_);
__ str(value, MemOperand(object, index));
} }
__ CheckPageFlag(object, scratch0, auto ool = new (zone()) OutOfLineRecordWrite(
MemoryChunk::kPointersFromHereAreInterestingMask, ne, this, object, offset, value, mode, DetermineStubCallMode(),
ool->entry()); &unwinding_info_writer_);
__ CheckPageFlag(object, MemoryChunk::kPointersFromHereAreInterestingMask,
ne, ool->entry());
__ bind(ool->exit()); __ bind(ool->exit());
break; break;
} }
......
...@@ -528,12 +528,10 @@ void InstructionSelector::VisitStore(Node* node) { ...@@ -528,12 +528,10 @@ void InstructionSelector::VisitStore(Node* node) {
inputs[input_count++] = g.UseUniqueRegister(value); inputs[input_count++] = g.UseUniqueRegister(value);
RecordWriteMode record_write_mode = RecordWriteMode record_write_mode =
WriteBarrierKindToRecordWriteMode(write_barrier_kind); WriteBarrierKindToRecordWriteMode(write_barrier_kind);
InstructionOperand temps[] = {g.TempRegister(), g.TempRegister()};
size_t const temp_count = arraysize(temps);
InstructionCode code = kArchStoreWithWriteBarrier; InstructionCode code = kArchStoreWithWriteBarrier;
code |= AddressingModeField::encode(addressing_mode); code |= AddressingModeField::encode(addressing_mode);
code |= MiscField::encode(static_cast<int>(record_write_mode)); code |= MiscField::encode(static_cast<int>(record_write_mode));
Emit(code, 0, nullptr, input_count, inputs, temp_count, temps); Emit(code, 0, nullptr, input_count, inputs);
} else { } else {
InstructionCode opcode = kArchNop; InstructionCode opcode = kArchNop;
switch (rep) { switch (rep) {
......
...@@ -260,16 +260,14 @@ namespace { ...@@ -260,16 +260,14 @@ namespace {
class OutOfLineRecordWrite final : public OutOfLineCode { class OutOfLineRecordWrite final : public OutOfLineCode {
public: public:
OutOfLineRecordWrite(CodeGenerator* gen, Register object, Operand index, OutOfLineRecordWrite(CodeGenerator* gen, Register object, Operand offset,
Register value, Register scratch0, Register scratch1, Register value, RecordWriteMode mode,
RecordWriteMode mode, StubCallMode stub_mode, StubCallMode stub_mode,
UnwindingInfoWriter* unwinding_info_writer) UnwindingInfoWriter* unwinding_info_writer)
: OutOfLineCode(gen), : OutOfLineCode(gen),
object_(object), object_(object),
index_(index), offset_(offset),
value_(value), value_(value),
scratch0_(scratch0),
scratch1_(scratch1),
mode_(mode), mode_(mode),
stub_mode_(stub_mode), stub_mode_(stub_mode),
must_save_lr_(!gen->frame_access_state()->has_frame()), must_save_lr_(!gen->frame_access_state()->has_frame()),
...@@ -283,10 +281,8 @@ class OutOfLineRecordWrite final : public OutOfLineCode { ...@@ -283,10 +281,8 @@ class OutOfLineRecordWrite final : public OutOfLineCode {
if (COMPRESS_POINTERS_BOOL) { if (COMPRESS_POINTERS_BOOL) {
__ DecompressTaggedPointer(value_, value_); __ DecompressTaggedPointer(value_, value_);
} }
__ CheckPageFlag(value_, scratch0_, __ CheckPageFlag(value_, MemoryChunk::kPointersToHereAreInterestingMask, ne,
MemoryChunk::kPointersToHereAreInterestingMask, ne,
exit()); exit());
__ Add(scratch1_, object_, index_);
RememberedSetAction const remembered_set_action = RememberedSetAction const remembered_set_action =
mode_ > RecordWriteMode::kValueIsMap ? EMIT_REMEMBERED_SET mode_ > RecordWriteMode::kValueIsMap ? EMIT_REMEMBERED_SET
: OMIT_REMEMBERED_SET; : OMIT_REMEMBERED_SET;
...@@ -298,15 +294,15 @@ class OutOfLineRecordWrite final : public OutOfLineCode { ...@@ -298,15 +294,15 @@ class OutOfLineRecordWrite final : public OutOfLineCode {
unwinding_info_writer_->MarkLinkRegisterOnTopOfStack(__ pc_offset(), sp); unwinding_info_writer_->MarkLinkRegisterOnTopOfStack(__ pc_offset(), sp);
} }
if (mode_ == RecordWriteMode::kValueIsEphemeronKey) { if (mode_ == RecordWriteMode::kValueIsEphemeronKey) {
__ CallEphemeronKeyBarrier(object_, scratch1_, save_fp_mode); __ CallEphemeronKeyBarrier(object_, offset_, save_fp_mode);
} else if (stub_mode_ == StubCallMode::kCallWasmRuntimeStub) { } else if (stub_mode_ == StubCallMode::kCallWasmRuntimeStub) {
// 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, __ CallRecordWriteStub(object_, offset_, remembered_set_action,
save_fp_mode, wasm::WasmCode::kWasmRecordWrite); save_fp_mode, wasm::WasmCode::kWasmRecordWrite);
} else { } else {
__ CallRecordWriteStub(object_, scratch1_, remembered_set_action, __ CallRecordWriteStub(object_, offset_, remembered_set_action,
save_fp_mode); save_fp_mode);
} }
if (must_save_lr_) { if (must_save_lr_) {
...@@ -317,10 +313,8 @@ class OutOfLineRecordWrite final : public OutOfLineCode { ...@@ -317,10 +313,8 @@ class OutOfLineRecordWrite final : public OutOfLineCode {
private: private:
Register const object_; Register const object_;
Operand const index_; Operand const offset_;
Register const value_; Register const value_;
Register const scratch0_;
Register const scratch1_;
RecordWriteMode const mode_; RecordWriteMode const mode_;
StubCallMode const stub_mode_; StubCallMode const stub_mode_;
bool must_save_lr_; bool must_save_lr_;
...@@ -851,26 +845,23 @@ CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction( ...@@ -851,26 +845,23 @@ CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction(
AddressingMode addressing_mode = AddressingMode addressing_mode =
AddressingModeField::decode(instr->opcode()); AddressingModeField::decode(instr->opcode());
Register object = i.InputRegister(0); Register object = i.InputRegister(0);
Operand index(0); Operand offset(0);
if (addressing_mode == kMode_MRI) { if (addressing_mode == kMode_MRI) {
index = Operand(i.InputInt64(1)); offset = Operand(i.InputInt64(1));
} else { } else {
DCHECK_EQ(addressing_mode, kMode_MRR); DCHECK_EQ(addressing_mode, kMode_MRR);
index = Operand(i.InputRegister(1)); offset = Operand(i.InputRegister(1));
} }
Register value = i.InputRegister(2); Register value = i.InputRegister(2);
Register scratch0 = i.TempRegister(0);
Register scratch1 = i.TempRegister(1);
auto ool = new (zone()) OutOfLineRecordWrite( auto ool = new (zone()) OutOfLineRecordWrite(
this, object, index, value, scratch0, scratch1, mode, this, object, offset, value, mode, DetermineStubCallMode(),
DetermineStubCallMode(), &unwinding_info_writer_); &unwinding_info_writer_);
__ StoreTaggedField(value, MemOperand(object, index)); __ StoreTaggedField(value, MemOperand(object, offset));
if (COMPRESS_POINTERS_BOOL) { if (COMPRESS_POINTERS_BOOL) {
__ DecompressTaggedPointer(object, object); __ DecompressTaggedPointer(object, object);
} }
__ CheckPageFlag(object, scratch0, __ CheckPageFlag(object, MemoryChunk::kPointersFromHereAreInterestingMask,
MemoryChunk::kPointersFromHereAreInterestingMask, eq, eq, ool->entry());
ool->entry());
__ Bind(ool->exit()); __ Bind(ool->exit());
break; break;
} }
......
...@@ -706,12 +706,10 @@ void InstructionSelector::VisitStore(Node* node) { ...@@ -706,12 +706,10 @@ void InstructionSelector::VisitStore(Node* node) {
inputs[input_count++] = g.UseUniqueRegister(value); inputs[input_count++] = g.UseUniqueRegister(value);
RecordWriteMode record_write_mode = RecordWriteMode record_write_mode =
WriteBarrierKindToRecordWriteMode(write_barrier_kind); WriteBarrierKindToRecordWriteMode(write_barrier_kind);
InstructionOperand temps[] = {g.TempRegister(), g.TempRegister()};
size_t const temp_count = arraysize(temps);
InstructionCode code = kArchStoreWithWriteBarrier; InstructionCode code = kArchStoreWithWriteBarrier;
code |= AddressingModeField::encode(addressing_mode); code |= AddressingModeField::encode(addressing_mode);
code |= MiscField::encode(static_cast<int>(record_write_mode)); code |= MiscField::encode(static_cast<int>(record_write_mode));
Emit(code, 0, nullptr, input_count, inputs, temp_count, temps); Emit(code, 0, nullptr, input_count, inputs);
} else { } else {
InstructionOperand inputs[4]; InstructionOperand inputs[4];
size_t input_count = 0; size_t input_count = 0;
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include "src/arm/assembler-arm-inl.h" #include "src/arm/assembler-arm-inl.h"
#include "src/macro-assembler.h" #include "src/macro-assembler.h"
#include "src/ostreams.h"
#include "src/simulator.h" #include "src/simulator.h"
#include "test/common/assembler-tester.h" #include "test/common/assembler-tester.h"
#include "test/unittests/test-utils.h" #include "test/unittests/test-utils.h"
...@@ -69,6 +70,117 @@ TEST_F(TurboAssemblerTest, TestCheck) { ...@@ -69,6 +70,117 @@ TEST_F(TurboAssemblerTest, TestCheck) {
ASSERT_DEATH_IF_SUPPORTED({ f.Call(17); }, ERROR_MESSAGE("abort: no reason")); ASSERT_DEATH_IF_SUPPORTED({ f.Call(17); }, ERROR_MESSAGE("abort: no reason"));
} }
struct MoveObjectAndSlotTestCase {
const char* comment;
Register dst_object;
Register dst_slot;
Register object;
Register offset_register = no_reg;
};
const MoveObjectAndSlotTestCase kMoveObjectAndSlotTestCases[] = {
{"no overlap", r0, r1, r2},
{"no overlap", r0, r1, r2, r3},
{"object == dst_object", r2, r1, r2},
{"object == dst_object", r2, r1, r2, r3},
{"object == dst_slot", r1, r2, r2},
{"object == dst_slot", r1, r2, r2, r3},
{"offset == dst_object", r0, r1, r2, r0},
{"offset == dst_object && object == dst_slot", r0, r1, r1, r0},
{"offset == dst_slot", r0, r1, r2, r1},
{"offset == dst_slot && object == dst_object", r0, r1, r0, r1}};
// Make sure we include offsets that cannot be encoded in an add instruction.
const int kOffsets[] = {0, 42, kMaxRegularHeapObjectSize, 0x101001};
template <typename T>
class TurboAssemblerTestWithParam : public TurboAssemblerTest,
public ::testing::WithParamInterface<T> {};
using TurboAssemblerTestMoveObjectAndSlot =
TurboAssemblerTestWithParam<MoveObjectAndSlotTestCase>;
TEST_P(TurboAssemblerTestMoveObjectAndSlot, MoveObjectAndSlot) {
const MoveObjectAndSlotTestCase test_case = GetParam();
TRACED_FOREACH(int32_t, offset, kOffsets) {
auto buffer = AllocateAssemblerBuffer();
TurboAssembler tasm(nullptr, AssemblerOptions{}, CodeObjectRequired::kNo,
buffer->CreateView());
__ Push(r0);
__ Move(test_case.object, r1);
Register src_object = test_case.object;
Register dst_object = test_case.dst_object;
Register dst_slot = test_case.dst_slot;
Operand offset_operand(0);
if (test_case.offset_register == no_reg) {
offset_operand = Operand(offset);
} else {
__ mov(test_case.offset_register, Operand(offset));
offset_operand = Operand(test_case.offset_register);
}
std::stringstream comment;
comment << "-- " << test_case.comment << ": MoveObjectAndSlot("
<< dst_object << ", " << dst_slot << ", " << src_object << ", ";
if (test_case.offset_register == no_reg) {
comment << "#" << offset;
} else {
comment << test_case.offset_register;
}
comment << ") --";
__ RecordComment(comment.str().c_str());
__ MoveObjectAndSlot(dst_object, dst_slot, src_object, offset_operand);
__ RecordComment("--");
// The `result` pointer was saved on the stack.
UseScratchRegisterScope temps(&tasm);
Register scratch = temps.Acquire();
__ Pop(scratch);
__ str(dst_object, MemOperand(scratch));
__ str(dst_slot, MemOperand(scratch, kSystemPointerSize));
__ Ret();
CodeDesc desc;
tasm.GetCode(nullptr, &desc);
if (FLAG_print_code) {
Handle<Code> code =
Factory::CodeBuilder(isolate(), desc, Code::STUB).Build();
StdoutStream os;
code->Print(os);
}
buffer->MakeExecutable();
// We need an isolate here to execute in the simulator.
auto f = GeneratedCode<void, byte**, byte*>::FromBuffer(isolate(),
buffer->start());
byte* object = new byte[offset];
byte* result[] = {nullptr, nullptr};
f.Call(result, object);
// The first element must be the address of the object, and the second the
// slot addressed by `offset`.
EXPECT_EQ(result[0], &object[0]);
EXPECT_EQ(result[1], &object[offset]);
delete[] object;
}
}
INSTANTIATE_TEST_SUITE_P(TurboAssemblerTest,
TurboAssemblerTestMoveObjectAndSlot,
::testing::ValuesIn(kMoveObjectAndSlotTestCases));
#undef __ #undef __
#undef ERROR_MESSAGE #undef ERROR_MESSAGE
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include "src/arm64/macro-assembler-arm64-inl.h" #include "src/arm64/macro-assembler-arm64-inl.h"
#include "src/macro-assembler.h" #include "src/macro-assembler.h"
#include "src/ostreams.h"
#include "src/simulator.h" #include "src/simulator.h"
#include "test/common/assembler-tester.h" #include "test/common/assembler-tester.h"
#include "test/unittests/test-utils.h" #include "test/unittests/test-utils.h"
...@@ -69,6 +70,118 @@ TEST_F(TurboAssemblerTest, TestCheck) { ...@@ -69,6 +70,118 @@ TEST_F(TurboAssemblerTest, TestCheck) {
ASSERT_DEATH_IF_SUPPORTED({ f.Call(17); }, ERROR_MESSAGE("abort: no reason")); ASSERT_DEATH_IF_SUPPORTED({ f.Call(17); }, ERROR_MESSAGE("abort: no reason"));
} }
struct MoveObjectAndSlotTestCase {
const char* comment;
Register dst_object;
Register dst_slot;
Register object;
Register offset_register = no_reg;
};
const MoveObjectAndSlotTestCase kMoveObjectAndSlotTestCases[] = {
{"no overlap", x0, x1, x2},
{"no overlap", x0, x1, x2, x3},
{"object == dst_object", x2, x1, x2},
{"object == dst_object", x2, x1, x2, x3},
{"object == dst_slot", x1, x2, x2},
{"object == dst_slot", x1, x2, x2, x3},
{"offset == dst_object", x0, x1, x2, x0},
{"offset == dst_object && object == dst_slot", x0, x1, x1, x0},
{"offset == dst_slot", x0, x1, x2, x1},
{"offset == dst_slot && object == dst_object", x0, x1, x0, x1}};
// Make sure we include offsets that cannot be encoded in an add instruction.
const int kOffsets[] = {0, 42, kMaxRegularHeapObjectSize, 0x101001};
template <typename T>
class TurboAssemblerTestWithParam : public TurboAssemblerTest,
public ::testing::WithParamInterface<T> {};
using TurboAssemblerTestMoveObjectAndSlot =
TurboAssemblerTestWithParam<MoveObjectAndSlotTestCase>;
TEST_P(TurboAssemblerTestMoveObjectAndSlot, MoveObjectAndSlot) {
const MoveObjectAndSlotTestCase test_case = GetParam();
TRACED_FOREACH(int32_t, offset, kOffsets) {
auto buffer = AllocateAssemblerBuffer();
TurboAssembler tasm(nullptr, AssemblerOptions{}, CodeObjectRequired::kNo,
buffer->CreateView());
__ Push(x0, padreg);
__ Mov(test_case.object, x1);
Register src_object = test_case.object;
Register dst_object = test_case.dst_object;
Register dst_slot = test_case.dst_slot;
Operand offset_operand(0);
if (test_case.offset_register.Is(no_reg)) {
offset_operand = Operand(offset);
} else {
__ Mov(test_case.offset_register, Operand(offset));
offset_operand = Operand(test_case.offset_register);
}
std::stringstream comment;
comment << "-- " << test_case.comment << ": MoveObjectAndSlot("
<< dst_object << ", " << dst_slot << ", " << src_object << ", ";
if (test_case.offset_register.Is(no_reg)) {
comment << "#" << offset;
} else {
comment << test_case.offset_register;
}
comment << ") --";
__ RecordComment(comment.str().c_str());
__ MoveObjectAndSlot(dst_object, dst_slot, src_object, offset_operand);
__ RecordComment("--");
// The `result` pointer was saved on the stack.
UseScratchRegisterScope temps(&tasm);
Register scratch = temps.AcquireX();
__ Pop(padreg, scratch);
__ Str(dst_object, MemOperand(scratch));
__ Str(dst_slot, MemOperand(scratch, kSystemPointerSize));
__ Ret();
CodeDesc desc;
tasm.GetCode(nullptr, &desc);
if (FLAG_print_code) {
Handle<Code> code =
Factory::CodeBuilder(isolate(), desc, Code::STUB).Build();
StdoutStream os;
code->Print(os);
}
buffer->MakeExecutable();
// We need an isolate here to execute in the simulator.
auto f = GeneratedCode<void, byte**, byte*>::FromBuffer(isolate(),
buffer->start());
byte* object = new byte[offset];
byte* result[] = {nullptr, nullptr};
f.Call(result, object);
// The first element must be the address of the object, and the second the
// slot addressed by `offset`.
EXPECT_EQ(result[0], &object[0]);
EXPECT_EQ(result[1], &object[offset]);
delete[] object;
}
}
INSTANTIATE_TEST_SUITE_P(TurboAssemblerTest,
TurboAssemblerTestMoveObjectAndSlot,
::testing::ValuesIn(kMoveObjectAndSlotTestCases));
#undef __ #undef __
#undef ERROR_MESSAGE #undef ERROR_MESSAGE
......
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