From 340b0ca90095d838f095271aaa1098fa1bd5ecbe Mon Sep 17 00:00:00 2001 From: Venkata Ramanaiah Nalamothu Date: Mon, 6 Jun 2022 21:07:09 +0530 Subject: [PATCH] [llvm] Add DW_CC_nocall to function debug metadata when either return values or arguments are removed Adding the `DW_CC_nocall` calling convention to the function debug metadata is needed when either the return values or the arguments of a function are removed as this helps in informing debugger that it may not be safe to call this function or try to interpret the return value. This translates to setting `DW_AT_calling_convention` with `DW_CC_nocall` for appropriate DWARF DIEs. The DWARF5 spec (section 3.3.1.1 Calling Convention Information) says: If the `DW_AT_calling_convention` attribute is not present, or its value is the constant `DW_CC_normal`, then the subroutine may be safely called by obeying the `standard` calling conventions of the target architecture. If the value of the calling convention attribute is the constant `DW_CC_nocall`, the subroutine does not obey standard calling conventions, and it may not be safe for the debugger to call this subroutine. Reviewed By: dblaikie Differential Revision: https://reviews.llvm.org/D127134 --- llvm/include/llvm/IR/DebugInfoMetadata.h | 10 ++++++++++ llvm/lib/Transforms/IPO/DeadArgumentElimination.cpp | 11 +++++++++++ llvm/test/Transforms/DeadArgElim/2010-04-30-DbgInfo.ll | 10 ++++++++-- 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/llvm/include/llvm/IR/DebugInfoMetadata.h b/llvm/include/llvm/IR/DebugInfoMetadata.h index bc916fe..0eb96d2 100644 --- a/llvm/include/llvm/IR/DebugInfoMetadata.h +++ b/llvm/include/llvm/IR/DebugInfoMetadata.h @@ -1298,6 +1298,12 @@ public: (Flags, CC, TypeArray)) TempDISubroutineType clone() const { return cloneImpl(); } + // Returns a new temporary DISubroutineType with updated CC + TempDISubroutineType cloneWithCC(uint8_t CC) const { + auto NewTy = clone(); + NewTy->CC = CC; + return NewTy; + } uint8_t getCC() const { return CC; } @@ -1996,6 +2002,10 @@ public: DIType *getContainingType() const { return cast_or_null(getRawContainingType()); } + void replaceType(DISubroutineType *Ty) { + assert(isDistinct() && "Only distinct nodes can mutate"); + replaceOperandWith(4, Ty); + } DICompileUnit *getUnit() const { return cast_or_null(getRawUnit()); diff --git a/llvm/lib/Transforms/IPO/DeadArgumentElimination.cpp b/llvm/lib/Transforms/IPO/DeadArgumentElimination.cpp index ddc2d5b..5ba8fe9 100644 --- a/llvm/lib/Transforms/IPO/DeadArgumentElimination.cpp +++ b/llvm/lib/Transforms/IPO/DeadArgumentElimination.cpp @@ -23,6 +23,7 @@ #include "llvm/IR/Attributes.h" #include "llvm/IR/BasicBlock.h" #include "llvm/IR/Constants.h" +#include "llvm/IR/DIBuilder.h" #include "llvm/IR/DerivedTypes.h" #include "llvm/IR/Function.h" #include "llvm/IR/IRBuilder.h" @@ -1073,6 +1074,16 @@ bool DeadArgumentEliminationPass::RemoveDeadStuffFromFunction(Function *F) { for (auto MD : MDs) NF->addMetadata(MD.first, *MD.second); + // If either the return value(s) or argument(s) are removed, then probably the + // function does not follow standard calling conventions anymore. Hence add + // DW_CC_nocall to DISubroutineType to inform debugger that it may not safe to + // call this function or try to interpret the return value. + if (NFTy != FTy && NF->getSubprogram()) { + DISubprogram *SP = NF->getSubprogram(); + auto Temp = SP->getType()->cloneWithCC(llvm::dwarf::DW_CC_nocall); + SP->replaceType(MDNode::replaceWithPermanent(std::move(Temp))); + } + // Now that the old function is dead, delete it. F->eraseFromParent(); diff --git a/llvm/test/Transforms/DeadArgElim/2010-04-30-DbgInfo.ll b/llvm/test/Transforms/DeadArgElim/2010-04-30-DbgInfo.ll index 0056962..047545a 100644 --- a/llvm/test/Transforms/DeadArgElim/2010-04-30-DbgInfo.ll +++ b/llvm/test/Transforms/DeadArgElim/2010-04-30-DbgInfo.ll @@ -1,9 +1,12 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature ; RUN: opt -S -passes=deadargelim < %s | FileCheck %s +; Apart from checking if debug metadata is correctly propagated, this also tests whether DW_CC_nocall +; calling convention is added when either return values or arguments are removed. + @.str = private constant [1 x i8] zeroinitializer, align 1 ; <[1 x i8]*> [#uses=1] -define i8* @vfs_addname(i8* %name, i32 %len, i32 %hash, i32 %flags) nounwind ssp { +define i8* @vfs_addname(i8* %name, i32 %len, i32 %hash, i32 %flags) nounwind ssp !dbg !1 { ; entry: call void @llvm.dbg.value(metadata i8* %name, metadata !0, metadata !DIExpression()), !dbg !DILocation(scope: !1) @@ -17,7 +20,7 @@ entry: declare void @llvm.dbg.declare(metadata, metadata, metadata) nounwind readnone -define internal fastcc i8* @add_name_internal(i8* %name, i32 %len, i32 %hash, i8 zeroext %extra, i32 %flags) noinline nounwind ssp { +define internal fastcc i8* @add_name_internal(i8* %name, i32 %len, i32 %hash, i8 zeroext %extra, i32 %flags) noinline nounwind ssp !dbg !16 { ; entry: call void @llvm.dbg.value(metadata i8* %name, metadata !15, metadata !DIExpression()), !dbg !DILocation(scope: !16) @@ -64,7 +67,10 @@ declare void @llvm.dbg.value(metadata, metadata, metadata) nounwind readnone !13 = !DILocation(line: 13, scope: !14) !14 = distinct !DILexicalBlock(line: 12, column: 0, file: !28, scope: !1) !15 = !DILocalVariable(name: "name", line: 17, arg: 1, scope: !16, file: !2, type: !6) +; CHECK: !DISubprogram(name: "add_name_internal" +; CHECK-SAME: type: ![[MD:[0-9]+]] !16 = distinct !DISubprogram(name: "add_name_internal", linkageName: "add_name_internal", line: 22, isLocal: true, isDefinition: true, virtualIndex: 6, isOptimized: false, unit: !3, file: !28, scope: !2, type: !17) +; CHECK: ![[MD]] = !DISubroutineType(cc: DW_CC_nocall, types: !{{[0-9]+}}) !17 = !DISubroutineType(types: !18) !18 = !{!6, !6, !9, !9, !19, !9} !19 = !DIBasicType(tag: DW_TAG_base_type, name: "unsigned char", size: 8, align: 8, encoding: DW_ATE_unsigned_char) -- 2.7.4