[llvm] 75aa3d5 - Add a DIExpression const-folder to prevent silly expressions.

Paul Robinson via llvm-commits llvm-commits at lists.llvm.org
Thu Aug 5 06:14:48 PDT 2021


Author: Paul Robinson
Date: 2021-08-05T06:14:40-07:00
New Revision: 75aa3d520d53fb0d2a279736245ad255ec720a5b

URL: https://github.com/llvm/llvm-project/commit/75aa3d520d53fb0d2a279736245ad255ec720a5b
DIFF: https://github.com/llvm/llvm-project/commit/75aa3d520d53fb0d2a279736245ad255ec720a5b.diff

LOG: 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

Added: 
    llvm/test/DebugInfo/X86/DIExpr-const-folding.ll

Modified: 
    llvm/include/llvm/IR/DebugInfoMetadata.h
    llvm/lib/CodeGen/SelectionDAG/FastISel.cpp
    llvm/lib/CodeGen/SelectionDAG/InstrEmitter.cpp
    llvm/lib/IR/DebugInfoMetadata.cpp
    llvm/test/DebugInfo/X86/convert-debugloc.ll
    llvm/test/DebugInfo/X86/convert-linked.ll
    llvm/test/DebugInfo/X86/convert-loclist.ll
    llvm/unittests/IR/MetadataTest.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/include/llvm/IR/DebugInfoMetadata.h b/llvm/include/llvm/IR/DebugInfoMetadata.h
index 20a032f049093..b59a7b3a46f3d 100644
--- a/llvm/include/llvm/IR/DebugInfoMetadata.h
+++ b/llvm/include/llvm/IR/DebugInfoMetadata.h
@@ -2876,6 +2876,12 @@ class DIExpression : public MDNode {
     return getNumElements() > 0 &&
            getElement(0) == dwarf::DW_OP_LLVM_entry_value;
   }
+
+  /// Try to shorten an expression with an initial constant operand.
+  /// Returns a new expression and constant on success, or the original
+  /// expression and constant on failure.
+  std::pair<DIExpression *, const ConstantInt *>
+  constantFold(const ConstantInt *CI);
 };
 
 inline bool operator==(const DIExpression::FragmentInfo &A,

diff  --git a/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp b/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp
index 4ca731cfdf622..89dacd2b498d1 100644
--- a/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp
@@ -1292,18 +1292,22 @@ bool FastISel::selectIntrinsicCall(const IntrinsicInst *II) {
       BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, II, false, 0U,
               DI->getVariable(), DI->getExpression());
     } else if (const auto *CI = dyn_cast<ConstantInt>(V)) {
+      // See if there's an expression to constant-fold.
+      DIExpression *Expr = DI->getExpression();
+      if (Expr)
+        std::tie(Expr, CI) = Expr->constantFold(CI);
       if (CI->getBitWidth() > 64)
         BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, II)
             .addCImm(CI)
             .addImm(0U)
             .addMetadata(DI->getVariable())
-            .addMetadata(DI->getExpression());
+            .addMetadata(Expr);
       else
         BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, II)
             .addImm(CI->getZExtValue())
             .addImm(0U)
             .addMetadata(DI->getVariable())
-            .addMetadata(DI->getExpression());
+            .addMetadata(Expr);
     } else if (const auto *CF = dyn_cast<ConstantFP>(V)) {
       BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, II)
           .addFPImm(CF)

diff  --git a/llvm/lib/CodeGen/SelectionDAG/InstrEmitter.cpp b/llvm/lib/CodeGen/SelectionDAG/InstrEmitter.cpp
index 348fad6daf8f0..195aa14c0a96e 100644
--- a/llvm/lib/CodeGen/SelectionDAG/InstrEmitter.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/InstrEmitter.cpp
@@ -872,17 +872,28 @@ MachineInstr *
 InstrEmitter::EmitDbgValueFromSingleOp(SDDbgValue *SD,
                                        DenseMap<SDValue, Register> &VRBaseMap) {
   MDNode *Var = SD->getVariable();
-  MDNode *Expr = SD->getExpression();
+  DIExpression *Expr = SD->getExpression();
   DebugLoc DL = SD->getDebugLoc();
   const MCInstrDesc &II = TII->get(TargetOpcode::DBG_VALUE);
 
   assert(SD->getLocationOps().size() == 1 &&
          "Non variadic dbg_value should have only one location op");
 
+  // See about constant-folding the expression.
+  // Copy the location operand in case we replace it.
+  SmallVector<SDDbgOperand, 1> LocationOps(1, SD->getLocationOps()[0]);
+  if (Expr && LocationOps[0].getKind() == SDDbgOperand::CONST) {
+    const Value *V = LocationOps[0].getConst();
+    if (auto *C = dyn_cast<ConstantInt>(V)) {
+      std::tie(Expr, C) = Expr->constantFold(C);
+      LocationOps[0] = SDDbgOperand::fromConst(C);
+    }
+  }
+
   // Emit non-variadic dbg_value nodes as DBG_VALUE.
   // DBG_VALUE := "DBG_VALUE" loc, isIndirect, var, expr
   auto MIB = BuildMI(*MF, DL, II);
-  AddDbgValueLocationOps(MIB, II, SD->getLocationOps(), VRBaseMap);
+  AddDbgValueLocationOps(MIB, II, LocationOps, VRBaseMap);
 
   if (SD->isIndirect())
     MIB.addImm(0U);

diff  --git a/llvm/lib/IR/DebugInfoMetadata.cpp b/llvm/lib/IR/DebugInfoMetadata.cpp
index 7b0dab799e1a9..e335064257e1e 100644
--- a/llvm/lib/IR/DebugInfoMetadata.cpp
+++ b/llvm/lib/IR/DebugInfoMetadata.cpp
@@ -1476,6 +1476,45 @@ Optional<DIExpression *> DIExpression::createFragmentExpression(
   return DIExpression::get(Expr->getContext(), Ops);
 }
 
+std::pair<DIExpression *, const ConstantInt *>
+DIExpression::constantFold(const ConstantInt *CI) {
+  // Copy the APInt so we can modify it.
+  APInt NewInt = CI->getValue();
+  SmallVector<uint64_t, 8> Ops;
+
+  // Fold operators only at the beginning of the expression.
+  bool First = true;
+  bool Changed = false;
+  for (auto Op : expr_ops()) {
+    switch (Op.getOp()) {
+    default:
+      // We fold only the leading part of the expression; if we get to a part
+      // that we're going to copy unchanged, and haven't done any folding,
+      // then the entire expression is unchanged and we can return early.
+      if (!Changed)
+        return {this, CI};
+      First = false;
+      break;
+    case dwarf::DW_OP_LLVM_convert:
+      if (!First)
+        break;
+      Changed = true;
+      if (Op.getArg(1) == dwarf::DW_ATE_signed)
+        NewInt = NewInt.sextOrTrunc(Op.getArg(0));
+      else {
+        assert(Op.getArg(1) == dwarf::DW_ATE_unsigned && "Unexpected operand");
+        NewInt = NewInt.zextOrTrunc(Op.getArg(0));
+      }
+      continue;
+    }
+    Op.appendToVector(Ops);
+  }
+  if (!Changed)
+    return {this, CI};
+  return {DIExpression::get(getContext(), Ops),
+          ConstantInt::get(getContext(), NewInt)};
+}
+
 uint64_t DIExpression::getNumLocationOperands() const {
   uint64_t Result = 0;
   for (auto ExprOp : expr_ops())

diff  --git a/llvm/test/DebugInfo/X86/DIExpr-const-folding.ll b/llvm/test/DebugInfo/X86/DIExpr-const-folding.ll
new file mode 100644
index 0000000000000..725ede01c5fae
--- /dev/null
+++ b/llvm/test/DebugInfo/X86/DIExpr-const-folding.ll
@@ -0,0 +1,95 @@
+; RUN: llc -mtriple=x86_64 -filetype=obj < %s \
+; RUN:   | llvm-dwarfdump -debug-info - | FileCheck %s
+; RUN: llc -mtriple=x86_64 -filetype=obj -fast-isel < %s \
+; RUN:   | llvm-dwarfdump -debug-info - | FileCheck %s
+
+;; The important thing is the DW_OP_lit2 with no converts.
+;; TODO: Make this work with global isel
+;; Indirectly related FIXME: Should be able to emit DW_AT_const_value instead.
+
+; CHECK: DW_TAG_variable
+; CHECK-NEXT: DW_AT_location (DW_OP_lit2, DW_OP_stack_value)
+; CHECK-NEXT: DW_AT_name ("bIsShowingCollision")
+
+%class.UClient = type { %class.UWorld*, %struct.FFlags }
+%class.UWorld = type { i16 }
+%struct.FFlags = type { [9 x i8], i32 }
+
+define dso_local void @_ZN7UClient13ToggleVolumesEv(%class.UClient* nocapture nonnull align 8 dereferenceable(24) %this) local_unnamed_addr align 2 !dbg !8 {
+entry:
+  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
+  %World = getelementptr inbounds %class.UClient, %class.UClient* %this, i64 0, i32 0, !dbg !49
+  %0 = load %class.UWorld*, %class.UWorld** %World, align 8, !dbg !49, !tbaa !51
+  %1 = getelementptr %class.UWorld, %class.UWorld* %0, i64 0, i32 0, !dbg !58
+  store i16 2, i16* %1, align 1, !dbg !59
+  ret void, !dbg !60
+}
+
+; Function Attrs: nofree nosync nounwind readnone speculatable willreturn
+declare void @llvm.dbg.value(metadata, metadata, metadata)
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!3, !4, !5, !6}
+!llvm.ident = !{!7}
+
+!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)
+!1 = !DIFile(filename: "min-test-3.cpp", directory: "/home/probinson/projects/scratch/tc8251")
+!2 = !{}
+!3 = !{i32 7, !"Dwarf Version", i32 4}
+!4 = !{i32 2, !"Debug Info Version", i32 3}
+!5 = !{i32 1, !"wchar_size", i32 4}
+!6 = !{i32 7, !"uwtable", i32 1}
+!7 = !{!"clang version 13.0.0"}
+!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)
+!9 = distinct !DICompositeType(tag: DW_TAG_class_type, name: "UClient", file: !1, line: 31, size: 192, flags: DIFlagTypePassByValue | DIFlagNonTrivial, elements: !10, identifier: "_ZTS7UClient")
+!10 = !{!11, !20, !36}
+!11 = !DIDerivedType(tag: DW_TAG_member, name: "World", scope: !9, file: !1, line: 34, baseType: !12, size: 64, flags: DIFlagPublic)
+!12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64)
+!13 = distinct !DICompositeType(tag: DW_TAG_class_type, name: "UWorld", file: !1, line: 8, size: 16, flags: DIFlagTypePassByValue, elements: !14, identifier: "_ZTS6UWorld")
+!14 = !{!15, !18, !19}
+!15 = !DIDerivedType(tag: DW_TAG_member, name: "bCollision", scope: !13, file: !1, line: 11, baseType: !16, size: 1, flags: DIFlagPublic | DIFlagBitField, extraData: i64 0)
+!16 = !DIDerivedType(tag: DW_TAG_typedef, name: "uint8", file: !1, line: 1, baseType: !17)
+!17 = !DIBasicType(name: "unsigned char", size: 8, encoding: DW_ATE_unsigned_char)
+!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)
+!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)
+!20 = !DIDerivedType(tag: DW_TAG_member, name: "EngineShowFlags", scope: !9, file: !1, line: 35, baseType: !21, size: 128, offset: 64, flags: DIFlagPublic)
+!21 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "FFlags", file: !1, line: 16, size: 128, flags: DIFlagTypePassByValue | DIFlagNonTrivial, elements: !22, identifier: "_ZTS6FFlags")
+!22 = !{!23, !26, !27, !28, !29, !30, !32}
+!23 = !DIDerivedType(tag: DW_TAG_member, name: "Volumes", scope: !21, file: !1, line: 18, baseType: !24, size: 1, flags: DIFlagBitField, extraData: i64 0)
+!24 = !DIDerivedType(tag: DW_TAG_typedef, name: "uint32", file: !1, line: 2, baseType: !25)
+!25 = !DIBasicType(name: "unsigned int", size: 32, encoding: DW_ATE_unsigned)
+!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)
+!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)
+!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)
+!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)
+!30 = !DIDerivedType(tag: DW_TAG_member, name: "CustomShowFlags", scope: !21, file: !1, line: 24, baseType: !31, size: 32, offset: 96)
+!31 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!32 = !DISubprogram(name: "FFlags", scope: !21, file: !1, line: 25, type: !33, scopeLine: 25, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized)
+!33 = !DISubroutineType(types: !34)
+!34 = !{null, !35}
+!35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !21, size: 64, flags: DIFlagArtificial | DIFlagObjectPointer)
+!36 = !DISubprogram(name: "ToggleVolumes", linkageName: "_ZN7UClient13ToggleVolumesEv", scope: !9, file: !1, line: 36, type: !37, scopeLine: 36, flags: DIFlagPublic | DIFlagPrototyped, spFlags: DISPFlagOptimized)
+!37 = !DISubroutineType(types: !38)
+!38 = !{null, !39}
+!39 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !9, size: 64, flags: DIFlagArtificial | DIFlagObjectPointer)
+!40 = !{!41, !43}
+!41 = !DILocalVariable(name: "this", arg: 1, scope: !8, type: !42, flags: DIFlagArtificial | DIFlagObjectPointer)
+!42 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !9, size: 64)
+!43 = !DILocalVariable(name: "bIsShowingCollision", scope: !44, file: !1, line: 45, type: !46)
+!44 = distinct !DILexicalBlock(scope: !45, file: !1, line: 42, column: 2)
+!45 = distinct !DILexicalBlock(scope: !8, file: !1, line: 41, column: 6)
+!46 = !DIDerivedType(tag: DW_TAG_const_type, baseType: !47)
+!47 = !DIBasicType(name: "bool", size: 8, encoding: DW_ATE_boolean)
+!48 = !DILocation(line: 0, scope: !44)
+!49 = !DILocation(line: 51, column: 8, scope: !50)
+!50 = distinct !DILexicalBlock(scope: !44, file: !1, line: 51, column: 8)
+!51 = !{!52, !53, i64 0}
+!52 = !{!"_ZTS7UClient", !53, i64 0, !56, i64 8}
+!53 = !{!"any pointer", !54, i64 0}
+!54 = !{!"omnipotent char", !55, i64 0}
+!55 = !{!"Simple C++ TBAA"}
+!56 = !{!"_ZTS6FFlags", !57, i64 0, !57, i64 0, !57, i64 0, !57, i64 4, !57, i64 8, !57, i64 12}
+!57 = !{!"int", !54, i64 0}
+!58 = !DILocation(line: 52, column: 12, scope: !50)
+!59 = !DILocation(line: 52, column: 23, scope: !50)
+!60 = !DILocation(line: 55, column: 1, scope: !8)

diff  --git a/llvm/test/DebugInfo/X86/convert-debugloc.ll b/llvm/test/DebugInfo/X86/convert-debugloc.ll
index fc31c3f72549e..21e41dcc4c2a9 100644
--- a/llvm/test/DebugInfo/X86/convert-debugloc.ll
+++ b/llvm/test/DebugInfo/X86/convert-debugloc.ll
@@ -27,7 +27,7 @@
 ; RUN:   | FileCheck %s --check-prefix=VERBOSE --check-prefix=CONV "--implicit-check-not={{DW_TAG|NULL}}"
 
 
-; SPLITCONV: Compile Unit:{{.*}} DWO_id = 0xe91d8d1d7f9782c0
+; SPLITCONV: Compile Unit:{{.*}} DWO_id = 0x62f17241069b1fa3
 ; SPLIT: DW_TAG_skeleton_unit
 
 ; CONV: DW_TAG_compile_unit
@@ -44,7 +44,7 @@
 ; CONV:   DW_TAG_subprogram
 ; CONV:     DW_TAG_formal_parameter
 ; CONV:     DW_TAG_variable
-; CONV:     DW_AT_location {{.*}}DW_OP_constu 0x20, DW_OP_convert (
+; CONV:     DW_AT_location {{.*}}DW_OP_constu 0x20, DW_OP_lit0, DW_OP_plus, DW_OP_convert (
 ; VERBOSE-SAME: [[SIG8]] ->
 ; CONV-SAME: [[SIG8]]) "DW_ATE_signed_8", DW_OP_convert (
 ; VERBOSE-SAME: [[SIG32]] ->
@@ -76,7 +76,11 @@
 define dso_local signext i8 @foo(i8 signext %x) !dbg !7 {
 entry:
   call void @llvm.dbg.value(metadata i8 %x, metadata !11, metadata !DIExpression()), !dbg !12
-  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
+;; This test depends on "convert" surviving all the way to the final object.
+;; So, insert something before DW_OP_LLVM_convert that the expression folder
+;; will not attempt to eliminate.  At the moment, only "convert" ops are folded.
+;; If you have to change the expression, the expected DWO_id also changes.
+  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
   ret i8 %x, !dbg !16
 }
 

diff  --git a/llvm/test/DebugInfo/X86/convert-linked.ll b/llvm/test/DebugInfo/X86/convert-linked.ll
index 97218e65075bb..6a74a6310bc34 100644
--- a/llvm/test/DebugInfo/X86/convert-linked.ll
+++ b/llvm/test/DebugInfo/X86/convert-linked.ll
@@ -19,14 +19,20 @@
 define dso_local signext i8 @foo(i8 signext %x) !dbg !9 {
 entry:
   call void @llvm.dbg.value(metadata i8 %x, metadata !13, metadata !DIExpression()), !dbg !14
-  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
+;; This test depends on "convert" surviving all the way to the final object.
+;; So, insert something before DW_OP_LLVM_convert that the expression folder
+;; will not attempt to eliminate.  At the moment, only "convert" ops are folded.
+  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
   ret i8 %x, !dbg !18
 }
 
 define dso_local signext i8 @bar(i8 signext %x) !dbg !19 {
 entry:
   call void @llvm.dbg.value(metadata i8 %x, metadata !20, metadata !DIExpression()), !dbg !21
-  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
+;; This test depends on "convert" surviving all the way to the final object.
+;; So, insert something before DW_OP_LLVM_convert that the expression folder
+;; will not attempt to eliminate.  At the moment, only "convert" ops are folded.
+  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
   ret i8 %x, !dbg !25
 }
 

diff  --git a/llvm/test/DebugInfo/X86/convert-loclist.ll b/llvm/test/DebugInfo/X86/convert-loclist.ll
index b34e0420fb92f..953400b5106b7 100644
--- a/llvm/test/DebugInfo/X86/convert-loclist.ll
+++ b/llvm/test/DebugInfo/X86/convert-loclist.ll
@@ -13,7 +13,7 @@
 ; often - add another IR file with a 
diff erent DW_OP_convert that's otherwise
 ; identical and demonstrate that they have 
diff erent DWO IDs.
 
-; SPLIT: 0x00000000: Compile Unit: {{.*}} DWO_id = 0xafd73565c68bc661
+; SPLIT: 0x00000000: Compile Unit: {{.*}} DWO_id = 0xecf2563326b0bdd3
 
 ; Regression testing a fairly quirky bug where instead of hashing (see above),
 ; extra bytes would be emitted into the output assembly in no
@@ -23,13 +23,17 @@
 ; CHECK: 0x{{0*}}[[TYPE:.*]]: DW_TAG_base_type
 ; CHECK-NEXT:                   DW_AT_name ("DW_ATE_unsigned_32")
 
-; CHECK: DW_LLE_offset_pair ({{.*}}): DW_OP_consts +7, DW_OP_convert 0x[[TYPE]], DW_OP_stack_value
+; CHECK: DW_LLE_offset_pair ({{.*}}): DW_OP_consts +7, DW_OP_lit0, DW_OP_plus, DW_OP_convert 0x[[TYPE]], DW_OP_stack_value
 
 ; Function Attrs: uwtable
 define dso_local void @_Z2f2v() local_unnamed_addr #0 !dbg !11 {
 entry:
   tail call void @_Z2f1v(), !dbg !15
-  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
+;; This test depends on "convert" surviving all the way to the final object.
+;; So, insert something before DW_OP_LLVM_convert that the expression folder
+;; will not attempt to eliminate.  At the moment, only "convert" ops are folded.
+;; If you have to change the expression, the expected DWO_id also changes.
+  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
   tail call void @_Z2f1v(), !dbg !17
   ret void, !dbg !18
 }

diff  --git a/llvm/unittests/IR/MetadataTest.cpp b/llvm/unittests/IR/MetadataTest.cpp
index 02856aecc544a..838e2736ee9ff 100644
--- a/llvm/unittests/IR/MetadataTest.cpp
+++ b/llvm/unittests/IR/MetadataTest.cpp
@@ -2894,6 +2894,55 @@ TEST_F(DIExpressionTest, replaceArg) {
 #undef EXPECT_REPLACE_ARG_EQ
 }
 
+TEST_F(DIExpressionTest, foldConstant) {
+  const ConstantInt *Int;
+  const ConstantInt *NewInt;
+  DIExpression *Expr;
+  DIExpression *NewExpr;
+
+#define EXPECT_FOLD_CONST(StartWidth, StartValue, EndWidth, EndValue, NumElts)  \
+  Int = ConstantInt::get(Context, APInt(StartWidth, StartValue));               \
+  std::tie(NewExpr, NewInt) = Expr->constantFold(Int);                          \
+  ASSERT_EQ(NewInt->getBitWidth(), EndWidth##u);                                \
+  EXPECT_EQ(NewInt->getValue(), APInt(EndWidth, EndValue));                     \
+  EXPECT_EQ(NewExpr->getNumElements(), NumElts##u)
+
+  // Unfoldable expression should return the original unmodified Int/Expr.
+  Expr = DIExpression::get(Context, {dwarf::DW_OP_deref});
+  EXPECT_FOLD_CONST(32, 117, 32, 117, 1);
+  EXPECT_EQ(NewExpr, Expr);
+  EXPECT_EQ(NewInt, Int);
+  EXPECT_TRUE(NewExpr->startsWithDeref());
+
+  // One unsigned bit-width conversion.
+  Expr = DIExpression::get(
+      Context, {dwarf::DW_OP_LLVM_convert, 72, dwarf::DW_ATE_unsigned});
+  EXPECT_FOLD_CONST(8, 12, 72, 12, 0);
+
+  // Two unsigned bit-width conversions (mask truncation).
+  Expr = DIExpression::get(
+      Context, {dwarf::DW_OP_LLVM_convert, 8, dwarf::DW_ATE_unsigned,
+                dwarf::DW_OP_LLVM_convert, 16, dwarf::DW_ATE_unsigned});
+  EXPECT_FOLD_CONST(32, -1, 16, 0xff, 0);
+
+  // Sign extension.
+  Expr = DIExpression::get(
+      Context, {dwarf::DW_OP_LLVM_convert, 32, dwarf::DW_ATE_signed});
+  EXPECT_FOLD_CONST(16, -1, 32, -1, 0);
+
+  // Get non-foldable operations back in the new Expr.
+  uint64_t Elements[] = {dwarf::DW_OP_deref, dwarf::DW_OP_stack_value};
+  ArrayRef<uint64_t> Expected = Elements;
+  Expr = DIExpression::get(
+      Context, {dwarf::DW_OP_LLVM_convert, 32, dwarf::DW_ATE_signed});
+  Expr = DIExpression::append(Expr, Expected);
+  ASSERT_EQ(Expr->getNumElements(), 5u);
+  EXPECT_FOLD_CONST(16, -1, 32, -1, 2);
+  EXPECT_EQ(NewExpr->getElements(), Expected);
+
+#undef EXPECT_FOLD_CONST
+}
+
 typedef MetadataTest DIObjCPropertyTest;
 
 TEST_F(DIObjCPropertyTest, get) {


        


More information about the llvm-commits mailing list