diff --git a/llvm/include/llvm/BinaryFormat/Dwarf.def b/llvm/include/llvm/BinaryFormat/Dwarf.def index 724a14ccc7aea..de4215e088dd5 100644 --- a/llvm/include/llvm/BinaryFormat/Dwarf.def +++ b/llvm/include/llvm/BinaryFormat/Dwarf.def @@ -268,6 +268,7 @@ HANDLE_DW_TAG(0x5111, ALTIUM_rom, 0, ALTIUM, DW_KIND_NONE) // LLVM HANDLE_DW_TAG(0x6000, LLVM_annotation, 0, LLVM, DW_KIND_NONE) +HANDLE_DW_TAG(0x6001, LLVM_func_args_changed, 0, LLVM, DW_KIND_NONE) // Green Hills. HANDLE_DW_TAG(0x8004, GHS_namespace, 0, GHS, DW_KIND_NONE) @@ -624,6 +625,7 @@ HANDLE_DW_AT(0x3e09, LLVM_ptrauth_authenticates_null_values, 0, LLVM) HANDLE_DW_AT(0x3e0a, LLVM_ptrauth_authentication_mode, 0, LLVM) HANDLE_DW_AT(0x3e0b, LLVM_num_extra_inhabitants, 0, LLVM) HANDLE_DW_AT(0x3e0c, LLVM_stmt_sequence, 0, LLVM) +HANDLE_DW_AT(0x3e0d, LLVM_func_retval_removed, 0, LLVM) // Apple extensions. diff --git a/llvm/include/llvm/IR/DebugInfoFlags.def b/llvm/include/llvm/IR/DebugInfoFlags.def index df375b6c68e81..e693addab7879 100644 --- a/llvm/include/llvm/IR/DebugInfoFlags.def +++ b/llvm/include/llvm/IR/DebugInfoFlags.def @@ -91,11 +91,13 @@ HANDLE_DISP_FLAG((1u << 8), MainSubprogram) // for defaulted functions HANDLE_DISP_FLAG((1u << 9), Deleted) HANDLE_DISP_FLAG((1u << 11), ObjCDirect) +HANDLE_DISP_FLAG((1u << 12), ArgChanged) +HANDLE_DISP_FLAG((1u << 13), RetvalRemoved) #ifdef DISP_FLAG_LARGEST_NEEDED // Intended to be used with ADT/BitmaskEnum.h. // NOTE: Always must be equal to largest flag, check this when adding new flags. -HANDLE_DISP_FLAG((1 << 11), Largest) +HANDLE_DISP_FLAG((1 << 13), Largest) #undef DISP_FLAG_LARGEST_NEEDED #endif diff --git a/llvm/include/llvm/IR/DebugInfoMetadata.h b/llvm/include/llvm/IR/DebugInfoMetadata.h index 8515d8eda8568..73d568b5576b4 100644 --- a/llvm/include/llvm/IR/DebugInfoMetadata.h +++ b/llvm/include/llvm/IR/DebugInfoMetadata.h @@ -1907,6 +1907,11 @@ class DISubprogram : public DILocalScope { DIScope *getScope() const { return cast_or_null(getRawScope()); } + void setArgChanged() { SPFlags |= SPFlagArgChanged; } + bool getArgChanged() const { return SPFlags & SPFlagArgChanged; } + void setRetvalRemoved() { SPFlags |= SPFlagRetvalRemoved; } + bool getRetvalRemoved() const { return SPFlags & SPFlagRetvalRemoved; } + StringRef getName() const { return getStringOperand(2); } StringRef getLinkageName() const { return getStringOperand(3); } /// Only used by clients of CloneFunction, and only right after the cloning. diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp index ddf0275ddfe6a..a80d764683db7 100644 --- a/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp @@ -1123,6 +1123,8 @@ DIE &DwarfCompileUnit::constructSubprogramScopeDIE(const DISubprogram *Sub, MCSymbol *LineTableSym) { DIE &ScopeDIE = updateSubprogramScopeDIE(Sub, LineTableSym); + DwarfUnit::addLLVMChangedArgs(ScopeDIE, Sub); + if (Scope) { assert(!Scope->getInlinedAt()); assert(!Scope->isAbstractScope()); diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp index 5347c8a049ba6..7dcc10eccab20 100644 --- a/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp @@ -654,6 +654,15 @@ DIE *DwarfUnit::getOrCreateTypeDIE(const MDNode *TyNode) { ->createTypeDIE(Context, *ContextDIE, Ty); } +void DwarfUnit::addLLVMChangedArgs(DIE &ScopeDIE, const DISubprogram *SP) { + if (!SP->getArgChanged()) + return; + + auto *LocalDie = + DIE::get(DIEValueAllocator, dwarf::DW_TAG_LLVM_func_args_changed); + ScopeDIE.addChild(LocalDie); +} + void DwarfUnit::updateAcceleratorTables(const DIScope *Context, const DIType *Ty, const DIE &TyDIE) { if (Ty->getName().empty()) @@ -1328,6 +1337,9 @@ void DwarfUnit::applySubprogramAttributes(const DISubprogram *SP, DIE &SPDie, if (!SkipSPSourceLocation) addSourceLine(SPDie, SP); + if (SP->getRetvalRemoved()) + addFlag(SPDie, dwarf::DW_AT_LLVM_func_retval_removed); + // Skip the rest of the attributes under -gmlt to save space. if (SkipSPAttributes) return; diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.h b/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.h index 9ddd6f8c14175..49724bcfc87ff 100644 --- a/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.h +++ b/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.h @@ -324,6 +324,9 @@ class DwarfUnit : public DIEUnit { void updateAcceleratorTables(const DIScope *Context, const DIType *Ty, const DIE &TyDIE); + /// Add DW_TAG_LLVM_func_args_changed. + void addLLVMChangedArgs(DIE &ScopeDIE, const DISubprogram *SP); + protected: ~DwarfUnit(); diff --git a/llvm/lib/Transforms/IPO/ArgumentPromotion.cpp b/llvm/lib/Transforms/IPO/ArgumentPromotion.cpp index c440638884322..e81adb830a86b 100644 --- a/llvm/lib/Transforms/IPO/ArgumentPromotion.cpp +++ b/llvm/lib/Transforms/IPO/ArgumentPromotion.cpp @@ -51,6 +51,7 @@ #include "llvm/IR/CFG.h" #include "llvm/IR/Constants.h" #include "llvm/IR/DataLayout.h" +#include "llvm/IR/DebugInfoMetadata.h" #include "llvm/IR/DerivedTypes.h" #include "llvm/IR/Dominators.h" #include "llvm/IR/Function.h" @@ -132,6 +133,7 @@ doPromotion(Function *F, FunctionAnalysisManager &FAM, // First, determine the new argument list unsigned ArgNo = 0, NewArgNo = 0; + bool CurrFuncArgChanged = false; for (Function::arg_iterator I = F->arg_begin(), E = F->arg_end(); I != E; ++I, ++ArgNo) { if (!ArgsToPromote.count(&*I)) { @@ -142,6 +144,7 @@ doPromotion(Function *F, FunctionAnalysisManager &FAM, } else if (I->use_empty()) { // Dead argument (which are always marked as promotable) ++NumArgumentsDead; + CurrFuncArgChanged = true; ORE.emit([&]() { return OptimizationRemark(DEBUG_TYPE, "ArgumentRemoved", F) << "eliminating argument " << ore::NV("ArgName", I->getName()) @@ -156,6 +159,7 @@ doPromotion(Function *F, FunctionAnalysisManager &FAM, ArgAttrVec.push_back(AttributeSet()); } ++NumArgumentsPromoted; + CurrFuncArgChanged = true; ORE.emit([&]() { return OptimizationRemark(DEBUG_TYPE, "ArgumentPromoted", F) << "promoting argument " << ore::NV("ArgName", I->getName()) @@ -433,6 +437,10 @@ doPromotion(Function *F, FunctionAnalysisManager &FAM, PromoteMemToReg(Allocas, DT, &AC); } + DISubprogram *SP = NF->getSubprogram(); + if (SP && CurrFuncArgChanged) + SP->setArgChanged(); + return NF; } diff --git a/llvm/lib/Transforms/IPO/DeadArgumentElimination.cpp b/llvm/lib/Transforms/IPO/DeadArgumentElimination.cpp index ed93b4491c50e..5df3d47daa8dd 100644 --- a/llvm/lib/Transforms/IPO/DeadArgumentElimination.cpp +++ b/llvm/lib/Transforms/IPO/DeadArgumentElimination.cpp @@ -757,6 +757,8 @@ bool DeadArgumentEliminationPass::removeDeadStuffFromFunction(Function *F) { // a new set of parameter attributes to correspond. Skip the first parameter // attribute, since that belongs to the return value. unsigned ArgI = 0; + bool CurrFuncArgEliminated = false; + bool CurrFuncRetEliminated = false; for (Function::arg_iterator I = F->arg_begin(), E = F->arg_end(); I != E; ++I, ++ArgI) { RetOrArg Arg = createArg(F, ArgI); @@ -767,6 +769,7 @@ bool DeadArgumentEliminationPass::removeDeadStuffFromFunction(Function *F) { HasLiveReturnedArg |= PAL.hasParamAttr(ArgI, Attribute::Returned); } else { ++NumArgumentsEliminated; + CurrFuncArgEliminated = true; ORE.emit([&]() { return OptimizationRemark(DEBUG_TYPE, "ArgumentRemoved", F) @@ -818,6 +821,7 @@ bool DeadArgumentEliminationPass::removeDeadStuffFromFunction(Function *F) { NewRetIdxs[Ri] = RetTypes.size() - 1; } else { ++NumRetValsEliminated; + CurrFuncRetEliminated = true; ORE.emit([&]() { return OptimizationRemark(DEBUG_TYPE, "ReturnValueRemoved", F) @@ -1099,6 +1103,12 @@ bool DeadArgumentEliminationPass::removeDeadStuffFromFunction(Function *F) { // to call this function or try to interpret the return value. if (NFTy != FTy && NF->getSubprogram()) { DISubprogram *SP = NF->getSubprogram(); + + if (CurrFuncArgEliminated) + SP->setArgChanged(); + if (CurrFuncRetEliminated) + SP->setRetvalRemoved(); + auto Temp = SP->getType()->cloneWithCC(llvm::dwarf::DW_CC_nocall); SP->replaceType(MDNode::replaceWithPermanent(std::move(Temp))); } diff --git a/llvm/test/DebugInfo/X86/arg-prom.ll b/llvm/test/DebugInfo/X86/arg-prom.ll new file mode 100644 index 0000000000000..4af58be2f7b18 --- /dev/null +++ b/llvm/test/DebugInfo/X86/arg-prom.ll @@ -0,0 +1,95 @@ +; RUN: opt -S -passes=argpromotion < %s | FileCheck %s +; +; Source code: +; __attribute__((noinline)) static int is_absolute_path(const char *path) +; { +; return path[0] == '/'; +; } +; +; void quit(char *buf); +; const char *make_nonrelative_path(char *buf, const char *path) +; { +; if (is_absolute_path(path)) +; quit(buf); +; return buf; +; } + +define dso_local ptr @make_nonrelative_path(ptr noundef %buf, ptr noundef %path) local_unnamed_addr #0 !dbg !10 { + #dbg_value(ptr %buf, !18, !DIExpression(), !20) + #dbg_value(ptr %path, !19, !DIExpression(), !20) + %x = call fastcc i32 @is_absolute_path(ptr noundef %path), !dbg !21 + %y = icmp eq i32 %x, 0, !dbg !21 + br i1 %y, label %to_ret, label %to_quit, !dbg !21 + +to_quit: + call void @quit(ptr noundef %buf), !dbg !23 + br label %to_ret, !dbg !23 + +to_ret: + ret ptr %buf, !dbg !24 +} + +; Function Attrs: noinline nounwind uwtable +define internal fastcc range(i32 0, 2) i32 @is_absolute_path(ptr noundef %path) unnamed_addr #1 !dbg !25 { + #dbg_value(ptr %path, !30, !DIExpression(), !31) + %x = load i8, ptr %path, align 1, !dbg !32, !tbaa !33 + %y = icmp eq i8 %x, 47, !dbg !36 + %z = zext i1 %y to i32, !dbg !36 + ret i32 %z, !dbg !37 +} + +; CHECK: define internal fastcc range(i32 0, 2) i32 @is_absolute_path(i8 {{.*}}) + +declare !dbg !38 void @quit(ptr noundef) local_unnamed_addr + +attributes #0 = { nounwind } +attributes #1 = { noinline nounwind } + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!2, !3, !4, !5, !6, !7, !8} +!llvm.ident = !{!9} + +!0 = distinct !DICompileUnit(language: DW_LANG_C11, file: !1, producer: "clang version 21.0.0git (git@github.com:yonghong-song/llvm-project.git 25cfee009e78194d1f7ca70779d63ef1936cc7b9)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None) +!1 = !DIFile(filename: "test.c", directory: "/tmp/tests/sig-change/prom", checksumkind: CSK_MD5, checksum: "bcc8cf18726713f5d2ab6d82e8ff459d") +!2 = !{i32 7, !"Dwarf Version", i32 5} +!3 = !{i32 2, !"Debug Info Version", i32 3} +!4 = !{i32 1, !"wchar_size", i32 4} +!5 = !{i32 8, !"PIC Level", i32 2} +!6 = !{i32 7, !"PIE Level", i32 2} +!7 = !{i32 7, !"uwtable", i32 2} +!8 = !{i32 7, !"debug-info-assignment-tracking", i1 true} +!9 = !{!"clang version 21.0.0git (git@github.com:yonghong-song/llvm-project.git 25cfee009e78194d1f7ca70779d63ef1936cc7b9)"} +!10 = distinct !DISubprogram(name: "make_nonrelative_path", scope: !1, file: !1, line: 7, type: !11, scopeLine: 8, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !17) +!11 = !DISubroutineType(types: !12) +!12 = !{!13, !16, !13} +!13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) +!14 = !DIDerivedType(tag: DW_TAG_const_type, baseType: !15) +!15 = !DIBasicType(name: "char", size: 8, encoding: DW_ATE_signed_char) +!16 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !15, size: 64) +!17 = !{!18, !19} +!18 = !DILocalVariable(name: "buf", arg: 1, scope: !10, file: !1, line: 7, type: !16) +!19 = !DILocalVariable(name: "path", arg: 2, scope: !10, file: !1, line: 7, type: !13) +!20 = !DILocation(line: 0, scope: !10) +!21 = !DILocation(line: 9, column: 7, scope: !22) +!22 = distinct !DILexicalBlock(scope: !10, file: !1, line: 9, column: 7) +!23 = !DILocation(line: 10, column: 5, scope: !22) +!24 = !DILocation(line: 11, column: 3, scope: !10) +!25 = distinct !DISubprogram(name: "is_absolute_path", scope: !1, file: !1, line: 1, type: !26, scopeLine: 2, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !29) + +; CHECK: distinct !DISubprogram(name: "is_absolute_path", scope: ![[#]], file: ![[#]], line: [[#]], type: ![[#]], scopeLine: [[#]], flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized | DISPFlagArgChanged, unit: ![[#]], retainedNodes: ![[#]]) + +!26 = !DISubroutineType(types: !27) +!27 = !{!28, !13} +!28 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!29 = !{!30} +!30 = !DILocalVariable(name: "path", arg: 1, scope: !25, file: !1, line: 1, type: !13) +!31 = !DILocation(line: 0, scope: !25) +!32 = !DILocation(line: 3, column: 10, scope: !25) +!33 = !{!34, !34, i64 0} +!34 = !{!"omnipotent char", !35, i64 0} +!35 = !{!"Simple C/C++ TBAA"} +!36 = !DILocation(line: 3, column: 18, scope: !25) +!37 = !DILocation(line: 3, column: 3, scope: !25) +!38 = !DISubprogram(name: "quit", scope: !1, file: !1, line: 6, type: !39, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized) +!39 = !DISubroutineType(types: !40) +!40 = !{null, !16} diff --git a/llvm/test/DebugInfo/X86/arg-retval-elim.ll b/llvm/test/DebugInfo/X86/arg-retval-elim.ll new file mode 100644 index 0000000000000..8129bc51d8d34 --- /dev/null +++ b/llvm/test/DebugInfo/X86/arg-retval-elim.ll @@ -0,0 +1,76 @@ +; RUN: opt -S -passes=deadargelim < %s | FileCheck %s +; +; Source code: +; int tar(int a); +; __attribute__((noinline)) static int foo(int a, int b) +; { +; return tar(a) + tar(a + 1); +; } +; int bar(int a) +; { +; foo(a, 1); +; return 0; +; } + +define dso_local noundef i32 @bar(i32 noundef %a) local_unnamed_addr #0 !dbg !10 { + #dbg_value(i32 %a, !15, !DIExpression(), !16) + %2 = tail call fastcc i32 @foo(i32 noundef %a, i32 noundef 1), !dbg !17 + ret i32 0, !dbg !18 +} + +define internal fastcc i32 @foo(i32 noundef %a, i32 noundef %b) unnamed_addr #1 !dbg !19 { + #dbg_value(i32 %a, !23, !DIExpression(), !25) + #dbg_value(i32 %b, !24, !DIExpression(), !25) + %x = tail call i32 @tar(i32 noundef %a), !dbg !26 + %t = add nsw i32 %a, 1, !dbg !27 + %y = tail call i32 @tar(i32 noundef %t), !dbg !28 + %z = add nsw i32 %y, %x, !dbg !29 + ret i32 %z, !dbg !30 +} + +; CHECK: define internal fastcc void @foo(i32 noundef %a) + +declare !dbg !31 i32 @tar(i32 noundef) local_unnamed_addr + +attributes #0 = { nounwind } +attributes #1 = { noinline nounwind } + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!2, !3, !4, !5, !6, !7, !8} +!llvm.ident = !{!9} + +!0 = distinct !DICompileUnit(language: DW_LANG_C11, file: !1, producer: "clang version 21.0.0git (git@github.com:yonghong-song/llvm-project.git 25cfee009e78194d1f7ca70779d63ef1936cc7b9)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None) +!1 = !DIFile(filename: "test.c", directory: "/tmp/tests/sig-change/deadret", checksumkind: CSK_MD5, checksum: "728d225e6425c104712ae21cee1db99b") +!2 = !{i32 7, !"Dwarf Version", i32 5} +!3 = !{i32 2, !"Debug Info Version", i32 3} +!4 = !{i32 1, !"wchar_size", i32 4} +!5 = !{i32 8, !"PIC Level", i32 2} +!6 = !{i32 7, !"PIE Level", i32 2} +!7 = !{i32 7, !"uwtable", i32 2} +!8 = !{i32 7, !"debug-info-assignment-tracking", i1 true} +!9 = !{!"clang version 21.0.0git (git@github.com:yonghong-song/llvm-project.git 25cfee009e78194d1f7ca70779d63ef1936cc7b9)"} +!10 = distinct !DISubprogram(name: "bar", scope: !1, file: !1, line: 6, type: !11, scopeLine: 7, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !14) +!11 = !DISubroutineType(types: !12) +!12 = !{!13, !13} +!13 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!14 = !{!15} +!15 = !DILocalVariable(name: "a", arg: 1, scope: !10, file: !1, line: 6, type: !13) +!16 = !DILocation(line: 0, scope: !10) +!17 = !DILocation(line: 8, column: 3, scope: !10) +!18 = !DILocation(line: 9, column: 3, scope: !10) +!19 = distinct !DISubprogram(name: "foo", scope: !1, file: !1, line: 2, type: !20, scopeLine: 3, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !22) + +; CHECK: distinct !DISubprogram(name: "foo", scope: ![[#]], file: ![[#]], line: [[#]], type: ![[#]], scopeLine: [[#]], flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized | DISPFlagArgChanged | DISPFlagRetvalRemoved, unit: !0, retainedNodes: ![[#]]) + +!20 = !DISubroutineType(types: !21) +!21 = !{!13, !13, !13} +!22 = !{!23, !24} +!23 = !DILocalVariable(name: "a", arg: 1, scope: !19, file: !1, line: 2, type: !13) +!24 = !DILocalVariable(name: "b", arg: 2, scope: !19, file: !1, line: 2, type: !13) +!25 = !DILocation(line: 0, scope: !19) +!26 = !DILocation(line: 4, column: 10, scope: !19) +!27 = !DILocation(line: 4, column: 25, scope: !19) +!28 = !DILocation(line: 4, column: 19, scope: !19) +!29 = !DILocation(line: 4, column: 17, scope: !19) +!30 = !DILocation(line: 4, column: 3, scope: !19) +!31 = !DISubprogram(name: "tar", scope: !1, file: !1, line: 1, type: !11, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized)