https://github.com/nikic updated https://github.com/llvm/llvm-project/pull/193768
>From ce9ccb25d169d477a04e20f990eb655b46025de4 Mon Sep 17 00:00:00 2001 From: Nikita Popov <[email protected]> Date: Thu, 23 Apr 2026 14:49:52 +0200 Subject: [PATCH 1/7] [IR][FunctionAttrs] Clarify memory effects of atomics FunctionAttrs was treating atomic instructions, including with ordering strong than monotonic, as only reading/writing their operand. I don't think doing this is correct, because we model the ordering constraints of synchronizing atomics via reading/writing "all" memory. So e.g. if you have a function with a release store on an argument, marking it as argmem-only is wrong, because that would permit reordering accesses to other locations around it. (What this PR is doing is not *sufficient* to model this correctly due to the fence-like effects on not-yet-escaped memory, but it brings us closer to correctness.) I initially tried to implement mayReadFromMemory() and mayWriteToMemory() on top of getMemoryEffects(), but this caused significant compile-time regressions, so I've kept the logic duplicated. (I'm not touching Attributor, which needs some major rework to properly deduce memory effects.) --- llvm/include/llvm/IR/Instruction.h | 5 +++ llvm/lib/IR/Instruction.cpp | 45 +++++++++++++++++++ llvm/lib/Transforms/IPO/FunctionAttrs.cpp | 40 ++++++++--------- llvm/test/Transforms/FunctionAttrs/atomic.ll | 8 ++-- .../Transforms/FunctionAttrs/nocapture.ll | 6 +-- llvm/test/Transforms/FunctionAttrs/nosync.ll | 12 ++--- .../Transforms/FunctionAttrs/writeonly.ll | 2 +- 7 files changed, 82 insertions(+), 36 deletions(-) diff --git a/llvm/include/llvm/IR/Instruction.h b/llvm/include/llvm/IR/Instruction.h index 0e6c122a4e2ef..2e50b267ee810 100644 --- a/llvm/include/llvm/IR/Instruction.h +++ b/llvm/include/llvm/IR/Instruction.h @@ -24,6 +24,7 @@ #include "llvm/IR/Value.h" #include "llvm/Support/AtomicOrdering.h" #include "llvm/Support/Compiler.h" +#include "llvm/Support/ModRef.h" #include <cstdint> #include <utility> @@ -835,6 +836,10 @@ class Instruction : public User, return Opcode == Xor; } + /// Return memory effects of the instruction. argmem here refers to the + /// operands of the instruction. + LLVM_ABI MemoryEffects getMemoryEffects() const LLVM_READONLY; + /// Return true if this instruction may modify memory. LLVM_ABI bool mayWriteToMemory() const LLVM_READONLY; diff --git a/llvm/lib/IR/Instruction.cpp b/llvm/lib/IR/Instruction.cpp index b002832ec0624..868a9d910dad2 100644 --- a/llvm/lib/IR/Instruction.cpp +++ b/llvm/lib/IR/Instruction.cpp @@ -1060,6 +1060,51 @@ bool Instruction::isUsedOutsideOfBlock(const BasicBlock *BB) const { return false; } +MemoryEffects Instruction::getMemoryEffects() const { + switch (getOpcode()) { + default: + return MemoryEffects::none(); + case Instruction::VAArg: + return MemoryEffects::argMemOnly(); + case Instruction::CatchPad: + case Instruction::CatchRet: + case Instruction::Fence: + case Instruction::AtomicCmpXchg: + case Instruction::AtomicRMW: + return MemoryEffects::unknown(); + case Instruction::Call: + case Instruction::Invoke: + case Instruction::CallBr: + return cast<CallBase>(this)->getMemoryEffects(); + case Instruction::Load: { + auto *LI = cast<LoadInst>(this); + if (isStrongerThanMonotonic(LI->getOrdering())) + return MemoryEffects::unknown(); + + MemoryEffects ME = MemoryEffects::argMemOnly( + LI->isUnordered() ? ModRefInfo::Ref : ModRefInfo::ModRef); + if (LI->isVolatile()) + ME |= MemoryEffects::inaccessibleMemOnly(); + return ME; + } + case Instruction::Store: { + auto *SI = cast<StoreInst>(this); + if (isStrongerThanMonotonic(SI->getOrdering())) + return MemoryEffects::unknown(); + + MemoryEffects ME = MemoryEffects::argMemOnly( + SI->isUnordered() ? ModRefInfo::Mod : ModRefInfo::ModRef); + if (SI->isVolatile()) + ME |= MemoryEffects::inaccessibleMemOnly(); + return ME; + } + } +} + +// This is duplicating the logic from getMemoryEffects() for performance +// reasons. Computing the full MemoryEffects just to perform a Mod/Ref check +// is expensive. + bool Instruction::mayReadFromMemory() const { switch (getOpcode()) { default: return false; diff --git a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp index 3dc0cc6bdaf46..e76b5a123860d 100644 --- a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp +++ b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp @@ -186,6 +186,18 @@ checkFunctionMemoryAccess(Function &F, bool ThisBody, AAResults &AAR, // Additional locations accessed if the SCC accesses argmem. MemoryEffects RecursiveArgME = MemoryEffects::none(); + auto AddNonArgMemoryEffects = [&ME](MemoryEffects InstME) { + // Merge instruction memory effects, including inaccessible and errno + // memory, but excluding argument memory, which is handled separately. + ME |= InstME.getWithoutLoc(IRMemLocation::ArgMem); + + // If the instruction accesses captured memory (currently part of "other") + // and an argument is captured (currently not tracked), then it may also + // access argument memory. + ModRefInfo OtherMR = InstME.getModRef(IRMemLocation::Other); + ME |= MemoryEffects::argMemOnly(OtherMR); + }; + // Inalloca and preallocated arguments are always clobbered by the call. if (F.getAttributes().hasAttrSomewhere(Attribute::InAlloca) || F.getAttributes().hasAttrSomewhere(Attribute::Preallocated)) @@ -222,16 +234,7 @@ checkFunctionMemoryAccess(Function &F, bool ThisBody, AAResults &AAR, if (isa<PseudoProbeInst>(I)) continue; - // Merge callee's memory effects into caller's ones, including - // inaccessible and errno memory, but excluding argument memory, which is - // handled separately. - ME |= CallME.getWithoutLoc(IRMemLocation::ArgMem); - - // If the call accesses captured memory (currently part of "other") and - // an argument is captured (currently not tracked), then it may also - // access argument memory. - ModRefInfo OtherMR = CallME.getModRef(IRMemLocation::Other); - ME |= MemoryEffects::argMemOnly(OtherMR); + AddNonArgMemoryEffects(CallME); // Check whether all pointer arguments point to local memory, and // ignore calls that only access local memory. @@ -241,27 +244,20 @@ checkFunctionMemoryAccess(Function &F, bool ThisBody, AAResults &AAR, continue; } - ModRefInfo MR = ModRefInfo::NoModRef; - if (I.mayWriteToMemory()) - MR |= ModRefInfo::Mod; - if (I.mayReadFromMemory()) - MR |= ModRefInfo::Ref; - if (MR == ModRefInfo::NoModRef) + MemoryEffects InstME = I.getMemoryEffects(); + if (InstME.doesNotAccessMemory()) continue; std::optional<MemoryLocation> Loc = MemoryLocation::getOrNone(&I); if (!Loc) { // If no location is known, conservatively assume anything can be // accessed. - ME |= MemoryEffects(MR); + ME |= MemoryEffects(InstME.getModRef()); continue; } - // Volatile operations may access inaccessible memory. - if (I.isVolatile()) - ME |= MemoryEffects::inaccessibleMemOnly(MR); - - addLocAccess(ME, *Loc, MR, AAR); + AddNonArgMemoryEffects(InstME); + addLocAccess(ME, *Loc, InstME.getModRef(IRMemLocation::ArgMem), AAR); } return {OrigME & ME, RecursiveArgME}; diff --git a/llvm/test/Transforms/FunctionAttrs/atomic.ll b/llvm/test/Transforms/FunctionAttrs/atomic.ll index 8635f2bbdc498..140542e986bbf 100644 --- a/llvm/test/Transforms/FunctionAttrs/atomic.ll +++ b/llvm/test/Transforms/FunctionAttrs/atomic.ll @@ -1,10 +1,10 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-attributes ; RUN: opt -passes=function-attrs -S < %s | FileCheck %s -; Atomic load/store to local doesn't affect whether a function is -; readnone/readonly. +; Even though the load/store is on alloca, we can't mark the function as +; readnone due to the synchronization effect. define i32 @test1(i32 %x) uwtable ssp { -; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind ssp willreturn memory(none) uwtable +; CHECK: Function Attrs: mustprogress nofree norecurse nounwind ssp willreturn uwtable ; CHECK-LABEL: @test1( ; CHECK-NEXT: entry: ; CHECK-NEXT: [[X_ADDR:%.*]] = alloca i32, align 4 @@ -21,7 +21,7 @@ entry: ; A function with an Acquire load is not readonly. define i32 @test2(ptr %x) uwtable ssp { -; CHECK: Function Attrs: mustprogress nofree norecurse nounwind ssp willreturn memory(argmem: readwrite) uwtable +; CHECK: Function Attrs: mustprogress nofree norecurse nounwind ssp willreturn uwtable ; CHECK-LABEL: @test2( ; CHECK-NEXT: entry: ; CHECK-NEXT: [[R:%.*]] = load atomic i32, ptr [[X:%.*]] seq_cst, align 4 diff --git a/llvm/test/Transforms/FunctionAttrs/nocapture.ll b/llvm/test/Transforms/FunctionAttrs/nocapture.ll index e28891b7f1229..16adb3115e712 100644 --- a/llvm/test/Transforms/FunctionAttrs/nocapture.ll +++ b/llvm/test/Transforms/FunctionAttrs/nocapture.ll @@ -646,7 +646,7 @@ define void @test6_2(ptr %x6_2, ptr %y6_2, ptr %z6_2) { } define void @test_cmpxchg(ptr %p) { -; FNATTRS: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(argmem: readwrite) +; FNATTRS: Function Attrs: mustprogress nofree norecurse nounwind willreturn ; FNATTRS-LABEL: define void @test_cmpxchg ; FNATTRS-SAME: (ptr captures(none) [[P:%.*]]) #[[ATTR13:[0-9]+]] { ; FNATTRS-NEXT: [[TMP1:%.*]] = cmpxchg ptr [[P]], i32 0, i32 1 acquire monotonic, align 4 @@ -663,7 +663,7 @@ define void @test_cmpxchg(ptr %p) { } define void @test_cmpxchg_ptr(ptr %p, ptr %q) { -; FNATTRS: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(argmem: readwrite) +; FNATTRS: Function Attrs: mustprogress nofree norecurse nounwind willreturn ; FNATTRS-LABEL: define void @test_cmpxchg_ptr ; FNATTRS-SAME: (ptr captures(none) [[P:%.*]], ptr [[Q:%.*]]) #[[ATTR13]] { ; FNATTRS-NEXT: [[TMP1:%.*]] = cmpxchg ptr [[P]], ptr null, ptr [[Q]] acquire monotonic, align 8 @@ -680,7 +680,7 @@ define void @test_cmpxchg_ptr(ptr %p, ptr %q) { } define void @test_atomicrmw(ptr %p) { -; FNATTRS: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(argmem: readwrite) +; FNATTRS: Function Attrs: mustprogress nofree norecurse nounwind willreturn ; FNATTRS-LABEL: define void @test_atomicrmw ; FNATTRS-SAME: (ptr captures(none) [[P:%.*]]) #[[ATTR13]] { ; FNATTRS-NEXT: [[TMP1:%.*]] = atomicrmw add ptr [[P]], i32 1 seq_cst, align 4 diff --git a/llvm/test/Transforms/FunctionAttrs/nosync.ll b/llvm/test/Transforms/FunctionAttrs/nosync.ll index db2aaa06aad1f..f1ea429fc22d8 100644 --- a/llvm/test/Transforms/FunctionAttrs/nosync.ll +++ b/llvm/test/Transforms/FunctionAttrs/nosync.ll @@ -48,7 +48,7 @@ define i32 @test4(i32 %a, i32 %b) { ; negative case - explicit sync define void @test5(ptr %p) { -; CHECK: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(argmem: readwrite) +; CHECK: Function Attrs: mustprogress nofree norecurse nounwind willreturn ; CHECK-LABEL: @test5( ; CHECK-NEXT: store atomic i8 0, ptr [[P:%.*]] seq_cst, align 1 ; CHECK-NEXT: ret void @@ -59,7 +59,7 @@ define void @test5(ptr %p) { ; negative case - explicit sync define i8 @test6(ptr %p) { -; CHECK: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(argmem: readwrite) +; CHECK: Function Attrs: mustprogress nofree norecurse nounwind willreturn ; CHECK-LABEL: @test6( ; CHECK-NEXT: [[V:%.*]] = load atomic i8, ptr [[P:%.*]] seq_cst, align 1 ; CHECK-NEXT: ret i8 [[V]] @@ -70,7 +70,7 @@ define i8 @test6(ptr %p) { ; negative case - explicit sync define void @test7(ptr %p) { -; CHECK: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(argmem: readwrite) +; CHECK: Function Attrs: mustprogress nofree norecurse nounwind willreturn ; CHECK-LABEL: @test7( ; CHECK-NEXT: [[TMP1:%.*]] = atomicrmw add ptr [[P:%.*]], i8 0 seq_cst, align 1 ; CHECK-NEXT: ret void @@ -126,7 +126,7 @@ define void @store_monotonic(ptr nocapture %0) norecurse nounwind uwtable { ; negative, should not deduce nosync ; atomic load with acquire ordering. define i32 @load_acquire(ptr nocapture readonly %0) norecurse nounwind uwtable { -; CHECK: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(argmem: readwrite) uwtable +; CHECK: Function Attrs: mustprogress nofree norecurse nounwind willreturn uwtable ; CHECK-LABEL: @load_acquire( ; CHECK-NEXT: [[TMP2:%.*]] = load atomic i32, ptr [[TMP0:%.*]] acquire, align 4 ; CHECK-NEXT: ret i32 [[TMP2]] @@ -160,7 +160,7 @@ define void @store_unordered(ptr nocapture %0) norecurse nounwind uwtable { ; negative, should not deduce nosync ; atomic load with release ordering define void @load_release(ptr nocapture %0) norecurse nounwind uwtable { -; CHECK: Function Attrs: nofree norecurse nounwind memory(argmem: readwrite, inaccessiblemem: readwrite) uwtable +; CHECK: Function Attrs: nofree norecurse nounwind uwtable ; CHECK-LABEL: @load_release( ; CHECK-NEXT: store atomic volatile i32 10, ptr [[TMP0:%.*]] release, align 4 ; CHECK-NEXT: ret void @@ -171,7 +171,7 @@ define void @load_release(ptr nocapture %0) norecurse nounwind uwtable { ; negative volatile, relaxed atomic define void @load_volatile_release(ptr nocapture %0) norecurse nounwind uwtable { -; CHECK: Function Attrs: nofree norecurse nounwind memory(argmem: readwrite, inaccessiblemem: readwrite) uwtable +; CHECK: Function Attrs: nofree norecurse nounwind uwtable ; CHECK-LABEL: @load_volatile_release( ; CHECK-NEXT: store atomic volatile i32 10, ptr [[TMP0:%.*]] release, align 4 ; CHECK-NEXT: ret void diff --git a/llvm/test/Transforms/FunctionAttrs/writeonly.ll b/llvm/test/Transforms/FunctionAttrs/writeonly.ll index 75fdb171d94ec..8d6d038911159 100644 --- a/llvm/test/Transforms/FunctionAttrs/writeonly.ll +++ b/llvm/test/Transforms/FunctionAttrs/writeonly.ll @@ -162,7 +162,7 @@ define void @test_volatile(ptr %p) { } define void @test_atomicrmw(ptr %p) { -; FNATTRS: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(argmem: readwrite) +; FNATTRS: Function Attrs: mustprogress nofree norecurse nounwind willreturn ; FNATTRS-LABEL: define {{[^@]+}}@test_atomicrmw ; FNATTRS-SAME: (ptr captures(none) [[P:%.*]]) #[[ATTR7:[0-9]+]] { ; FNATTRS-NEXT: [[TMP1:%.*]] = atomicrmw add ptr [[P]], i8 0 seq_cst, align 1 >From 12432b40786034edd6500f51a0f9e7345ee9206c Mon Sep 17 00:00:00 2001 From: Nikita Popov <[email protected]> Date: Fri, 24 Apr 2026 09:44:35 +0200 Subject: [PATCH 2/7] Clarify comment --- llvm/test/Transforms/FunctionAttrs/atomic.ll | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/llvm/test/Transforms/FunctionAttrs/atomic.ll b/llvm/test/Transforms/FunctionAttrs/atomic.ll index 140542e986bbf..247b73d158d15 100644 --- a/llvm/test/Transforms/FunctionAttrs/atomic.ll +++ b/llvm/test/Transforms/FunctionAttrs/atomic.ll @@ -1,8 +1,9 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-attributes ; RUN: opt -passes=function-attrs -S < %s | FileCheck %s -; Even though the load/store is on alloca, we can't mark the function as -; readnone due to the synchronization effect. +; While it would be fine in this specific case (the alloca does not escape), +; we generally can't ignore synchronizing operations on allocas and should not +; infer readnone here. Non-escaping cases will typically be optimized by SROA. define i32 @test1(i32 %x) uwtable ssp { ; CHECK: Function Attrs: mustprogress nofree norecurse nounwind ssp willreturn uwtable ; CHECK-LABEL: @test1( >From 297df2c24291d81877d1c3c0b80e52e7c3807481 Mon Sep 17 00:00:00 2001 From: Nikita Popov <[email protected]> Date: Fri, 24 Apr 2026 14:53:40 +0200 Subject: [PATCH 3/7] Handle monotonic atomicrmw/cmpxchg more precisely --- llvm/lib/IR/Instruction.cpp | 43 ++++++++------ llvm/test/Transforms/FunctionAttrs/atomic.ll | 60 ++++++++++++++++++++ 2 files changed, 85 insertions(+), 18 deletions(-) diff --git a/llvm/lib/IR/Instruction.cpp b/llvm/lib/IR/Instruction.cpp index 868a9d910dad2..b0aa9fb4be849 100644 --- a/llvm/lib/IR/Instruction.cpp +++ b/llvm/lib/IR/Instruction.cpp @@ -1061,6 +1061,19 @@ bool Instruction::isUsedOutsideOfBlock(const BasicBlock *BB) const { } MemoryEffects Instruction::getMemoryEffects() const { + auto GetEffects = [](ModRefInfo BaseMR, AtomicOrdering Ordering, + bool IsVolatile) { + if (isStrongerThanMonotonic(Ordering)) + return MemoryEffects::unknown(); + + if (IsVolatile) + return MemoryEffects::inaccessibleOrArgMemOnly(); + + if (isStrongerThanUnordered(Ordering)) + return MemoryEffects::argMemOnly(); + + return MemoryEffects::argMemOnly(BaseMR); + }; switch (getOpcode()) { default: return MemoryEffects::none(); @@ -1069,8 +1082,6 @@ MemoryEffects Instruction::getMemoryEffects() const { case Instruction::CatchPad: case Instruction::CatchRet: case Instruction::Fence: - case Instruction::AtomicCmpXchg: - case Instruction::AtomicRMW: return MemoryEffects::unknown(); case Instruction::Call: case Instruction::Invoke: @@ -1078,25 +1089,21 @@ MemoryEffects Instruction::getMemoryEffects() const { return cast<CallBase>(this)->getMemoryEffects(); case Instruction::Load: { auto *LI = cast<LoadInst>(this); - if (isStrongerThanMonotonic(LI->getOrdering())) - return MemoryEffects::unknown(); - - MemoryEffects ME = MemoryEffects::argMemOnly( - LI->isUnordered() ? ModRefInfo::Ref : ModRefInfo::ModRef); - if (LI->isVolatile()) - ME |= MemoryEffects::inaccessibleMemOnly(); - return ME; + return GetEffects(ModRefInfo::Ref, LI->getOrdering(), LI->isVolatile()); } case Instruction::Store: { auto *SI = cast<StoreInst>(this); - if (isStrongerThanMonotonic(SI->getOrdering())) - return MemoryEffects::unknown(); - - MemoryEffects ME = MemoryEffects::argMemOnly( - SI->isUnordered() ? ModRefInfo::Mod : ModRefInfo::ModRef); - if (SI->isVolatile()) - ME |= MemoryEffects::inaccessibleMemOnly(); - return ME; + return GetEffects(ModRefInfo::Mod, SI->getOrdering(), SI->isVolatile()); + } + case Instruction::AtomicRMW: { + auto *RMW = cast<AtomicRMWInst>(this); + return GetEffects(ModRefInfo::ModRef, RMW->getOrdering(), + RMW->isVolatile()); + } + case Instruction::AtomicCmpXchg: { + auto *CX = cast<AtomicCmpXchgInst>(this); + return GetEffects(ModRefInfo::ModRef, CX->getSuccessOrdering(), + CX->isVolatile()); } } } diff --git a/llvm/test/Transforms/FunctionAttrs/atomic.ll b/llvm/test/Transforms/FunctionAttrs/atomic.ll index 247b73d158d15..9f48ff6bb504e 100644 --- a/llvm/test/Transforms/FunctionAttrs/atomic.ll +++ b/llvm/test/Transforms/FunctionAttrs/atomic.ll @@ -32,3 +32,63 @@ entry: %r = load atomic i32, ptr %x seq_cst, align 4 ret i32 %r } + +define void @atomicrmw_monotonic_arg(ptr %x) { +; CHECK: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(argmem: readwrite) +; CHECK-LABEL: @atomicrmw_monotonic_arg( +; CHECK-NEXT: [[TMP1:%.*]] = atomicrmw add ptr [[X:%.*]], i32 1 monotonic, align 4 +; CHECK-NEXT: ret void +; + atomicrmw add ptr %x, i32 1 monotonic, align 4 + ret void +} + +define void @atomicrmw_acq_rel_arg(ptr %x) { +; CHECK: Function Attrs: mustprogress nofree norecurse nounwind willreturn +; CHECK-LABEL: @atomicrmw_acq_rel_arg( +; CHECK-NEXT: [[TMP1:%.*]] = atomicrmw add ptr [[X:%.*]], i32 1 acq_rel, align 4 +; CHECK-NEXT: ret void +; + atomicrmw add ptr %x, i32 1 acq_rel, align 4 + ret void +} + +define void @atomicrmw_monotonic_volatile_arg(ptr %x) { +; CHECK: Function Attrs: nofree norecurse nounwind memory(argmem: readwrite, inaccessiblemem: readwrite) +; CHECK-LABEL: @atomicrmw_monotonic_volatile_arg( +; CHECK-NEXT: [[TMP1:%.*]] = atomicrmw volatile add ptr [[X:%.*]], i32 1 monotonic, align 4 +; CHECK-NEXT: ret void +; + atomicrmw volatile add ptr %x, i32 1 monotonic, align 4 + ret void +} + +define void @cmpxchg_monotonic_arg(ptr %x) { +; CHECK: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(argmem: readwrite) +; CHECK-LABEL: @cmpxchg_monotonic_arg( +; CHECK-NEXT: [[TMP1:%.*]] = cmpxchg ptr [[X:%.*]], i32 0, i32 1 monotonic monotonic, align 4 +; CHECK-NEXT: ret void +; + cmpxchg ptr %x, i32 0, i32 1 monotonic monotonic + ret void +} + +define void @cmpxchg_acq_rel_arg(ptr %x) { +; CHECK: Function Attrs: mustprogress nofree norecurse nounwind willreturn +; CHECK-LABEL: @cmpxchg_acq_rel_arg( +; CHECK-NEXT: [[TMP1:%.*]] = cmpxchg ptr [[X:%.*]], i32 0, i32 1 acq_rel monotonic, align 4 +; CHECK-NEXT: ret void +; + cmpxchg ptr %x, i32 0, i32 1 acq_rel monotonic + ret void +} + +define void @cmpxchg_monotonic_volatile_arg(ptr %x) { +; CHECK: Function Attrs: nofree norecurse nounwind memory(argmem: readwrite, inaccessiblemem: readwrite) +; CHECK-LABEL: @cmpxchg_monotonic_volatile_arg( +; CHECK-NEXT: [[TMP1:%.*]] = cmpxchg volatile ptr [[X:%.*]], i32 0, i32 1 monotonic monotonic, align 4 +; CHECK-NEXT: ret void +; + cmpxchg volatile ptr %x, i32 0, i32 1 monotonic monotonic + ret void +} >From cd42b222579dcdff202b3ab46f8877beed80cd2f Mon Sep 17 00:00:00 2001 From: Nikita Popov <[email protected]> Date: Tue, 5 May 2026 09:57:41 +0200 Subject: [PATCH 4/7] Add more tests --- llvm/test/Transforms/FunctionAttrs/atomic.ll | 63 +++++++++++++++++--- 1 file changed, 55 insertions(+), 8 deletions(-) diff --git a/llvm/test/Transforms/FunctionAttrs/atomic.ll b/llvm/test/Transforms/FunctionAttrs/atomic.ll index 9f48ff6bb504e..736e2342157ab 100644 --- a/llvm/test/Transforms/FunctionAttrs/atomic.ll +++ b/llvm/test/Transforms/FunctionAttrs/atomic.ll @@ -20,19 +20,66 @@ entry: ret i32 %r } -; A function with an Acquire load is not readonly. -define i32 @test2(ptr %x) uwtable ssp { -; CHECK: Function Attrs: mustprogress nofree norecurse nounwind ssp willreturn uwtable -; CHECK-LABEL: @test2( -; CHECK-NEXT: entry: -; CHECK-NEXT: [[R:%.*]] = load atomic i32, ptr [[X:%.*]] seq_cst, align 4 +define i32 @load_monotonic(ptr %x) { +; CHECK: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(argmem: readwrite) +; CHECK-LABEL: @load_monotonic( +; CHECK-NEXT: [[R:%.*]] = load atomic i32, ptr [[X:%.*]] monotonic, align 4 ; CHECK-NEXT: ret i32 [[R]] ; -entry: - %r = load atomic i32, ptr %x seq_cst, align 4 + %r = load atomic i32, ptr %x monotonic, align 4 ret i32 %r } +define i32 @load_acquire(ptr %x) { +; CHECK: Function Attrs: mustprogress nofree norecurse nounwind willreturn +; CHECK-LABEL: @load_acquire( +; CHECK-NEXT: [[R:%.*]] = load atomic i32, ptr [[X:%.*]] acquire, align 4 +; CHECK-NEXT: ret i32 [[R]] +; + %r = load atomic i32, ptr %x acquire, align 4 + ret i32 %r +} + +define i32 @load_seq_cst(ptr %x) { +; CHECK: Function Attrs: mustprogress nofree norecurse nounwind willreturn +; CHECK-LABEL: @load_seq_cst( +; CHECK-NEXT: [[R:%.*]] = load atomic i32, ptr [[X:%.*]] acquire, align 4 +; CHECK-NEXT: ret i32 [[R]] +; + %r = load atomic i32, ptr %x acquire, align 4 + ret i32 %r +} + +define void @store_monotonic(ptr %x) { +; CHECK: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(argmem: readwrite) +; CHECK-LABEL: @store_monotonic( +; CHECK-NEXT: store atomic i32 0, ptr [[X:%.*]] monotonic, align 4 +; CHECK-NEXT: ret void +; + store atomic i32 0, ptr %x monotonic, align 4 + ret void +} + +define void @store_release(ptr %x) { +; CHECK: Function Attrs: mustprogress nofree norecurse nounwind willreturn +; CHECK-LABEL: @store_release( +; CHECK-NEXT: store atomic i32 0, ptr [[X:%.*]] release, align 4 +; CHECK-NEXT: ret void +; + store atomic i32 0, ptr %x release, align 4 + ret void +} + +define void @store_seq_cst(ptr %x) { +; CHECK: Function Attrs: mustprogress nofree norecurse nounwind willreturn +; CHECK-LABEL: @store_seq_cst( +; CHECK-NEXT: store atomic i32 0, ptr [[X:%.*]] seq_cst, align 4 +; CHECK-NEXT: ret void +; + store atomic i32 0, ptr %x seq_cst, align 4 + ret void +} + define void @atomicrmw_monotonic_arg(ptr %x) { ; CHECK: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(argmem: readwrite) ; CHECK-LABEL: @atomicrmw_monotonic_arg( >From ca09ba3096d41c97581858d86f3f8dbe8aee554b Mon Sep 17 00:00:00 2001 From: Nikita Popov <[email protected]> Date: Tue, 5 May 2026 09:58:44 +0200 Subject: [PATCH 5/7] Regenerate test with function signature checks --- llvm/test/Transforms/FunctionAttrs/atomic.ll | 69 ++++++++++++-------- 1 file changed, 41 insertions(+), 28 deletions(-) diff --git a/llvm/test/Transforms/FunctionAttrs/atomic.ll b/llvm/test/Transforms/FunctionAttrs/atomic.ll index 736e2342157ab..45ce345f9627d 100644 --- a/llvm/test/Transforms/FunctionAttrs/atomic.ll +++ b/llvm/test/Transforms/FunctionAttrs/atomic.ll @@ -1,4 +1,4 @@ -; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-attributes +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-attributes --version 6 ; RUN: opt -passes=function-attrs -S < %s | FileCheck %s ; While it would be fine in this specific case (the alloca does not escape), @@ -6,10 +6,11 @@ ; infer readnone here. Non-escaping cases will typically be optimized by SROA. define i32 @test1(i32 %x) uwtable ssp { ; CHECK: Function Attrs: mustprogress nofree norecurse nounwind ssp willreturn uwtable -; CHECK-LABEL: @test1( -; CHECK-NEXT: entry: +; CHECK-LABEL: define i32 @test1( +; CHECK-SAME: i32 [[X:%.*]]) #[[ATTR0:[0-9]+]] { +; CHECK-NEXT: [[ENTRY:.*:]] ; CHECK-NEXT: [[X_ADDR:%.*]] = alloca i32, align 4 -; CHECK-NEXT: store atomic i32 [[X:%.*]], ptr [[X_ADDR]] seq_cst, align 4 +; CHECK-NEXT: store atomic i32 [[X]], ptr [[X_ADDR]] seq_cst, align 4 ; CHECK-NEXT: [[R:%.*]] = load atomic i32, ptr [[X_ADDR]] seq_cst, align 4 ; CHECK-NEXT: ret i32 [[R]] ; @@ -22,8 +23,9 @@ entry: define i32 @load_monotonic(ptr %x) { ; CHECK: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(argmem: readwrite) -; CHECK-LABEL: @load_monotonic( -; CHECK-NEXT: [[R:%.*]] = load atomic i32, ptr [[X:%.*]] monotonic, align 4 +; CHECK-LABEL: define i32 @load_monotonic( +; CHECK-SAME: ptr readonly captures(none) [[X:%.*]]) #[[ATTR1:[0-9]+]] { +; CHECK-NEXT: [[R:%.*]] = load atomic i32, ptr [[X]] monotonic, align 4 ; CHECK-NEXT: ret i32 [[R]] ; %r = load atomic i32, ptr %x monotonic, align 4 @@ -32,8 +34,9 @@ define i32 @load_monotonic(ptr %x) { define i32 @load_acquire(ptr %x) { ; CHECK: Function Attrs: mustprogress nofree norecurse nounwind willreturn -; CHECK-LABEL: @load_acquire( -; CHECK-NEXT: [[R:%.*]] = load atomic i32, ptr [[X:%.*]] acquire, align 4 +; CHECK-LABEL: define i32 @load_acquire( +; CHECK-SAME: ptr readonly captures(none) [[X:%.*]]) #[[ATTR2:[0-9]+]] { +; CHECK-NEXT: [[R:%.*]] = load atomic i32, ptr [[X]] acquire, align 4 ; CHECK-NEXT: ret i32 [[R]] ; %r = load atomic i32, ptr %x acquire, align 4 @@ -42,8 +45,9 @@ define i32 @load_acquire(ptr %x) { define i32 @load_seq_cst(ptr %x) { ; CHECK: Function Attrs: mustprogress nofree norecurse nounwind willreturn -; CHECK-LABEL: @load_seq_cst( -; CHECK-NEXT: [[R:%.*]] = load atomic i32, ptr [[X:%.*]] acquire, align 4 +; CHECK-LABEL: define i32 @load_seq_cst( +; CHECK-SAME: ptr readonly captures(none) [[X:%.*]]) #[[ATTR2]] { +; CHECK-NEXT: [[R:%.*]] = load atomic i32, ptr [[X]] acquire, align 4 ; CHECK-NEXT: ret i32 [[R]] ; %r = load atomic i32, ptr %x acquire, align 4 @@ -52,8 +56,9 @@ define i32 @load_seq_cst(ptr %x) { define void @store_monotonic(ptr %x) { ; CHECK: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(argmem: readwrite) -; CHECK-LABEL: @store_monotonic( -; CHECK-NEXT: store atomic i32 0, ptr [[X:%.*]] monotonic, align 4 +; CHECK-LABEL: define void @store_monotonic( +; CHECK-SAME: ptr writeonly captures(none) [[X:%.*]]) #[[ATTR1]] { +; CHECK-NEXT: store atomic i32 0, ptr [[X]] monotonic, align 4 ; CHECK-NEXT: ret void ; store atomic i32 0, ptr %x monotonic, align 4 @@ -62,8 +67,9 @@ define void @store_monotonic(ptr %x) { define void @store_release(ptr %x) { ; CHECK: Function Attrs: mustprogress nofree norecurse nounwind willreturn -; CHECK-LABEL: @store_release( -; CHECK-NEXT: store atomic i32 0, ptr [[X:%.*]] release, align 4 +; CHECK-LABEL: define void @store_release( +; CHECK-SAME: ptr writeonly captures(none) [[X:%.*]]) #[[ATTR2]] { +; CHECK-NEXT: store atomic i32 0, ptr [[X]] release, align 4 ; CHECK-NEXT: ret void ; store atomic i32 0, ptr %x release, align 4 @@ -72,8 +78,9 @@ define void @store_release(ptr %x) { define void @store_seq_cst(ptr %x) { ; CHECK: Function Attrs: mustprogress nofree norecurse nounwind willreturn -; CHECK-LABEL: @store_seq_cst( -; CHECK-NEXT: store atomic i32 0, ptr [[X:%.*]] seq_cst, align 4 +; CHECK-LABEL: define void @store_seq_cst( +; CHECK-SAME: ptr writeonly captures(none) [[X:%.*]]) #[[ATTR2]] { +; CHECK-NEXT: store atomic i32 0, ptr [[X]] seq_cst, align 4 ; CHECK-NEXT: ret void ; store atomic i32 0, ptr %x seq_cst, align 4 @@ -82,8 +89,9 @@ define void @store_seq_cst(ptr %x) { define void @atomicrmw_monotonic_arg(ptr %x) { ; CHECK: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(argmem: readwrite) -; CHECK-LABEL: @atomicrmw_monotonic_arg( -; CHECK-NEXT: [[TMP1:%.*]] = atomicrmw add ptr [[X:%.*]], i32 1 monotonic, align 4 +; CHECK-LABEL: define void @atomicrmw_monotonic_arg( +; CHECK-SAME: ptr captures(none) [[X:%.*]]) #[[ATTR1]] { +; CHECK-NEXT: [[TMP1:%.*]] = atomicrmw add ptr [[X]], i32 1 monotonic, align 4 ; CHECK-NEXT: ret void ; atomicrmw add ptr %x, i32 1 monotonic, align 4 @@ -92,8 +100,9 @@ define void @atomicrmw_monotonic_arg(ptr %x) { define void @atomicrmw_acq_rel_arg(ptr %x) { ; CHECK: Function Attrs: mustprogress nofree norecurse nounwind willreturn -; CHECK-LABEL: @atomicrmw_acq_rel_arg( -; CHECK-NEXT: [[TMP1:%.*]] = atomicrmw add ptr [[X:%.*]], i32 1 acq_rel, align 4 +; CHECK-LABEL: define void @atomicrmw_acq_rel_arg( +; CHECK-SAME: ptr captures(none) [[X:%.*]]) #[[ATTR2]] { +; CHECK-NEXT: [[TMP1:%.*]] = atomicrmw add ptr [[X]], i32 1 acq_rel, align 4 ; CHECK-NEXT: ret void ; atomicrmw add ptr %x, i32 1 acq_rel, align 4 @@ -102,8 +111,9 @@ define void @atomicrmw_acq_rel_arg(ptr %x) { define void @atomicrmw_monotonic_volatile_arg(ptr %x) { ; CHECK: Function Attrs: nofree norecurse nounwind memory(argmem: readwrite, inaccessiblemem: readwrite) -; CHECK-LABEL: @atomicrmw_monotonic_volatile_arg( -; CHECK-NEXT: [[TMP1:%.*]] = atomicrmw volatile add ptr [[X:%.*]], i32 1 monotonic, align 4 +; CHECK-LABEL: define void @atomicrmw_monotonic_volatile_arg( +; CHECK-SAME: ptr [[X:%.*]]) #[[ATTR3:[0-9]+]] { +; CHECK-NEXT: [[TMP1:%.*]] = atomicrmw volatile add ptr [[X]], i32 1 monotonic, align 4 ; CHECK-NEXT: ret void ; atomicrmw volatile add ptr %x, i32 1 monotonic, align 4 @@ -112,8 +122,9 @@ define void @atomicrmw_monotonic_volatile_arg(ptr %x) { define void @cmpxchg_monotonic_arg(ptr %x) { ; CHECK: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(argmem: readwrite) -; CHECK-LABEL: @cmpxchg_monotonic_arg( -; CHECK-NEXT: [[TMP1:%.*]] = cmpxchg ptr [[X:%.*]], i32 0, i32 1 monotonic monotonic, align 4 +; CHECK-LABEL: define void @cmpxchg_monotonic_arg( +; CHECK-SAME: ptr captures(none) [[X:%.*]]) #[[ATTR1]] { +; CHECK-NEXT: [[TMP1:%.*]] = cmpxchg ptr [[X]], i32 0, i32 1 monotonic monotonic, align 4 ; CHECK-NEXT: ret void ; cmpxchg ptr %x, i32 0, i32 1 monotonic monotonic @@ -122,8 +133,9 @@ define void @cmpxchg_monotonic_arg(ptr %x) { define void @cmpxchg_acq_rel_arg(ptr %x) { ; CHECK: Function Attrs: mustprogress nofree norecurse nounwind willreturn -; CHECK-LABEL: @cmpxchg_acq_rel_arg( -; CHECK-NEXT: [[TMP1:%.*]] = cmpxchg ptr [[X:%.*]], i32 0, i32 1 acq_rel monotonic, align 4 +; CHECK-LABEL: define void @cmpxchg_acq_rel_arg( +; CHECK-SAME: ptr captures(none) [[X:%.*]]) #[[ATTR2]] { +; CHECK-NEXT: [[TMP1:%.*]] = cmpxchg ptr [[X]], i32 0, i32 1 acq_rel monotonic, align 4 ; CHECK-NEXT: ret void ; cmpxchg ptr %x, i32 0, i32 1 acq_rel monotonic @@ -132,8 +144,9 @@ define void @cmpxchg_acq_rel_arg(ptr %x) { define void @cmpxchg_monotonic_volatile_arg(ptr %x) { ; CHECK: Function Attrs: nofree norecurse nounwind memory(argmem: readwrite, inaccessiblemem: readwrite) -; CHECK-LABEL: @cmpxchg_monotonic_volatile_arg( -; CHECK-NEXT: [[TMP1:%.*]] = cmpxchg volatile ptr [[X:%.*]], i32 0, i32 1 monotonic monotonic, align 4 +; CHECK-LABEL: define void @cmpxchg_monotonic_volatile_arg( +; CHECK-SAME: ptr [[X:%.*]]) #[[ATTR3]] { +; CHECK-NEXT: [[TMP1:%.*]] = cmpxchg volatile ptr [[X]], i32 0, i32 1 monotonic monotonic, align 4 ; CHECK-NEXT: ret void ; cmpxchg volatile ptr %x, i32 0, i32 1 monotonic monotonic >From 5841f7222a13bd146869d1ecfe7493d264d3ce42 Mon Sep 17 00:00:00 2001 From: Nikita Popov <[email protected]> Date: Tue, 5 May 2026 10:58:02 +0200 Subject: [PATCH 6/7] Do not report ordered atomic as readonly/writeonly --- llvm/lib/Transforms/IPO/FunctionAttrs.cpp | 12 ++++++------ llvm/test/Transforms/FunctionAttrs/atomic.ll | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp index e76b5a123860d..90ed210077d5a 100644 --- a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp +++ b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp @@ -958,9 +958,9 @@ determinePointerAccessAttrs(Argument *A, } case Instruction::Load: - // A volatile load has side effects beyond what readonly can be relied - // upon. - if (cast<LoadInst>(I)->isVolatile()) + // Volatile and ordered atomic accesses are modelled as reading and + // writing the location. + if (!cast<LoadInst>(I)->isUnordered()) return Attribute::None; IsRead = true; @@ -971,9 +971,9 @@ determinePointerAccessAttrs(Argument *A, // untrackable capture return Attribute::None; - // A volatile store has side effects beyond what writeonly can be relied - // upon. - if (cast<StoreInst>(I)->isVolatile()) + // Volatile and ordered atomic accesses are modelled as reading and + // writing the location. + if (!cast<StoreInst>(I)->isUnordered()) return Attribute::None; IsWrite = true; diff --git a/llvm/test/Transforms/FunctionAttrs/atomic.ll b/llvm/test/Transforms/FunctionAttrs/atomic.ll index 45ce345f9627d..c3db4fba585bd 100644 --- a/llvm/test/Transforms/FunctionAttrs/atomic.ll +++ b/llvm/test/Transforms/FunctionAttrs/atomic.ll @@ -24,7 +24,7 @@ entry: define i32 @load_monotonic(ptr %x) { ; CHECK: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(argmem: readwrite) ; CHECK-LABEL: define i32 @load_monotonic( -; CHECK-SAME: ptr readonly captures(none) [[X:%.*]]) #[[ATTR1:[0-9]+]] { +; CHECK-SAME: ptr captures(none) [[X:%.*]]) #[[ATTR1:[0-9]+]] { ; CHECK-NEXT: [[R:%.*]] = load atomic i32, ptr [[X]] monotonic, align 4 ; CHECK-NEXT: ret i32 [[R]] ; @@ -35,7 +35,7 @@ define i32 @load_monotonic(ptr %x) { define i32 @load_acquire(ptr %x) { ; CHECK: Function Attrs: mustprogress nofree norecurse nounwind willreturn ; CHECK-LABEL: define i32 @load_acquire( -; CHECK-SAME: ptr readonly captures(none) [[X:%.*]]) #[[ATTR2:[0-9]+]] { +; CHECK-SAME: ptr captures(none) [[X:%.*]]) #[[ATTR2:[0-9]+]] { ; CHECK-NEXT: [[R:%.*]] = load atomic i32, ptr [[X]] acquire, align 4 ; CHECK-NEXT: ret i32 [[R]] ; @@ -46,7 +46,7 @@ define i32 @load_acquire(ptr %x) { define i32 @load_seq_cst(ptr %x) { ; CHECK: Function Attrs: mustprogress nofree norecurse nounwind willreturn ; CHECK-LABEL: define i32 @load_seq_cst( -; CHECK-SAME: ptr readonly captures(none) [[X:%.*]]) #[[ATTR2]] { +; CHECK-SAME: ptr captures(none) [[X:%.*]]) #[[ATTR2]] { ; CHECK-NEXT: [[R:%.*]] = load atomic i32, ptr [[X]] acquire, align 4 ; CHECK-NEXT: ret i32 [[R]] ; @@ -57,7 +57,7 @@ define i32 @load_seq_cst(ptr %x) { define void @store_monotonic(ptr %x) { ; CHECK: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(argmem: readwrite) ; CHECK-LABEL: define void @store_monotonic( -; CHECK-SAME: ptr writeonly captures(none) [[X:%.*]]) #[[ATTR1]] { +; CHECK-SAME: ptr captures(none) [[X:%.*]]) #[[ATTR1]] { ; CHECK-NEXT: store atomic i32 0, ptr [[X]] monotonic, align 4 ; CHECK-NEXT: ret void ; @@ -68,7 +68,7 @@ define void @store_monotonic(ptr %x) { define void @store_release(ptr %x) { ; CHECK: Function Attrs: mustprogress nofree norecurse nounwind willreturn ; CHECK-LABEL: define void @store_release( -; CHECK-SAME: ptr writeonly captures(none) [[X:%.*]]) #[[ATTR2]] { +; CHECK-SAME: ptr captures(none) [[X:%.*]]) #[[ATTR2]] { ; CHECK-NEXT: store atomic i32 0, ptr [[X]] release, align 4 ; CHECK-NEXT: ret void ; @@ -79,7 +79,7 @@ define void @store_release(ptr %x) { define void @store_seq_cst(ptr %x) { ; CHECK: Function Attrs: mustprogress nofree norecurse nounwind willreturn ; CHECK-LABEL: define void @store_seq_cst( -; CHECK-SAME: ptr writeonly captures(none) [[X:%.*]]) #[[ATTR2]] { +; CHECK-SAME: ptr captures(none) [[X:%.*]]) #[[ATTR2]] { ; CHECK-NEXT: store atomic i32 0, ptr [[X]] seq_cst, align 4 ; CHECK-NEXT: ret void ; >From e0c6ffa0d9a2f2bfc72e0ed6a5f3d27a9156c1b2 Mon Sep 17 00:00:00 2001 From: Nikita Popov <[email protected]> Date: Tue, 5 May 2026 12:10:03 +0200 Subject: [PATCH 7/7] Update clang test --- ...atomic-builtins-default-to-device-scope.cl | 37 ++++++++++--------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/clang/test/CodeGenOpenCL/atomic-builtins-default-to-device-scope.cl b/clang/test/CodeGenOpenCL/atomic-builtins-default-to-device-scope.cl index 0cf961fc572b9..b3464f0306a7a 100644 --- a/clang/test/CodeGenOpenCL/atomic-builtins-default-to-device-scope.cl +++ b/clang/test/CodeGenOpenCL/atomic-builtins-default-to-device-scope.cl @@ -5,26 +5,26 @@ // RUN: | FileCheck %s --check-prefix=SPIRV // AMDGCN-LABEL: define dso_local i32 @load( -// AMDGCN-SAME: ptr noundef readonly captures(none) [[P:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] { +// AMDGCN-SAME: ptr noundef captures(none) [[P:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] { // AMDGCN-NEXT: [[ENTRY:.*:]] // AMDGCN-NEXT: [[TMP0:%.*]] = load atomic i32, ptr [[P]] syncscope("agent") seq_cst, align 4 // AMDGCN-NEXT: ret i32 [[TMP0]] // // SPIRV-LABEL: define spir_func i32 @load( -// SPIRV-SAME: ptr addrspace(4) noundef readonly captures(none) [[P:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] { +// SPIRV-SAME: ptr addrspace(4) noundef captures(none) [[P:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] { // SPIRV-NEXT: [[ENTRY:.*:]] // SPIRV-NEXT: [[TMP0:%.*]] = load atomic i32, ptr addrspace(4) [[P]] syncscope("device") seq_cst, align 4 // SPIRV-NEXT: ret i32 [[TMP0]] // int load(int *p) { return __atomic_load_n(p, __ATOMIC_SEQ_CST); } // AMDGCN-LABEL: define dso_local void @store( -// AMDGCN-SAME: ptr noundef writeonly captures(none) [[P:%.*]], i32 noundef [[X:%.*]]) local_unnamed_addr #[[ATTR0]] { +// AMDGCN-SAME: ptr noundef captures(none) [[P:%.*]], i32 noundef [[X:%.*]]) local_unnamed_addr #[[ATTR0]] { // AMDGCN-NEXT: [[ENTRY:.*:]] // AMDGCN-NEXT: store atomic i32 [[X]], ptr [[P]] syncscope("agent") seq_cst, align 4 // AMDGCN-NEXT: ret void // // SPIRV-LABEL: define spir_func void @store( -// SPIRV-SAME: ptr addrspace(4) noundef writeonly captures(none) [[P:%.*]], i32 noundef [[X:%.*]]) local_unnamed_addr #[[ATTR0]] { +// SPIRV-SAME: ptr addrspace(4) noundef captures(none) [[P:%.*]], i32 noundef [[X:%.*]]) local_unnamed_addr #[[ATTR0]] { // SPIRV-NEXT: [[ENTRY:.*:]] // SPIRV-NEXT: store atomic i32 [[X]], ptr addrspace(4) [[P]] syncscope("device") seq_cst, align 4 // SPIRV-NEXT: ret void @@ -33,7 +33,7 @@ void store(int *p, int x) { return __atomic_store_n(p, x, __ATOMIC_SEQ_CST); } // AMDGCN-LABEL: define dso_local i32 @add( // AMDGCN-SAME: ptr noundef captures(none) [[P:%.*]], i32 noundef [[X:%.*]]) local_unnamed_addr #[[ATTR0]] { // AMDGCN-NEXT: [[ENTRY:.*:]] -// AMDGCN-NEXT: [[TMP0:%.*]] = atomicrmw add ptr [[P]], i32 [[X]] syncscope("agent") seq_cst, align 4 +// AMDGCN-NEXT: [[TMP0:%.*]] = atomicrmw add ptr [[P]], i32 [[X]] syncscope("agent") seq_cst, align 4, !amdgpu.no.fine.grained.memory [[META7:![0-9]+]], !amdgpu.no.remote.memory [[META7]] // AMDGCN-NEXT: ret i32 [[TMP0]] // // SPIRV-LABEL: define spir_func i32 @add( @@ -46,7 +46,7 @@ int add(int *p, int x) { return __atomic_fetch_add(p, x, __ATOMIC_SEQ_CST); } // AMDGCN-LABEL: define dso_local float @fadd( // AMDGCN-SAME: ptr noundef captures(none) [[P:%.*]], float noundef [[X:%.*]]) local_unnamed_addr #[[ATTR0]] { // AMDGCN-NEXT: [[ENTRY:.*:]] -// AMDGCN-NEXT: [[TMP0:%.*]] = atomicrmw fadd ptr [[P]], float [[X]] syncscope("agent") seq_cst, align 4 +// AMDGCN-NEXT: [[TMP0:%.*]] = atomicrmw fadd ptr [[P]], float [[X]] syncscope("agent") seq_cst, align 4, !amdgpu.no.fine.grained.memory [[META7]], !amdgpu.no.remote.memory [[META7]] // AMDGCN-NEXT: ret float [[TMP0]] // // SPIRV-LABEL: define spir_func float @fadd( @@ -59,7 +59,7 @@ float fadd(float *p, float x) { return __atomic_fetch_add(p, x, __ATOMIC_SEQ_CST // AMDGCN-LABEL: define dso_local i32 @sub( // AMDGCN-SAME: ptr noundef captures(none) [[P:%.*]], i32 noundef [[X:%.*]]) local_unnamed_addr #[[ATTR0]] { // AMDGCN-NEXT: [[ENTRY:.*:]] -// AMDGCN-NEXT: [[TMP0:%.*]] = atomicrmw sub ptr [[P]], i32 [[X]] syncscope("agent") seq_cst, align 4 +// AMDGCN-NEXT: [[TMP0:%.*]] = atomicrmw sub ptr [[P]], i32 [[X]] syncscope("agent") seq_cst, align 4, !amdgpu.no.fine.grained.memory [[META7]], !amdgpu.no.remote.memory [[META7]] // AMDGCN-NEXT: ret i32 [[TMP0]] // // SPIRV-LABEL: define spir_func i32 @sub( @@ -72,7 +72,7 @@ int sub(int *p, int x) { return __atomic_fetch_sub(p, x, __ATOMIC_SEQ_CST); } // AMDGCN-LABEL: define dso_local float @fsub( // AMDGCN-SAME: ptr noundef captures(none) [[P:%.*]], float noundef [[X:%.*]]) local_unnamed_addr #[[ATTR0]] { // AMDGCN-NEXT: [[ENTRY:.*:]] -// AMDGCN-NEXT: [[TMP0:%.*]] = atomicrmw fsub ptr [[P]], float [[X]] syncscope("agent") seq_cst, align 4 +// AMDGCN-NEXT: [[TMP0:%.*]] = atomicrmw fsub ptr [[P]], float [[X]] syncscope("agent") seq_cst, align 4, !amdgpu.no.fine.grained.memory [[META7]], !amdgpu.no.remote.memory [[META7]] // AMDGCN-NEXT: ret float [[TMP0]] // // SPIRV-LABEL: define spir_func float @fsub( @@ -85,7 +85,7 @@ float fsub(float *p, float x) { return __atomic_fetch_sub(p, x, __ATOMIC_SEQ_CST // AMDGCN-LABEL: define dso_local i32 @and( // AMDGCN-SAME: ptr noundef captures(none) [[P:%.*]], i32 noundef [[X:%.*]]) local_unnamed_addr #[[ATTR0]] { // AMDGCN-NEXT: [[ENTRY:.*:]] -// AMDGCN-NEXT: [[TMP0:%.*]] = atomicrmw and ptr [[P]], i32 [[X]] syncscope("agent") seq_cst, align 4 +// AMDGCN-NEXT: [[TMP0:%.*]] = atomicrmw and ptr [[P]], i32 [[X]] syncscope("agent") seq_cst, align 4, !amdgpu.no.fine.grained.memory [[META7]], !amdgpu.no.remote.memory [[META7]] // AMDGCN-NEXT: ret i32 [[TMP0]] // // SPIRV-LABEL: define spir_func i32 @and( @@ -98,7 +98,7 @@ int and(int *p, int x) { return __atomic_fetch_and(p, x, __ATOMIC_SEQ_CST); } // AMDGCN-LABEL: define dso_local i32 @nand( // AMDGCN-SAME: ptr noundef captures(none) [[P:%.*]], i32 noundef [[X:%.*]]) local_unnamed_addr #[[ATTR0]] { // AMDGCN-NEXT: [[ENTRY:.*:]] -// AMDGCN-NEXT: [[TMP0:%.*]] = atomicrmw nand ptr [[P]], i32 [[X]] syncscope("agent") seq_cst, align 4 +// AMDGCN-NEXT: [[TMP0:%.*]] = atomicrmw nand ptr [[P]], i32 [[X]] syncscope("agent") seq_cst, align 4, !amdgpu.no.fine.grained.memory [[META7]], !amdgpu.no.remote.memory [[META7]] // AMDGCN-NEXT: ret i32 [[TMP0]] // // SPIRV-LABEL: define spir_func i32 @nand( @@ -111,7 +111,7 @@ int nand(int *p, int x) { return __atomic_fetch_nand(p, x, __ATOMIC_SEQ_CST); } // AMDGCN-LABEL: define dso_local i32 @or( // AMDGCN-SAME: ptr noundef captures(none) [[P:%.*]], i32 noundef [[X:%.*]]) local_unnamed_addr #[[ATTR0]] { // AMDGCN-NEXT: [[ENTRY:.*:]] -// AMDGCN-NEXT: [[TMP0:%.*]] = atomicrmw or ptr [[P]], i32 [[X]] syncscope("agent") seq_cst, align 4 +// AMDGCN-NEXT: [[TMP0:%.*]] = atomicrmw or ptr [[P]], i32 [[X]] syncscope("agent") seq_cst, align 4, !amdgpu.no.fine.grained.memory [[META7]], !amdgpu.no.remote.memory [[META7]] // AMDGCN-NEXT: ret i32 [[TMP0]] // // SPIRV-LABEL: define spir_func i32 @or( @@ -124,7 +124,7 @@ int or(int *p, int x) { return __atomic_fetch_or(p, x, __ATOMIC_SEQ_CST); } // AMDGCN-LABEL: define dso_local i32 @xor( // AMDGCN-SAME: ptr noundef captures(none) [[P:%.*]], i32 noundef [[X:%.*]]) local_unnamed_addr #[[ATTR0]] { // AMDGCN-NEXT: [[ENTRY:.*:]] -// AMDGCN-NEXT: [[TMP0:%.*]] = atomicrmw xor ptr [[P]], i32 [[X]] syncscope("agent") seq_cst, align 4 +// AMDGCN-NEXT: [[TMP0:%.*]] = atomicrmw xor ptr [[P]], i32 [[X]] syncscope("agent") seq_cst, align 4, !amdgpu.no.fine.grained.memory [[META7]], !amdgpu.no.remote.memory [[META7]] // AMDGCN-NEXT: ret i32 [[TMP0]] // // SPIRV-LABEL: define spir_func i32 @xor( @@ -137,7 +137,7 @@ int xor(int *p, int x) { return __atomic_fetch_xor(p, x, __ATOMIC_SEQ_CST); } // AMDGCN-LABEL: define dso_local i32 @min( // AMDGCN-SAME: ptr noundef captures(none) [[P:%.*]], i32 noundef [[X:%.*]]) local_unnamed_addr #[[ATTR0]] { // AMDGCN-NEXT: [[ENTRY:.*:]] -// AMDGCN-NEXT: [[TMP0:%.*]] = atomicrmw min ptr [[P]], i32 [[X]] syncscope("agent") seq_cst, align 4 +// AMDGCN-NEXT: [[TMP0:%.*]] = atomicrmw min ptr [[P]], i32 [[X]] syncscope("agent") seq_cst, align 4, !amdgpu.no.fine.grained.memory [[META7]], !amdgpu.no.remote.memory [[META7]] // AMDGCN-NEXT: ret i32 [[TMP0]] // // SPIRV-LABEL: define spir_func i32 @min( @@ -150,7 +150,7 @@ int min(int *p, int x) { return __atomic_fetch_min(p, x, __ATOMIC_SEQ_CST); } // AMDGCN-LABEL: define dso_local float @fmin( // AMDGCN-SAME: ptr noundef captures(none) [[P:%.*]], float noundef [[X:%.*]]) local_unnamed_addr #[[ATTR0]] { // AMDGCN-NEXT: [[ENTRY:.*:]] -// AMDGCN-NEXT: [[TMP0:%.*]] = atomicrmw fmin ptr [[P]], float [[X]] syncscope("agent") seq_cst, align 4 +// AMDGCN-NEXT: [[TMP0:%.*]] = atomicrmw fmin ptr [[P]], float [[X]] syncscope("agent") seq_cst, align 4, !amdgpu.no.fine.grained.memory [[META7]], !amdgpu.no.remote.memory [[META7]] // AMDGCN-NEXT: ret float [[TMP0]] // // SPIRV-LABEL: define spir_func float @fmin( @@ -163,7 +163,7 @@ float fmin(float *p, float x) { return __atomic_fetch_min(p, x, __ATOMIC_SEQ_CST // AMDGCN-LABEL: define dso_local i32 @max( // AMDGCN-SAME: ptr noundef captures(none) [[P:%.*]], i32 noundef [[X:%.*]]) local_unnamed_addr #[[ATTR0]] { // AMDGCN-NEXT: [[ENTRY:.*:]] -// AMDGCN-NEXT: [[TMP0:%.*]] = atomicrmw max ptr [[P]], i32 [[X]] syncscope("agent") seq_cst, align 4 +// AMDGCN-NEXT: [[TMP0:%.*]] = atomicrmw max ptr [[P]], i32 [[X]] syncscope("agent") seq_cst, align 4, !amdgpu.no.fine.grained.memory [[META7]], !amdgpu.no.remote.memory [[META7]] // AMDGCN-NEXT: ret i32 [[TMP0]] // // SPIRV-LABEL: define spir_func i32 @max( @@ -176,7 +176,7 @@ int max(int *p, int x) { return __atomic_fetch_max(p, x, __ATOMIC_SEQ_CST); } // AMDGCN-LABEL: define dso_local float @fmax( // AMDGCN-SAME: ptr noundef captures(none) [[P:%.*]], float noundef [[X:%.*]]) local_unnamed_addr #[[ATTR0]] { // AMDGCN-NEXT: [[ENTRY:.*:]] -// AMDGCN-NEXT: [[TMP0:%.*]] = atomicrmw fmax ptr [[P]], float [[X]] syncscope("agent") seq_cst, align 4 +// AMDGCN-NEXT: [[TMP0:%.*]] = atomicrmw fmax ptr [[P]], float [[X]] syncscope("agent") seq_cst, align 4, !amdgpu.no.fine.grained.memory [[META7]], !amdgpu.no.remote.memory [[META7]] // AMDGCN-NEXT: ret float [[TMP0]] // // SPIRV-LABEL: define spir_func float @fmax( @@ -189,7 +189,7 @@ float fmax(float *p, float x) { return __atomic_fetch_max(p, x, __ATOMIC_SEQ_CST // AMDGCN-LABEL: define dso_local i32 @xchg( // AMDGCN-SAME: ptr noundef captures(none) [[P:%.*]], i32 noundef [[X:%.*]]) local_unnamed_addr #[[ATTR0]] { // AMDGCN-NEXT: [[ENTRY:.*:]] -// AMDGCN-NEXT: [[TMP0:%.*]] = atomicrmw xchg ptr [[P]], i32 [[X]] syncscope("agent") seq_cst, align 4 +// AMDGCN-NEXT: [[TMP0:%.*]] = atomicrmw xchg ptr [[P]], i32 [[X]] syncscope("agent") seq_cst, align 4, !amdgpu.no.fine.grained.memory [[META7]], !amdgpu.no.remote.memory [[META7]] // AMDGCN-NEXT: ret i32 [[TMP0]] // // SPIRV-LABEL: define spir_func i32 @xchg( @@ -233,3 +233,6 @@ int cmpxchg(int *p, int x, int y) { return __atomic_compare_exchange(p, &x, &y, // SPIRV-NEXT: ret i32 [[CONV]] // int cmpxchg_weak(int *p, int x, int y) { return __atomic_compare_exchange(p, &x, &y, 1, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); } +//. +// AMDGCN: [[META7]] = !{} +//. _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
