481 lines · cpp
1//===- MemProfUseTest.cpp - MemProf use tests -----------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "llvm/Transforms/Instrumentation/MemProfUse.h"10#include "llvm/Analysis/TargetLibraryInfo.h"11#include "llvm/AsmParser/Parser.h"12#include "llvm/IR/LLVMContext.h"13#include "llvm/IR/Module.h"14#include "llvm/Passes/PassBuilder.h"15#include "llvm/ProfileData/IndexedMemProfData.h"16#include "llvm/ProfileData/InstrProfReader.h"17#include "llvm/ProfileData/InstrProfWriter.h"18#include "llvm/ProfileData/MemProf.h"19#include "llvm/Support/SourceMgr.h"20#include "llvm/Testing/Support/Error.h"21 22#include "gmock/gmock.h"23#include "gtest/gtest.h"24 25namespace llvm {26namespace memprof {27namespace {28using testing::Contains;29using testing::ElementsAre;30using testing::Pair;31using testing::SizeIs;32using testing::UnorderedElementsAre;33 34TEST(MemProf, ExtractDirectCallsFromIR) {35 // The following IR is generated from:36 //37 // void f1();38 // void f2();39 // void f3();40 //41 // void foo() {42 // f1();43 // f2(); f3();44 // }45 StringRef IR = R"IR(46define dso_local void @_Z3foov() !dbg !10 {47entry:48 call void @_Z2f1v(), !dbg !1349 call void @_Z2f2v(), !dbg !1450 call void @_Z2f3v(), !dbg !1551 ret void, !dbg !1652}53 54declare !dbg !17 void @_Z2f1v()55 56declare !dbg !18 void @_Z2f2v()57 58declare !dbg !19 void @_Z2f3v()59 60!llvm.dbg.cu = !{!0}61!llvm.module.flags = !{!2, !3, !4, !5, !6, !7, !8}62!llvm.ident = !{!9}63 64!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)65!1 = !DIFile(filename: "foobar.cc", directory: "/")66!2 = !{i32 7, !"Dwarf Version", i32 5}67!3 = !{i32 2, !"Debug Info Version", i32 3}68!4 = !{i32 1, !"wchar_size", i32 4}69!5 = !{i32 1, !"MemProfProfileFilename", !"memprof.profraw"}70!6 = !{i32 8, !"PIC Level", i32 2}71!7 = !{i32 7, !"PIE Level", i32 2}72!8 = !{i32 7, !"uwtable", i32 2}73!9 = !{!"clang"}74!10 = distinct !DISubprogram(name: "foo", linkageName: "_Z3foov", scope: !1, file: !1, line: 5, type: !11, scopeLine: 5, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0)75!11 = !DISubroutineType(types: !12)76!12 = !{}77!13 = !DILocation(line: 6, column: 3, scope: !10)78!14 = !DILocation(line: 7, column: 3, scope: !10)79!15 = !DILocation(line: 7, column: 9, scope: !10)80!16 = !DILocation(line: 8, column: 1, scope: !10)81!17 = !DISubprogram(name: "f1", linkageName: "_Z2f1v", scope: !1, file: !1, line: 1, type: !11, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized)82!18 = !DISubprogram(name: "f2", linkageName: "_Z2f2v", scope: !1, file: !1, line: 2, type: !11, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized)83!19 = !DISubprogram(name: "f3", linkageName: "_Z2f3v", scope: !1, file: !1, line: 3, type: !11, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized)84)IR";85 86 LLVMContext Ctx;87 SMDiagnostic Err;88 std::unique_ptr<Module> M = parseAssemblyString(IR, Err, Ctx);89 ASSERT_TRUE(M);90 91 auto *F = M->getFunction("_Z3foov");92 ASSERT_NE(F, nullptr);93 94 TargetLibraryInfoWrapperPass WrapperPass(M->getTargetTriple());95 auto &TLI = WrapperPass.getTLI(*F);96 auto Calls = extractCallsFromIR(*M, TLI);97 98 // Expect exactly one caller.99 ASSERT_THAT(Calls, SizeIs(1));100 101 auto It = Calls.begin();102 ASSERT_NE(It, Calls.end());103 104 const auto &[CallerGUID, CallSites] = *It;105 EXPECT_EQ(CallerGUID, memprof::getGUID("_Z3foov"));106 107 // Verify that call sites show up in the ascending order of their source108 // locations.109 EXPECT_THAT(110 CallSites,111 ElementsAre(Pair(LineLocation(1, 3), memprof::getGUID("_Z2f1v")),112 Pair(LineLocation(2, 3), memprof::getGUID("_Z2f2v")),113 Pair(LineLocation(2, 9), memprof::getGUID("_Z2f3v"))));114}115 116TEST(MemProf, ExtractDirectCallsFromIRInline) {117 // The following IR is generated from:118 //119 // void f1();120 // static inline void f2() {121 // // For an interesting line number.122 // f1();123 // }124 // static inline void f3() {125 // /****/ f2(); // For an interesting column number.126 // }127 //128 // void g1();129 // void g2();130 // static inline void g3() {131 // /**/ g1(); // For an interesting column number.132 // g2();133 // }134 //135 // void foo() {136 // f3();137 // /***/ g3(); // For an interesting column number.138 // }139 StringRef IR = R"IR(140define dso_local void @_Z3foov() local_unnamed_addr !dbg !10 {141entry:142 tail call void @_Z2f1v(), !dbg !13143 tail call void @_Z2g1v(), !dbg !18144 tail call void @_Z2g2v(), !dbg !21145 ret void, !dbg !22146}147 148declare !dbg !23 void @_Z2f1v() local_unnamed_addr149 150declare !dbg !24 void @_Z2g1v() local_unnamed_addr151 152declare !dbg !25 void @_Z2g2v() local_unnamed_addr153 154!llvm.dbg.cu = !{!0}155!llvm.module.flags = !{!2, !3, !4, !5, !6, !7, !8}156!llvm.ident = !{!9}157 158!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)159!1 = !DIFile(filename: "foobar.cc", directory: "/")160!2 = !{i32 7, !"Dwarf Version", i32 5}161!3 = !{i32 2, !"Debug Info Version", i32 3}162!4 = !{i32 1, !"wchar_size", i32 4}163!5 = !{i32 1, !"MemProfProfileFilename", !"memprof.profraw"}164!6 = !{i32 8, !"PIC Level", i32 2}165!7 = !{i32 7, !"PIE Level", i32 2}166!8 = !{i32 7, !"uwtable", i32 2}167!9 = !{!"clang"}168!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)169!11 = !DISubroutineType(types: !12)170!12 = !{}171!13 = !DILocation(line: 4, column: 3, scope: !14, inlinedAt: !15)172!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)173!15 = distinct !DILocation(line: 7, column: 10, scope: !16, inlinedAt: !17)174!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)175!17 = distinct !DILocation(line: 18, column: 3, scope: !10)176!18 = !DILocation(line: 13, column: 8, scope: !19, inlinedAt: !20)177!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)178!20 = distinct !DILocation(line: 19, column: 9, scope: !10)179!21 = !DILocation(line: 14, column: 3, scope: !19, inlinedAt: !20)180!22 = !DILocation(line: 20, column: 1, scope: !10)181!23 = !DISubprogram(name: "f1", linkageName: "_Z2f1v", scope: !1, file: !1, line: 1, type: !11, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized)182!24 = !DISubprogram(name: "g1", linkageName: "_Z2g1v", scope: !1, file: !1, line: 10, type: !11, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized)183!25 = !DISubprogram(name: "g2", linkageName: "_Z2g2v", scope: !1, file: !1, line: 11, type: !11, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized)184)IR";185 186 LLVMContext Ctx;187 SMDiagnostic Err;188 std::unique_ptr<Module> M = parseAssemblyString(IR, Err, Ctx);189 ASSERT_TRUE(M);190 191 auto *F = M->getFunction("_Z3foov");192 ASSERT_NE(F, nullptr);193 194 TargetLibraryInfoWrapperPass WrapperPass(M->getTargetTriple());195 auto &TLI = WrapperPass.getTLI(*F);196 auto Calls = extractCallsFromIR(*M, TLI);197 198 // Expect exactly 4 callers.199 ASSERT_THAT(Calls, SizeIs(4));200 201 // Verify each key-value pair.202 203 auto FooIt = Calls.find(memprof::getGUID("_Z3foov"));204 ASSERT_NE(FooIt, Calls.end());205 const auto &[FooCallerGUID, FooCallSites] = *FooIt;206 EXPECT_EQ(FooCallerGUID, memprof::getGUID("_Z3foov"));207 EXPECT_THAT(208 FooCallSites,209 ElementsAre(Pair(LineLocation(1, 3), memprof::getGUID("_ZL2f3v")),210 Pair(LineLocation(2, 9), memprof::getGUID("_ZL2g3v"))));211 212 auto F2It = Calls.find(memprof::getGUID("_ZL2f2v"));213 ASSERT_NE(F2It, Calls.end());214 const auto &[F2CallerGUID, F2CallSites] = *F2It;215 EXPECT_EQ(F2CallerGUID, memprof::getGUID("_ZL2f2v"));216 EXPECT_THAT(F2CallSites, ElementsAre(Pair(LineLocation(2, 3),217 memprof::getGUID("_Z2f1v"))));218 219 auto F3It = Calls.find(memprof::getGUID("_ZL2f3v"));220 ASSERT_NE(F3It, Calls.end());221 const auto &[F3CallerGUID, F3CallSites] = *F3It;222 EXPECT_EQ(F3CallerGUID, memprof::getGUID("_ZL2f3v"));223 EXPECT_THAT(F3CallSites, ElementsAre(Pair(LineLocation(1, 10),224 memprof::getGUID("_ZL2f2v"))));225 226 auto G3It = Calls.find(memprof::getGUID("_ZL2g3v"));227 ASSERT_NE(G3It, Calls.end());228 const auto &[G3CallerGUID, G3CallSites] = *G3It;229 EXPECT_EQ(G3CallerGUID, memprof::getGUID("_ZL2g3v"));230 EXPECT_THAT(231 G3CallSites,232 ElementsAre(Pair(LineLocation(1, 8), memprof::getGUID("_Z2g1v")),233 Pair(LineLocation(2, 3), memprof::getGUID("_Z2g2v"))));234}235 236TEST(MemProf, ExtractDirectCallsFromIRCallingNew) {237 // The following IR is generated from:238 //239 // int *foo() {240 // return ::new (int);241 // }242 StringRef IR = R"IR(243define dso_local noundef ptr @_Z3foov() #0 !dbg !10 {244entry:245 %call = call noalias noundef nonnull ptr @_Znwm(i64 noundef 4) #2, !dbg !13246 ret ptr %call, !dbg !14247}248 249; Function Attrs: nobuiltin allocsize(0)250declare noundef nonnull ptr @_Znwm(i64 noundef) #1251 252attributes #0 = { mustprogress uwtable "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }253attributes #1 = { nobuiltin allocsize(0) "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }254attributes #2 = { builtin allocsize(0) }255 256!llvm.dbg.cu = !{!0}257!llvm.module.flags = !{!2, !3, !4, !5, !6, !7, !8}258!llvm.ident = !{!9}259 260!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)261!1 = !DIFile(filename: "foobar.cc", directory: "/")262!2 = !{i32 7, !"Dwarf Version", i32 5}263!3 = !{i32 2, !"Debug Info Version", i32 3}264!4 = !{i32 1, !"wchar_size", i32 4}265!5 = !{i32 1, !"MemProfProfileFilename", !"memprof.profraw"}266!6 = !{i32 8, !"PIC Level", i32 2}267!7 = !{i32 7, !"PIE Level", i32 2}268!8 = !{i32 7, !"uwtable", i32 2}269!9 = !{!"clang"}270!10 = distinct !DISubprogram(name: "foo", linkageName: "_Z3foov", scope: !1, file: !1, line: 1, type: !11, scopeLine: 1, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0)271!11 = !DISubroutineType(types: !12)272!12 = !{}273!13 = !DILocation(line: 2, column: 10, scope: !10)274!14 = !DILocation(line: 2, column: 3, scope: !10)275)IR";276 277 LLVMContext Ctx;278 SMDiagnostic Err;279 std::unique_ptr<Module> M = parseAssemblyString(IR, Err, Ctx);280 ASSERT_TRUE(M);281 282 auto *F = M->getFunction("_Z3foov");283 ASSERT_NE(F, nullptr);284 285 TargetLibraryInfoWrapperPass WrapperPass(M->getTargetTriple());286 auto &TLI = WrapperPass.getTLI(*F);287 auto Calls = extractCallsFromIR(*M, TLI);288 289 // Expect exactly one caller.290 ASSERT_THAT(Calls, SizeIs(1));291 292 // Verify each key-value pair.293 294 auto FooIt = Calls.find(memprof::getGUID("_Z3foov"));295 ASSERT_NE(FooIt, Calls.end());296 const auto &[FooCallerGUID, FooCallSites] = *FooIt;297 EXPECT_EQ(FooCallerGUID, memprof::getGUID("_Z3foov"));298 EXPECT_THAT(FooCallSites, ElementsAre(Pair(LineLocation(1, 10), 0)));299}300 301// Populate those fields returned by getHotColdSchema.302MemInfoBlock makePartialMIB() {303 MemInfoBlock MIB;304 MIB.AllocCount = 1;305 MIB.TotalSize = 5;306 MIB.TotalLifetime = 10;307 MIB.TotalLifetimeAccessDensity = 23;308 return MIB;309}310 311IndexedMemProfRecord312makeRecordV2(std::initializer_list<CallStackId> AllocFrames,313 std::initializer_list<CallStackId> CallSiteFrames,314 const MemInfoBlock &Block, const MemProfSchema &Schema) {315 IndexedMemProfRecord MR;316 for (const auto &CSId : AllocFrames)317 MR.AllocSites.emplace_back(CSId, Block, Schema);318 for (const auto &CSId : CallSiteFrames)319 MR.CallSites.push_back(IndexedCallSiteInfo(CSId));320 return MR;321}322 323static const auto Err = [](Error E) {324 FAIL() << E;325 consumeError(std::move(E));326};327 328// Make sure that we can undrift direct calls.329TEST(MemProf, ComputeUndriftingMap) {330 // Suppose that the source code has changed from:331 //332 // void bar();333 // void baz();334 // void zzz();335 //336 // void foo() {337 // /**/ bar(); // LineLocation(1, 8)338 // zzz(); // LineLocation(2, 3)339 // baz(); // LineLocation(3, 3)340 // }341 //342 // to:343 //344 // void bar();345 // void baz();346 //347 // void foo() {348 // bar(); // LineLocation(1, 3)349 // /**/ baz(); // LineLocation(2, 8)350 // }351 //352 // Notice that the calls to bar and baz have drifted while zzz has been353 // removed.354 StringRef IR = R"IR(355define dso_local void @_Z3foov() #0 !dbg !10 {356entry:357 call void @_Z3barv(), !dbg !13358 call void @_Z3bazv(), !dbg !14359 ret void, !dbg !15360}361 362declare !dbg !16 void @_Z3barv() #1363 364declare !dbg !17 void @_Z3bazv() #1365 366attributes #0 = { mustprogress uwtable "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }367attributes #1 = { "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }368 369!llvm.dbg.cu = !{!0}370!llvm.module.flags = !{!2, !3, !4, !5, !6, !7, !8}371!llvm.ident = !{!9}372 373!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)374!1 = !DIFile(filename: "foobar.cc", directory: "/")375!2 = !{i32 7, !"Dwarf Version", i32 5}376!3 = !{i32 2, !"Debug Info Version", i32 3}377!4 = !{i32 1, !"wchar_size", i32 4}378!5 = !{i32 1, !"MemProfProfileFilename", !"memprof.profraw"}379!6 = !{i32 8, !"PIC Level", i32 2}380!7 = !{i32 7, !"PIE Level", i32 2}381!8 = !{i32 7, !"uwtable", i32 2}382!9 = !{!"clang"}383!10 = distinct !DISubprogram(name: "foo", linkageName: "_Z3foov", scope: !1, file: !1, line: 4, type: !11, scopeLine: 4, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0)384!11 = !DISubroutineType(types: !12)385!12 = !{}386!13 = !DILocation(line: 5, column: 3, scope: !10)387!14 = !DILocation(line: 6, column: 8, scope: !10)388!15 = !DILocation(line: 7, column: 1, scope: !10)389!16 = !DISubprogram(name: "bar", linkageName: "_Z3barv", scope: !1, file: !1, line: 1, type: !11, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized)390!17 = !DISubprogram(name: "baz", linkageName: "_Z3bazv", scope: !1, file: !1, line: 2, type: !11, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized)391)IR";392 393 LLVMContext Ctx;394 SMDiagnostic SMErr;395 std::unique_ptr<Module> M = parseAssemblyString(IR, SMErr, Ctx);396 ASSERT_TRUE(M);397 398 auto *F = M->getFunction("_Z3foov");399 ASSERT_NE(F, nullptr);400 401 TargetLibraryInfoWrapperPass WrapperPass(M->getTargetTriple());402 auto &TLI = WrapperPass.getTLI(*F);403 auto Calls = extractCallsFromIR(*M, TLI);404 405 uint64_t GUIDFoo = memprof::getGUID("_Z3foov");406 uint64_t GUIDBar = memprof::getGUID("_Z3barv");407 uint64_t GUIDBaz = memprof::getGUID("_Z3bazv");408 uint64_t GUIDZzz = memprof::getGUID("_Z3zzzv");409 410 // Verify that extractCallsFromIR extracts caller-callee pairs as expected.411 EXPECT_THAT(Calls,412 UnorderedElementsAre(Pair(413 GUIDFoo, ElementsAre(Pair(LineLocation(1, 3), GUIDBar),414 Pair(LineLocation(2, 8), GUIDBaz)))));415 416 llvm::InstrProfWriter Writer;417 std::unique_ptr<IndexedInstrProfReader> Reader;418 419 const MemInfoBlock MIB = makePartialMIB();420 421 Writer.setMemProfVersionRequested(Version3);422 Writer.setMemProfFullSchema(false);423 424 ASSERT_THAT_ERROR(Writer.mergeProfileKind(InstrProfKind::MemProf),425 Succeeded());426 427 const IndexedMemProfRecord IndexedMR = makeRecordV2(428 /*AllocFrames=*/{0x111, 0x222, 0x333},429 /*CallSiteFrames=*/{}, MIB, getHotColdSchema());430 431 IndexedMemProfData MemProfData;432 // The call sites within foo.433 MemProfData.Frames.try_emplace(0, GUIDFoo, 1, 8, false);434 MemProfData.Frames.try_emplace(1, GUIDFoo, 2, 3, false);435 MemProfData.Frames.try_emplace(2, GUIDFoo, 3, 3, false);436 // Line/column numbers below don't matter.437 MemProfData.Frames.try_emplace(3, GUIDBar, 9, 9, false);438 MemProfData.Frames.try_emplace(4, GUIDZzz, 9, 9, false);439 MemProfData.Frames.try_emplace(5, GUIDBaz, 9, 9, false);440 MemProfData.CallStacks.try_emplace(441 0x111, std::initializer_list<FrameId>{3, 0}); // bar called by foo442 MemProfData.CallStacks.try_emplace(443 0x222, std::initializer_list<FrameId>{4, 1}); // zzz called by foo444 MemProfData.CallStacks.try_emplace(445 0x333, std::initializer_list<FrameId>{5, 2}); // baz called by foo446 MemProfData.Records.try_emplace(0x9999, IndexedMR);447 Writer.addMemProfData(MemProfData, Err);448 449 auto Profile = Writer.writeBuffer();450 451 auto ReaderOrErr =452 IndexedInstrProfReader::create(std::move(Profile), nullptr);453 EXPECT_THAT_ERROR(ReaderOrErr.takeError(), Succeeded());454 Reader = std::move(ReaderOrErr.get());455 456 // Verify that getMemProfCallerCalleePairs extracts caller-callee pairs as457 // expected.458 auto Pairs = Reader->getMemProfCallerCalleePairs();459 ASSERT_THAT(Pairs, SizeIs(4));460 ASSERT_THAT(461 Pairs,462 Contains(Pair(GUIDFoo, ElementsAre(Pair(LineLocation(1, 8), GUIDBar),463 Pair(LineLocation(2, 3), GUIDZzz),464 Pair(LineLocation(3, 3), GUIDBaz)))));465 466 // Verify that computeUndriftMap identifies undrifting opportunities:467 //468 // Profile IR469 // (Line: 1, Column: 8) -> (Line: 1, Column: 3)470 // (Line: 3, Column: 3) -> (Line: 2, Column: 8)471 auto UndriftMap = computeUndriftMap(*M, Reader.get(), TLI);472 ASSERT_THAT(UndriftMap,473 UnorderedElementsAre(Pair(474 GUIDFoo, UnorderedElementsAre(475 Pair(LineLocation(1, 8), LineLocation(1, 3)),476 Pair(LineLocation(3, 3), LineLocation(2, 8))))));477}478} // namespace479} // namespace memprof480} // namespace llvm481