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,
MemOperand(kRootRegister, RootRegisterOffsetForRootIndex(index)), cond);
}
void MacroAssembler::RecordWriteField(Register object, int offset,
Register value, Register dst,
Register value,
LinkRegisterStatus lr_status,
SaveFPRegsMode save_fp,
RememberedSetAction remembered_set_action,
......@@ -630,26 +629,21 @@ void MacroAssembler::RecordWriteField(Register object, int offset,
// of the object, so so offset must be a multiple of kPointerSize.
DCHECK(IsAligned(offset, kPointerSize));
add(dst, object, Operand(offset - kHeapObjectTag));
if (emit_debug_code()) {
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);
stop("Unaligned cell in write barrier");
bind(&ok);
}
RecordWrite(object, dst, value, lr_status, save_fp, remembered_set_action,
OMIT_SMI_CHECK);
RecordWrite(object, Operand(offset - kHeapObjectTag), value, lr_status,
save_fp, remembered_set_action, OMIT_SMI_CHECK);
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) {
......@@ -675,7 +669,7 @@ void TurboAssembler::RestoreRegisters(RegList registers) {
ldm(ia_w, sp, regs);
}
void TurboAssembler::CallEphemeronKeyBarrier(Register object, Register address,
void TurboAssembler::CallEphemeronKeyBarrier(Register object, Operand offset,
SaveFPRegsMode fp_mode) {
EphemeronKeyBarrierDescriptor descriptor;
RegList registers = descriptor.allocatable_registers();
......@@ -689,7 +683,7 @@ void TurboAssembler::CallEphemeronKeyBarrier(Register object, Register address,
Register fp_mode_parameter(
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));
Call(isolate()->builtins()->builtin_handle(Builtins::kEphemeronKeyBarrier),
RelocInfo::CODE_TARGET);
......@@ -697,26 +691,24 @@ void TurboAssembler::CallEphemeronKeyBarrier(Register object, Register address,
}
void TurboAssembler::CallRecordWriteStub(
Register object, Register address,
RememberedSetAction remembered_set_action, SaveFPRegsMode fp_mode) {
Register object, Operand offset, RememberedSetAction remembered_set_action,
SaveFPRegsMode fp_mode) {
CallRecordWriteStub(
object, address, remembered_set_action, fp_mode,
object, offset, remembered_set_action, fp_mode,
isolate()->builtins()->builtin_handle(Builtins::kRecordWrite),
kNullAddress);
}
void TurboAssembler::CallRecordWriteStub(
Register object, Register address,
RememberedSetAction remembered_set_action, SaveFPRegsMode fp_mode,
Address wasm_target) {
CallRecordWriteStub(object, address, remembered_set_action, fp_mode,
Register object, Operand offset, RememberedSetAction remembered_set_action,
SaveFPRegsMode fp_mode, Address wasm_target) {
CallRecordWriteStub(object, offset, remembered_set_action, fp_mode,
Handle<Code>::null(), wasm_target);
}
void TurboAssembler::CallRecordWriteStub(
Register object, Register address,
RememberedSetAction remembered_set_action, SaveFPRegsMode fp_mode,
Handle<Code> code_target, Address wasm_target) {
Register object, Operand offset, RememberedSetAction remembered_set_action,
SaveFPRegsMode fp_mode, Handle<Code> code_target, Address wasm_target) {
DCHECK_NE(code_target.is_null(), wasm_target == kNullAddress);
// 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
......@@ -737,7 +729,7 @@ void TurboAssembler::CallRecordWriteStub(
Register fp_mode_parameter(
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(fp_mode_parameter, Smi::FromEnum(fp_mode));
......@@ -750,20 +742,54 @@ void TurboAssembler::CallRecordWriteStub(
RestoreRegisters(registers);
}
// Will clobber 3 registers: object, address, and value. 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, Register address,
void TurboAssembler::MoveObjectAndSlot(Register dst_object, Register dst_slot,
Register object, Operand offset) {
DCHECK_NE(dst_object, dst_slot);
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,
SaveFPRegsMode fp_mode,
RememberedSetAction remembered_set_action,
SmiCheck smi_check) {
DCHECK(object != value);
DCHECK_NE(object, value);
if (emit_debug_code()) {
{
UseScratchRegisterScope temps(this);
Register scratch = temps.Acquire();
ldr(scratch, MemOperand(address));
add(scratch, object, offset);
ldr(scratch, MemOperand(scratch));
cmp(scratch, value);
}
Check(eq, AbortReason::kWrongAddressOrValuePassedToRecordWrite);
......@@ -782,32 +808,21 @@ void MacroAssembler::RecordWrite(Register object, Register address,
JumpIfSmi(value, &done);
}
CheckPageFlag(value,
value, // Used as scratch.
MemoryChunk::kPointersToHereAreInterestingMask, eq, &done);
CheckPageFlag(object,
value, // Used as scratch.
MemoryChunk::kPointersFromHereAreInterestingMask,
eq,
CheckPageFlag(value, MemoryChunk::kPointersToHereAreInterestingMask, eq,
&done);
CheckPageFlag(object, MemoryChunk::kPointersFromHereAreInterestingMask, eq,
&done);
// Record the actual write.
if (lr_status == kLRHasNotBeenSaved) {
push(lr);
}
CallRecordWriteStub(object, address, remembered_set_action, fp_mode);
CallRecordWriteStub(object, offset, remembered_set_action, fp_mode);
if (lr_status == kLRHasNotBeenSaved) {
pop(lr);
}
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) {
......@@ -2458,8 +2473,10 @@ void TurboAssembler::CallCFunctionHelper(Register function,
}
}
void TurboAssembler::CheckPageFlag(Register object, Register scratch, int mask,
Condition cc, Label* condition_met) {
void TurboAssembler::CheckPageFlag(Register object, int mask, Condition cc,
Label* condition_met) {
UseScratchRegisterScope temps(this);
Register scratch = temps.Acquire();
DCHECK(cc == eq || cc == ne);
Bfc(scratch, object, 0, kPageSizeBits);
ldr(scratch, MemOperand(scratch, MemoryChunk::kFlagsOffset));
......
......@@ -337,7 +337,7 @@ class V8_EXPORT_PRIVATE TurboAssembler : public TurboAssemblerBase {
void VmovLow(Register dst, DwVfpRegister 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);
// 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 {
void SaveRegisters(RegList registers);
void RestoreRegisters(RegList registers);
void CallRecordWriteStub(Register object, Register address,
void CallRecordWriteStub(Register object, Operand offset,
RememberedSetAction remembered_set_action,
SaveFPRegsMode fp_mode);
void CallRecordWriteStub(Register object, Register address,
void CallRecordWriteStub(Register object, Operand offset,
RememberedSetAction remembered_set_action,
SaveFPRegsMode fp_mode, Address wasm_target);
void CallEphemeronKeyBarrier(Register object, Register address,
void CallEphemeronKeyBarrier(Register object, Operand offset,
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
// values to location, saving [d0..(d15|d31)].
void SaveFPRegs(Register location, Register scratch);
......@@ -541,7 +551,7 @@ class V8_EXPORT_PRIVATE TurboAssembler : public TurboAssemblerBase {
void CallCFunctionHelper(Register function, int num_reg_arguments,
int num_double_arguments);
void CallRecordWriteStub(Register object, Register address,
void CallRecordWriteStub(Register object, Operand offset,
RememberedSetAction remembered_set_action,
SaveFPRegsMode fp_mode, Handle<Code> code_target,
Address wasm_target);
......@@ -566,20 +576,19 @@ class V8_EXPORT_PRIVATE MacroAssembler : public TurboAssembler {
// Notify the garbage collector that we wrote a pointer into an object.
// |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 tagged HeapObject pointer. For use with FieldMemOperand(reg, off).
void RecordWriteField(
Register object, int offset, Register value, Register scratch,
LinkRegisterStatus lr_status, SaveFPRegsMode save_fp,
Register object, int offset, Register value, LinkRegisterStatus lr_status,
SaveFPRegsMode save_fp,
RememberedSetAction remembered_set_action = EMIT_REMEMBERED_SET,
SmiCheck smi_check = INLINE_SMI_CHECK);
// For a given |object| notify the garbage collector that the slot |address|
// has been written. |value| is the object being stored. The value and
// address registers are clobbered by the operation.
// For a given |object| notify the garbage collector that the slot at |offset|
// has been written. |value| is the object being stored.
void RecordWrite(
Register object, Register address, Register value,
Register object, Operand offset, Register value,
LinkRegisterStatus lr_status, SaveFPRegsMode save_fp,
RememberedSetAction remembered_set_action = EMIT_REMEMBERED_SET,
SmiCheck smi_check = INLINE_SMI_CHECK);
......
......@@ -2931,9 +2931,10 @@ int MacroAssembler::SafepointRegisterStackIndex(int reg_code) {
}
}
void TurboAssembler::CheckPageFlag(const Register& object,
const Register& scratch, int mask,
void TurboAssembler::CheckPageFlag(const Register& object, int mask,
Condition cc, Label* condition_met) {
UseScratchRegisterScope temps(this);
Register scratch = temps.AcquireX();
And(scratch, object, ~kPageAlignmentMask);
Ldr(scratch, MemOperand(scratch, MemoryChunk::kFlagsOffset));
if (cc == eq) {
......@@ -2945,7 +2946,7 @@ void TurboAssembler::CheckPageFlag(const Register& object,
}
void MacroAssembler::RecordWriteField(Register object, int offset,
Register value, Register scratch,
Register value,
LinkRegisterStatus lr_status,
SaveFPRegsMode save_fp,
RememberedSetAction remembered_set_action,
......@@ -2963,26 +2964,21 @@ void MacroAssembler::RecordWriteField(Register object, int offset,
// of the object, so offset must be a multiple of kTaggedSize.
DCHECK(IsAligned(offset, kTaggedSize));
Add(scratch, object, offset - kHeapObjectTag);
if (emit_debug_code()) {
Label ok;
UseScratchRegisterScope temps(this);
Register scratch = temps.AcquireX();
Add(scratch, object, offset - kHeapObjectTag);
Tst(scratch, kTaggedSize - 1);
B(eq, &ok);
Abort(AbortReason::kUnalignedCellInWriteBarrier);
Bind(&ok);
}
RecordWrite(object, scratch, value, lr_status, save_fp, remembered_set_action,
OMIT_SMI_CHECK);
RecordWrite(object, Operand(offset - kHeapObjectTag), value, lr_status,
save_fp, remembered_set_action, OMIT_SMI_CHECK);
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) {
......@@ -3009,7 +3005,7 @@ void TurboAssembler::RestoreRegisters(RegList registers) {
PopCPURegList(regs);
}
void TurboAssembler::CallEphemeronKeyBarrier(Register object, Register address,
void TurboAssembler::CallEphemeronKeyBarrier(Register object, Operand offset,
SaveFPRegsMode fp_mode) {
EphemeronKeyBarrierDescriptor descriptor;
RegList registers = descriptor.allocatable_registers();
......@@ -3023,7 +3019,7 @@ void TurboAssembler::CallEphemeronKeyBarrier(Register object, Register address,
Register fp_mode_parameter(
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));
Call(isolate()->builtins()->builtin_handle(Builtins::kEphemeronKeyBarrier),
......@@ -3032,26 +3028,24 @@ void TurboAssembler::CallEphemeronKeyBarrier(Register object, Register address,
}
void TurboAssembler::CallRecordWriteStub(
Register object, Register address,
RememberedSetAction remembered_set_action, SaveFPRegsMode fp_mode) {
Register object, Operand offset, RememberedSetAction remembered_set_action,
SaveFPRegsMode fp_mode) {
CallRecordWriteStub(
object, address, remembered_set_action, fp_mode,
object, offset, remembered_set_action, fp_mode,
isolate()->builtins()->builtin_handle(Builtins::kRecordWrite),
kNullAddress);
}
void TurboAssembler::CallRecordWriteStub(
Register object, Register address,
RememberedSetAction remembered_set_action, SaveFPRegsMode fp_mode,
Address wasm_target) {
CallRecordWriteStub(object, address, remembered_set_action, fp_mode,
Register object, Operand offset, RememberedSetAction remembered_set_action,
SaveFPRegsMode fp_mode, Address wasm_target) {
CallRecordWriteStub(object, offset, remembered_set_action, fp_mode,
Handle<Code>::null(), wasm_target);
}
void TurboAssembler::CallRecordWriteStub(
Register object, Register address,
RememberedSetAction remembered_set_action, SaveFPRegsMode fp_mode,
Handle<Code> code_target, Address wasm_target) {
Register object, Operand offset, RememberedSetAction remembered_set_action,
SaveFPRegsMode fp_mode, Handle<Code> code_target, Address wasm_target) {
DCHECK_NE(code_target.is_null(), wasm_target == kNullAddress);
// 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
......@@ -3072,7 +3066,7 @@ void TurboAssembler::CallRecordWriteStub(
Register fp_mode_parameter(
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(fp_mode_parameter, Smi::FromEnum(fp_mode));
......@@ -3085,12 +3079,44 @@ void TurboAssembler::CallRecordWriteStub(
RestoreRegisters(registers);
}
// Will clobber: object, address, value.
// If lr_status is kLRHasBeenSaved, lr will also be clobbered.
void TurboAssembler::MoveObjectAndSlot(Register dst_object, Register dst_slot,
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
// shifted away.
void MacroAssembler::RecordWrite(Register object, Register address,
void MacroAssembler::RecordWrite(Register object, Operand offset,
Register value, LinkRegisterStatus lr_status,
SaveFPRegsMode fp_mode,
RememberedSetAction remembered_set_action,
......@@ -3102,7 +3128,8 @@ void MacroAssembler::RecordWrite(Register object, Register address,
UseScratchRegisterScope temps(this);
Register temp = temps.AcquireX();
LoadTaggedPointerField(temp, MemOperand(address));
Add(temp, object, offset);
LoadTaggedPointerField(temp, MemOperand(temp));
Cmp(temp, value);
Check(eq, AbortReason::kWrongAddressOrValuePassedToRecordWrite);
}
......@@ -3115,31 +3142,22 @@ void MacroAssembler::RecordWrite(Register object, Register address,
DCHECK_EQ(0, kSmiTag);
JumpIfSmi(value, &done);
}
CheckPageFlag(value,
value, // Used as scratch.
MemoryChunk::kPointersToHereAreInterestingMask, ne, &done);
CheckPageFlag(value, MemoryChunk::kPointersToHereAreInterestingMask, ne,
&done);
CheckPageFlag(object,
value, // Used as scratch.
MemoryChunk::kPointersFromHereAreInterestingMask, ne, &done);
CheckPageFlag(object, MemoryChunk::kPointersFromHereAreInterestingMask, ne,
&done);
// Record the actual write.
if (lr_status == kLRHasNotBeenSaved) {
Push(padreg, lr);
}
CallRecordWriteStub(object, address, remembered_set_action, fp_mode);
CallRecordWriteStub(object, offset, remembered_set_action, fp_mode);
if (lr_status == kLRHasNotBeenSaved) {
Pop(lr, padreg);
}
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) {
......
......@@ -740,15 +740,24 @@ class V8_EXPORT_PRIVATE TurboAssembler : public TurboAssemblerBase {
void SaveRegisters(RegList registers);
void RestoreRegisters(RegList registers);
void CallRecordWriteStub(Register object, Register address,
void CallRecordWriteStub(Register object, Operand offset,
RememberedSetAction remembered_set_action,
SaveFPRegsMode fp_mode);
void CallRecordWriteStub(Register object, Register address,
void CallRecordWriteStub(Register object, Operand offset,
RememberedSetAction remembered_set_action,
SaveFPRegsMode fp_mode, Address wasm_target);
void CallEphemeronKeyBarrier(Register object, Register address,
void CallEphemeronKeyBarrier(Register object, Operand offset,
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
// specifies the registers that are to be pushed or popped. Higher-numbered
// registers are associated with higher memory addresses (as in the A32 push
......@@ -783,8 +792,8 @@ class V8_EXPORT_PRIVATE TurboAssembler : public TurboAssemblerBase {
Operand MoveImmediateForShiftedOp(const Register& dst, int64_t imm,
PreShiftImmMode mode);
void CheckPageFlag(const Register& object, const Register& scratch, int mask,
Condition cc, Label* condition_met);
void CheckPageFlag(const Register& object, int mask, Condition cc,
Label* condition_met);
// Test the bits of register defined by bit_pattern, and branch if ANY of
// those bits are set. May corrupt the status flags.
......@@ -1273,7 +1282,7 @@ class V8_EXPORT_PRIVATE TurboAssembler : public TurboAssemblerBase {
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,
SaveFPRegsMode fp_mode, Handle<Code> code_target,
Address wasm_target);
......@@ -1929,20 +1938,19 @@ class V8_EXPORT_PRIVATE MacroAssembler : public TurboAssembler {
// Notify the garbage collector that we wrote a pointer into an object.
// |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 tagged HeapObject pointer. For use with FieldMemOperand(reg, off).
void RecordWriteField(
Register object, int offset, Register value, Register scratch,
LinkRegisterStatus lr_status, SaveFPRegsMode save_fp,
Register object, int offset, Register value, LinkRegisterStatus lr_status,
SaveFPRegsMode save_fp,
RememberedSetAction remembered_set_action = EMIT_REMEMBERED_SET,
SmiCheck smi_check = INLINE_SMI_CHECK);
// For a given |object| notify the garbage collector that the slot |address|
// has been written. |value| is the object being stored. The value and
// address registers are clobbered by the operation.
// For a given |object| notify the garbage collector that the slot at |offset|
// has been written. |value| is the object being stored.
void RecordWrite(
Register object, Register address, Register value,
Register object, Operand offset, Register value,
LinkRegisterStatus lr_status, SaveFPRegsMode save_fp,
RememberedSetAction remembered_set_action = EMIT_REMEMBERED_SET,
SmiCheck smi_check = INLINE_SMI_CHECK);
......
......@@ -403,7 +403,7 @@ void Builtins::Generate_ResumeGeneratorTrampoline(MacroAssembler* masm) {
// Store input value into generator object.
__ str(r0, FieldMemOperand(r1, JSGeneratorObject::kInputOrDebugPosOffset));
__ RecordWriteField(r1, JSGeneratorObject::kInputOrDebugPosOffset, r0, r3,
__ RecordWriteField(r1, JSGeneratorObject::kInputOrDebugPosOffset, r0,
kLRHasNotBeenSaved, kDontSaveFPRegs);
// Load suspended function and context.
......@@ -841,13 +841,12 @@ void Builtins::Generate_RunMicrotasksTrampoline(MacroAssembler* masm) {
__ Jump(BUILTIN_CODE(masm->isolate(), RunMicrotasks), RelocInfo::CODE_TARGET);
}
static void ReplaceClosureCodeWithOptimizedCode(
MacroAssembler* masm, Register optimized_code, Register closure,
Register scratch1, Register scratch2, Register scratch3) {
static void ReplaceClosureCodeWithOptimizedCode(MacroAssembler* masm,
Register optimized_code,
Register closure) {
// Store code entry in the closure.
__ str(optimized_code, FieldMemOperand(closure, JSFunction::kCodeOffset));
__ mov(scratch1, optimized_code); // Write barrier clobbers scratch1 below.
__ RecordWriteField(closure, JSFunction::kCodeOffset, scratch1, scratch2,
__ RecordWriteField(closure, JSFunction::kCodeOffset, optimized_code,
kLRHasNotBeenSaved, kDontSaveFPRegs, OMIT_REMEMBERED_SET,
OMIT_SMI_CHECK);
}
......@@ -882,14 +881,14 @@ static void TailCallRuntimeIfMarkerEquals(MacroAssembler* masm,
static void MaybeTailCallOptimizedCodeSlot(MacroAssembler* masm,
Register feedback_vector,
Register scratch1, Register scratch2,
Register scratch3) {
Register scratch1,
Register scratch2) {
// ----------- S t a t e -------------
// -- r3 : new target (preserved for callee if needed, and caller)
// -- r1 : target function (preserved for callee if needed, and caller)
// -- 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;
......@@ -962,8 +961,7 @@ static void MaybeTailCallOptimizedCodeSlot(MacroAssembler* masm,
// the optimized functions list, then tail call the optimized code.
// The feedback vector is no longer used, so re-use it as a scratch
// register.
ReplaceClosureCodeWithOptimizedCode(masm, optimized_code_entry, closure,
scratch2, scratch3, feedback_vector);
ReplaceClosureCodeWithOptimizedCode(masm, optimized_code_entry, closure);
static_assert(kJavaScriptCallCodeStartRegister == r2, "ABI mismatch");
__ LoadCodeObjectEntry(r2, optimized_code_entry);
__ Jump(r2);
......@@ -1082,7 +1080,7 @@ void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) {
// Read off the optimized code slot in the feedback vector, and if there
// 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.
__ ldr(r9, FieldMemOperand(feedback_vector,
......
......@@ -473,7 +473,7 @@ void Builtins::Generate_ResumeGeneratorTrampoline(MacroAssembler* masm) {
// Store input value into generator object.
__ StoreTaggedField(
x0, FieldMemOperand(x1, JSGeneratorObject::kInputOrDebugPosOffset));
__ RecordWriteField(x1, JSGeneratorObject::kInputOrDebugPosOffset, x0, x3,
__ RecordWriteField(x1, JSGeneratorObject::kInputOrDebugPosOffset, x0,
kLRHasNotBeenSaved, kDontSaveFPRegs);
// Load suspended function and context.
......@@ -957,14 +957,13 @@ void Builtins::Generate_RunMicrotasksTrampoline(MacroAssembler* masm) {
__ Jump(BUILTIN_CODE(masm->isolate(), RunMicrotasks), RelocInfo::CODE_TARGET);
}
static void ReplaceClosureCodeWithOptimizedCode(
MacroAssembler* masm, Register optimized_code, Register closure,
Register scratch1, Register scratch2, Register scratch3) {
static void ReplaceClosureCodeWithOptimizedCode(MacroAssembler* masm,
Register optimized_code,
Register closure) {
// Store code entry in the closure.
__ StoreTaggedField(optimized_code,
FieldMemOperand(closure, JSFunction::kCodeOffset));
__ Mov(scratch1, optimized_code); // Write barrier clobbers scratch1 below.
__ RecordWriteField(closure, JSFunction::kCodeOffset, scratch1, scratch2,
__ RecordWriteField(closure, JSFunction::kCodeOffset, optimized_code,
kLRHasNotBeenSaved, kDontSaveFPRegs, OMIT_REMEMBERED_SET,
OMIT_SMI_CHECK);
}
......@@ -1003,14 +1002,14 @@ static void TailCallRuntimeIfMarkerEquals(MacroAssembler* masm,
static void MaybeTailCallOptimizedCodeSlot(MacroAssembler* masm,
Register feedback_vector,
Register scratch1, Register scratch2,
Register scratch3) {
Register scratch1,
Register scratch2) {
// ----------- S t a t e -------------
// -- x3 : new target (preserved for callee if needed, and caller)
// -- x1 : target function (preserved for callee if needed, and caller)
// -- 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;
......@@ -1084,8 +1083,7 @@ static void MaybeTailCallOptimizedCodeSlot(MacroAssembler* masm,
// the optimized functions list, then tail call the optimized code.
// The feedback vector is no longer used, so re-use it as a scratch
// register.
ReplaceClosureCodeWithOptimizedCode(masm, optimized_code_entry, closure,
scratch2, scratch3, feedback_vector);
ReplaceClosureCodeWithOptimizedCode(masm, optimized_code_entry, closure);
static_assert(kJavaScriptCallCodeStartRegister == x2, "ABI mismatch");
__ LoadCodeObjectEntry(x2, optimized_code_entry);
__ Jump(x2);
......@@ -1207,7 +1205,7 @@ void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) {
// Read off the optimized code slot in the feedback vector, and if there
// 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.
// MaybeTailCallOptimizedCodeSlot preserves feedback_vector, so safe to reuse
......
......@@ -168,34 +168,14 @@ namespace {
class OutOfLineRecordWrite final : public OutOfLineCode {
public:
OutOfLineRecordWrite(CodeGenerator* gen, Register object, Register index,
Register value, Register scratch0, Register scratch1,
RecordWriteMode mode, StubCallMode stub_mode,
OutOfLineRecordWrite(CodeGenerator* gen, Register object, Operand offset,
Register value, RecordWriteMode mode,
StubCallMode stub_mode,
UnwindingInfoWriter* unwinding_info_writer)
: OutOfLineCode(gen),
object_(object),
index_(index),
index_immediate_(0),
offset_(offset),
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),
stub_mode_(stub_mode),
must_save_lr_(!gen->frame_access_state()->has_frame()),
......@@ -206,15 +186,8 @@ class OutOfLineRecordWrite final : public OutOfLineCode {
if (mode_ > RecordWriteMode::kValueIsPointer) {
__ JumpIfSmi(value_, exit());
}
__ CheckPageFlag(value_, scratch0_,
MemoryChunk::kPointersToHereAreInterestingMask, eq,
__ CheckPageFlag(value_, MemoryChunk::kPointersToHereAreInterestingMask, eq,
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 =
mode_ > RecordWriteMode::kValueIsMap ? EMIT_REMEMBERED_SET
: OMIT_REMEMBERED_SET;
......@@ -226,12 +199,12 @@ class OutOfLineRecordWrite final : public OutOfLineCode {
unwinding_info_writer_->MarkLinkRegisterOnTopOfStack(__ pc_offset());
}
if (mode_ == RecordWriteMode::kValueIsEphemeronKey) {
__ CallEphemeronKeyBarrier(object_, scratch1_, save_fp_mode);
__ CallEphemeronKeyBarrier(object_, offset_, save_fp_mode);
} else if (stub_mode_ == StubCallMode::kCallWasmRuntimeStub) {
__ CallRecordWriteStub(object_, scratch1_, remembered_set_action,
__ CallRecordWriteStub(object_, offset_, remembered_set_action,
save_fp_mode, wasm::WasmCode::kWasmRecordWrite);
} else {
__ CallRecordWriteStub(object_, scratch1_, remembered_set_action,
__ CallRecordWriteStub(object_, offset_, remembered_set_action,
save_fp_mode);
}
if (must_save_lr_) {
......@@ -242,11 +215,8 @@ class OutOfLineRecordWrite final : public OutOfLineCode {
private:
Register const object_;
Register const index_;
int32_t const index_immediate_; // Valid if index_==no_reg.
Operand const offset_;
Register const value_;
Register const scratch0_;
Register const scratch1_;
RecordWriteMode const mode_;
StubCallMode stub_mode_;
bool must_save_lr_;
......@@ -972,29 +942,25 @@ CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction(
static_cast<RecordWriteMode>(MiscField::decode(instr->opcode()));
Register object = i.InputRegister(0);
Register value = i.InputRegister(2);
Register scratch0 = i.TempRegister(0);
Register scratch1 = i.TempRegister(1);
OutOfLineRecordWrite* ool;
AddressingMode addressing_mode =
AddressingModeField::decode(instr->opcode());
Operand offset(0);
if (addressing_mode == kMode_Offset_RI) {
int32_t index = i.InputInt32(1);
ool = new (zone()) OutOfLineRecordWrite(
this, object, index, value, scratch0, scratch1, mode,
DetermineStubCallMode(), &unwinding_info_writer_);
__ str(value, MemOperand(object, index));
int32_t immediate = i.InputInt32(1);
offset = Operand(immediate);
__ str(value, MemOperand(object, immediate));
} else {
DCHECK_EQ(kMode_Offset_RR, addressing_mode);
Register index(i.InputRegister(1));
ool = new (zone()) OutOfLineRecordWrite(
this, object, index, value, scratch0, scratch1, mode,
DetermineStubCallMode(), &unwinding_info_writer_);
__ str(value, MemOperand(object, index));
Register reg = i.InputRegister(1);
offset = Operand(reg);
__ str(value, MemOperand(object, reg));
}
__ CheckPageFlag(object, scratch0,
MemoryChunk::kPointersFromHereAreInterestingMask, ne,
ool->entry());
auto ool = new (zone()) OutOfLineRecordWrite(
this, object, offset, value, mode, DetermineStubCallMode(),
&unwinding_info_writer_);
__ CheckPageFlag(object, MemoryChunk::kPointersFromHereAreInterestingMask,
ne, ool->entry());
__ bind(ool->exit());
break;
}
......
......@@ -528,12 +528,10 @@ void InstructionSelector::VisitStore(Node* node) {
inputs[input_count++] = g.UseUniqueRegister(value);
RecordWriteMode record_write_mode =
WriteBarrierKindToRecordWriteMode(write_barrier_kind);
InstructionOperand temps[] = {g.TempRegister(), g.TempRegister()};
size_t const temp_count = arraysize(temps);
InstructionCode code = kArchStoreWithWriteBarrier;
code |= AddressingModeField::encode(addressing_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 {
InstructionCode opcode = kArchNop;
switch (rep) {
......
......@@ -260,16 +260,14 @@ namespace {
class OutOfLineRecordWrite final : public OutOfLineCode {
public:
OutOfLineRecordWrite(CodeGenerator* gen, Register object, Operand index,
Register value, Register scratch0, Register scratch1,
RecordWriteMode mode, StubCallMode stub_mode,
OutOfLineRecordWrite(CodeGenerator* gen, Register object, Operand offset,
Register value, RecordWriteMode mode,
StubCallMode stub_mode,
UnwindingInfoWriter* unwinding_info_writer)
: OutOfLineCode(gen),
object_(object),
index_(index),
offset_(offset),
value_(value),
scratch0_(scratch0),
scratch1_(scratch1),
mode_(mode),
stub_mode_(stub_mode),
must_save_lr_(!gen->frame_access_state()->has_frame()),
......@@ -283,10 +281,8 @@ class OutOfLineRecordWrite final : public OutOfLineCode {
if (COMPRESS_POINTERS_BOOL) {
__ DecompressTaggedPointer(value_, value_);
}
__ CheckPageFlag(value_, scratch0_,
MemoryChunk::kPointersToHereAreInterestingMask, ne,
__ CheckPageFlag(value_, MemoryChunk::kPointersToHereAreInterestingMask, ne,
exit());
__ Add(scratch1_, object_, index_);
RememberedSetAction const remembered_set_action =
mode_ > RecordWriteMode::kValueIsMap ? EMIT_REMEMBERED_SET
: OMIT_REMEMBERED_SET;
......@@ -298,15 +294,15 @@ class OutOfLineRecordWrite final : public OutOfLineCode {
unwinding_info_writer_->MarkLinkRegisterOnTopOfStack(__ pc_offset(), sp);
}
if (mode_ == RecordWriteMode::kValueIsEphemeronKey) {
__ CallEphemeronKeyBarrier(object_, scratch1_, save_fp_mode);
__ CallEphemeronKeyBarrier(object_, offset_, save_fp_mode);
} else if (stub_mode_ == StubCallMode::kCallWasmRuntimeStub) {
// 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,
__ CallRecordWriteStub(object_, offset_, remembered_set_action,
save_fp_mode, wasm::WasmCode::kWasmRecordWrite);
} else {
__ CallRecordWriteStub(object_, scratch1_, remembered_set_action,
__ CallRecordWriteStub(object_, offset_, remembered_set_action,
save_fp_mode);
}
if (must_save_lr_) {
......@@ -317,10 +313,8 @@ class OutOfLineRecordWrite final : public OutOfLineCode {
private:
Register const object_;
Operand const index_;
Operand const offset_;
Register const value_;
Register const scratch0_;
Register const scratch1_;
RecordWriteMode const mode_;
StubCallMode const stub_mode_;
bool must_save_lr_;
......@@ -851,26 +845,23 @@ CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction(
AddressingMode addressing_mode =
AddressingModeField::decode(instr->opcode());
Register object = i.InputRegister(0);
Operand index(0);
Operand offset(0);
if (addressing_mode == kMode_MRI) {
index = Operand(i.InputInt64(1));
offset = Operand(i.InputInt64(1));
} else {
DCHECK_EQ(addressing_mode, kMode_MRR);
index = Operand(i.InputRegister(1));
offset = Operand(i.InputRegister(1));
}
Register value = i.InputRegister(2);
Register scratch0 = i.TempRegister(0);
Register scratch1 = i.TempRegister(1);
auto ool = new (zone()) OutOfLineRecordWrite(
this, object, index, value, scratch0, scratch1, mode,
DetermineStubCallMode(), &unwinding_info_writer_);
__ StoreTaggedField(value, MemOperand(object, index));
this, object, offset, value, mode, DetermineStubCallMode(),
&unwinding_info_writer_);
__ StoreTaggedField(value, MemOperand(object, offset));
if (COMPRESS_POINTERS_BOOL) {
__ DecompressTaggedPointer(object, object);
}
__ CheckPageFlag(object, scratch0,
MemoryChunk::kPointersFromHereAreInterestingMask, eq,
ool->entry());
__ CheckPageFlag(object, MemoryChunk::kPointersFromHereAreInterestingMask,
eq, ool->entry());
__ Bind(ool->exit());
break;
}
......
......@@ -706,12 +706,10 @@ void InstructionSelector::VisitStore(Node* node) {
inputs[input_count++] = g.UseUniqueRegister(value);
RecordWriteMode record_write_mode =
WriteBarrierKindToRecordWriteMode(write_barrier_kind);
InstructionOperand temps[] = {g.TempRegister(), g.TempRegister()};
size_t const temp_count = arraysize(temps);
InstructionCode code = kArchStoreWithWriteBarrier;
code |= AddressingModeField::encode(addressing_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 {
InstructionOperand inputs[4];
size_t input_count = 0;
......
......@@ -4,6 +4,7 @@
#include "src/arm/assembler-arm-inl.h"
#include "src/macro-assembler.h"
#include "src/ostreams.h"
#include "src/simulator.h"
#include "test/common/assembler-tester.h"
#include "test/unittests/test-utils.h"
......@@ -69,6 +70,117 @@ TEST_F(TurboAssemblerTest, TestCheck) {
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 ERROR_MESSAGE
......
......@@ -4,6 +4,7 @@
#include "src/arm64/macro-assembler-arm64-inl.h"
#include "src/macro-assembler.h"
#include "src/ostreams.h"
#include "src/simulator.h"
#include "test/common/assembler-tester.h"
#include "test/unittests/test-utils.h"
......@@ -69,6 +70,118 @@ TEST_F(TurboAssemblerTest, TestCheck) {
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 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