[llvm] [IR] Add an "atomicity" operand bundle (PR #223298)
via llvm-commits
llvm-commits at lists.llvm.org
Sun Sep 13 21:06:20 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-llvm-ir
Author: Arseniy Obolenskiy (aobolensk)
<details>
<summary>Changes</summary>
Record that a call implements an atomic memory access for cases where the atomicity cannot be recovered from the call itself
---
Patch is 20.21 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/223298.diff
15 Files Affected:
- (modified) llvm/docs/LangRef.md (+24)
- (modified) llvm/include/llvm/IR/InstrTypes.h (+11)
- (modified) llvm/include/llvm/IR/Instructions.h (+4)
- (modified) llvm/include/llvm/IR/LLVMContext.h (+2-1)
- (modified) llvm/include/llvm/Support/AtomicOrdering.h (+14)
- (modified) llvm/lib/CodeGen/MIRParser/MIParser.cpp (+2-10)
- (modified) llvm/lib/IR/Instruction.cpp (+16)
- (modified) llvm/lib/IR/Instructions.cpp (+22-2)
- (modified) llvm/lib/IR/LLVMContext.cpp (+2)
- (modified) llvm/lib/IR/Verifier.cpp (+27-1)
- (modified) llvm/lib/Transforms/IPO/Attributor.cpp (+7-6)
- (modified) llvm/lib/Transforms/Scalar/TailRecursionElimination.cpp (+7-6)
- (modified) llvm/test/Bitcode/operand-bundles-bc-analyzer.ll (+1)
- (added) llvm/test/Verifier/atomicity-bundle.ll (+39)
- (modified) llvm/unittests/IR/InstructionsTest.cpp (+28)
``````````diff
diff --git a/llvm/docs/LangRef.md b/llvm/docs/LangRef.md
index 14caff88243c1..71d0ef9bbc006 100644
--- a/llvm/docs/LangRef.md
+++ b/llvm/docs/LangRef.md
@@ -3456,6 +3456,30 @@ value of its first argument instead of calling the specified function
or intrinsic. This is achieved with `PATCHINST` relocations on the
target instructions (see the AArch64 psABI for details).
+(atomicity)=
+
+#### Atomicity Operand Bundles
+
+An `"atomicity"` operand bundle records that a call implements an atomic
+memory access, for cases where the atomicity cannot be recovered from the
+call itself. At most one such bundle may be present, and it must have exactly
+two operands: a metadata string naming an atomic ordering and a metadata
+string naming a synchronization scope, where the empty string denotes system
+scope.
+
+```llvm
+call void @store_release(ptr %p, i32 %val)
+ [ "atomicity"(metadata !"release", metadata !"agent") ]
+```
+
+A call carrying the bundle reports `true` from `Instruction::isAtomic`, so
+generic transforms treat it like any other atomic operation. The bundle
+carries no memory effects of its own, so it does not make an
+otherwise-analyzable call opaque.
+
+A target lowers the bundle onto the selected instruction. Targets are
+expected to document the orderings and scope names they accept.
+
(moduleasm)=
### Module-Level Inline Assembly
diff --git a/llvm/include/llvm/IR/InstrTypes.h b/llvm/include/llvm/IR/InstrTypes.h
index 5f7df6a4eb6f8..f2d5c05b26371 100644
--- a/llvm/include/llvm/IR/InstrTypes.h
+++ b/llvm/include/llvm/IR/InstrTypes.h
@@ -1165,6 +1165,13 @@ using ConstOperandBundleDef = OperandBundleDefT<const Value *>;
//===----------------------------------------------------------------------===//
/// Base class for all callable instructions (InvokeInst and CallInst)
+/// The atomicity of a memory access implemented by a call, as recorded by an
+/// "atomicity" operand bundle.
+struct AtomicityBundleInfo {
+ AtomicOrdering Order;
+ SyncScope::ID SSID;
+};
+
/// Holds everything related to calling a function.
///
/// All call-like instructions are required to use a common operand layout:
@@ -2203,6 +2210,10 @@ class CallBase : public Instruction {
/// may write to the heap.
LLVM_ABI bool hasClobberingOperandBundles() const;
+ /// Return the atomicity recorded by the "atomicity" operand bundle, or
+ /// std::nullopt if there is no such bundle.
+ LLVM_ABI std::optional<AtomicityBundleInfo> getAtomicityBundleInfo() const;
+
/// Return true if the bundle operand at index \p OpIdx has the
/// attribute \p A.
bool bundleOperandHasAttr(unsigned OpIdx, Attribute::AttrKind A) const {
diff --git a/llvm/include/llvm/IR/Instructions.h b/llvm/include/llvm/IR/Instructions.h
index 00d689283479f..2477f56517b60 100644
--- a/llvm/include/llvm/IR/Instructions.h
+++ b/llvm/include/llvm/IR/Instructions.h
@@ -5334,6 +5334,8 @@ inline std::optional<SyncScope::ID> getAtomicSyncScopeID(const Instruction *I) {
return AI->getSyncScopeID();
if (auto *AI = dyn_cast<AtomicRMWInst>(I))
return AI->getSyncScopeID();
+ if (auto *CB = dyn_cast<CallBase>(I))
+ return CB->getAtomicityBundleInfo()->SSID;
llvm_unreachable("unhandled atomic operation");
}
@@ -5351,6 +5353,8 @@ inline void setAtomicSyncScopeID(Instruction *I, SyncScope::ID SSID) {
else if (auto *AI = dyn_cast<AtomicRMWInst>(I))
AI->setSyncScopeID(SSID);
else
+ // A call records its scope in an operand bundle, which cannot be mutated
+ // in place.
llvm_unreachable("unhandled atomic operation");
}
diff --git a/llvm/include/llvm/IR/LLVMContext.h b/llvm/include/llvm/IR/LLVMContext.h
index ced9b9227538e..393d460c820dd 100644
--- a/llvm/include/llvm/IR/LLVMContext.h
+++ b/llvm/include/llvm/IR/LLVMContext.h
@@ -100,7 +100,8 @@ class LLVMContext {
OB_kcfi, // "kcfi"
OB_convergencectrl, // "convergencectrl"
OB_deactivation_symbol, // "deactivation-symbol"
- OB_LastBundleID = OB_deactivation_symbol
+ OB_atomicity, // "atomicity"
+ OB_LastBundleID = OB_atomicity
};
/// getMDKindID - Return a unique non-zero ID for the specified metadata kind.
diff --git a/llvm/include/llvm/Support/AtomicOrdering.h b/llvm/include/llvm/Support/AtomicOrdering.h
index ad35cc3d4582f..68dd2f2b46233 100644
--- a/llvm/include/llvm/Support/AtomicOrdering.h
+++ b/llvm/include/llvm/Support/AtomicOrdering.h
@@ -17,7 +17,9 @@
#ifndef LLVM_SUPPORT_ATOMICORDERING_H
#define LLVM_SUPPORT_ATOMICORDERING_H
+#include "llvm/ADT/StringRef.h"
#include <cstddef>
+#include <optional>
namespace llvm {
@@ -86,6 +88,18 @@ inline const char *toIRString(AtomicOrdering ao) {
return names[static_cast<size_t>(ao)];
}
+/// Inverse of toIRString. "not_atomic" is rejected: it is the absence of an
+/// ordering, not an IR keyword. Returns std::nullopt if Str names none.
+inline std::optional<AtomicOrdering> parseAtomicOrdering(StringRef Str) {
+ for (size_t I = static_cast<size_t>(AtomicOrdering::Unordered);
+ I <= static_cast<size_t>(AtomicOrdering::SequentiallyConsistent); ++I) {
+ if (isValidAtomicOrdering(I) &&
+ Str == toIRString(static_cast<AtomicOrdering>(I)))
+ return static_cast<AtomicOrdering>(I);
+ }
+ return std::nullopt;
+}
+
/// Returns true if ao is stronger than other as defined by the AtomicOrdering
/// lattice, which is based on C++'s definition.
inline bool isStrongerThan(AtomicOrdering AO, AtomicOrdering Other) {
diff --git a/llvm/lib/CodeGen/MIRParser/MIParser.cpp b/llvm/lib/CodeGen/MIRParser/MIParser.cpp
index 4b951785e9c41..f0546499b1221 100644
--- a/llvm/lib/CodeGen/MIRParser/MIParser.cpp
+++ b/llvm/lib/CodeGen/MIRParser/MIParser.cpp
@@ -3576,16 +3576,8 @@ bool MIParser::parseOptionalAtomicOrdering(AtomicOrdering &Order) {
if (Token.isNot(MIToken::Identifier))
return false;
- Order = StringSwitch<AtomicOrdering>(Token.stringValue())
- .Case("unordered", AtomicOrdering::Unordered)
- .Case("monotonic", AtomicOrdering::Monotonic)
- .Case("acquire", AtomicOrdering::Acquire)
- .Case("release", AtomicOrdering::Release)
- .Case("acq_rel", AtomicOrdering::AcquireRelease)
- .Case("seq_cst", AtomicOrdering::SequentiallyConsistent)
- .Default(AtomicOrdering::NotAtomic);
-
- if (Order != AtomicOrdering::NotAtomic) {
+ if (auto ParsedOrder = parseAtomicOrdering(Token.stringValue())) {
+ Order = *ParsedOrder;
lex();
return false;
}
diff --git a/llvm/lib/IR/Instruction.cpp b/llvm/lib/IR/Instruction.cpp
index 60e4f353585db..48e58330125ae 100644
--- a/llvm/lib/IR/Instruction.cpp
+++ b/llvm/lib/IR/Instruction.cpp
@@ -1186,6 +1186,14 @@ bool Instruction::isAtomic() const {
return cast<LoadInst>(this)->getOrdering() != AtomicOrdering::NotAtomic;
case Instruction::Store:
return cast<StoreInst>(this)->getOrdering() != AtomicOrdering::NotAtomic;
+ case Instruction::Call:
+ case Instruction::Invoke:
+ case Instruction::CallBr:
+ // A call implementing an atomic access records it in an "atomicity"
+ // operand bundle.
+ return cast<CallBase>(this)
+ ->getOperandBundle(LLVMContext::OB_atomicity)
+ .has_value();
}
}
@@ -1198,6 +1206,10 @@ bool Instruction::hasAtomicLoad() const {
case Instruction::AtomicRMW:
case Instruction::Load:
return true;
+ case Instruction::Call:
+ case Instruction::Invoke:
+ case Instruction::CallBr:
+ return mayReadFromMemory();
}
}
@@ -1210,6 +1222,10 @@ bool Instruction::hasAtomicStore() const {
case Instruction::AtomicRMW:
case Instruction::Store:
return true;
+ case Instruction::Call:
+ case Instruction::Invoke:
+ case Instruction::CallBr:
+ return mayWriteToMemory();
}
}
diff --git a/llvm/lib/IR/Instructions.cpp b/llvm/lib/IR/Instructions.cpp
index 325850b0a880d..90113028ac21b 100644
--- a/llvm/lib/IR/Instructions.cpp
+++ b/llvm/lib/IR/Instructions.cpp
@@ -639,7 +639,8 @@ bool CallBase::hasReadingOperandBundles() const {
return hasOperandBundlesOtherThan({LLVMContext::OB_ptrauth,
LLVMContext::OB_kcfi,
LLVMContext::OB_convergencectrl,
- LLVMContext::OB_deactivation_symbol}) &&
+ LLVMContext::OB_deactivation_symbol,
+ LLVMContext::OB_atomicity}) &&
getIntrinsicID() != Intrinsic::assume;
}
@@ -648,10 +649,29 @@ bool CallBase::hasClobberingOperandBundles() const {
{LLVMContext::OB_deopt, LLVMContext::OB_funclet,
LLVMContext::OB_ptrauth, LLVMContext::OB_kcfi,
LLVMContext::OB_convergencectrl,
- LLVMContext::OB_deactivation_symbol}) &&
+ LLVMContext::OB_deactivation_symbol,
+ LLVMContext::OB_atomicity}) &&
getIntrinsicID() != Intrinsic::assume;
}
+std::optional<AtomicityBundleInfo> CallBase::getAtomicityBundleInfo() const {
+ std::optional<OperandBundleUse> Bundle =
+ getOperandBundle(LLVMContext::OB_atomicity);
+ if (!Bundle)
+ return std::nullopt;
+
+ // The Verifier guarantees the shape of the bundle.
+ assert(Bundle->Inputs.size() == 2 && "malformed \"atomicity\" bundle");
+ auto GetString = [](const Value *V) {
+ return cast<MDString>(cast<MetadataAsValue>(V)->getMetadata())->getString();
+ };
+ std::optional<AtomicOrdering> Order =
+ parseAtomicOrdering(GetString(Bundle->Inputs[0]));
+ assert(Order && "malformed \"atomicity\" bundle ordering");
+ return AtomicityBundleInfo{*Order, getContext().getOrInsertSyncScopeID(
+ GetString(Bundle->Inputs[1]))};
+}
+
MemoryEffects CallBase::getMemoryEffects() const {
MemoryEffects ME = getAttributes().getMemoryEffects();
if (auto *Fn = dyn_cast<Function>(getCalledOperand())) {
diff --git a/llvm/lib/IR/LLVMContext.cpp b/llvm/lib/IR/LLVMContext.cpp
index 1fcdd3bc4c142..509ed1379290b 100644
--- a/llvm/lib/IR/LLVMContext.cpp
+++ b/llvm/lib/IR/LLVMContext.cpp
@@ -58,6 +58,8 @@ static StringRef knownBundleName(unsigned BundleTagID) {
return "convergencectrl";
case LLVMContext::OB_deactivation_symbol:
return "deactivation-symbol";
+ case LLVMContext::OB_atomicity:
+ return "atomicity";
default:
llvm_unreachable("unknown bundle id");
}
diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp
index 12ab6e9c34a5e..56ab864a12e8c 100644
--- a/llvm/lib/IR/Verifier.cpp
+++ b/llvm/lib/IR/Verifier.cpp
@@ -504,6 +504,8 @@ class Verifier : public InstVisitor<Verifier>, VerifierSupport {
void verifyAttachedCallBundle(const CallBase &Call,
const OperandBundleUse &BU);
+ void verifyAtomicityBundle(const CallBase &Call, const OperandBundleUse &BU);
+
/// Verify the llvm.experimental.noalias.scope.decl declarations
void verifyNoAliasScopeDecl();
};
@@ -4173,7 +4175,7 @@ void Verifier::visitCallBase(CallBase &Call) {
FoundGCTransitionBundle = false, FoundCFGuardTargetBundle = false,
FoundPreallocatedBundle = false, FoundGCLiveBundle = false,
FoundPtrauthBundle = false, FoundKCFIBundle = false,
- FoundAttachedCallBundle = false;
+ FoundAttachedCallBundle = false, FoundAtomicityBundle = false;
for (unsigned i = 0, e = Call.getNumOperandBundles(); i < e; ++i) {
OperandBundleUse BU = Call.getOperandBundleAt(i);
for (const Value *Input : BU.Inputs)
@@ -4239,6 +4241,11 @@ void Verifier::visitCallBase(CallBase &Call) {
"Multiple \"clang.arc.attachedcall\" operand bundles", Call);
FoundAttachedCallBundle = true;
verifyAttachedCallBundle(Call, BU);
+ } else if (Tag == LLVMContext::OB_atomicity) {
+ Check(!FoundAtomicityBundle, "Multiple \"atomicity\" operand bundles",
+ Call);
+ FoundAtomicityBundle = true;
+ verifyAtomicityBundle(Call, BU);
}
}
@@ -7723,6 +7730,25 @@ void Verifier::verifyAttachedCallBundle(const CallBase &Call,
}
}
+void Verifier::verifyAtomicityBundle(const CallBase &Call,
+ const OperandBundleUse &BU) {
+ Check(BU.Inputs.size() == 2,
+ "Expected exactly two \"atomicity\" bundle operands", Call);
+
+ auto GetMDString = [](const Value *V) -> const MDString * {
+ const auto *MAV = dyn_cast<MetadataAsValue>(V);
+ return MAV ? dyn_cast<MDString>(MAV->getMetadata()) : nullptr;
+ };
+
+ const MDString *Ordering = GetMDString(BU.Inputs[0]);
+ Check(Ordering && parseAtomicOrdering(Ordering->getString()),
+ "\"atomicity\" ordering operand must be a metadata string naming an "
+ "atomic ordering",
+ Call);
+ Check(GetMDString(BU.Inputs[1]),
+ "\"atomicity\" syncscope operand must be a metadata string", Call);
+}
+
void Verifier::verifyNoAliasScopeDecl() {
if (NoAliasScopeDecls.empty())
return;
diff --git a/llvm/lib/Transforms/IPO/Attributor.cpp b/llvm/lib/Transforms/IPO/Attributor.cpp
index 338240b2f8087..01a35f49c50ce 100644
--- a/llvm/lib/Transforms/IPO/Attributor.cpp
+++ b/llvm/lib/Transforms/IPO/Attributor.cpp
@@ -1336,10 +1336,11 @@ SubsumingPositionIterator::SubsumingPositionIterator(const IRPosition &IRP) {
IRPositions.emplace_back(IRP);
// Helper to determine if operand bundles on a call site are benign or
- // potentially problematic. We handle only llvm.assume for now.
+ // potentially problematic. We handle llvm.assume and "atomicity".
auto CanIgnoreOperandBundles = [](const CallBase &CB) {
- return (isa<IntrinsicInst>(CB) &&
- cast<IntrinsicInst>(CB).getIntrinsicID() == Intrinsic ::assume);
+ return !CB.hasOperandBundlesOtherThan(LLVMContext::OB_atomicity) ||
+ (isa<IntrinsicInst>(CB) &&
+ cast<IntrinsicInst>(CB).getIntrinsicID() == Intrinsic::assume);
};
const auto *CB = dyn_cast<CallBase>(&IRP.getAnchorValue());
@@ -1356,7 +1357,7 @@ SubsumingPositionIterator::SubsumingPositionIterator(const IRPosition &IRP) {
assert(CB && "Expected call site!");
// TODO: We need to look at the operand bundles similar to the redirection
// in CallBase.
- if (!CB->hasOperandBundles() || CanIgnoreOperandBundles(*CB))
+ if (CanIgnoreOperandBundles(*CB))
if (auto *Callee = dyn_cast_if_present<Function>(CB->getCalledOperand()))
IRPositions.emplace_back(IRPosition::function(*Callee));
return;
@@ -1364,7 +1365,7 @@ SubsumingPositionIterator::SubsumingPositionIterator(const IRPosition &IRP) {
assert(CB && "Expected call site!");
// TODO: We need to look at the operand bundles similar to the redirection
// in CallBase.
- if (!CB->hasOperandBundles() || CanIgnoreOperandBundles(*CB)) {
+ if (CanIgnoreOperandBundles(*CB)) {
if (auto *Callee =
dyn_cast_if_present<Function>(CB->getCalledOperand())) {
IRPositions.emplace_back(IRPosition::returned(*Callee));
@@ -1385,7 +1386,7 @@ SubsumingPositionIterator::SubsumingPositionIterator(const IRPosition &IRP) {
assert(CB && "Expected call site!");
// TODO: We need to look at the operand bundles similar to the redirection
// in CallBase.
- if (!CB->hasOperandBundles() || CanIgnoreOperandBundles(*CB)) {
+ if (CanIgnoreOperandBundles(*CB)) {
auto *Callee = dyn_cast_if_present<Function>(CB->getCalledOperand());
if (Callee) {
if (Argument *Arg = IRP.getAssociatedArgument())
diff --git a/llvm/lib/Transforms/Scalar/TailRecursionElimination.cpp b/llvm/lib/Transforms/Scalar/TailRecursionElimination.cpp
index 880b0bda90d70..f0d518af5d3a2 100644
--- a/llvm/lib/Transforms/Scalar/TailRecursionElimination.cpp
+++ b/llvm/lib/Transforms/Scalar/TailRecursionElimination.cpp
@@ -303,13 +303,14 @@ static bool markTails(Function &F, OptimizationRemarkEmitter *ORE,
if (II->getIntrinsicID() == Intrinsic::stackrestore)
continue;
- // Special-case operand bundles "clang.arc.attachedcall", "ptrauth", and
- // "kcfi".
+ // Special-case operand bundles "clang.arc.attachedcall", "ptrauth",
+ // "kcfi", and "atomicity".
bool DisableForCold = shouldDisableTailCallsForCold(CI, &F, PSI, BFI);
- bool IsNoTail = CI->isNoTailCall() || DisableForCold ||
- CI->hasOperandBundlesOtherThan(
- {LLVMContext::OB_clang_arc_attachedcall,
- LLVMContext::OB_ptrauth, LLVMContext::OB_kcfi});
+ bool IsNoTail =
+ CI->isNoTailCall() || DisableForCold ||
+ CI->hasOperandBundlesOtherThan(
+ {LLVMContext::OB_clang_arc_attachedcall, LLVMContext::OB_ptrauth,
+ LLVMContext::OB_kcfi, LLVMContext::OB_atomicity});
if (!CI->isNoTailCall() && DisableForCold)
++NumTREPreventedCold;
diff --git a/llvm/test/Bitcode/operand-bundles-bc-analyzer.ll b/llvm/test/Bitcode/operand-bundles-bc-analyzer.ll
index 2ef0e61dd126b..d54b90ba8bcf3 100644
--- a/llvm/test/Bitcode/operand-bundles-bc-analyzer.ll
+++ b/llvm/test/Bitcode/operand-bundles-bc-analyzer.ll
@@ -22,6 +22,7 @@
; CHECK-NEXT: <OPERAND_BUNDLE_TAG
; CHECK-NEXT: <OPERAND_BUNDLE_TAG
; CHECK-NEXT: <OPERAND_BUNDLE_TAG
+; CHECK-NEXT: <OPERAND_BUNDLE_TAG
; CHECK-NEXT: </OPERAND_BUNDLE_TAGS_BLOCK
; CHECK: <FUNCTION_BLOCK
diff --git a/llvm/test/Verifier/atomicity-bundle.ll b/llvm/test/Verifier/atomicity-bundle.ll
new file mode 100644
index 0000000000000..1f170f8ace7f6
--- /dev/null
+++ b/llvm/test/Verifier/atomicity-bundle.ll
@@ -0,0 +1,39 @@
+; RUN: not llvm-as < %s -o /dev/null 2>&1 | FileCheck %s
+
+declare void @f()
+
+; The bundle is not restricted to any particular callee.
+define void @ordinary_call() {
+ call void @f() [ "atomicity"(metadata !"release", metadata !"agent") ]
+ ret void
+}
+
+; CHECK: Multiple "atomicity" operand bundles
+define void @multiple() {
+ call void @f() [ "atomicity"(metadata !"release", metadata !"agent"), "atomicity"(metadata !"release", metadata !"agent") ]
+ ret void
+}
+
+; CHECK: Expected exactly two "atomicity" bundle operands
+define void @wrong_operand_count() {
+ call void @f() [ "atomicity"(metadata !"release") ]
+ ret void
+}
+
+; CHECK: "atomicity" ordering operand must be a metadata string naming an atomic ordering
+define void @not_an_ordering() {
+ call void @f() [ "atomicity"(metadata !"consume", metadata !"agent") ]
+ ret void
+}
+
+; CHECK: "atomicity" ordering operand must be a metadata string naming an atomic ordering
+define void @ordering_not_a_string() {
+ call void @f() [ "atomicity"(i32 0, metadata !"agent") ]
+ ret void
+}
+
+; CHECK: "atomicity" syncscope operand must be a metadata string
+define void @scope_not_a_string() {
+ call void @f() [ "atomicity"(metadata !"release", i32 0) ]
+ ret void
+}
diff --git a/llvm/unittests/IR/InstructionsTest.cpp b/llvm/unittests/IR/InstructionsTest.cpp
index 5f92f325f793f..f1faff246083c 100644
--- a/llvm/unittests/IR/InstructionsTest.cpp
+++ b/llvm/unittests/IR/InstructionsTest.cpp
@@ -2054,6 +2054,34 @@ TEST(InstructionsTest, AtomicSyncscope) {
EXPECT_TRUE(LLVMIsAtomicSingleThread(CmpXchg));
}
+TEST(InstructionsTest, AtomicityBundle) {
+ LLVMContext Ctx;
+
+ Module M("Mod", Ctx);
+ FunctionType *FT = FunctionType::get(Type::getVoidTy(Ctx), {}, false);
+ Function *F = Function::Create(FT, Function::ExternalLinkage, "Fun", M);
+ Function *Callee =
+ Function::Create(FT, Function::ExternalLinkage, "Callee", M);
+ BasicBlock *BB = BasicBlock::Create(Ctx, "Entry", F);
+ IRBuilder<> Builder(BB);
+
+ CallInst *Plain = Builder.CreateCall(Callee);
+ EXPECT_FALSE(Plain->isAtomic());
+ EXPECT_FALSE(Plain->getAtomicityBundleInfo().has_value());
+ EXPECT_EQ(getAtomicSyncScopeID(Plain), std::nullopt);
+
+ Value *Ops[] = {MetadataAsValue::get(Ctx, MDString::get(Ctx, "release")),
+ MetadataAsValue::get(Ctx, MDString::get(Ctx, "agent"))};
+ CallInst *Atomic =
+ Builder.CreateCall(Callee, {}, {OperandBundleDef("atomicity", Ops)});
+ EXPECT_TRUE(Atomic->isAtomic());
+ EXPECT_EQ(Atomic->getAtomicityBundleInfo()->Order, AtomicOrdering::Release);
+ EXPECT_EQ(getAtomicSyncScopeID(Atomic), Ctx.getOrInsertSyncScopeID("agent"));
+ // The callee is not readnone, so the call both reads and writes memory.
+ EXPECT_TRUE(Atomic->hasAtomicLoad());
+ EXPECT_TRUE(A...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/223298
More information about the llvm-commits
mailing list