[llvm-branch-commits] [clang] [llvm] CodeGen: Replace TargetOptions::ThreadModel with module flag (PR #223157)
Matt Arsenault via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Sat Sep 12 08:44:01 PDT 2026
https://github.com/arsenm created https://github.com/llvm/llvm-project/pull/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) <noreply at anthropic.com>
>From 6c4e42ed4e53db4af08aae67f95d77aff074dc45 Mon Sep 17 00:00:00 2001
From: Matt Arsenault <Matthew.Arsenault at amd.com>
Date: Thu, 10 Sep 2026 20:08:40 +0200
Subject: [PATCH] CodeGen: Replace TargetOptions::ThreadModel with module flag
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) <noreply at anthropic.com>
---
clang/lib/CodeGen/BackendUtil.cpp | 8 -
.../llvm/Analysis/TargetTransformInfo.h | 2 -
.../llvm/Analysis/TargetTransformInfoImpl.h | 2 -
llvm/include/llvm/CodeGen/BasicTTIImpl.h | 5 -
llvm/include/llvm/CodeGen/CommandFlags.h | 2 -
llvm/include/llvm/Target/TargetOptions.h | 4 -
llvm/lib/Analysis/TargetTransformInfo.cpp | 4 -
llvm/lib/CodeGen/AtomicExpandPass.cpp | 48 +++++-
llvm/lib/CodeGen/CommandFlags.cpp | 11 --
llvm/lib/Target/ARM/ARMTargetMachine.cpp | 5 +-
llvm/lib/Transforms/Scalar/LICM.cpp | 17 ++-
llvm/test/CodeGen/ARM/atomic-op.ll | 18 ++-
.../AtomicExpand/ARM/thread-model-single.ll | 138 ++++++++++++++++++
13 files changed, 210 insertions(+), 54 deletions(-)
create mode 100644 llvm/test/Transforms/AtomicExpand/ARM/thread-model-single.ll
diff --git a/clang/lib/CodeGen/BackendUtil.cpp b/clang/lib/CodeGen/BackendUtil.cpp
index 9d8fd319f6f17..9aa8826a3c853 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 841cb22808d05..288d824daf579 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 2bf9877e318f9..45dcf38f9915b 100644
--- a/llvm/include/llvm/Target/TargetOptions.h
+++ b/llvm/include/llvm/Target/TargetOptions.h
@@ -306,10 +306,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 8410c10843bd4..b95e3454c828a 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)
@@ -157,15 +156,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"),
@@ -590,7 +580,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..8377a31765b2d
--- /dev/null
+++ b/llvm/test/Transforms/AtomicExpand/ARM/thread-model-single.ll
@@ -0,0 +1,138 @@
+; 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
+}
+
+;; 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"}
More information about the llvm-branch-commits
mailing list