Skip to content

Commit eaea27c

Browse files
[DebugInfo] Handle DW_OP_LLVM_extract_bits in SROA
Changes to make SROA handle DW_OP_LLVM_extract_bits TODO: Better commit message TODO: Handling of sign mismatch in getActiveBits maybe not right TODO: Maybe I can do something about the FIXMEs TODO: Adjusting getFragmentSizeInBits probably wrong, instead use getActiveBits in valueCoversEntireFragment in Transforms/Utils/Local.cpp
1 parent 342011f commit eaea27c

File tree

5 files changed

+209
-9
lines changed

5 files changed

+209
-9
lines changed

llvm/include/llvm/IR/DebugInfoMetadata.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2903,6 +2903,12 @@ class DIExpression : public MDNode {
29032903
}
29042904
};
29052905

2906+
/// Return the number of bits that have an active value, i.e. those that
2907+
/// aren't known to be zero/sign (depending on the type of Var) and which
2908+
/// are within the size of this fragment (if it is one). If we can't deduce
2909+
/// anything from the expression this will return the size of Var.
2910+
std::optional<uint64_t> getActiveBits(DIVariable *Var);
2911+
29062912
/// Retrieve the details of this fragment expression.
29072913
static std::optional<FragmentInfo> getFragmentInfo(expr_op_iterator Start,
29082914
expr_op_iterator End);

llvm/lib/IR/DebugInfoMetadata.cpp

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1679,6 +1679,26 @@ DIExpression::getFragmentInfo(expr_op_iterator Start, expr_op_iterator End) {
16791679
return std::nullopt;
16801680
}
16811681

1682+
std::optional<uint64_t> DIExpression::getActiveBits(DIVariable *Var) {
1683+
std::optional<uint64_t> BitWidth = Var->getSizeInBits();
1684+
for (auto Op : expr_ops()) {
1685+
if ((Op.getOp() == dwarf::DW_OP_LLVM_extract_bits_zext && Var->getSignedness() != DIBasicType::Signedness::Unsigned) ||
1686+
(Op.getOp() == dwarf::DW_OP_LLVM_extract_bits_sext && Var->getSignedness() != DIBasicType::Signedness::Signed)) {
1687+
BitWidth = Var->getSizeInBits();
1688+
continue;
1689+
}
1690+
if (Op.getOp() == dwarf::DW_OP_LLVM_extract_bits_zext ||
1691+
Op.getOp() == dwarf::DW_OP_LLVM_extract_bits_sext ||
1692+
Op.getOp() == dwarf::DW_OP_LLVM_fragment) {
1693+
if (BitWidth)
1694+
BitWidth = std::min(*BitWidth, Op.getArg(1));
1695+
else
1696+
BitWidth = Op.getArg(1);
1697+
}
1698+
}
1699+
return BitWidth;
1700+
}
1701+
16821702
void DIExpression::appendOffset(SmallVectorImpl<uint64_t> &Ops,
16831703
int64_t Offset) {
16841704
if (Offset > 0) {
@@ -1931,6 +1951,8 @@ std::optional<DIExpression *> DIExpression::createFragmentExpression(
19311951
// Track whether it's safe to split the value at the top of the DWARF stack,
19321952
// assuming that it'll be used as an implicit location value.
19331953
bool CanSplitValue = true;
1954+
// Track whether we need to add a fragment expression to the end of Expr.
1955+
bool EmitFragment = true;
19341956
// Copy over the expression, but leave off any trailing DW_OP_LLVM_fragment.
19351957
if (Expr) {
19361958
for (auto Op : Expr->expr_ops()) {
@@ -1966,6 +1988,11 @@ std::optional<DIExpression *> DIExpression::createFragmentExpression(
19661988
return std::nullopt;
19671989
break;
19681990
case dwarf::DW_OP_LLVM_fragment: {
1991+
// If we've decided we don't need a fragment then give up if we see that
1992+
// there's already a fragment expression.
1993+
// FIXME: We could probably do better here
1994+
if (!EmitFragment)
1995+
return std::nullopt;
19691996
// Make the new offset point into the existing fragment.
19701997
uint64_t FragmentOffsetInBits = Op.getArg(0);
19711998
uint64_t FragmentSizeInBits = Op.getArg(1);
@@ -1975,15 +2002,37 @@ std::optional<DIExpression *> DIExpression::createFragmentExpression(
19752002
OffsetInBits += FragmentOffsetInBits;
19762003
continue;
19772004
}
2005+
case dwarf::DW_OP_LLVM_extract_bits_zext:
2006+
case dwarf::DW_OP_LLVM_extract_bits_sext: {
2007+
// If we're extracting bits from inside of the fragment that we're
2008+
// creating then we don't have a fragment after all, and just need to
2009+
// adjust the offset that we're extracting from.
2010+
uint64_t ExtractOffsetInBits = Op.getArg(0);
2011+
uint64_t ExtractSizeInBits = Op.getArg(1);
2012+
if (ExtractOffsetInBits >= OffsetInBits &&
2013+
ExtractOffsetInBits + ExtractSizeInBits <= OffsetInBits + SizeInBits) {
2014+
Ops.push_back(Op.getOp());
2015+
Ops.push_back(ExtractOffsetInBits - OffsetInBits);
2016+
Ops.push_back(ExtractSizeInBits);
2017+
EmitFragment = false;
2018+
continue;
2019+
}
2020+
// If the extracted bits aren't fully contained within the fragment then
2021+
// give up.
2022+
// FIXME: We could probably do better here
2023+
return std::nullopt;
2024+
}
19782025
}
19792026
Op.appendToVector(Ops);
19802027
}
19812028
}
19822029
assert((!Expr->isImplicit() || CanSplitValue) && "Expr can't be split");
19832030
assert(Expr && "Unknown DIExpression");
1984-
Ops.push_back(dwarf::DW_OP_LLVM_fragment);
1985-
Ops.push_back(OffsetInBits);
1986-
Ops.push_back(SizeInBits);
2031+
if (EmitFragment) {
2032+
Ops.push_back(dwarf::DW_OP_LLVM_fragment);
2033+
Ops.push_back(OffsetInBits);
2034+
Ops.push_back(SizeInBits);
2035+
}
19872036
return DIExpression::get(Expr->getContext(), Ops);
19882037
}
19892038

llvm/lib/IR/DebugProgramInstruction.cpp

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -372,9 +372,7 @@ bool DbgVariableRecord::isKillLocation() const {
372372
}
373373

374374
std::optional<uint64_t> DbgVariableRecord::getFragmentSizeInBits() const {
375-
if (auto Fragment = getExpression()->getFragmentInfo())
376-
return Fragment->SizeInBits;
377-
return getVariable()->getSizeInBits();
375+
return getExpression()->getActiveBits(getVariable());
378376
}
379377

380378
DbgRecord *DbgRecord::clone() const {

llvm/lib/IR/IntrinsicInst.cpp

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -196,9 +196,7 @@ void DbgVariableIntrinsic::addVariableLocationOps(ArrayRef<Value *> NewValues,
196196
}
197197

198198
std::optional<uint64_t> DbgVariableIntrinsic::getFragmentSizeInBits() const {
199-
if (auto Fragment = getExpression()->getFragmentInfo())
200-
return Fragment->SizeInBits;
201-
return getVariable()->getSizeInBits();
199+
return getExpression()->getActiveBits(getVariable());
202200
}
203201

204202
Value *DbgAssignIntrinsic::getAddress() const {
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 4
2+
; RUN: opt -passes='sroa<preserve-cfg>' %s -S | FileCheck %s
3+
; RUN: opt -passes='sroa<modify-cfg>' %s -S | FileCheck %s
4+
5+
declare void @llvm.dbg.declare(metadata, metadata, metadata) #0
6+
7+
; The alloca is split into two fragments: variable x is in the first, variables y and z are in the second
8+
define i8 @test1(i32 %arg) {
9+
; CHECK-LABEL: define i8 @test1(
10+
; CHECK-SAME: i32 [[ARG:%.*]]) {
11+
; CHECK-NEXT: entry:
12+
; CHECK-NEXT: [[PTR_SROA_0_0_EXTRACT_TRUNC:%.*]] = trunc i32 [[ARG]] to i8
13+
; CHECK-NEXT: tail call void @llvm.dbg.value(metadata i8 [[PTR_SROA_0_0_EXTRACT_TRUNC]], metadata [[META2:![0-9]+]], metadata !DIExpression(DW_OP_LLVM_extract_bits_unsigned, 0, 8)), !dbg [[DBG7:![0-9]+]]
14+
; CHECK-NEXT: [[PTR_SROA_2_0_EXTRACT_SHIFT:%.*]] = lshr i32 [[ARG]], 8
15+
; CHECK-NEXT: [[PTR_SROA_2_0_EXTRACT_TRUNC:%.*]] = trunc i32 [[PTR_SROA_2_0_EXTRACT_SHIFT]] to i24
16+
; CHECK-NEXT: tail call void @llvm.dbg.value(metadata i24 [[PTR_SROA_2_0_EXTRACT_TRUNC]], metadata [[META8:![0-9]+]], metadata !DIExpression(DW_OP_LLVM_extract_bits_unsigned, 8, 16)), !dbg [[DBG7]]
17+
; CHECK-NEXT: tail call void @llvm.dbg.value(metadata i24 [[PTR_SROA_2_0_EXTRACT_TRUNC]], metadata [[META9:![0-9]+]], metadata !DIExpression(DW_OP_LLVM_extract_bits_unsigned, 0, 8)), !dbg [[DBG7]]
18+
; CHECK-NEXT: ret i8 [[PTR_SROA_0_0_EXTRACT_TRUNC]]
19+
;
20+
entry:
21+
%ptr = alloca i32, align 4
22+
call void @llvm.dbg.declare(metadata ptr %ptr, metadata !2, metadata !DIExpression(DW_OP_LLVM_extract_bits_unsigned, 0, 8)), !dbg !7
23+
call void @llvm.dbg.declare(metadata ptr %ptr, metadata !9, metadata !DIExpression(DW_OP_LLVM_extract_bits_unsigned, 8, 8)), !dbg !7
24+
call void @llvm.dbg.declare(metadata ptr %ptr, metadata !8, metadata !DIExpression(DW_OP_LLVM_extract_bits_unsigned, 16, 16)), !dbg !7
25+
store i32 %arg, ptr %ptr, align 4
26+
%ret = load i8, ptr %ptr, align 4
27+
ret i8 %ret
28+
}
29+
30+
; The alloca is split into three fragments corresponding to the variables x, y, z
31+
define i8 @test2(i32 %arg1, i8 %arg2) {
32+
; CHECK-LABEL: define i8 @test2(
33+
; CHECK-SAME: i32 [[ARG1:%.*]], i8 [[ARG2:%.*]]) {
34+
; CHECK-NEXT: entry:
35+
; CHECK-NEXT: [[PTR_SROA_0_0_EXTRACT_TRUNC:%.*]] = trunc i32 [[ARG1]] to i8
36+
; CHECK-NEXT: tail call void @llvm.dbg.value(metadata i8 [[PTR_SROA_0_0_EXTRACT_TRUNC]], metadata [[META2]], metadata !DIExpression(DW_OP_LLVM_extract_bits_unsigned, 0, 8)), !dbg [[DBG7]]
37+
; CHECK-NEXT: [[PTR_SROA_2_0_EXTRACT_SHIFT:%.*]] = lshr i32 [[ARG1]], 8
38+
; CHECK-NEXT: [[PTR_SROA_2_0_EXTRACT_TRUNC:%.*]] = trunc i32 [[PTR_SROA_2_0_EXTRACT_SHIFT]] to i16
39+
; CHECK-NEXT: tail call void @llvm.dbg.value(metadata i16 [[PTR_SROA_2_0_EXTRACT_TRUNC]], metadata [[META9]], metadata !DIExpression(DW_OP_LLVM_extract_bits_unsigned, 0, 16)), !dbg [[DBG7]]
40+
; CHECK-NEXT: [[PTR_SROA_21_0_EXTRACT_SHIFT:%.*]] = lshr i32 [[ARG1]], 24
41+
; CHECK-NEXT: [[PTR_SROA_21_0_EXTRACT_TRUNC:%.*]] = trunc i32 [[PTR_SROA_21_0_EXTRACT_SHIFT]] to i8
42+
; CHECK-NEXT: tail call void @llvm.dbg.value(metadata i8 [[PTR_SROA_21_0_EXTRACT_TRUNC]], metadata [[META8]], metadata !DIExpression(DW_OP_LLVM_extract_bits_unsigned, 0, 8)), !dbg [[DBG7]]
43+
; CHECK-NEXT: tail call void @llvm.dbg.value(metadata i8 [[ARG2]], metadata [[META8]], metadata !DIExpression(DW_OP_LLVM_extract_bits_unsigned, 0, 8)), !dbg [[DBG7]]
44+
; CHECK-NEXT: ret i8 [[PTR_SROA_0_0_EXTRACT_TRUNC]]
45+
;
46+
entry:
47+
%ptr = alloca i32, align 4
48+
call void @llvm.dbg.declare(metadata ptr %ptr, metadata !2, metadata !DIExpression(DW_OP_LLVM_extract_bits_unsigned, 0, 8)), !dbg !7
49+
call void @llvm.dbg.declare(metadata ptr %ptr, metadata !9, metadata !DIExpression(DW_OP_LLVM_extract_bits_unsigned, 8, 16)), !dbg !7
50+
call void @llvm.dbg.declare(metadata ptr %ptr, metadata !8, metadata !DIExpression(DW_OP_LLVM_extract_bits_unsigned, 24, 8)), !dbg !7
51+
store i32 %arg1, ptr %ptr, align 4
52+
%gep = getelementptr i8, ptr %ptr, i32 3
53+
store i8 %arg2, ptr %gep, align 1
54+
%ret = load i8, ptr %ptr, align 4
55+
ret i8 %ret
56+
}
57+
58+
; The alloca is split into two fragments, with variable x being half in one and half in the other
59+
; FIXME: We currently generate no debug info for x in this case
60+
define i8 @test3(i32 %arg) {
61+
; CHECK-LABEL: define i8 @test3(
62+
; CHECK-SAME: i32 [[ARG:%.*]]) {
63+
; CHECK-NEXT: entry:
64+
; CHECK-NEXT: [[PTR_SROA_0_0_EXTRACT_TRUNC:%.*]] = trunc i32 [[ARG]] to i8
65+
; CHECK-NEXT: [[PTR_SROA_2_0_EXTRACT_SHIFT:%.*]] = lshr i32 [[ARG]], 8
66+
; CHECK-NEXT: [[PTR_SROA_2_0_EXTRACT_TRUNC:%.*]] = trunc i32 [[PTR_SROA_2_0_EXTRACT_SHIFT]] to i24
67+
; CHECK-NEXT: ret i8 [[PTR_SROA_0_0_EXTRACT_TRUNC]]
68+
;
69+
entry:
70+
%ptr = alloca i32, align 4
71+
call void @llvm.dbg.declare(metadata ptr %ptr, metadata !2, metadata !DIExpression(DW_OP_LLVM_extract_bits_unsigned, 0, 16)), !dbg !7
72+
store i32 %arg, ptr %ptr, align 4
73+
%ret = load i8, ptr %ptr, align 4
74+
ret i8 %ret
75+
}
76+
77+
; The alloca is split into two fragments, with variable y being half in one and half in the other
78+
; FIXME: We currently generate no debug info for y in this case
79+
define i16 @test4(i32 %arg) {
80+
; CHECK-LABEL: define i16 @test4(
81+
; CHECK-SAME: i32 [[ARG:%.*]]) {
82+
; CHECK-NEXT: entry:
83+
; CHECK-NEXT: [[PTR_SROA_0_0_EXTRACT_TRUNC:%.*]] = trunc i32 [[ARG]] to i16
84+
; CHECK-NEXT: tail call void @llvm.dbg.value(metadata i16 [[PTR_SROA_0_0_EXTRACT_TRUNC]], metadata [[META2]], metadata !DIExpression(DW_OP_LLVM_extract_bits_unsigned, 0, 8)), !dbg [[DBG7]]
85+
; CHECK-NEXT: [[PTR_SROA_2_0_EXTRACT_SHIFT:%.*]] = lshr i32 [[ARG]], 16
86+
; CHECK-NEXT: [[PTR_SROA_2_0_EXTRACT_TRUNC:%.*]] = trunc i32 [[PTR_SROA_2_0_EXTRACT_SHIFT]] to i16
87+
; CHECK-NEXT: tail call void @llvm.dbg.value(metadata i16 [[PTR_SROA_2_0_EXTRACT_TRUNC]], metadata [[META8]], metadata !DIExpression(DW_OP_LLVM_extract_bits_unsigned, 8, 8)), !dbg [[DBG7]]
88+
; CHECK-NEXT: ret i16 [[PTR_SROA_0_0_EXTRACT_TRUNC]]
89+
;
90+
entry:
91+
%ptr = alloca i32, align 4
92+
call void @llvm.dbg.declare(metadata ptr %ptr, metadata !2, metadata !DIExpression(DW_OP_LLVM_extract_bits_unsigned, 0, 8)), !dbg !7
93+
call void @llvm.dbg.declare(metadata ptr %ptr, metadata !9, metadata !DIExpression(DW_OP_LLVM_extract_bits_unsigned, 8, 16)), !dbg !7
94+
call void @llvm.dbg.declare(metadata ptr %ptr, metadata !8, metadata !DIExpression(DW_OP_LLVM_extract_bits_unsigned, 24, 8)), !dbg !7
95+
store i32 %arg, ptr %ptr, align 4
96+
%ret = load i16, ptr %ptr, align 4
97+
ret i16 %ret
98+
}
99+
100+
; Struct where the first element is an ordinary char, the second is a bitfield of two elements, and the third is padding
101+
%struct.struct_t = type <{ i8, i16, i8 }>
102+
define i8 @test5(i32 %arg) {
103+
; CHECK-LABEL: define i8 @test5(
104+
; CHECK-SAME: i32 [[ARG:%.*]]) {
105+
; CHECK-NEXT: entry:
106+
; CHECK-NEXT: [[PTR_SROA_0_0_EXTRACT_TRUNC:%.*]] = trunc i32 [[ARG]] to i8
107+
; CHECK-NEXT: tail call void @llvm.dbg.value(metadata i8 [[PTR_SROA_0_0_EXTRACT_TRUNC]], metadata [[META10:![0-9]+]], metadata !DIExpression()), !dbg [[DBG7]]
108+
; CHECK-NEXT: [[PTR_SROA_2_0_EXTRACT_SHIFT:%.*]] = lshr i32 [[ARG]], 8
109+
; CHECK-NEXT: [[PTR_SROA_2_0_EXTRACT_TRUNC:%.*]] = trunc i32 [[PTR_SROA_2_0_EXTRACT_SHIFT]] to i24
110+
; CHECK-NEXT: tail call void @llvm.dbg.value(metadata i24 [[PTR_SROA_2_0_EXTRACT_TRUNC]], metadata [[META8]], metadata !DIExpression(DW_OP_LLVM_extract_bits_unsigned, 8, 8)), !dbg [[DBG7]]
111+
; CHECK-NEXT: tail call void @llvm.dbg.value(metadata i24 [[PTR_SROA_2_0_EXTRACT_TRUNC]], metadata [[META9]], metadata !DIExpression(DW_OP_LLVM_extract_bits_unsigned, 0, 8)), !dbg [[DBG7]]
112+
; CHECK-NEXT: ret i8 [[PTR_SROA_0_0_EXTRACT_TRUNC]]
113+
;
114+
entry:
115+
%ptr = alloca %struct.struct_t, align 4
116+
call void @llvm.dbg.declare(metadata ptr %ptr, metadata !10, metadata !DIExpression()), !dbg !7
117+
call void @llvm.dbg.declare(metadata ptr %ptr, metadata !9, metadata !DIExpression(DW_OP_LLVM_extract_bits_unsigned, 8, 8)), !dbg !7
118+
call void @llvm.dbg.declare(metadata ptr %ptr, metadata !8, metadata !DIExpression(DW_OP_LLVM_extract_bits_unsigned, 16, 8)), !dbg !7
119+
store i32 %arg, ptr %ptr, align 4
120+
%ret = load i8, ptr %ptr, align 4
121+
ret i8 %ret
122+
}
123+
124+
!llvm.module.flags = !{!0, !1}
125+
!0 = !{i32 7, !"Dwarf Version", i32 5}
126+
!1 = !{i32 2, !"Debug Info Version", i32 3}
127+
!2 = !DILocalVariable(name: "x", scope: !3, type: !6)
128+
!3 = distinct !DISubprogram(name: "test", unit: !4)
129+
!4 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !5, emissionKind: FullDebug)
130+
!5 = !DIFile(filename: "dbg-bit-piece.cpp", directory: "")
131+
!6 = !DIBasicType(name: "unsigned int", size: 32, encoding: DW_ATE_unsigned)
132+
!7 = !DILocation(line: 0, column: 0, scope: !3)
133+
!8 = !DILocalVariable(name: "z", scope: !3, type: !6)
134+
!9 = !DILocalVariable(name: "y", scope: !3, type: !6)
135+
!10 = !DILocalVariable(name: "x", scope: !3, type: !11)
136+
!11 = !DIBasicType(name: "char", size: 8, encoding: DW_ATE_signed_char)
137+
138+
;.
139+
; CHECK: [[META2]] = !DILocalVariable(name: "x", scope: [[META3:![0-9]+]], type: [[META6:![0-9]+]])
140+
; CHECK: [[META3]] = distinct !DISubprogram(name: "test", scope: null, spFlags: DISPFlagDefinition, unit: [[META4:![0-9]+]])
141+
; CHECK: [[META4]] = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: [[META5:![0-9]+]], isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug)
142+
; CHECK: [[META5]] = !DIFile(filename: "dbg-bit-piece.cpp", directory: "")
143+
; CHECK: [[META6]] = !DIBasicType(name: "unsigned int", size: 32, encoding: DW_ATE_unsigned)
144+
; CHECK: [[DBG7]] = !DILocation(line: 0, scope: [[META3]])
145+
; CHECK: [[META8]] = !DILocalVariable(name: "z", scope: [[META3]], type: [[META6]])
146+
; CHECK: [[META9]] = !DILocalVariable(name: "y", scope: [[META3]], type: [[META6]])
147+
; CHECK: [[META10]] = !DILocalVariable(name: "x", scope: [[META3]], type: [[META11:![0-9]+]])
148+
; CHECK: [[META11]] = !DIBasicType(name: "char", size: 8, encoding: DW_ATE_signed_char)
149+
;.

0 commit comments

Comments
 (0)