You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
562 lines
21 KiB
562 lines
21 KiB
/*
|
|
* Copyright (C) 2021 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 "instruction_simplifier.h"
|
|
|
|
#include <initializer_list>
|
|
#include <tuple>
|
|
|
|
#include "gtest/gtest.h"
|
|
|
|
#include "class_root-inl.h"
|
|
#include "nodes.h"
|
|
#include "optimizing/data_type.h"
|
|
#include "optimizing_unit_test.h"
|
|
|
|
namespace art {
|
|
|
|
namespace mirror {
|
|
class ClassExt;
|
|
class Throwable;
|
|
} // namespace mirror
|
|
|
|
template<typename SuperClass>
|
|
class InstructionSimplifierTestBase : public SuperClass, public OptimizingUnitTestHelper {
|
|
public:
|
|
void SetUp() override {
|
|
SuperClass::SetUp();
|
|
gLogVerbosity.compiler = true;
|
|
}
|
|
|
|
void TearDown() override {
|
|
SuperClass::TearDown();
|
|
gLogVerbosity.compiler = false;
|
|
}
|
|
};
|
|
|
|
class InstructionSimplifierTest : public InstructionSimplifierTestBase<CommonCompilerTest> {};
|
|
|
|
// Various configs we can use for testing. Currently used in PartialComparison tests.
|
|
enum class InstanceOfKind {
|
|
kSelf,
|
|
kUnrelatedLoaded,
|
|
kUnrelatedUnloaded,
|
|
kSupertype,
|
|
};
|
|
|
|
std::ostream& operator<<(std::ostream& os, const InstanceOfKind& comp) {
|
|
switch (comp) {
|
|
case InstanceOfKind::kSupertype:
|
|
return os << "kSupertype";
|
|
case InstanceOfKind::kSelf:
|
|
return os << "kSelf";
|
|
case InstanceOfKind::kUnrelatedLoaded:
|
|
return os << "kUnrelatedLoaded";
|
|
case InstanceOfKind::kUnrelatedUnloaded:
|
|
return os << "kUnrelatedUnloaded";
|
|
}
|
|
}
|
|
|
|
class InstanceOfInstructionSimplifierTestGroup
|
|
: public InstructionSimplifierTestBase<CommonCompilerTestWithParam<InstanceOfKind>> {
|
|
public:
|
|
bool GetConstantResult() const {
|
|
switch (GetParam()) {
|
|
case InstanceOfKind::kSupertype:
|
|
case InstanceOfKind::kSelf:
|
|
return true;
|
|
case InstanceOfKind::kUnrelatedLoaded:
|
|
case InstanceOfKind::kUnrelatedUnloaded:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
std::pair<HLoadClass*, HLoadClass*> GetLoadClasses(VariableSizedHandleScope* vshs) {
|
|
InstanceOfKind kind = GetParam();
|
|
ScopedObjectAccess soa(Thread::Current());
|
|
// New inst always needs to have a valid rti since we dcheck that.
|
|
HLoadClass* new_inst = MakeClassLoad(
|
|
/* ti= */ std::nullopt, vshs->NewHandle<mirror::Class>(GetClassRoot<mirror::ClassExt>()));
|
|
new_inst->SetValidLoadedClassRTI();
|
|
if (kind == InstanceOfKind::kSelf) {
|
|
return {new_inst, new_inst};
|
|
}
|
|
if (kind == InstanceOfKind::kUnrelatedUnloaded) {
|
|
HLoadClass* target_class = MakeClassLoad();
|
|
EXPECT_FALSE(target_class->GetLoadedClassRTI().IsValid());
|
|
return {new_inst, target_class};
|
|
}
|
|
// Force both classes to be a real classes.
|
|
// For simplicity we use class-roots as the types. The new-inst will always
|
|
// be a ClassExt, unrelated-loaded will always be Throwable and super will
|
|
// always be Object
|
|
HLoadClass* target_class = MakeClassLoad(
|
|
/* ti= */ std::nullopt,
|
|
vshs->NewHandle<mirror::Class>(kind == InstanceOfKind::kSupertype ?
|
|
GetClassRoot<mirror::Object>() :
|
|
GetClassRoot<mirror::Throwable>()));
|
|
target_class->SetValidLoadedClassRTI();
|
|
EXPECT_TRUE(target_class->GetLoadedClassRTI().IsValid());
|
|
return {new_inst, target_class};
|
|
}
|
|
};
|
|
|
|
// // ENTRY
|
|
// switch (param) {
|
|
// case 1:
|
|
// obj1 = param2; break;
|
|
// case 2:
|
|
// obj1 = param3; break;
|
|
// default:
|
|
// obj2 = new Obj();
|
|
// }
|
|
// val_phi = PHI[3,4,10]
|
|
// target_phi = PHI[param2, param3, obj2]
|
|
// return PredFieldGet[val_phi, target_phi] => PredFieldGet[val_phi, target_phi]
|
|
TEST_F(InstructionSimplifierTest, SimplifyPredicatedFieldGetNoMerge) {
|
|
ScopedObjectAccess soa(Thread::Current());
|
|
VariableSizedHandleScope vshs(soa.Self());
|
|
CreateGraph(&vshs);
|
|
AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
|
|
"exit",
|
|
{{"entry", "case1"},
|
|
{"entry", "case2"},
|
|
{"entry", "case3"},
|
|
{"case1", "breturn"},
|
|
{"case2", "breturn"},
|
|
{"case3", "breturn"},
|
|
{"breturn", "exit"}}));
|
|
#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
|
|
GET_BLOCK(entry);
|
|
GET_BLOCK(exit);
|
|
GET_BLOCK(case1);
|
|
GET_BLOCK(case2);
|
|
GET_BLOCK(case3);
|
|
GET_BLOCK(breturn);
|
|
#undef GET_BLOCK
|
|
|
|
HInstruction* bool_value = MakeParam(DataType::Type::kInt32);
|
|
HInstruction* obj1_param = MakeParam(DataType::Type::kReference);
|
|
HInstruction* obj2_param = MakeParam(DataType::Type::kReference);
|
|
HInstruction* c3 = graph_->GetIntConstant(3);
|
|
HInstruction* c4 = graph_->GetIntConstant(4);
|
|
HInstruction* c10 = graph_->GetIntConstant(10);
|
|
|
|
HInstruction* cls = MakeClassLoad();
|
|
HInstruction* switch_inst = new (GetAllocator()) HPackedSwitch(0, 2, bool_value);
|
|
entry->AddInstruction(cls);
|
|
entry->AddInstruction(switch_inst);
|
|
ManuallyBuildEnvFor(cls, {});
|
|
|
|
HInstruction* goto_c1 = new (GetAllocator()) HGoto();
|
|
case1->AddInstruction(goto_c1);
|
|
|
|
HInstruction* goto_c2 = new (GetAllocator()) HGoto();
|
|
case2->AddInstruction(goto_c2);
|
|
|
|
HInstruction* obj3 = MakeNewInstance(cls);
|
|
HInstruction* goto_c3 = new (GetAllocator()) HGoto();
|
|
case3->AddInstruction(obj3);
|
|
case3->AddInstruction(goto_c3);
|
|
|
|
HPhi* val_phi = MakePhi({c3, c4, c10});
|
|
HPhi* obj_phi = MakePhi({obj1_param, obj2_param, obj3});
|
|
HPredicatedInstanceFieldGet* read_end =
|
|
new (GetAllocator()) HPredicatedInstanceFieldGet(obj_phi,
|
|
nullptr,
|
|
val_phi,
|
|
val_phi->GetType(),
|
|
MemberOffset(10),
|
|
false,
|
|
42,
|
|
0,
|
|
graph_->GetDexFile(),
|
|
0);
|
|
HInstruction* return_exit = new (GetAllocator()) HReturn(read_end);
|
|
breturn->AddPhi(val_phi);
|
|
breturn->AddPhi(obj_phi);
|
|
breturn->AddInstruction(read_end);
|
|
breturn->AddInstruction(return_exit);
|
|
|
|
SetupExit(exit);
|
|
|
|
LOG(INFO) << "Pre simplification " << blks;
|
|
graph_->ClearDominanceInformation();
|
|
graph_->BuildDominatorTree();
|
|
InstructionSimplifier simp(graph_, /*codegen=*/nullptr);
|
|
simp.Run();
|
|
|
|
LOG(INFO) << "Post simplify " << blks;
|
|
|
|
EXPECT_INS_RETAINED(read_end);
|
|
|
|
EXPECT_INS_EQ(read_end->GetTarget(), obj_phi);
|
|
EXPECT_INS_EQ(read_end->GetDefaultValue(), val_phi);
|
|
}
|
|
|
|
// // ENTRY
|
|
// switch (param) {
|
|
// case 1:
|
|
// obj1 = param2; break;
|
|
// case 2:
|
|
// obj1 = param3; break;
|
|
// default:
|
|
// obj2 = new Obj();
|
|
// }
|
|
// val_phi = PHI[3,3,10]
|
|
// target_phi = PHI[param2, param3, obj2]
|
|
// return PredFieldGet[val_phi, target_phi] => PredFieldGet[3, target_phi]
|
|
TEST_F(InstructionSimplifierTest, SimplifyPredicatedFieldGetMerge) {
|
|
ScopedObjectAccess soa(Thread::Current());
|
|
VariableSizedHandleScope vshs(soa.Self());
|
|
CreateGraph(&vshs);
|
|
AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
|
|
"exit",
|
|
{{"entry", "case1"},
|
|
{"entry", "case2"},
|
|
{"entry", "case3"},
|
|
{"case1", "breturn"},
|
|
{"case2", "breturn"},
|
|
{"case3", "breturn"},
|
|
{"breturn", "exit"}}));
|
|
#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
|
|
GET_BLOCK(entry);
|
|
GET_BLOCK(exit);
|
|
GET_BLOCK(case1);
|
|
GET_BLOCK(case2);
|
|
GET_BLOCK(case3);
|
|
GET_BLOCK(breturn);
|
|
#undef GET_BLOCK
|
|
|
|
HInstruction* bool_value = MakeParam(DataType::Type::kInt32);
|
|
HInstruction* obj1_param = MakeParam(DataType::Type::kReference);
|
|
HInstruction* obj2_param = MakeParam(DataType::Type::kReference);
|
|
HInstruction* c3 = graph_->GetIntConstant(3);
|
|
HInstruction* c10 = graph_->GetIntConstant(10);
|
|
|
|
HInstruction* cls = MakeClassLoad();
|
|
HInstruction* switch_inst = new (GetAllocator()) HPackedSwitch(0, 2, bool_value);
|
|
entry->AddInstruction(cls);
|
|
entry->AddInstruction(switch_inst);
|
|
ManuallyBuildEnvFor(cls, {});
|
|
|
|
HInstruction* goto_c1 = new (GetAllocator()) HGoto();
|
|
case1->AddInstruction(goto_c1);
|
|
|
|
HInstruction* goto_c2 = new (GetAllocator()) HGoto();
|
|
case2->AddInstruction(goto_c2);
|
|
|
|
HInstruction* obj3 = MakeNewInstance(cls);
|
|
HInstruction* goto_c3 = new (GetAllocator()) HGoto();
|
|
case3->AddInstruction(obj3);
|
|
case3->AddInstruction(goto_c3);
|
|
|
|
HPhi* val_phi = MakePhi({c3, c3, c10});
|
|
HPhi* obj_phi = MakePhi({obj1_param, obj2_param, obj3});
|
|
HPredicatedInstanceFieldGet* read_end =
|
|
new (GetAllocator()) HPredicatedInstanceFieldGet(obj_phi,
|
|
nullptr,
|
|
val_phi,
|
|
val_phi->GetType(),
|
|
MemberOffset(10),
|
|
false,
|
|
42,
|
|
0,
|
|
graph_->GetDexFile(),
|
|
0);
|
|
HInstruction* return_exit = new (GetAllocator()) HReturn(read_end);
|
|
breturn->AddPhi(val_phi);
|
|
breturn->AddPhi(obj_phi);
|
|
breturn->AddInstruction(read_end);
|
|
breturn->AddInstruction(return_exit);
|
|
|
|
SetupExit(exit);
|
|
|
|
LOG(INFO) << "Pre simplification " << blks;
|
|
graph_->ClearDominanceInformation();
|
|
graph_->BuildDominatorTree();
|
|
InstructionSimplifier simp(graph_, /*codegen=*/nullptr);
|
|
simp.Run();
|
|
|
|
LOG(INFO) << "Post simplify " << blks;
|
|
|
|
EXPECT_FALSE(obj3->CanBeNull());
|
|
EXPECT_INS_RETAINED(read_end);
|
|
|
|
EXPECT_INS_EQ(read_end->GetTarget(), obj_phi);
|
|
EXPECT_INS_EQ(read_end->GetDefaultValue(), c3);
|
|
}
|
|
|
|
// // ENTRY
|
|
// if (param) {
|
|
// obj1 = new Obj();
|
|
// } else {
|
|
// obj2 = new Obj();
|
|
// }
|
|
// val_phi = PHI[3,10]
|
|
// target_phi = PHI[obj1, obj2]
|
|
// return PredFieldGet[val_phi, target_phi] => FieldGet[target_phi]
|
|
TEST_F(InstructionSimplifierTest, SimplifyPredicatedFieldGetNoNull) {
|
|
ScopedObjectAccess soa(Thread::Current());
|
|
VariableSizedHandleScope vshs(soa.Self());
|
|
CreateGraph(&vshs);
|
|
AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
|
|
"exit",
|
|
{{"entry", "left"},
|
|
{"entry", "right"},
|
|
{"left", "breturn"},
|
|
{"right", "breturn"},
|
|
{"breturn", "exit"}}));
|
|
#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
|
|
GET_BLOCK(entry);
|
|
GET_BLOCK(exit);
|
|
GET_BLOCK(left);
|
|
GET_BLOCK(right);
|
|
GET_BLOCK(breturn);
|
|
#undef GET_BLOCK
|
|
|
|
HInstruction* bool_value = MakeParam(DataType::Type::kBool);
|
|
HInstruction* c3 = graph_->GetIntConstant(3);
|
|
HInstruction* c10 = graph_->GetIntConstant(10);
|
|
|
|
HInstruction* cls = MakeClassLoad();
|
|
HInstruction* if_inst = new (GetAllocator()) HIf(bool_value);
|
|
entry->AddInstruction(cls);
|
|
entry->AddInstruction(if_inst);
|
|
ManuallyBuildEnvFor(cls, {});
|
|
|
|
HInstruction* obj1 = MakeNewInstance(cls);
|
|
HInstruction* goto_left = new (GetAllocator()) HGoto();
|
|
left->AddInstruction(obj1);
|
|
left->AddInstruction(goto_left);
|
|
|
|
HInstruction* obj2 = MakeNewInstance(cls);
|
|
HInstruction* goto_right = new (GetAllocator()) HGoto();
|
|
right->AddInstruction(obj2);
|
|
right->AddInstruction(goto_right);
|
|
|
|
HPhi* val_phi = MakePhi({c3, c10});
|
|
HPhi* obj_phi = MakePhi({obj1, obj2});
|
|
obj_phi->SetCanBeNull(false);
|
|
HInstruction* read_end = new (GetAllocator()) HPredicatedInstanceFieldGet(obj_phi,
|
|
nullptr,
|
|
val_phi,
|
|
val_phi->GetType(),
|
|
MemberOffset(10),
|
|
false,
|
|
42,
|
|
0,
|
|
graph_->GetDexFile(),
|
|
0);
|
|
HInstruction* return_exit = new (GetAllocator()) HReturn(read_end);
|
|
breturn->AddPhi(val_phi);
|
|
breturn->AddPhi(obj_phi);
|
|
breturn->AddInstruction(read_end);
|
|
breturn->AddInstruction(return_exit);
|
|
|
|
SetupExit(exit);
|
|
|
|
LOG(INFO) << "Pre simplification " << blks;
|
|
graph_->ClearDominanceInformation();
|
|
graph_->BuildDominatorTree();
|
|
InstructionSimplifier simp(graph_, /*codegen=*/nullptr);
|
|
simp.Run();
|
|
|
|
LOG(INFO) << "Post simplify " << blks;
|
|
|
|
EXPECT_FALSE(obj1->CanBeNull());
|
|
EXPECT_FALSE(obj2->CanBeNull());
|
|
EXPECT_INS_REMOVED(read_end);
|
|
|
|
HInstanceFieldGet* ifget = FindSingleInstruction<HInstanceFieldGet>(graph_, breturn);
|
|
ASSERT_NE(ifget, nullptr);
|
|
EXPECT_INS_EQ(ifget->InputAt(0), obj_phi);
|
|
}
|
|
|
|
// // ENTRY
|
|
// obj = new Obj();
|
|
// // Make sure this graph isn't broken
|
|
// if (obj instanceof <other>) {
|
|
// // LEFT
|
|
// } else {
|
|
// // RIGHT
|
|
// }
|
|
// EXIT
|
|
// return obj.field
|
|
TEST_P(InstanceOfInstructionSimplifierTestGroup, ExactClassInstanceOfOther) {
|
|
ScopedObjectAccess soa(Thread::Current());
|
|
VariableSizedHandleScope vshs(soa.Self());
|
|
InitGraph(/*handles=*/&vshs);
|
|
|
|
AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
|
|
"exit",
|
|
{{"entry", "left"},
|
|
{"entry", "right"},
|
|
{"left", "breturn"},
|
|
{"right", "breturn"},
|
|
{"breturn", "exit"}}));
|
|
#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
|
|
GET_BLOCK(entry);
|
|
GET_BLOCK(exit);
|
|
GET_BLOCK(breturn);
|
|
GET_BLOCK(left);
|
|
GET_BLOCK(right);
|
|
#undef GET_BLOCK
|
|
EnsurePredecessorOrder(breturn, {left, right});
|
|
HInstruction* test_res = graph_->GetIntConstant(GetConstantResult() ? 1 : 0);
|
|
|
|
auto [new_inst_klass, target_klass] = GetLoadClasses(&vshs);
|
|
HInstruction* new_inst = MakeNewInstance(new_inst_klass);
|
|
new_inst->SetReferenceTypeInfo(
|
|
ReferenceTypeInfo::Create(new_inst_klass->GetClass(), /*is_exact=*/true));
|
|
HInstanceOf* instance_of = new (GetAllocator()) HInstanceOf(new_inst,
|
|
target_klass,
|
|
TypeCheckKind::kClassHierarchyCheck,
|
|
target_klass->GetClass(),
|
|
0u,
|
|
GetAllocator(),
|
|
nullptr,
|
|
nullptr);
|
|
if (target_klass->GetLoadedClassRTI().IsValid()) {
|
|
instance_of->SetValidTargetClassRTI();
|
|
}
|
|
HInstruction* if_inst = new (GetAllocator()) HIf(instance_of);
|
|
entry->AddInstruction(new_inst_klass);
|
|
if (new_inst_klass != target_klass) {
|
|
entry->AddInstruction(target_klass);
|
|
}
|
|
entry->AddInstruction(new_inst);
|
|
entry->AddInstruction(instance_of);
|
|
entry->AddInstruction(if_inst);
|
|
ManuallyBuildEnvFor(new_inst_klass, {});
|
|
if (new_inst_klass != target_klass) {
|
|
target_klass->CopyEnvironmentFrom(new_inst_klass->GetEnvironment());
|
|
}
|
|
new_inst->CopyEnvironmentFrom(new_inst_klass->GetEnvironment());
|
|
|
|
HInstruction* goto_left = new (GetAllocator()) HGoto();
|
|
left->AddInstruction(goto_left);
|
|
|
|
HInstruction* goto_right = new (GetAllocator()) HGoto();
|
|
right->AddInstruction(goto_right);
|
|
|
|
HInstruction* read_bottom = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32));
|
|
HInstruction* return_exit = new (GetAllocator()) HReturn(read_bottom);
|
|
breturn->AddInstruction(read_bottom);
|
|
breturn->AddInstruction(return_exit);
|
|
|
|
SetupExit(exit);
|
|
|
|
// PerformLSE expects this to be empty.
|
|
graph_->ClearDominanceInformation();
|
|
|
|
LOG(INFO) << "Pre simplification " << blks;
|
|
graph_->ClearDominanceInformation();
|
|
graph_->BuildDominatorTree();
|
|
InstructionSimplifier simp(graph_, /*codegen=*/nullptr);
|
|
simp.Run();
|
|
|
|
LOG(INFO) << "Post simplify " << blks;
|
|
|
|
if (!GetConstantResult() || GetParam() == InstanceOfKind::kSelf) {
|
|
EXPECT_INS_RETAINED(target_klass);
|
|
} else {
|
|
EXPECT_INS_REMOVED(target_klass);
|
|
}
|
|
EXPECT_INS_REMOVED(instance_of);
|
|
EXPECT_INS_EQ(if_inst->InputAt(0), test_res);
|
|
}
|
|
|
|
// // ENTRY
|
|
// obj = new Obj();
|
|
// (<other>)obj;
|
|
// // Make sure this graph isn't broken
|
|
// EXIT
|
|
// return obj
|
|
TEST_P(InstanceOfInstructionSimplifierTestGroup, ExactClassCheckCastOther) {
|
|
ScopedObjectAccess soa(Thread::Current());
|
|
VariableSizedHandleScope vshs(soa.Self());
|
|
InitGraph(/*handles=*/&vshs);
|
|
|
|
AdjacencyListGraph blks(SetupFromAdjacencyList("entry", "exit", {{"entry", "exit"}}));
|
|
#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
|
|
GET_BLOCK(entry);
|
|
GET_BLOCK(exit);
|
|
#undef GET_BLOCK
|
|
|
|
auto [new_inst_klass, target_klass] = GetLoadClasses(&vshs);
|
|
HInstruction* new_inst = MakeNewInstance(new_inst_klass);
|
|
new_inst->SetReferenceTypeInfo(
|
|
ReferenceTypeInfo::Create(new_inst_klass->GetClass(), /*is_exact=*/true));
|
|
HCheckCast* check_cast = new (GetAllocator()) HCheckCast(new_inst,
|
|
target_klass,
|
|
TypeCheckKind::kClassHierarchyCheck,
|
|
target_klass->GetClass(),
|
|
0u,
|
|
GetAllocator(),
|
|
nullptr,
|
|
nullptr);
|
|
if (target_klass->GetLoadedClassRTI().IsValid()) {
|
|
check_cast->SetValidTargetClassRTI();
|
|
}
|
|
HInstruction* entry_return = new (GetAllocator()) HReturn(new_inst);
|
|
entry->AddInstruction(new_inst_klass);
|
|
if (new_inst_klass != target_klass) {
|
|
entry->AddInstruction(target_klass);
|
|
}
|
|
entry->AddInstruction(new_inst);
|
|
entry->AddInstruction(check_cast);
|
|
entry->AddInstruction(entry_return);
|
|
ManuallyBuildEnvFor(new_inst_klass, {});
|
|
if (new_inst_klass != target_klass) {
|
|
target_klass->CopyEnvironmentFrom(new_inst_klass->GetEnvironment());
|
|
}
|
|
new_inst->CopyEnvironmentFrom(new_inst_klass->GetEnvironment());
|
|
|
|
SetupExit(exit);
|
|
|
|
// PerformLSE expects this to be empty.
|
|
graph_->ClearDominanceInformation();
|
|
|
|
LOG(INFO) << "Pre simplification " << blks;
|
|
graph_->ClearDominanceInformation();
|
|
graph_->BuildDominatorTree();
|
|
InstructionSimplifier simp(graph_, /*codegen=*/nullptr);
|
|
simp.Run();
|
|
|
|
LOG(INFO) << "Post simplify " << blks;
|
|
|
|
if (!GetConstantResult() || GetParam() == InstanceOfKind::kSelf) {
|
|
EXPECT_INS_RETAINED(target_klass);
|
|
} else {
|
|
EXPECT_INS_REMOVED(target_klass);
|
|
}
|
|
if (GetConstantResult()) {
|
|
EXPECT_INS_REMOVED(check_cast);
|
|
} else {
|
|
EXPECT_INS_RETAINED(check_cast);
|
|
}
|
|
}
|
|
|
|
INSTANTIATE_TEST_SUITE_P(InstructionSimplifierTest,
|
|
InstanceOfInstructionSimplifierTestGroup,
|
|
testing::Values(InstanceOfKind::kSelf,
|
|
InstanceOfKind::kUnrelatedLoaded,
|
|
InstanceOfKind::kUnrelatedUnloaded,
|
|
InstanceOfKind::kSupertype));
|
|
|
|
} // namespace art
|