Skip to content

Commit 28f9636

Browse files
committed
[Assignment Tracking][7/*] Add assignment tracking functionality to clang
The Assignment Tracking debug-info feature is outlined in this RFC: https://discourse.llvm.org/t/ rfc-assignment-tracking-a-better-way-of-specifying-variable-locations-in-ir This patch plumbs the AssignmentTrackingPass (AKA declare-to-assign), added in the previous patch in this set, into the optimisation pipeline from clang. clang/test/CodeGen/assignment-tracking/assignment-tracking.cpp is the main test for this patch. Note: while clang (with the help of the declare-to-assign pass) can now emit Assignment Tracking metadata, the llvm middle and back ends don't yet understand it. Reviewed By: jmorse Differential Revision: https://reviews.llvm.org/D132226
1 parent 445a2ab commit 28f9636

File tree

6 files changed

+207
-0
lines changed

6 files changed

+207
-0
lines changed

clang/lib/CodeGen/BackendUtil.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include "llvm/CodeGen/SchedulerRegistry.h"
3131
#include "llvm/CodeGen/TargetSubtargetInfo.h"
3232
#include "llvm/IR/DataLayout.h"
33+
#include "llvm/IR/DebugInfo.h"
3334
#include "llvm/IR/IRPrintingPasses.h"
3435
#include "llvm/IR/LegacyPassManager.h"
3536
#include "llvm/IR/Module.h"
@@ -830,6 +831,13 @@ void EmitAssemblyHelper::RunOptimizationPipeline(
830831
SI.registerCallbacks(PIC, &FAM);
831832
PassBuilder PB(TM.get(), PTO, PGOOpt, &PIC);
832833

834+
if (CodeGenOpts.EnableAssignmentTracking) {
835+
PB.registerPipelineStartEPCallback(
836+
[&](ModulePassManager &MPM, OptimizationLevel Level) {
837+
MPM.addPass(AssignmentTrackingPass());
838+
});
839+
}
840+
833841
// Enable verify-debuginfo-preserve-each for new PM.
834842
DebugifyEachInstrumentation Debugify;
835843
DebugInfoPerPass DebugInfoBeforePass;
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
// RUN: %clang_cc1 -triple x86_64-none-linux-gnu -debug-info-kind=standalone -O0 \
2+
// RUN: -emit-llvm -fexperimental-assignment-tracking %s -o - \
3+
// RUN: | FileCheck %s --implicit-check-not="call void @llvm.dbg"
4+
5+
// Based on llvm/test/DebugInfo/Generic/track-assignments.ll - check that using
6+
// -Xclang -fexperimental-assignment-tracking results in emitting (or, as it is
7+
// set up currently, telling llvm to create) assignment tracking metadata.
8+
//
9+
// See the original test for more info.
10+
11+
struct Inner { int A, B; };
12+
struct Outer { Inner A, B; };
13+
struct Large { int A[10]; };
14+
struct LCopyCtor { int A[4]; LCopyCtor(); LCopyCtor(LCopyCtor const &); };
15+
int Value, Index, Cond;
16+
Inner InnerA, InnerB;
17+
Large L;
18+
19+
void zeroInit() { int Z[3] = {0, 0, 0}; }
20+
// CHECK-LABEL: define dso_local void @_Z8zeroInitv
21+
// CHECK: %Z = alloca [3 x i32], align 4, !DIAssignID ![[ID_0:[0-9]+]]
22+
// CHECK-NEXT: call void @llvm.dbg.assign(metadata i1 undef, metadata ![[VAR_0:[0-9]+]], metadata !DIExpression(), metadata ![[ID_0]], metadata [3 x i32]* %Z, metadata !DIExpression())
23+
// CHECK: @llvm.memset.p0i8.i64{{.*}}, !DIAssignID ![[ID_1:[0-9]+]]
24+
// CHECK-NEXT: call void @llvm.dbg.assign(metadata i8 0, metadata ![[VAR_0]], metadata !DIExpression(), metadata ![[ID_1]], metadata i8* %0, metadata !DIExpression())
25+
26+
void memcpyInit() { int A[4] = {0, 1, 2, 3}; }
27+
// CHECK-LABEL: define dso_local void @_Z10memcpyInitv
28+
// CHECK: %A = alloca [4 x i32], align 16, !DIAssignID ![[ID_2:[0-9]+]]
29+
// CHECK-NEXT: call void @llvm.dbg.assign(metadata i1 undef, metadata ![[VAR_1:[0-9]+]], metadata !DIExpression(), metadata ![[ID_2]], metadata [4 x i32]* %A, metadata !DIExpression())
30+
// CHECK: @llvm.memcpy.p0i8.p0i8.i64{{.*}}, !DIAssignID ![[ID_3:[0-9]+]]
31+
// CHECK-NEXT: call void @llvm.dbg.assign(metadata i1 undef, metadata ![[VAR_1]], metadata !DIExpression(), metadata ![[ID_3]], metadata i8* %0, metadata !DIExpression())
32+
33+
void setField() {
34+
Outer O;
35+
O.A.B = Value;
36+
}
37+
// CHECK-LABEL: define dso_local void @_Z8setFieldv
38+
// CHECK: %O = alloca %struct.Outer, align 4, !DIAssignID ![[ID_4:[0-9]+]]
39+
// CHECK-NEXT: call void @llvm.dbg.assign(metadata i1 undef, metadata ![[VAR_2:[0-9]+]], metadata !DIExpression(), metadata ![[ID_4]], metadata %struct.Outer* %O, metadata !DIExpression())
40+
// CHECK: store i32 %0, i32* %B, align 4,{{.*}}!DIAssignID ![[ID_5:[0-9]+]]
41+
// CHECK-NEXT: call void @llvm.dbg.assign(metadata i32 %0, metadata ![[VAR_2]], metadata !DIExpression(DW_OP_LLVM_fragment, 32, 32), metadata ![[ID_5]], metadata i32* %B, metadata !DIExpression())
42+
43+
void unknownOffset() {
44+
int A[2];
45+
A[Index] = Value;
46+
}
47+
// CHECK-LABEL: define dso_local void @_Z13unknownOffsetv
48+
// CHECK: %A = alloca [2 x i32], align 4, !DIAssignID ![[ID_6:[0-9]+]]
49+
// CHECK-NEXT: call void @llvm.dbg.assign(metadata i1 undef, metadata ![[VAR_3:[0-9]+]], metadata !DIExpression(), metadata ![[ID_6]], metadata [2 x i32]* %A, metadata !DIExpression())
50+
51+
Inner sharedAlloca() {
52+
if (Cond) {
53+
Inner A = InnerA;
54+
return A;
55+
} else {
56+
Inner B = InnerB;
57+
return B;
58+
}
59+
}
60+
// CHECK-LABEL: define dso_local i64 @_Z12sharedAllocav
61+
// CHECK: %retval = alloca %struct.Inner, align 4, !DIAssignID ![[ID_7:[0-9]+]]
62+
// CHECK-NEXT: call void @llvm.dbg.assign(metadata i1 undef, metadata ![[VAR_4:[0-9]+]], metadata !DIExpression(), metadata ![[ID_7]], metadata %struct.Inner* %retval, metadata !DIExpression())
63+
// CHECK-NEXT: call void @llvm.dbg.assign(metadata i1 undef, metadata ![[VAR_5:[0-9]+]], metadata !DIExpression(), metadata ![[ID_7]], metadata %struct.Inner* %retval, metadata !DIExpression())
64+
// CHECK: if.then:
65+
// CHECK: call void @llvm.memcpy{{.*}}, !DIAssignID ![[ID_8:[0-9]+]]
66+
// CHECK-NEXT: call void @llvm.dbg.assign(metadata i1 undef, metadata ![[VAR_4]], metadata !DIExpression(), metadata ![[ID_8]], metadata i8* %1, metadata !DIExpression())
67+
// CHECK-NEXT: call void @llvm.dbg.assign(metadata i1 undef, metadata ![[VAR_5]], metadata !DIExpression(), metadata ![[ID_8]], metadata i8* %1, metadata !DIExpression())
68+
// CHECK: if.else:
69+
// CHECK: call void @llvm.memcpy{{.*}}, !DIAssignID ![[ID_9:[0-9]+]]
70+
// CHECK-NEXT: call void @llvm.dbg.assign(metadata i1 undef, metadata ![[VAR_4]], metadata !DIExpression(), metadata ![[ID_9]], metadata i8* %2, metadata !DIExpression())
71+
// CHECK-NEXT: call void @llvm.dbg.assign(metadata i1 undef, metadata ![[VAR_5]], metadata !DIExpression(), metadata ![[ID_9]], metadata i8* %2, metadata !DIExpression())
72+
73+
Large sret() {
74+
Large X = L;
75+
return X;
76+
}
77+
// CHECK-LABEL: define dso_local void @_Z4sretv
78+
// CHECK: llvm.dbg.declare
79+
80+
void byval(Large X) {}
81+
// CHECK-LABEL: define dso_local void @_Z5byval5Large
82+
// CHECK: llvm.dbg.declare
83+
84+
LCopyCtor indirectReturn() {
85+
LCopyCtor R;
86+
return R;
87+
}
88+
// CHECK-LABEL: define dso_local void @_Z14indirectReturnv
89+
// CHECK: call void @llvm.dbg.declare
90+
91+
// CHECK-DAG: ![[VAR_0]] = !DILocalVariable(name: "Z",
92+
// CHECK-DAG: ![[VAR_1]] = !DILocalVariable(name: "A",
93+
// CHECK-DAG: ![[VAR_2]] = !DILocalVariable(name: "O",
94+
// CHECK-DAG: ![[VAR_3]] = !DILocalVariable(name: "A",
95+
// CHECK-DAG: ![[VAR_4]] = !DILocalVariable(name: "B",
96+
// CHECK-DAG: ![[VAR_5]] = !DILocalVariable(name: "A",
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// RUN: %clang_cc1 -triple x86_64-none-linux-gnu -debug-info-kind=standalone -O0 \
2+
// RUN: -emit-llvm -fexperimental-assignment-tracking %s -o - \
3+
// RUN: | FileCheck %s --check-prefixes=FLAG
4+
// RUN: %clang_cc1 -triple x86_64-none-linux-gnu -debug-info-kind=standalone -O0 \
5+
// RUN: -emit-llvm %s -o - \
6+
// RUN: | FileCheck %s --check-prefixes=NO-FLAG
7+
8+
// Check some assignment-tracking stuff appears in the output when the flag
9+
// -fexperimental-assignment-tracking is used, and that it doesn't when
10+
// the flag is not used (default behaviour: no assignment tracking).
11+
12+
// FLAG: DIAssignID
13+
// FLAG: dbg.assign
14+
15+
// NO-FLAG-NOT: DIAssignID
16+
// NO-FLAG-NOT: dbg.assign
17+
18+
void fun(int a) {}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// RUN: %clang_cc1 -triple x86_64-none-linux-gnu -debug-info-kind=standalone -O0 \
2+
// RUN: -emit-llvm -fexperimental-assignment-tracking %s -o - \
3+
// RUN: | FileCheck %s
4+
5+
// Check that the (debug) codegen looks right with assignment tracking
6+
// enabled. Each fragment that is written to should have a dbg.assign that has
7+
// the DIAssignID of the write as an argument. The fragment offset and size
8+
// should match the offset into the base storage and size of the store. Each of
9+
// the scenarios below results in slightly different arguments generated for
10+
// the memcpy.
11+
12+
// Test write a complete struct field only.
13+
void fragmentWhole()
14+
{
15+
struct Record {
16+
int num;
17+
char ch;
18+
};
19+
20+
Record dest;
21+
char src = '\0';
22+
__builtin_memcpy(&dest.ch, &src, sizeof(char));
23+
}
24+
// CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %ch, i8* align 1 %src, i64 1, i1 false), !dbg !{{[0-9]+}}, !DIAssignID ![[memberID:[0-9]+]]
25+
// CHECK-NEXT: call void @llvm.dbg.assign(metadata{{.*}}undef, metadata !{{[0-9]+}}, metadata !DIExpression(DW_OP_LLVM_fragment, 32, 8), metadata ![[memberID]], metadata i8* %ch, metadata !DIExpression())
26+
27+
// Write starting at a field and overlapping part of another.
28+
void fragmentWholeToPartial()
29+
{
30+
struct Record {
31+
int num1;
32+
int num2;
33+
};
34+
35+
Record dest;
36+
char src[5]="\0\0\0\0";
37+
__builtin_memcpy(&dest.num1, &src, 5);
38+
}
39+
// CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %1, i8* align 1 %2, i64 5, i1 false), !dbg !{{[0-9]+}}, !DIAssignID ![[exceed:[0-9]+]]
40+
// CHECK-NEXT: call void @llvm.dbg.assign(metadata{{.*}}undef, metadata !{{[0-9]+}}, metadata !DIExpression(DW_OP_LLVM_fragment, 0, 40), metadata ![[exceed]], metadata i8* %1, metadata !DIExpression())
41+
42+
// Write starting between fields.
43+
void fragmentPartialToWhole()
44+
{
45+
struct record {
46+
int num1;
47+
int num2;
48+
int num3;
49+
};
50+
51+
record dest;
52+
char src[5]="\0\0\0\0";
53+
__builtin_memcpy((char*)&(dest.num2) + 3, &src, 5);
54+
}
55+
// CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 1 %add.ptr, i8* align 1 %2, i64 5, i1 false), !dbg !{{[0-9]+}}, !DIAssignID ![[addendID:[0-9]+]]
56+
// CHECK-NEXT: call void @llvm.dbg.assign(metadata{{.*}}undef, metadata !{{.*}}, metadata !DIExpression(DW_OP_LLVM_fragment, 56, 40), metadata ![[addendID]], metadata i8* %add.ptr, metadata !DIExpression())
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// RUN: %clang_cc1 -triple x86_64-none-linux-gnu -debug-info-kind=standalone -O0 \
2+
// RUN: -emit-llvm -fexperimental-assignment-tracking %s -o - \
3+
// RUN: | FileCheck %s
4+
5+
// Check that dbg.assign intrinsics get a !dbg with with the same scope as
6+
// their variable.
7+
8+
// CHECK: call void @llvm.dbg.assign({{.+}}, metadata [[local:![0-9]+]], {{.+}}, {{.+}}, {{.+}}), !dbg [[dbg:![0-9]+]]
9+
// CHECK-DAG: [[local]] = !DILocalVariable(name: "local", scope: [[scope:![0-9]+]],
10+
// CHECK-DAG: [[dbg]] = !DILocation({{.+}}, scope: [[scope]])
11+
// CHECK-DAG: [[scope]] = distinct !DILexicalBlock
12+
13+
void ext(int*);
14+
void fun() {
15+
{
16+
int local;
17+
}
18+
}
19+
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// RUN: %clang -### -S %s -g -target x86_64-linux-gnu 2>&1 | FileCheck --check-prefix=CHECK-NO-AT %s
2+
// RUN: %clang -### -S %s -g -target x86_64-linux-gnu 2>&1 \
3+
// RUN: -Xclang -fexperimental-assignment-tracking \
4+
// RUN: | FileCheck --check-prefix=CHECK-AT %s
5+
6+
// CHECK-NO-AT-NOT: "-mllvm" "-experimental-assignment-tracking"
7+
// CHECK-NO-AT-NOT: "-fexperimental-assignment-tracking"
8+
9+
// CHECK-AT: "-mllvm" "-experimental-assignment-tracking"
10+
// CHECK-AT: "-fexperimental-assignment-tracking"

0 commit comments

Comments
 (0)