llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-compiler-rt-sanitizer Author: Joseph Huber (jhuber6) <details> <summary>Changes</summary> Summary: Add an LLVM ConcurrencySanitizer pass to instrument memory and atomic operations for a sampling race detector. Fundamentally the pass iterates through every candidate memory operation and adds a check above it. Unlike TSAN, we do not replace any operations, which heavily simplifies the instrumentation. We do not re-use the TSAN instrumentaiton because the appraoches are different enough and the passes simple enough that the extra code is not a large concern. More differences will be added in the future, so keeping them separate is easlier. --- <sub>Stack created with <a href="https://github.com/github/gh-stack">GitHub Stacks CLI</a> • <a href="https://gh.io/stacks-feedback">Give Feedback 💬</a></sub> --- Patch is 52.25 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/225780.diff 16 Files Affected: - (added) llvm/include/llvm/Transforms/Instrumentation/ConcurrencySanitizer.h (+29) - (modified) llvm/lib/Passes/PassBuilder.cpp (+1) - (modified) llvm/lib/Passes/PassRegistry.def (+2) - (modified) llvm/lib/Transforms/Instrumentation/CMakeLists.txt (+1) - (added) llvm/lib/Transforms/Instrumentation/ConcurrencySanitizer.cpp (+614) - (added) llvm/test/Instrumentation/ConcurrencySanitizer/address_space.ll (+106) - (added) llvm/test/Instrumentation/ConcurrencySanitizer/atomic.ll (+86) - (added) llvm/test/Instrumentation/ConcurrencySanitizer/basic.ll (+49) - (added) llvm/test/Instrumentation/ConcurrencySanitizer/callbacks.ll (+72) - (added) llvm/test/Instrumentation/ConcurrencySanitizer/function_control.ll (+59) - (added) llvm/test/Instrumentation/ConcurrencySanitizer/memintrinsics.ll (+46) - (added) llvm/test/Instrumentation/ConcurrencySanitizer/module.ll (+12) - (added) llvm/test/Instrumentation/ConcurrencySanitizer/no_sanitize.ll (+57) - (added) llvm/test/Instrumentation/ConcurrencySanitizer/selection.ll (+131) - (added) llvm/test/Instrumentation/ConcurrencySanitizer/shared_alloca.ll (+23) - (added) llvm/test/Instrumentation/ConcurrencySanitizer/unusual_sizes.ll (+33) ``````````diff diff --git a/llvm/include/llvm/Transforms/Instrumentation/ConcurrencySanitizer.h b/llvm/include/llvm/Transforms/Instrumentation/ConcurrencySanitizer.h new file mode 100644 index 0000000000000..9bf3b8bde6a13 --- /dev/null +++ b/llvm/include/llvm/Transforms/Instrumentation/ConcurrencySanitizer.h @@ -0,0 +1,29 @@ +//===- ConcurrencySanitizer.h - ConcurrencySanitizer ------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TRANSFORMS_INSTRUMENTATION_CONCURRENCYSANITIZER_H +#define LLVM_TRANSFORMS_INSTRUMENTATION_CONCURRENCYSANITIZER_H + +#include "llvm/IR/PassManager.h" +#include "llvm/Support/Compiler.h" + +namespace llvm { + +struct ConcurrencySanitizerPass + : public RequiredPassInfoMixin<ConcurrencySanitizerPass> { + LLVM_ABI PreservedAnalyses run(Function &F, FunctionAnalysisManager &FAM); +}; + +struct ModuleConcurrencySanitizerPass + : public RequiredPassInfoMixin<ModuleConcurrencySanitizerPass> { + LLVM_ABI PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM); +}; + +} // namespace llvm + +#endif // LLVM_TRANSFORMS_INSTRUMENTATION_CONCURRENCYSANITIZER_H diff --git a/llvm/lib/Passes/PassBuilder.cpp b/llvm/lib/Passes/PassBuilder.cpp index 858c76706427e..d2c25e85beeb4 100644 --- a/llvm/lib/Passes/PassBuilder.cpp +++ b/llvm/lib/Passes/PassBuilder.cpp @@ -274,6 +274,7 @@ #include "llvm/Transforms/Instrumentation/AllocToken.h" #include "llvm/Transforms/Instrumentation/BoundsChecking.h" #include "llvm/Transforms/Instrumentation/CGProfile.h" +#include "llvm/Transforms/Instrumentation/ConcurrencySanitizer.h" #include "llvm/Transforms/Instrumentation/ControlHeightReduction.h" #include "llvm/Transforms/Instrumentation/CopyProf.h" #include "llvm/Transforms/Instrumentation/DataFlowSanitizer.h" diff --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def index 33c9e19988d7a..3913d0aaf75d4 100644 --- a/llvm/lib/Passes/PassRegistry.def +++ b/llvm/lib/Passes/PassRegistry.def @@ -67,6 +67,7 @@ MODULE_PASS("coro-cleanup", CoroCleanupPass()) MODULE_PASS("coro-early", CoroEarlyPass()) MODULE_PASS("lower-comment-string", LowerCommentStringPass()) MODULE_PASS("cross-dso-cfi", CrossDSOCFIPass()) +MODULE_PASS("csan-module", ModuleConcurrencySanitizerPass()) MODULE_PASS("ctx-instr-gen", PGOInstrumentationGen(PGOInstrumentationType::CTXPROF)) MODULE_PASS("ctx-prof-flatten", @@ -429,6 +430,7 @@ FUNCTION_PASS("copyprof-stores", CopyProfStoresPass()) FUNCTION_PASS("coro-elide", CoroElidePass()) FUNCTION_PASS("correlated-propagation", CorrelatedValuePropagationPass()) FUNCTION_PASS("count-visits", CountVisitsPass()) +FUNCTION_PASS("csan", ConcurrencySanitizerPass()) FUNCTION_PASS("dce", DCEPass()) FUNCTION_PASS("declare-to-assign", llvm::AssignmentTrackingPass()) FUNCTION_PASS("dfa-jump-threading", DFAJumpThreadingPass()) diff --git a/llvm/lib/Transforms/Instrumentation/CMakeLists.txt b/llvm/lib/Transforms/Instrumentation/CMakeLists.txt index 7d2e658acb5c1..4d08d0fa42184 100644 --- a/llvm/lib/Transforms/Instrumentation/CMakeLists.txt +++ b/llvm/lib/Transforms/Instrumentation/CMakeLists.txt @@ -25,6 +25,7 @@ add_llvm_component_library(LLVMInstrumentation SanitizerBinaryMetadata.cpp ValueProfileCollector.cpp ThreadSanitizer.cpp + ConcurrencySanitizer.cpp TypeSanitizer.cpp HWAddressSanitizer.cpp RealtimeSanitizer.cpp diff --git a/llvm/lib/Transforms/Instrumentation/ConcurrencySanitizer.cpp b/llvm/lib/Transforms/Instrumentation/ConcurrencySanitizer.cpp new file mode 100644 index 0000000000000..0a14d67f9f5bc --- /dev/null +++ b/llvm/lib/Transforms/Instrumentation/ConcurrencySanitizer.cpp @@ -0,0 +1,614 @@ +//===- ConcurrencySanitizer.cpp - watchpoint race detector ----------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements instrumentation for ConcurrencySanitizer, a sampling +// data-race detector. Memory accesses are preceded by runtime probes while the +// original operations remain unchanged. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Transforms/Instrumentation/ConcurrencySanitizer.h" +#include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/Statistic.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/bit.h" +#include "llvm/Analysis/CaptureTracking.h" +#include "llvm/Analysis/TargetLibraryInfo.h" +#include "llvm/Analysis/ValueTracking.h" +#include "llvm/IR/DataLayout.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/IntrinsicInst.h" +#include "llvm/IR/Intrinsics.h" +#include "llvm/IR/Module.h" +#include "llvm/ProfileData/InstrProf.h" +#include "llvm/Support/AMDGPUAddrSpace.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/NVPTXAddrSpace.h" +#include "llvm/TargetParser/Triple.h" +#include "llvm/Transforms/Utils/EscapeEnumerator.h" +#include "llvm/Transforms/Utils/Instrumentation.h" +#include "llvm/Transforms/Utils/Local.h" +#include "llvm/Transforms/Utils/ModuleUtils.h" + +using namespace llvm; + +#define DEBUG_TYPE "csan" + +static cl::opt<bool> ClInstrumentMemoryAccesses( + "csan-instrument-memory-accesses", cl::init(true), + cl::desc("Instrument memory accesses"), cl::Hidden); +static cl::opt<bool> + ClInstrumentFuncEntryExit("csan-instrument-func-entry-exit", cl::init(true), + cl::desc("Instrument function entry and exit"), + cl::Hidden); +static cl::opt<bool> ClHandleCxxExceptions( + "csan-handle-cxx-exceptions", cl::init(true), + cl::desc("Handle C++ exceptions (insert cleanup blocks for unwinding)"), + cl::Hidden); +static cl::opt<bool> ClInstrumentAtomics("csan-instrument-atomics", + cl::init(true), + cl::desc("Instrument atomics"), + cl::Hidden); +static cl::opt<bool> ClInstrumentMemIntrinsics( + "csan-instrument-memintrinsics", cl::init(true), + cl::desc("Instrument memintrinsics (memset/memcpy/memmove)"), cl::Hidden); +static cl::opt<bool> ClDistinguishVolatile( + "csan-distinguish-volatile", cl::init(false), + cl::desc("Emit special instrumentation for accesses to volatiles"), + cl::Hidden); +STATISTIC(NumInstrumentedReads, "Number of instrumented reads"); +STATISTIC(NumInstrumentedWrites, "Number of instrumented writes"); +STATISTIC(NumOmittedReadsBeforeWrite, + "Number of reads ignored due to following writes"); + +static constexpr char kCsanModuleCtorName[] = "csan.module_ctor"; +static constexpr char kCsanInitName[] = "__csan_init"; + +namespace { + +/// Must match CSAN_ACCESS_* in compiler-rt/lib/csan/csan_defs.h. +enum AccessFlags : unsigned { + AF_None = 0, + AF_Atomic = 1u << 0, + AF_Compound = 1u << 1, +}; + +static bool isAtomicMemoryAccess(const Instruction *I) { + auto SSID = getAtomicSyncScopeID(I); + if (!SSID) + return false; + if (isa<LoadInst>(I) || isa<StoreInst>(I)) + return *SSID != SyncScope::SingleThread; + return true; +} + +static Value *getCallbackAddress(IRBuilderBase &IRB, Value *Addr) { + return IRB.CreateAddrSpaceCast(Addr, IRB.getPtrTy()); +} + +static ConstantInt *createOrdering(IRBuilderBase &IRB, AtomicOrdering Ord) { + uint32_t Value = 0; + switch (Ord) { + case AtomicOrdering::NotAtomic: + llvm_unreachable("unexpected atomic ordering"); + case AtomicOrdering::Unordered: + case AtomicOrdering::Monotonic: + Value = 0; + break; + case AtomicOrdering::Acquire: + Value = 2; + break; + case AtomicOrdering::Release: + Value = 3; + break; + case AtomicOrdering::AcquireRelease: + Value = 4; + break; + case AtomicOrdering::SequentiallyConsistent: + Value = 5; + break; + } + return IRB.getInt32(Value); +} + +static int getAccessSizeIndex(Type *Ty, const DataLayout &DL) { + assert(Ty->isSized()); + if (Ty->isScalableTy()) + return -1; + uint32_t TypeSize = DL.getTypeStoreSizeInBits(Ty); + if (TypeSize != 8 && TypeSize != 16 && TypeSize != 32 && TypeSize != 64 && + TypeSize != 128) + return -1; + unsigned Idx = llvm::countr_zero(TypeSize / 8); + return static_cast<int>(Idx); +} + +static bool addressSpaceMayRace(const Triple &T, unsigned AS) { + if (T.isAMDGPU()) + // GDS and buffer fat pointers cannot form a generic watchpoint key. + return AS == AMDGPUAS::FLAT_ADDRESS || AS == AMDGPUAS::GLOBAL_ADDRESS || + AS == AMDGPUAS::LOCAL_ADDRESS; + if (T.isNVPTX()) + return AS == NVPTXAS::ADDRESS_SPACE_GENERIC || + AS == NVPTXAS::ADDRESS_SPACE_GLOBAL || + AS == NVPTXAS::ADDRESS_SPACE_SHARED || + AS == NVPTXAS::ADDRESS_SPACE_SHARED_CLUSTER; + if (T.isSPIRV()) + // FIXME: No exposed address spaces for SPIR-V. + return false; + return AS == 0; +} + +struct ConcurrencySanitizer { + bool sanitizeFunction(Function &F, const TargetLibraryInfo &TLI); + +private: + struct MemoryAccessLists { + SmallVector<Instruction *, 8> LoadsAndStores; + SmallVector<Instruction *, 8> AtomicAccesses; + SmallVector<MemIntrinsic *, 8> MemIntrinCalls; + bool HasCalls = false; + }; + + void initialize(Module &M, const TargetLibraryInfo &TLI); + void collectMemoryAccesses(Function &F, MemoryAccessLists &Out); + bool instrumentLoadOrStore(Instruction *I, const DataLayout &DL); + bool instrumentAtomic(Instruction *I, const DataLayout &DL); + bool instrumentMemIntrinsic(MemIntrinsic *M); + bool insertAccessProbe(Instruction *I, Value *Addr, Type *AccessTy, + const DataLayout &DL, bool IsWrite, bool IsCompound, + bool IsAtomic); + bool shouldInstrumentAddress(Value *Addr) const; + bool shouldInstrumentAccess(Instruction *I) const; + void insertFuncEntryExit(Function &F); + void insertRuntimeIgnores(Function &F); + + Module *Mod = nullptr; + Type *IntptrTy = nullptr; + IntegerType *FlagsTy = nullptr; + + // Accesses sizes are powers of two: 1, 2, 4, 8, 16. + static const size_t kNumAccessSizes = 5; + // void __csan_readN(ptr, i32); + FunctionCallee CsanRead[kNumAccessSizes]; + // void __csan_writeN(ptr, i32); + FunctionCallee CsanWrite[kNumAccessSizes]; + // void __csan_unaligned_readN(ptr, i32); + FunctionCallee CsanUnalignedRead[kNumAccessSizes]; + // void __csan_unaligned_writeN(ptr, i32); + FunctionCallee CsanUnalignedWrite[kNumAccessSizes]; + // void __csan_volatile_readN(ptr, i32); + FunctionCallee CsanVolatileRead[kNumAccessSizes]; + // void __csan_volatile_writeN(ptr, i32); + FunctionCallee CsanVolatileWrite[kNumAccessSizes]; + // void __csan_unaligned_volatile_readN(ptr, i32); + FunctionCallee CsanUnalignedVolatileRead[kNumAccessSizes]; + // void __csan_unaligned_volatile_writeN(ptr, i32); + FunctionCallee CsanUnalignedVolatileWrite[kNumAccessSizes]; + // void __csan_read_writeN(ptr, i32); + FunctionCallee CsanCompoundRW[kNumAccessSizes]; + // void __csan_unaligned_read_writeN(ptr, i32); + FunctionCallee CsanUnalignedCompoundRW[kNumAccessSizes]; + // void __csan_func_entry(ptr); + FunctionCallee CsanFuncEntry; + // void __csan_func_exit(); + FunctionCallee CsanFuncExit; + // void __csan_ignore_thread_begin(); + FunctionCallee CsanIgnoreBegin; + // void __csan_ignore_thread_end(); + FunctionCallee CsanIgnoreEnd; + // void __csan_read_range(ptr, intptr_t, i32); + FunctionCallee CsanReadRange; + // void __csan_write_range(ptr, intptr_t, i32); + FunctionCallee CsanWriteRange; + // void __csan_atomic_thread_fence(i32); + FunctionCallee CsanAtomicThreadFence; + // void __csan_atomic_signal_fence(i32); + FunctionCallee CsanAtomicSignalFence; +}; + +void insertModuleCtor(Module &M) { + getOrCreateSanitizerCtorAndInitFunctions( + M, kCsanModuleCtorName, kCsanInitName, /*InitArgTypes=*/{}, + /*InitArgs=*/{}, + [&](Function *Ctor, FunctionCallee) { appendToGlobalCtors(M, Ctor, 0); }); +} + +} // namespace + +PreservedAnalyses ConcurrencySanitizerPass::run(Function &F, + FunctionAnalysisManager &FAM) { + ConcurrencySanitizer CSan; + if (CSan.sanitizeFunction(F, FAM.getResult<TargetLibraryAnalysis>(F))) + return PreservedAnalyses::none(); + return PreservedAnalyses::all(); +} + +PreservedAnalyses ModuleConcurrencySanitizerPass::run(Module &M, + ModuleAnalysisManager &) { + if (checkIfAlreadyInstrumented(M, "nosanitize_concurrency")) + return PreservedAnalyses::all(); + insertModuleCtor(M); + return PreservedAnalyses::none(); +} + +void ConcurrencySanitizer::initialize(Module &M, const TargetLibraryInfo &TLI) { + LLVMContext &Ctx = M.getContext(); + Mod = &M; + IntptrTy = M.getDataLayout().getIntPtrType(Ctx); + FlagsTy = Type::getInt32Ty(Ctx); + + AttributeList Attr = AttributeList().addFnAttribute(Ctx, Attribute::NoUnwind); + IRBuilder<> IRB(Ctx); + Type *VoidTy = IRB.getVoidTy(); + Type *PtrTy = IRB.getPtrTy(); + CsanFuncEntry = + M.getOrInsertFunction("__csan_func_entry", Attr, VoidTy, PtrTy); + CsanFuncExit = M.getOrInsertFunction("__csan_func_exit", Attr, VoidTy); + CsanIgnoreBegin = + M.getOrInsertFunction("__csan_ignore_thread_begin", Attr, VoidTy); + CsanIgnoreEnd = + M.getOrInsertFunction("__csan_ignore_thread_end", Attr, VoidTy); + for (unsigned I = 0; I < kNumAccessSizes; ++I) { + std::string ByteSize = utostr(1U << I); + auto AccessFn = [&](const Twine &Name) { + return M.getOrInsertFunction(("__csan_" + Name).str(), Attr, VoidTy, + PtrTy, FlagsTy); + }; + CsanRead[I] = AccessFn("read" + ByteSize); + CsanWrite[I] = AccessFn("write" + ByteSize); + CsanUnalignedRead[I] = AccessFn("unaligned_read" + ByteSize); + CsanUnalignedWrite[I] = AccessFn("unaligned_write" + ByteSize); + CsanVolatileRead[I] = AccessFn("volatile_read" + ByteSize); + CsanVolatileWrite[I] = AccessFn("volatile_write" + ByteSize); + CsanUnalignedVolatileRead[I] = + AccessFn("unaligned_volatile_read" + ByteSize); + CsanUnalignedVolatileWrite[I] = + AccessFn("unaligned_volatile_write" + ByteSize); + CsanCompoundRW[I] = AccessFn("read_write" + ByteSize); + CsanUnalignedCompoundRW[I] = AccessFn("unaligned_read_write" + ByteSize); + } + IntegerType *OrdTy = IRB.getInt32Ty(); + CsanReadRange = M.getOrInsertFunction("__csan_read_range", Attr, VoidTy, + PtrTy, IntptrTy, FlagsTy); + CsanWriteRange = M.getOrInsertFunction("__csan_write_range", Attr, VoidTy, + PtrTy, IntptrTy, FlagsTy); + CsanAtomicThreadFence = M.getOrInsertFunction( + "__csan_atomic_thread_fence", + TLI.getAttrList(&Ctx, {0}, /*Signed=*/true, /*Ret=*/false, Attr), VoidTy, + OrdTy); + CsanAtomicSignalFence = M.getOrInsertFunction( + "__csan_atomic_signal_fence", + TLI.getAttrList(&Ctx, {0}, /*Signed=*/true, /*Ret=*/false, Attr), VoidTy, + OrdTy); +} + +bool ConcurrencySanitizer::sanitizeFunction(Function &F, + const TargetLibraryInfo &TLI) { + // This is required to prevent instrumenting call to __csan_init from within + // the module constructor. + if (F.getName() == kCsanModuleCtorName) + return false; + // Naked functions can not have prologue/epilogue + // (__csan_func_entry/__csan_func_exit) generated, so don't instrument them at + // all. + if (F.hasFnAttribute(Attribute::Naked)) + return false; + + // __attribute__(disable_sanitizer_instrumentation) prevents all kinds of + // instrumentation. + if (F.hasFnAttribute(Attribute::DisableSanitizerInstrumentation)) + return false; + + Mod = F.getParent(); + MemoryAccessLists Acc; + collectMemoryAccesses(F, Acc); + + const bool SuppressChecking = + F.hasFnAttribute("sanitize_concurrency_no_checking_at_run_time"); + const bool SanitizeFunction = + F.hasFnAttribute(Attribute::SanitizeConcurrency) && !SuppressChecking; + const bool MayInstrument = + SanitizeFunction && + ((ClInstrumentMemoryAccesses && !Acc.LoadsAndStores.empty()) || + (ClInstrumentAtomics && !Acc.AtomicAccesses.empty()) || + (ClInstrumentMemIntrinsics && !Acc.MemIntrinCalls.empty())); + const bool NeedsRuntimeIgnores = SuppressChecking && Acc.HasCalls; + const bool NeedsFuncEntryExit = ClInstrumentFuncEntryExit && Acc.HasCalls; + if (!MayInstrument && !NeedsRuntimeIgnores && !NeedsFuncEntryExit) + return false; + + initialize(*Mod, TLI); + bool Res = false; + const DataLayout &DL = F.getDataLayout(); + + if (ClInstrumentMemoryAccesses && SanitizeFunction) + for (Instruction *I : Acc.LoadsAndStores) + Res |= instrumentLoadOrStore(I, DL); + + if (ClInstrumentAtomics && SanitizeFunction) + for (Instruction *I : Acc.AtomicAccesses) + Res |= instrumentAtomic(I, DL); + + if (ClInstrumentMemIntrinsics && SanitizeFunction) + for (MemIntrinsic *MI : Acc.MemIntrinCalls) + Res |= instrumentMemIntrinsic(MI); + + if (NeedsRuntimeIgnores) { + insertRuntimeIgnores(F); + Res = true; + } + + if ((Res || Acc.HasCalls) && ClInstrumentFuncEntryExit) { + insertFuncEntryExit(F); + Res = true; + } + // Callback declarations may have changed the module. + return true; +} + +bool ConcurrencySanitizer::shouldInstrumentAddress(Value *Addr) const { + Value *BaseAddr = Addr->stripInBoundsOffsets(); + if (auto *GV = dyn_cast<GlobalVariable>(BaseAddr)) { + if (GV->hasSection()) { + StringRef SectionName = GV->getSection(); + auto OF = Mod->getTargetTriple().getObjectFormat(); + if (SectionName.ends_with( + getInstrProfSectionName(IPSK_cnts, OF, /*AddSegmentInfo=*/false))) + return false; + } + } + + Type *PtrTy = cast<PointerType>(Addr->getType()->getScalarType()); + unsigned AS = PtrTy->getPointerAddressSpace(); + if (Mod->getDataLayout().getPointerSizeInBits(AS) > 64) + return false; + return addressSpaceMayRace(Mod->getTargetTriple(), AS); +} + +bool ConcurrencySanitizer::shouldInstrumentAccess(Instruction *I) const { + const bool IsWrite = isa<StoreInst>(I); + Value *Addr = getLoadStorePointerOperand(I); + if (!shouldInstrumentAddress(Addr)) + return false; + + if (!IsWrite) + if (auto *GV = dyn_cast<GlobalVariable>(getUnderlyingObject(Addr))) + if (GV->isConstant()) + return false; + + const AllocaInst *AI = findAllocaForValue(Addr); + if (!AI || PointerMayBeCaptured(AI, /*ReturnCaptures=*/true)) + return true; + + const Triple &T = AI->getModule()->getTargetTriple(); + return T.isGPU() && addressSpaceMayRace(T, AI->getAddressSpace()); +} + +void ConcurrencySanitizer::collectMemoryAccesses(Function &F, + MemoryAccessLists &Out) { + SmallVector<Instruction *, 8> LocalLoadsAndStores; + auto FlushLocalAccesses = [&] { + DenseSet<Value *> WriteTargets; + for (Instruction *Inst : reverse(LocalLoadsAndStores)) { + const bool IsWrite = isa<StoreInst>(Inst); + Value *Addr = getLoadStorePointerOperand(Inst); + if (!IsWrite && WriteTargets.contains(Addr)) { + ++NumOmittedReadsBeforeWrite; + continue; + } + + Out.LoadsAndStores.push_back(Inst); + if (IsWrite) + WriteTargets.insert(Addr); + } + LocalLoadsAndStores.clear(); + }; + + for (BasicBlock &BB : F) { + for (Instruction &Inst : BB) { + // Skip instructions inserted by another instrumentation. + if (Inst.hasMetadata(LLVMContext::MD_nosanitize)) + continue; + if (isAtomicMemoryAccess(&Inst)) + Out.AtomicAccesses.push_back(&Inst); + else if ((isa<LoadInst>(Inst) || isa<StoreInst>(Inst)) && + shouldInstrumentAccess(&Inst)) + LocalLoadsAndStores.push_back(&Inst); + else if (isa<CallInst>(Inst) || isa<InvokeInst>(Inst)) { + FlushLocalAccesses(); + ... [truncated] `````````` </details> https://github.com/llvm/llvm-project/pull/225780 _______________________________________________ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
