diff options
author | Alex Light <allight@google.com> | 2021-04-21 17:04:13 -0700 |
---|---|---|
committer | Alex Light <allight@google.com> | 2021-04-26 09:10:12 -0700 |
commit | bb550e415da77e7e21c8f800657984c145bb42e1 (patch) | |
tree | 0596ce5d5b1b2f58cef50f8ef133febdd053399c /compiler/optimizing/load_store_analysis_test.cc | |
parent | adfa1ad73a3a557bdec67d59894139b2727aaa7d (diff) |
Fix issue with Partial LSE and casts/instanceof
If PartialLSE encounters an instanceof or check-cast before the
escapes it could not correctly handle it. Due to how java language
code is typically developed and compiled this is generally not a
problem but could lead to incorrect codegen on release builds or
DCHECK failures on debug builds. This fixes the issues by (1) causing
partial LSE to consider check-cast and instance-ofs to be escaping.
This also updates the instruction simplifier to be much more
aggressive in removing instance-of and check-casts.
Test: ./test.py --host
Bug: 186041085
Change-Id: Ia513c4210a87a0dfa92f10adc530e17ee631d006
Diffstat (limited to 'compiler/optimizing/load_store_analysis_test.cc')
-rw-r--r-- | compiler/optimizing/load_store_analysis_test.cc | 188 |
1 files changed, 184 insertions, 4 deletions
diff --git a/compiler/optimizing/load_store_analysis_test.cc b/compiler/optimizing/load_store_analysis_test.cc index fd15802fd3..67abc0fc20 100644 --- a/compiler/optimizing/load_store_analysis_test.cc +++ b/compiler/optimizing/load_store_analysis_test.cc @@ -38,9 +38,9 @@ namespace art { -class LoadStoreAnalysisTest : public OptimizingUnitTest { +class LoadStoreAnalysisTest : public CommonCompilerTest, public OptimizingUnitTestHelper { public: - LoadStoreAnalysisTest() : graph_(CreateGraph()) {} + LoadStoreAnalysisTest() {} AdjacencyListGraph SetupFromAdjacencyList( const std::string_view entry_name, @@ -58,11 +58,10 @@ class LoadStoreAnalysisTest : public OptimizingUnitTest { } void CheckReachability(const AdjacencyListGraph& adj, const std::vector<AdjacencyListGraph::Edge>& reach); - - HGraph* graph_; }; TEST_F(LoadStoreAnalysisTest, ArrayHeapLocations) { + CreateGraph(); HBasicBlock* entry = new (GetAllocator()) HBasicBlock(graph_); graph_->AddBlock(entry); graph_->SetEntryBlock(entry); @@ -145,6 +144,7 @@ TEST_F(LoadStoreAnalysisTest, ArrayHeapLocations) { } TEST_F(LoadStoreAnalysisTest, FieldHeapLocations) { + CreateGraph(); HBasicBlock* entry = new (GetAllocator()) HBasicBlock(graph_); graph_->AddBlock(entry); graph_->SetEntryBlock(entry); @@ -225,6 +225,7 @@ TEST_F(LoadStoreAnalysisTest, FieldHeapLocations) { } TEST_F(LoadStoreAnalysisTest, ArrayIndexAliasingTest) { + CreateGraph(); HBasicBlock* entry = new (GetAllocator()) HBasicBlock(graph_); graph_->AddBlock(entry); graph_->SetEntryBlock(entry); @@ -318,6 +319,7 @@ TEST_F(LoadStoreAnalysisTest, ArrayIndexAliasingTest) { } TEST_F(LoadStoreAnalysisTest, ArrayAliasingTest) { + CreateGraph(); HBasicBlock* entry = new (GetAllocator()) HBasicBlock(graph_); graph_->AddBlock(entry); graph_->SetEntryBlock(entry); @@ -529,6 +531,7 @@ TEST_F(LoadStoreAnalysisTest, ArrayAliasingTest) { } TEST_F(LoadStoreAnalysisTest, ArrayIndexCalculationOverflowTest) { + CreateGraph(); HBasicBlock* entry = new (GetAllocator()) HBasicBlock(graph_); graph_->AddBlock(entry); graph_->SetEntryBlock(entry); @@ -647,6 +650,7 @@ TEST_F(LoadStoreAnalysisTest, ArrayIndexCalculationOverflowTest) { } TEST_F(LoadStoreAnalysisTest, TestHuntOriginalRef) { + CreateGraph(); HBasicBlock* entry = new (GetAllocator()) HBasicBlock(graph_); graph_->AddBlock(entry); graph_->SetEntryBlock(entry); @@ -753,6 +757,7 @@ void LoadStoreAnalysisTest::CheckReachability(const AdjacencyListGraph& adj, } TEST_F(LoadStoreAnalysisTest, ReachabilityTest1) { + CreateGraph(); AdjacencyListGraph blks(SetupFromAdjacencyList( "entry", "exit", @@ -768,6 +773,7 @@ TEST_F(LoadStoreAnalysisTest, ReachabilityTest1) { } TEST_F(LoadStoreAnalysisTest, ReachabilityTest2) { + CreateGraph(); AdjacencyListGraph blks(SetupFromAdjacencyList( "entry", "exit", @@ -784,6 +790,7 @@ TEST_F(LoadStoreAnalysisTest, ReachabilityTest2) { } TEST_F(LoadStoreAnalysisTest, ReachabilityTest3) { + CreateGraph(); AdjacencyListGraph blks(SetupFromAdjacencyList("entry", "exit", { { "entry", "loop-header" }, @@ -839,6 +846,7 @@ static bool AreExclusionsIndependent(HGraph* graph, const ExecutionSubgraph* esg // // EXIT // obj.field; TEST_F(LoadStoreAnalysisTest, PartialEscape) { + CreateGraph(); AdjacencyListGraph blks(SetupFromAdjacencyList( "entry", "exit", @@ -946,6 +954,7 @@ TEST_F(LoadStoreAnalysisTest, PartialEscape) { // // EXIT // obj.field2; TEST_F(LoadStoreAnalysisTest, PartialEscape2) { + CreateGraph(); AdjacencyListGraph blks(SetupFromAdjacencyList( "entry", "exit", @@ -1054,6 +1063,7 @@ TEST_F(LoadStoreAnalysisTest, PartialEscape2) { // // EXIT // obj.field; TEST_F(LoadStoreAnalysisTest, PartialEscape3) { + CreateGraph(); AdjacencyListGraph blks(SetupFromAdjacencyList( "entry", "exit", @@ -1162,6 +1172,171 @@ TEST_F(LoadStoreAnalysisTest, PartialEscape3) { ASSERT_TRUE(contents.find(blks.Get("exit")) != contents.end()); } +// For simplicity Partial LSE considers check-casts to escape. It means we don't +// need to worry about inserting throws. +// // ENTRY +// obj = new Obj(); +// obj.field = 10; +// if (parameter_value) { +// // LEFT +// (Foo)obj; +// } else { +// // RIGHT +// obj.field = 20; +// } +// // EXIT +// obj.field; +TEST_F(LoadStoreAnalysisTest, PartialEscape4) { + CreateGraph(); + AdjacencyListGraph blks(SetupFromAdjacencyList( + "entry", + "exit", + { { "entry", "left" }, { "entry", "right" }, { "left", "exit" }, { "right", "exit" } })); + HBasicBlock* entry = blks.Get("entry"); + HBasicBlock* left = blks.Get("left"); + HBasicBlock* right = blks.Get("right"); + HBasicBlock* exit = blks.Get("exit"); + + HInstruction* bool_value = new (GetAllocator()) + HParameterValue(graph_->GetDexFile(), dex::TypeIndex(1), 1, DataType::Type::kBool); + HInstruction* c10 = graph_->GetIntConstant(10); + HInstruction* c20 = graph_->GetIntConstant(20); + HInstruction* cls = MakeClassLoad(); + HInstruction* new_inst = MakeNewInstance(cls); + + HInstruction* write_entry = MakeIFieldSet(new_inst, c10, MemberOffset(32)); + HInstruction* if_inst = new (GetAllocator()) HIf(bool_value); + entry->AddInstruction(bool_value); + entry->AddInstruction(cls); + entry->AddInstruction(new_inst); + entry->AddInstruction(write_entry); + entry->AddInstruction(if_inst); + + ScopedNullHandle<mirror::Class> null_klass_; + HInstruction* cls2 = MakeClassLoad(); + HInstruction* check_cast = new (GetAllocator()) HCheckCast( + new_inst, cls2, TypeCheckKind::kExactCheck, null_klass_, 0, GetAllocator(), nullptr, nullptr); + HInstruction* goto_left = new (GetAllocator()) HGoto(); + left->AddInstruction(cls2); + left->AddInstruction(check_cast); + left->AddInstruction(goto_left); + + HInstruction* write_right = MakeIFieldSet(new_inst, c20, MemberOffset(32)); + HInstruction* goto_right = new (GetAllocator()) HGoto(); + right->AddInstruction(write_right); + right->AddInstruction(goto_right); + + HInstruction* read_final = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); + exit->AddInstruction(read_final); + + ScopedArenaAllocator allocator(graph_->GetArenaStack()); + LoadStoreAnalysis lsa(graph_, nullptr, &allocator, LoadStoreAnalysisType::kFull); + lsa.Run(); + + const HeapLocationCollector& heap_location_collector = lsa.GetHeapLocationCollector(); + ReferenceInfo* info = heap_location_collector.FindReferenceInfoOf(new_inst); + const ExecutionSubgraph* esg = info->GetNoEscapeSubgraph(); + + ASSERT_TRUE(esg->IsValid()); + ASSERT_TRUE(IsValidSubgraph(esg)); + ASSERT_TRUE(AreExclusionsIndependent(graph_, esg)); + std::unordered_set<const HBasicBlock*> contents(esg->ReachableBlocks().begin(), + esg->ReachableBlocks().end()); + + ASSERT_EQ(contents.size(), 3u); + ASSERT_TRUE(contents.find(blks.Get("left")) == contents.end()); + + ASSERT_TRUE(contents.find(blks.Get("right")) != contents.end()); + ASSERT_TRUE(contents.find(blks.Get("entry")) != contents.end()); + ASSERT_TRUE(contents.find(blks.Get("exit")) != contents.end()); +} + +// For simplicity Partial LSE considers instance-ofs with bitvectors to escape. +// // ENTRY +// obj = new Obj(); +// obj.field = 10; +// if (parameter_value) { +// // LEFT +// obj instanceof /*bitvector*/ Foo; +// } else { +// // RIGHT +// obj.field = 20; +// } +// // EXIT +// obj.field; +TEST_F(LoadStoreAnalysisTest, PartialEscape5) { + VariableSizedHandleScope vshs(Thread::Current()); + CreateGraph(&vshs); + AdjacencyListGraph blks(SetupFromAdjacencyList( + "entry", + "exit", + { { "entry", "left" }, { "entry", "right" }, { "left", "exit" }, { "right", "exit" } })); + HBasicBlock* entry = blks.Get("entry"); + HBasicBlock* left = blks.Get("left"); + HBasicBlock* right = blks.Get("right"); + HBasicBlock* exit = blks.Get("exit"); + + HInstruction* bool_value = new (GetAllocator()) + HParameterValue(graph_->GetDexFile(), dex::TypeIndex(1), 1, DataType::Type::kBool); + HInstruction* c10 = graph_->GetIntConstant(10); + HInstruction* c20 = graph_->GetIntConstant(20); + HIntConstant* bs1 = graph_->GetIntConstant(0xffff); + HIntConstant* bs2 = graph_->GetIntConstant(0x00ff); + HInstruction* cls = MakeClassLoad(); + HInstruction* null_const = graph_->GetNullConstant(); + HInstruction* new_inst = MakeNewInstance(cls); + + HInstruction* write_entry = MakeIFieldSet(new_inst, c10, MemberOffset(32)); + HInstruction* if_inst = new (GetAllocator()) HIf(bool_value); + entry->AddInstruction(bool_value); + entry->AddInstruction(cls); + entry->AddInstruction(new_inst); + entry->AddInstruction(write_entry); + entry->AddInstruction(if_inst); + + ScopedNullHandle<mirror::Class> null_klass_; + HInstruction* instanceof = new (GetAllocator()) HInstanceOf(new_inst, + null_const, + TypeCheckKind::kBitstringCheck, + null_klass_, + 0, + GetAllocator(), + bs1, + bs2); + HInstruction* goto_left = new (GetAllocator()) HGoto(); + left->AddInstruction(instanceof); + left->AddInstruction(goto_left); + + HInstruction* write_right = MakeIFieldSet(new_inst, c20, MemberOffset(32)); + HInstruction* goto_right = new (GetAllocator()) HGoto(); + right->AddInstruction(write_right); + right->AddInstruction(goto_right); + + HInstruction* read_final = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); + exit->AddInstruction(read_final); + + ScopedArenaAllocator allocator(graph_->GetArenaStack()); + LoadStoreAnalysis lsa(graph_, nullptr, &allocator, LoadStoreAnalysisType::kFull); + lsa.Run(); + + const HeapLocationCollector& heap_location_collector = lsa.GetHeapLocationCollector(); + ReferenceInfo* info = heap_location_collector.FindReferenceInfoOf(new_inst); + const ExecutionSubgraph* esg = info->GetNoEscapeSubgraph(); + + ASSERT_TRUE(esg->IsValid()); + ASSERT_TRUE(IsValidSubgraph(esg)); + ASSERT_TRUE(AreExclusionsIndependent(graph_, esg)); + std::unordered_set<const HBasicBlock*> contents(esg->ReachableBlocks().begin(), + esg->ReachableBlocks().end()); + + ASSERT_EQ(contents.size(), 3u); + ASSERT_TRUE(contents.find(blks.Get("left")) == contents.end()); + + ASSERT_TRUE(contents.find(blks.Get("right")) != contents.end()); + ASSERT_TRUE(contents.find(blks.Get("entry")) != contents.end()); + ASSERT_TRUE(contents.find(blks.Get("exit")) != contents.end()); +} + // before we had predicated-set we needed to be able to remove the store as // well. This test makes sure that still works. // // ENTRY @@ -1177,6 +1352,7 @@ TEST_F(LoadStoreAnalysisTest, PartialEscape3) { // // call_func prevents the elimination of this store. // obj.f2 = 0; TEST_F(LoadStoreAnalysisTest, TotalEscapeAdjacentNoPredicated) { + CreateGraph(); AdjacencyListGraph blks(SetupFromAdjacencyList( "entry", "exit", @@ -1288,6 +1464,7 @@ TEST_F(LoadStoreAnalysisTest, TotalEscapeAdjacentNoPredicated) { // // call_func prevents the elimination of this store. // obj.f2 = 0; TEST_F(LoadStoreAnalysisTest, TotalEscapeAdjacent) { + CreateGraph(); AdjacencyListGraph blks(SetupFromAdjacencyList( "entry", "exit", @@ -1397,6 +1574,7 @@ TEST_F(LoadStoreAnalysisTest, TotalEscapeAdjacent) { // // EXIT // obj.f0; TEST_F(LoadStoreAnalysisTest, TotalEscape) { + CreateGraph(); AdjacencyListGraph blks(SetupFromAdjacencyList( "entry", "exit", @@ -1509,6 +1687,7 @@ TEST_F(LoadStoreAnalysisTest, TotalEscape) { // // EXIT // return obj; TEST_F(LoadStoreAnalysisTest, TotalEscape2) { + CreateGraph(); AdjacencyListGraph blks(SetupFromAdjacencyList("entry", "exit", { { "entry", "exit" } })); HBasicBlock* entry = blks.Get("entry"); HBasicBlock* exit = blks.Get("exit"); @@ -1587,6 +1766,7 @@ TEST_F(LoadStoreAnalysisTest, TotalEscape2) { // // EXIT // obj.f0 TEST_F(LoadStoreAnalysisTest, DoubleDiamondEscape) { + CreateGraph(); AdjacencyListGraph blks(SetupFromAdjacencyList("entry", "exit", { { "entry", "high_left" }, |