[llvm] 7afc7db - [Assignment Tracking] Trim assignments for untagged out of bounds stores (#66095)

via llvm-commits llvm-commits at lists.llvm.org
Fri Sep 15 01:10:58 PDT 2023


Author: Orlando Cazalet-Hyams
Date: 2023-09-15T09:10:53+01:00
New Revision: 7afc7db7fc807a4a7ee623db491622698e8edcca

URL: https://github.com/llvm/llvm-project/commit/7afc7db7fc807a4a7ee623db491622698e8edcca
DIFF: https://github.com/llvm/llvm-project/commit/7afc7db7fc807a4a7ee623db491622698e8edcca.diff

LOG: [Assignment Tracking] Trim assignments for untagged out of bounds stores (#66095)

Fixes #65004 by trimming assignments from out of bounds stores (out of bounds
of either the base variable or the backing alloca). If there's no overlap at
all or the out of bounds access starts at a negative offset from the alloca,
the assignment is simply skipped.

Added: 
    llvm/test/DebugInfo/assignment-tracking/X86/untagged-store-assignment-extra-checks.ll
    llvm/test/DebugInfo/assignment-tracking/X86/untagged-store-assignment-outside-variable.ll

Modified: 
    llvm/lib/CodeGen/AssignmentTrackingAnalysis.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/lib/CodeGen/AssignmentTrackingAnalysis.cpp b/llvm/lib/CodeGen/AssignmentTrackingAnalysis.cpp
index 5ef850d09d925f0..5b56975587a7343 100644
--- a/llvm/lib/CodeGen/AssignmentTrackingAnalysis.cpp
+++ b/llvm/lib/CodeGen/AssignmentTrackingAnalysis.cpp
@@ -1979,20 +1979,23 @@ static AssignmentTrackingLowering::OverlapMap buildOverlapMapAndRecordDeclares(
                      I, Fn.getParent()->getDataLayout())) {
         // Find markers linked to this alloca.
         for (DbgAssignIntrinsic *DAI : at::getAssignmentMarkers(Info->Base)) {
-          // Discard the fragment if it covers the entire variable.
-          std::optional<DIExpression::FragmentInfo> FragInfo =
-              [&Info, DAI]() -> std::optional<DIExpression::FragmentInfo> {
-            DIExpression::FragmentInfo F;
-            F.OffsetInBits = Info->OffsetInBits;
-            F.SizeInBits = Info->SizeInBits;
-            if (auto ExistingFrag = DAI->getExpression()->getFragmentInfo())
-              F.OffsetInBits += ExistingFrag->OffsetInBits;
-            if (auto Sz = DAI->getVariable()->getSizeInBits()) {
-              if (F.OffsetInBits == 0 && F.SizeInBits == *Sz)
-                return std::nullopt;
-            }
-            return F;
-          }();
+          std::optional<DIExpression::FragmentInfo> FragInfo;
+
+          // Skip this assignment if the affected bits are outside of the
+          // variable fragment.
+          if (!at::calculateFragmentIntersect(
+                  I.getModule()->getDataLayout(), Info->Base,
+                  Info->OffsetInBits, Info->SizeInBits, DAI, FragInfo) ||
+              (FragInfo && FragInfo->SizeInBits == 0))
+            continue;
+
+          // FragInfo from calculateFragmentIntersect is nullopt if the
+          // resultant fragment matches DAI's fragment or entire variable - in
+          // which case copy the fragment info from DAI. If FragInfo is still
+          // nullopt after the copy it means "no fragment info" instead, which
+          // is how it is usually interpreted.
+          if (!FragInfo)
+            FragInfo = DAI->getExpression()->getFragmentInfo();
 
           DebugVariable DV = DebugVariable(DAI->getVariable(), FragInfo,
                                            DAI->getDebugLoc().getInlinedAt());

diff  --git a/llvm/test/DebugInfo/assignment-tracking/X86/untagged-store-assignment-extra-checks.ll b/llvm/test/DebugInfo/assignment-tracking/X86/untagged-store-assignment-extra-checks.ll
new file mode 100644
index 000000000000000..c407a7da7fa2c2c
--- /dev/null
+++ b/llvm/test/DebugInfo/assignment-tracking/X86/untagged-store-assignment-extra-checks.ll
@@ -0,0 +1,93 @@
+; RUN: llc %s -stop-after=finalize-isel -o - \
+; RUN: | FileCheck %s --implicit-check-not=DBG_
+
+;; Similarly to untagged-store-assignment-outside-variable.ll this test checks
+;; that out of bounds stores that have no DIAssignID are interpreted correctly
+;; (see inline comments and checks). Hand-written IR.
+
+target triple = "x86_64-unknown-linux-gnu"
+
+declare dso_local void @a(i32)
+
+define dso_local void @b() local_unnamed_addr !dbg !14 {
+entry:
+  %c = alloca [4 x i16], align 8, !DIAssignID !24
+  %arrayidx = getelementptr inbounds [4 x i16], ptr %c, i64 0, i64 2
+  call void @llvm.dbg.assign(metadata i1 undef, metadata !18, metadata !DIExpression(DW_OP_LLVM_fragment, 128, 32), metadata !24, metadata ptr %arrayidx, metadata !DIExpression()), !dbg !26
+
+;; Set variable value to create a non-stack DBG_VALUE.
+; CHECK: DBG_VALUE 0, $noreg, ![[#]], !DIExpression(DW_OP_LLVM_fragment, 128, 32)
+  call void @llvm.dbg.assign(metadata i64 0, metadata !18, metadata !DIExpression(DW_OP_LLVM_fragment, 128, 32), metadata !29, metadata ptr %c, metadata !DIExpression()), !dbg !26
+
+;; Trim assignment that leaks outside alloca (upper 32 bits don't fit inside %c alloca).
+; CHECK: DBG_VALUE %stack.0.c, $noreg, ![[#]], !DIExpression(DW_OP_plus_uconst, 4, DW_OP_deref, DW_OP_LLVM_fragment, 128, 32)
+  store i64 1, ptr %arrayidx, align 4
+;; Set variable value (use a call to prevent eliminating redundant DBG_VALUEs).
+; CHECK: DBG_VALUE 10, $noreg, ![[#]], !DIExpression(DW_OP_LLVM_fragment, 128, 32)
+  call void @a(i32 1)
+  call void @llvm.dbg.assign(metadata i64 10, metadata !18, metadata !DIExpression(DW_OP_LLVM_fragment, 128, 32), metadata !29, metadata ptr %c, metadata !DIExpression()), !dbg !26
+
+;; Trim assignment that doesn't align with fragment start and leaks outside
+;; alloca (16 bit offset from fragment start, upper 48 bits don't fit inside %c
+;; alloca).
+; CHECK: DBG_VALUE %stack.0.c, $noreg, ![[#]], !DIExpression(DW_OP_plus_uconst, 6, DW_OP_deref, DW_OP_LLVM_fragment, 144, 16)
+  %arrayidx1 = getelementptr inbounds [4 x i16], ptr %c, i64 0, i64 3
+  store i64 2, ptr %arrayidx1, align 4
+;; Set variable value (use a call to prevent eliminating redundant DBG_VALUEs).
+; CHECK: DBG_VALUE 20, $noreg, ![[#]], !DIExpression(DW_OP_LLVM_fragment, 128, 32)
+  call void @a(i32 2)
+  call void @llvm.dbg.assign(metadata i64 20, metadata !18, metadata !DIExpression(DW_OP_LLVM_fragment, 128, 32), metadata !29, metadata ptr %c, metadata !DIExpression()), !dbg !26
+
+;; Negative accesses are skipped.
+  %arrayidx2 = getelementptr inbounds [4 x i16], ptr %c, i64 0, i64 -1
+  store i128 3, ptr %arrayidx2, align 4
+;; Set variable value (use a call to prevent eliminating redundant DBG_VALUEs).
+; CHECK: DBG_VALUE 30, $noreg, ![[#]], !DIExpression(DW_OP_LLVM_fragment, 128, 32)
+  call void @a(i32 3)
+  call void @llvm.dbg.assign(metadata i64 30, metadata !18, metadata !DIExpression(DW_OP_LLVM_fragment, 128, 32), metadata !29, metadata ptr %c, metadata !DIExpression()), !dbg !26
+
+;; Skip assignment outside base variable fragment.
+  store i32 4, ptr %c, align 4
+;; Set variable value (use a call to prevent eliminating redundant DBG_VALUEs).
+; CHECK: DBG_VALUE 40, $noreg, ![[#]], !DIExpression(DW_OP_LLVM_fragment, 128, 32)
+  call void @a(i32 4)
+  call void @llvm.dbg.assign(metadata i64 40, metadata !18, metadata !DIExpression(DW_OP_LLVM_fragment, 128, 32), metadata !29, metadata ptr %c, metadata !DIExpression()), !dbg !26
+
+;; Trim partial overlap (lower 32 bits of store don't intersect base fragment
+;; and upper 64 bits don't actually fit inside the alloca).
+  store i128 5, ptr %c, align 4
+; CHECK: DBG_VALUE %stack.0.c, $noreg, ![[#]], !DIExpression(DW_OP_deref, DW_OP_LLVM_fragment, 128, 32)
+;; Set variable value (use a call to prevent eliminating redundant DBG_VALUEs).
+; CHECK: DBG_VALUE 50, $noreg, ![[#]], !DIExpression(DW_OP_LLVM_fragment, 128, 32)
+  call void @a(i32 5)
+  call void @llvm.dbg.assign(metadata i64 50, metadata !18, metadata !DIExpression(DW_OP_LLVM_fragment, 128, 32), metadata !29, metadata ptr %c, metadata !DIExpression()), !dbg !26
+
+  ret void
+}
+
+declare void @llvm.dbg.assign(metadata, metadata, metadata, metadata, metadata, metadata) #2
+declare void @llvm.dbg.value(metadata, metadata, metadata) #3
+
+!llvm.dbg.cu = !{!2}
+!llvm.module.flags = !{!6, !7, !12}
+!llvm.ident = !{!13}
+
+!2 = distinct !DICompileUnit(language: DW_LANG_C11, file: !3, producer: "clang version 17.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, globals: !4, splitDebugInlining: false, nameTableKind: None)
+!3 = !DIFile(filename: "test.c", directory: "/")
+!4 = !{}
+!5 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!6 = !{i32 7, !"Dwarf Version", i32 5}
+!7 = !{i32 2, !"Debug Info Version", i32 3}
+!12 = !{i32 7, !"debug-info-assignment-tracking", i1 true}
+!13 = !{!"clang version 17.0.0"}
+!14 = distinct !DISubprogram(name: "b", scope: !3, file: !3, line: 2, type: !15, scopeLine: 2, flags: DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !17)
+!15 = !DISubroutineType(types: !16)
+!16 = !{null}
+!17 = !{!18}
+!18 = !DILocalVariable(name: "c", scope: !14, file: !3, line: 3, type: !19)
+!19 = !DICompositeType(tag: DW_TAG_array_type, baseType: !5, size: 160, elements: !20)
+!20 = !{!21}
+!21 = !DISubrange(count: 2)
+!24 = distinct !DIAssignID()
+!26 = !DILocation(line: 0, scope: !14)
+!29 = distinct !DIAssignID()

diff  --git a/llvm/test/DebugInfo/assignment-tracking/X86/untagged-store-assignment-outside-variable.ll b/llvm/test/DebugInfo/assignment-tracking/X86/untagged-store-assignment-outside-variable.ll
new file mode 100644
index 000000000000000..3a7c528d320a738
--- /dev/null
+++ b/llvm/test/DebugInfo/assignment-tracking/X86/untagged-store-assignment-outside-variable.ll
@@ -0,0 +1,97 @@
+; RUN: llc %s -stop-after=finalize-isel -o - \
+; RUN: | FileCheck %s --implicit-check-not=DBG_
+
+;; Generated from following C source that contains UB (read and write to
+;; out of bounds static array element.
+;; int a;
+;; void b() {
+;;   int c[2] = {0, 0};
+;;   __attribute__((nodebug)) unsigned d = -1;
+;;   if (a)
+;;     c[a] = c[d] &= a;
+;;   b();
+;; }
+;;
+;; $ clang -O1 -g test.c -emit-llvm -S -o -
+;;
+;; Check the assignment c[d] isn't tracked (--implicit-check-not and
+;; no assertion triggered, see llvm.org/PR65004).
+
+; CHECK: bb.1.tailrecurse:
+; CHECK: DBG_VALUE $noreg, $noreg, !18, !DIExpression()
+; CHECK: DBG_VALUE %stack.0.c, $noreg, !18, !DIExpression(DW_OP_deref)
+; CHECK: bb.2.if.then:
+
+target triple = "x86_64-unknown-linux-gnu"
+
+ at a = dso_local local_unnamed_addr global i32 0, align 4, !dbg !0
+
+define dso_local void @b() local_unnamed_addr !dbg !14 {
+entry:
+  %c = alloca [2 x i32], align 8, !DIAssignID !22
+  br label %tailrecurse, !dbg !23
+
+tailrecurse:                                      ; preds = %if.end, %entry
+  call void @llvm.dbg.assign(metadata i1 undef, metadata !18, metadata !DIExpression(), metadata !22, metadata ptr %c, metadata !DIExpression()), !dbg !24
+  store i64 0, ptr %c, align 8, !dbg !26, !DIAssignID !27
+  call void @llvm.dbg.assign(metadata i64 0, metadata !18, metadata !DIExpression(), metadata !27, metadata ptr %c, metadata !DIExpression()), !dbg !24
+  %0 = load i32, ptr @a, align 4, !dbg !28
+  %tobool.not = icmp eq i32 %0, 0, !dbg !28
+  br i1 %tobool.not, label %if.end, label %if.then, !dbg !34
+
+if.then:                                          ; preds = %tailrecurse
+  %arrayidx = getelementptr inbounds [2 x i32], ptr %c, i64 0, i64 4294967295, !dbg !35
+  %1 = load i32, ptr %arrayidx, align 4, !dbg !36
+  %and = and i32 %1, %0, !dbg !36
+  store i32 %and, ptr %arrayidx, align 4, !dbg !36
+  %idxprom1 = sext i32 %0 to i64, !dbg !37
+  %arrayidx2 = getelementptr inbounds [2 x i32], ptr %c, i64 0, i64 %idxprom1, !dbg !37
+  store i32 %and, ptr %arrayidx2, align 4, !dbg !38
+  br label %if.end, !dbg !37
+
+if.end:                                           ; preds = %if.then, %tailrecurse
+  br label %tailrecurse, !dbg !23
+}
+
+declare void @llvm.dbg.assign(metadata, metadata, metadata, metadata, metadata, metadata)
+
+!llvm.dbg.cu = !{!2}
+!llvm.module.flags = !{!6, !7, !8, !9, !10, !11, !12}
+!llvm.ident = !{!13}
+
+!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression())
+!1 = distinct !DIGlobalVariable(name: "a", scope: !2, file: !3, line: 1, type: !5, isLocal: false, isDefinition: true)
+!2 = distinct !DICompileUnit(language: DW_LANG_C11, file: !3, producer: "clang version 17.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, globals: !4, splitDebugInlining: false, nameTableKind: None)
+!3 = !DIFile(filename: "test.c", directory: "/")
+!4 = !{!0}
+!5 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!6 = !{i32 7, !"Dwarf Version", i32 5}
+!7 = !{i32 2, !"Debug Info Version", i32 3}
+!8 = !{i32 1, !"wchar_size", i32 4}
+!9 = !{i32 8, !"PIC Level", i32 2}
+!10 = !{i32 7, !"PIE Level", i32 2}
+!11 = !{i32 7, !"uwtable", i32 2}
+!12 = !{i32 7, !"debug-info-assignment-tracking", i1 true}
+!13 = !{!"clang version 17.0.0"}
+!14 = distinct !DISubprogram(name: "b", scope: !3, file: !3, line: 2, type: !15, scopeLine: 2, flags: DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !17)
+!15 = !DISubroutineType(types: !16)
+!16 = !{null}
+!17 = !{!18}
+!18 = !DILocalVariable(name: "c", scope: !14, file: !3, line: 3, type: !19)
+!19 = !DICompositeType(tag: DW_TAG_array_type, baseType: !5, size: 64, elements: !20)
+!20 = !{!21}
+!21 = !DISubrange(count: 2)
+!22 = distinct !DIAssignID()
+!23 = !DILocation(line: 7, column: 3, scope: !14)
+!24 = !DILocation(line: 0, scope: !14)
+!25 = !DILocation(line: 3, column: 3, scope: !14)
+!26 = !DILocation(line: 3, column: 7, scope: !14)
+!27 = distinct !DIAssignID()
+!28 = !DILocation(line: 5, column: 7, scope: !29)
+!29 = distinct !DILexicalBlock(scope: !14, file: !3, line: 5, column: 7)
+!34 = !DILocation(line: 5, column: 7, scope: !14)
+!35 = !DILocation(line: 6, column: 12, scope: !29)
+!36 = !DILocation(line: 6, column: 17, scope: !29)
+!37 = !DILocation(line: 6, column: 5, scope: !29)
+!38 = !DILocation(line: 6, column: 10, scope: !29)
+!39 = !DILocation(line: 8, column: 1, scope: !14)


        


More information about the llvm-commits mailing list