diff options
Diffstat (limited to 'compiler/optimizing/graph_checker.cc')
-rw-r--r-- | compiler/optimizing/graph_checker.cc | 95 |
1 files changed, 95 insertions, 0 deletions
diff --git a/compiler/optimizing/graph_checker.cc b/compiler/optimizing/graph_checker.cc index 8f2f25355d..36ac7bc82c 100644 --- a/compiler/optimizing/graph_checker.cc +++ b/compiler/optimizing/graph_checker.cc @@ -31,6 +31,7 @@ #include "mirror/class.h" #include "nodes.h" #include "obj_ptr-inl.h" +#include "optimizing/data_type.h" #include "scoped_thread_state_change-inl.h" #include "subtype_check.h" @@ -1280,6 +1281,26 @@ void GraphChecker::VisitNeg(HNeg* instruction) { } } +HInstruction* HuntForOriginalReference(HInstruction* ref) { + // An original reference can be transformed by instructions like: + // i0 NewArray + // i1 HInstruction(i0) <-- NullCheck, BoundType, IntermediateAddress. + // i2 ArraySet(i1, index, value) + DCHECK(ref != nullptr); + while (ref->IsNullCheck() || ref->IsBoundType() || ref->IsIntermediateAddress()) { + ref = ref->InputAt(0); + } + return ref; +} + +bool IsRemovedWriteBarrier(DataType::Type type, + WriteBarrierKind write_barrier_kind, + HInstruction* value) { + return write_barrier_kind == WriteBarrierKind::kDontEmit && + type == DataType::Type::kReference && + !value->IsNullConstant(); +} + void GraphChecker::VisitArraySet(HArraySet* instruction) { VisitInstruction(instruction); @@ -1293,6 +1314,80 @@ void GraphChecker::VisitArraySet(HArraySet* instruction) { StrBool(instruction->NeedsTypeCheck()), StrBool(instruction->GetSideEffects().Includes(SideEffects::CanTriggerGC())))); } + + if (IsRemovedWriteBarrier(instruction->GetComponentType(), + instruction->GetWriteBarrierKind(), + instruction->GetValue())) { + CheckWriteBarrier(instruction, [](HInstruction* it_instr) { + return it_instr->AsArraySet()->GetWriteBarrierKind(); + }); + } +} + +void GraphChecker::VisitInstanceFieldSet(HInstanceFieldSet* instruction) { + VisitInstruction(instruction); + if (IsRemovedWriteBarrier(instruction->GetFieldType(), + instruction->GetWriteBarrierKind(), + instruction->GetValue())) { + CheckWriteBarrier(instruction, [](HInstruction* it_instr) { + return it_instr->AsInstanceFieldSet()->GetWriteBarrierKind(); + }); + } +} + +void GraphChecker::VisitStaticFieldSet(HStaticFieldSet* instruction) { + VisitInstruction(instruction); + if (IsRemovedWriteBarrier(instruction->GetFieldType(), + instruction->GetWriteBarrierKind(), + instruction->GetValue())) { + CheckWriteBarrier(instruction, [](HInstruction* it_instr) { + return it_instr->AsStaticFieldSet()->GetWriteBarrierKind(); + }); + } +} + +template <typename GetWriteBarrierKind> +void GraphChecker::CheckWriteBarrier(HInstruction* instruction, + GetWriteBarrierKind&& get_write_barrier_kind) { + DCHECK(instruction->IsStaticFieldSet() || + instruction->IsInstanceFieldSet() || + instruction->IsArraySet()); + + // For removed write barriers, we expect that the write barrier they are relying on is: + // A) In the same block, and + // B) There's no instruction between them that can trigger a GC. + HInstruction* object = HuntForOriginalReference(instruction->InputAt(0)); + bool found = false; + for (HBackwardInstructionIterator it(instruction); !it.Done(); it.Advance()) { + if (instruction->GetKind() == it.Current()->GetKind() && + object == HuntForOriginalReference(it.Current()->InputAt(0)) && + get_write_barrier_kind(it.Current()) == WriteBarrierKind::kEmitBeingReliedOn) { + // Found the write barrier we are relying on. + found = true; + break; + } + + // We check the `SideEffects::CanTriggerGC` after failing to find the write barrier since having + // a write barrier that's relying on an ArraySet that can trigger GC is fine because the card + // table is marked after the GC happens. + if (it.Current()->GetSideEffects().Includes(SideEffects::CanTriggerGC())) { + AddError( + StringPrintf("%s %d from block %d was expecting a write barrier and it didn't find " + "any. %s %d can trigger GC", + instruction->DebugName(), + instruction->GetId(), + instruction->GetBlock()->GetBlockId(), + it.Current()->DebugName(), + it.Current()->GetId())); + } + } + + if (!found) { + AddError(StringPrintf("%s %d in block %d didn't find a write barrier to latch onto", + instruction->DebugName(), + instruction->GetId(), + instruction->GetBlock()->GetBlockId())); + } } void GraphChecker::VisitBinaryOperation(HBinaryOperation* op) { |