| //===- MachineSizeOptsTest.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/MachineSizeOpts.h" |
| #include "llvm/Analysis/BlockFrequencyInfo.h" |
| #include "llvm/Analysis/BranchProbabilityInfo.h" |
| #include "llvm/Analysis/LoopInfo.h" |
| #include "llvm/Analysis/ProfileSummaryInfo.h" |
| #include "llvm/CodeGen/MIRParser/MIRParser.h" |
| #include "llvm/CodeGen/MachineBlockFrequencyInfo.h" |
| #include "llvm/CodeGen/MachineBranchProbabilityInfo.h" |
| #include "llvm/CodeGen/MachineDominators.h" |
| #include "llvm/CodeGen/MachineLoopInfo.h" |
| #include "llvm/CodeGen/MachineModuleInfo.h" |
| #include "llvm/IR/Module.h" |
| #include "llvm/MC/TargetRegistry.h" |
| #include "llvm/Support/MemoryBuffer.h" |
| #include "llvm/Support/TargetSelect.h" |
| #include "llvm/Target/TargetMachine.h" |
| #include "gtest/gtest.h" |
| |
| using namespace llvm; |
| |
| namespace { |
| |
| std::unique_ptr<TargetMachine> createTargetMachine() { |
| Triple TT("x86_64--"); |
| std::string Error; |
| const Target *TheTarget = TargetRegistry::lookupTarget(TT, Error); |
| return std::unique_ptr<TargetMachine>( |
| TheTarget->createTargetMachine(TT, "", "", TargetOptions(), std::nullopt, |
| std::nullopt, CodeGenOptLevel::Default)); |
| } |
| |
| class MachineSizeOptsTest : public testing::Test { |
| protected: |
| static const char* MIRString; |
| LLVMContext Context; |
| std::unique_ptr<TargetMachine> TM; |
| std::unique_ptr<MachineModuleInfo> MMI; |
| std::unique_ptr<MIRParser> Parser; |
| std::unique_ptr<Module> M; |
| struct BFIData { |
| std::unique_ptr<MachineDominatorTree> MDT; |
| std::unique_ptr<MachineLoopInfo> MLI; |
| std::unique_ptr<MachineBranchProbabilityInfo> MBPI; |
| std::unique_ptr<MachineBlockFrequencyInfo> MBFI; |
| BFIData(MachineFunction &MF) { |
| MDT.reset(new MachineDominatorTree(MF)); |
| MLI.reset(new MachineLoopInfo(*MDT)); |
| MBPI.reset(new MachineBranchProbabilityInfo()); |
| MBFI.reset(new MachineBlockFrequencyInfo(MF, *MBPI, *MLI)); |
| } |
| MachineBlockFrequencyInfo *get() { return MBFI.get(); } |
| }; |
| |
| static void SetUpTestCase() { |
| LLVMInitializeX86TargetInfo(); |
| LLVMInitializeX86Target(); |
| LLVMInitializeX86TargetMC(); |
| } |
| |
| void SetUp() override { |
| TM = createTargetMachine(); |
| std::unique_ptr<MemoryBuffer> MBuffer = |
| MemoryBuffer::getMemBuffer(MIRString); |
| Parser = createMIRParser(std::move(MBuffer), Context); |
| if (!Parser) |
| report_fatal_error("null MIRParser"); |
| M = Parser->parseIRModule(); |
| if (!M) |
| report_fatal_error("parseIRModule failed"); |
| M->setTargetTriple(TM->getTargetTriple()); |
| M->setDataLayout(TM->createDataLayout()); |
| MMI = std::make_unique<MachineModuleInfo>(TM.get()); |
| if (Parser->parseMachineFunctions(*M, *MMI)) |
| report_fatal_error("parseMachineFunctions failed"); |
| } |
| |
| MachineFunction *getMachineFunction(Module *M, StringRef Name) { |
| auto F = M->getFunction(Name); |
| if (!F) |
| report_fatal_error("null Function"); |
| auto &MF = MMI->getOrCreateMachineFunction(*F); |
| return &MF; |
| } |
| }; |
| |
| TEST_F(MachineSizeOptsTest, Test) { |
| MachineFunction *F = getMachineFunction(M.get(), "f"); |
| ASSERT_TRUE(F != nullptr); |
| MachineFunction *G = getMachineFunction(M.get(), "g"); |
| ASSERT_TRUE(G != nullptr); |
| MachineFunction *H = getMachineFunction(M.get(), "h"); |
| ASSERT_TRUE(H != nullptr); |
| ProfileSummaryInfo PSI = ProfileSummaryInfo(*M); |
| ASSERT_TRUE(PSI.hasProfileSummary()); |
| BFIData BFID_F(*F); |
| BFIData BFID_G(*G); |
| BFIData BFID_H(*H); |
| MachineBlockFrequencyInfo *MBFI_F = BFID_F.get(); |
| MachineBlockFrequencyInfo *MBFI_G = BFID_G.get(); |
| MachineBlockFrequencyInfo *MBFI_H = BFID_H.get(); |
| MachineBasicBlock &BB0 = F->front(); |
| auto iter = BB0.succ_begin(); |
| MachineBasicBlock *BB1 = *iter; |
| iter++; |
| MachineBasicBlock *BB2 = *iter; |
| iter++; |
| ASSERT_TRUE(iter == BB0.succ_end()); |
| MachineBasicBlock *BB3 = *BB1->succ_begin(); |
| ASSERT_TRUE(BB3 == *BB2->succ_begin()); |
| EXPECT_FALSE(shouldOptimizeForSize(F, &PSI, MBFI_F, PGSOQueryType::Test)); |
| EXPECT_TRUE(shouldOptimizeForSize(G, &PSI, MBFI_G, PGSOQueryType::Test)); |
| EXPECT_FALSE(shouldOptimizeForSize(H, &PSI, MBFI_H, PGSOQueryType::Test)); |
| EXPECT_FALSE(shouldOptimizeForSize(&BB0, &PSI, MBFI_F, PGSOQueryType::Test)); |
| EXPECT_FALSE(shouldOptimizeForSize(BB1, &PSI, MBFI_F, PGSOQueryType::Test)); |
| EXPECT_TRUE(shouldOptimizeForSize(BB2, &PSI, MBFI_F, PGSOQueryType::Test)); |
| EXPECT_FALSE(shouldOptimizeForSize(BB3, &PSI, MBFI_F, PGSOQueryType::Test)); |
| } |
| |
| const char* MachineSizeOptsTest::MIRString = R"MIR( |
| --- | |
| define i32 @g(i32 %x) !prof !14 { |
| ret i32 0 |
| } |
| |
| define i32 @h(i32 %x) !prof !15 { |
| ret i32 0 |
| } |
| |
| define i32 @f(i32 %x) !prof !16 { |
| bb0: |
| %y1 = icmp eq i32 %x, 0 |
| br i1 %y1, label %bb1, label %bb2, !prof !17 |
| |
| bb1: ; preds = %bb0 |
| %z1 = call i32 @g(i32 %x) |
| br label %bb3 |
| |
| bb2: ; preds = %bb0 |
| %z2 = call i32 @h(i32 %x) |
| br label %bb3 |
| |
| bb3: ; preds = %bb2, %bb1 |
| %y2 = phi i32 [ 0, %bb1 ], [ 1, %bb2 ] |
| ret i32 %y2 |
| } |
| |
| !llvm.module.flags = !{!0} |
| |
| !0 = !{i32 1, !"ProfileSummary", !1} |
| !1 = !{!2, !3, !4, !5, !6, !7, !8, !9} |
| !2 = !{!"ProfileFormat", !"InstrProf"} |
| !3 = !{!"TotalCount", i64 10000} |
| !4 = !{!"MaxCount", i64 10} |
| !5 = !{!"MaxInternalCount", i64 1} |
| !6 = !{!"MaxFunctionCount", i64 1000} |
| !7 = !{!"NumCounts", i64 3} |
| !8 = !{!"NumFunctions", i64 3} |
| !9 = !{!"DetailedSummary", !10} |
| !10 = !{!11, !12, !13} |
| !11 = !{i32 10000, i64 1000, i32 1} |
| !12 = !{i32 999000, i64 300, i32 3} |
| !13 = !{i32 999999, i64 5, i32 10} |
| !14 = !{!"function_entry_count", i64 1} |
| !15 = !{!"function_entry_count", i64 100} |
| !16 = !{!"function_entry_count", i64 400} |
| !17 = !{!"branch_weights", i32 100, i32 1} |
| |
| ... |
| --- |
| name: g |
| body: | |
| bb.0: |
| %1:gr32 = MOV32r0 implicit-def dead $eflags |
| $eax = COPY %1 |
| RET 0, $eax |
| |
| ... |
| --- |
| name: h |
| body: | |
| bb.0: |
| %1:gr32 = MOV32r0 implicit-def dead $eflags |
| $eax = COPY %1 |
| RET 0, $eax |
| |
| ... |
| --- |
| name: f |
| tracksRegLiveness: true |
| body: | |
| bb.0: |
| successors: %bb.1(0x7ebb907a), %bb.2(0x01446f86) |
| liveins: $edi |
| |
| %1:gr32 = COPY $edi |
| TEST32rr %1, %1, implicit-def $eflags |
| JCC_1 %bb.2, 5, implicit $eflags |
| JMP_1 %bb.1 |
| |
| bb.1: |
| successors: %bb.3(0x80000000) |
| |
| ADJCALLSTACKDOWN64 0, 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp |
| $edi = COPY %1 |
| CALL64pcrel32 @g, csr_64, implicit $rsp, implicit $ssp, implicit $edi, implicit-def $rsp, implicit-def $ssp, implicit-def $eax |
| ADJCALLSTACKUP64 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp |
| %5:gr32 = COPY $eax |
| %4:gr32 = MOV32r0 implicit-def dead $eflags |
| JMP_1 %bb.3 |
| |
| bb.2: |
| successors: %bb.3(0x80000000) |
| |
| ADJCALLSTACKDOWN64 0, 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp |
| $edi = COPY %1 |
| CALL64pcrel32 @h, csr_64, implicit $rsp, implicit $ssp, implicit $edi, implicit-def $rsp, implicit-def $ssp, implicit-def $eax |
| ADJCALLSTACKUP64 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp |
| %3:gr32 = COPY $eax |
| %2:gr32 = MOV32ri 1 |
| |
| bb.3: |
| %0:gr32 = PHI %2, %bb.2, %4, %bb.1 |
| $eax = COPY %0 |
| RET 0, $eax |
| |
| ... |
| )MIR"; |
| |
| } // anonymous namespace |