Skip to content

Commit 20ddcb4

Browse files
pogo59adrian-prantl
authored andcommitted
Add a DIExpression const-folder to prevent silly expressions.
It's entirely possible (because it actually happened) for a bool variable to end up with a 256-bit DW_AT_const_value. This came about when a local bool variable was initialized from a bitfield in a 32-byte struct of bitfields, and after inlining and constant propagation, the variable did have a constant value. The sequence of optimizations had it carrying "i256" values around, but once the constant made it into the llvm.dbg.value, no further IR changes could affect it. Technically the llvm.dbg.value did have a DIExpression to reduce it back down to 8 bits, but the compiler is in no way ready to emit an oversized constant *and* a DWARF expression to manipulate it. Depending on the circumstances, we had either just the very fat bool value, or an expression with no starting value. The sequence of optimizations that led to this state did seem pretty reasonable, so the solution I came up with was to invent a DWARF constant expression folder. Currently it only does convert ops, but there's no reason it couldn't do other ops if that became useful. This broke three tests that depended on having convert ops survive into the DWARF, so I added an operator that would abort the folder to each of those tests. Differential Revision: https://reviews.llvm.org/D106915 (cherry picked from commit 75aa3d5)
1 parent 0d7d612 commit 20ddcb4

File tree

9 files changed

+230
-12
lines changed

9 files changed

+230
-12
lines changed

llvm/include/llvm/IR/DebugInfoMetadata.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2932,6 +2932,12 @@ class DIExpression : public MDNode {
29322932
return getNumElements() > 0 &&
29332933
getElement(0) == dwarf::DW_OP_LLVM_entry_value;
29342934
}
2935+
2936+
/// Try to shorten an expression with an initial constant operand.
2937+
/// Returns a new expression and constant on success, or the original
2938+
/// expression and constant on failure.
2939+
std::pair<DIExpression *, const ConstantInt *>
2940+
constantFold(const ConstantInt *CI);
29352941
};
29362942

29372943
inline bool operator==(const DIExpression::FragmentInfo &A,

llvm/lib/CodeGen/SelectionDAG/FastISel.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1292,18 +1292,22 @@ bool FastISel::selectIntrinsicCall(const IntrinsicInst *II) {
12921292
BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, II, false, 0U,
12931293
DI->getVariable(), DI->getExpression());
12941294
} else if (const auto *CI = dyn_cast<ConstantInt>(V)) {
1295+
// See if there's an expression to constant-fold.
1296+
DIExpression *Expr = DI->getExpression();
1297+
if (Expr)
1298+
std::tie(Expr, CI) = Expr->constantFold(CI);
12951299
if (CI->getBitWidth() > 64)
12961300
BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, II)
12971301
.addCImm(CI)
12981302
.addImm(0U)
12991303
.addMetadata(DI->getVariable())
1300-
.addMetadata(DI->getExpression());
1304+
.addMetadata(Expr);
13011305
else
13021306
BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, II)
13031307
.addImm(CI->getZExtValue())
13041308
.addImm(0U)
13051309
.addMetadata(DI->getVariable())
1306-
.addMetadata(DI->getExpression());
1310+
.addMetadata(Expr);
13071311
} else if (const auto *CF = dyn_cast<ConstantFP>(V)) {
13081312
BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, II)
13091313
.addFPImm(CF)

llvm/lib/CodeGen/SelectionDAG/InstrEmitter.cpp

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -872,17 +872,28 @@ MachineInstr *
872872
InstrEmitter::EmitDbgValueFromSingleOp(SDDbgValue *SD,
873873
DenseMap<SDValue, Register> &VRBaseMap) {
874874
MDNode *Var = SD->getVariable();
875-
MDNode *Expr = SD->getExpression();
875+
DIExpression *Expr = SD->getExpression();
876876
DebugLoc DL = SD->getDebugLoc();
877877
const MCInstrDesc &II = TII->get(TargetOpcode::DBG_VALUE);
878878

879879
assert(SD->getLocationOps().size() == 1 &&
880880
"Non variadic dbg_value should have only one location op");
881881

882+
// See about constant-folding the expression.
883+
// Copy the location operand in case we replace it.
884+
SmallVector<SDDbgOperand, 1> LocationOps(1, SD->getLocationOps()[0]);
885+
if (Expr && LocationOps[0].getKind() == SDDbgOperand::CONST) {
886+
const Value *V = LocationOps[0].getConst();
887+
if (auto *C = dyn_cast<ConstantInt>(V)) {
888+
std::tie(Expr, C) = Expr->constantFold(C);
889+
LocationOps[0] = SDDbgOperand::fromConst(C);
890+
}
891+
}
892+
882893
// Emit non-variadic dbg_value nodes as DBG_VALUE.
883894
// DBG_VALUE := "DBG_VALUE" loc, isIndirect, var, expr
884895
auto MIB = BuildMI(*MF, DL, II);
885-
AddDbgValueLocationOps(MIB, II, SD->getLocationOps(), VRBaseMap);
896+
AddDbgValueLocationOps(MIB, II, LocationOps, VRBaseMap);
886897

887898
if (SD->isIndirect())
888899
MIB.addImm(0U);

llvm/lib/IR/DebugInfoMetadata.cpp

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1478,6 +1478,45 @@ Optional<DIExpression *> DIExpression::createFragmentExpression(
14781478
return DIExpression::get(Expr->getContext(), Ops);
14791479
}
14801480

1481+
std::pair<DIExpression *, const ConstantInt *>
1482+
DIExpression::constantFold(const ConstantInt *CI) {
1483+
// Copy the APInt so we can modify it.
1484+
APInt NewInt = CI->getValue();
1485+
SmallVector<uint64_t, 8> Ops;
1486+
1487+
// Fold operators only at the beginning of the expression.
1488+
bool First = true;
1489+
bool Changed = false;
1490+
for (auto Op : expr_ops()) {
1491+
switch (Op.getOp()) {
1492+
default:
1493+
// We fold only the leading part of the expression; if we get to a part
1494+
// that we're going to copy unchanged, and haven't done any folding,
1495+
// then the entire expression is unchanged and we can return early.
1496+
if (!Changed)
1497+
return {this, CI};
1498+
First = false;
1499+
break;
1500+
case dwarf::DW_OP_LLVM_convert:
1501+
if (!First)
1502+
break;
1503+
Changed = true;
1504+
if (Op.getArg(1) == dwarf::DW_ATE_signed)
1505+
NewInt = NewInt.sextOrTrunc(Op.getArg(0));
1506+
else {
1507+
assert(Op.getArg(1) == dwarf::DW_ATE_unsigned && "Unexpected operand");
1508+
NewInt = NewInt.zextOrTrunc(Op.getArg(0));
1509+
}
1510+
continue;
1511+
}
1512+
Op.appendToVector(Ops);
1513+
}
1514+
if (!Changed)
1515+
return {this, CI};
1516+
return {DIExpression::get(getContext(), Ops),
1517+
ConstantInt::get(getContext(), NewInt)};
1518+
}
1519+
14811520
uint64_t DIExpression::getNumLocationOperands() const {
14821521
uint64_t Result = 0;
14831522
for (auto ExprOp : expr_ops())
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
; RUN: llc -mtriple=x86_64 -filetype=obj < %s \
2+
; RUN: | llvm-dwarfdump -debug-info - | FileCheck %s
3+
; RUN: llc -mtriple=x86_64 -filetype=obj -fast-isel < %s \
4+
; RUN: | llvm-dwarfdump -debug-info - | FileCheck %s
5+
6+
;; The important thing is the DW_OP_lit2 with no converts.
7+
;; TODO: Make this work with global isel
8+
;; Indirectly related FIXME: Should be able to emit DW_AT_const_value instead.
9+
10+
; CHECK: DW_TAG_variable
11+
; CHECK-NEXT: DW_AT_location (DW_OP_lit2, DW_OP_stack_value)
12+
; CHECK-NEXT: DW_AT_name ("bIsShowingCollision")
13+
14+
%class.UClient = type { %class.UWorld*, %struct.FFlags }
15+
%class.UWorld = type { i16 }
16+
%struct.FFlags = type { [9 x i8], i32 }
17+
18+
define dso_local void @_ZN7UClient13ToggleVolumesEv(%class.UClient* nocapture nonnull align 8 dereferenceable(24) %this) local_unnamed_addr align 2 !dbg !8 {
19+
entry:
20+
call void @llvm.dbg.value(metadata i72 2, metadata !43, metadata !DIExpression(DW_OP_LLVM_convert, 72, DW_ATE_unsigned, DW_OP_LLVM_convert, 8, DW_ATE_unsigned, DW_OP_stack_value)), !dbg !48
21+
%World = getelementptr inbounds %class.UClient, %class.UClient* %this, i64 0, i32 0, !dbg !49
22+
%0 = load %class.UWorld*, %class.UWorld** %World, align 8, !dbg !49, !tbaa !51
23+
%1 = getelementptr %class.UWorld, %class.UWorld* %0, i64 0, i32 0, !dbg !58
24+
store i16 2, i16* %1, align 1, !dbg !59
25+
ret void, !dbg !60
26+
}
27+
28+
; Function Attrs: nofree nosync nounwind readnone speculatable willreturn
29+
declare void @llvm.dbg.value(metadata, metadata, metadata)
30+
31+
!llvm.dbg.cu = !{!0}
32+
!llvm.module.flags = !{!3, !4, !5, !6}
33+
!llvm.ident = !{!7}
34+
35+
!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version 13.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, splitDebugInlining: false, nameTableKind: None)
36+
!1 = !DIFile(filename: "min-test-3.cpp", directory: "/home/probinson/projects/scratch/tc8251")
37+
!2 = !{}
38+
!3 = !{i32 7, !"Dwarf Version", i32 4}
39+
!4 = !{i32 2, !"Debug Info Version", i32 3}
40+
!5 = !{i32 1, !"wchar_size", i32 4}
41+
!6 = !{i32 7, !"uwtable", i32 1}
42+
!7 = !{!"clang version 13.0.0"}
43+
!8 = distinct !DISubprogram(name: "ToggleVolumes", linkageName: "_ZN7UClient13ToggleVolumesEv", scope: !9, file: !1, line: 39, type: !37, scopeLine: 40, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, declaration: !36, retainedNodes: !40)
44+
!9 = distinct !DICompositeType(tag: DW_TAG_class_type, name: "UClient", file: !1, line: 31, size: 192, flags: DIFlagTypePassByValue | DIFlagNonTrivial, elements: !10, identifier: "_ZTS7UClient")
45+
!10 = !{!11, !20, !36}
46+
!11 = !DIDerivedType(tag: DW_TAG_member, name: "World", scope: !9, file: !1, line: 34, baseType: !12, size: 64, flags: DIFlagPublic)
47+
!12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64)
48+
!13 = distinct !DICompositeType(tag: DW_TAG_class_type, name: "UWorld", file: !1, line: 8, size: 16, flags: DIFlagTypePassByValue, elements: !14, identifier: "_ZTS6UWorld")
49+
!14 = !{!15, !18, !19}
50+
!15 = !DIDerivedType(tag: DW_TAG_member, name: "bCollision", scope: !13, file: !1, line: 11, baseType: !16, size: 1, flags: DIFlagPublic | DIFlagBitField, extraData: i64 0)
51+
!16 = !DIDerivedType(tag: DW_TAG_typedef, name: "uint8", file: !1, line: 1, baseType: !17)
52+
!17 = !DIBasicType(name: "unsigned char", size: 8, encoding: DW_ATE_unsigned_char)
53+
!18 = !DIDerivedType(tag: DW_TAG_member, name: "dummyA", scope: !13, file: !1, line: 12, baseType: !16, size: 7, offset: 1, flags: DIFlagPublic | DIFlagBitField, extraData: i64 0)
54+
!19 = !DIDerivedType(tag: DW_TAG_member, name: "dummyB", scope: !13, file: !1, line: 13, baseType: !16, size: 1, offset: 8, flags: DIFlagPublic | DIFlagBitField, extraData: i64 0)
55+
!20 = !DIDerivedType(tag: DW_TAG_member, name: "EngineShowFlags", scope: !9, file: !1, line: 35, baseType: !21, size: 128, offset: 64, flags: DIFlagPublic)
56+
!21 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "FFlags", file: !1, line: 16, size: 128, flags: DIFlagTypePassByValue | DIFlagNonTrivial, elements: !22, identifier: "_ZTS6FFlags")
57+
!22 = !{!23, !26, !27, !28, !29, !30, !32}
58+
!23 = !DIDerivedType(tag: DW_TAG_member, name: "Volumes", scope: !21, file: !1, line: 18, baseType: !24, size: 1, flags: DIFlagBitField, extraData: i64 0)
59+
!24 = !DIDerivedType(tag: DW_TAG_typedef, name: "uint32", file: !1, line: 2, baseType: !25)
60+
!25 = !DIBasicType(name: "unsigned int", size: 32, encoding: DW_ATE_unsigned)
61+
!26 = !DIDerivedType(tag: DW_TAG_member, name: "Collision", scope: !21, file: !1, line: 19, baseType: !24, size: 1, offset: 1, flags: DIFlagBitField, extraData: i64 0)
62+
!27 = !DIDerivedType(tag: DW_TAG_member, name: "dummy1", scope: !21, file: !1, line: 20, baseType: !24, size: 30, offset: 2, flags: DIFlagBitField, extraData: i64 0)
63+
!28 = !DIDerivedType(tag: DW_TAG_member, name: "dummy2", scope: !21, file: !1, line: 21, baseType: !24, size: 32, offset: 32, flags: DIFlagBitField, extraData: i64 0)
64+
!29 = !DIDerivedType(tag: DW_TAG_member, name: "dummy3", scope: !21, file: !1, line: 22, baseType: !24, size: 1, offset: 64, flags: DIFlagBitField, extraData: i64 0)
65+
!30 = !DIDerivedType(tag: DW_TAG_member, name: "CustomShowFlags", scope: !21, file: !1, line: 24, baseType: !31, size: 32, offset: 96)
66+
!31 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
67+
!32 = !DISubprogram(name: "FFlags", scope: !21, file: !1, line: 25, type: !33, scopeLine: 25, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized)
68+
!33 = !DISubroutineType(types: !34)
69+
!34 = !{null, !35}
70+
!35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !21, size: 64, flags: DIFlagArtificial | DIFlagObjectPointer)
71+
!36 = !DISubprogram(name: "ToggleVolumes", linkageName: "_ZN7UClient13ToggleVolumesEv", scope: !9, file: !1, line: 36, type: !37, scopeLine: 36, flags: DIFlagPublic | DIFlagPrototyped, spFlags: DISPFlagOptimized)
72+
!37 = !DISubroutineType(types: !38)
73+
!38 = !{null, !39}
74+
!39 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !9, size: 64, flags: DIFlagArtificial | DIFlagObjectPointer)
75+
!40 = !{!41, !43}
76+
!41 = !DILocalVariable(name: "this", arg: 1, scope: !8, type: !42, flags: DIFlagArtificial | DIFlagObjectPointer)
77+
!42 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !9, size: 64)
78+
!43 = !DILocalVariable(name: "bIsShowingCollision", scope: !44, file: !1, line: 45, type: !46)
79+
!44 = distinct !DILexicalBlock(scope: !45, file: !1, line: 42, column: 2)
80+
!45 = distinct !DILexicalBlock(scope: !8, file: !1, line: 41, column: 6)
81+
!46 = !DIDerivedType(tag: DW_TAG_const_type, baseType: !47)
82+
!47 = !DIBasicType(name: "bool", size: 8, encoding: DW_ATE_boolean)
83+
!48 = !DILocation(line: 0, scope: !44)
84+
!49 = !DILocation(line: 51, column: 8, scope: !50)
85+
!50 = distinct !DILexicalBlock(scope: !44, file: !1, line: 51, column: 8)
86+
!51 = !{!52, !53, i64 0}
87+
!52 = !{!"_ZTS7UClient", !53, i64 0, !56, i64 8}
88+
!53 = !{!"any pointer", !54, i64 0}
89+
!54 = !{!"omnipotent char", !55, i64 0}
90+
!55 = !{!"Simple C++ TBAA"}
91+
!56 = !{!"_ZTS6FFlags", !57, i64 0, !57, i64 0, !57, i64 0, !57, i64 4, !57, i64 8, !57, i64 12}
92+
!57 = !{!"int", !54, i64 0}
93+
!58 = !DILocation(line: 52, column: 12, scope: !50)
94+
!59 = !DILocation(line: 52, column: 23, scope: !50)
95+
!60 = !DILocation(line: 55, column: 1, scope: !8)

llvm/test/DebugInfo/X86/convert-debugloc.ll

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
; RUN: | FileCheck %s --check-prefix=VERBOSE --check-prefix=CONV "--implicit-check-not={{DW_TAG|NULL}}"
2828

2929

30-
; SPLITCONV: Compile Unit:{{.*}} DWO_id = 0xe91d8d1d7f9782c0
30+
; SPLITCONV: Compile Unit:{{.*}} DWO_id = 0x62f17241069b1fa3
3131
; SPLIT: DW_TAG_skeleton_unit
3232

3333
; CONV: DW_TAG_compile_unit
@@ -44,7 +44,7 @@
4444
; CONV: DW_TAG_subprogram
4545
; CONV: DW_TAG_formal_parameter
4646
; CONV: DW_TAG_variable
47-
; CONV: DW_AT_location {{.*}}DW_OP_constu 0x20, DW_OP_convert (
47+
; CONV: DW_AT_location {{.*}}DW_OP_constu 0x20, DW_OP_lit0, DW_OP_plus, DW_OP_convert (
4848
; VERBOSE-SAME: [[SIG8]] ->
4949
; CONV-SAME: [[SIG8]]) "DW_ATE_signed_8", DW_OP_convert (
5050
; VERBOSE-SAME: [[SIG32]] ->
@@ -76,7 +76,11 @@
7676
define dso_local signext i8 @foo(i8 signext %x) !dbg !7 {
7777
entry:
7878
call void @llvm.dbg.value(metadata i8 %x, metadata !11, metadata !DIExpression()), !dbg !12
79-
call void @llvm.dbg.value(metadata i8 32, metadata !13, metadata !DIExpression(DW_OP_LLVM_convert, 8, DW_ATE_signed, DW_OP_LLVM_convert, 32, DW_ATE_signed, DW_OP_stack_value)), !dbg !15
79+
;; This test depends on "convert" surviving all the way to the final object.
80+
;; So, insert something before DW_OP_LLVM_convert that the expression folder
81+
;; will not attempt to eliminate. At the moment, only "convert" ops are folded.
82+
;; If you have to change the expression, the expected DWO_id also changes.
83+
call void @llvm.dbg.value(metadata i8 32, metadata !13, metadata !DIExpression(DW_OP_lit0, DW_OP_plus, DW_OP_LLVM_convert, 8, DW_ATE_signed, DW_OP_LLVM_convert, 32, DW_ATE_signed, DW_OP_stack_value)), !dbg !15
8084
ret i8 %x, !dbg !16
8185
}
8286

llvm/test/DebugInfo/X86/convert-linked.ll

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,20 @@
1919
define dso_local signext i8 @foo(i8 signext %x) !dbg !9 {
2020
entry:
2121
call void @llvm.dbg.value(metadata i8 %x, metadata !13, metadata !DIExpression()), !dbg !14
22-
call void @llvm.dbg.value(metadata i8 32, metadata !15, metadata !DIExpression(DW_OP_LLVM_convert, 8, DW_ATE_signed, DW_OP_LLVM_convert, 32, DW_ATE_signed, DW_OP_stack_value)), !dbg !17
22+
;; This test depends on "convert" surviving all the way to the final object.
23+
;; So, insert something before DW_OP_LLVM_convert that the expression folder
24+
;; will not attempt to eliminate. At the moment, only "convert" ops are folded.
25+
call void @llvm.dbg.value(metadata i8 32, metadata !15, metadata !DIExpression(DW_OP_lit0, DW_OP_plus, DW_OP_LLVM_convert, 8, DW_ATE_signed, DW_OP_LLVM_convert, 32, DW_ATE_signed, DW_OP_stack_value)), !dbg !17
2326
ret i8 %x, !dbg !18
2427
}
2528

2629
define dso_local signext i8 @bar(i8 signext %x) !dbg !19 {
2730
entry:
2831
call void @llvm.dbg.value(metadata i8 %x, metadata !20, metadata !DIExpression()), !dbg !21
29-
call void @llvm.dbg.value(metadata i8 32, metadata !22, metadata !DIExpression(DW_OP_LLVM_convert, 8, DW_ATE_signed, DW_OP_LLVM_convert, 16, DW_ATE_signed, DW_OP_stack_value)), !dbg !24
32+
;; This test depends on "convert" surviving all the way to the final object.
33+
;; So, insert something before DW_OP_LLVM_convert that the expression folder
34+
;; will not attempt to eliminate. At the moment, only "convert" ops are folded.
35+
call void @llvm.dbg.value(metadata i8 32, metadata !22, metadata !DIExpression(DW_OP_lit0, DW_OP_plus, DW_OP_LLVM_convert, 8, DW_ATE_signed, DW_OP_LLVM_convert, 16, DW_ATE_signed, DW_OP_stack_value)), !dbg !24
3036
ret i8 %x, !dbg !25
3137
}
3238

llvm/test/DebugInfo/X86/convert-loclist.ll

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
; often - add another IR file with a different DW_OP_convert that's otherwise
1414
; identical and demonstrate that they have different DWO IDs.
1515

16-
; SPLIT: 0x00000000: Compile Unit: {{.*}} DWO_id = 0xafd73565c68bc661
16+
; SPLIT: 0x00000000: Compile Unit: {{.*}} DWO_id = 0xecf2563326b0bdd3
1717

1818
; Regression testing a fairly quirky bug where instead of hashing (see above),
1919
; extra bytes would be emitted into the output assembly in no
@@ -23,13 +23,17 @@
2323
; CHECK: 0x{{0*}}[[TYPE:.*]]: DW_TAG_base_type
2424
; CHECK-NEXT: DW_AT_name ("DW_ATE_unsigned_32")
2525

26-
; CHECK: DW_LLE_offset_pair ({{.*}}): DW_OP_consts +7, DW_OP_convert 0x[[TYPE]], DW_OP_stack_value
26+
; CHECK: DW_LLE_offset_pair ({{.*}}): DW_OP_consts +7, DW_OP_lit0, DW_OP_plus, DW_OP_convert 0x[[TYPE]], DW_OP_stack_value
2727

2828
; Function Attrs: uwtable
2929
define dso_local void @_Z2f2v() local_unnamed_addr #0 !dbg !11 {
3030
entry:
3131
tail call void @_Z2f1v(), !dbg !15
32-
call void @llvm.dbg.value(metadata i32 7, metadata !13, metadata !DIExpression(DW_OP_LLVM_convert, 32, DW_ATE_unsigned, DW_OP_stack_value)), !dbg !16
32+
;; This test depends on "convert" surviving all the way to the final object.
33+
;; So, insert something before DW_OP_LLVM_convert that the expression folder
34+
;; will not attempt to eliminate. At the moment, only "convert" ops are folded.
35+
;; If you have to change the expression, the expected DWO_id also changes.
36+
call void @llvm.dbg.value(metadata i32 7, metadata !13, metadata !DIExpression(DW_OP_lit0, DW_OP_plus, DW_OP_LLVM_convert, 32, DW_ATE_unsigned, DW_OP_stack_value)), !dbg !16
3337
tail call void @_Z2f1v(), !dbg !17
3438
ret void, !dbg !18
3539
}

llvm/unittests/IR/MetadataTest.cpp

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2923,6 +2923,55 @@ TEST_F(DIExpressionTest, replaceArg) {
29232923
#undef EXPECT_REPLACE_ARG_EQ
29242924
}
29252925

2926+
TEST_F(DIExpressionTest, foldConstant) {
2927+
const ConstantInt *Int;
2928+
const ConstantInt *NewInt;
2929+
DIExpression *Expr;
2930+
DIExpression *NewExpr;
2931+
2932+
#define EXPECT_FOLD_CONST(StartWidth, StartValue, EndWidth, EndValue, NumElts) \
2933+
Int = ConstantInt::get(Context, APInt(StartWidth, StartValue)); \
2934+
std::tie(NewExpr, NewInt) = Expr->constantFold(Int); \
2935+
ASSERT_EQ(NewInt->getBitWidth(), EndWidth##u); \
2936+
EXPECT_EQ(NewInt->getValue(), APInt(EndWidth, EndValue)); \
2937+
EXPECT_EQ(NewExpr->getNumElements(), NumElts##u)
2938+
2939+
// Unfoldable expression should return the original unmodified Int/Expr.
2940+
Expr = DIExpression::get(Context, {dwarf::DW_OP_deref});
2941+
EXPECT_FOLD_CONST(32, 117, 32, 117, 1);
2942+
EXPECT_EQ(NewExpr, Expr);
2943+
EXPECT_EQ(NewInt, Int);
2944+
EXPECT_TRUE(NewExpr->startsWithDeref());
2945+
2946+
// One unsigned bit-width conversion.
2947+
Expr = DIExpression::get(
2948+
Context, {dwarf::DW_OP_LLVM_convert, 72, dwarf::DW_ATE_unsigned});
2949+
EXPECT_FOLD_CONST(8, 12, 72, 12, 0);
2950+
2951+
// Two unsigned bit-width conversions (mask truncation).
2952+
Expr = DIExpression::get(
2953+
Context, {dwarf::DW_OP_LLVM_convert, 8, dwarf::DW_ATE_unsigned,
2954+
dwarf::DW_OP_LLVM_convert, 16, dwarf::DW_ATE_unsigned});
2955+
EXPECT_FOLD_CONST(32, -1, 16, 0xff, 0);
2956+
2957+
// Sign extension.
2958+
Expr = DIExpression::get(
2959+
Context, {dwarf::DW_OP_LLVM_convert, 32, dwarf::DW_ATE_signed});
2960+
EXPECT_FOLD_CONST(16, -1, 32, -1, 0);
2961+
2962+
// Get non-foldable operations back in the new Expr.
2963+
uint64_t Elements[] = {dwarf::DW_OP_deref, dwarf::DW_OP_stack_value};
2964+
ArrayRef<uint64_t> Expected = Elements;
2965+
Expr = DIExpression::get(
2966+
Context, {dwarf::DW_OP_LLVM_convert, 32, dwarf::DW_ATE_signed});
2967+
Expr = DIExpression::append(Expr, Expected);
2968+
ASSERT_EQ(Expr->getNumElements(), 5u);
2969+
EXPECT_FOLD_CONST(16, -1, 32, -1, 2);
2970+
EXPECT_EQ(NewExpr->getElements(), Expected);
2971+
2972+
#undef EXPECT_FOLD_CONST
2973+
}
2974+
29262975
typedef MetadataTest DIObjCPropertyTest;
29272976

29282977
TEST_F(DIObjCPropertyTest, get) {

0 commit comments

Comments
 (0)