[vm, gc] Don't use alignment bits when verifying eliminated write barriers.

TEST=ci
Change-Id: I6affde0b9db6ec951458905391670fe602dd3b61
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/262266
Reviewed-by: Alexander Markov <alexmarkov@google.com>
Commit-Queue: Ryan Macnak <rmacnak@google.com>
This commit is contained in:
Ryan Macnak 2022-10-04 20:10:15 +00:00 committed by Commit Queue
parent 9a53b32c9c
commit 7e4f448f78
11 changed files with 49 additions and 239 deletions

View file

@ -1704,38 +1704,6 @@ void Assembler::CompareObject(Register rn, const Object& object) {
}
}
// Preserves object and value registers.
void Assembler::StoreIntoObjectFilter(Register object,
Register value,
Label* label,
CanBeSmi value_can_be_smi,
BarrierFilterMode how_to_jump) {
COMPILE_ASSERT((target::ObjectAlignment::kNewObjectAlignmentOffset ==
target::kWordSize) &&
(target::ObjectAlignment::kOldObjectAlignmentOffset == 0));
// For the value we are only interested in the new/old bit and the tag bit.
// And the new bit with the tag bit. The resulting bit will be 0 for a Smi.
if (value_can_be_smi == kValueCanBeSmi) {
and_(
IP, value,
Operand(value, LSL, target::ObjectAlignment::kObjectAlignmentLog2 - 1));
// And the result with the negated space bit of the object.
bic(IP, IP, Operand(object));
} else {
#if defined(DEBUG)
Label okay;
BranchIfNotSmi(value, &okay);
Stop("Unexpected Smi!");
Bind(&okay);
#endif
bic(IP, value, Operand(object));
}
tst(IP, Operand(target::ObjectAlignment::kNewObjectAlignmentOffset));
if (how_to_jump != kNoJump) {
b(label, how_to_jump == kJumpToNoUpdate ? EQ : NE);
}
}
Register UseRegister(Register reg, RegList* used) {
ASSERT(reg != THR);
ASSERT(reg != SP);
@ -1782,7 +1750,7 @@ void Assembler::StoreIntoObject(Register object,
// Compare UntaggedObject::StorePointer.
Label done;
if (can_be_smi == kValueCanBeSmi) {
BranchIfSmi(value, &done);
BranchIfSmi(value, &done, kNearJump);
}
const bool preserve_lr = lr_state().LRContainsReturnAddress();
if (preserve_lr) {
@ -1853,7 +1821,7 @@ void Assembler::StoreIntoArray(Register object,
// Compare UntaggedObject::StorePointer.
Label done;
if (can_be_smi == kValueCanBeSmi) {
BranchIfSmi(value, &done);
BranchIfSmi(value, &done, kNearJump);
}
const bool preserve_lr = lr_state().LRContainsReturnAddress();
if (preserve_lr) {
@ -1915,13 +1883,14 @@ void Assembler::StoreIntoObjectNoBarrier(Register object,
// reachable via a constant pool, so it doesn't matter if it is not traced via
// 'object'.
Label done;
StoreIntoObjectFilter(object, value, &done, kValueCanBeSmi, kJumpToNoUpdate);
BranchIfSmi(value, &done, kNearJump);
ldrb(TMP, FieldAddress(value, target::Object::tags_offset()));
tst(TMP, Operand(1 << target::UntaggedObject::kNewBit));
b(&done, ZERO);
ldrb(TMP, FieldAddress(object, target::Object::tags_offset()));
tst(TMP, Operand(1 << target::UntaggedObject::kOldAndNotRememberedBit));
b(&done, ZERO);
Stop("Store buffer update is required");
Stop("Write barrier is required");
Bind(&done);
#endif // defined(DEBUG)
// No store buffer update.
@ -1998,16 +1967,6 @@ void Assembler::InitializeFieldsNoBarrier(Register object,
strd(value_even, value_odd, begin, -2 * target::kWordSize, LS);
b(&init_loop, CC);
str(value_even, Address(begin, -2 * target::kWordSize), HI);
#if defined(DEBUG)
Label done;
StoreIntoObjectFilter(object, value_even, &done, kValueCanBeSmi,
kJumpToNoUpdate);
StoreIntoObjectFilter(object, value_odd, &done, kValueCanBeSmi,
kJumpToNoUpdate);
Stop("Store buffer update is required");
Bind(&done);
#endif // defined(DEBUG)
// No store buffer update.
}
void Assembler::InitializeFieldsNoBarrierUnrolled(Register object,
@ -2026,16 +1985,6 @@ void Assembler::InitializeFieldsNoBarrierUnrolled(Register object,
str(value_even, Address(base, current_offset));
current_offset += target::kWordSize;
}
#if defined(DEBUG)
Label done;
StoreIntoObjectFilter(object, value_even, &done, kValueCanBeSmi,
kJumpToNoUpdate);
StoreIntoObjectFilter(object, value_odd, &done, kValueCanBeSmi,
kJumpToNoUpdate);
Stop("Store buffer update is required");
Bind(&done);
#endif // defined(DEBUG)
// No store buffer update.
}
void Assembler::StoreIntoSmiField(const Address& dest, Register value) {

View file

@ -1645,27 +1645,6 @@ class Assembler : public AssemblerBase {
int32_t EncodeTstOffset(int32_t offset, int32_t inst);
int32_t DecodeTstOffset(int32_t inst);
enum BarrierFilterMode {
// Filter falls through into the barrier update code. Target label
// is a "after-store" label.
kJumpToNoUpdate,
// Filter falls through to the "after-store" code. Target label
// is barrier update code label.
kJumpToBarrier,
// Filter falls through into the conditional barrier update code and does
// not jump. Target label is unused. The barrier should run if the NE
// condition is set.
kNoJump
};
void StoreIntoObjectFilter(Register object,
Register value,
Label* label,
CanBeSmi can_be_smi,
BarrierFilterMode barrier_filter_mode);
friend class dart::FlowGraphCompiler;
std::function<void(Condition, Register)>
generate_invoke_write_barrier_wrapper_;

View file

@ -994,43 +994,6 @@ void Assembler::LoadCompressedSmiFromOffset(Register dest,
#endif
}
// Preserves object and value registers.
void Assembler::StoreIntoObjectFilter(Register object,
Register value,
Label* label,
CanBeSmi value_can_be_smi,
BarrierFilterMode how_to_jump) {
COMPILE_ASSERT((target::ObjectAlignment::kNewObjectAlignmentOffset ==
target::kWordSize) &&
(target::ObjectAlignment::kOldObjectAlignmentOffset == 0));
// Write-barrier triggers if the value is in the new space (has bit set) and
// the object is in the old space (has bit cleared).
if (value_can_be_smi == kValueIsNotSmi) {
#if defined(DEBUG)
Label okay;
BranchIfNotSmi(value, &okay);
Stop("Unexpected Smi!");
Bind(&okay);
#endif
// To check that, we compute value & ~object and skip the write barrier
// if the bit is not set. We can't destroy the object.
bic(TMP, value, Operand(object));
} else {
// For the value we are only interested in the new/old bit and the tag bit.
// And the new bit with the tag bit. The resulting bit will be 0 for a Smi.
and_(TMP, value,
Operand(value, LSL, target::ObjectAlignment::kNewObjectBitPosition));
// And the result with the negated space bit of the object.
bic(TMP, TMP, Operand(object));
}
if (how_to_jump == kJumpToNoUpdate) {
tbz(label, TMP, target::ObjectAlignment::kNewObjectBitPosition);
} else {
tbnz(label, TMP, target::ObjectAlignment::kNewObjectBitPosition);
}
}
void Assembler::StoreIntoObjectOffset(Register object,
int32_t offset,
Register value,
@ -1220,13 +1183,12 @@ void Assembler::StoreIntoObjectNoBarrier(Register object,
// reachable via a constant pool, so it doesn't matter if it is not traced via
// 'object'.
Label done;
StoreIntoObjectFilter(object, value, &done, kValueCanBeSmi, kJumpToNoUpdate);
BranchIfSmi(value, &done, kNearJump);
ldr(TMP, FieldAddress(value, target::Object::tags_offset()), kUnsignedByte);
tbz(&done, TMP, target::UntaggedObject::kNewBit);
ldr(TMP, FieldAddress(object, target::Object::tags_offset()), kUnsignedByte);
tsti(TMP, Immediate(1 << target::UntaggedObject::kOldAndNotRememberedBit));
b(&done, ZERO);
Stop("Store buffer update is required");
tbz(&done, TMP, target::UntaggedObject::kOldAndNotRememberedBit);
Stop("Write barrier is required");
Bind(&done);
#endif // defined(DEBUG)
// No store buffer update.
@ -1246,13 +1208,12 @@ void Assembler::StoreCompressedIntoObjectNoBarrier(Register object,
// reachable via a constant pool, so it doesn't matter if it is not traced via
// 'object'.
Label done;
StoreIntoObjectFilter(object, value, &done, kValueCanBeSmi, kJumpToNoUpdate);
BranchIfSmi(value, &done, kNearJump);
ldr(TMP, FieldAddress(value, target::Object::tags_offset()), kUnsignedByte);
tbz(&done, TMP, target::UntaggedObject::kNewBit);
ldr(TMP, FieldAddress(object, target::Object::tags_offset()), kUnsignedByte);
tsti(TMP, Immediate(1 << target::UntaggedObject::kOldAndNotRememberedBit));
b(&done, ZERO);
Stop("Store buffer update is required");
tbz(&done, TMP, target::UntaggedObject::kOldAndNotRememberedBit);
Stop("Write barrier is required");
Bind(&done);
#endif // defined(DEBUG)
// No store buffer update.

View file

@ -2982,22 +2982,6 @@ class Assembler : public AssemblerBase {
Emit(encoding);
}
enum BarrierFilterMode {
// Filter falls through into the barrier update code. Target label
// is a "after-store" label.
kJumpToNoUpdate,
// Filter falls through to the "after-store" code. Target label
// is barrier update code label.
kJumpToBarrier,
};
void StoreIntoObjectFilter(Register object,
Register value,
Label* label,
CanBeSmi can_be_smi,
BarrierFilterMode barrier_filter_mode);
friend class dart::FlowGraphCompiler;
std::function<void(Register reg)> generate_invoke_write_barrier_wrapper_;
std::function<void()> generate_invoke_array_write_barrier_;

View file

@ -2086,16 +2086,15 @@ void Assembler::StoreIntoObjectNoBarrier(Register object,
// reachable via a constant pool, so it doesn't matter if it is not traced via
// 'object'.
Label done;
pushl(value);
StoreIntoObjectFilter(object, value, &done, kValueCanBeSmi, kJumpToNoUpdate);
BranchIfSmi(value, &done, kNearJump);
testb(FieldAddress(value, target::Object::tags_offset()),
Immediate(1 << target::UntaggedObject::kNewBit));
j(ZERO, &done, Assembler::kNearJump);
testb(FieldAddress(object, target::Object::tags_offset()),
Immediate(1 << target::UntaggedObject::kOldAndNotRememberedBit));
j(ZERO, &done, Assembler::kNearJump);
Stop("Store buffer update is required");
Stop("Write barrier is required");
Bind(&done);
popl(value);
#endif // defined(DEBUG)
// No store buffer update.
}

View file

@ -3144,15 +3144,14 @@ void Assembler::StoreIntoObjectNoBarrier(Register object,
// reachable via a constant pool, so it doesn't matter if it is not traced via
// 'object'.
Label done;
beq(object, value, &done, kNearJump);
BranchIfSmi(value, &done, kNearJump);
lbu(TMP, FieldAddress(object, target::Object::tags_offset()));
lbu(TMP2, FieldAddress(value, target::Object::tags_offset()));
srli(TMP, TMP, target::UntaggedObject::kBarrierOverlapShift);
and_(TMP, TMP, TMP2);
andi(TMP, TMP, target::UntaggedObject::kGenerationalBarrierMask);
beqz(TMP, &done, kNearJump);
Stop("Store buffer update is required");
andi(TMP2, TMP2, 1 << target::UntaggedObject::kNewBit);
beqz(TMP2, &done, kNearJump);
lbu(TMP2, FieldAddress(object, target::Object::tags_offset()));
andi(TMP2, TMP2, 1 << target::UntaggedObject::kOldAndNotRememberedBit);
beqz(TMP2, &done, kNearJump);
Stop("Write barrier is required");
Bind(&done);
#endif
}
@ -3178,15 +3177,14 @@ void Assembler::StoreIntoObjectOffsetNoBarrier(Register object,
// reachable via a constant pool, so it doesn't matter if it is not traced via
// 'object'.
Label done;
beq(object, value, &done, kNearJump);
BranchIfSmi(value, &done, kNearJump);
lbu(TMP, FieldAddress(object, target::Object::tags_offset()));
lbu(TMP2, FieldAddress(value, target::Object::tags_offset()));
srli(TMP, TMP, target::UntaggedObject::kBarrierOverlapShift);
and_(TMP, TMP, TMP2);
andi(TMP, TMP, target::UntaggedObject::kGenerationalBarrierMask);
beqz(TMP, &done, kNearJump);
Stop("Store buffer update is required");
andi(TMP2, TMP2, 1 << target::UntaggedObject::kNewBit);
beqz(TMP2, &done, kNearJump);
lbu(TMP2, FieldAddress(object, target::Object::tags_offset()));
andi(TMP2, TMP2, 1 << target::UntaggedObject::kOldAndNotRememberedBit);
beqz(TMP2, &done, kNearJump);
Stop("Write barrier is required");
Bind(&done);
#endif
}

View file

@ -1478,22 +1478,6 @@ class Assembler : public MicroAssembler {
// Note: the function never clobbers TMP, TMP2 scratch registers.
void LoadObjectHelper(Register dst, const Object& obj, bool is_unique);
enum BarrierFilterMode {
// Filter falls through into the barrier update code. Target label
// is a "after-store" label.
kJumpToNoUpdate,
// Filter falls through to the "after-store" code. Target label
// is barrier update code label.
kJumpToBarrier,
};
void StoreIntoObjectFilter(Register object,
Register value,
Label* label,
CanBeSmi can_be_smi,
BarrierFilterMode barrier_filter_mode);
friend class dart::FlowGraphCompiler;
std::function<void(Register reg)> generate_invoke_write_barrier_wrapper_;
std::function<void()> generate_invoke_array_write_barrier_;

View file

@ -1434,44 +1434,6 @@ void Assembler::LoadCompressedSmi(Register dest, const Address& slot) {
#endif
}
// Destroys the value register.
void Assembler::StoreIntoObjectFilter(Register object,
Register value,
Label* label,
CanBeSmi can_be_smi,
BarrierFilterMode how_to_jump) {
COMPILE_ASSERT((target::ObjectAlignment::kNewObjectAlignmentOffset ==
target::kWordSize) &&
(target::ObjectAlignment::kOldObjectAlignmentOffset == 0));
if (can_be_smi == kValueIsNotSmi) {
#if defined(DEBUG)
Label okay;
BranchIfNotSmi(value, &okay);
Stop("Unexpected Smi!");
Bind(&okay);
#endif
// Write-barrier triggers if the value is in the new space (has bit set) and
// the object is in the old space (has bit cleared).
// To check that we could compute value & ~object and skip the write barrier
// if the bit is not set. However we can't destroy the object.
// However to preserve the object we compute negated expression
// ~value | object instead and skip the write barrier if the bit is set.
notl(value);
orl(value, object);
testl(value, Immediate(target::ObjectAlignment::kNewObjectAlignmentOffset));
} else {
ASSERT(kHeapObjectTag == 1);
// Detect value being ...1001 and object being ...0001.
andl(value, Immediate(0xf));
leal(value, Address(value, object, TIMES_2, 0x15));
testl(value, Immediate(0x1f));
}
Condition condition = how_to_jump == kJumpToNoUpdate ? NOT_ZERO : ZERO;
JumpDistance distance = how_to_jump == kJumpToNoUpdate ? kNearJump : kFarJump;
j(condition, label, distance);
}
void Assembler::StoreIntoObject(Register object,
const Address& dest,
Register value,
@ -1515,8 +1477,7 @@ void Assembler::StoreBarrier(Register object,
// Compare UntaggedObject::StorePointer.
Label done;
if (can_be_smi == kValueCanBeSmi) {
testq(value, Immediate(kSmiTagMask));
j(ZERO, &done, kNearJump);
BranchIfSmi(value, &done, kNearJump);
}
movb(ByteRegisterOf(TMP),
FieldAddress(object, target::Object::tags_offset()));
@ -1582,8 +1543,7 @@ void Assembler::StoreIntoArrayBarrier(Register object,
// Compare UntaggedObject::StorePointer.
Label done;
if (can_be_smi == kValueCanBeSmi) {
testq(value, Immediate(kSmiTagMask));
j(ZERO, &done, kNearJump);
BranchIfSmi(value, &done, kNearJump);
}
movb(ByteRegisterOf(TMP),
FieldAddress(object, target::Object::tags_offset()));
@ -1621,16 +1581,15 @@ void Assembler::StoreIntoObjectNoBarrier(Register object,
// reachable via a constant pool, so it doesn't matter if it is not traced via
// 'object'.
Label done;
pushq(value);
StoreIntoObjectFilter(object, value, &done, kValueCanBeSmi, kJumpToNoUpdate);
BranchIfSmi(value, &done, kNearJump);
testb(FieldAddress(value, target::Object::tags_offset()),
Immediate(1 << target::UntaggedObject::kNewBit));
j(ZERO, &done, Assembler::kNearJump);
testb(FieldAddress(object, target::Object::tags_offset()),
Immediate(1 << target::UntaggedObject::kOldAndNotRememberedBit));
j(ZERO, &done, Assembler::kNearJump);
Stop("Store buffer update is required");
Stop("Write barrier is required");
Bind(&done);
popq(value);
#endif // defined(DEBUG)
// No store buffer update.
}
@ -1651,16 +1610,15 @@ void Assembler::StoreCompressedIntoObjectNoBarrier(Register object,
// reachable via a constant pool, so it doesn't matter if it is not traced via
// 'object'.
Label done;
pushq(value);
StoreIntoObjectFilter(object, value, &done, kValueCanBeSmi, kJumpToNoUpdate);
BranchIfSmi(value, &done, kNearJump);
testb(FieldAddress(value, target::Object::tags_offset()),
Immediate(1 << target::UntaggedObject::kNewBit));
j(ZERO, &done, Assembler::kNearJump);
testb(FieldAddress(object, target::Object::tags_offset()),
Immediate(1 << target::UntaggedObject::kOldAndNotRememberedBit));
j(ZERO, &done, Assembler::kNearJump);
Stop("Store buffer update is required");
Stop("Write barrier is required");
Bind(&done);
popq(value);
#endif // defined(DEBUG)
// No store buffer update.
}

View file

@ -1491,11 +1491,6 @@ class Assembler : public AssemblerBase {
kJumpToBarrier,
};
void StoreIntoObjectFilter(Register object,
Register value,
Label* label,
CanBeSmi can_be_smi,
BarrierFilterMode barrier_filter_mode);
void StoreIntoArrayBarrier(Register object,
Register slot,
Register value,

View file

@ -360,6 +360,8 @@ const word UntaggedObject::kCardRememberedBit =
const word UntaggedObject::kCanonicalBit = dart::UntaggedObject::kCanonicalBit;
const word UntaggedObject::kNewBit = dart::UntaggedObject::kNewBit;
const word UntaggedObject::kOldAndNotRememberedBit =
dart::UntaggedObject::kOldAndNotRememberedBit;

View file

@ -411,6 +411,7 @@ class UntaggedObject : public AllStatic {
public:
static const word kCardRememberedBit;
static const word kCanonicalBit;
static const word kNewBit;
static const word kOldAndNotRememberedBit;
static const word kOldAndNotMarkedBit;
static const word kImmutableBit;