//===----------- llvm/unittest/CodeGen/LexicalScopesTest.cpp --------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "llvm/CodeGen/LexicalScopes.h" #include "llvm/CodeGen/MachineBasicBlock.h" #include "llvm/CodeGen/MachineFunction.h" #include "llvm/CodeGen/MachineInstr.h" #include "llvm/CodeGen/MachineMemOperand.h" #include "llvm/CodeGen/MachineModuleInfo.h" #include "llvm/CodeGen/TargetFrameLowering.h" #include "llvm/CodeGen/TargetInstrInfo.h" #include "llvm/CodeGen/TargetLowering.h" #include "llvm/CodeGen/TargetSubtargetInfo.h" #include "llvm/IR/DIBuilder.h" #include "llvm/IR/DebugInfoMetadata.h" #include "llvm/IR/IRBuilder.h" #include "llvm/IR/ModuleSlotTracker.h" #include "llvm/MC/MCAsmInfo.h" #include "llvm/MC/MCSymbol.h" #include "llvm/Support/TargetRegistry.h" #include "llvm/Support/TargetSelect.h" #include "llvm/Target/TargetMachine.h" #include "llvm/Target/TargetOptions.h" #include "gtest/gtest.h" using namespace llvm; namespace { // Include helper functions to ease the manipulation of MachineFunctions #include "MFCommon.inc" class LexicalScopesTest : public testing::Test { public: // Boilerplate, LLVMContext Ctx; Module Mod; std::unique_ptr MF; DICompileUnit *OurCU; DIFile *OurFile; DISubprogram *OurFunc; DILexicalBlock *OurBlock, *AnotherBlock; DISubprogram *ToInlineFunc; DILexicalBlock *ToInlineBlock; // DebugLocs that we'll used to create test environments. DebugLoc OutermostLoc, InBlockLoc, NotNestedBlockLoc, InlinedLoc; // Test environment blocks -- these form a diamond control flow pattern, // MBB1 being the entry block, blocks two and three being the branches, and // block four joining the branches and being an exit block. MachineBasicBlock *MBB1, *MBB2, *MBB3, *MBB4; // Some meaningless instructions -- the first is fully meaningless, // while the second is supposed to impersonate DBG_VALUEs through its // opcode. MCInstrDesc BeanInst; MCInstrDesc DbgValueInst; LexicalScopesTest() : Ctx(), Mod("beehives", Ctx) { memset(&BeanInst, 0, sizeof(BeanInst)); BeanInst.Opcode = 1; BeanInst.Size = 1; memset(&DbgValueInst, 0, sizeof(DbgValueInst)); DbgValueInst.Opcode = TargetOpcode::DBG_VALUE; DbgValueInst.Size = 1; // Boilerplate that creates a MachineFunction and associated blocks. MF = createMachineFunction(Ctx, Mod); llvm::Function &F = const_cast(MF->getFunction()); auto BB1 = BasicBlock::Create(Ctx, "a", &F); auto BB2 = BasicBlock::Create(Ctx, "b", &F); auto BB3 = BasicBlock::Create(Ctx, "c", &F); auto BB4 = BasicBlock::Create(Ctx, "d", &F); IRBuilder<> IRB1(BB1), IRB2(BB2), IRB3(BB3), IRB4(BB4); IRB1.CreateBr(BB2); IRB2.CreateBr(BB3); IRB3.CreateBr(BB4); IRB4.CreateRetVoid(); MBB1 = MF->CreateMachineBasicBlock(BB1); MF->insert(MF->end(), MBB1); MBB2 = MF->CreateMachineBasicBlock(BB2); MF->insert(MF->end(), MBB2); MBB3 = MF->CreateMachineBasicBlock(BB3); MF->insert(MF->end(), MBB3); MBB4 = MF->CreateMachineBasicBlock(BB4); MF->insert(MF->end(), MBB4); MBB1->addSuccessor(MBB2); MBB1->addSuccessor(MBB3); MBB2->addSuccessor(MBB4); MBB3->addSuccessor(MBB4); // Create metadata: CU, subprogram, some blocks and an inline function // scope. DIBuilder DIB(Mod); OurFile = DIB.createFile("xyzzy.c", "/cave"); OurCU = DIB.createCompileUnit(dwarf::DW_LANG_C99, OurFile, "nou", false, "", 0); auto OurSubT = DIB.createSubroutineType(DIB.getOrCreateTypeArray(None)); OurFunc = DIB.createFunction(OurCU, "bees", "", OurFile, 1, OurSubT, 1, DINode::FlagZero, DISubprogram::SPFlagDefinition); F.setSubprogram(OurFunc); OurBlock = DIB.createLexicalBlock(OurFunc, OurFile, 2, 3); AnotherBlock = DIB.createLexicalBlock(OurFunc, OurFile, 2, 6); ToInlineFunc = DIB.createFunction(OurFile, "shoes", "", OurFile, 10, OurSubT, 10, DINode::FlagZero, DISubprogram::SPFlagDefinition); // Make some nested scopes. OutermostLoc = DILocation::get(Ctx, 3, 1, OurFunc); InBlockLoc = DILocation::get(Ctx, 4, 1, OurBlock); InlinedLoc = DILocation::get(Ctx, 10, 1, ToInlineFunc, InBlockLoc.get()); // Make a scope that isn't nested within the others. NotNestedBlockLoc = DILocation::get(Ctx, 4, 1, AnotherBlock); DIB.finalize(); } }; // Fill blocks with dummy instructions, test some base lexical scope // functionaliy. TEST_F(LexicalScopesTest, FlatLayout) { BuildMI(*MBB1, MBB1->end(), OutermostLoc, BeanInst); BuildMI(*MBB2, MBB2->end(), OutermostLoc, BeanInst); BuildMI(*MBB3, MBB3->end(), OutermostLoc, BeanInst); BuildMI(*MBB4, MBB4->end(), OutermostLoc, BeanInst); LexicalScopes LS; EXPECT_TRUE(LS.empty()); LS.reset(); EXPECT_EQ(LS.getCurrentFunctionScope(), nullptr); LS.initialize(*MF); EXPECT_FALSE(LS.empty()); LexicalScope *FuncScope = LS.getCurrentFunctionScope(); EXPECT_EQ(FuncScope->getParent(), nullptr); EXPECT_EQ(FuncScope->getDesc(), OurFunc); EXPECT_EQ(FuncScope->getInlinedAt(), nullptr); EXPECT_EQ(FuncScope->getScopeNode(), OurFunc); EXPECT_FALSE(FuncScope->isAbstractScope()); EXPECT_EQ(FuncScope->getChildren().size(), 0u); // There should be one range, covering the whole function. Test that it // points at the correct instructions. auto &Ranges = FuncScope->getRanges(); ASSERT_EQ(Ranges.size(), 1u); EXPECT_EQ(Ranges.front().first, &*MF->begin()->begin()); auto BBIt = MF->end(); BBIt = std::prev(BBIt); EXPECT_EQ(Ranges.front().second, &*BBIt->begin()); EXPECT_TRUE(FuncScope->dominates(FuncScope)); SmallPtrSet MBBVec; LS.getMachineBasicBlocks(OutermostLoc.get(), MBBVec); EXPECT_EQ(MBBVec.size(), 4u); // All the blocks should be in that set; the outermost loc should dominate // them; and no other scope should. for (auto &MBB : *MF) { EXPECT_EQ(MBBVec.count(&MBB), 1u); EXPECT_TRUE(LS.dominates(OutermostLoc.get(), &MBB)); EXPECT_FALSE(LS.dominates(InBlockLoc.get(), &MBB)); EXPECT_FALSE(LS.dominates(InlinedLoc.get(), &MBB)); } } // Examine relationship between two nested scopes inside the function, the // outer function and the lexical block within it. TEST_F(LexicalScopesTest, BlockScopes) { BuildMI(*MBB1, MBB1->end(), InBlockLoc, BeanInst); BuildMI(*MBB2, MBB2->end(), InBlockLoc, BeanInst); BuildMI(*MBB3, MBB3->end(), InBlockLoc, BeanInst); BuildMI(*MBB4, MBB4->end(), InBlockLoc, BeanInst); LexicalScopes LS; LS.initialize(*MF); LexicalScope *FuncScope = LS.getCurrentFunctionScope(); EXPECT_EQ(FuncScope->getDesc(), OurFunc); auto &Children = FuncScope->getChildren(); ASSERT_EQ(Children.size(), 1u); auto *BlockScope = Children[0]; EXPECT_EQ(LS.findLexicalScope(InBlockLoc.get()), BlockScope); EXPECT_EQ(BlockScope->getDesc(), InBlockLoc->getScope()); EXPECT_FALSE(BlockScope->isAbstractScope()); EXPECT_TRUE(FuncScope->dominates(BlockScope)); EXPECT_FALSE(BlockScope->dominates(FuncScope)); EXPECT_EQ(FuncScope->getParent(), nullptr); EXPECT_EQ(BlockScope->getParent(), FuncScope); SmallPtrSet MBBVec; LS.getMachineBasicBlocks(OutermostLoc.get(), MBBVec); EXPECT_EQ(MBBVec.size(), 4u); for (auto &MBB : *MF) { EXPECT_EQ(MBBVec.count(&MBB), 1u); EXPECT_TRUE(LS.dominates(OutermostLoc.get(), &MBB)); EXPECT_TRUE(LS.dominates(InBlockLoc.get(), &MBB)); EXPECT_FALSE(LS.dominates(InlinedLoc.get(), &MBB)); } } // Test inlined scopes functionality and relationship with the outer scopes. TEST_F(LexicalScopesTest, InlinedScopes) { BuildMI(*MBB1, MBB1->end(), InlinedLoc, BeanInst); BuildMI(*MBB2, MBB2->end(), InlinedLoc, BeanInst); BuildMI(*MBB3, MBB3->end(), InlinedLoc, BeanInst); BuildMI(*MBB4, MBB4->end(), InlinedLoc, BeanInst); LexicalScopes LS; LS.initialize(*MF); LexicalScope *FuncScope = LS.getCurrentFunctionScope(); auto &Children = FuncScope->getChildren(); ASSERT_EQ(Children.size(), 1u); auto *BlockScope = Children[0]; auto &BlockChildren = BlockScope->getChildren(); ASSERT_EQ(BlockChildren.size(), 1u); auto *InlinedScope = BlockChildren[0]; EXPECT_FALSE(InlinedScope->isAbstractScope()); EXPECT_EQ(InlinedScope->getInlinedAt(), InlinedLoc.getInlinedAt()); EXPECT_EQ(InlinedScope->getDesc(), InlinedLoc.getScope()); EXPECT_EQ(InlinedScope->getChildren().size(), 0u); EXPECT_EQ(FuncScope->getParent(), nullptr); EXPECT_EQ(BlockScope->getParent(), FuncScope); EXPECT_EQ(InlinedScope->getParent(), BlockScope); const auto &AbstractScopes = LS.getAbstractScopesList(); ASSERT_EQ(AbstractScopes.size(), 1u); const auto &AbstractScope = *AbstractScopes[0]; EXPECT_TRUE(AbstractScope.isAbstractScope()); EXPECT_EQ(AbstractScope.getDesc(), InlinedLoc.getScope()); EXPECT_EQ(AbstractScope.getInlinedAt(), nullptr); EXPECT_EQ(AbstractScope.getParent(), nullptr); } // Test behaviour in a function that has empty DebugLocs. TEST_F(LexicalScopesTest, FuncWithEmptyGap) { BuildMI(*MBB1, MBB1->end(), OutermostLoc, BeanInst); BuildMI(*MBB2, MBB2->end(), DebugLoc(), BeanInst); BuildMI(*MBB3, MBB3->end(), DebugLoc(), BeanInst); BuildMI(*MBB4, MBB4->end(), OutermostLoc, BeanInst); LexicalScopes LS; LS.initialize(*MF); LexicalScope *FuncScope = LS.getCurrentFunctionScope(); // A gap in a range that contains no other location, is not actually a // gap as far as lexical scopes are concerned. auto &Ranges = FuncScope->getRanges(); ASSERT_EQ(Ranges.size(), 1u); EXPECT_EQ(Ranges[0].first, &*MF->begin()->begin()); auto BBIt = MF->end(); BBIt = std::prev(BBIt); EXPECT_EQ(Ranges[0].second, &*BBIt->begin()); } // Now a function with intervening not-in-scope instructions. TEST_F(LexicalScopesTest, FuncWithRealGap) { MachineInstr *FirstI = BuildMI(*MBB1, MBB1->end(), InBlockLoc, BeanInst); BuildMI(*MBB2, MBB2->end(), OutermostLoc, BeanInst); BuildMI(*MBB3, MBB3->end(), OutermostLoc, BeanInst); MachineInstr *LastI = BuildMI(*MBB4, MBB4->end(), InBlockLoc, BeanInst); LexicalScopes LS; LS.initialize(*MF); LexicalScope *BlockScope = LS.findLexicalScope(InBlockLoc.get()); ASSERT_NE(BlockScope, nullptr); // Within the block scope, there's a gap between the first and last // block / instruction, where it's only the outermost scope. auto &Ranges = BlockScope->getRanges(); ASSERT_EQ(Ranges.size(), 2u); EXPECT_EQ(Ranges[0].first, FirstI); EXPECT_EQ(Ranges[0].second, FirstI); EXPECT_EQ(Ranges[1].first, LastI); EXPECT_EQ(Ranges[1].second, LastI); // The outer function scope should cover the whole function, including // blocks the lexicalblock covers. LexicalScope *FuncScope = LS.getCurrentFunctionScope(); auto &FuncRanges = FuncScope->getRanges(); ASSERT_EQ(FuncRanges.size(), 1u); EXPECT_NE(FuncRanges[0].first, FuncRanges[0].second); EXPECT_EQ(FuncRanges[0].first, FirstI); EXPECT_EQ(FuncRanges[0].second, LastI); } // Examine the relationship between two scopes that don't nest (are siblings). TEST_F(LexicalScopesTest, NotNested) { MachineInstr *FirstI = BuildMI(*MBB1, MBB1->end(), InBlockLoc, BeanInst); MachineInstr *SecondI = BuildMI(*MBB2, MBB2->end(), NotNestedBlockLoc, BeanInst); MachineInstr *ThirdI = BuildMI(*MBB3, MBB3->end(), NotNestedBlockLoc, BeanInst); MachineInstr *FourthI = BuildMI(*MBB4, MBB4->end(), InBlockLoc, BeanInst); LexicalScopes LS; LS.initialize(*MF); LexicalScope *FuncScope = LS.getCurrentFunctionScope(); LexicalScope *BlockScope = LS.findLexicalScope(InBlockLoc.get()); LexicalScope *OtherBlockScope = LS.findLexicalScope(NotNestedBlockLoc.get()); ASSERT_NE(FuncScope, nullptr); ASSERT_NE(BlockScope, nullptr); ASSERT_NE(OtherBlockScope, nullptr); // The function should cover everything; the two blocks are distinct and // should not. auto &FuncRanges = FuncScope->getRanges(); ASSERT_EQ(FuncRanges.size(), 1u); EXPECT_EQ(FuncRanges[0].first, FirstI); EXPECT_EQ(FuncRanges[0].second, FourthI); // Two ranges, start and end instructions. auto &BlockRanges = BlockScope->getRanges(); ASSERT_EQ(BlockRanges.size(), 2u); EXPECT_EQ(BlockRanges[0].first, FirstI); EXPECT_EQ(BlockRanges[0].second, FirstI); EXPECT_EQ(BlockRanges[1].first, FourthI); EXPECT_EQ(BlockRanges[1].second, FourthI); // One inner range, covering the two inner blocks. auto &OtherBlockRanges = OtherBlockScope->getRanges(); ASSERT_EQ(OtherBlockRanges.size(), 1u); EXPECT_EQ(OtherBlockRanges[0].first, SecondI); EXPECT_EQ(OtherBlockRanges[0].second, ThirdI); } // Test the scope-specific and block-specific dominates methods. TEST_F(LexicalScopesTest, TestDominates) { BuildMI(*MBB1, MBB1->end(), InBlockLoc, BeanInst); BuildMI(*MBB2, MBB2->end(), NotNestedBlockLoc, BeanInst); BuildMI(*MBB3, MBB3->end(), NotNestedBlockLoc, BeanInst); BuildMI(*MBB4, MBB4->end(), InBlockLoc, BeanInst); LexicalScopes LS; LS.initialize(*MF); LexicalScope *FuncScope = LS.getCurrentFunctionScope(); LexicalScope *BlockScope = LS.findLexicalScope(InBlockLoc.get()); LexicalScope *OtherBlockScope = LS.findLexicalScope(NotNestedBlockLoc.get()); ASSERT_NE(FuncScope, nullptr); ASSERT_NE(BlockScope, nullptr); ASSERT_NE(OtherBlockScope, nullptr); EXPECT_TRUE(FuncScope->dominates(BlockScope)); EXPECT_TRUE(FuncScope->dominates(OtherBlockScope)); EXPECT_FALSE(BlockScope->dominates(FuncScope)); EXPECT_FALSE(BlockScope->dominates(OtherBlockScope)); EXPECT_FALSE(OtherBlockScope->dominates(FuncScope)); EXPECT_FALSE(OtherBlockScope->dominates(BlockScope)); // Outermost scope dominates everything, as all insts are within it. EXPECT_TRUE(LS.dominates(OutermostLoc.get(), MBB1)); EXPECT_TRUE(LS.dominates(OutermostLoc.get(), MBB2)); EXPECT_TRUE(LS.dominates(OutermostLoc.get(), MBB3)); EXPECT_TRUE(LS.dominates(OutermostLoc.get(), MBB4)); // One inner block dominates the outer pair of blocks, EXPECT_TRUE(LS.dominates(InBlockLoc.get(), MBB1)); EXPECT_FALSE(LS.dominates(InBlockLoc.get(), MBB2)); EXPECT_FALSE(LS.dominates(InBlockLoc.get(), MBB3)); EXPECT_TRUE(LS.dominates(InBlockLoc.get(), MBB4)); // While the other dominates the inner two blocks. EXPECT_FALSE(LS.dominates(NotNestedBlockLoc.get(), MBB1)); EXPECT_TRUE(LS.dominates(NotNestedBlockLoc.get(), MBB2)); EXPECT_TRUE(LS.dominates(NotNestedBlockLoc.get(), MBB3)); EXPECT_FALSE(LS.dominates(NotNestedBlockLoc.get(), MBB4)); } // Test getMachineBasicBlocks returns all dominated blocks. TEST_F(LexicalScopesTest, TestGetBlocks) { BuildMI(*MBB1, MBB1->end(), InBlockLoc, BeanInst); BuildMI(*MBB2, MBB2->end(), NotNestedBlockLoc, BeanInst); BuildMI(*MBB3, MBB3->end(), NotNestedBlockLoc, BeanInst); BuildMI(*MBB4, MBB4->end(), InBlockLoc, BeanInst); LexicalScopes LS; LS.initialize(*MF); LexicalScope *FuncScope = LS.getCurrentFunctionScope(); LexicalScope *BlockScope = LS.findLexicalScope(InBlockLoc.get()); LexicalScope *OtherBlockScope = LS.findLexicalScope(NotNestedBlockLoc.get()); ASSERT_NE(FuncScope, nullptr); ASSERT_NE(BlockScope, nullptr); ASSERT_NE(OtherBlockScope, nullptr); SmallPtrSet OutermostBlocks, InBlockBlocks, NotNestedBlockBlocks; LS.getMachineBasicBlocks(OutermostLoc.get(), OutermostBlocks); LS.getMachineBasicBlocks(InBlockLoc.get(), InBlockBlocks); LS.getMachineBasicBlocks(NotNestedBlockLoc.get(), NotNestedBlockBlocks); EXPECT_EQ(OutermostBlocks.count(MBB1), 1u); EXPECT_EQ(OutermostBlocks.count(MBB2), 1u); EXPECT_EQ(OutermostBlocks.count(MBB3), 1u); EXPECT_EQ(OutermostBlocks.count(MBB4), 1u); EXPECT_EQ(InBlockBlocks.count(MBB1), 1u); EXPECT_EQ(InBlockBlocks.count(MBB2), 0u); EXPECT_EQ(InBlockBlocks.count(MBB3), 0u); EXPECT_EQ(InBlockBlocks.count(MBB4), 1u); EXPECT_EQ(NotNestedBlockBlocks.count(MBB1), 0u); EXPECT_EQ(NotNestedBlockBlocks.count(MBB2), 1u); EXPECT_EQ(NotNestedBlockBlocks.count(MBB3), 1u); EXPECT_EQ(NotNestedBlockBlocks.count(MBB4), 0u); } TEST_F(LexicalScopesTest, TestMetaInst) { // Instruction Layout looks like this, where 'F' means funcscope, and // 'B' blockscope: // bb1: // F: bean // B: bean // bb2: // F: bean // B: DBG_VALUE // bb3: // F: bean // B: DBG_VALUE // bb4: // F: bean // B: bean // The block / 'B' should only dominate bb1 and bb4. DBG_VALUE is a meta // instruction, and shouldn't contribute to scopes. BuildMI(*MBB1, MBB1->end(), OutermostLoc, BeanInst); BuildMI(*MBB1, MBB1->end(), InBlockLoc, BeanInst); BuildMI(*MBB2, MBB2->end(), OutermostLoc, BeanInst); BuildMI(*MBB2, MBB2->end(), InBlockLoc, DbgValueInst); BuildMI(*MBB3, MBB3->end(), OutermostLoc, BeanInst); BuildMI(*MBB3, MBB3->end(), InBlockLoc, DbgValueInst); BuildMI(*MBB4, MBB4->end(), OutermostLoc, BeanInst); BuildMI(*MBB4, MBB4->end(), InBlockLoc, BeanInst); LexicalScopes LS; LS.initialize(*MF); LexicalScope *FuncScope = LS.getCurrentFunctionScope(); LexicalScope *BlockScope = LS.findLexicalScope(InBlockLoc.get()); ASSERT_NE(FuncScope, nullptr); ASSERT_NE(BlockScope, nullptr); EXPECT_TRUE(LS.dominates(OutermostLoc.get(), MBB1)); EXPECT_TRUE(LS.dominates(OutermostLoc.get(), MBB2)); EXPECT_TRUE(LS.dominates(OutermostLoc.get(), MBB3)); EXPECT_TRUE(LS.dominates(OutermostLoc.get(), MBB4)); EXPECT_TRUE(LS.dominates(InBlockLoc.get(), MBB1)); EXPECT_FALSE(LS.dominates(InBlockLoc.get(), MBB2)); EXPECT_FALSE(LS.dominates(InBlockLoc.get(), MBB3)); EXPECT_TRUE(LS.dominates(InBlockLoc.get(), MBB4)); } } // anonymous namespace