[AA] Respect potential synchronization effects of inline asm (#196965)
Respect potential synchronization effects of inline assembly calls on
not-yet-escaped memory.
We only do this if the call is both non-nosync and ModRefs "other"
memory. This is consistent with the atomic memory effects established in
https://github.com/llvm/llvm-project/pull/193768 and makes sure that
things like readonly/argmemonly continue to work as expected even for
frontends that do not emit nosync (which, right now, is all of them).
The limitation to inline asm should not actually exist: The issue
applies to all calls. This just fixes a particularly important case in a
targeted way. (The fact that inline asm memory barrier do not work as
expected is a problem for making optimizations of monotonic accesses
more aggressive, e.g. it caused issues for
https://github.com/llvm/llvm-project/pull/195015.)
The ability of inline asm (with a `~{memory}` clobber) to synchronize
was explicitly specified in
https://github.com/llvm/llvm-project/pull/150191.diff --git a/llvm/include/llvm/Analysis/AliasAnalysis.h b/llvm/include/llvm/Analysis/AliasAnalysis.h
index 4997c41..7e9b17b 100644
--- a/llvm/include/llvm/Analysis/AliasAnalysis.h
+++ b/llvm/include/llvm/Analysis/AliasAnalysis.h
@@ -995,6 +995,11 @@
LLVM_ABI bool isWritableObject(const Value *Object,
bool &ExplicitlyDereferenceableOnly);
+/// Get ModRefInfo for a synchronizing operation, such as a fence or stronger
+/// than monotonic atomic load/store.
+LLVM_ABI ModRefInfo getSyncEffects(AAResults *AA, const MemoryLocation &Loc,
+ AAQueryInfo &AAQI);
+
/// A manager for alias analyses.
///
/// This class can have analyses registered with it and when run, it will run
diff --git a/llvm/lib/Analysis/AliasAnalysis.cpp b/llvm/lib/Analysis/AliasAnalysis.cpp
index 8f311cd..9d89a6d 100644
--- a/llvm/lib/Analysis/AliasAnalysis.cpp
+++ b/llvm/lib/Analysis/AliasAnalysis.cpp
@@ -458,10 +458,8 @@
// Helper method implementation
//===----------------------------------------------------------------------===//
-/// Get ModRefInfo for a synchronizing operation, such as a fence or stronger
-/// than monotonic atomic load/store.
-static ModRefInfo getSyncEffects(AAResults *AA, const MemoryLocation &Loc,
- AAQueryInfo &AAQI) {
+ModRefInfo llvm::getSyncEffects(AAResults *AA, const MemoryLocation &Loc,
+ AAQueryInfo &AAQI) {
if (!Loc.Ptr)
return ModRefInfo::ModRef;
diff --git a/llvm/lib/Analysis/BasicAliasAnalysis.cpp b/llvm/lib/Analysis/BasicAliasAnalysis.cpp
index 2d54d98..8b120f0 100644
--- a/llvm/lib/Analysis/BasicAliasAnalysis.cpp
+++ b/llvm/lib/Analysis/BasicAliasAnalysis.cpp
@@ -959,6 +959,20 @@
ModRefInfo ErrnoMR = ME.getModRef(IRMemLocation::ErrnoMem);
ModRefInfo OtherMR = ME.getModRef(IRMemLocation::Other);
+ // Take into account potential synchronization effects of the call.
+ // We assume synchronization can not occur if the call does not read/write
+ // other memory (this in particular ensures that readonly/argmemonly continue
+ // to work as expected for frontends that do not emit nosync).
+ // FIXME: This should apply to all calls, but is limited to inline asm to
+ // limit impact. This ensures that inline asm memory barriers work correctly.
+ ModRefInfo SyncMR = ModRefInfo::NoModRef;
+ if (isModAndRefSet(OtherMR) && Call->maySynchronize() &&
+ Call->isInlineAsm()) {
+ SyncMR = getSyncEffects(&AAQI.AAR, Loc, AAQI);
+ if (isModAndRefSet(SyncMR))
+ return SyncMR;
+ }
+
// An identified function-local object that does not escape can only be
// accessed via call arguments. Reduce OtherMR (which includes accesses to
// escaped memory) based on that.
@@ -1002,7 +1016,7 @@
ArgMR = NewArgMR;
}
- ModRefInfo Result = ArgMR | OtherMR;
+ ModRefInfo Result = ArgMR | OtherMR | SyncMR;
// Refine accesses to errno memory.
if ((ErrnoMR | Result) != Result) {
diff --git a/llvm/test/Analysis/BasicAA/atomics.ll b/llvm/test/Analysis/BasicAA/atomics.ll
index 9a96b94..0aedd7f 100644
--- a/llvm/test/Analysis/BasicAA/atomics.ll
+++ b/llvm/test/Analysis/BasicAA/atomics.ll
@@ -2,6 +2,7 @@
declare void @escape(ptr)
declare noalias ptr @malloc(i64)
+declare void @call()
; CHECK-LABEL: Function: alloca_no_escape:
; CHECK: NoModRef: Ptr: i32* %a <-> %1 = atomicrmw add ptr %x, i32 1 monotonic, align 4
@@ -207,3 +208,37 @@
ret ptr %a
}
+
+; CHECK-LABEL: Function: inline_asm
+; CHECK: Both ModRef: Ptr: i32* %a <-> call void asm sideeffect "", "~{memory}"()
+; CHECK: NoModRef: Ptr: i32* %a <-> call void asm sideeffect "", "~{memory}"() #0
+; CHECK: NoModRef: Ptr: i32* %a <-> call void asm sideeffect "", "~{memory}"() #1
+; CHECK: NoModRef: Ptr: i32* %a <-> call void asm sideeffect "", "~{memory}"() #2
+define ptr @inline_asm() {
+ %a = call ptr @malloc(i64 4)
+ store i32 0, ptr %a
+
+ call void asm sideeffect "", "~{memory}"()
+ call void asm sideeffect "", "~{memory}"() memory(read)
+ call void asm sideeffect "", "~{memory}"() memory(argmem: readwrite)
+ call void asm sideeffect "", "~{memory}"() nosync
+
+ ret ptr %a
+}
+
+; CHECK-LABEL: Function: arbitrary_call
+; CHECK: NoModRef: Ptr: i32* %a <-> call void @call()
+; CHECK: NoModRef: Ptr: i32* %a <-> call void @call() #0
+; CHECK: NoModRef: Ptr: i32* %a <-> call void @call() #1
+; CHECK: NoModRef: Ptr: i32* %a <-> call void @call() #2
+define ptr @arbitrary_call() {
+ %a = call ptr @malloc(i64 4)
+ store i32 0, ptr %a
+
+ call void @call()
+ call void @call() memory(read)
+ call void @call() memory(argmem: readwrite)
+ call void @call() nosync
+
+ ret ptr %a
+}