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
