diff --git a/llvm/include/llvm/IR/DebugLoc.h b/llvm/include/llvm/IR/DebugLoc.h --- a/llvm/include/llvm/IR/DebugLoc.h +++ b/llvm/include/llvm/IR/DebugLoc.h @@ -86,6 +86,13 @@ /// Gets the inlined-at scope for a DebugLoc. MDNode *getInlinedAtScope() const; + /// Rebuild the entire inline-at chain by replacing the subprogram at the + /// end of the chain with NewSP. + static DebugLoc + replaceInlinedAtSubprogram(const DebugLoc &DL, DISubprogram &NewSP, + LLVMContext &Ctx, + DenseMap &Cache); + /// Find the debug info location for the start of the function. /// /// Walk up the scope chain of given debug loc and find line number info 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,6 +67,81 @@ } } +/// Updates the operands of N, replacing OperandToReplace with Replacement. +void replaceOperand(MDNode &N, Metadata *OperandToReplace, + MDNode *Replacement) { + assert(!N.isUniqued() && "Expected distinct or temporary nodes"); + for (auto Idx : seq(0u, N.getNumOperands())) { + Metadata *Old = N.getOperand(Idx); + if (Old == OperandToReplace) + N.replaceOperandWith(Idx, Replacement); + } +} + +/// Traverses the scope chain rooted at RootScope until it hits a Subprogram, +/// recreating the chain with "NewSP" instead. +static DIScope *replaceSubprogram(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); + } + + DIScope *UpdatedScope = CachedResult ? CachedResult : &NewSP; + for (DIScope *ScopeToUpdate : reverse(ScopeChain)) { + TempMDNode ClonedScope = ScopeToUpdate->clone(); + replaceOperand(*ClonedScope, ScopeToUpdate->getScope(), UpdatedScope); + UpdatedScope = + cast(MDNode::replaceWithUniqued(std::move(ClonedScope))); + Cache.try_emplace(ScopeToUpdate, UpdatedScope); + } + return UpdatedScope; +} + +DebugLoc DebugLoc::replaceInlinedAtSubprogram( + const DebugLoc &RootLoc, DISubprogram &NewSP, LLVMContext &Ctx, + DenseMap &Cache) { + SmallVector LocChain; + DILocation *CachedResult = nullptr; + + for (DILocation *Loc = RootLoc; Loc; Loc = Loc->getInlinedAt()) { + if (auto It = Cache.find(Loc); It != Cache.end()) { + CachedResult = cast(It->second); + break; + } + LocChain.push_back(Loc); + } + + DILocation *UpdatedLoc = CachedResult; + if (!UpdatedLoc) { + // If no cache hits, then back() is the end of the inline chain and we need + // to update its *scope* chain. + DILocation *LocToUpdate = LocChain.pop_back_val(); + DIScope *NewScope = + replaceSubprogram(*LocToUpdate->getScope(), NewSP, Ctx, Cache); + UpdatedLoc = DILocation::get(Ctx, LocToUpdate->getLine(), + LocToUpdate->getColumn(), NewScope); + Cache.try_emplace(LocToUpdate, UpdatedLoc); + } + + for (const DILocation *LocToUpdate : reverse(LocChain)) { + UpdatedLoc = + DILocation::get(Ctx, LocToUpdate->getLine(), LocToUpdate->getColumn(), + LocToUpdate->getScope(), UpdatedLoc); + Cache.try_emplace(LocToUpdate, UpdatedLoc); + } + + return UpdatedLoc; +} + DebugLoc DebugLoc::appendInlinedAt(const DebugLoc &DL, DILocation *InlinedAt, LLVMContext &Ctx, DenseMap &Cache) { 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 @@ -1605,9 +1605,11 @@ // 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(DILocation::get(Ctx, DL.getLine(), DL.getCol(), NewSP)); + I.setDebugLoc( + DebugLoc::replaceInlinedAtSubprogram(DL, *NewSP, Ctx, Cache)); // Loop info metadata may contain line locations. Fix them up. auto updateLoopInfoLoc = [&Ctx, NewSP](Metadata *MD) -> Metadata * { 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 @@ -28,15 +28,24 @@ ; - Expressions inside of dbg.value intrinsics are preserved ; CHECK-NEXT: llvm.dbg.value(metadata i32 [[ADD1]], metadata [[VAR1]], metadata !DIExpression(DW_OP_constu, 1, DW_OP_plus, DW_OP_stack_value) +; CHECK-NEXT: call void @sink(i32 [[ADD1]]), !dbg [[LINE2:![0-9]+]] + ; - The DISubprogram for @foo.cold.1 has an empty DISubroutineType ; CHECK: [[FILE:![0-9]+]] = !DIFile(filename: "" ; CHECK: [[EMPTY_MD:![0-9]+]] = !{} ; CHECK: [[EMPTY_TYPE:![0-9]+]] = !DISubroutineType(types: [[EMPTY_MD]]) +; CHECK: [[ORIGINAL_FOO_SCOPE:![0-9]+]] = distinct !DISubprogram(name: "inline_me" ; CHECK: [[NEWSCOPE:![0-9]+]] = distinct !DISubprogram(name: "foo.cold.1", linkageName: "foo.cold.1", scope: null, file: [[FILE]], type: [[EMPTY_TYPE]], spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized ; - Line locations in @foo.cold.1 point to the new scope for @foo.cold.1 ; CHECK: [[LINE1]] = !DILocation(line: 1, column: 1, scope: [[NEWSCOPE]]) +; CHECK: [[LINE2]] = !DILocation(line: 2, column: 2, scope: [[ORIGINAL_FOO_SCOPE]] +; CHECK-SAME: inlinedAt: [[INLINED_AT:![0-9]*]] +; CHECK: [[INLINED_AT]] = !DILocation(line: 3, column: 3, scope: [[INLINED_SCOPE1:![0-9]*]] +; 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) + define void @foo(i32 %arg1) !dbg !6 { entry: %var = add i32 0, 0, !dbg !11 @@ -52,6 +61,8 @@ call void @sink(i32 %add1), !dbg !11 call void @llvm.dbg.value(metadata i32 %add1, metadata !9, metadata !DIExpression()), !dbg !11 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 ret void } @@ -59,6 +70,10 @@ declare void @sink(i32) cold +define void @inline_me() !dbg !12{ + ret void +} + !llvm.dbg.cu = !{!0} !llvm.debugify = !{!3, !4} !llvm.module.flags = !{!5} @@ -75,3 +90,8 @@ !9 = !DILocalVariable(name: "1", scope: !6, file: !1, line: 1, type: !10) !10 = !DIBasicType(name: "ty32", size: 32, encoding: DW_ATE_unsigned) !11 = !DILocation(line: 1, column: 1, scope: !6) +!12 = distinct !DISubprogram(name: "inline_me", linkageName: "inline_me", scope: null, file: !1, line: 1, type: !7, isLocal: false, isDefinition: true, scopeLine: 1, isOptimized: true, unit: !0, retainedNodes: !8) +!13 = !DILocation(line: 2, column: 2, scope: !12, inlinedAt: !14) +!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)