diff options
author | 2022-10-14 16:55:47 +0100 | |
---|---|---|
committer | 2022-10-24 16:05:21 +0000 | |
commit | 2e2c94d32fcf3ee221e3d2f6cbb68ec6251f8c86 (patch) | |
tree | 1903374e48f34ed58300ea2ce64cac392d32e26d /compiler/optimizing | |
parent | e4e701b55ef341156d36db9dfc23f63d59dbf928 (diff) |
Allow LSA to run with acquire/release operations
LSA will run in graphs with acquire loads (i.e. monitor enter and
volatile load) and release stores (i.e. monitor exit and volatile
stores).
Helps both LSE and the Scheduler, and brings code size and memory use
reductions. For example, ~40KB (~0.1%) reduction in memory use when
compiling android framework in armv8.
Code size gains (locally run on Pixel 5 w/ AOSP):
Android Google Search App (AGSA): 209KB
System server: 44KB
System UI: 20KB
which is ~0.1% for each compile.
Bug: 227283233
Test: art/test/testrunner/testrunner.py --host --64 --optimizing -b
Change-Id: I9ac79cf2324348414186f95e531c98b4215b28ea
Diffstat (limited to 'compiler/optimizing')
-rw-r--r-- | compiler/optimizing/load_store_analysis.cc | 8 | ||||
-rw-r--r-- | compiler/optimizing/load_store_analysis.h | 19 | ||||
-rw-r--r-- | compiler/optimizing/load_store_elimination.cc | 74 | ||||
-rw-r--r-- | compiler/optimizing/scheduler.cc | 1 |
4 files changed, 69 insertions, 33 deletions
diff --git a/compiler/optimizing/load_store_analysis.cc b/compiler/optimizing/load_store_analysis.cc index 3fe42aff2e..5e4cffab84 100644 --- a/compiler/optimizing/load_store_analysis.cc +++ b/compiler/optimizing/load_store_analysis.cc @@ -283,14 +283,6 @@ bool LoadStoreAnalysis::Run() { heap_location_collector_.CleanUp(); return false; } - if (heap_location_collector_.HasVolatile() || heap_location_collector_.HasMonitorOps()) { - // Don't do load/store elimination if the method has volatile field accesses or - // monitor operations, for now. - // TODO: do it right. - heap_location_collector_.CleanUp(); - return false; - } - heap_location_collector_.BuildAliasingMatrix(); heap_location_collector_.DumpReferenceStats(stats_); return true; diff --git a/compiler/optimizing/load_store_analysis.h b/compiler/optimizing/load_store_analysis.h index 4975bae2a2..b12e70b194 100644 --- a/compiler/optimizing/load_store_analysis.h +++ b/compiler/optimizing/load_store_analysis.h @@ -253,8 +253,6 @@ class HeapLocationCollector : public HGraphVisitor { heap_locations_(allocator->Adapter(kArenaAllocLSA)), aliasing_matrix_(allocator, kInitialAliasingMatrixBitVectorSize, true, kArenaAllocLSA), has_heap_stores_(false), - has_volatile_(false), - has_monitor_operations_(false), lse_type_(lse_type) { aliasing_matrix_.ClearAllBits(); } @@ -350,14 +348,6 @@ class HeapLocationCollector : public HGraphVisitor { return has_heap_stores_; } - bool HasVolatile() const { - return has_volatile_; - } - - bool HasMonitorOps() const { - return has_monitor_operations_; - } - // Find and return the heap location index in heap_locations_. // NOTE: When heap locations are created, potentially aliasing/overlapping // accesses are given different indexes. This find function also @@ -540,9 +530,6 @@ class HeapLocationCollector : public HGraphVisitor { } void VisitFieldAccess(HInstruction* ref, const FieldInfo& field_info) { - if (field_info.IsVolatile()) { - has_volatile_ = true; - } DataType::Type type = field_info.GetFieldType(); const uint16_t declaring_class_def_index = field_info.GetDeclaringClassDefIndex(); const size_t offset = field_info.GetFieldOffset().SizeValue(); @@ -637,18 +624,12 @@ class HeapLocationCollector : public HGraphVisitor { CreateReferenceInfoForReferenceType(instruction); } - void VisitMonitorOperation(HMonitorOperation* monitor ATTRIBUTE_UNUSED) override { - has_monitor_operations_ = true; - } - ScopedArenaAllocator* allocator_; ScopedArenaVector<ReferenceInfo*> ref_info_array_; // All references used for heap accesses. ScopedArenaVector<HeapLocation*> heap_locations_; // All heap locations. ArenaBitVector aliasing_matrix_; // aliasing info between each pair of locations. bool has_heap_stores_; // If there is no heap stores, LSE acts as GVN with better // alias analysis and won't be as effective. - bool has_volatile_; // If there are volatile field accesses. - bool has_monitor_operations_; // If there are monitor operations. LoadStoreAnalysisType lse_type_; DISALLOW_COPY_AND_ASSIGN(HeapLocationCollector); diff --git a/compiler/optimizing/load_store_elimination.cc b/compiler/optimizing/load_store_elimination.cc index cafc3e9c16..4862f5810b 100644 --- a/compiler/optimizing/load_store_elimination.cc +++ b/compiler/optimizing/load_store_elimination.cc @@ -974,13 +974,63 @@ class LSEVisitor final : private HGraphDelegateVisitor { << " but LSE should be the only source of predicated-ifield-gets!"; } + void HandleAcquireLoad(HInstruction* instruction) { + DCHECK((instruction->IsInstanceFieldGet() && instruction->AsInstanceFieldGet()->IsVolatile()) || + (instruction->IsStaticFieldGet() && instruction->AsStaticFieldGet()->IsVolatile()) || + (instruction->IsMonitorOperation() && instruction->AsMonitorOperation()->IsEnter())) + << "Unexpected instruction " << instruction->GetId() << ": " << instruction->DebugName(); + + // Acquire operations e.g. MONITOR_ENTER change the thread's view of the memory, so we must + // invalidate all current values. + ScopedArenaVector<ValueRecord>& heap_values = + heap_values_for_[instruction->GetBlock()->GetBlockId()]; + for (size_t i = 0u, size = heap_values.size(); i != size; ++i) { + KeepStores(heap_values[i].stored_by); + heap_values[i].stored_by = Value::PureUnknown(); + heap_values[i].value = Value::PartialUnknown(heap_values[i].value); + } + + // Note that there's no need to record the load as subsequent acquire loads shouldn't be + // eliminated either. + } + + void HandleReleaseStore(HInstruction* instruction) { + DCHECK((instruction->IsInstanceFieldSet() && instruction->AsInstanceFieldSet()->IsVolatile()) || + (instruction->IsStaticFieldSet() && instruction->AsStaticFieldSet()->IsVolatile()) || + (instruction->IsMonitorOperation() && !instruction->AsMonitorOperation()->IsEnter())) + << "Unexpected instruction " << instruction->GetId() << ": " << instruction->DebugName(); + + // Release operations e.g. MONITOR_EXIT do not affect this thread's view of the memory, but + // they will push the modifications for other threads to see. Therefore, we must keep the + // stores but there's no need to clobber the value. + ScopedArenaVector<ValueRecord>& heap_values = + heap_values_for_[instruction->GetBlock()->GetBlockId()]; + for (size_t i = 0u, size = heap_values.size(); i != size; ++i) { + KeepStores(heap_values[i].stored_by); + heap_values[i].stored_by = Value::PureUnknown(); + } + + // Note that there's no need to record the store as subsequent release store shouldn't be + // eliminated either. + } + void VisitInstanceFieldGet(HInstanceFieldGet* instruction) override { + if (instruction->IsVolatile()) { + HandleAcquireLoad(instruction); + return; + } + HInstruction* object = instruction->InputAt(0); const FieldInfo& field = instruction->GetFieldInfo(); VisitGetLocation(instruction, heap_location_collector_.GetFieldHeapLocation(object, &field)); } void VisitInstanceFieldSet(HInstanceFieldSet* instruction) override { + if (instruction->IsVolatile()) { + HandleReleaseStore(instruction); + return; + } + HInstruction* object = instruction->InputAt(0); const FieldInfo& field = instruction->GetFieldInfo(); HInstruction* value = instruction->InputAt(1); @@ -989,12 +1039,22 @@ class LSEVisitor final : private HGraphDelegateVisitor { } void VisitStaticFieldGet(HStaticFieldGet* instruction) override { + if (instruction->IsVolatile()) { + HandleAcquireLoad(instruction); + return; + } + HInstruction* cls = instruction->InputAt(0); const FieldInfo& field = instruction->GetFieldInfo(); VisitGetLocation(instruction, heap_location_collector_.GetFieldHeapLocation(cls, &field)); } void VisitStaticFieldSet(HStaticFieldSet* instruction) override { + if (instruction->IsVolatile()) { + HandleReleaseStore(instruction); + return; + } + HInstruction* cls = instruction->InputAt(0); const FieldInfo& field = instruction->GetFieldInfo(); HInstruction* value = instruction->InputAt(1); @@ -1002,6 +1062,14 @@ class LSEVisitor final : private HGraphDelegateVisitor { VisitSetLocation(instruction, idx, value); } + void VisitMonitorOperation(HMonitorOperation* monitor_op) override { + if (monitor_op->IsEnter()) { + HandleAcquireLoad(monitor_op); + } else { + HandleReleaseStore(monitor_op); + } + } + void VisitArrayGet(HArrayGet* instruction) override { VisitGetLocation(instruction, heap_location_collector_.GetArrayHeapLocation(instruction)); } @@ -1152,12 +1220,6 @@ class LSEVisitor final : private HGraphDelegateVisitor { HandleThrowingInstruction(check_cast); } - void VisitMonitorOperation(HMonitorOperation* monitor_op) override { - if (monitor_op->CanThrow()) { - HandleThrowingInstruction(monitor_op); - } - } - void HandleInvoke(HInstruction* instruction) { // If `instruction` can throw we have to presume all stores are visible. const bool can_throw = instruction->CanThrow(); diff --git a/compiler/optimizing/scheduler.cc b/compiler/optimizing/scheduler.cc index d228aba95a..d6568980c8 100644 --- a/compiler/optimizing/scheduler.cc +++ b/compiler/optimizing/scheduler.cc @@ -721,6 +721,7 @@ bool HScheduler::IsSchedulable(const HInstruction* instruction) const { // HNop // HThrow // HTryBoundary + // All volatile field access e.g. HInstanceFieldGet // TODO: Some of the instructions above may be safe to schedule (maybe as // scheduling barriers). return instruction->IsArrayGet() || |