diff --git a/llvm/lib/Transforms/Utils/CloneFunction.cpp b/llvm/lib/Transforms/Utils/CloneFunction.cpp --- a/llvm/lib/Transforms/Utils/CloneFunction.cpp +++ b/llvm/lib/Transforms/Utils/CloneFunction.cpp @@ -206,9 +206,20 @@ }; // Avoid cloning types, compile units, and (other) subprograms. - for (DISubprogram *ISP : DIFinder->subprograms()) - if (ISP != SPClonedWithinModule) + SmallPtrSet MappedToSelfSPs; + for (DISubprogram *ISP : DIFinder->subprograms()) { + if (ISP != SPClonedWithinModule) { mapToSelfIfNew(ISP); + MappedToSelfSPs.insert(ISP); + } + } + + // If a subprogram isn't going to be cloned skip its lexical blocks as well. + for (DIScope *S : DIFinder->scopes()) { + auto *LScope = dyn_cast(S); + if (LScope && MappedToSelfSPs.count(LScope->getSubprogram())) + mapToSelfIfNew(S); + } for (DICompileUnit *CU : DIFinder->compile_units()) mapToSelfIfNew(CU); diff --git a/llvm/test/Transforms/Coroutines/coro-debug-frame-variable.ll b/llvm/test/Transforms/Coroutines/coro-debug-frame-variable.ll --- a/llvm/test/Transforms/Coroutines/coro-debug-frame-variable.ll +++ b/llvm/test/Transforms/Coroutines/coro-debug-frame-variable.ll @@ -27,7 +27,7 @@ ; static (!11 and !13), whereas the coroutine funclet will have its own new ; ones with identical line and column numbers. ; -; CHECK-LABEL: define void @f() { +; CHECK-LABEL: define void @f() {{.*}} { ; CHECK: entry: ; CHECK: %j = alloca i32, align 4 ; CHECK: call void @llvm.dbg.declare(metadata i32* %j, metadata ![[JVAR:[0-9]+]], metadata !DIExpression()), !dbg ![[JDBGLOC:[0-9]+]] @@ -36,7 +36,7 @@ ; CHECK: call void @llvm.dbg.declare(metadata i8* %[[MEMORY]], metadata ![[IVAR:[0-9]+]], metadata !DIExpression(DW_OP_plus_uconst, 20)), !dbg ![[IDBGLOC]] ; CHECK: await.ready: ; -; CHECK-LABEL: define internal fastcc void @f.resume({{.*}}) { +; CHECK-LABEL: define internal fastcc void @f.resume({{.*}}) {{.*}} { ; CHECK: entry.resume: ; CHECK-NEXT: %[[DBG_PTR:.*]] = alloca %f.Frame* ; CHECK-NEXT: call void @llvm.dbg.declare(metadata %f.Frame** %[[DBG_PTR]], metadata ![[XVAR_RESUME:[0-9]+]], metadata !DIExpression(DW_OP_deref, DW_OP_plus_uconst, 32)), !dbg @@ -48,7 +48,7 @@ ; CHECK: await.ready: ; ; CHECK-DAG: ![[IVAR]] = !DILocalVariable(name: "i" -; CHECK-DAG: ![[SCOPE:[0-9]+]] = distinct !DILexicalBlock(scope: !8, file: !1, line: 23, column: 12) +; CHECK-DAG: ![[SCOPE:[0-9]+]] = distinct !DILexicalBlock(scope: !6, file: !1, line: 23, column: 12) ; CHECK-DAG: ![[IDBGLOC]] = !DILocation(line: 24, column: 7, scope: ![[SCOPE]]) ; CHECK-DAG: ![[XVAR]] = !DILocalVariable(name: "x" ; CHECK-DAG: ![[JVAR]] = !DILocalVariable(name: "j" @@ -56,11 +56,11 @@ ; CHECK-DAG: ![[XVAR_RESUME]] = !DILocalVariable(name: "x" ; CHECK-DAG: ![[IDBGLOC_RESUME]] = !DILocation(line: 24, column: 7, scope: ![[RESUME_SCOPE:[0-9]+]]) -; CHECK-DAG: ![[RESUME_SCOPE]] = distinct !DILexicalBlock(scope: !8, file: !1, line: 23, column: 12) +; CHECK-DAG: ![[RESUME_SCOPE]] = distinct !DILexicalBlock(scope: !23, file: !1, line: 23, column: 12) ; CHECK-DAG: ![[IVAR_RESUME]] = !DILocalVariable(name: "i" ; CHECK-DAG: ![[JVAR_RESUME]] = !DILocalVariable(name: "j" ; CHECK-DAG: ![[JDBGLOC_RESUME]] = !DILocation(line: 32, column: 7, scope: ![[RESUME_SCOPE]]) -define void @f() presplitcoroutine { +define void @f() presplitcoroutine !dbg !8 { entry: %__promise = alloca i8, align 8 %i = alloca i32, align 4 diff --git a/llvm/unittests/Transforms/Utils/CloningTest.cpp b/llvm/unittests/Transforms/Utils/CloningTest.cpp --- a/llvm/unittests/Transforms/Utils/CloningTest.cpp +++ b/llvm/unittests/Transforms/Utils/CloningTest.cpp @@ -799,6 +799,58 @@ EXPECT_FALSE(verifyModule(*ImplModule, &errs())); } +TEST(CloneFunction, CloneFunctionWithInlinedSubprograms) { + StringRef ImplAssembly = R"( + declare void @llvm.dbg.declare(metadata, metadata, metadata) + + define void @test() !dbg !3 { + call void @llvm.dbg.declare(metadata i8* undef, metadata !5, metadata !DIExpression()), !dbg !7 + ret void + } + + declare void @cloned() + + !llvm.dbg.cu = !{!0} + !llvm.module.flags = !{!2} + !0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1) + !1 = !DIFile(filename: "test.cpp", directory: "") + !2 = !{i32 1, !"Debug Info Version", i32 3} + !3 = distinct !DISubprogram(name: "test", scope: !0, unit: !0) + !4 = distinct !DISubprogram(name: "inlined", scope: !0, unit: !0, retainedNodes: !{!5}) + !5 = !DILocalVariable(name: "awaitables", scope: !4) + !6 = distinct !DILexicalBlock(scope: !4, file: !1, line: 1) + !7 = !DILocation(line: 1, scope: !6, inlinedAt: !8) + !8 = !DILocation(line: 10, scope: !3) + )"; + + LLVMContext Context; + SMDiagnostic Error; + + auto ImplModule = parseAssemblyString(ImplAssembly, Error, Context); + EXPECT_TRUE(ImplModule != nullptr); + auto *Func = ImplModule->getFunction("test"); + EXPECT_TRUE(Func != nullptr); + auto *ClonedFunc = ImplModule->getFunction("cloned"); + EXPECT_TRUE(ClonedFunc != nullptr); + + ValueToValueMapTy VMap; + SmallVector Returns; + ClonedCodeInfo CCI; + CloneFunctionInto(ClonedFunc, Func, VMap, + CloneFunctionChangeType::GlobalChanges, Returns, "", &CCI); + + EXPECT_FALSE(verifyModule(*ImplModule, &errs())); + + // Check that DILexicalBlock of inlined function was not cloned. + auto DbgDeclareI = Func->begin()->begin(); + auto ClonedDbgDeclareI = ClonedFunc->begin()->begin(); + const DebugLoc &DbgLoc = DbgDeclareI->getDebugLoc(); + const DebugLoc &ClonedDbgLoc = ClonedDbgDeclareI->getDebugLoc(); + EXPECT_NE(DbgLoc.get(), ClonedDbgLoc.get()); + EXPECT_EQ(cast(DbgLoc.getScope()), + cast(ClonedDbgLoc.getScope())); +} + TEST(CloneFunction, CloneFunctionToDifferentModule) { StringRef ImplAssembly = R"( define void @foo() {