diff --git a/llvm/include/llvm/IR/DebugInfoMetadata.h b/llvm/include/llvm/IR/DebugInfoMetadata.h --- a/llvm/include/llvm/IR/DebugInfoMetadata.h +++ b/llvm/include/llvm/IR/DebugInfoMetadata.h @@ -1580,6 +1580,13 @@ /// chain. DISubprogram *getSubprogram() const; + /// Traverses the scope chain rooted at RootScope until it hits a Subprogram, + /// recreating the chain with "NewSP" instead. + static DILocalScope * + cloneScopeForSubprogram(DILocalScope &RootScope, DISubprogram &NewSP, + LLVMContext &Ctx, + DenseMap &Cache); + /// Get the first non DILexicalBlockFile scope of this scope. /// /// Return this if it's not a \a DILexicalBlockFIle; otherwise, look up the diff --git a/llvm/lib/IR/DebugInfoMetadata.cpp b/llvm/lib/IR/DebugInfoMetadata.cpp --- a/llvm/lib/IR/DebugInfoMetadata.cpp +++ b/llvm/lib/IR/DebugInfoMetadata.cpp @@ -974,6 +974,35 @@ return const_cast(this); } +DILocalScope *DILocalScope::cloneScopeForSubprogram( + DILocalScope &RootScope, DISubprogram &NewSP, LLVMContext &Ctx, + DenseMap &Cache) { + SmallVector ScopeChain; + DIScope *CachedResult = nullptr; + + for (DIScope *Scope = &RootScope; !isa(Scope); + Scope = Scope->getScope()) { + if (auto It = Cache.find(Scope); It != Cache.end()) { + CachedResult = cast(It->second); + break; + } + ScopeChain.push_back(Scope); + } + + // Recreate the scope chain, bottom-up, starting at the new subprogram (or a + // cached result). + DIScope *UpdatedScope = CachedResult ? CachedResult : &NewSP; + for (DIScope *ScopeToUpdate : reverse(ScopeChain)) { + TempMDNode ClonedScope = ScopeToUpdate->clone(); + cast(*ClonedScope).replaceScope(UpdatedScope); + UpdatedScope = + cast(MDNode::replaceWithUniqued(std::move(ClonedScope))); + Cache[ScopeToUpdate] = UpdatedScope; + } + + return cast(UpdatedScope); +} + DISubprogram::DISPFlags DISubprogram::getFlag(StringRef Flag) { return StringSwitch(Flag) #define HANDLE_DISP_FLAG(ID, NAME) .Case("DISPFlag" #NAME, SPFlag##NAME) diff --git a/llvm/lib/IR/DebugLoc.cpp b/llvm/lib/IR/DebugLoc.cpp --- a/llvm/lib/IR/DebugLoc.cpp +++ b/llvm/lib/IR/DebugLoc.cpp @@ -67,37 +67,6 @@ } } -/// Traverses the scope chain rooted at RootScope until it hits a Subprogram, -/// recreating the chain with "NewSP" instead. -static DIScope * -cloneScopeForSubprogram(DILocalScope &RootScope, DISubprogram &NewSP, - LLVMContext &Ctx, - DenseMap &Cache) { - SmallVector ScopeChain; - DIScope *CachedResult = nullptr; - - for (DIScope *Scope = &RootScope; !isa(Scope); - Scope = Scope->getScope()) { - if (auto It = Cache.find(Scope); It != Cache.end()) { - CachedResult = cast(It->second); - break; - } - ScopeChain.push_back(Scope); - } - - // Recreate the scope chain, bottom-up, starting at the new subprogram (or a - // cached result). - DIScope *UpdatedScope = CachedResult ? CachedResult : &NewSP; - for (DIScope *ScopeToUpdate : reverse(ScopeChain)) { - TempMDNode ClonedScope = ScopeToUpdate->clone(); - cast(*ClonedScope).replaceScope(UpdatedScope); - UpdatedScope = - cast(MDNode::replaceWithUniqued(std::move(ClonedScope))); - Cache[ScopeToUpdate] = UpdatedScope; - } - return UpdatedScope; -} - DebugLoc DebugLoc::replaceInlinedAtSubprogram( const DebugLoc &RootLoc, DISubprogram &NewSP, LLVMContext &Ctx, DenseMap &Cache) { @@ -119,8 +88,8 @@ // If no cache hits, then back() is the end of the inline chain, that is, // the DILocation whose scope ends in the Subprogram to be replaced. DILocation *LocToUpdate = LocChain.pop_back_val(); - DIScope *NewScope = - cloneScopeForSubprogram(*LocToUpdate->getScope(), NewSP, Ctx, Cache); + DIScope *NewScope = DILocalScope::cloneScopeForSubprogram( + *LocToUpdate->getScope(), NewSP, Ctx, Cache); UpdatedLoc = DILocation::get(Ctx, LocToUpdate->getLine(), LocToUpdate->getColumn(), NewScope); Cache[LocToUpdate] = UpdatedLoc; diff --git a/llvm/lib/Transforms/Utils/CodeExtractor.cpp b/llvm/lib/Transforms/Utils/CodeExtractor.cpp --- a/llvm/lib/Transforms/Utils/CodeExtractor.cpp +++ b/llvm/lib/Transforms/Utils/CodeExtractor.cpp @@ -1556,6 +1556,7 @@ // point to a variable in the wrong scope. SmallDenseMap RemappedMetadata; SmallVector DebugIntrinsicsToDelete; + DenseMap Cache; for (Instruction &I : instructions(NewFunc)) { auto *DII = dyn_cast(&I); if (!DII) @@ -1588,24 +1589,29 @@ DebugIntrinsicsToDelete.push_back(DVI); continue; } - - // Point the intrinsic to a fresh variable within the new function. - DILocalVariable *OldVar = DVI->getVariable(); - DINode *&NewVar = RemappedMetadata[OldVar]; - if (!NewVar) - NewVar = DIB.createAutoVariable( - NewSP, OldVar->getName(), OldVar->getFile(), OldVar->getLine(), - OldVar->getType(), /*AlwaysPreserve=*/false, DINode::FlagZero, - OldVar->getAlignInBits()); - DVI->setVariable(cast(NewVar)); + // If the variable was in the scope of the old function, i.e. it was not + // inlined, point the intrinsic to a fresh variable within the new function. + if (DVI->getDebugLoc().getInlinedAt() == nullptr) { + DILocalVariable *OldVar = DVI->getVariable(); + DINode *&NewVar = RemappedMetadata[OldVar]; + if (!NewVar) { + DILocalScope *NewScope = DILocalScope::cloneScopeForSubprogram( + *OldVar->getScope(), *NewSP, Ctx, Cache); + NewVar = DIB.createAutoVariable( + NewScope, OldVar->getName(), OldVar->getFile(), OldVar->getLine(), + OldVar->getType(), /*AlwaysPreserve=*/false, DINode::FlagZero, + OldVar->getAlignInBits()); + } + DVI->setVariable(cast(NewVar)); + } } + for (auto *DII : DebugIntrinsicsToDelete) DII->eraseFromParent(); DIB.finalizeSubprogram(NewSP); // Fix up the scope information attached to the line locations in the new // function. - DenseMap Cache; for (Instruction &I : instructions(NewFunc)) { if (const DebugLoc &DL = I.getDebugLoc()) I.setDebugLoc( diff --git a/llvm/test/Transforms/HotColdSplit/transfer-debug-info.ll b/llvm/test/Transforms/HotColdSplit/transfer-debug-info.ll --- a/llvm/test/Transforms/HotColdSplit/transfer-debug-info.ll +++ b/llvm/test/Transforms/HotColdSplit/transfer-debug-info.ll @@ -31,6 +31,15 @@ ; CHECK-NEXT: call void @sink(i32 [[ADD1]]), !dbg [[LINE2:![0-9]+]] ; CHECK-NEXT: call void @sink(i32 [[ADD1]]), !dbg [[LINE3:![0-9]+]] +; CHECK-NEXT: call void @llvm.dbg.value(metadata i32 [[ADD1]] +; CHECK-SAME: metadata [[VAR_FROM_INLINE_ME:![0-9]+]] +; CHECK-SAME: !dbg [[LINE2]] + +; CHECK-NEXT: call void @llvm.dbg.value(metadata i32 [[ADD1]] +; CHECK-SAME: metadata [[VAR2:![0-9]+]] +; CHECK-SAME: !dbg [[LINE4:![0-9]+]] + + ; - The DISubprogram for @foo.cold.1 has an empty DISubroutineType ; CHECK: [[FILE:![0-9]+]] = !DIFile(filename: "" ; CHECK: [[EMPTY_MD:![0-9]+]] = !{} @@ -47,6 +56,14 @@ ; CHECK: [[INLINED_SCOPE1]] = !DILexicalBlock(scope: [[INLINED_SCOPE2:![0-9]*]], file: [[FILE]], line: 4, column: 4) ; CHECK: [[INLINED_SCOPE2]] = !DILexicalBlock(scope: [[NEWSCOPE]], file: [[FILE]], line: 5, column: 5) +; CHECK: [[VAR_FROM_INLINE_ME]] = !DILocalVariable(name: "var_from_inline_me", +; CHECK-SAME: scope: [[INLINE_ME_SCOPE]] + +; CHECK: [[VAR2]] = !DILocalVariable(name: "var_from_scope_in_foo", +; CHECK-SAME: scope: [[NEWSCOPE2:![0-9]+]] +; CHECK: [[NEWSCOPE2]] = !DILexicalBlock(scope: [[NEWSCOPE]], file: [[FILE]], line: 7, column: 7) +; CHECK: [[LINE4]] = !DILocation(line: 6, column: 6, scope: [[NEWSCOPE2]] + define void @foo(i32 %arg1) !dbg !6 { entry: %var = add i32 0, 0, !dbg !11 @@ -64,6 +81,8 @@ call void @llvm.dbg.value(metadata i32 %add1, metadata !9, metadata !DIExpression(DW_OP_constu, 1, DW_OP_plus, DW_OP_stack_value)), !dbg !11 call void @sink(i32 %add1), !dbg !13 ; inlined from @inline_me call void @sink(i32 %add1), !dbg !14 ; not inlined, but inside some scope of foo + call void @llvm.dbg.value(metadata i32 %add1, metadata !17, metadata !DIExpression()), !dbg !13 ; variable from @inline_me, should preserve scope in !17. + call void @llvm.dbg.value(metadata i32 %add1, metadata !18, metadata !DIExpression()), !dbg !19 ; variable not inlined, but inside some scope of foo ret void } @@ -96,3 +115,7 @@ !14 = !DILocation(line: 3, column: 3, scope: !15) !15 = distinct !DILexicalBlock(scope: !16, file: !1, line: 4, column: 4) !16 = distinct !DILexicalBlock(scope: !6, file: !1, line: 5, column: 5) +!17 = !DILocalVariable(name: "var_from_inline_me", scope: !12, file: !1, line: 1, type: !10) +!18 = !DILocalVariable(name: "var_from_scope_in_foo", scope: !20, file: !1, line: 1, type: !10) +!19 = !DILocation(line: 6, column: 6, scope: !20) +!20 = distinct !DILexicalBlock(scope: !6, file: !1, line: 7, column: 7)