diff --git a/llvm/lib/Transforms/Instrumentation/ThreadSanitizer.cpp b/llvm/lib/Transforms/Instrumentation/ThreadSanitizer.cpp --- a/llvm/lib/Transforms/Instrumentation/ThreadSanitizer.cpp +++ b/llvm/lib/Transforms/Instrumentation/ThreadSanitizer.cpp @@ -133,8 +133,10 @@ }; 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 &Local, SmallVectorImpl &All, @@ -181,6 +183,27 @@ [&](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 @@ } 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 @@ // 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 @@ // 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 @@ 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::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(*II.Inst); Value *Addr = IsWrite ? cast(II.Inst)->getPointerOperand() : cast(II.Inst)->getPointerOperand(); @@ -722,8 +748,9 @@ // 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(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 --- /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)