summaryrefslogtreecommitdiff
path: root/compiler/optimizing
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/optimizing')
-rw-r--r--compiler/optimizing/load_store_analysis.h37
-rw-r--r--compiler/optimizing/load_store_elimination.cc81
-rw-r--r--compiler/optimizing/load_store_elimination_test.cc406
3 files changed, 491 insertions, 33 deletions
diff --git a/compiler/optimizing/load_store_analysis.h b/compiler/optimizing/load_store_analysis.h
index 999026cb6a..aa8b5bbdc9 100644
--- a/compiler/optimizing/load_store_analysis.h
+++ b/compiler/optimizing/load_store_analysis.h
@@ -32,8 +32,7 @@ class ReferenceInfo : public ArenaObject<kArenaAllocLSA> {
position_(pos),
is_singleton_(true),
is_singleton_and_not_returned_(true),
- is_singleton_and_not_deopt_visible_(true),
- has_index_aliasing_(false) {
+ is_singleton_and_not_deopt_visible_(true) {
CalculateEscape(reference_,
nullptr,
&is_singleton_,
@@ -70,16 +69,6 @@ class ReferenceInfo : public ArenaObject<kArenaAllocLSA> {
(!is_singleton_and_not_returned_ || !is_singleton_and_not_deopt_visible_);
}
- bool HasIndexAliasing() {
- return has_index_aliasing_;
- }
-
- void SetHasIndexAliasing(bool has_index_aliasing) {
- // Only allow setting to true.
- DCHECK(has_index_aliasing);
- has_index_aliasing_ = has_index_aliasing;
- }
-
private:
HInstruction* const reference_;
const size_t position_; // position in HeapLocationCollector's ref_info_array_.
@@ -90,9 +79,6 @@ class ReferenceInfo : public ArenaObject<kArenaAllocLSA> {
bool is_singleton_and_not_returned_;
// Is singleton and not used as an environment local of HDeoptimize.
bool is_singleton_and_not_deopt_visible_;
- // Some heap locations with reference_ have array index aliasing,
- // e.g. arr[i] and arr[j] may be the same location.
- bool has_index_aliasing_;
DISALLOW_COPY_AND_ASSIGN(ReferenceInfo);
};
@@ -117,7 +103,8 @@ class HeapLocation : public ArenaObject<kArenaAllocLSA> {
index_(index),
vector_length_(vector_length),
declaring_class_def_index_(declaring_class_def_index),
- value_killed_by_loop_side_effects_(true) {
+ value_killed_by_loop_side_effects_(true),
+ has_aliased_locations_(false) {
DCHECK(ref_info != nullptr);
DCHECK((offset == kInvalidFieldOffset && index != nullptr) ||
(offset != kInvalidFieldOffset && index == nullptr));
@@ -151,6 +138,14 @@ class HeapLocation : public ArenaObject<kArenaAllocLSA> {
value_killed_by_loop_side_effects_ = val;
}
+ bool HasAliasedLocations() const {
+ return has_aliased_locations_;
+ }
+
+ void SetHasAliasedLocations(bool val) {
+ has_aliased_locations_ = val;
+ }
+
private:
// Reference for instance/static field, array element or vector data.
ReferenceInfo* const ref_info_;
@@ -173,6 +168,11 @@ class HeapLocation : public ArenaObject<kArenaAllocLSA> {
// value may be killed by loop side effects.
bool value_killed_by_loop_side_effects_;
+ // Has aliased heap locations in the method, due to either the
+ // reference is aliased or the array element is aliased via different
+ // index names.
+ bool has_aliased_locations_;
+
DISALLOW_COPY_AND_ASSIGN(HeapLocation);
};
@@ -377,6 +377,7 @@ class HeapLocationCollector : public HGraphVisitor {
// Compute if two locations may alias to each other.
bool ComputeMayAlias(size_t index1, size_t index2) const {
+ DCHECK_NE(index1, index2);
HeapLocation* loc1 = heap_locations_[index1];
HeapLocation* loc2 = heap_locations_[index2];
if (loc1->GetOffset() != loc2->GetOffset()) {
@@ -399,9 +400,9 @@ class HeapLocationCollector : public HGraphVisitor {
if (!CanArrayElementsAlias(idx1, vector_length1, idx2, vector_length2)) {
return false;
}
- ReferenceInfo* ref_info = loc1->GetReferenceInfo();
- ref_info->SetHasIndexAliasing(true);
}
+ loc1->SetHasAliasedLocations(true);
+ loc2->SetHasAliasedLocations(true);
return true;
}
diff --git a/compiler/optimizing/load_store_elimination.cc b/compiler/optimizing/load_store_elimination.cc
index 8678fab655..7d953c0963 100644
--- a/compiler/optimizing/load_store_elimination.cc
+++ b/compiler/optimizing/load_store_elimination.cc
@@ -83,7 +83,8 @@ class LSEVisitor : public HGraphDelegateVisitor {
DCHECK(load != nullptr);
DCHECK(load->IsInstanceFieldGet() ||
load->IsStaticFieldGet() ||
- load->IsArrayGet());
+ load->IsArrayGet() ||
+ load->IsVecLoad());
HInstruction* substitute = substitute_instructions_for_loads_[i];
DCHECK(substitute != nullptr);
// Keep tracing substitute till one that's not removed.
@@ -98,7 +99,10 @@ class LSEVisitor : public HGraphDelegateVisitor {
// At this point, stores in possibly_removed_stores_ can be safely removed.
for (HInstruction* store : possibly_removed_stores_) {
- DCHECK(store->IsInstanceFieldSet() || store->IsStaticFieldSet() || store->IsArraySet());
+ DCHECK(store->IsInstanceFieldSet() ||
+ store->IsStaticFieldSet() ||
+ store->IsArraySet() ||
+ store->IsVecStore());
store->GetBlock()->RemoveInstruction(store);
}
@@ -137,7 +141,9 @@ class LSEVisitor : public HGraphDelegateVisitor {
void KeepIfIsStore(HInstruction* heap_value) {
if (heap_value == kDefaultHeapValue ||
heap_value == kUnknownHeapValue ||
- !(heap_value->IsInstanceFieldSet() || heap_value->IsArraySet())) {
+ !(heap_value->IsInstanceFieldSet() ||
+ heap_value->IsArraySet() ||
+ heap_value->IsVecStore())) {
return;
}
auto idx = std::find(possibly_removed_stores_.begin(),
@@ -320,7 +326,9 @@ class LSEVisitor : public HGraphDelegateVisitor {
return;
}
if (heap_value != kUnknownHeapValue) {
- if (heap_value->IsInstanceFieldSet() || heap_value->IsArraySet()) {
+ if (heap_value->IsInstanceFieldSet() ||
+ heap_value->IsArraySet() ||
+ heap_value->IsVecStore()) {
HInstruction* store = heap_value;
// This load must be from a singleton since it's from the same
// field/element that a "removed" store puts the value. That store
@@ -384,8 +392,10 @@ class LSEVisitor : public HGraphDelegateVisitor {
if (Equal(heap_value, value)) {
// Store into the heap location with the same value.
same_value = true;
- } else if (index != nullptr && ref_info->HasIndexAliasing()) {
- // For array element, don't eliminate stores if the index can be aliased.
+ } else if (index != nullptr &&
+ heap_location_collector_.GetHeapLocation(idx)->HasAliasedLocations()) {
+ // For array element, don't eliminate stores if the location can be aliased
+ // (due to either ref or index aliasing).
} else if (ref_info->IsSingleton()) {
// Store into a field/element of a singleton. The value cannot be killed due to
// aliasing/invocation. It can be redundant since future loads can
@@ -416,7 +426,9 @@ class LSEVisitor : public HGraphDelegateVisitor {
if (!same_value) {
if (possibly_redundant) {
- DCHECK(instruction->IsInstanceFieldSet() || instruction->IsArraySet());
+ DCHECK(instruction->IsInstanceFieldSet() ||
+ instruction->IsArraySet() ||
+ instruction->IsVecStore());
// Put the store as the heap value. If the value is loaded from heap
// by a load later, this store isn't really redundant.
heap_values[idx] = instruction;
@@ -429,8 +441,24 @@ class LSEVisitor : public HGraphDelegateVisitor {
if (i == idx) {
continue;
}
- if (heap_values[i] == value) {
- // Same value should be kept even if aliasing happens.
+ if (heap_values[i] == value && !instruction->IsVecOperation()) {
+ // For field/array, same value should be kept even if aliasing happens.
+ //
+ // For vector values , this is NOT safe. For example:
+ // packed_data = [0xA, 0xB, 0xC, 0xD]; <-- Different values in each lane.
+ // VecStore array[i ,i+1,i+2,i+3] = packed_data;
+ // VecStore array[i+1,i+2,i+3,i+4] = packed_data; <-- We are here (partial overlap).
+ // VecLoad vx = array[i,i+1,i+2,i+3]; <-- Cannot be eliminated.
+ //
+ // TODO: to allow such 'same value' optimization on vector data,
+ // LSA needs to report more fine-grain MAY alias information:
+ // (1) May alias due to two vector data partial overlap.
+ // e.g. a[i..i+3] and a[i+1,..,i+4].
+ // (2) May alias due to two vector data may complete overlap each other.
+ // e.g. a[i..i+3] and b[i..i+3].
+ // (3) May alias but the exact relationship between two locations is unknown.
+ // e.g. a[i..i+3] and b[j..j+3], where values of a,b,i,j are all unknown.
+ // This 'same value' optimization can apply only on case (2).
continue;
}
if (heap_values[i] == kUnknownHeapValue) {
@@ -520,6 +548,32 @@ class LSEVisitor : public HGraphDelegateVisitor {
value);
}
+ void VisitVecLoad(HVecLoad* instruction) OVERRIDE {
+ HInstruction* array = instruction->InputAt(0);
+ HInstruction* index = instruction->InputAt(1);
+ size_t vector_length = instruction->GetVectorLength();
+ VisitGetLocation(instruction,
+ array,
+ HeapLocation::kInvalidFieldOffset,
+ index,
+ vector_length,
+ HeapLocation::kDeclaringClassDefIndexForArrays);
+ }
+
+ void VisitVecStore(HVecStore* instruction) OVERRIDE {
+ HInstruction* array = instruction->InputAt(0);
+ HInstruction* index = instruction->InputAt(1);
+ HInstruction* value = instruction->InputAt(2);
+ size_t vector_length = instruction->GetVectorLength();
+ VisitSetLocation(instruction,
+ array,
+ HeapLocation::kInvalidFieldOffset,
+ index,
+ vector_length,
+ HeapLocation::kDeclaringClassDefIndexForArrays,
+ value);
+ }
+
void VisitDeoptimize(HDeoptimize* instruction) {
const ScopedArenaVector<HInstruction*>& heap_values =
heap_values_for_[instruction->GetBlock()->GetBlockId()];
@@ -529,7 +583,9 @@ class LSEVisitor : public HGraphDelegateVisitor {
continue;
}
// A store is kept as the heap value for possibly removed stores.
- if (heap_value->IsInstanceFieldSet() || heap_value->IsArraySet()) {
+ if (heap_value->IsInstanceFieldSet() ||
+ heap_value->IsArraySet() ||
+ heap_value->IsVecStore()) {
// Check whether the reference for a store is used by an environment local of
// HDeoptimize.
HInstruction* reference = heap_value->InputAt(0);
@@ -687,11 +743,6 @@ void LoadStoreElimination::Run() {
return;
}
- // TODO: analyze VecLoad/VecStore better.
- if (graph_->HasSIMD()) {
- return;
- }
-
LSEVisitor lse_visitor(graph_, heap_location_collector, side_effects_, stats_);
for (HBasicBlock* block : graph_->GetReversePostOrder()) {
lse_visitor.VisitBasicBlock(block);
diff --git a/compiler/optimizing/load_store_elimination_test.cc b/compiler/optimizing/load_store_elimination_test.cc
new file mode 100644
index 0000000000..6f42d96154
--- /dev/null
+++ b/compiler/optimizing/load_store_elimination_test.cc
@@ -0,0 +1,406 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "side_effects_analysis.h"
+#include "load_store_analysis.h"
+#include "load_store_elimination.h"
+#include "nodes.h"
+#include "optimizing_unit_test.h"
+
+#include "gtest/gtest.h"
+
+namespace art {
+
+class LoadStoreEliminationTest : public OptimizingUnitTest {
+ public:
+ LoadStoreEliminationTest() : pool_() {}
+
+ void PerformLSE() {
+ graph_->BuildDominatorTree();
+ SideEffectsAnalysis side_effects(graph_);
+ side_effects.Run();
+ LoadStoreAnalysis lsa(graph_);
+ lsa.Run();
+ LoadStoreElimination lse(graph_, side_effects, lsa, nullptr);
+ lse.Run();
+ }
+
+ void CreateTestControlFlowGraph() {
+ graph_ = CreateGraph();
+
+ entry_ = new (GetAllocator()) HBasicBlock(graph_);
+ pre_header_ = new (GetAllocator()) HBasicBlock(graph_);
+ loop_header_ = new (GetAllocator()) HBasicBlock(graph_);
+ loop_body_ = new (GetAllocator()) HBasicBlock(graph_);
+ exit_ = new (GetAllocator()) HBasicBlock(graph_);
+
+ graph_->AddBlock(entry_);
+ graph_->AddBlock(pre_header_);
+ graph_->AddBlock(loop_header_);
+ graph_->AddBlock(loop_body_);
+ graph_->AddBlock(exit_);
+
+ graph_->SetEntryBlock(entry_);
+
+ // This common CFG has been used by all cases in this load_store_elimination_test.
+ // entry
+ // |
+ // pre_header
+ // |
+ // loop_header <--+
+ // | |
+ // loop_body -----+
+ // |
+ // exit
+
+ entry_->AddSuccessor(pre_header_);
+ pre_header_->AddSuccessor(loop_header_);
+ loop_header_->AddSuccessor(exit_); // true successor
+ loop_header_->AddSuccessor(loop_body_); // false successor
+ loop_body_->AddSuccessor(loop_header_);
+
+ HInstruction* c0 = graph_->GetIntConstant(0);
+ HInstruction* c1 = graph_->GetIntConstant(1);
+ HInstruction* c4 = graph_->GetIntConstant(4);
+ HInstruction* c128 = graph_->GetIntConstant(128);
+
+ // entry block has following instructions:
+ // array, i, j, i+1, i+4.
+ array_ = new (GetAllocator()) HParameterValue(graph_->GetDexFile(),
+ dex::TypeIndex(0),
+ 0,
+ DataType::Type::kReference);
+ i_ = new (GetAllocator()) HParameterValue(graph_->GetDexFile(),
+ dex::TypeIndex(1),
+ 1,
+ DataType::Type::kInt32);
+ j_ = new (GetAllocator()) HParameterValue(graph_->GetDexFile(),
+ dex::TypeIndex(1),
+ 2,
+ DataType::Type::kInt32);
+ i_add1_ = new (GetAllocator()) HAdd(DataType::Type::kInt32, i_, c1);
+ i_add4_ = new (GetAllocator()) HAdd(DataType::Type::kInt32, i_, c4);
+ entry_->AddInstruction(array_);
+ entry_->AddInstruction(i_);
+ entry_->AddInstruction(j_);
+ entry_->AddInstruction(i_add1_);
+ entry_->AddInstruction(i_add4_);
+ entry_->AddInstruction(new (GetAllocator()) HGoto());
+
+ // pre_header block
+ pre_header_->AddInstruction(new (GetAllocator()) HGoto());
+
+ // loop header block has following instructions:
+ // phi = 0;
+ // if (phi >= 128);
+ phi_ = new (GetAllocator()) HPhi(GetAllocator(), 0, 0, DataType::Type::kInt32);
+ cmp_ = new (GetAllocator()) HGreaterThanOrEqual(phi_, c128);
+ if_ = new (GetAllocator()) HIf(cmp_);
+ loop_header_->AddPhi(phi_);
+ loop_header_->AddInstruction(cmp_);
+ loop_header_->AddInstruction(if_);
+ phi_->AddInput(c0);
+
+ // loop body block has following instructions:
+ // phi++;
+ HInstruction* inc_phi = new (GetAllocator()) HAdd(DataType::Type::kInt32, phi_, c1);
+ loop_body_->AddInstruction(inc_phi);
+ loop_body_->AddInstruction(new (GetAllocator()) HGoto());
+ phi_->AddInput(inc_phi);
+
+ // exit block
+ exit_->AddInstruction(new (GetAllocator()) HExit());
+ }
+
+ // To avoid tedious HIR assembly in test functions.
+ HInstruction* AddVecLoad(HBasicBlock* block, HInstruction* array, HInstruction* index) {
+ DCHECK(block != nullptr);
+ DCHECK(array != nullptr);
+ DCHECK(index != nullptr);
+ HInstruction* vload = new (GetAllocator()) HVecLoad(
+ GetAllocator(),
+ array,
+ index,
+ DataType::Type::kInt32,
+ SideEffects::ArrayReadOfType(DataType::Type::kInt32),
+ 4,
+ /*is_string_char_at*/ false,
+ kNoDexPc);
+ block->InsertInstructionBefore(vload, block->GetLastInstruction());
+ return vload;
+ }
+
+ HInstruction* AddVecStore(HBasicBlock* block,
+ HInstruction* array,
+ HInstruction* index,
+ HVecOperation* vdata = nullptr) {
+ DCHECK(block != nullptr);
+ DCHECK(array != nullptr);
+ DCHECK(index != nullptr);
+ if (vdata == nullptr) {
+ HInstruction* c1 = graph_->GetIntConstant(1);
+ vdata = new (GetAllocator()) HVecReplicateScalar(GetAllocator(),
+ c1,
+ DataType::Type::kInt32,
+ 4,
+ kNoDexPc);
+ block->InsertInstructionBefore(vdata, block->GetLastInstruction());
+ }
+ HInstruction* vstore = new (GetAllocator()) HVecStore(
+ GetAllocator(),
+ array,
+ index,
+ vdata,
+ DataType::Type::kInt32,
+ SideEffects::ArrayWriteOfType(DataType::Type::kInt32),
+ 4,
+ kNoDexPc);
+ block->InsertInstructionBefore(vstore, block->GetLastInstruction());
+ return vstore;
+ }
+
+ HInstruction* AddArrayGet(HBasicBlock* block, HInstruction* array, HInstruction* index) {
+ DCHECK(block != nullptr);
+ DCHECK(array != nullptr);
+ DCHECK(index != nullptr);
+ HInstruction* get = new (GetAllocator()) HArrayGet(array, index, DataType::Type::kInt32, 0);
+ block->InsertInstructionBefore(get, block->GetLastInstruction());
+ return get;
+ }
+
+ HInstruction* AddArraySet(HBasicBlock* block,
+ HInstruction* array,
+ HInstruction* index,
+ HInstruction* data = nullptr) {
+ DCHECK(block != nullptr);
+ DCHECK(array != nullptr);
+ DCHECK(index != nullptr);
+ if (data == nullptr) {
+ data = graph_->GetIntConstant(1);
+ }
+ HInstruction* store = new (GetAllocator()) HArraySet(array,
+ index,
+ data,
+ DataType::Type::kInt32,
+ 0);
+ block->InsertInstructionBefore(store, block->GetLastInstruction());
+ return store;
+ }
+
+ ArenaPool pool_;
+
+ HGraph* graph_;
+ HBasicBlock* entry_;
+ HBasicBlock* pre_header_;
+ HBasicBlock* loop_header_;
+ HBasicBlock* loop_body_;
+ HBasicBlock* exit_;
+
+ HInstruction* array_;
+ HInstruction* i_;
+ HInstruction* j_;
+ HInstruction* i_add1_;
+ HInstruction* i_add4_;
+
+ HPhi* phi_;
+ HInstruction* cmp_;
+ HInstruction* if_;
+};
+
+TEST_F(LoadStoreEliminationTest, ArrayGetSetElimination) {
+ CreateTestControlFlowGraph();
+
+ HInstruction* c1 = graph_->GetIntConstant(1);
+ HInstruction* c2 = graph_->GetIntConstant(2);
+ HInstruction* c3 = graph_->GetIntConstant(3);
+
+ // array[1] = 1;
+ // x = array[1]; <--- Remove.
+ // y = array[2];
+ // array[1] = 1; <--- Remove, since it stores same value.
+ // array[i] = 3; <--- MAY alias.
+ // array[1] = 1; <--- Cannot remove, even if it stores the same value.
+ AddArraySet(entry_, array_, c1, c1);
+ HInstruction* load1 = AddArrayGet(entry_, array_, c1);
+ HInstruction* load2 = AddArrayGet(entry_, array_, c2);
+ HInstruction* store1 = AddArraySet(entry_, array_, c1, c1);
+ AddArraySet(entry_, array_, i_, c3);
+ HInstruction* store2 = AddArraySet(entry_, array_, c1, c1);
+
+ PerformLSE();
+
+ ASSERT_TRUE(IsRemoved(load1));
+ ASSERT_FALSE(IsRemoved(load2));
+ ASSERT_TRUE(IsRemoved(store1));
+ ASSERT_FALSE(IsRemoved(store2));
+}
+
+TEST_F(LoadStoreEliminationTest, SameHeapValue) {
+ CreateTestControlFlowGraph();
+
+ HInstruction* c1 = graph_->GetIntConstant(1);
+ HInstruction* c2 = graph_->GetIntConstant(2);
+
+ // Test LSE handling same value stores on array.
+ // array[1] = 1;
+ // array[2] = 1;
+ // array[1] = 1; <--- Can remove.
+ // array[1] = 2; <--- Can NOT remove.
+ AddArraySet(entry_, array_, c1, c1);
+ AddArraySet(entry_, array_, c2, c1);
+ HInstruction* store1 = AddArraySet(entry_, array_, c1, c1);
+ HInstruction* store2 = AddArraySet(entry_, array_, c1, c2);
+
+ // Test LSE handling same value stores on vector.
+ // vdata = [0x1, 0x2, 0x3, 0x4, ...]
+ // VecStore array[i...] = vdata;
+ // VecStore array[j...] = vdata; <--- MAY ALIAS.
+ // VecStore array[i...] = vdata; <--- Cannot Remove, even if it's same value.
+ AddVecStore(entry_, array_, i_);
+ AddVecStore(entry_, array_, j_);
+ HInstruction* vstore1 = AddVecStore(entry_, array_, i_);
+
+ // VecStore array[i...] = vdata;
+ // VecStore array[i+1...] = vdata; <--- MAY alias due to partial overlap.
+ // VecStore array[i...] = vdata; <--- Cannot remove, even if it's same value.
+ AddVecStore(entry_, array_, i_);
+ AddVecStore(entry_, array_, i_add1_);
+ HInstruction* vstore2 = AddVecStore(entry_, array_, i_);
+
+ PerformLSE();
+
+ ASSERT_TRUE(IsRemoved(store1));
+ ASSERT_FALSE(IsRemoved(store2));
+ ASSERT_FALSE(IsRemoved(vstore1));
+ ASSERT_FALSE(IsRemoved(vstore2));
+}
+
+TEST_F(LoadStoreEliminationTest, OverlappingLoadStore) {
+ CreateTestControlFlowGraph();
+
+ HInstruction* c1 = graph_->GetIntConstant(1);
+
+ // Test LSE handling array LSE when there is vector store in between.
+ // a[i] = 1;
+ // .. = a[i]; <-- Remove.
+ // a[i,i+1,i+2,i+3] = data; <-- PARTIAL OVERLAP !
+ // .. = a[i]; <-- Cannot remove.
+ AddArraySet(entry_, array_, i_, c1);
+ HInstruction* load1 = AddArrayGet(entry_, array_, i_);
+ AddVecStore(entry_, array_, i_);
+ HInstruction* load2 = AddArrayGet(entry_, array_, i_);
+
+ // Test LSE handling vector load/store partial overlap.
+ // a[i,i+1,i+2,i+3] = data;
+ // a[i+4,i+5,i+6,i+7] = data;
+ // .. = a[i,i+1,i+2,i+3];
+ // .. = a[i+4,i+5,i+6,i+7];
+ // a[i+1,i+2,i+3,i+4] = data; <-- PARTIAL OVERLAP !
+ // .. = a[i,i+1,i+2,i+3];
+ // .. = a[i+4,i+5,i+6,i+7];
+ AddVecStore(entry_, array_, i_);
+ AddVecStore(entry_, array_, i_add4_);
+ HInstruction* vload1 = AddVecLoad(entry_, array_, i_);
+ HInstruction* vload2 = AddVecLoad(entry_, array_, i_add4_);
+ AddVecStore(entry_, array_, i_add1_);
+ HInstruction* vload3 = AddVecLoad(entry_, array_, i_);
+ HInstruction* vload4 = AddVecLoad(entry_, array_, i_add4_);
+
+ // Test LSE handling vector LSE when there is array store in between.
+ // a[i,i+1,i+2,i+3] = data;
+ // a[i+1] = 1; <-- PARTIAL OVERLAP !
+ // .. = a[i,i+1,i+2,i+3];
+ AddVecStore(entry_, array_, i_);
+ AddArraySet(entry_, array_, i_, c1);
+ HInstruction* vload5 = AddVecLoad(entry_, array_, i_);
+
+ PerformLSE();
+
+ ASSERT_TRUE(IsRemoved(load1));
+ ASSERT_FALSE(IsRemoved(load2));
+
+ ASSERT_TRUE(IsRemoved(vload1));
+ ASSERT_TRUE(IsRemoved(vload2));
+ ASSERT_FALSE(IsRemoved(vload3));
+ ASSERT_FALSE(IsRemoved(vload4));
+
+ ASSERT_FALSE(IsRemoved(vload5));
+}
+
+// function (int[] a, int j) {
+// a[j] = 1;
+// for (int i=0; i<128; i++) {
+// /* doesn't do any write */
+// }
+// a[j] = 1;
+TEST_F(LoadStoreEliminationTest, Loop1) {
+ CreateTestControlFlowGraph();
+
+ HInstruction* c1 = graph_->GetIntConstant(1);
+
+ // a[j] = 1
+ AddArraySet(pre_header_, array_, j_, c1);
+
+ // LOOP BODY:
+ // .. = a[i,i+1,i+2,i+3];
+ AddVecLoad(loop_body_, array_, phi_);
+
+ // a[j] = 1;
+ HInstruction* array_set = AddArraySet(exit_, array_, j_, c1);
+
+ PerformLSE();
+
+ ASSERT_TRUE(IsRemoved(array_set));
+}
+
+// function (int[] a, int index) {
+// a[index] = 1;
+// int[] b = new int[128];
+// for (int i=0; i<128; i++) {
+// a[i,i+1,i+2,i+3] = vdata;
+// b[i,i+1,i+2,i+3] = a[i,i+1,i+2,i+3];
+// }
+// a[index] = 1;
+// }
+TEST_F(LoadStoreEliminationTest, Loop2) {
+ CreateTestControlFlowGraph();
+
+ HInstruction* c0 = graph_->GetIntConstant(0);
+ HInstruction* c1 = graph_->GetIntConstant(1);
+ HInstruction* c128 = graph_->GetIntConstant(128);
+
+ HInstruction* array_b = new (GetAllocator()) HNewArray(c0, c128, 0);
+ entry_->AddInstruction(array_b);
+
+ // a[index] = 1;
+ AddArraySet(pre_header_, array_, i_, c1);
+
+ // a[i,i+1,i+2,i+3] = vdata;
+ // b[i,i+1,i+2,i+3] = a[i,i+1,i+2,i+3];
+ AddVecStore(loop_body_, array_, phi_);
+ HInstruction* vload = AddVecLoad(loop_body_, array_, phi_);
+ AddVecStore(loop_body_, array_b, phi_, vload->AsVecLoad());
+
+ // a[index] = 1;
+ HInstruction* a_set = AddArraySet(exit_, array_, i_, c1);
+
+ PerformLSE();
+
+ ASSERT_TRUE(IsRemoved(vload));
+ ASSERT_FALSE(IsRemoved(a_set)); // Cannot remove due to side effect in loop.
+}
+
+} // namespace art