Skip to content

Commit efe2f88

Browse files
author
Yonghong Song
committed
[Dwarf][Transforms] Add dwarf support when func signature changed
The goal is to add additional tag/attribute to dwarf so users will know that function signatures get changed. See [1] for motivation. Otherwise, users may assume function signature remaining the same as its source, and bpf tracing may get wrong results. With explicit tag/attribute in dwarf to indicate a func signature change, for bpf tracing, users will either go into the asm code to find the exact signature or go to find another non-signature-change function for tracing, instead of debugging the otherwise rong results. Earlier I have a pull request [2] attempts to add suffix to indicate signature change as gcc did this already. But later upstream suggested to use dwarf to encode such suffix change info ([1]). This patch introduced a new tag LLVM_func_args_changed and a new attr LLVM_func_retval_removed. In DeadArgumentElimination pass, if a function return value is removed, LLVM_func_retval_removed attr will be added to that func in the dwarf. In DeadArgumentElimination and ArgumentPromotion passes, if the function signature is changed, LLVM_func_args_changed tag is added to dwarf. Here, LLVM_func_args_changed tag is used so later on, we could add more debug info about what changes. Regarding to potential more info under LLVM_func_args_changed, we might need the following info. 1. Trying to have a new set of formal argument types. The existing types should be available in related Transforms passes, but will need DIBuilder to build DIType's and looks like there is not easy DIBuilder API to do this. 2. Trying to relate old func signature (from source) to new func signature. For example, original arg index 2 becomes new arg index 1, etc. More complexity will come from argument promotion and struct arguments where struct argument has size greater than an arch register size. [1] https://discourse.llvm.org/t/rfc-identify-func-signature-change-in-llvm-compiled-kernel-image/82609 [2] llvm#109899
1 parent 9ebb618 commit efe2f88

File tree

10 files changed

+304
-1
lines changed

10 files changed

+304
-1
lines changed

llvm/include/llvm/BinaryFormat/Dwarf.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,7 @@ HANDLE_DW_TAG(0x5111, ALTIUM_rom, 0, ALTIUM, DW_KIND_NONE)
268268

269269
// LLVM
270270
HANDLE_DW_TAG(0x6000, LLVM_annotation, 0, LLVM, DW_KIND_NONE)
271+
HANDLE_DW_TAG(0x6001, LLVM_func_args_changed, 0, LLVM, DW_KIND_NONE)
271272

272273
// Green Hills.
273274
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)
624625
HANDLE_DW_AT(0x3e0a, LLVM_ptrauth_authentication_mode, 0, LLVM)
625626
HANDLE_DW_AT(0x3e0b, LLVM_num_extra_inhabitants, 0, LLVM)
626627
HANDLE_DW_AT(0x3e0c, LLVM_stmt_sequence, 0, LLVM)
628+
HANDLE_DW_AT(0x3e0d, LLVM_func_retval_removed, 0, LLVM)
627629

628630
// Apple extensions.
629631

llvm/include/llvm/IR/DebugInfoFlags.def

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,11 +91,13 @@ HANDLE_DISP_FLAG((1u << 8), MainSubprogram)
9191
// for defaulted functions
9292
HANDLE_DISP_FLAG((1u << 9), Deleted)
9393
HANDLE_DISP_FLAG((1u << 11), ObjCDirect)
94+
HANDLE_DISP_FLAG((1u << 12), ArgChanged)
95+
HANDLE_DISP_FLAG((1u << 13), RetvalRemoved)
9496

9597
#ifdef DISP_FLAG_LARGEST_NEEDED
9698
// Intended to be used with ADT/BitmaskEnum.h.
9799
// NOTE: Always must be equal to largest flag, check this when adding new flags.
98-
HANDLE_DISP_FLAG((1 << 11), Largest)
100+
HANDLE_DISP_FLAG((1 << 13), Largest)
99101
#undef DISP_FLAG_LARGEST_NEEDED
100102
#endif
101103

llvm/include/llvm/IR/DebugInfoMetadata.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1907,6 +1907,11 @@ class DISubprogram : public DILocalScope {
19071907

19081908
DIScope *getScope() const { return cast_or_null<DIScope>(getRawScope()); }
19091909

1910+
void setArgChanged() { SPFlags |= SPFlagArgChanged; }
1911+
bool getArgChanged() const { return SPFlags & SPFlagArgChanged; }
1912+
void setRetvalRemoved() { SPFlags |= SPFlagRetvalRemoved; }
1913+
bool getRetvalRemoved() const { return SPFlags & SPFlagRetvalRemoved; }
1914+
19101915
StringRef getName() const { return getStringOperand(2); }
19111916
StringRef getLinkageName() const { return getStringOperand(3); }
19121917
/// Only used by clients of CloneFunction, and only right after the cloning.

llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1123,6 +1123,8 @@ DIE &DwarfCompileUnit::constructSubprogramScopeDIE(const DISubprogram *Sub,
11231123
MCSymbol *LineTableSym) {
11241124
DIE &ScopeDIE = updateSubprogramScopeDIE(Sub, LineTableSym);
11251125

1126+
DwarfUnit::addLLVMChangedArgs(ScopeDIE, Sub);
1127+
11261128
if (Scope) {
11271129
assert(!Scope->getInlinedAt());
11281130
assert(!Scope->isAbstractScope());

llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -654,6 +654,14 @@ DIE *DwarfUnit::getOrCreateTypeDIE(const MDNode *TyNode) {
654654
->createTypeDIE(Context, *ContextDIE, Ty);
655655
}
656656

657+
void DwarfUnit::addLLVMChangedArgs(DIE &ScopeDIE, const DISubprogram *SP) {
658+
if (!SP->getArgChanged())
659+
return;
660+
661+
auto *LocalDie = DIE::get(DIEValueAllocator, dwarf::DW_TAG_LLVM_func_args_changed);
662+
ScopeDIE.addChild(LocalDie);
663+
}
664+
657665
void DwarfUnit::updateAcceleratorTables(const DIScope *Context,
658666
const DIType *Ty, const DIE &TyDIE) {
659667
if (Ty->getName().empty())
@@ -1328,6 +1336,9 @@ void DwarfUnit::applySubprogramAttributes(const DISubprogram *SP, DIE &SPDie,
13281336
if (!SkipSPSourceLocation)
13291337
addSourceLine(SPDie, SP);
13301338

1339+
if (SP->getRetvalRemoved())
1340+
addFlag(SPDie, dwarf::DW_AT_LLVM_func_retval_removed);
1341+
13311342
// Skip the rest of the attributes under -gmlt to save space.
13321343
if (SkipSPAttributes)
13331344
return;

llvm/lib/CodeGen/AsmPrinter/DwarfUnit.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,9 @@ class DwarfUnit : public DIEUnit {
324324
void updateAcceleratorTables(const DIScope *Context, const DIType *Ty,
325325
const DIE &TyDIE);
326326

327+
/// Add DW_TAG_LLVM_func_args_changed.
328+
void addLLVMChangedArgs(DIE &ScopeDIE, const DISubprogram *SP);
329+
327330
protected:
328331
~DwarfUnit();
329332

llvm/lib/Transforms/IPO/ArgumentPromotion.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
#include "llvm/IR/CFG.h"
5252
#include "llvm/IR/Constants.h"
5353
#include "llvm/IR/DataLayout.h"
54+
#include "llvm/IR/DebugInfoMetadata.h"
5455
#include "llvm/IR/DerivedTypes.h"
5556
#include "llvm/IR/Dominators.h"
5657
#include "llvm/IR/Function.h"
@@ -132,6 +133,7 @@ doPromotion(Function *F, FunctionAnalysisManager &FAM,
132133

133134
// First, determine the new argument list
134135
unsigned ArgNo = 0, NewArgNo = 0;
136+
bool CurrFuncArgChanged = false;
135137
for (Function::arg_iterator I = F->arg_begin(), E = F->arg_end(); I != E;
136138
++I, ++ArgNo) {
137139
if (!ArgsToPromote.count(&*I)) {
@@ -142,6 +144,7 @@ doPromotion(Function *F, FunctionAnalysisManager &FAM,
142144
} else if (I->use_empty()) {
143145
// Dead argument (which are always marked as promotable)
144146
++NumArgumentsDead;
147+
CurrFuncArgChanged = true;
145148
ORE.emit([&]() {
146149
return OptimizationRemark(DEBUG_TYPE, "ArgumentRemoved", F)
147150
<< "eliminating argument " << ore::NV("ArgName", I->getName())
@@ -156,6 +159,7 @@ doPromotion(Function *F, FunctionAnalysisManager &FAM,
156159
ArgAttrVec.push_back(AttributeSet());
157160
}
158161
++NumArgumentsPromoted;
162+
CurrFuncArgChanged = true;
159163
ORE.emit([&]() {
160164
return OptimizationRemark(DEBUG_TYPE, "ArgumentPromoted", F)
161165
<< "promoting argument " << ore::NV("ArgName", I->getName())
@@ -433,6 +437,10 @@ doPromotion(Function *F, FunctionAnalysisManager &FAM,
433437
PromoteMemToReg(Allocas, DT, &AC);
434438
}
435439

440+
DISubprogram *SP = NF->getSubprogram();
441+
if (SP && CurrFuncArgChanged)
442+
SP->setArgChanged();
443+
436444
return NF;
437445
}
438446

llvm/lib/Transforms/IPO/DeadArgumentElimination.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -757,6 +757,8 @@ bool DeadArgumentEliminationPass::removeDeadStuffFromFunction(Function *F) {
757757
// a new set of parameter attributes to correspond. Skip the first parameter
758758
// attribute, since that belongs to the return value.
759759
unsigned ArgI = 0;
760+
bool CurrFuncArgEliminated = false;
761+
bool CurrFuncRetEliminated = false;
760762
for (Function::arg_iterator I = F->arg_begin(), E = F->arg_end(); I != E;
761763
++I, ++ArgI) {
762764
RetOrArg Arg = createArg(F, ArgI);
@@ -767,6 +769,7 @@ bool DeadArgumentEliminationPass::removeDeadStuffFromFunction(Function *F) {
767769
HasLiveReturnedArg |= PAL.hasParamAttr(ArgI, Attribute::Returned);
768770
} else {
769771
++NumArgumentsEliminated;
772+
CurrFuncArgEliminated = true;
770773

771774
ORE.emit([&]() {
772775
return OptimizationRemark(DEBUG_TYPE, "ArgumentRemoved", F)
@@ -818,6 +821,7 @@ bool DeadArgumentEliminationPass::removeDeadStuffFromFunction(Function *F) {
818821
NewRetIdxs[Ri] = RetTypes.size() - 1;
819822
} else {
820823
++NumRetValsEliminated;
824+
CurrFuncRetEliminated = true;
821825

822826
ORE.emit([&]() {
823827
return OptimizationRemark(DEBUG_TYPE, "ReturnValueRemoved", F)
@@ -1099,6 +1103,12 @@ bool DeadArgumentEliminationPass::removeDeadStuffFromFunction(Function *F) {
10991103
// to call this function or try to interpret the return value.
11001104
if (NFTy != FTy && NF->getSubprogram()) {
11011105
DISubprogram *SP = NF->getSubprogram();
1106+
1107+
if (CurrFuncArgEliminated)
1108+
SP->setArgChanged();
1109+
if (CurrFuncRetEliminated)
1110+
SP->setRetvalRemoved();
1111+
11021112
auto Temp = SP->getType()->cloneWithCC(llvm::dwarf::DW_CC_nocall);
11031113
SP->replaceType(MDNode::replaceWithPermanent(std::move(Temp)));
11041114
}

llvm/test/DebugInfo/arg-prom.ll

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
; REQUIRES: x86-registered-target
2+
; RUN: opt -O3 -S < %s | FileCheck %s
3+
;
4+
; Source code:
5+
; __attribute__((noinline)) static int is_absolute_path(const char *path)
6+
; {
7+
; return path[0] == '/';
8+
; }
9+
;
10+
; int scpy(char *, const char *, int);
11+
; int quit(void);
12+
; const char *make_nonrelative_path(char *buf, int sz, const char *path)
13+
; {
14+
; if (is_absolute_path(path)) {
15+
; if (scpy(buf, path, sz) >= sz)
16+
; quit();
17+
; }
18+
; return buf;
19+
; }
20+
; Compilation flag:
21+
; clang -O2 -g -S -emit-llvm -Xclang -disable-llvm-passes test.c
22+
23+
; ModuleID = 'test.c'
24+
source_filename = "test.c"
25+
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
26+
target triple = "x86_64-unknown-linux-gnu"
27+
28+
; Function Attrs: nounwind uwtable
29+
define dso_local ptr @make_nonrelative_path(ptr noundef %0, i32 noundef %1, ptr noundef %2) #0 !dbg !9 {
30+
%4 = alloca ptr, align 8
31+
%5 = alloca i32, align 4
32+
%6 = alloca ptr, align 8
33+
store ptr %0, ptr %4, align 8, !tbaa !21
34+
#dbg_declare(ptr %4, !18, !DIExpression(), !26)
35+
store i32 %1, ptr %5, align 4, !tbaa !27
36+
#dbg_declare(ptr %5, !19, !DIExpression(), !29)
37+
store ptr %2, ptr %6, align 8, !tbaa !21
38+
#dbg_declare(ptr %6, !20, !DIExpression(), !30)
39+
%7 = load ptr, ptr %6, align 8, !dbg !31, !tbaa !21
40+
%8 = call i32 @is_absolute_path(ptr noundef %7), !dbg !33
41+
%9 = icmp ne i32 %8, 0, !dbg !33
42+
br i1 %9, label %10, label %20, !dbg !33
43+
44+
10: ; preds = %3
45+
%11 = load ptr, ptr %4, align 8, !dbg !34, !tbaa !21
46+
%12 = load ptr, ptr %6, align 8, !dbg !37, !tbaa !21
47+
%13 = load i32, ptr %5, align 4, !dbg !38, !tbaa !27
48+
%14 = call i32 @scpy(ptr noundef %11, ptr noundef %12, i32 noundef %13), !dbg !39
49+
%15 = load i32, ptr %5, align 4, !dbg !40, !tbaa !27
50+
%16 = icmp sge i32 %14, %15, !dbg !41
51+
br i1 %16, label %17, label %19, !dbg !41
52+
53+
17: ; preds = %10
54+
%18 = call i32 @quit(), !dbg !42
55+
br label %19, !dbg !42
56+
57+
19: ; preds = %17, %10
58+
br label %20, !dbg !43
59+
60+
20: ; preds = %19, %3
61+
%21 = load ptr, ptr %4, align 8, !dbg !44, !tbaa !21
62+
ret ptr %21, !dbg !45
63+
}
64+
65+
; Function Attrs: noinline nounwind uwtable
66+
define internal i32 @is_absolute_path(ptr noundef %0) #1 !dbg !46 {
67+
%2 = alloca ptr, align 8
68+
store ptr %0, ptr %2, align 8, !tbaa !21
69+
#dbg_declare(ptr %2, !50, !DIExpression(), !51)
70+
%3 = load ptr, ptr %2, align 8, !dbg !52, !tbaa !21
71+
%4 = getelementptr inbounds i8, ptr %3, i64 0, !dbg !52
72+
%5 = load i8, ptr %4, align 1, !dbg !52, !tbaa !53
73+
%6 = sext i8 %5 to i32, !dbg !52
74+
%7 = icmp eq i32 %6, 47, !dbg !54
75+
%8 = zext i1 %7 to i32, !dbg !54
76+
ret i32 %8, !dbg !55
77+
}
78+
79+
; CHECK: define internal fastcc range(i32 0, 2) i32 @is_absolute_path(i8 {{.*}})
80+
81+
declare !dbg !56 i32 @scpy(ptr noundef, ptr noundef, i32 noundef) #2
82+
83+
declare !dbg !59 i32 @quit() #2
84+
85+
attributes #0 = { nounwind uwtable "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
86+
attributes #1 = { noinline nounwind uwtable "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
87+
attributes #2 = { "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
88+
89+
!llvm.dbg.cu = !{!0}
90+
!llvm.module.flags = !{!2, !3, !4, !5, !6, !7}
91+
!llvm.ident = !{!8}
92+
93+
!0 = distinct !DICompileUnit(language: DW_LANG_C11, file: !1, producer: "clang version 21.0.0git ([email protected]:yonghong-song/llvm-project.git bbfd0a15ade80596f6d6dde8add7d50f4875dde1)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None)
94+
!1 = !DIFile(filename: "test.c", directory: "/tmp/tests/sig-change/prom", checksumkind: CSK_MD5, checksum: "1befb35eb4507489630adb56cb20fe09")
95+
!2 = !{i32 7, !"Dwarf Version", i32 5}
96+
!3 = !{i32 2, !"Debug Info Version", i32 3}
97+
!4 = !{i32 1, !"wchar_size", i32 4}
98+
!5 = !{i32 8, !"PIC Level", i32 2}
99+
!6 = !{i32 7, !"PIE Level", i32 2}
100+
!7 = !{i32 7, !"uwtable", i32 2}
101+
!8 = !{!"clang version 21.0.0git ([email protected]:yonghong-song/llvm-project.git bbfd0a15ade80596f6d6dde8add7d50f4875dde1)"}
102+
!9 = distinct !DISubprogram(name: "make_nonrelative_path", scope: !1, file: !1, line: 8, type: !10, scopeLine: 9, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !17)
103+
!10 = !DISubroutineType(types: !11)
104+
!11 = !{!12, !15, !16, !12}
105+
!12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64)
106+
!13 = !DIDerivedType(tag: DW_TAG_const_type, baseType: !14)
107+
!14 = !DIBasicType(name: "char", size: 8, encoding: DW_ATE_signed_char)
108+
!15 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64)
109+
!16 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
110+
!17 = !{!18, !19, !20}
111+
!18 = !DILocalVariable(name: "buf", arg: 1, scope: !9, file: !1, line: 8, type: !15)
112+
!19 = !DILocalVariable(name: "sz", arg: 2, scope: !9, file: !1, line: 8, type: !16)
113+
!20 = !DILocalVariable(name: "path", arg: 3, scope: !9, file: !1, line: 8, type: !12)
114+
!21 = !{!22, !22, i64 0}
115+
!22 = !{!"p1 omnipotent char", !23, i64 0}
116+
!23 = !{!"any pointer", !24, i64 0}
117+
!24 = !{!"omnipotent char", !25, i64 0}
118+
!25 = !{!"Simple C/C++ TBAA"}
119+
!26 = !DILocation(line: 8, column: 41, scope: !9)
120+
!27 = !{!28, !28, i64 0}
121+
!28 = !{!"int", !24, i64 0}
122+
!29 = !DILocation(line: 8, column: 50, scope: !9)
123+
!30 = !DILocation(line: 8, column: 66, scope: !9)
124+
!31 = !DILocation(line: 10, column: 30, scope: !32)
125+
!32 = distinct !DILexicalBlock(scope: !9, file: !1, line: 10, column: 13)
126+
!33 = !DILocation(line: 10, column: 13, scope: !32)
127+
!34 = !DILocation(line: 11, column: 26, scope: !35)
128+
!35 = distinct !DILexicalBlock(scope: !36, file: !1, line: 11, column: 21)
129+
!36 = distinct !DILexicalBlock(scope: !32, file: !1, line: 10, column: 37)
130+
!37 = !DILocation(line: 11, column: 31, scope: !35)
131+
!38 = !DILocation(line: 11, column: 37, scope: !35)
132+
!39 = !DILocation(line: 11, column: 21, scope: !35)
133+
!40 = !DILocation(line: 11, column: 44, scope: !35)
134+
!41 = !DILocation(line: 11, column: 41, scope: !35)
135+
!42 = !DILocation(line: 12, column: 4, scope: !35)
136+
!43 = !DILocation(line: 13, column: 9, scope: !36)
137+
!44 = !DILocation(line: 14, column: 16, scope: !9)
138+
!45 = !DILocation(line: 14, column: 9, scope: !9)
139+
!46 = distinct !DISubprogram(name: "is_absolute_path", scope: !1, file: !1, line: 1, type: !47, scopeLine: 2, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !49)
140+
141+
; CHECK: distinct !DISubprogram(name: "is_absolute_path", scope: ![[#]], file: ![[#]], line: [[#]], type: ![[#]], scopeLine: [[#]], flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized | DISPFlagArgChanged, unit: ![[#]], retainedNodes: ![[#]])
142+
143+
!47 = !DISubroutineType(types: !48)
144+
!48 = !{!16, !12}
145+
!49 = !{!50}
146+
!50 = !DILocalVariable(name: "path", arg: 1, scope: !46, file: !1, line: 1, type: !12)
147+
!51 = !DILocation(line: 1, column: 67, scope: !46)
148+
!52 = !DILocation(line: 3, column: 16, scope: !46)
149+
!53 = !{!24, !24, i64 0}
150+
!54 = !DILocation(line: 3, column: 24, scope: !46)
151+
!55 = !DILocation(line: 3, column: 9, scope: !46)
152+
!56 = !DISubprogram(name: "scpy", scope: !1, file: !1, line: 6, type: !57, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized)
153+
!57 = !DISubroutineType(types: !58)
154+
!58 = !{!16, !15, !12, !16}
155+
!59 = !DISubprogram(name: "quit", scope: !1, file: !1, line: 7, type: !60, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized)
156+
!60 = !DISubroutineType(types: !61)
157+
!61 = !{!16}

0 commit comments

Comments
 (0)