[llvm] 47bdea3 - [ThreadSanitizer] Add fallback DebugLocation for instrumentation calls

Marco Elver via llvm-commits llvm-commits at lists.llvm.org
Thu May 5 06:50:11 PDT 2022


Author: Marco Elver
Date: 2022-05-05T15:21:35+02:00
New Revision: 47bdea3f7eb4bb70288e69635d73807c5d03dacc

URL: https://github.com/llvm/llvm-project/commit/47bdea3f7eb4bb70288e69635d73807c5d03dacc
DIFF: https://github.com/llvm/llvm-project/commit/47bdea3f7eb4bb70288e69635d73807c5d03dacc.diff

LOG: [ThreadSanitizer] Add fallback DebugLocation for instrumentation calls

When building with debug info enabled, some load/store instructions do
not have a DebugLocation attached. When using the default IRBuilder, it
attempts to copy the DebugLocation from the insertion-point instruction.
When there's no DebugLocation, no attempt is made to add one.

This is problematic for inserted calls, where the enclosing function has
debug info but the call ends up without a DebugLocation in e.g. LTO
builds that verify that both the enclosing function and calls to
inlinable functions have debug info attached.

This issue was noticed in Linux kernel KCSAN builds with LTO and debug
info enabled:

  | ...
  | inlinable function call in a function with debug info must have a !dbg location
  |   call void @__tsan_read8(i8* %432)
  | ...

To fix, ensure that all calls to the runtime have a DebugLocation
attached, where the possibility exists that the insertion-point might
not have any DebugLocation attached to it.

Reviewed By: nickdesaulniers

Differential Revision: https://reviews.llvm.org/D124937

Added: 
    llvm/test/Instrumentation/ThreadSanitizer/missing_dbg.ll

Modified: 
    llvm/lib/Transforms/Instrumentation/ThreadSanitizer.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Transforms/Instrumentation/ThreadSanitizer.cpp b/llvm/lib/Transforms/Instrumentation/ThreadSanitizer.cpp
index 53d87583702983..18d5a679c12da6 100644
--- a/llvm/lib/Transforms/Instrumentation/ThreadSanitizer.cpp
+++ b/llvm/lib/Transforms/Instrumentation/ThreadSanitizer.cpp
@@ -133,8 +133,10 @@ struct ThreadSanitizer {
   };
 
   void initialize(Module &M);
-  bool instrumentLoadOrStore(const InstructionInfo &II, const DataLayout &DL);
-  bool instrumentAtomic(Instruction *I, const DataLayout &DL);
+  bool instrumentLoadOrStore(const InstructionInfo &II, const DataLayout &DL,
+                             const Function &F);
+  bool instrumentAtomic(Instruction *I, const DataLayout &DL,
+                        const Function &F);
   bool instrumentMemIntrinsic(Instruction *I);
   void chooseInstructionsToInstrument(SmallVectorImpl<Instruction *> &Local,
                                       SmallVectorImpl<InstructionInfo> &All,
@@ -181,6 +183,27 @@ void insertModuleCtor(Module &M) {
       [&](Function *Ctor, FunctionCallee) { appendToGlobalCtors(M, Ctor, 0); });
 }
 
+// Use to ensure the inserted instrumentation has a DebugLocation; if none is
+// attached to the source instruction, try to use a DILocation with offset 0
+// scoped to surrounding function (if it has a DebugLocation).
+//
+// Some non-call instructions may be missing debug info, but when inserting
+// instrumentation calls, some builds (e.g. LTO) want calls to have debug info
+// if the enclosing function does.
+struct InstrumentationIRBuilder : IRBuilder<> {
+  static void ensureDebugInfo(IRBuilder<> &IRB, const Function &F) {
+    if (IRB.getCurrentDebugLocation())
+      return;
+    if (DISubprogram *SP = F.getSubprogram())
+      IRB.SetCurrentDebugLocation(DILocation::get(SP->getContext(), 0, 0, SP));
+  }
+
+  explicit InstrumentationIRBuilder(Instruction *I, const Function &F)
+      : IRBuilder<>(I) {
+    ensureDebugInfo(*this, F);
+  }
+};
+
 }  // namespace
 
 PreservedAnalyses ThreadSanitizerPass::run(Function &F,
@@ -491,10 +514,11 @@ static bool isTsanAtomic(const Instruction *I) {
 }
 
 void ThreadSanitizer::InsertRuntimeIgnores(Function &F) {
-  IRBuilder<> IRB(F.getEntryBlock().getFirstNonPHI());
+  InstrumentationIRBuilder IRB(F.getEntryBlock().getFirstNonPHI(), F);
   IRB.CreateCall(TsanIgnoreBegin);
   EscapeEnumerator EE(F, "tsan_ignore_cleanup", ClHandleCxxExceptions);
   while (IRBuilder<> *AtExit = EE.Next()) {
+    InstrumentationIRBuilder::ensureDebugInfo(*AtExit, F);
     AtExit->CreateCall(TsanIgnoreEnd);
   }
 }
@@ -554,14 +578,14 @@ bool ThreadSanitizer::sanitizeFunction(Function &F,
   // Instrument memory accesses only if we want to report bugs in the function.
   if (ClInstrumentMemoryAccesses && SanitizeFunction)
     for (const auto &II : AllLoadsAndStores) {
-      Res |= instrumentLoadOrStore(II, DL);
+      Res |= instrumentLoadOrStore(II, DL, F);
     }
 
   // Instrument atomic memory accesses in any case (they can be used to
   // implement synchronization).
   if (ClInstrumentAtomics)
     for (auto Inst : AtomicAccesses) {
-      Res |= instrumentAtomic(Inst, DL);
+      Res |= instrumentAtomic(Inst, DL, F);
     }
 
   if (ClInstrumentMemIntrinsics && SanitizeFunction)
@@ -577,7 +601,7 @@ bool ThreadSanitizer::sanitizeFunction(Function &F,
 
   // Instrument function entry/exit points if there were instrumented accesses.
   if ((Res || HasCalls) && ClInstrumentFuncEntryExit) {
-    IRBuilder<> IRB(F.getEntryBlock().getFirstNonPHI());
+    InstrumentationIRBuilder IRB(F.getEntryBlock().getFirstNonPHI(), F);
     Value *ReturnAddress = IRB.CreateCall(
         Intrinsic::getDeclaration(F.getParent(), Intrinsic::returnaddress),
         IRB.getInt32(0));
@@ -585,6 +609,7 @@ bool ThreadSanitizer::sanitizeFunction(Function &F,
 
     EscapeEnumerator EE(F, "tsan_cleanup", ClHandleCxxExceptions);
     while (IRBuilder<> *AtExit = EE.Next()) {
+      InstrumentationIRBuilder::ensureDebugInfo(*AtExit, F);
       AtExit->CreateCall(TsanFuncExit, {});
     }
     Res = true;
@@ -593,8 +618,9 @@ bool ThreadSanitizer::sanitizeFunction(Function &F,
 }
 
 bool ThreadSanitizer::instrumentLoadOrStore(const InstructionInfo &II,
-                                            const DataLayout &DL) {
-  IRBuilder<> IRB(II.Inst);
+                                            const DataLayout &DL,
+                                            const Function &F) {
+  InstrumentationIRBuilder IRB(II.Inst, F);
   const bool IsWrite = isa<StoreInst>(*II.Inst);
   Value *Addr = IsWrite ? cast<StoreInst>(II.Inst)->getPointerOperand()
                         : cast<LoadInst>(II.Inst)->getPointerOperand();
@@ -722,8 +748,9 @@ bool ThreadSanitizer::instrumentMemIntrinsic(Instruction *I) {
 // The following page contains more background information:
 // http://www.hpl.hp.com/personal/Hans_Boehm/c++mm/
 
-bool ThreadSanitizer::instrumentAtomic(Instruction *I, const DataLayout &DL) {
-  IRBuilder<> IRB(I);
+bool ThreadSanitizer::instrumentAtomic(Instruction *I, const DataLayout &DL,
+                                       const Function &F) {
+  InstrumentationIRBuilder IRB(I, F);
   if (LoadInst *LI = dyn_cast<LoadInst>(I)) {
     Value *Addr = LI->getPointerOperand();
     Type *OrigTy = LI->getType();

diff  --git a/llvm/test/Instrumentation/ThreadSanitizer/missing_dbg.ll b/llvm/test/Instrumentation/ThreadSanitizer/missing_dbg.ll
new file mode 100644
index 00000000000000..3909f5f19a84fd
--- /dev/null
+++ b/llvm/test/Instrumentation/ThreadSanitizer/missing_dbg.ll
@@ -0,0 +1,40 @@
+; RUN: opt < %s -passes=tsan -S | FileCheck %s
+
+target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128"
+
+define i32 @with_dbg(i32* %a) sanitize_thread !dbg !3 {
+entry:
+  %tmp1 = load i32, i32* %a, align 4
+  ret i32 %tmp1
+}
+; CHECK-LABEL: @with_dbg
+; CHECK-NEXT:  entry:
+; CHECK:       call void @__tsan_func_entry(i8* %0), !dbg [[DBG:![0-9]+]]
+; CHECK:       call void @__tsan_read4(i8* %1), !dbg [[DBG]]
+; CHECK:       call void @__tsan_func_exit(), !dbg [[DBG]]
+
+define i32 @without_dbg(i32* %a) sanitize_thread {
+entry:
+  %tmp1 = load i32, i32* %a, align 4
+  ret i32 %tmp1
+}
+; CHECK-LABEL: @without_dbg
+; CHECK-NEXT:  entry:
+; CHECK-NOT:   call void @__tsan_func_entry(i8* %0), !dbg
+; CHECK-NOT:   call void @__tsan_read4(i8* %1), !dbg
+; CHECK-NOT:   call void @__tsan_func_exit(), !dbg
+; CHECK:       call void @__tsan_func_entry(i8* %0)
+; CHECK:       call void @__tsan_read4(i8* %1)
+; CHECK:       call void @__tsan_func_exit()
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!2}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C89, file: !1, producer: "", isOptimized: true, runtimeVersion: 0, emissionKind: LineTablesOnly, splitDebugInlining: false, nameTableKind: None)
+!1 = !DIFile(filename: "foo.c", directory: "")
+!2 = !{i32 2, !"Debug Info Version", i32 3}
+!3 = distinct !DISubprogram(name: "foo", scope: !1, file: !1, line: 190, type: !4, scopeLine: 192, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: !0)
+!4 = !DISubroutineType(types: !5)
+!5 = !{}
+
+; CHECK:       [[DBG]] = !DILocation(line: 0, scope: !3)


        


More information about the llvm-commits mailing list