[llvm] [IR] Add Instruction::canSynchronize() method (NFC) (PR #196052)

Nikita Popov via llvm-commits llvm-commits at lists.llvm.org
Wed May 6 05:17:15 PDT 2026


https://github.com/nikic created https://github.com/llvm/llvm-project/pull/196052

Extract the logic from FunctionAttrs into a generic API on Instruction, which will check the nosync flag on calls and ordering for atomic instructions.

For now, I've retained the previous implementation which considers monotonic ordering to be synchronizing, which is unnecessarily conservative and doesn't match LangRef (or the Attributor implementation).

This is to make it easier to reuse this logic in more places.

>From 7118f03b0020b156a591883dc64109ed8982503f Mon Sep 17 00:00:00 2001
From: Nikita Popov <npopov at redhat.com>
Date: Wed, 6 May 2026 14:10:51 +0200
Subject: [PATCH] [IR] Add Instruction::canSynchronize() method

Extract the logic from FunctionAttrs into a generic API on
Instruction, which will check the nosync flag on calls and
ordering for atomic instructions.

For now, I've retained the previous implementation which considers
monotonic ordering to be synchronizing, which is unnecessarily
conservative and doesn't match LangRef (or the Attributor
implementation).

This is to make it easier to reuse this logic in more places.
---
 llvm/include/llvm/IR/Instruction.h        |  4 +++
 llvm/lib/IR/Instruction.cpp               | 27 ++++++++++++++
 llvm/lib/Transforms/IPO/FunctionAttrs.cpp | 43 ++++-------------------
 3 files changed, 37 insertions(+), 37 deletions(-)

diff --git a/llvm/include/llvm/IR/Instruction.h b/llvm/include/llvm/IR/Instruction.h
index 0e6c122a4e2ef..ab9108d57b4ec 100644
--- a/llvm/include/llvm/IR/Instruction.h
+++ b/llvm/include/llvm/IR/Instruction.h
@@ -859,6 +859,10 @@ class Instruction : public User,
   /// Return true if this instruction has a volatile memory access.
   LLVM_ABI bool isVolatile() const LLVM_READONLY;
 
+  /// Return true if this instruction can synchronize, in the sense that it
+  /// may introduce a synchronizes-with edge.
+  LLVM_ABI bool canSynchronize() const LLVM_READONLY;
+
   /// Return the type this instruction accesses in memory, if any.
   LLVM_ABI Type *getAccessType() const LLVM_READONLY;
 
diff --git a/llvm/lib/IR/Instruction.cpp b/llvm/lib/IR/Instruction.cpp
index 34861c5817d61..65b26541b4085 100644
--- a/llvm/lib/IR/Instruction.cpp
+++ b/llvm/lib/IR/Instruction.cpp
@@ -1170,6 +1170,33 @@ bool Instruction::isVolatile() const {
   }
 }
 
+bool Instruction::canSynchronize() const {
+  // FIXME: This currently treats atomics with monotonic ordering as
+  // synchronizing. This is unnecessarily conservative and does not match
+  // our LangRef definition of the property.
+  switch (getOpcode()) {
+  default:
+    assert(!isAtomic() && "Unhandled atomic instruction");
+    return false;
+  case Instruction::Fence: {
+    // All legal orderings for fence are stronger than monotonic.
+    auto *FI = cast<FenceInst>(this);
+    return FI->getSyncScopeID() != SyncScope::SingleThread;
+  }
+  case Instruction::AtomicRMW:
+  case Instruction::AtomicCmpXchg:
+    return true;
+  case Instruction::Store:
+    return isStrongerThanUnordered(cast<StoreInst>(this)->getOrdering());
+  case Instruction::Load:
+    return isStrongerThanUnordered(cast<LoadInst>(this)->getOrdering());
+  case Instruction::Call:
+  case Instruction::Invoke:
+  case Instruction::CallBr:
+    return !cast<CallBase>(this)->hasFnAttr(Attribute::NoSync);
+  }
+}
+
 Type *Instruction::getAccessType() const {
   switch (getOpcode()) {
   case Instruction::Store:
diff --git a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp
index 3dc0cc6bdaf46..86bd9d9b8c620 100644
--- a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp
+++ b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp
@@ -1933,46 +1933,15 @@ static bool InstrBreaksNoFree(Instruction &I, const SCCNodeSet &SCCNodes) {
   return true;
 }
 
-// Return true if this is an atomic which has an ordering stronger than
-// unordered.  Note that this is different than the predicate we use in
-// Attributor.  Here we chose to be conservative and consider monotonic
-// operations potentially synchronizing.  We generally don't do much with
-// monotonic operations, so this is simply risk reduction.
-static bool isOrderedAtomic(Instruction *I) {
-  if (!I->isAtomic())
-    return false;
-
-  if (auto *FI = dyn_cast<FenceInst>(I))
-    // All legal orderings for fence are stronger than monotonic.
-    return FI->getSyncScopeID() != SyncScope::SingleThread;
-  else if (isa<AtomicCmpXchgInst>(I) || isa<AtomicRMWInst>(I))
-    return true;
-  else if (auto *SI = dyn_cast<StoreInst>(I))
-    return !SI->isUnordered();
-  else if (auto *LI = dyn_cast<LoadInst>(I))
-    return !LI->isUnordered();
-  else {
-    llvm_unreachable("unknown atomic instruction?");
-  }
-}
-
 static bool InstrBreaksNoSync(Instruction &I, const SCCNodeSet &SCCNodes) {
-  // An ordered atomic may synchronize.  (See comment about on monotonic.)
-  if (isOrderedAtomic(&I))
-    return true;
-
-  auto *CB = dyn_cast<CallBase>(&I);
-  if (!CB)
-    // Non call site cases covered by the two checks above
+  if (!I.canSynchronize())
     return false;
 
-  if (CB->hasFnAttr(Attribute::NoSync))
-    return false;
-
-  // Speculatively assume in SCC.
-  if (Function *Callee = CB->getCalledFunction())
-    if (SCCNodes.contains(Callee))
-      return false;
+  // Speculatively assume in nosync in SCC.
+  if (auto *CB = dyn_cast<CallBase>(&I))
+    if (Function *Callee = CB->getCalledFunction())
+      if (SCCNodes.contains(Callee))
+        return false;
 
   return true;
 }



More information about the llvm-commits mailing list