[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
+}