[llvm] r277637 - [MSSA] Add logic for special handling of atomics/volatiles.

George Burgess IV via llvm-commits llvm-commits at lists.llvm.org
Wed Aug 3 12:39:54 PDT 2016


Author: gbiv
Date: Wed Aug  3 14:39:54 2016
New Revision: 277637

URL: http://llvm.org/viewvc/llvm-project?rev=277637&view=rev
Log:
[MSSA] Add logic for special handling of atomics/volatiles.

This patch makes MemorySSA recognize atomic/volatile loads, and makes
MSSA treat said loads specially. This allows us to be a bit more
aggressive in some cases.

Administrative note: Revision was LGTM'ed by reames in person.
Additionally, this doesn't include the `invariant.load` recognition in
the differential revision, because I feel it's better to commit that
separately. Will commit soon.

Differential Revision: https://reviews.llvm.org/D16875

Added:
    llvm/trunk/test/Transforms/Util/MemorySSA/constant-memory.ll
    llvm/trunk/test/Transforms/Util/MemorySSA/invariant-groups.ll
Modified:
    llvm/trunk/lib/Transforms/Utils/MemorySSA.cpp
    llvm/trunk/test/Transforms/Util/MemorySSA/atomic-clobber.ll
    llvm/trunk/test/Transforms/Util/MemorySSA/volatile-clobber.ll

Modified: llvm/trunk/lib/Transforms/Utils/MemorySSA.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Transforms/Utils/MemorySSA.cpp?rev=277637&r1=277636&r2=277637&view=diff
==============================================================================
--- llvm/trunk/lib/Transforms/Utils/MemorySSA.cpp (original)
+++ llvm/trunk/lib/Transforms/Utils/MemorySSA.cpp Wed Aug  3 14:39:54 2016
@@ -207,6 +207,50 @@ static bool lifetimeEndsAt(MemoryDef *MD
   return false;
 }
 
+enum class Reorderability {
+  Always,
+  IfNoAlias,
+  Never
+};
+
+/// This does one-way checks to see if Use could theoretically be hoisted above
+/// MayClobber. This will not check the other way around.
+///
+/// This assumes that, for the purposes of MemorySSA, Use comes directly after
+/// MayClobber, with no potentially clobbering operations in between them.
+/// (Where potentially clobbering ops are memory barriers, aliased stores, etc.)
+static Reorderability getLoadReorderability(const LoadInst *Use,
+                                            const LoadInst *MayClobber) {
+  bool VolatileUse = Use->isVolatile();
+  bool VolatileClobber = MayClobber->isVolatile();
+  // Volatile operations may never be reordered with other volatile operations.
+  if (VolatileUse && VolatileClobber)
+    return Reorderability::Never;
+
+  // The lang ref allows reordering of volatile and non-volatile operations.
+  // Whether an aliasing nonvolatile load and volatile load can be reordered,
+  // though, is ambiguous. Because it may not be best to exploit this ambiguity,
+  // we only allow volatile/non-volatile reordering if the volatile and
+  // non-volatile operations don't alias.
+  Reorderability Result = VolatileUse || VolatileClobber
+                              ? Reorderability::IfNoAlias
+                              : Reorderability::Always;
+
+  // If a load is seq_cst, it cannot be moved above other loads. If its ordering
+  // is weaker, it can be moved above other loads. We just need to be sure that
+  // MayClobber isn't an acquire load, because loads can't be moved above
+  // acquire loads.
+  //
+  // Note that this explicitly *does* allow the free reordering of monotonic (or
+  // weaker) loads of the same address.
+  bool SeqCstUse = Use->getOrdering() == AtomicOrdering::SequentiallyConsistent;
+  bool MayClobberIsAcquire = isAtLeastOrStrongerThan(MayClobber->getOrdering(),
+                                                     AtomicOrdering::Acquire);
+  if (SeqCstUse || MayClobberIsAcquire)
+    return Reorderability::Never;
+  return Result;
+}
+
 static bool instructionClobbersQuery(MemoryDef *MD,
                                      const MemoryLocation &UseLoc,
                                      const Instruction *UseInst,
@@ -234,6 +278,20 @@ static bool instructionClobbersQuery(Mem
     ModRefInfo I = AA.getModRefInfo(DefInst, UseCS);
     return I != MRI_NoModRef;
   }
+
+  if (auto *DefLoad = dyn_cast<LoadInst>(DefInst)) {
+    if (auto *UseLoad = dyn_cast<LoadInst>(UseInst)) {
+      switch (getLoadReorderability(UseLoad, DefLoad)) {
+      case Reorderability::Always:
+        return false;
+      case Reorderability::Never:
+        return true;
+      case Reorderability::IfNoAlias:
+        return !AA.isNoAlias(UseLoc, MemoryLocation::get(DefLoad));
+      }
+    }
+  }
+
   return AA.getModRefInfo(DefInst, UseLoc) & MRI_Mod;
 }
 

Modified: llvm/trunk/test/Transforms/Util/MemorySSA/atomic-clobber.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/Transforms/Util/MemorySSA/atomic-clobber.ll?rev=277637&r1=277636&r2=277637&view=diff
==============================================================================
--- llvm/trunk/test/Transforms/Util/MemorySSA/atomic-clobber.ll (original)
+++ llvm/trunk/test/Transforms/Util/MemorySSA/atomic-clobber.ll Wed Aug  3 14:39:54 2016
@@ -3,6 +3,7 @@
 ;
 ; Ensures that atomic loads count as MemoryDefs
 
+; CHECK-LABEL: define i32 @foo
 define i32 @foo(i32* %a, i32* %b) {
 ; CHECK: 1 = MemoryDef(liveOnEntry)
 ; CHECK-NEXT: store i32 4
@@ -16,3 +17,103 @@ define i32 @foo(i32* %a, i32* %b) {
   %3 = add i32 %1, %2
   ret i32 %3
 }
+
+; CHECK-LABEL: define void @bar
+define void @bar(i32* %a) {
+; CHECK: MemoryUse(liveOnEntry)
+; CHECK-NEXT: load atomic i32, i32* %a unordered, align 4
+  load atomic i32, i32* %a unordered, align 4
+; CHECK: 1 = MemoryDef(liveOnEntry)
+; CHECK-NEXT: load atomic i32, i32* %a monotonic, align 4
+  load atomic i32, i32* %a monotonic, align 4
+; CHECK: 2 = MemoryDef(1)
+; CHECK-NEXT: load atomic i32, i32* %a acquire, align 4
+  load atomic i32, i32* %a acquire, align 4
+; CHECK: 3 = MemoryDef(2)
+; CHECK-NEXT: load atomic i32, i32* %a seq_cst, align 4
+  load atomic i32, i32* %a seq_cst, align 4
+  ret void
+}
+
+; CHECK-LABEL: define void @baz
+define void @baz(i32* %a) {
+; CHECK: 1 = MemoryDef(liveOnEntry)
+; CHECK-NEXT: %1 = load atomic i32
+  %1 = load atomic i32, i32* %a acquire, align 4
+; CHECK: MemoryUse(1)
+; CHECK-NEXT: %2 = load atomic i32, i32* %a unordered, align 4
+  %2 = load atomic i32, i32* %a unordered, align 4
+; CHECK: 2 = MemoryDef(1)
+; CHECK-NEXT: %3 = load atomic i32, i32* %a monotonic, align 4
+  %3 = load atomic i32, i32* %a monotonic, align 4
+  ret void
+}
+
+; CHECK-LABEL: define void @fences
+define void @fences(i32* %a) {
+; CHECK: 1 = MemoryDef(liveOnEntry)
+; CHECK-NEXT: fence acquire
+  fence acquire
+; CHECK: MemoryUse(1)
+; CHECK-NEXT: %1 = load i32, i32* %a
+  %1 = load i32, i32* %a
+
+; CHECK: 2 = MemoryDef(1)
+; CHECK-NEXT: fence release
+  fence release
+; CHECK: MemoryUse(2)
+; CHECK-NEXT: %2 = load i32, i32* %a
+  %2 = load i32, i32* %a
+
+; CHECK: 3 = MemoryDef(2)
+; CHECK-NEXT: fence acq_rel
+  fence acq_rel
+; CHECK: MemoryUse(3)
+; CHECK-NEXT: %3 = load i32, i32* %a
+  %3 = load i32, i32* %a
+
+; CHECK: 4 = MemoryDef(3)
+; CHECK-NEXT: fence seq_cst
+  fence seq_cst
+; CHECK: MemoryUse(4)
+; CHECK-NEXT: %4 = load i32, i32* %a
+  %4 = load i32, i32* %a
+  ret void
+}
+
+; CHECK-LABEL: define void @seq_cst_clobber
+define void @seq_cst_clobber(i32* noalias %a, i32* noalias %b) {
+; CHECK: 1 = MemoryDef(liveOnEntry)
+; CHECK-NEXT: %1 = load atomic i32, i32* %a monotonic, align 4
+  load atomic i32, i32* %a monotonic, align 4
+
+; CHECK: 2 = MemoryDef(1)
+; CHECK-NEXT: %2 = load atomic i32, i32* %a seq_cst, align 4
+  load atomic i32, i32* %a seq_cst, align 4
+
+; CHECK: 3 = MemoryDef(2)
+; CHECK-NEXT: load atomic i32, i32* %a monotonic, align 4
+  load atomic i32, i32* %a monotonic, align 4
+
+  ret void
+}
+
+; Ensure that AA hands us MRI_Mod on unreorderable atomic ops.
+;
+; This test is a bit implementation-specific. In particular, it depends on that
+; we pass cmpxchg-load queries to AA, without trying to reason about them on
+; our own.
+;
+; If AA gets more aggressive, we can find another way.
+;
+; CHECK-LABEL: define void @check_aa_is_sane
+define void @check_aa_is_sane(i32* noalias %a, i32* noalias %b) {
+; CHECK: 1 = MemoryDef(liveOnEntry)
+; CHECK-NEXT: cmpxchg i32* %a, i32 0, i32 1 acquire acquire
+  cmpxchg i32* %a, i32 0, i32 1 acquire acquire
+; CHECK: MemoryUse(1)
+; CHECK-NEXT: load i32, i32* %b, align 4
+  load i32, i32* %b, align 4
+
+  ret void
+}

Added: llvm/trunk/test/Transforms/Util/MemorySSA/constant-memory.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/Transforms/Util/MemorySSA/constant-memory.ll?rev=277637&view=auto
==============================================================================
--- llvm/trunk/test/Transforms/Util/MemorySSA/constant-memory.ll (added)
+++ llvm/trunk/test/Transforms/Util/MemorySSA/constant-memory.ll Wed Aug  3 14:39:54 2016
@@ -0,0 +1,41 @@
+; RUN: opt -basicaa -print-memoryssa -verify-memoryssa -analyze < %s 2>&1 | FileCheck %s
+;
+; Things that BasicAA can prove points to constant memory should be
+; liveOnEntry, as well.
+
+declare void @clobberAllTheThings()
+
+ at str = private unnamed_addr constant [2 x i8] c"hi"
+
+define i8 @foo() {
+; CHECK: 1 = MemoryDef(liveOnEntry)
+; CHECK-NEXT: call void @clobberAllTheThings()
+  call void @clobberAllTheThings()
+  %1 = getelementptr [2 x i8], [2 x i8]* @str, i64 0, i64 0
+; CHECK: MemoryUse(liveOnEntry)
+; CHECK-NEXT: %2 = load i8
+  %2 = load i8, i8* %1, align 1
+  %3 = getelementptr [2 x i8], [2 x i8]* @str, i64 0, i64 1
+; CHECK: MemoryUse(liveOnEntry)
+; CHECK-NEXT: %4 = load i8
+  %4 = load i8, i8* %3, align 1
+  %5 = add i8 %2, %4
+  ret i8 %5
+}
+
+define i8 @select(i1 %b) {
+  %1 = alloca i8, align 1
+; CHECK: 1 = MemoryDef(liveOnEntry)
+; CHECK-NEXT: store i8 0
+  store i8 0, i8* %1, align 1
+
+; CHECK: 2 = MemoryDef(1)
+; CHECK-NEXT: call void @clobberAllTheThings()
+  call void @clobberAllTheThings()
+  %2 = getelementptr [2 x i8], [2 x i8]* @str, i64 0, i64 0
+  %3 = select i1 %b, i8* %2, i8* %1
+; CHECK: MemoryUse(2)
+; CHECK-NEXT: %4 = load i8
+  %4 = load i8, i8* %3, align 1
+  ret i8 %4
+}

Added: llvm/trunk/test/Transforms/Util/MemorySSA/invariant-groups.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/Transforms/Util/MemorySSA/invariant-groups.ll?rev=277637&view=auto
==============================================================================
--- llvm/trunk/test/Transforms/Util/MemorySSA/invariant-groups.ll (added)
+++ llvm/trunk/test/Transforms/Util/MemorySSA/invariant-groups.ll Wed Aug  3 14:39:54 2016
@@ -0,0 +1,30 @@
+; RUN: opt -basicaa -print-memoryssa -verify-memoryssa -analyze < %s 2>&1 | FileCheck %s
+;
+; Currently, MemorySSA doesn't support invariant groups. So, we should ignore
+; invariant.group.barrier intrinsics entirely. We'll need to pay attention to
+; them when/if we decide to support invariant groups.
+
+ at g = external global i32
+
+define i32 @foo(i32* %a) {
+; CHECK: 1 = MemoryDef(liveOnEntry)
+; CHECK-NEXT: store i32 0
+  store i32 0, i32* %a, align 4, !llvm.invariant.group !0
+
+; CHECK: 2 = MemoryDef(1)
+; CHECK-NEXT: store i32 1
+  store i32 1, i32* @g, align 4
+
+  %1 = bitcast i32* %a to i8*
+  %a8 = call i8* @llvm.invariant.group.barrier(i8* %1)
+  %a32 = bitcast i8* %a8 to i32*
+
+; CHECK: MemoryUse(2)
+; CHECK-NEXT: %2 = load i32
+  %2 = load i32, i32* %a32, align 4, !llvm.invariant.group !0
+  ret i32 %2
+}
+
+declare i8* @llvm.invariant.group.barrier(i8*)
+
+!0 = !{!"group1"}

Modified: llvm/trunk/test/Transforms/Util/MemorySSA/volatile-clobber.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/Transforms/Util/MemorySSA/volatile-clobber.ll?rev=277637&r1=277636&r2=277637&view=diff
==============================================================================
--- llvm/trunk/test/Transforms/Util/MemorySSA/volatile-clobber.ll (original)
+++ llvm/trunk/test/Transforms/Util/MemorySSA/volatile-clobber.ll Wed Aug  3 14:39:54 2016
@@ -3,6 +3,7 @@
 ;
 ; Ensures that volatile stores/loads count as MemoryDefs
 
+; CHECK-LABEL: define i32 @foo
 define i32 @foo() {
   %1 = alloca i32, align 4
 ; CHECK: 1 = MemoryDef(liveOnEntry)
@@ -20,3 +21,74 @@ define i32 @foo() {
   %4 = add i32 %3, %2
   ret i32 %4
 }
+
+; Ensuring that we don't automatically hoist nonvolatile loads around volatile
+; loads
+; CHECK-LABEL define void @volatile_only
+define void @volatile_only(i32* %arg1, i32* %arg2) {
+  ; Trivially NoAlias/MustAlias
+  %a = alloca i32
+  %b = alloca i32
+
+; CHECK: 1 = MemoryDef(liveOnEntry)
+; CHECK-NEXT: load volatile i32, i32* %a
+  load volatile i32, i32* %a
+; CHECK: MemoryUse(liveOnEntry)
+; CHECK-NEXT: load i32, i32* %b
+  load i32, i32* %b
+; CHECK: MemoryUse(1)
+; CHECK-NEXT: load i32, i32* %a
+  load i32, i32* %a
+
+  ; MayAlias
+; CHECK: 2 = MemoryDef(1)
+; CHECK-NEXT: load volatile i32, i32* %arg1
+  load volatile i32, i32* %arg1
+; CHECK: MemoryUse(2)
+; CHECK-NEXT: load i32, i32* %arg2
+  load i32, i32* %arg2
+
+  ret void
+}
+
+; Ensuring that volatile atomic operations work properly.
+; CHECK-LABEL define void @volatile_atomics
+define void @volatile_atomics(i32* %arg1, i32* %arg2) {
+  %a = alloca i32
+  %b = alloca i32
+
+ ; Trivially NoAlias/MustAlias
+
+; CHECK: 1 = MemoryDef(liveOnEntry)
+; CHECK-NEXT: load atomic volatile i32, i32* %a acquire, align 4
+  load atomic volatile i32, i32* %a acquire, align 4
+; CHECK: MemoryUse(1)
+; CHECK-NEXT: load i32, i32* %b
+  load i32, i32* %b
+
+; CHECK: 2 = MemoryDef(1)
+; CHECK-NEXT: load atomic volatile i32, i32* %a monotonic, align 4
+  load atomic volatile i32, i32* %a monotonic, align 4
+; CHECK: MemoryUse(1)
+; CHECK-NEXT: load i32, i32* %b
+  load i32, i32* %b
+; CHECK: MemoryUse(1)
+; CHECK-NEXT: load atomic i32, i32* %b unordered, align 4
+  load atomic i32, i32* %b unordered, align 4
+; CHECK: MemoryUse(2)
+; CHECK-NEXT: load atomic i32, i32* %a unordered, align 4
+  load atomic i32, i32* %a unordered, align 4
+; CHECK: MemoryUse(2)
+; CHECK-NEXT: load i32, i32* %a
+  load i32, i32* %a
+
+  ; MayAlias
+; CHECK: 3 = MemoryDef(2)
+; CHECK-NEXT: load atomic volatile i32, i32* %arg1 monotonic, align 4
+  load atomic volatile i32, i32* %arg1 monotonic, align 4
+; CHECK: MemoryUse(3)
+; CHECK-NEXT: load i32, i32* %arg2
+  load i32, i32* %arg2
+
+  ret void
+}




More information about the llvm-commits mailing list