Author: Matt Arsenault
Date: 2026-09-15T10:04:01Z
New Revision: e733cebdf7c9c87bec77547f3c377f514a22c9b3

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

LOG: CodeGen: Replace TargetOptions::ThreadModel with module flag (#223157)

Migrate the threading-model consumers off TargetOptions::ThreadModel and
onto the "thread-model" module flag. The main user was ARM's pass config,
which is moved into AtomicExpand, which now performs the single-thread 
expansion directly.

Few tests use the -thread-model flag, so delete it and migrate those
cases to use the module flag.

Co-authored-by: Claude (Claude-Opus-4.8) <[email protected]>

Co-authored-by: Claude (Claude-Opus-4.8) <[email protected]>

Added: 
    llvm/test/Transforms/AtomicExpand/ARM/thread-model-single.ll

Modified: 
    clang/lib/CodeGen/BackendUtil.cpp
    llvm/include/llvm/Analysis/TargetTransformInfo.h
    llvm/include/llvm/Analysis/TargetTransformInfoImpl.h
    llvm/include/llvm/CodeGen/BasicTTIImpl.h
    llvm/include/llvm/CodeGen/CommandFlags.h
    llvm/include/llvm/Target/TargetOptions.h
    llvm/lib/Analysis/TargetTransformInfo.cpp
    llvm/lib/CodeGen/AtomicExpandPass.cpp
    llvm/lib/CodeGen/CommandFlags.cpp
    llvm/lib/Target/ARM/ARMTargetMachine.cpp
    llvm/lib/Transforms/Scalar/LICM.cpp
    llvm/test/CodeGen/ARM/atomic-op.ll

Removed: 
    


################################################################################
diff  --git a/clang/lib/CodeGen/BackendUtil.cpp 
b/clang/lib/CodeGen/BackendUtil.cpp
index cd4d5489bb8cb..9ee7e3e3b8f89 100644
--- a/clang/lib/CodeGen/BackendUtil.cpp
+++ b/clang/lib/CodeGen/BackendUtil.cpp
@@ -379,14 +379,6 @@ static bool initTargetOptions(const CompilerInstance &CI,
   const auto &TargetOpts = CI.getTargetOpts();
   const auto &LangOpts = CI.getLangOpts();
   const auto &HSOpts = CI.getHeaderSearchOpts();
-  switch (LangOpts.getThreadModel()) {
-  case LangOptions::ThreadModelKind::POSIX:
-    Options.ThreadModel = llvm::ThreadModel::POSIX;
-    break;
-  case LangOptions::ThreadModelKind::Single:
-    Options.ThreadModel = llvm::ThreadModel::Single;
-    break;
-  }
 
   Options.MCOptions.BinutilsVersion =
       llvm::MCTargetOptions::parseBinutilsVersion(CodeGenOpts.BinutilsVersion);

diff  --git a/llvm/include/llvm/Analysis/TargetTransformInfo.h 
b/llvm/include/llvm/Analysis/TargetTransformInfo.h
index cf5e940eeb1f4..f1731bc364555 100644
--- a/llvm/include/llvm/Analysis/TargetTransformInfo.h
+++ b/llvm/include/llvm/Analysis/TargetTransformInfo.h
@@ -614,8 +614,6 @@ class TargetTransformInfo {
 
   LLVM_ABI unsigned getAssumedAddrSpace(const Value *V) const;
 
-  LLVM_ABI bool isSingleThreaded() const;
-
   LLVM_ABI std::pair<const Value *, unsigned>
   getPredicatedAddrSpace(const Value *V) const;
 

diff  --git a/llvm/include/llvm/Analysis/TargetTransformInfoImpl.h 
b/llvm/include/llvm/Analysis/TargetTransformInfoImpl.h
index d9e7f82496d49..84cb3a6e664b9 100644
--- a/llvm/include/llvm/Analysis/TargetTransformInfoImpl.h
+++ b/llvm/include/llvm/Analysis/TargetTransformInfoImpl.h
@@ -205,8 +205,6 @@ class LLVM_ABI TargetTransformInfoImplBase {
 
   virtual unsigned getAssumedAddrSpace(const Value *V) const { return -1; }
 
-  virtual bool isSingleThreaded() const { return false; }
-
   virtual std::pair<const Value *, unsigned>
   getPredicatedAddrSpace(const Value *V) const {
     return std::make_pair(nullptr, -1);

diff  --git a/llvm/include/llvm/CodeGen/BasicTTIImpl.h 
b/llvm/include/llvm/CodeGen/BasicTTIImpl.h
index ae720e221dd04..9723016ca23b4 100644
--- a/llvm/include/llvm/CodeGen/BasicTTIImpl.h
+++ b/llvm/include/llvm/CodeGen/BasicTTIImpl.h
@@ -450,11 +450,6 @@ class BasicTTIImplBase : public 
TargetTransformInfoImplCRTPBase<T> {
     return getTLI()->getTargetMachine().getAssumedAddrSpace(V);
   }
 
-  bool isSingleThreaded() const override {
-    return getTLI()->getTargetMachine().Options.ThreadModel ==
-           ThreadModel::Single;
-  }
-
   std::pair<const Value *, unsigned>
   getPredicatedAddrSpace(const Value *V) const override {
     return getTLI()->getTargetMachine().getPredicatedAddrSpace(V);

diff  --git a/llvm/include/llvm/CodeGen/CommandFlags.h 
b/llvm/include/llvm/CodeGen/CommandFlags.h
index 780f1495e728b..767c59c1a262a 100644
--- a/llvm/include/llvm/CodeGen/CommandFlags.h
+++ b/llvm/include/llvm/CodeGen/CommandFlags.h
@@ -44,8 +44,6 @@ LLVM_ABI std::vector<std::string> getMAttrs();
 LLVM_ABI Reloc::Model getRelocModel();
 LLVM_ABI std::optional<Reloc::Model> getExplicitRelocModel();
 
-LLVM_ABI ThreadModel getThreadModel();
-
 LLVM_ABI CodeModel::Model getCodeModel();
 LLVM_ABI std::optional<CodeModel::Model> getExplicitCodeModel();
 

diff  --git a/llvm/include/llvm/Target/TargetOptions.h 
b/llvm/include/llvm/Target/TargetOptions.h
index 726c5b49b028b..78ad27a00e4f9 100644
--- a/llvm/include/llvm/Target/TargetOptions.h
+++ b/llvm/include/llvm/Target/TargetOptions.h
@@ -300,10 +300,6 @@ class TargetOptions {
   /// If greater than 0, override TargetLoweringBase::PrefLoopAlignment.
   unsigned LoopAlignment = 0;
 
-  /// ThreadModel - This flag specifies the type of threading model to assume
-  /// for things like atomics
-  llvm::ThreadModel ThreadModel = llvm::ThreadModel::POSIX;
-
   /// EABIVersion - This flag specifies the EABI version
   EABI EABIVersion = EABI::Default;
 

diff  --git a/llvm/lib/Analysis/TargetTransformInfo.cpp 
b/llvm/lib/Analysis/TargetTransformInfo.cpp
index 71f55f2e7e046..d1fc00164b442 100644
--- a/llvm/lib/Analysis/TargetTransformInfo.cpp
+++ b/llvm/lib/Analysis/TargetTransformInfo.cpp
@@ -351,10 +351,6 @@ unsigned TargetTransformInfo::getAssumedAddrSpace(const 
Value *V) const {
   return TTIImpl->getAssumedAddrSpace(V);
 }
 
-bool TargetTransformInfo::isSingleThreaded() const {
-  return TTIImpl->isSingleThreaded();
-}
-
 std::pair<const Value *, unsigned>
 TargetTransformInfo::getPredicatedAddrSpace(const Value *V) const {
   return TTIImpl->getPredicatedAddrSpace(V);

diff  --git a/llvm/lib/CodeGen/AtomicExpandPass.cpp 
b/llvm/lib/CodeGen/AtomicExpandPass.cpp
index f2ffa40030cc0..296b411c17717 100644
--- a/llvm/lib/CodeGen/AtomicExpandPass.cpp
+++ b/llvm/lib/CodeGen/AtomicExpandPass.cpp
@@ -65,6 +65,7 @@ class AtomicExpandImpl {
   const TargetLowering *TLI = nullptr;
   const LibcallLoweringInfo *LibcallLowering = nullptr;
   const DataLayout *DL = nullptr;
+  bool SingleThreaded = false;
 
 private:
   /// Callback type for emitting a cmpxchg instruction during RMW expansion.
@@ -148,6 +149,7 @@ class AtomicExpandImpl {
   bool expandAtomicRMWToCmpXchg(AtomicRMWInst *AI,
                                 CreateCmpXchgInstFun CreateCmpXchg);
 
+  bool lowerToNonAtomic(Instruction *I);
   bool processAtomicInstr(Instruction *I);
 
 public:
@@ -333,7 +335,48 @@ bool AtomicExpandImpl::tryInsertFencesForAtomic(AtomicInst 
*AtomicI,
   return false;
 }
 
+/// In a single-threaded environment, atomic operations can be lowered to their
+/// non-atomic equivalents: fences are removed, and atomic loads, stores, RMW,
+/// and cmpxchg become plain memory operations.
+bool AtomicExpandImpl::lowerToNonAtomic(Instruction *I) {
+  if (auto *FI = dyn_cast<FenceInst>(I)) {
+    FI->eraseFromParent();
+    return true;
+  }
+
+  if (auto *CXI = dyn_cast<AtomicCmpXchgInst>(I))
+    return lowerAtomicCmpXchgInst(CXI);
+
+  if (auto *RMWI = dyn_cast<AtomicRMWInst>(I))
+    return lowerAtomicRMWInst(RMWI);
+
+  if (auto *LI = dyn_cast<LoadInst>(I)) {
+    if (LI->isAtomic()) {
+      LI->setAtomic(AtomicOrdering::NotAtomic);
+      LI->setElementwise(false);
+      return true;
+    }
+
+    return false;
+  }
+
+  if (auto *SI = dyn_cast<StoreInst>(I)) {
+    if (SI->isAtomic()) {
+      SI->setAtomic(AtomicOrdering::NotAtomic);
+      SI->setElementwise(false);
+      return true;
+    }
+
+    return false;
+  }
+
+  return false;
+}
+
 bool AtomicExpandImpl::processAtomicInstr(Instruction *I) {
+  if (SingleThreaded)
+    return lowerToNonAtomic(I);
+
   if (auto *LI = dyn_cast<LoadInst>(I)) {
     if (!LI->isAtomic())
       return false;
@@ -457,8 +500,11 @@ bool AtomicExpandImpl::processAtomicInstr(Instruction *I) {
 bool AtomicExpandImpl::run(Function &F,
                            const ModuleLibcallLoweringInfo &LibcallResult,
                            const TargetMachine *TM) {
+  SingleThreaded = F.getParent()->getThreadModel() == ThreadModel::Single;
+
   const auto *Subtarget = TM->getSubtargetImpl(F);
-  if (!Subtarget->enableAtomicExpand())
+  // In a single-threaded environment atomics are lowered to non-atomic form
+  if (!SingleThreaded && !Subtarget->enableAtomicExpand())
     return false;
   TLI = Subtarget->getTargetLowering();
   LibcallLowering = &getLibcallLowering(LibcallResult, *Subtarget);

diff  --git a/llvm/lib/CodeGen/CommandFlags.cpp 
b/llvm/lib/CodeGen/CommandFlags.cpp
index 7f1b60dcb271a..bf53b28c13a82 100644
--- a/llvm/lib/CodeGen/CommandFlags.cpp
+++ b/llvm/lib/CodeGen/CommandFlags.cpp
@@ -70,7 +70,6 @@ CGOPT(std::string, MCPU)
 CGOPT(std::string, MTune)
 CGLIST(std::string, MAttrs)
 CGOPT_EXP(Reloc::Model, RelocModel)
-CGOPT(ThreadModel, ThreadModel)
 CGOPT_EXP(CodeModel::Model, CodeModel)
 CGOPT_EXP(uint64_t, LargeDataThreshold)
 CGOPT(ExceptionHandling, ExceptionModel)
@@ -156,15 +155,6 @@ codegen::RegisterCodeGenFlags::RegisterCodeGenFlags() {
                      "Combination of ropi and rwpi")));
   CGBINDOPT(RelocModel);
 
-  static cl::opt<ThreadModel> ThreadModel(
-      "thread-model", cl::desc("Choose threading model"),
-      cl::init(llvm::ThreadModel::POSIX),
-      cl::values(
-          clEnumValN(llvm::ThreadModel::POSIX, "posix", "POSIX thread model"),
-          clEnumValN(llvm::ThreadModel::Single, "single",
-                     "Single thread model")));
-  CGBINDOPT(ThreadModel);
-
   static cl::opt<CodeModel::Model> CodeModel(
       "code-model", cl::desc("Choose code model"),
       cl::values(clEnumValN(CodeModel::Tiny, "tiny", "Tiny code model"),
@@ -585,7 +575,6 @@ codegen::InitTargetOptionsFromCodeGenFlags(const Triple 
&TheTriple) {
 
   Options.MCOptions = mc::InitMCTargetOptionsFromFlags();
 
-  Options.ThreadModel = getThreadModel();
   Options.EABIVersion = getEABIVersion();
   Options.DebuggerTuning = getDebuggerTuningOpt();
   Options.SwiftAsyncFramePointer = getSwiftAsyncFramePointer();

diff  --git a/llvm/lib/Target/ARM/ARMTargetMachine.cpp 
b/llvm/lib/Target/ARM/ARMTargetMachine.cpp
index 914b362b9acaf..66c6c09b4b27b 100644
--- a/llvm/lib/Target/ARM/ARMTargetMachine.cpp
+++ b/llvm/lib/Target/ARM/ARMTargetMachine.cpp
@@ -410,10 +410,7 @@ std::unique_ptr<CSEConfigBase> 
ARMPassConfig::getCSEConfig() const {
 }
 
 void ARMPassConfig::addIRPasses() {
-  if (TM->Options.ThreadModel == ThreadModel::Single)
-    addPass(createLowerAtomicPass());
-  else
-    addPass(createAtomicExpandLegacyPass());
+  addPass(createAtomicExpandLegacyPass());
 
   // Cmpxchg instructions are often used with a subsequent comparison to
   // determine whether it succeeded. We can exploit existing control-flow in

diff  --git a/llvm/lib/Transforms/Scalar/LICM.cpp 
b/llvm/lib/Transforms/Scalar/LICM.cpp
index cda59610ff4fc..a82b249a4384c 100644
--- a/llvm/lib/Transforms/Scalar/LICM.cpp
+++ b/llvm/lib/Transforms/Scalar/LICM.cpp
@@ -73,6 +73,7 @@
 #include "llvm/IR/IntrinsicInst.h"
 #include "llvm/IR/LLVMContext.h"
 #include "llvm/IR/Metadata.h"
+#include "llvm/IR/Module.h"
 #include "llvm/IR/PatternMatch.h"
 #include "llvm/IR/PredIteratorCache.h"
 #include "llvm/InitializePasses.h"
@@ -1986,13 +1987,17 @@ bool isNotVisibleOnUnwindInLoop(const Value *Object, 
const Loop *L,
          isNotCapturedBeforeOrInLoop(Object, L, DT);
 }
 
-bool isThreadLocalObject(const Value *Object, const Loop *L, DominatorTree *DT,
-                         TargetTransformInfo *TTI) {
+bool isThreadLocalObject(const Value *Object, const Loop *L,
+                         DominatorTree *DT) {
   // The object must be function-local to start with, and then not captured
   // before/in the loop.
-  return (isIdentifiedFunctionLocal(Object) &&
-          isNotCapturedBeforeOrInLoop(Object, L, DT)) ||
-         (TTI->isSingleThreaded() || SingleThread);
+  if (isIdentifiedFunctionLocal(Object) &&
+      isNotCapturedBeforeOrInLoop(Object, L, DT))
+    return true;
+
+  // In a single-threaded environment, all objects are effectively 
thread-local.
+  const Module *M = L->getHeader()->getModule();
+  return M->getThreadModel() == ThreadModel::Single || SingleThread;
 }
 
 } // namespace
@@ -2241,7 +2246,7 @@ bool llvm::promoteLoopAccessesToScalars(
         (!ExplicitlyDereferenceableOnly ||
          isDereferenceablePointer(SomePtr, AccessTy, MDL,
                                   /*IgnoreFree=*/true)) &&
-        isThreadLocalObject(Object, CurLoop, DT, TTI))
+        isThreadLocalObject(Object, CurLoop, DT))
       StoreSafety = StoreSafe;
   }
 

diff  --git a/llvm/test/CodeGen/ARM/atomic-op.ll 
b/llvm/test/CodeGen/ARM/atomic-op.ll
index 9cbed5e98b004..6b12ba704fbd3 100644
--- a/llvm/test/CodeGen/ARM/atomic-op.ll
+++ b/llvm/test/CodeGen/ARM/atomic-op.ll
@@ -1,9 +1,12 @@
-; RUN: llc < %s -mtriple=armv7-apple-ios -verify-machineinstrs | FileCheck %s 
--check-prefix=CHECK --check-prefix CHECK-ARMV7
-; RUN: llc < %s -mtriple=thumbv7-apple-ios -verify-machineinstrs | FileCheck 
%s --check-prefix=CHECK --check-prefix=CHECK-T2
-; RUN: llc < %s -mtriple=thumbv6-apple-ios -verify-machineinstrs | FileCheck 
%s --check-prefix=CHECK-T1
-; RUN: llc < %s -mtriple=thumbv6-apple-ios -verify-machineinstrs 
-mcpu=cortex-m0 | FileCheck %s --check-prefix=CHECK-T1-M0
-; RUN: llc < %s -mtriple=thumbv7--none-eabi -thread-model single 
-verify-machineinstrs | FileCheck %s --check-prefix=CHECK-BAREMETAL
+; RUN: split-file %s %t
+; RUN: llc < %t/body.ll -mtriple=armv7-apple-ios -verify-machineinstrs | 
FileCheck %s --check-prefix=CHECK --check-prefix CHECK-ARMV7
+; RUN: llc < %t/body.ll -mtriple=thumbv7-apple-ios -verify-machineinstrs | 
FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-T2
+; RUN: llc < %t/body.ll -mtriple=thumbv6-apple-ios -verify-machineinstrs | 
FileCheck %s --check-prefix=CHECK-T1
+; RUN: llc < %t/body.ll -mtriple=thumbv6-apple-ios -verify-machineinstrs 
-mcpu=cortex-m0 | FileCheck %s --check-prefix=CHECK-T1-M0
+;; The single-threaded thread model is selected by the "thread-model" module 
flag.
+; RUN: cat %t/body.ll %t/single.ll | llc -mtriple=thumbv7--none-eabi 
-verify-machineinstrs | FileCheck %s --check-prefix=CHECK-BAREMETAL
 
+;--- body.ll
 target datalayout = "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64"
 
 ; CHECK-LABEL: _func:
@@ -426,3 +429,8 @@ define void @load_fence_store_monotonic(ptr %mem1, ptr 
%mem2) {
 
   ret void
 }
+
+;--- single.ll
+; Appended to the body to select the single-threaded thread model.
+!llvm.module.flags = !{!0}
+!0 = !{i32 1, !"thread-model", !"single"}

diff  --git a/llvm/test/Transforms/AtomicExpand/ARM/thread-model-single.ll 
b/llvm/test/Transforms/AtomicExpand/ARM/thread-model-single.ll
new file mode 100644
index 0000000000000..8f2ef27ad4be8
--- /dev/null
+++ b/llvm/test/Transforms/AtomicExpand/ARM/thread-model-single.ll
@@ -0,0 +1,182 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; RUN: opt -passes='require<libcall-lowering-info>,atomic-expand' -S 
-mtriple=thumbv7--none-eabi %s | FileCheck %s
+
+;; With the "thread-model"="single" module flag, AtomicExpand lowers
+;; atomic operations to their non-atomic form. fences are dropped and
+;; atomic loads/stores/rmw/cmpxchg become plain memory operations,
+;; with no ldrex/strex or dmb barriers emitted.
+
+define i32 @load(ptr %p) {
+; CHECK-LABEL: @load(
+; CHECK-NEXT:    [[V:%.*]] = load i32, ptr [[P:%.*]], align 4
+; CHECK-NEXT:    ret i32 [[V]]
+;
+  %v = load atomic i32, ptr %p seq_cst, align 4
+  ret i32 %v
+}
+
+define void @store(ptr %p, i32 %v) {
+; CHECK-LABEL: @store(
+; CHECK-NEXT:    store i32 [[V:%.*]], ptr [[P:%.*]], align 4
+; CHECK-NEXT:    ret void
+;
+  store atomic i32 %v, ptr %p seq_cst, align 4
+  ret void
+}
+
+define i32 @rmw(ptr %p, i32 %v) {
+; CHECK-LABEL: @rmw(
+; CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[P:%.*]], align 4
+; CHECK-NEXT:    [[NEW:%.*]] = add i32 [[TMP1]], [[V:%.*]]
+; CHECK-NEXT:    store i32 [[NEW]], ptr [[P]], align 4
+; CHECK-NEXT:    ret i32 [[TMP1]]
+;
+  %old = atomicrmw add ptr %p, i32 %v seq_cst
+  ret i32 %old
+}
+
+define { i32, i1 } @cmpxchg(ptr %p, i32 %cmp, i32 %new) {
+; CHECK-LABEL: @cmpxchg(
+; CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[P:%.*]], align 4
+; CHECK-NEXT:    [[TMP2:%.*]] = icmp eq i32 [[TMP1]], [[CMP:%.*]]
+; CHECK-NEXT:    [[TMP3:%.*]] = select i1 [[TMP2]], i32 [[NEW:%.*]], i32 
[[TMP1]]
+; CHECK-NEXT:    store i32 [[TMP3]], ptr [[P]], align 4
+; CHECK-NEXT:    [[TMP4:%.*]] = insertvalue { i32, i1 } poison, i32 [[TMP1]], 0
+; CHECK-NEXT:    [[TMP5:%.*]] = insertvalue { i32, i1 } [[TMP4]], i1 [[TMP2]], 
1
+; CHECK-NEXT:    ret { i32, i1 } [[TMP5]]
+;
+  %res = cmpxchg ptr %p, i32 %cmp, i32 %new seq_cst seq_cst
+  ret { i32, i1 } %res
+}
+
+define void @fence() {
+; CHECK-LABEL: @fence(
+; CHECK-NEXT:    ret void
+;
+  fence seq_cst
+  ret void
+}
+
+;; The volatile qualifier is preserved
+define i32 @load_volatile(ptr %p) {
+; CHECK-LABEL: @load_volatile(
+; CHECK-NEXT:    [[V:%.*]] = load volatile i32, ptr [[P:%.*]], align 4
+; CHECK-NEXT:    ret i32 [[V]]
+;
+  %v = load atomic volatile i32, ptr %p seq_cst, align 4
+  ret i32 %v
+}
+
+define void @store_volatile(ptr %p, i32 %v) {
+; CHECK-LABEL: @store_volatile(
+; CHECK-NEXT:    store volatile i32 [[V:%.*]], ptr [[P:%.*]], align 4
+; CHECK-NEXT:    ret void
+;
+  store atomic volatile i32 %v, ptr %p seq_cst, align 4
+  ret void
+}
+
+define i32 @rmw_volatile(ptr %p, i32 %v) {
+; CHECK-LABEL: @rmw_volatile(
+; CHECK-NEXT:    [[TMP1:%.*]] = load volatile i32, ptr [[P:%.*]], align 4
+; CHECK-NEXT:    [[NEW:%.*]] = add i32 [[TMP1]], [[V:%.*]]
+; CHECK-NEXT:    store volatile i32 [[NEW]], ptr [[P]], align 4
+; CHECK-NEXT:    ret i32 [[TMP1]]
+;
+  %old = atomicrmw volatile add ptr %p, i32 %v seq_cst
+  ret i32 %old
+}
+
+define { i32, i1 } @cmpxchg_volatile(ptr %p, i32 %cmp, i32 %new) {
+; CHECK-LABEL: @cmpxchg_volatile(
+; CHECK-NEXT:    [[TMP1:%.*]] = load volatile i32, ptr [[P:%.*]], align 4
+; CHECK-NEXT:    [[TMP2:%.*]] = icmp eq i32 [[TMP1]], [[CMP:%.*]]
+; CHECK-NEXT:    [[TMP3:%.*]] = select i1 [[TMP2]], i32 [[NEW:%.*]], i32 
[[TMP1]]
+; CHECK-NEXT:    store volatile i32 [[TMP3]], ptr [[P]], align 4
+; CHECK-NEXT:    [[TMP4:%.*]] = insertvalue { i32, i1 } poison, i32 [[TMP1]], 0
+; CHECK-NEXT:    [[TMP5:%.*]] = insertvalue { i32, i1 } [[TMP4]], i1 [[TMP2]], 
1
+; CHECK-NEXT:    ret { i32, i1 } [[TMP5]]
+;
+  %res = cmpxchg volatile ptr %p, i32 %cmp, i32 %new seq_cst seq_cst
+  ret { i32, i1 } %res
+}
+
+;; Elementwise atomic loads/stores/rmw also lose their atomicity, and the
+;; elementwise marker is dropped along with it.
+define <2 x i32> @load_elementwise(ptr %p) {
+; CHECK-LABEL: @load_elementwise(
+; CHECK-NEXT:    [[V:%.*]] = load <2 x i32>, ptr [[P:%.*]], align 8
+; CHECK-NEXT:    ret <2 x i32> [[V]]
+;
+  %v = load atomic elementwise <2 x i32>, ptr %p monotonic, align 8
+  ret <2 x i32> %v
+}
+
+define void @store_elementwise(ptr %p, <2 x i32> %v) {
+; CHECK-LABEL: @store_elementwise(
+; CHECK-NEXT:    store <2 x i32> [[V:%.*]], ptr [[P:%.*]], align 8
+; CHECK-NEXT:    ret void
+;
+  store atomic elementwise <2 x i32> %v, ptr %p monotonic, align 8
+  ret void
+}
+
+define <2 x i32> @rmw_elementwise(ptr %p, <2 x i32> %v) {
+; CHECK-LABEL: @rmw_elementwise(
+; CHECK-NEXT:    [[TMP1:%.*]] = load <2 x i32>, ptr [[P:%.*]], align 8
+; CHECK-NEXT:    [[NEW:%.*]] = add <2 x i32> [[TMP1]], [[V:%.*]]
+; CHECK-NEXT:    store <2 x i32> [[NEW]], ptr [[P]], align 8
+; CHECK-NEXT:    ret <2 x i32> [[TMP1]]
+;
+  %old = atomicrmw elementwise add ptr %p, <2 x i32> %v monotonic, align 8
+  ret <2 x i32> %old
+}
+
+;; A non-system syncscope is dropped along with the atomicity: the resulting
+;; non-atomic load/store/rmw must not carry a syncscope (the verifier rejects a
+;; non-atomic access with a synchronization scope).
+define i32 @load_syncscope(ptr %p) {
+; CHECK-LABEL: @load_syncscope(
+; CHECK-NEXT:    [[V:%.*]] = load i32, ptr [[P:%.*]], align 4
+; CHECK-NEXT:    ret i32 [[V]]
+;
+  %v = load atomic i32, ptr %p syncscope("singlethread") seq_cst, align 4
+  ret i32 %v
+}
+
+define void @store_syncscope(ptr %p, i32 %v) {
+; CHECK-LABEL: @store_syncscope(
+; CHECK-NEXT:    store i32 [[V:%.*]], ptr [[P:%.*]], align 4
+; CHECK-NEXT:    ret void
+;
+  store atomic i32 %v, ptr %p syncscope("singlethread") seq_cst, align 4
+  ret void
+}
+
+define i32 @rmw_syncscope(ptr %p, i32 %v) {
+; CHECK-LABEL: @rmw_syncscope(
+; CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[P:%.*]], align 4
+; CHECK-NEXT:    [[NEW:%.*]] = add i32 [[TMP1]], [[V:%.*]]
+; CHECK-NEXT:    store i32 [[NEW]], ptr [[P]], align 4
+; CHECK-NEXT:    ret i32 [[TMP1]]
+;
+  %old = atomicrmw add ptr %p, i32 %v syncscope("agent") seq_cst
+  ret i32 %old
+}
+
+define { i32, i1 } @cmpxchg_syncscope(ptr %p, i32 %cmp, i32 %new) {
+; CHECK-LABEL: @cmpxchg_syncscope(
+; CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[P:%.*]], align 4
+; CHECK-NEXT:    [[TMP2:%.*]] = icmp eq i32 [[TMP1]], [[CMP:%.*]]
+; CHECK-NEXT:    [[TMP3:%.*]] = select i1 [[TMP2]], i32 [[NEW:%.*]], i32 
[[TMP1]]
+; CHECK-NEXT:    store i32 [[TMP3]], ptr [[P]], align 4
+; CHECK-NEXT:    [[TMP4:%.*]] = insertvalue { i32, i1 } poison, i32 [[TMP1]], 0
+; CHECK-NEXT:    [[TMP5:%.*]] = insertvalue { i32, i1 } [[TMP4]], i1 [[TMP2]], 
1
+; CHECK-NEXT:    ret { i32, i1 } [[TMP5]]
+;
+  %res = cmpxchg ptr %p, i32 %cmp, i32 %new syncscope("agent") seq_cst seq_cst
+  ret { i32, i1 } %res
+}
+
+!llvm.module.flags = !{!0}
+!0 = !{i32 1, !"thread-model", !"single"}


        
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to