[llvm] [IR] Add the sanitize_concurrency function attribute (PR #225779)

Joseph Huber via llvm-commits llvm-commits at lists.llvm.org
Wed Sep 23 07:38:15 PDT 2026


https://github.com/jhuber6 updated https://github.com/llvm/llvm-project/pull/225779

>From 9cdec1588de53b499f9be7635ceee495ecf153ce Mon Sep 17 00:00:00 2001
From: Joseph Huber <huberjn at outlook.com>
Date: Mon, 20 Jul 2026 16:16:19 -0500
Subject: [PATCH] [IR] Add the sanitize_concurrency function attribute

This introduces a new concurrency sanitizer attribute to be used with
the runtime of the same name. This has no major change and simply
updates the IR to handle it properly. This attribute is invalid when
combined with the thread sanitizer one as they are mutually exclusive.
---
 llvm/docs/LangRef.md                          |  4 ++
 llvm/include/llvm/Bitcode/LLVMBitCodes.h      |  1 +
 llvm/include/llvm/IR/Attributes.td            |  5 +++
 llvm/lib/Analysis/Loads.cpp                   |  1 +
 llvm/lib/Bitcode/Reader/BitcodeReader.cpp     |  2 +
 llvm/lib/Bitcode/Writer/BitcodeWriter.cpp     |  2 +
 llvm/lib/CodeGen/ShrinkWrap.cpp               |  1 +
 llvm/lib/IR/Verifier.cpp                      |  6 +++
 llvm/lib/Target/AMDGPU/AMDGPUAttributor.cpp   |  1 +
 llvm/lib/Transforms/IPO/HotColdSplitting.cpp  |  1 +
 llvm/lib/Transforms/Scalar/ExpandMemCmp.cpp   |  1 +
 llvm/lib/Transforms/Utils/CodeExtractor.cpp   |  1 +
 llvm/test/Bitcode/attributes.ll               |  6 +++
 llvm/test/Bitcode/compatibility.ll            |  8 +++-
 .../amdgpu-attributor-min-agpr-alloc.ll       | 40 ++++++++++++-------
 .../X86/x86-sanitizer-shrink-wrapping.ll      | 30 ++++++++++++++
 .../CodeExtractor/PartialInlineAttributes.ll  |  4 +-
 .../ExpandMemCmp/X86/sanitizer-skip.ll        | 12 +++++-
 .../HotColdSplit/X86/do-not-split.ll          | 14 +++++++
 .../no_speculative_loads_with_tsan.ll         | 25 ++++++++++++
 .../Verifier/sanitize-concurrency-attrs.ll    |  9 +++++
 21 files changed, 155 insertions(+), 19 deletions(-)
 create mode 100644 llvm/test/Verifier/sanitize-concurrency-attrs.ll

diff --git a/llvm/docs/LangRef.md b/llvm/docs/LangRef.md
index 3d906ee36a9979..e8c4a8e1de27f2 100644
--- a/llvm/docs/LangRef.md
+++ b/llvm/docs/LangRef.md
@@ -2714,6 +2714,10 @@ fn -> other_fn -> other_fn ; fn is norecurse
 :   This attribute indicates that ThreadSanitizer checks
     (dynamic thread safety analysis) are enabled for this function.
 
+`sanitize_concurrency`
+:   This attribute indicates that ConcurrencySanitizer checks
+    (probabilistic data race detection) are enabled for this function.
+
 `sanitize_hwaddress`
 :   This attribute indicates that HWAddressSanitizer checks
     (dynamic address safety analysis based on tagged pointers) are enabled for
diff --git a/llvm/include/llvm/Bitcode/LLVMBitCodes.h b/llvm/include/llvm/Bitcode/LLVMBitCodes.h
index 4099b57f5482e2..0e024945440e9c 100644
--- a/llvm/include/llvm/Bitcode/LLVMBitCodes.h
+++ b/llvm/include/llvm/Bitcode/LLVMBitCodes.h
@@ -831,6 +831,7 @@ enum AttributeKindCodes {
   ATTR_KIND_FLATTEN = 108,
   ATTR_KIND_NOIPA = 109,
   ATTR_KIND_NOFREEOBJ = 110,
+  ATTR_KIND_SANITIZE_CONCURRENCY = 111,
 };
 
 enum ComdatSelectionKindCodes {
diff --git a/llvm/include/llvm/IR/Attributes.td b/llvm/include/llvm/IR/Attributes.td
index c43b499061ddc7..449b24db9458e4 100644
--- a/llvm/include/llvm/IR/Attributes.td
+++ b/llvm/include/llvm/IR/Attributes.td
@@ -342,6 +342,10 @@ def SanitizeAddress : EnumAttr<"sanitize_address", IntersectPreserve, [FnAttr]>;
 /// ThreadSanitizer is on.
 def SanitizeThread : EnumAttr<"sanitize_thread", IntersectPreserve, [FnAttr]>;
 
+/// ConcurrencySanitizer is on.
+def SanitizeConcurrency
+    : EnumAttr<"sanitize_concurrency", IntersectPreserve, [FnAttr]>;
+
 /// TypeSanitizer is on.
 def SanitizeType : EnumAttr<"sanitize_type", IntersectPreserve, [FnAttr]>;
 
@@ -465,6 +469,7 @@ class CompatRuleStrAttr<string F, string Attr> : CompatRule<F> {
 
 def : CompatRule<"isEqual<SanitizeAddressAttr>">;
 def : CompatRule<"isEqual<SanitizeThreadAttr>">;
+def : CompatRule<"isEqual<SanitizeConcurrencyAttr>">;
 def : CompatRule<"isEqual<SanitizeTypeAttr>">;
 def : CompatRule<"isEqual<SanitizeMemoryAttr>">;
 def : CompatRule<"isEqual<SanitizeHWAddressAttr>">;
diff --git a/llvm/lib/Analysis/Loads.cpp b/llvm/lib/Analysis/Loads.cpp
index 2e8c884e13729c..4016fe70493767 100644
--- a/llvm/lib/Analysis/Loads.cpp
+++ b/llvm/lib/Analysis/Loads.cpp
@@ -444,6 +444,7 @@ static bool suppressSpeculativeLoadForSanitizers(const Instruction &CtxI) {
   const Function &F = *CtxI.getFunction();
   // Speculative load may create a race that did not exist in the source.
   return F.hasFnAttribute(Attribute::SanitizeThread) ||
+         F.hasFnAttribute(Attribute::SanitizeConcurrency) ||
          // Speculative load may load data from dirty regions.
          F.hasFnAttribute(Attribute::SanitizeAddress) ||
          F.hasFnAttribute(Attribute::SanitizeHWAddress);
diff --git a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
index d75e85b0b0f9e1..a0cd1bf84e3c44 100644
--- a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
+++ b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
@@ -2266,6 +2266,8 @@ static Attribute::AttrKind getAttrFromCode(uint64_t Code) {
     return Attribute::SanitizeHWAddress;
   case bitc::ATTR_KIND_SANITIZE_THREAD:
     return Attribute::SanitizeThread;
+  case bitc::ATTR_KIND_SANITIZE_CONCURRENCY:
+    return Attribute::SanitizeConcurrency;
   case bitc::ATTR_KIND_SANITIZE_TYPE:
     return Attribute::SanitizeType;
   case bitc::ATTR_KIND_SANITIZE_MEMORY:
diff --git a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
index c99a1edd31fb45..9dc93dc885e8da 100644
--- a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
+++ b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
@@ -950,6 +950,8 @@ static uint64_t getAttrKindEncoding(Attribute::AttrKind Kind) {
     return bitc::ATTR_KIND_SANITIZE_HWADDRESS;
   case Attribute::SanitizeThread:
     return bitc::ATTR_KIND_SANITIZE_THREAD;
+  case Attribute::SanitizeConcurrency:
+    return bitc::ATTR_KIND_SANITIZE_CONCURRENCY;
   case Attribute::SanitizeType:
     return bitc::ATTR_KIND_SANITIZE_TYPE;
   case Attribute::SanitizeMemory:
diff --git a/llvm/lib/CodeGen/ShrinkWrap.cpp b/llvm/lib/CodeGen/ShrinkWrap.cpp
index 32cae27489c39a..8328ce7175b78a 100644
--- a/llvm/lib/CodeGen/ShrinkWrap.cpp
+++ b/llvm/lib/CodeGen/ShrinkWrap.cpp
@@ -1051,6 +1051,7 @@ bool ShrinkWrapImpl::isShrinkWrapEnabled(const MachineFunction &MF) {
            // sanitizers to be able to get a correct stack frame.
            !(MF.getFunction().hasFnAttribute(Attribute::SanitizeAddress) ||
              MF.getFunction().hasFnAttribute(Attribute::SanitizeThread) ||
+             MF.getFunction().hasFnAttribute(Attribute::SanitizeConcurrency) ||
              MF.getFunction().hasFnAttribute(Attribute::SanitizeMemory) ||
              MF.getFunction().hasFnAttribute(Attribute::SanitizeType) ||
              MF.getFunction().hasFnAttribute(Attribute::SanitizeHWAddress));
diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp
index 1440b95896474f..b4a0a395b1ba8d 100644
--- a/llvm/lib/IR/Verifier.cpp
+++ b/llvm/lib/IR/Verifier.cpp
@@ -2555,6 +2555,12 @@ void Verifier::verifyFunctionAttrs(FunctionType *FT, AttributeList Attrs,
         "'sanitize_realtime and sanitize_realtime_blocking' are incompatible!",
         V);
 
+  Check(!(Attrs.hasFnAttr(Attribute::SanitizeThread) &&
+          Attrs.hasFnAttr(Attribute::SanitizeConcurrency)),
+        "Attributes 'sanitize_thread and sanitize_concurrency' are "
+        "incompatible!",
+        V);
+
   if (Attrs.hasFnAttr(Attribute::OptimizeForDebugging)) {
     Check(!Attrs.hasFnAttr(Attribute::OptimizeForSize),
           "Attributes 'optsize and optdebug' are incompatible!", V);
diff --git a/llvm/lib/Target/AMDGPU/AMDGPUAttributor.cpp b/llvm/lib/Target/AMDGPU/AMDGPUAttributor.cpp
index 2ed1be7c2ade05..a535081b5a74ad 100644
--- a/llvm/lib/Target/AMDGPU/AMDGPUAttributor.cpp
+++ b/llvm/lib/Target/AMDGPU/AMDGPUAttributor.cpp
@@ -141,6 +141,7 @@ static bool isDSAddress(const Constant *C) {
 static bool hasSanitizerAttributes(const Function &F) {
   return F.hasFnAttribute(Attribute::SanitizeAddress) ||
          F.hasFnAttribute(Attribute::SanitizeThread) ||
+         F.hasFnAttribute(Attribute::SanitizeConcurrency) ||
          F.hasFnAttribute(Attribute::SanitizeMemory) ||
          F.hasFnAttribute(Attribute::SanitizeHWAddress) ||
          F.hasFnAttribute(Attribute::SanitizeMemTag);
diff --git a/llvm/lib/Transforms/IPO/HotColdSplitting.cpp b/llvm/lib/Transforms/IPO/HotColdSplitting.cpp
index 97183c76a079ba..d76b0bf78ae0da 100644
--- a/llvm/lib/Transforms/IPO/HotColdSplitting.cpp
+++ b/llvm/lib/Transforms/IPO/HotColdSplitting.cpp
@@ -269,6 +269,7 @@ bool HotColdSplitting::shouldOutlineFrom(const Function &F) const {
   if (F.hasFnAttribute(Attribute::SanitizeAddress) ||
       F.hasFnAttribute(Attribute::SanitizeHWAddress) ||
       F.hasFnAttribute(Attribute::SanitizeThread) ||
+      F.hasFnAttribute(Attribute::SanitizeConcurrency) ||
       F.hasFnAttribute(Attribute::SanitizeMemory))
     return false;
 
diff --git a/llvm/lib/Transforms/Scalar/ExpandMemCmp.cpp b/llvm/lib/Transforms/Scalar/ExpandMemCmp.cpp
index e05a5d2e9e5855..637ba0600fb740 100644
--- a/llvm/lib/Transforms/Scalar/ExpandMemCmp.cpp
+++ b/llvm/lib/Transforms/Scalar/ExpandMemCmp.cpp
@@ -1102,6 +1102,7 @@ PreservedAnalyses ExpandMemCmpPass::run(Function &F,
   if (F.hasFnAttribute(Attribute::SanitizeAddress) ||
       F.hasFnAttribute(Attribute::SanitizeMemory) ||
       F.hasFnAttribute(Attribute::SanitizeThread) ||
+      F.hasFnAttribute(Attribute::SanitizeConcurrency) ||
       F.hasFnAttribute(Attribute::SanitizeHWAddress))
     return PreservedAnalyses::all();
 
diff --git a/llvm/lib/Transforms/Utils/CodeExtractor.cpp b/llvm/lib/Transforms/Utils/CodeExtractor.cpp
index 6a53688fb70272..123ec196fb381b 100644
--- a/llvm/lib/Transforms/Utils/CodeExtractor.cpp
+++ b/llvm/lib/Transforms/Utils/CodeExtractor.cpp
@@ -1008,6 +1008,7 @@ Function *CodeExtractor::constructFunctionDeclaration(
       case Attribute::SanitizeMemory:
       case Attribute::SanitizeNumericalStability:
       case Attribute::SanitizeThread:
+      case Attribute::SanitizeConcurrency:
       case Attribute::SanitizeType:
       case Attribute::SanitizeHWAddress:
       case Attribute::SanitizeMemTag:
diff --git a/llvm/test/Bitcode/attributes.ll b/llvm/test/Bitcode/attributes.ll
index ae5e806a592107..9cf0bda60f2050 100644
--- a/llvm/test/Bitcode/attributes.ll
+++ b/llvm/test/Bitcode/attributes.ll
@@ -607,6 +607,11 @@ define nofreeobj ptr @nofreeobj(ptr nofreeobj %p) {
   ret ptr %p
 }
 
+; CHECK: define void @f_sanitize_concurrency() [[SANITIZE_CONCURRENCY:#[0-9]+]]
+define void @f_sanitize_concurrency() sanitize_concurrency {
+  ret void
+}
+
 ; CHECK: attributes #0 = { noreturn }
 ; CHECK: attributes #1 = { nounwind }
 ; CHECK: attributes #2 = { memory(none) }
@@ -671,4 +676,5 @@ define nofreeobj ptr @nofreeobj(ptr nofreeobj %p) {
 ; CHECK: attributes [[OPTDEBUG]] = { optdebug }
 ; CHECK: attributes [[NODIVERGENCESOURCE]] = { nodivergencesource }
 ; CHECK: attributes [[NOIPA]] = { noipa }
+; CHECK: attributes [[SANITIZE_CONCURRENCY]] = { sanitize_concurrency }
 ; CHECK: attributes #[[NOBUILTIN]] = { nobuiltin }
diff --git a/llvm/test/Bitcode/compatibility.ll b/llvm/test/Bitcode/compatibility.ll
index d8784b4d8a2f21..3a5a9be9afca5e 100644
--- a/llvm/test/Bitcode/compatibility.ll
+++ b/llvm/test/Bitcode/compatibility.ll
@@ -1827,7 +1827,7 @@ exit:
   ; CHECK: select <2 x i1> <i1 true, i1 false>, <2 x i8> <i8 2, i8 3>, <2 x i8> <i8 3, i8 2>
 
   call void @f.nobuiltin() builtin
-  ; CHECK: call void @f.nobuiltin() #87
+  ; CHECK: call void @f.nobuiltin() #88
 
   call fastcc noalias ptr @f.noalias() noinline
   ; CHECK: call fastcc noalias ptr @f.noalias() #12
@@ -2536,6 +2536,9 @@ define void @denormal_fpenv__preservesign_preservesign_float_dynamic_dynamic() d
   ret void
 }
 
+declare void @f.sanitize_concurrency() sanitize_concurrency
+; CHECK: declare void @f.sanitize_concurrency() #87
+
 ; CHECK: attributes #0 = { alignstack=4 }
 ; CHECK: attributes #1 = { alignstack=8 }
 ; CHECK: attributes #2 = { alwaysinline }
@@ -2623,7 +2626,8 @@ define void @denormal_fpenv__preservesign_preservesign_float_dynamic_dynamic() d
 ; CHECK: attributes #84 = { denormal_fpenv(float: dynamic|positivezero) }
 ; CHECK: attributes #85 = { denormal_fpenv(float: dynamic) }
 ; CHECK: attributes #86 = { denormal_fpenv(preservesign, float: dynamic) }
-; CHECK: attributes #87 = { builtin }
+; CHECK: attributes #87 = { sanitize_concurrency }
+; CHECK: attributes #88 = { builtin }
 
 ;; Metadata
 
diff --git a/llvm/test/CodeGen/AMDGPU/amdgpu-attributor-min-agpr-alloc.ll b/llvm/test/CodeGen/AMDGPU/amdgpu-attributor-min-agpr-alloc.ll
index 02eef5ab5e98b8..f2ad67bebd2919 100644
--- a/llvm/test/CodeGen/AMDGPU/amdgpu-attributor-min-agpr-alloc.ll
+++ b/llvm/test/CodeGen/AMDGPU/amdgpu-attributor-min-agpr-alloc.ll
@@ -179,7 +179,7 @@ define amdgpu_kernel void @kernel_calls_extern() {
 
 define amdgpu_kernel void @kernel_calls_extern_marked_callsite() {
 ; CHECK-LABEL: define amdgpu_kernel void @kernel_calls_extern_marked_callsite() {
-; CHECK-NEXT:    call void @unknown() #[[ATTR35:[0-9]+]]
+; CHECK-NEXT:    call void @unknown() #[[ATTR36:[0-9]+]]
 ; CHECK-NEXT:    call void @use_most()
 ; CHECK-NEXT:    ret void
 ;
@@ -203,7 +203,7 @@ define amdgpu_kernel void @kernel_calls_indirect(ptr %indirect) {
 define amdgpu_kernel void @kernel_calls_indirect_marked_callsite(ptr %indirect) {
 ; CHECK-LABEL: define amdgpu_kernel void @kernel_calls_indirect_marked_callsite(
 ; CHECK-SAME: ptr [[INDIRECT:%.*]]) {
-; CHECK-NEXT:    call void [[INDIRECT]]() #[[ATTR35]]
+; CHECK-NEXT:    call void [[INDIRECT]]() #[[ATTR36]]
 ; CHECK-NEXT:    call void @use_most()
 ; CHECK-NEXT:    ret void
 ;
@@ -1051,10 +1051,21 @@ define amdgpu_kernel void @kernel_sanitize_thread() sanitize_thread {
   ret void
 }
 
+define amdgpu_kernel void @kernel_sanitize_concurrency() sanitize_concurrency {
+; CHECK: Function Attrs: sanitize_concurrency
+; CHECK-LABEL: define amdgpu_kernel void @kernel_sanitize_concurrency(
+; CHECK-SAME: ) #[[ATTR27:[0-9]+]] {
+; CHECK-NEXT:    call void @use_most()
+; CHECK-NEXT:    ret void
+;
+  call void @use_most()
+  ret void
+}
+
 define amdgpu_kernel void @kernel_sanitize_hwaddress() sanitize_hwaddress {
 ; CHECK: Function Attrs: sanitize_hwaddress
 ; CHECK-LABEL: define amdgpu_kernel void @kernel_sanitize_hwaddress(
-; CHECK-SAME: ) #[[ATTR27:[0-9]+]] {
+; CHECK-SAME: ) #[[ATTR28:[0-9]+]] {
 ; CHECK-NEXT:    call void @use_most()
 ; CHECK-NEXT:    ret void
 ;
@@ -1069,7 +1080,7 @@ define amdgpu_kernel void @kernel_sanitize_hwaddress() sanitize_hwaddress {
 define amdgpu_kernel void @kernel_sanitize_address_preannotated() #2 {
 ; CHECK: Function Attrs: sanitize_address
 ; CHECK-LABEL: define amdgpu_kernel void @kernel_sanitize_address_preannotated(
-; CHECK-SAME: ) #[[ATTR28:[0-9]+]] {
+; CHECK-SAME: ) #[[ATTR29:[0-9]+]] {
 ; CHECK-NEXT:    call void @use_most()
 ; CHECK-NEXT:    ret void
 ;
@@ -1092,7 +1103,7 @@ define void @sanitized_callee() sanitize_address {
 
 define amdgpu_kernel void @kernel_calls_sanitized_callee() {
 ; CHECK-LABEL: define amdgpu_kernel void @kernel_calls_sanitized_callee(
-; CHECK-SAME: ) #[[ATTR29:[0-9]+]] {
+; CHECK-SAME: ) #[[ATTR30:[0-9]+]] {
 ; CHECK-NEXT:    call void @sanitized_callee()
 ; CHECK-NEXT:    call void @use_most()
 ; CHECK-NEXT:    ret void
@@ -1140,15 +1151,16 @@ attributes #2 = { sanitize_address "amdgpu-agpr-alloc"="0" }
 ; CHECK: attributes #[[ATTR24]] = { sanitize_address "amdgpu-no-wwm" }
 ; CHECK: attributes #[[ATTR25]] = { sanitize_memory "amdgpu-no-wwm" }
 ; CHECK: attributes #[[ATTR26]] = { sanitize_thread "amdgpu-no-wwm" }
-; CHECK: attributes #[[ATTR27]] = { sanitize_hwaddress "amdgpu-no-wwm" }
-; CHECK: attributes #[[ATTR28]] = { sanitize_address "amdgpu-agpr-alloc"="0" "amdgpu-no-wwm" }
-; CHECK: attributes #[[ATTR29]] = { "amdgpu-no-wwm" }
-; CHECK: attributes #[[ATTR30:[0-9]+]] = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
-; CHECK: attributes #[[ATTR31:[0-9]+]] = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
-; CHECK: attributes #[[ATTR32:[0-9]+]] = { nocallback nofree nosync nounwind willreturn memory(read) }
-; CHECK: attributes #[[ATTR33:[0-9]+]] = { nounwind }
-; CHECK: attributes #[[ATTR34:[0-9]+]] = { nocallback nounwind }
-; CHECK: attributes #[[ATTR35]] = { "amdgpu-agpr-alloc"="0" }
+; CHECK: attributes #[[ATTR27]] = { sanitize_concurrency "amdgpu-no-wwm" }
+; CHECK: attributes #[[ATTR28]] = { sanitize_hwaddress "amdgpu-no-wwm" }
+; CHECK: attributes #[[ATTR29]] = { sanitize_address "amdgpu-agpr-alloc"="0" "amdgpu-no-wwm" }
+; CHECK: attributes #[[ATTR30]] = { "amdgpu-no-wwm" }
+; CHECK: attributes #[[ATTR31:[0-9]+]] = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
+; CHECK: attributes #[[ATTR32:[0-9]+]] = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
+; CHECK: attributes #[[ATTR33:[0-9]+]] = { nocallback nofree nosync nounwind willreturn memory(read) }
+; CHECK: attributes #[[ATTR34:[0-9]+]] = { nounwind }
+; CHECK: attributes #[[ATTR35:[0-9]+]] = { nocallback nounwind }
+; CHECK: attributes #[[ATTR36]] = { "amdgpu-agpr-alloc"="0" }
 ;.
 ; CHECK: [[META0]] = !{!"a55"}
 ; CHECK: [[META1]] = !{!"v55"}
diff --git a/llvm/test/CodeGen/X86/x86-sanitizer-shrink-wrapping.ll b/llvm/test/CodeGen/X86/x86-sanitizer-shrink-wrapping.ll
index 4379c408531c1d..43c2d1a7857ea3 100644
--- a/llvm/test/CodeGen/X86/x86-sanitizer-shrink-wrapping.ll
+++ b/llvm/test/CodeGen/X86/x86-sanitizer-shrink-wrapping.ll
@@ -35,6 +35,36 @@ end:
   ret void
 }
 
+; CHECK-LABEL: sanitize_concurrency:
+; CHECK: pushq
+; CHECK: incl 40
+; CHECK: popq
+; CHECK-NEXT: retq
+; CHECK: movl $40, %edi
+; CHECK-NEXT: callq ___asan_report_load4
+define void @sanitize_concurrency() #1 {
+entry:
+  %tmp = load i8, ptr inttoptr (i64 17592186044421 to ptr)
+  %tmp1 = icmp ne i8 %tmp, 0
+  br i1 %tmp1, label %if.then, label %else
+
+if.then:
+  %tmp3 = icmp sge i8 3, %tmp
+  br i1 %tmp3, label %else, label %end
+
+else:
+  call void @__asan_report_load4(i64 40)
+  call void asm sideeffect "", ""()
+  unreachable
+
+end:
+  %tmp6 = load i32, ptr inttoptr (i64 40 to ptr), align 8
+  %inc = add nsw i32 %tmp6, 1
+  store i32 %inc, ptr inttoptr (i64 40 to ptr), align 8
+  ret void
+}
+
 attributes #0 = { sanitize_address nounwind "frame-pointer"="all" }
+attributes #1 = { sanitize_concurrency nounwind "frame-pointer"="all" }
 
 declare void @__asan_report_load4(i64)
diff --git a/llvm/test/Transforms/CodeExtractor/PartialInlineAttributes.ll b/llvm/test/Transforms/CodeExtractor/PartialInlineAttributes.ll
index 1246c5de6834f3..d4991b8d8b0c0a 100644
--- a/llvm/test/Transforms/CodeExtractor/PartialInlineAttributes.ll
+++ b/llvm/test/Transforms/CodeExtractor/PartialInlineAttributes.ll
@@ -37,7 +37,7 @@ if.end:
   ret i32 %add
 }
 
-define i32 @callee_writeonly(i32 %v) writeonly ssp {
+define i32 @callee_writeonly(i32 %v) writeonly sanitize_concurrency ssp {
 entry:
   %cmp = icmp sgt i32 %v, 2000
   br i1 %cmp, label %if.then, label %if.end
@@ -76,7 +76,7 @@ attributes #0 = {
   sanitize_thread ssp sspreq sspstrong uwtable "foo"="bar"
   "patchable-function"="prologue-short-redirect" "probe-stack"="_foo_guard" "stack-probe-size"="4096" }
 
-; CHECK: attributes [[FN_ATTRS0]] = { ssp
+; CHECK: attributes [[FN_ATTRS0]] = { sanitize_concurrency ssp
 ; CHECK: attributes [[FN_ATTRS]] = { inlinehint minsize noduplicate noimplicitfloat norecurse noredzone nounwind nonlazybind optsize safestack sanitize_address sanitize_hwaddress sanitize_memory sanitize_thread ssp sspreq sspstrong uwtable "foo"="bar" "patchable-function"="prologue-short-redirect" "probe-stack"="_foo_guard" "stack-probe-size"="4096" }
 
 ; attributes to drop
diff --git a/llvm/test/Transforms/ExpandMemCmp/X86/sanitizer-skip.ll b/llvm/test/Transforms/ExpandMemCmp/X86/sanitizer-skip.ll
index d403b60f0a3dd4..b3260488325fdd 100644
--- a/llvm/test/Transforms/ExpandMemCmp/X86/sanitizer-skip.ll
+++ b/llvm/test/Transforms/ExpandMemCmp/X86/sanitizer-skip.ll
@@ -36,9 +36,19 @@ define i32 @cmp_tsan(ptr nocapture readonly %x, ptr nocapture readonly %y) sanit
   ret i32 %call
 }
 
+define i32 @cmp_csan(ptr nocapture readonly %x, ptr nocapture readonly %y) sanitize_concurrency {
+; CHECK-LABEL: define i32 @cmp_csan(
+; CHECK-SAME: ptr readonly captures(none) [[X:%.*]], ptr readonly captures(none) [[Y:%.*]]) #[[ATTR3:[0-9]+]] {
+; CHECK-NEXT:    [[CALL:%.*]] = tail call i32 @memcmp(ptr [[X]], ptr [[Y]], i64 4)
+; CHECK-NEXT:    ret i32 [[CALL]]
+;
+  %call = tail call i32 @memcmp(ptr %x, ptr %y, i64 4)
+  ret i32 %call
+}
+
 define i32 @cmp_hwasan(ptr nocapture readonly %x, ptr nocapture readonly %y) sanitize_hwaddress {
 ; CHECK-LABEL: define i32 @cmp_hwasan(
-; CHECK-SAME: ptr readonly captures(none) [[X:%.*]], ptr readonly captures(none) [[Y:%.*]]) #[[ATTR3:[0-9]+]] {
+; CHECK-SAME: ptr readonly captures(none) [[X:%.*]], ptr readonly captures(none) [[Y:%.*]]) #[[ATTR4:[0-9]+]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = tail call i32 @memcmp(ptr [[X]], ptr [[Y]], i64 4)
 ; CHECK-NEXT:    ret i32 [[CALL]]
 ;
diff --git a/llvm/test/Transforms/HotColdSplit/X86/do-not-split.ll b/llvm/test/Transforms/HotColdSplit/X86/do-not-split.ll
index 30ece1adc75be5..bce29ee592e224 100644
--- a/llvm/test/Transforms/HotColdSplit/X86/do-not-split.ll
+++ b/llvm/test/Transforms/HotColdSplit/X86/do-not-split.ll
@@ -162,6 +162,20 @@ if.end:                                           ; preds = %entry
   ret void
 }
 
+; CHECK-LABEL: @sanitize_concurrency
+; CHECK-NOT: sanitize_concurrency.cold.1
+define void @sanitize_concurrency(i1 %arg) sanitize_concurrency {
+entry:
+  br i1 %arg, label %if.then, label %if.end
+
+if.then:                                          ; preds = %entry
+  call void @sink()
+  ret void
+
+if.end:                                           ; preds = %entry
+  ret void
+}
+
 ; CHECK-LABEL: @sanitize_memory
 ; CHECK-NOT: sanitize_memory.cold.1
 define void @sanitize_memory(i1 %arg) sanitize_memory {
diff --git a/llvm/test/Transforms/SimplifyCFG/no_speculative_loads_with_tsan.ll b/llvm/test/Transforms/SimplifyCFG/no_speculative_loads_with_tsan.ll
index f201b4a5acea4c..49cb5d7adcccb6 100644
--- a/llvm/test/Transforms/SimplifyCFG/no_speculative_loads_with_tsan.ll
+++ b/llvm/test/Transforms/SimplifyCFG/no_speculative_loads_with_tsan.ll
@@ -48,3 +48,28 @@ return:                                           ; preds = %entry, %if.then
   %retval = phi i32 [ %0, %if.then ], [ 0, %entry ]
   ret i32 %retval
 }
+
+define i32 @TestCsan(i32 %cond) nounwind readonly uwtable sanitize_concurrency {
+; CHECK-LABEL: @TestCsan(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[TOBOOL:%.*]] = icmp eq i32 [[COND:%.*]], 0
+; CHECK-NEXT:    br i1 [[TOBOOL]], label [[RETURN:%.*]], label [[IF_THEN:%.*]]
+; CHECK:       if.then:
+; CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr @g, align 4
+; CHECK-NEXT:    br label [[RETURN]]
+; CHECK:       return:
+; CHECK-NEXT:    [[RETVAL:%.*]] = phi i32 [ [[TMP0]], [[IF_THEN]] ], [ 0, [[ENTRY:%.*]] ]
+; CHECK-NEXT:    ret i32 [[RETVAL]]
+;
+entry:
+  %tobool = icmp eq i32 %cond, 0
+  br i1 %tobool, label %return, label %if.then
+
+if.then:                                          ; preds = %entry
+  %0 = load i32, ptr @g, align 4
+  br label %return
+
+return:                                           ; preds = %entry, %if.then
+  %retval = phi i32 [ %0, %if.then ], [ 0, %entry ]
+  ret i32 %retval
+}
diff --git a/llvm/test/Verifier/sanitize-concurrency-attrs.ll b/llvm/test/Verifier/sanitize-concurrency-attrs.ll
new file mode 100644
index 00000000000000..70fda2f06f5232
--- /dev/null
+++ b/llvm/test/Verifier/sanitize-concurrency-attrs.ll
@@ -0,0 +1,9 @@
+; RUN: not llvm-as -disable-output %s 2>&1 | FileCheck %s
+
+; CHECK: Attributes 'sanitize_thread and sanitize_concurrency' are incompatible!
+; CHECK-NEXT: ptr @sanitize_unsafe
+define void @sanitize_unsafe() #0 {
+  ret void
+}
+
+attributes #0 = { sanitize_thread sanitize_concurrency }



More information about the llvm-commits mailing list