[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