[llvm-branch-commits] [clang] [Clang] Add support for the `-fsanitize=concurrency` runtime (PR #225781)
Joseph Huber via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Wed Sep 23 07:38:17 PDT 2026
https://github.com/jhuber6 updated https://github.com/llvm/llvm-project/pull/225781
>From e81d0e5949e69ca672a6cc3529326b073dbb8ece Mon Sep 17 00:00:00 2001
From: Joseph Huber <huberjn at outlook.com>
Date: Mon, 21 Sep 2026 17:34:54 -0500
Subject: [PATCH] [Clang] Add support for the `-fsanitize=concurrency` runtime
Summary:
Add the frontend sanitizer kind, function attributes, pass pipeline
integration, predefined macro, and driver handling for
ConcurrencySanitizer.
---
clang/include/clang/Basic/Features.def | 2 +
clang/include/clang/Basic/Sanitizers.def | 3 +
clang/include/clang/Driver/SanitizerArgs.h | 3 +
clang/lib/CodeGen/BackendUtil.cpp | 7 ++
clang/lib/CodeGen/CGDeclCXX.cpp | 4 +
clang/lib/CodeGen/CodeGenFunction.cpp | 13 ++-
clang/lib/Driver/SanitizerArgs.cpp | 14 +++-
clang/lib/Driver/ToolChains/AMDGPU.cpp | 2 +
clang/lib/Driver/ToolChains/Clang.cpp | 2 +-
clang/lib/Driver/ToolChains/CommonArgs.cpp | 38 +++++++--
clang/lib/Driver/ToolChains/Linux.cpp | 2 +
clang/lib/Frontend/InitPreprocessor.cpp | 2 +
clang/test/CodeGen/sanitize-concurrency.c | 82 +++++++++++++++++++
clang/test/Driver/fsanitize.c | 15 ++++
.../test/Preprocessor/sanitizer-predefines.c | 3 +
15 files changed, 177 insertions(+), 15 deletions(-)
create mode 100644 clang/test/CodeGen/sanitize-concurrency.c
diff --git a/clang/include/clang/Basic/Features.def b/clang/include/clang/Basic/Features.def
index cfd5bf69f938cf..461d8ee576278a 100644
--- a/clang/include/clang/Basic/Features.def
+++ b/clang/include/clang/Basic/Features.def
@@ -146,6 +146,8 @@ FEATURE(memory_sanitizer,
SanitizerKind::KernelMemory))
FEATURE(type_sanitizer, LangOpts.Sanitize.has(SanitizerKind::Type))
FEATURE(thread_sanitizer, LangOpts.Sanitize.has(SanitizerKind::Thread))
+FEATURE(concurrency_sanitizer,
+ LangOpts.Sanitize.has(SanitizerKind::Concurrency))
FEATURE(dataflow_sanitizer, LangOpts.Sanitize.has(SanitizerKind::DataFlow))
FEATURE(scudo, LangOpts.Sanitize.hasOneOf(SanitizerKind::Scudo))
FEATURE(ptrauth_intrinsics, LangOpts.PointerAuthIntrinsics &&
diff --git a/clang/include/clang/Basic/Sanitizers.def b/clang/include/clang/Basic/Sanitizers.def
index da854316250265..8120c5842919b4 100644
--- a/clang/include/clang/Basic/Sanitizers.def
+++ b/clang/include/clang/Basic/Sanitizers.def
@@ -198,6 +198,9 @@ SANITIZER("scudo", Scudo)
// AllocToken
SANITIZER("alloc-token", AllocToken)
+// ConcurrencySanitizer.
+SANITIZER("concurrency", Concurrency)
+
// Magic group, containing all sanitizers. For example, "-fno-sanitize=all"
// can be used to disable all the sanitizers.
SANITIZER_GROUP("all", All, ~SanitizerMask())
diff --git a/clang/include/clang/Driver/SanitizerArgs.h b/clang/include/clang/Driver/SanitizerArgs.h
index 62f154bae3ffd3..9d61165e940094 100644
--- a/clang/include/clang/Driver/SanitizerArgs.h
+++ b/clang/include/clang/Driver/SanitizerArgs.h
@@ -106,6 +106,9 @@ class SanitizerArgs {
}
bool needsTysanRt() const { return Sanitizers.has(SanitizerKind::Type); }
bool needsTsanRt() const { return Sanitizers.has(SanitizerKind::Thread); }
+ bool needsCsanRt() const {
+ return Sanitizers.has(SanitizerKind::Concurrency);
+ }
bool needsMsanRt() const { return Sanitizers.has(SanitizerKind::Memory); }
bool needsFuzzer() const { return Sanitizers.has(SanitizerKind::Fuzzer); }
bool needsLsanRt() const {
diff --git a/clang/lib/CodeGen/BackendUtil.cpp b/clang/lib/CodeGen/BackendUtil.cpp
index 8103b72cf4e3a2..8634cbd930d42f 100644
--- a/clang/lib/CodeGen/BackendUtil.cpp
+++ b/clang/lib/CodeGen/BackendUtil.cpp
@@ -74,6 +74,7 @@
#include "llvm/Transforms/Instrumentation/AddressSanitizer.h"
#include "llvm/Transforms/Instrumentation/AddressSanitizerOptions.h"
#include "llvm/Transforms/Instrumentation/BoundsChecking.h"
+#include "llvm/Transforms/Instrumentation/ConcurrencySanitizer.h"
#include "llvm/Transforms/Instrumentation/DataFlowSanitizer.h"
#include "llvm/Transforms/Instrumentation/GCOVProfiler.h"
#include "llvm/Transforms/Instrumentation/HWAddressSanitizer.h"
@@ -703,6 +704,12 @@ static void addSanitizers(const Triple &TargetTriple,
MPM.addPass(createModuleToFunctionPassAdaptor(ThreadSanitizerPass()));
}
+ if (LangOpts.Sanitize.has(SanitizerKind::Concurrency)) {
+ MPM.addPass(ModuleConcurrencySanitizerPass());
+ MPM.addPass(
+ createModuleToFunctionPassAdaptor(ConcurrencySanitizerPass()));
+ }
+
if (LangOpts.Sanitize.has(SanitizerKind::Type))
MPM.addPass(TypeSanitizerPass());
diff --git a/clang/lib/CodeGen/CGDeclCXX.cpp b/clang/lib/CodeGen/CGDeclCXX.cpp
index a54809c48037e8..f58492f1f41e02 100644
--- a/clang/lib/CodeGen/CGDeclCXX.cpp
+++ b/clang/lib/CodeGen/CGDeclCXX.cpp
@@ -487,6 +487,10 @@ llvm::Function *CodeGenModule::CreateGlobalInitOrCleanUpFunction(
!isInNoSanitizeList(SanitizerKind::Thread, Fn, Loc))
Fn->addFnAttr(llvm::Attribute::SanitizeThread);
+ if (getLangOpts().Sanitize.has(SanitizerKind::Concurrency) &&
+ !isInNoSanitizeList(SanitizerKind::Concurrency, Fn, Loc))
+ Fn->addFnAttr(llvm::Attribute::SanitizeConcurrency);
+
if (getLangOpts().Sanitize.has(SanitizerKind::NumericalStability) &&
!isInNoSanitizeList(SanitizerKind::NumericalStability, Fn, Loc))
Fn->addFnAttr(llvm::Attribute::SanitizeNumericalStability);
diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp
index 745423945ca838..e7e4e269ef526e 100644
--- a/clang/lib/CodeGen/CodeGenFunction.cpp
+++ b/clang/lib/CodeGen/CodeGenFunction.cpp
@@ -688,6 +688,10 @@ void CodeGenFunction::markAsIgnoreThreadCheckingAtRuntime(llvm::Function *Fn) {
Fn->addFnAttr("sanitize_thread_no_checking_at_run_time");
Fn->removeFnAttr(llvm::Attribute::SanitizeThread);
}
+ if (SanOpts.has(SanitizerKind::Concurrency)) {
+ Fn->addFnAttr("sanitize_concurrency_no_checking_at_run_time");
+ Fn->removeFnAttr(llvm::Attribute::SanitizeConcurrency);
+ }
}
/// Check if the return value of this function requires sanitization.
@@ -824,6 +828,8 @@ void CodeGenFunction::StartFunction(GlobalDecl GD, QualType RetTy,
Fn->addFnAttr(llvm::Attribute::SanitizeMemTag);
if (SanOpts.has(SanitizerKind::Thread))
Fn->addFnAttr(llvm::Attribute::SanitizeThread);
+ if (SanOpts.has(SanitizerKind::Concurrency))
+ Fn->addFnAttr(llvm::Attribute::SanitizeConcurrency);
if (SanOpts.has(SanitizerKind::Type))
Fn->addFnAttr(llvm::Attribute::SanitizeType);
if (SanOpts.has(SanitizerKind::NumericalStability))
@@ -851,9 +857,10 @@ void CodeGenFunction::StartFunction(GlobalDecl GD, QualType RetTy,
if (SanOpts.hasOneOf(SanitizerKind::Fuzzer | SanitizerKind::FuzzerNoLink))
Fn->addFnAttr(llvm::Attribute::OptForFuzzing);
- // Ignore TSan memory acesses from within ObjC/ObjC++ dealloc, initialize,
- // .cxx_destruct, __destroy_helper_block_ and all of their calees at run time.
- if (SanOpts.has(SanitizerKind::Thread)) {
+ // Ignore TSan/CSan memory accesses from within ObjC/ObjC++ dealloc,
+ // initialize, .cxx_destruct, __destroy_helper_block_ and all of their callees
+ // at run time.
+ if (SanOpts.hasOneOf(SanitizerKind::Thread | SanitizerKind::Concurrency)) {
if (const auto *OMD = dyn_cast_or_null<ObjCMethodDecl>(D)) {
const IdentifierInfo *II = OMD->getSelector().getIdentifierInfoForSlot(0);
if (OMD->getMethodFamily() == OMF_dealloc ||
diff --git a/clang/lib/Driver/SanitizerArgs.cpp b/clang/lib/Driver/SanitizerArgs.cpp
index 778cde8285aaf1..418c403f3ecc72 100644
--- a/clang/lib/Driver/SanitizerArgs.cpp
+++ b/clang/lib/Driver/SanitizerArgs.cpp
@@ -40,8 +40,8 @@ static const SanitizerMask NotAllowedWithExecuteOnly =
SanitizerKind::Function | SanitizerKind::KCFI;
static const SanitizerMask NeedsUnwindTables =
SanitizerKind::Address | SanitizerKind::HWAddress | SanitizerKind::Type |
- SanitizerKind::Thread | SanitizerKind::Memory | SanitizerKind::DataFlow |
- SanitizerKind::NumericalStability;
+ SanitizerKind::Thread | SanitizerKind::Concurrency | SanitizerKind::Memory |
+ SanitizerKind::DataFlow | SanitizerKind::NumericalStability;
static const SanitizerMask SupportsCoverage =
SanitizerKind::Address | SanitizerKind::HWAddress |
SanitizerKind::KernelAddress | SanitizerKind::KernelHWAddress |
@@ -53,7 +53,8 @@ static const SanitizerMask SupportsCoverage =
SanitizerKind::DataFlow | SanitizerKind::Fuzzer |
SanitizerKind::FuzzerNoLink | SanitizerKind::FloatDivideByZero |
SanitizerKind::SafeStack | SanitizerKind::ShadowCallStack |
- SanitizerKind::Thread | SanitizerKind::ObjCCast | SanitizerKind::KCFI |
+ SanitizerKind::Thread | SanitizerKind::Concurrency |
+ SanitizerKind::ObjCCast | SanitizerKind::KCFI |
SanitizerKind::NumericalStability | SanitizerKind::Vptr |
SanitizerKind::CFI | SanitizerKind::AllocToken;
static const SanitizerMask RecoverableByDefault =
@@ -738,7 +739,12 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
SanitizerKind::Address | SanitizerKind::KernelAddress |
SanitizerKind::Memory | SanitizerKind::Leak |
SanitizerKind::Thread),
- std::make_pair(SanitizerKind::Thread, SanitizerKind::Memory),
+ std::make_pair(SanitizerKind::Thread,
+ SanitizerKind::Memory | SanitizerKind::Concurrency),
+ std::make_pair(SanitizerKind::Concurrency,
+ SanitizerKind::Thread | SanitizerKind::Address |
+ SanitizerKind::Memory | SanitizerKind::Leak |
+ SanitizerKind::Undefined),
std::make_pair(SanitizerKind::Leak,
SanitizerKind::Thread | SanitizerKind::Memory),
std::make_pair(SanitizerKind::KernelAddress,
diff --git a/clang/lib/Driver/ToolChains/AMDGPU.cpp b/clang/lib/Driver/ToolChains/AMDGPU.cpp
index d6dacb4ccba839..b66e5ac0ca8ae8 100644
--- a/clang/lib/Driver/ToolChains/AMDGPU.cpp
+++ b/clang/lib/Driver/ToolChains/AMDGPU.cpp
@@ -1391,6 +1391,8 @@ SanitizerMask AMDGPUToolChain::getSupportedSanitizers(
// arch xnack support.
if (!BA || isXnackAvailable(getTriple(), BA.ArchName))
SupportedMask |= SanitizerKind::Address;
+ // Watchpoint probes do not require xnack.
+ SupportedMask |= SanitizerKind::Concurrency;
return SupportedMask;
}
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index 6636a5fd6e6551..274222cbed1f66 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -9809,7 +9809,7 @@ void LinkerWrapper::ConstructJob(Compilation &C, const JobAction &JA,
// Don't forward sanitizer arguments if the toolchain doesn't support it.
// Without this check using it on the host would result in linker errors.
if (requiresUBSanRT(ID) && !ToolChainHasRT(TC, "ubsan_minimal") &&
- !ToolChainHasRT(TC, "ubsan_standalone"))
+ !ToolChainHasRT(TC, "ubsan_standalone") && !ToolChainHasRT(TC, "csan"))
return false;
// Don't forward -mllvm to toolchains that don't support LLVM.
return TC.HasNativeLLVMSupport() || ID != OPT_mllvm;
diff --git a/clang/lib/Driver/ToolChains/CommonArgs.cpp b/clang/lib/Driver/ToolChains/CommonArgs.cpp
index 445eb4ccfbfa72..0747540a23ff92 100644
--- a/clang/lib/Driver/ToolChains/CommonArgs.cpp
+++ b/clang/lib/Driver/ToolChains/CommonArgs.cpp
@@ -1658,8 +1658,9 @@ void tools::linkSanitizerRuntimeDeps(const ToolChain &TC,
CmdArgs.push_back("-lresolv");
}
-// Host interceptor library for offload UBSan.
-static bool hostNeedsUbsanOffloadRt(Compilation &C, const ToolChain &HostTC) {
+template <typename Predicate>
+static bool hostNeedsOffloadRt(Compilation &C, const ToolChain &HostTC,
+ Predicate NeedsRuntime) {
if (HostTC.getTriple().isGPU())
return false;
@@ -1677,7 +1678,7 @@ static bool hostNeedsUbsanOffloadRt(Compilation &C, const ToolChain &HostTC) {
C.getDriver().getOffloadArchs(C, C.getArgs(), Kind, *DevTC)) {
const ArgList &DevArgs = C.getArgsForToolChain(DevTC, BA, Kind);
SanitizerArgs DevSan = DevTC->getSanitizerArgs(DevArgs, BA, Kind);
- if (DevSan.needsUbsanRt() && !DevSan.requiresMinimalRuntime())
+ if (NeedsRuntime(DevSan))
return true;
}
}
@@ -1685,6 +1686,17 @@ static bool hostNeedsUbsanOffloadRt(Compilation &C, const ToolChain &HostTC) {
return false;
}
+static bool hostNeedsUbsanOffloadRt(Compilation &C, const ToolChain &HostTC) {
+ return hostNeedsOffloadRt(C, HostTC, [](const SanitizerArgs &S) {
+ return S.needsUbsanRt() && !S.requiresMinimalRuntime();
+ });
+}
+
+static bool hostNeedsCsanOffloadRt(Compilation &C, const ToolChain &HostTC) {
+ return hostNeedsOffloadRt(
+ C, HostTC, [](const SanitizerArgs &S) { return S.needsCsanRt(); });
+}
+
static void
collectSanitizerRuntimes(Compilation &C, const ToolChain &TC,
const ArgList &Args,
@@ -1695,8 +1707,10 @@ collectSanitizerRuntimes(Compilation &C, const ToolChain &TC,
SmallVectorImpl<StringRef> &RequiredSymbols) {
assert(!TC.getTriple().isOSDarwin() && "it's not used by Darwin");
const SanitizerArgs &SanArgs = TC.getSanitizerArgs(Args);
- const bool NeedsOffloadRt = hostNeedsUbsanOffloadRt(C, TC);
- const bool NeedsUbsanRt = SanArgs.needsUbsanRt() || NeedsOffloadRt;
+ const bool NeedsUbsanOffloadRt = hostNeedsUbsanOffloadRt(C, TC);
+ const bool NeedsCsanOffloadRt = hostNeedsCsanOffloadRt(C, TC);
+ const bool NeedsUbsanRt = SanArgs.needsUbsanRt() || NeedsUbsanOffloadRt;
+ const bool NeedsCsanRt = SanArgs.needsCsanRt() || NeedsCsanOffloadRt;
// Collect shared runtimes.
if (SanArgs.needsSharedRt()) {
if (SanArgs.needsAsanRt()) {
@@ -1745,16 +1759,24 @@ collectSanitizerRuntimes(Compilation &C, const ToolChain &TC,
HelperStaticRuntimes.push_back("asan_static");
// Offloading images can live in DSOs, the host interceptors must follow.
- if (NeedsOffloadRt) {
+ if (NeedsUbsanOffloadRt) {
NonWholeStaticRuntimes.push_back("ubsan_offload");
RequiredSymbols.push_back("__ubsan_offload_init");
}
+ if (NeedsCsanOffloadRt) {
+ NonWholeStaticRuntimes.push_back("csan_offload");
+ RequiredSymbols.push_back("__csan_offload_init");
+ }
// Collect static runtimes.
if (Args.hasArg(options::OPT_shared)) {
// Don't link static runtimes into DSOs.
- if (NeedsOffloadRt && !SanArgs.needsSharedRt() && !SanArgs.needsUbsanRt())
+ if (NeedsUbsanOffloadRt && !SanArgs.needsSharedRt() &&
+ !SanArgs.needsUbsanRt())
StaticRuntimes.push_back("ubsan_standalone");
+ if (NeedsCsanOffloadRt && !SanArgs.needsSharedRt() &&
+ !SanArgs.needsCsanRt())
+ StaticRuntimes.push_back("csan");
return;
}
@@ -1813,6 +1835,8 @@ collectSanitizerRuntimes(Compilation &C, const ToolChain &TC,
StaticRuntimes.push_back("ubsan_standalone");
}
}
+ if (!SanArgs.needsSharedRt() && NeedsCsanRt)
+ StaticRuntimes.push_back("csan");
if (SanArgs.needsSafeStackRt()) {
NonWholeStaticRuntimes.push_back("safestack");
RequiredSymbols.push_back("__safestack_init");
diff --git a/clang/lib/Driver/ToolChains/Linux.cpp b/clang/lib/Driver/ToolChains/Linux.cpp
index e295b2516da16f..43b48903fc0533 100644
--- a/clang/lib/Driver/ToolChains/Linux.cpp
+++ b/clang/lib/Driver/ToolChains/Linux.cpp
@@ -1007,6 +1007,8 @@ Linux::getSupportedSanitizers(BoundArch BA,
if (IsX86_64 || IsMIPS64 || IsAArch64 || IsPowerPC64 || IsSystemZ ||
IsLoongArch64 || IsRISCV64)
Res |= SanitizerKind::Thread;
+ if (IsX86_64)
+ Res |= SanitizerKind::Concurrency;
if (IsX86_64 || IsAArch64 || IsSystemZ || IsHexagon)
Res |= SanitizerKind::Type;
if (IsX86_64 || IsSystemZ || IsPowerPC64)
diff --git a/clang/lib/Frontend/InitPreprocessor.cpp b/clang/lib/Frontend/InitPreprocessor.cpp
index 784ff9951a25ad..264412ea3f714b 100644
--- a/clang/lib/Frontend/InitPreprocessor.cpp
+++ b/clang/lib/Frontend/InitPreprocessor.cpp
@@ -1535,6 +1535,8 @@ static void InitializePredefinedMacros(const TargetInfo &TI,
Builder.defineMacro("__SANITIZE_HWADDRESS__");
if (LangOpts.Sanitize.has(SanitizerKind::Thread))
Builder.defineMacro("__SANITIZE_THREAD__");
+ if (LangOpts.Sanitize.has(SanitizerKind::Concurrency))
+ Builder.defineMacro("__SANITIZE_CONCURRENCY__");
if (LangOpts.Sanitize.has(SanitizerKind::AllocToken))
Builder.defineMacro("__SANITIZE_ALLOC_TOKEN__");
diff --git a/clang/test/CodeGen/sanitize-concurrency.c b/clang/test/CodeGen/sanitize-concurrency.c
new file mode 100644
index 00000000000000..5d6e45ed5257a7
--- /dev/null
+++ b/clang/test/CodeGen/sanitize-concurrency.c
@@ -0,0 +1,82 @@
+// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 6
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm -o - %s | FileCheck -check-prefixes CHECK,WITHOUT %s
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm -o - %s -fsanitize=concurrency | FileCheck -check-prefixes CHECK,CSAN %s
+
+#if defined(__SANITIZE_CONCURRENCY__)
+// CSAN-LABEL: define dso_local i32 @concurrency_macro_defined(
+// CSAN-SAME: ) #[[ATTR0:[0-9]+]] {
+// CSAN-NEXT: [[ENTRY:.*:]]
+// CSAN-NEXT: ret i32 1
+//
+int concurrency_macro_defined(void) { return 1; }
+#endif
+
+#if __has_feature(concurrency_sanitizer)
+// CSAN-LABEL: define dso_local i32 @concurrency_feature_defined(
+// CSAN-SAME: ) #[[ATTR0]] {
+// CSAN-NEXT: [[ENTRY:.*:]]
+// CSAN-NEXT: ret i32 1
+//
+int concurrency_feature_defined(void) { return 1; }
+#endif
+
+// WITHOUT-LABEL: define dso_local i32 @instrumented1(
+// WITHOUT-SAME: ptr noundef [[A:%.*]], ptr noundef [[B:%.*]]) #[[ATTR0:[0-9]+]] {
+// WITHOUT-NEXT: [[ENTRY:.*:]]
+// WITHOUT-NEXT: [[A_ADDR:%.*]] = alloca ptr, align 8
+// WITHOUT-NEXT: [[B_ADDR:%.*]] = alloca ptr, align 8
+// WITHOUT-NEXT: store ptr [[A]], ptr [[A_ADDR]], align 8
+// WITHOUT-NEXT: store ptr [[B]], ptr [[B_ADDR]], align 8
+// WITHOUT-NEXT: [[TMP0:%.*]] = load ptr, ptr [[A_ADDR]], align 8
+// WITHOUT-NEXT: [[TMP1:%.*]] = load i32, ptr [[TMP0]], align 4
+// WITHOUT-NEXT: [[TMP2:%.*]] = load ptr, ptr [[B_ADDR]], align 8
+// WITHOUT-NEXT: [[ATOMIC_LOAD:%.*]] = load atomic i32, ptr [[TMP2]] seq_cst, align 4
+// WITHOUT-NEXT: [[ADD:%.*]] = add nsw i32 [[TMP1]], [[ATOMIC_LOAD]]
+// WITHOUT-NEXT: ret i32 [[ADD]]
+//
+// CSAN-LABEL: define dso_local i32 @instrumented1(
+// CSAN-SAME: ptr noundef [[A:%.*]], ptr noundef [[B:%.*]]) #[[ATTR0]] {
+// CSAN-NEXT: [[ENTRY:.*:]]
+// CSAN-NEXT: [[TMP0:%.*]] = call ptr @llvm.returnaddress.p0(i32 0)
+// CSAN-NEXT: call void @__csan_func_entry(ptr [[TMP0]])
+// CSAN-NEXT: [[A_ADDR:%.*]] = alloca ptr, align 8
+// CSAN-NEXT: [[B_ADDR:%.*]] = alloca ptr, align 8
+// CSAN-NEXT: store ptr [[A]], ptr [[A_ADDR]], align 8
+// CSAN-NEXT: store ptr [[B]], ptr [[B_ADDR]], align 8
+// CSAN-NEXT: [[TMP1:%.*]] = load ptr, ptr [[A_ADDR]], align 8
+// CSAN-NEXT: call void @__csan_read4(ptr [[TMP1]])
+// CSAN-NEXT: [[TMP2:%.*]] = load i32, ptr [[TMP1]], align 4
+// CSAN-NEXT: [[TMP3:%.*]] = load ptr, ptr [[B_ADDR]], align 8
+// CSAN-NEXT: call void @__csan_read4(ptr [[TMP3]])
+// CSAN-NEXT: [[ATOMIC_LOAD:%.*]] = load atomic i32, ptr [[TMP3]] seq_cst, align 4
+// CSAN-NEXT: [[ADD:%.*]] = add nsw i32 [[TMP2]], [[ATOMIC_LOAD]]
+// CSAN-NEXT: call void @__csan_func_exit()
+// CSAN-NEXT: ret i32 [[ADD]]
+//
+int instrumented1(int *a, _Atomic int *b) {
+ return *a + *b;
+}
+
+// WITHOUT-LABEL: define dso_local i32 @suppressed(
+// WITHOUT-SAME: ptr noundef [[A:%.*]]) #[[ATTR0]] {
+// WITHOUT-NEXT: [[ENTRY:.*:]]
+// WITHOUT-NEXT: [[A_ADDR:%.*]] = alloca ptr, align 8
+// WITHOUT-NEXT: store ptr [[A]], ptr [[A_ADDR]], align 8
+// WITHOUT-NEXT: [[TMP0:%.*]] = load ptr, ptr [[A_ADDR]], align 8
+// WITHOUT-NEXT: [[TMP1:%.*]] = load i32, ptr [[TMP0]], align 4
+// WITHOUT-NEXT: ret i32 [[TMP1]]
+//
+// CSAN-LABEL: define dso_local i32 @suppressed(
+// CSAN-SAME: ptr noundef [[A:%.*]]) #[[ATTR1:[0-9]+]] {
+// CSAN-NEXT: [[ENTRY:.*:]]
+// CSAN-NEXT: [[A_ADDR:%.*]] = alloca ptr, align 8
+// CSAN-NEXT: store ptr [[A]], ptr [[A_ADDR]], align 8
+// CSAN-NEXT: [[TMP0:%.*]] = load ptr, ptr [[A_ADDR]], align 8
+// CSAN-NEXT: [[TMP1:%.*]] = load i32, ptr [[TMP0]], align 4
+// CSAN-NEXT: ret i32 [[TMP1]]
+//
+__attribute__((no_sanitize("concurrency"))) int suppressed(int *a) {
+ return *a;
+}
+//// NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
+// CHECK: {{.*}}
diff --git a/clang/test/Driver/fsanitize.c b/clang/test/Driver/fsanitize.c
index 97dbee7c15bc84..55579be3ae1e24 100644
--- a/clang/test/Driver/fsanitize.c
+++ b/clang/test/Driver/fsanitize.c
@@ -23,6 +23,21 @@
// RUN: not %clang --target=x86_64-linux-gnu -fsanitize=leak,thread -pie -fno-rtti %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-SANL-SANT
// CHECK-SANL-SANT: '-fsanitize=leak' not allowed with '-fsanitize=thread'
+// RUN: %clang --target=x86_64-linux-gnu -fsanitize=concurrency %s -### 2>&1 \
+// RUN: -resource-dir=%S/Inputs/resource_dir \
+// RUN: | FileCheck %s --check-prefix=CHECK-SANC-X64
+// CHECK-SANC-X64: "-fsanitize=concurrency"
+// CHECK-SANC-X64: libclang_rt.csan.a
+
+// RUN: %clang --target=amdgcn-amd-amdhsa -mcpu=gfx900 -nogpulib -fsanitize=concurrency %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-SANC-AMDGPU
+// CHECK-SANC-AMDGPU: "-fsanitize=concurrency"
+
+// RUN: not %clang --target=amdgcn-amd-amdhsa -mcpu=gfx900:xnack+ -nogpulib -fsanitize=concurrency,address %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-SANC-SANA
+// CHECK-SANC-SANA: '-fsanitize=concurrency' not allowed with '-fsanitize=address'
+
+// RUN: not %clang --target=x86_64-linux-gnu -fsanitize=concurrency,undefined %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-SANC-SANU
+// CHECK-SANC-SANU: '-fsanitize=concurrency' not allowed with '-fsanitize=undefined'
+
// RUN: not %clang --target=x86_64-linux-gnu -fsanitize=leak,memory -pie -fno-rtti %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-SANL-SANM
// CHECK-SANL-SANM: '-fsanitize=leak' not allowed with '-fsanitize=memory'
diff --git a/clang/test/Preprocessor/sanitizer-predefines.c b/clang/test/Preprocessor/sanitizer-predefines.c
index d903a40de050f4..cec6aa4ffee2ec 100644
--- a/clang/test/Preprocessor/sanitizer-predefines.c
+++ b/clang/test/Preprocessor/sanitizer-predefines.c
@@ -8,3 +8,6 @@
// RUN: %clang_cc1 -E -dM -triple aarch64-unknown-linux -fsanitize=thread %s | FileCheck %s --check-prefix=TSAN
// TSAN: #define __SANITIZE_THREAD__ 1
+
+// RUN: %clang_cc1 -E -dM -triple aarch64-unknown-linux -fsanitize=concurrency %s | FileCheck %s --check-prefix=CSAN
+// CSAN: #define __SANITIZE_CONCURRENCY__ 1
More information about the llvm-branch-commits
mailing list