diff --git a/llvm/lib/Transforms/Instrumentation/MemProfiler.cpp b/llvm/lib/Transforms/Instrumentation/MemProfiler.cpp index 0b4d3ff201e62..64e850c7d9316 100644 --- a/llvm/lib/Transforms/Instrumentation/MemProfiler.cpp +++ b/llvm/lib/Transforms/Instrumentation/MemProfiler.cpp @@ -810,10 +810,6 @@ memprof::extractCallsFromIR(Module &M) { for (auto &BB : F) { for (auto &I : BB) { - const DILocation *DIL = I.getDebugLoc(); - if (!DIL) - continue; - if (!isa(&I) || isa(&I)) continue; @@ -824,11 +820,17 @@ memprof::extractCallsFromIR(Module &M) { continue; StringRef CalleeName = CalledFunction->getName(); - uint64_t CallerGUID = - IndexedMemProfRecord::getGUID(DIL->getSubprogramLinkageName()); - uint64_t CalleeGUID = IndexedMemProfRecord::getGUID(CalleeName); - LineLocation Loc = {GetOffset(DIL), DIL->getColumn()}; - Calls[CallerGUID].emplace_back(Loc, CalleeGUID); + for (const DILocation *DIL = I.getDebugLoc(); DIL; + DIL = DIL->getInlinedAt()) { + StringRef CallerName = DIL->getSubprogramLinkageName(); + assert(!CallerName.empty() && + "Be sure to enable -fdebug-info-for-profiling"); + uint64_t CallerGUID = IndexedMemProfRecord::getGUID(CallerName); + uint64_t CalleeGUID = IndexedMemProfRecord::getGUID(CalleeName); + LineLocation Loc = {GetOffset(DIL), DIL->getColumn()}; + Calls[CallerGUID].emplace_back(Loc, CalleeGUID); + CalleeName = CallerName; + } } } } diff --git a/llvm/unittests/Transforms/Instrumentation/MemProfUseTest.cpp b/llvm/unittests/Transforms/Instrumentation/MemProfUseTest.cpp index a510a57099aba..c864b06e991dc 100644 --- a/llvm/unittests/Transforms/Instrumentation/MemProfUseTest.cpp +++ b/llvm/unittests/Transforms/Instrumentation/MemProfUseTest.cpp @@ -101,4 +101,123 @@ declare !dbg !19 void @_Z2f3v() EXPECT_THAT(CallSites[2], Pair(FieldsAre(2U, 9U), IndexedMemProfRecord::getGUID("_Z2f3v"))); } + +TEST(MemProf, ExtractDirectCallsFromIRInline) { + // The following IR is generated from: + // + // void f1(); + // static inline void f2() { + // // For an interesting line number. + // f1(); + // } + // static inline void f3() { + // /****/ f2(); // For an interesting column number. + // } + // + // void g1(); + // void g2(); + // static inline void g3() { + // /**/ g1(); // For an interesting column number. + // g2(); + // } + // + // void foo() { + // f3(); + // /***/ g3(); // For an interesting column number. + // } + StringRef IR = R"IR( +define dso_local void @_Z3foov() local_unnamed_addr !dbg !10 { +entry: + tail call void @_Z2f1v(), !dbg !13 + tail call void @_Z2g1v(), !dbg !18 + tail call void @_Z2g2v(), !dbg !21 + ret void, !dbg !22 +} + +declare !dbg !23 void @_Z2f1v() local_unnamed_addr + +declare !dbg !24 void @_Z2g1v() local_unnamed_addr + +declare !dbg !25 void @_Z2g2v() local_unnamed_addr + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!2, !3, !4, !5, !6, !7, !8} +!llvm.ident = !{!9} + +!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang", isOptimized: true, runtimeVersion: 0, emissionKind: LineTablesOnly, splitDebugInlining: false, debugInfoForProfiling: true, nameTableKind: None) +!1 = !DIFile(filename: "foobar.cc", directory: "/") +!2 = !{i32 7, !"Dwarf Version", i32 5} +!3 = !{i32 2, !"Debug Info Version", i32 3} +!4 = !{i32 1, !"wchar_size", i32 4} +!5 = !{i32 1, !"MemProfProfileFilename", !"memprof.profraw"} +!6 = !{i32 8, !"PIC Level", i32 2} +!7 = !{i32 7, !"PIE Level", i32 2} +!8 = !{i32 7, !"uwtable", i32 2} +!9 = !{!"clang"} +!10 = distinct !DISubprogram(name: "foo", linkageName: "_Z3foov", scope: !1, file: !1, line: 17, type: !11, scopeLine: 17, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0) +!11 = !DISubroutineType(types: !12) +!12 = !{} +!13 = !DILocation(line: 4, column: 3, scope: !14, inlinedAt: !15) +!14 = distinct !DISubprogram(name: "f2", linkageName: "_ZL2f2v", scope: !1, file: !1, line: 2, type: !11, scopeLine: 2, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: !0) +!15 = distinct !DILocation(line: 7, column: 10, scope: !16, inlinedAt: !17) +!16 = distinct !DISubprogram(name: "f3", linkageName: "_ZL2f3v", scope: !1, file: !1, line: 6, type: !11, scopeLine: 6, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: !0) +!17 = distinct !DILocation(line: 18, column: 3, scope: !10) +!18 = !DILocation(line: 13, column: 8, scope: !19, inlinedAt: !20) +!19 = distinct !DISubprogram(name: "g3", linkageName: "_ZL2g3v", scope: !1, file: !1, line: 12, type: !11, scopeLine: 12, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: !0) +!20 = distinct !DILocation(line: 19, column: 9, scope: !10) +!21 = !DILocation(line: 14, column: 3, scope: !19, inlinedAt: !20) +!22 = !DILocation(line: 20, column: 1, scope: !10) +!23 = !DISubprogram(name: "f1", linkageName: "_Z2f1v", scope: !1, file: !1, line: 1, type: !11, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized) +!24 = !DISubprogram(name: "g1", linkageName: "_Z2g1v", scope: !1, file: !1, line: 10, type: !11, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized) +!25 = !DISubprogram(name: "g2", linkageName: "_Z2g2v", scope: !1, file: !1, line: 11, type: !11, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized) +)IR"; + + LLVMContext Ctx; + SMDiagnostic Err; + std::unique_ptr M = parseAssemblyString(IR, Err, Ctx); + ASSERT_TRUE(M); + + auto Calls = extractCallsFromIR(*M); + + // Expect exactly 4 callers. + ASSERT_THAT(Calls, SizeIs(4)); + + // Verify each key-value pair. + + auto FooIt = Calls.find(IndexedMemProfRecord::getGUID("_Z3foov")); + ASSERT_NE(FooIt, Calls.end()); + const auto &[FooCallerGUID, FooCallSites] = *FooIt; + EXPECT_EQ(FooCallerGUID, IndexedMemProfRecord::getGUID("_Z3foov")); + ASSERT_THAT(FooCallSites, SizeIs(2)); + EXPECT_THAT(FooCallSites[0], Pair(FieldsAre(1U, 3U), + IndexedMemProfRecord::getGUID("_ZL2f3v"))); + EXPECT_THAT(FooCallSites[1], Pair(FieldsAre(2U, 9U), + IndexedMemProfRecord::getGUID("_ZL2g3v"))); + + auto F2It = Calls.find(IndexedMemProfRecord::getGUID("_ZL2f2v")); + ASSERT_NE(F2It, Calls.end()); + const auto &[F2CallerGUID, F2CallSites] = *F2It; + EXPECT_EQ(F2CallerGUID, IndexedMemProfRecord::getGUID("_ZL2f2v")); + ASSERT_THAT(F2CallSites, SizeIs(1)); + EXPECT_THAT(F2CallSites[0], + Pair(FieldsAre(2U, 3U), IndexedMemProfRecord::getGUID("_Z2f1v"))); + + auto F3It = Calls.find(IndexedMemProfRecord::getGUID("_ZL2f3v")); + ASSERT_NE(F3It, Calls.end()); + const auto &[F3CallerGUID, F3CallSites] = *F3It; + EXPECT_EQ(F3CallerGUID, IndexedMemProfRecord::getGUID("_ZL2f3v")); + ASSERT_THAT(F3CallSites, SizeIs(1)); + EXPECT_THAT(F3CallSites[0], Pair(FieldsAre(1U, 10U), + IndexedMemProfRecord::getGUID("_ZL2f2v"))); + + auto G3It = Calls.find(IndexedMemProfRecord::getGUID("_ZL2g3v")); + ASSERT_NE(G3It, Calls.end()); + const auto &[G3CallerGUID, G3CallSites] = *G3It; + EXPECT_EQ(G3CallerGUID, IndexedMemProfRecord::getGUID("_ZL2g3v")); + ASSERT_THAT(G3CallSites, SizeIs(2)); + EXPECT_THAT(G3CallSites[0], + Pair(FieldsAre(1U, 8U), IndexedMemProfRecord::getGUID("_Z2g1v"))); + EXPECT_THAT(G3CallSites[1], + Pair(FieldsAre(2U, 3U), IndexedMemProfRecord::getGUID("_Z2g2v"))); +} } // namespace