[llvm] [LLVM] Refine MemoryEffect handling for target-specific intrinsics (PR #155590)
via llvm-commits
llvm-commits at lists.llvm.org
Thu Nov 20 08:46:53 PST 2025
https://github.com/CarolineConcatto updated https://github.com/llvm/llvm-project/pull/155590
>From edff41fe9bd097902c744e79233349d7e0ddd9ec Mon Sep 17 00:00:00 2001
From: CarolineConcatto <caroline.concatto at arm.com>
Date: Fri, 31 Oct 2025 16:26:46 +0000
Subject: [PATCH 1/2] [Draft][LLVM]Extend memory effects for memory alias
analysis
This patch improves memory alias analysis between calls if they
change the target memory.
If both calls dont clobber the same location it can return without
setting ModeRefInfo among them. Otherwise it relies in the default
behaviour to set ModRefInfo to Read and Write.
---
llvm/include/llvm/IR/InstrTypes.h | 1 +
llvm/include/llvm/Support/ModRef.h | 7 ++
llvm/lib/Analysis/MemorySSA.cpp | 29 +++++++
llvm/lib/IR/Instructions.cpp | 5 ++
.../test/Transforms/EarlyCSE/target-memory.ll | 80 +++++++++++++++++++
5 files changed, 122 insertions(+)
create mode 100644 llvm/test/Transforms/EarlyCSE/target-memory.ll
diff --git a/llvm/include/llvm/IR/InstrTypes.h b/llvm/include/llvm/IR/InstrTypes.h
index 9f56779a9b935..f129039cb5299 100644
--- a/llvm/include/llvm/IR/InstrTypes.h
+++ b/llvm/include/llvm/IR/InstrTypes.h
@@ -1936,6 +1936,7 @@ class CallBase : public Instruction {
/// inaccessible from the IR.
LLVM_ABI bool onlyAccessesInaccessibleMemory() const;
LLVM_ABI void setOnlyAccessesInaccessibleMemory();
+ LLVM_ABI bool onlyAccessesTargetMemory() const;
/// Determine if the function may only access memory that is
/// either inaccessible from the IR or pointed to by its arguments.
diff --git a/llvm/include/llvm/Support/ModRef.h b/llvm/include/llvm/Support/ModRef.h
index 34f116e478966..86e0394593846 100644
--- a/llvm/include/llvm/Support/ModRef.h
+++ b/llvm/include/llvm/Support/ModRef.h
@@ -240,6 +240,13 @@ template <typename LocationEnum> class MemoryEffectsBase {
return getWithoutLoc(Location::InaccessibleMem).doesNotAccessMemory();
}
+ /// Whether this function only (at most) accesses target memory.
+ bool onlyAccessesTargetMem() const {
+ return getWithoutLoc(Location::TargetMem0)
+ .getWithoutLoc(Location::TargetMem1)
+ .doesNotAccessMemory();
+ }
+
/// Whether this function only (at most) accesses errno memory.
bool onlyAccessesErrnoMem() const {
return getWithoutLoc(Location::ErrnoMem).doesNotAccessMemory();
diff --git a/llvm/lib/Analysis/MemorySSA.cpp b/llvm/lib/Analysis/MemorySSA.cpp
index 0b2e3fcfd76df..48e00b7e71eed 100644
--- a/llvm/lib/Analysis/MemorySSA.cpp
+++ b/llvm/lib/Analysis/MemorySSA.cpp
@@ -277,6 +277,30 @@ static bool areLoadsReorderable(const LoadInst *Use,
return !(SeqCstUse || MayClobberIsAcquire);
}
+bool writeToSameTargetMemLoc(const CallBase *CallFirst,
+ const CallBase *CallSecond) {
+ MemoryEffects ME1 = CallFirst->getMemoryEffects();
+ MemoryEffects ME2 = CallSecond->getMemoryEffects();
+
+ auto writes = [](ModRefInfo m) {
+ return m != ModRefInfo::NoModRef && m != ModRefInfo::Ref;
+ };
+
+ for (unsigned ILoc = static_cast<unsigned>(IRMemLocation::FirstTarget);
+ ILoc <= static_cast<unsigned>(IRMemLocation::Last); ++ILoc) {
+ const auto Loc = static_cast<IRMemLocation>(ILoc);
+
+ if (!writes(ME1.getModRef(Loc)))
+ continue;
+ if (!writes(ME2.getModRef(Loc)))
+ continue;
+
+ // Both have write capability to the same location.
+ return true;
+ }
+ return false;
+}
+
template <typename AliasAnalysisType>
static bool
instructionClobbersQuery(const MemoryDef *MD, const MemoryLocation &UseLoc,
@@ -311,6 +335,11 @@ instructionClobbersQuery(const MemoryDef *MD, const MemoryLocation &UseLoc,
}
if (auto *CB = dyn_cast_or_null<CallBase>(UseInst)) {
+ if (auto *CU = dyn_cast_or_null<CallBase>(DefInst))
+ if (CU->onlyAccessesTargetMemory() || CB->onlyAccessesTargetMemory()) {
+ if (!writeToSameTargetMemLoc(CB, CU))
+ return false;
+ }
ModRefInfo I = AA.getModRefInfo(DefInst, CB);
return isModOrRefSet(I);
}
diff --git a/llvm/lib/IR/Instructions.cpp b/llvm/lib/IR/Instructions.cpp
index cd39970f5111f..06273cf9a6caf 100644
--- a/llvm/lib/IR/Instructions.cpp
+++ b/llvm/lib/IR/Instructions.cpp
@@ -693,6 +693,11 @@ void CallBase::setOnlyAccessesArgMemory() {
bool CallBase::onlyAccessesInaccessibleMemory() const {
return getMemoryEffects().onlyAccessesInaccessibleMem();
}
+
+bool CallBase::onlyAccessesTargetMemory() const {
+ return getMemoryEffects().onlyAccessesTargetMem();
+}
+
void CallBase::setOnlyAccessesInaccessibleMemory() {
setMemoryEffects(getMemoryEffects() & MemoryEffects::inaccessibleMemOnly());
}
diff --git a/llvm/test/Transforms/EarlyCSE/target-memory.ll b/llvm/test/Transforms/EarlyCSE/target-memory.ll
new file mode 100644
index 0000000000000..1d6235f97aa01
--- /dev/null
+++ b/llvm/test/Transforms/EarlyCSE/target-memory.ll
@@ -0,0 +1,80 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -S -passes='early-cse<memssa>' < %s | FileCheck %s
+
+define void @foo(){
+; CHECK-LABEL: define void @foo() {
+; CHECK-NEXT: call void @fn_write_target1()
+; CHECK-NEXT: call void @fn_readwrite_target0_read_target1()
+; CHECK-NEXT: call void @fn_readwrite_target0_read_target1()
+; CHECK-NEXT: ret void
+;
+ call void @fn_write_target1()
+ call void @fn_readwrite_target0_read_target1()
+ call void @fn_write_target1()
+ call void @fn_readwrite_target0_read_target1()
+ ret void
+}
+
+define void @goo(){
+; CHECK-LABEL: define void @goo() {
+; CHECK-NEXT: call void @fn_read_target0_read_target1()
+; CHECK-NEXT: call void @fn_read_target0()
+; CHECK-NEXT: call void @fn_read_target0_read_target1()
+; CHECK-NEXT: call void @fn_read_target0_read_target1()
+; CHECK-NEXT: ret void
+;
+ call void @fn_read_target0_read_target1()
+ call void @fn_read_target0()
+ call void @fn_read_target0_read_target1()
+ call void @fn_read_target0()
+ call void @fn_read_target0_read_target1()
+ ret void
+}
+
+;; In this case the clobber function is not even called
+define void @neg_foo(){
+; CHECK-LABEL: define void @neg_foo() {
+; CHECK-NEXT: call void @fn_readwrite_target0_read_target1()
+; CHECK-NEXT: call void @fn_read_target0_readwrite_target1()
+; CHECK-NEXT: call void @fn_readwrite_target0_read_target1()
+; CHECK-NEXT: call void @fn_read_target0_readwrite_target1()
+; CHECK-NEXT: ret void
+;
+ call void @fn_readwrite_target0_read_target1()
+ call void @fn_read_target0_readwrite_target1()
+ call void @fn_readwrite_target0_read_target1()
+ call void @fn_read_target0_readwrite_target1()
+ ret void
+}
+
+define void @foo_neg_foo(){
+; CHECK-LABEL: define void @foo_neg_foo() {
+; CHECK-NEXT: call void @fn_write_target1()
+; CHECK-NEXT: call void @fn_readwrite_target0_read_target1()
+; CHECK-NEXT: call void @neg_foo()
+; CHECK-NEXT: call void @fn_write_target1()
+; CHECK-NEXT: call void @fn_readwrite_target0_read_target1()
+; CHECK-NEXT: ret void
+;
+ call void @fn_write_target1()
+ call void @fn_readwrite_target0_read_target1()
+ call void @neg_foo()
+ call void @fn_write_target1()
+ call void @fn_readwrite_target0_read_target1()
+ ret void
+}
+
+declare void @fn_write_target1()
+ memory(target_mem1: write)
+
+declare void @fn_read_target0_read_target1()
+ memory(target_mem0: readwrite, target_mem1: read)
+
+declare void @fn_read_target0()
+ memory( target_mem0: read)
+
+declare void @fn_read_target0_readwrite_target1()
+ memory(target_mem0: read, target_mem1: readwrite)
+
+declare void @fn_readwrite_target0_read_target1()
+ memory(target_mem0: readwrite, target_mem1: read)
>From 78409293f23195baad5d842889c378d4fc63a319 Mon Sep 17 00:00:00 2001
From: CarolineConcatto <caroline.concatto at arm.com>
Date: Thu, 20 Nov 2025 16:42:28 +0000
Subject: [PATCH 2/2] Improve code for writeToSameTargetMemLoc
---
llvm/lib/Analysis/MemorySSA.cpp | 29 +++++++----------------------
1 file changed, 7 insertions(+), 22 deletions(-)
diff --git a/llvm/lib/Analysis/MemorySSA.cpp b/llvm/lib/Analysis/MemorySSA.cpp
index 48e00b7e71eed..8226e8a4c3efe 100644
--- a/llvm/lib/Analysis/MemorySSA.cpp
+++ b/llvm/lib/Analysis/MemorySSA.cpp
@@ -279,26 +279,13 @@ static bool areLoadsReorderable(const LoadInst *Use,
bool writeToSameTargetMemLoc(const CallBase *CallFirst,
const CallBase *CallSecond) {
+
MemoryEffects ME1 = CallFirst->getMemoryEffects();
MemoryEffects ME2 = CallSecond->getMemoryEffects();
-
- auto writes = [](ModRefInfo m) {
- return m != ModRefInfo::NoModRef && m != ModRefInfo::Ref;
- };
-
- for (unsigned ILoc = static_cast<unsigned>(IRMemLocation::FirstTarget);
- ILoc <= static_cast<unsigned>(IRMemLocation::Last); ++ILoc) {
- const auto Loc = static_cast<IRMemLocation>(ILoc);
-
- if (!writes(ME1.getModRef(Loc)))
- continue;
- if (!writes(ME2.getModRef(Loc)))
- continue;
-
- // Both have write capability to the same location.
- return true;
- }
- return false;
+ if (CallFirst->onlyAccessesTargetMemory() ||
+ CallSecond->onlyAccessesTargetMemory())
+ return !(ME1 & ME2 & MemoryEffects::writeOnly()).onlyReadsMemory();
+ return true;
}
template <typename AliasAnalysisType>
@@ -336,10 +323,8 @@ instructionClobbersQuery(const MemoryDef *MD, const MemoryLocation &UseLoc,
if (auto *CB = dyn_cast_or_null<CallBase>(UseInst)) {
if (auto *CU = dyn_cast_or_null<CallBase>(DefInst))
- if (CU->onlyAccessesTargetMemory() || CB->onlyAccessesTargetMemory()) {
- if (!writeToSameTargetMemLoc(CB, CU))
- return false;
- }
+ if (!writeToSameTargetMemLoc(CB, CU))
+ return false;
ModRefInfo I = AA.getModRefInfo(DefInst, CB);
return isModOrRefSet(I);
}
More information about the llvm-commits
mailing list