/* * 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 #include #include "gtest/gtest.h" #include "class_root-inl.h" #include "nodes.h" #include "optimizing/data_type.h" #include "optimizing_unit_test.h" namespace art HIDDEN { namespace mirror { class ClassExt; class Throwable; } // namespace mirror static constexpr bool kDebugSimplifierTests = false; template class InstructionSimplifierTestBase : public SuperClass, public OptimizingUnitTestHelper { public: InstructionSimplifierTestBase() { this->use_boot_image_ = true; // Make the Runtime creation cheaper. } void SetUp() override { SuperClass::SetUp(); gLogVerbosity.compiler = true; } void TearDown() override { SuperClass::TearDown(); gLogVerbosity.compiler = false; } void PerformSimplification() { if (kDebugSimplifierTests) { graph_->Dump(LOG_STREAM(INFO) << "Pre simplification ", /* codegen_= */ nullptr); } graph_->ClearDominanceInformation(); graph_->BuildDominatorTree(); InstructionSimplifier simp(graph_, /*codegen=*/nullptr); simp.Run(); if (kDebugSimplifierTests) { graph_->Dump(LOG_STREAM(INFO) << "Post simplify ", /* codegen_= */ nullptr); } } }; class InstructionSimplifierTest : public InstructionSimplifierTestBase {}; // 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> { public: bool GetConstantResult() const { switch (GetParam()) { case InstanceOfKind::kSupertype: case InstanceOfKind::kSelf: return true; case InstanceOfKind::kUnrelatedLoaded: case InstanceOfKind::kUnrelatedUnloaded: return false; } } std::pair GetLoadClasses(HBasicBlock* block, VariableSizedHandleScope* vshs) REQUIRES_SHARED(Locks::mutator_lock_) { InstanceOfKind kind = GetParam(); // New inst always needs to have a valid rti since we dcheck that. HLoadClass* new_inst = MakeLoadClass( block, /* ti= */ std::nullopt, vshs->NewHandle(GetClassRoot())); new_inst->SetValidLoadedClassRTI(); if (kind == InstanceOfKind::kSelf) { return {new_inst, new_inst}; } if (kind == InstanceOfKind::kUnrelatedUnloaded) { HLoadClass* target_class = MakeLoadClass(block); 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 = MakeLoadClass( block, /* ti= */ std::nullopt, vshs->NewHandle(kind == InstanceOfKind::kSupertype ? GetClassRoot() : GetClassRoot())); target_class->SetValidLoadedClassRTI(); EXPECT_TRUE(target_class->GetLoadedClassRTI().IsValid()); return {new_inst, target_class}; } }; // // ENTRY // obj = new Obj(); // // Make sure this graph isn't broken // if (obj instanceof ) { // // LEFT // } else { // // RIGHT // } // EXIT // return obj.field TEST_P(InstanceOfInstructionSimplifierTestGroup, ExactClassInstanceOfOther) { ScopedObjectAccess soa(Thread::Current()); VariableSizedHandleScope vshs(soa.Self()); HBasicBlock* breturn = InitEntryMainExitGraph(/*handles=*/&vshs); auto [if_block, left, right] = CreateDiamondPattern(breturn); EnsurePredecessorOrder(breturn, {left, right}); HInstruction* test_res = graph_->GetIntConstant(GetConstantResult() ? 1 : 0); auto [new_inst_klass, target_klass] = GetLoadClasses(if_block, &vshs); HInstruction* new_inst = MakeNewInstance(if_block, 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(); } if_block->AddInstruction(instance_of); HIf* if_inst = MakeIf(if_block, instance_of); HInstruction* read_bottom = MakeIFieldGet(breturn, new_inst, DataType::Type::kInt32, MemberOffset(32)); MakeReturn(breturn, read_bottom); PerformSimplification(); 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(); // ()obj; // // Make sure this graph isn't broken // EXIT // return obj TEST_P(InstanceOfInstructionSimplifierTestGroup, ExactClassCheckCastOther) { ScopedObjectAccess soa(Thread::Current()); VariableSizedHandleScope vshs(soa.Self()); HBasicBlock* main = InitEntryMainExitGraph(/*handles=*/&vshs); auto [new_inst_klass, target_klass] = GetLoadClasses(main, &vshs); HInstruction* new_inst = MakeNewInstance(main, 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(); } main->AddInstruction(check_cast); MakeReturn(main, new_inst); PerformSimplification(); 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