892 changes: 892 additions & 0 deletions llvm/include/llvm/Transforms/Utils/MemorySSA.h

Large diffs are not rendered by default.

11 changes: 11 additions & 0 deletions llvm/lib/IR/AsmWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3215,6 +3215,17 @@ void AssemblyWriter::printUseLists(const Function *F) {
// External Interface declarations
//===----------------------------------------------------------------------===//

void Function::print(raw_ostream &ROS, AssemblyAnnotationWriter *AAW,
bool ShouldPreserveUseListOrder,
bool IsForDebug) const {
SlotTracker SlotTable(this->getParent());
formatted_raw_ostream OS(ROS);
AssemblyWriter W(OS, SlotTable, this->getParent(), AAW,
IsForDebug,
ShouldPreserveUseListOrder);
W.printFunction(this);
}

void Module::print(raw_ostream &ROS, AssemblyAnnotationWriter *AAW,
bool ShouldPreserveUseListOrder, bool IsForDebug) const {
SlotTracker SlotTable(this);
Expand Down
1 change: 1 addition & 0 deletions llvm/lib/Transforms/Utils/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ add_llvm_library(LLVMTransformUtils
LowerInvoke.cpp
LowerSwitch.cpp
Mem2Reg.cpp
MemorySSA.cpp
MetaRenamer.cpp
ModuleUtils.cpp
PromoteMemoryToRegister.cpp
Expand Down
939 changes: 939 additions & 0 deletions llvm/lib/Transforms/Utils/MemorySSA.cpp

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions llvm/lib/Transforms/Utils/Utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ void llvm::initializeTransformUtils(PassRegistry &Registry) {
initializeUnifyFunctionExitNodesPass(Registry);
initializeInstSimplifierPass(Registry);
initializeMetaRenamerPass(Registry);
initializeMemorySSALazyPass(Registry);
initializeMemorySSAPrinterPassPass(Registry);
}

/// LLVMInitializeTransformUtils - C binding for initializeTransformUtilsPasses.
Expand Down
17 changes: 17 additions & 0 deletions llvm/test/Transforms/Util/MemorySSA/atomic-clobber.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
; RUN: opt -basicaa -print-memoryssa -verify-memoryssa -analyze < %s 2>&1 | FileCheck %s
;
; Ensures that atomic loads count as MemoryDefs

define i32 @foo(i32* %a, i32* %b) {
; CHECK: 1 = MemoryDef(liveOnEntry)
; CHECK-NEXT: store i32 4
store i32 4, i32* %a, align 4
; CHECK: 2 = MemoryDef(1)
; CHECK-NEXT: %1 = load atomic i32
%1 = load atomic i32, i32* %b acquire, align 4
; CHECK: MemoryUse(2)
; CHECK-NEXT: %2 = load i32
%2 = load i32, i32* %a, align 4
%3 = add i32 %1, %2
ret i32 %3
}
33 changes: 33 additions & 0 deletions llvm/test/Transforms/Util/MemorySSA/cyclicphi.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
; RUN: opt -basicaa -print-memoryssa -verify-memoryssa -analyze < %s 2>&1 | FileCheck %s

%struct.hoge = type { i32, %struct.widget }
%struct.widget = type { i64 }

define hidden void @quux(%struct.hoge *%f) align 2 {
%tmp = getelementptr inbounds %struct.hoge, %struct.hoge* %f, i64 0, i32 1, i32 0
%tmp24 = getelementptr inbounds %struct.hoge, %struct.hoge* %f, i64 0, i32 1
%tmp25 = bitcast %struct.widget* %tmp24 to i64**
br label %bb26

bb26: ; preds = %bb77, %0
; CHECK: 3 = MemoryPhi({%0,liveOnEntry},{bb77,2})
; CHECK-NEXT: br i1 undef, label %bb68, label %bb77
br i1 undef, label %bb68, label %bb77

bb68: ; preds = %bb26
; CHECK: MemoryUse(liveOnEntry)
; CHECK-NEXT: %tmp69 = load i64, i64* null, align 8
%tmp69 = load i64, i64* null, align 8
; CHECK: 1 = MemoryDef(3)
; CHECK-NEXT: store i64 %tmp69, i64* %tmp, align 8
store i64 %tmp69, i64* %tmp, align 8
br label %bb77

bb77: ; preds = %bb68, %bb26
; CHECK: 2 = MemoryPhi({bb26,3},{bb68,1})
; CHECK: MemoryUse(2)
; CHECK-NEXT: %tmp78 = load i64*, i64** %tmp25, align 8
%tmp78 = load i64*, i64** %tmp25, align 8
%tmp79 = getelementptr inbounds i64, i64* %tmp78, i64 undef
br label %bb26
}
26 changes: 26 additions & 0 deletions llvm/test/Transforms/Util/MemorySSA/function-clobber.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
; RUN: opt -basicaa -print-memoryssa -verify-memoryssa -analyze < %s 2>&1 | FileCheck %s
;
; Ensuring that external functions without attributes are MemoryDefs

@g = external global i32
declare void @modifyG()

define i32 @foo() {
; CHECK: MemoryUse(liveOnEntry)
; CHECK-NEXT: %1 = load i32
%1 = load i32, i32* @g

; CHECK: 1 = MemoryDef(liveOnEntry)
; CHECK-NEXT: store i32 4
store i32 4, i32* @g, align 4

; CHECK: 2 = MemoryDef(1)
; CHECK-NEXT: call void @modifyG()
call void @modifyG()

; CHECK: MemoryUse(2)
; CHECK-NEXT: %2 = load i32
%2 = load i32, i32* @g
%3 = add i32 %2, %1
ret i32 %3
}
58 changes: 58 additions & 0 deletions llvm/test/Transforms/Util/MemorySSA/function-mem-attrs.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
; RUN: opt -basicaa -print-memoryssa -verify-memoryssa -analyze < %s 2>&1 | FileCheck %s
;
; Test that various function attributes give us sane results.

@g = external global i32

declare void @readonlyFunction() readonly
declare void @noattrsFunction()

define void @readonlyAttr() {
; CHECK: 1 = MemoryDef(liveOnEntry)
; CHECK-NEXT: store i32 0
store i32 0, i32* @g, align 4

%1 = alloca i32, align 4
; CHECK: 2 = MemoryDef(1)
; CHECK-NEXT: store i32 0
store i32 0, i32* %1, align 4

; CHECK: MemoryUse(1)
; CHECK-NEXT: call void @readonlyFunction()
call void @readonlyFunction()

; CHECK: MemoryUse(1)
; CHECK-NEXT: call void @noattrsFunction() #
; Assume that #N is readonly
call void @noattrsFunction() readonly

; Sanity check that noattrsFunction is otherwise a MemoryDef
; CHECK: 3 = MemoryDef(2)
; CHECK-NEXT: call void @noattrsFunction()
call void @noattrsFunction()
ret void
}

declare void @argMemOnly(i32*) argmemonly

define void @inaccessableOnlyAttr() {
%1 = alloca i32, align 4
; CHECK: 1 = MemoryDef(liveOnEntry)
; CHECK-NEXT: store i32 0
store i32 0, i32* %1, align 4

; CHECK: 2 = MemoryDef(1)
; CHECK-NEXT: store i32 0
store i32 0, i32* @g, align 4

; CHECK: MemoryUse(1)
; CHECK-NEXT: call void @argMemOnly(i32* %1) #
; Assume that #N is readonly
call void @argMemOnly(i32* %1) readonly

; CHECK: 3 = MemoryDef(2)
; CHECK-NEXT: call void @argMemOnly(i32* %1)
call void @argMemOnly(i32* %1)

ret void
}
24 changes: 24 additions & 0 deletions llvm/test/Transforms/Util/MemorySSA/load-invariant.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
; XFAIL:
; RUN: opt -basicaa -print-memoryssa -verify-memoryssa -analyze < %s 2>&1 | FileCheck %s
;
; Invariant loads should be considered live on entry, because, once the
; location is known to be dereferenceable, the value can never change.
;
; Currently XFAILed because this optimization was held back from the initial
; commit.

@g = external global i32

declare void @clobberAllTheThings()

define i32 @foo() {
; CHECK: 1 = MemoryDef(liveOnEntry)
; CHECK-NEXT: call void @clobberAllTheThings()
call void @clobberAllTheThings()
; CHECK: MemoryUse(liveOnEntry)
; CHECK-NEXT: %1 = load i32
%1 = load i32, i32* @g, align 4, !invariant.load !0
ret i32 %1
}

!0 = !{}
76 changes: 76 additions & 0 deletions llvm/test/Transforms/Util/MemorySSA/many-dom-backedge.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
; RUN: opt -basicaa -print-memoryssa -verify-memoryssa -analyze < %s 2>&1 | FileCheck %s
;
; many-dom.ll, with an added back-edge back into the switch.
; Because people love their gotos.

declare i1 @getBool() readnone

define i32 @foo(i32* %p) {
entry:
br label %loopbegin

loopbegin:
; CHECK: 9 = MemoryPhi({entry,liveOnEntry},{sw.epilog,6})
; CHECK-NEXT: %n =
%n = phi i32 [ 0, %entry ], [ %1, %sw.epilog ]
%m = alloca i32, align 4
switch i32 %n, label %sw.default [
i32 0, label %sw.bb
i32 1, label %sw.bb1
i32 2, label %sw.bb2
i32 3, label %sw.bb3
]

sw.bb:
; CHECK: 1 = MemoryDef(9)
; CHECK-NEXT: store i32 1
store i32 1, i32* %m, align 4
br label %sw.epilog

sw.bb1:
; CHECK: 2 = MemoryDef(9)
; CHECK-NEXT: store i32 2
store i32 2, i32* %m, align 4
br label %sw.epilog

sw.bb2:
; CHECK: 3 = MemoryDef(9)
; CHECK-NEXT: store i32 3
store i32 3, i32* %m, align 4
br label %sw.epilog

sw.bb3:
; CHECK: 10 = MemoryPhi({loopbegin,9},{sw.almostexit,6})
; CHECK: 4 = MemoryDef(10)
; CHECK-NEXT: store i32 4
store i32 4, i32* %m, align 4
br label %sw.epilog

sw.default:
; CHECK: 5 = MemoryDef(9)
; CHECK-NEXT: store i32 5
store i32 5, i32* %m, align 4
br label %sw.epilog

sw.epilog:
; CHECK: 8 = MemoryPhi({sw.default,5},{sw.bb3,4},{sw.bb,1},{sw.bb1,2},{sw.bb2,3})
; CHECK-NEXT: MemoryUse(8)
; CHECK-NEXT: %0 =
%0 = load i32, i32* %m, align 4
; CHECK: 6 = MemoryDef(8)
; CHECK-NEXT: %1 =
%1 = load volatile i32, i32* %p, align 4
%2 = icmp eq i32 %0, %1
br i1 %2, label %sw.almostexit, label %loopbegin

sw.almostexit:
%3 = icmp eq i32 0, %1
br i1 %3, label %exit, label %sw.bb3

exit:
; CHECK: 7 = MemoryDef(6)
; CHECK-NEXT: %4 = load volatile i32
%4 = load volatile i32, i32* %p, align 4
%5 = add i32 %4, %1
ret i32 %5
}
66 changes: 66 additions & 0 deletions llvm/test/Transforms/Util/MemorySSA/many-doms.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
; RUN: opt -basicaa -print-memoryssa -verify-memoryssa -analyze < %s 2>&1 | FileCheck %s
;
; Testing many dominators, specifically from a switch statement in C.

declare i1 @getBool() readnone

define i32 @foo(i32* %p) {
entry:
br label %loopbegin

loopbegin:
; CHECK: 8 = MemoryPhi({entry,liveOnEntry},{sw.epilog,6})
; CHECK-NEXT: %n =
%n = phi i32 [ 0, %entry ], [ %1, %sw.epilog ]
%m = alloca i32, align 4
switch i32 %n, label %sw.default [
i32 0, label %sw.bb
i32 1, label %sw.bb1
i32 2, label %sw.bb2
i32 3, label %sw.bb3
]

sw.bb:
; CHECK: 1 = MemoryDef(8)
; CHECK-NEXT: store i32 1
store i32 1, i32* %m, align 4
br label %sw.epilog

sw.bb1:
; CHECK: 2 = MemoryDef(8)
; CHECK-NEXT: store i32 2
store i32 2, i32* %m, align 4
br label %sw.epilog

sw.bb2:
; CHECK: 3 = MemoryDef(8)
; CHECK-NEXT: store i32 3
store i32 3, i32* %m, align 4
br label %sw.epilog

sw.bb3:
; CHECK: 4 = MemoryDef(8)
; CHECK-NEXT: store i32 4
store i32 4, i32* %m, align 4
br label %sw.epilog

sw.default:
; CHECK: 5 = MemoryDef(8)
; CHECK-NEXT: store i32 5
store i32 5, i32* %m, align 4
br label %sw.epilog

sw.epilog:
; CHECK: 7 = MemoryPhi({sw.default,5},{sw.bb,1},{sw.bb1,2},{sw.bb2,3},{sw.bb3,4})
; CHECK-NEXT: MemoryUse(7)
; CHECK-NEXT: %0 =
%0 = load i32, i32* %m, align 4
; CHECK: 6 = MemoryDef(7)
; CHECK-NEXT: %1 =
%1 = load volatile i32, i32* %p, align 4
%2 = icmp eq i32 %0, %1
br i1 %2, label %exit, label %loopbegin

exit:
ret i32 %1
}
31 changes: 31 additions & 0 deletions llvm/test/Transforms/Util/MemorySSA/multi-edges.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
; RUN: opt -basicaa -print-memoryssa -verify-memoryssa -analyze < %s 2>&1 | FileCheck %s
;
; Makes sure we have a sane model if both successors of some block is the same
; block.

define i32 @foo(i1 %a) {
entry:
%0 = alloca i32, align 4
; CHECK: 1 = MemoryDef(liveOnEntry)
; CHECK-NEXT: store i32 4
store i32 4, i32* %0
br i1 %a, label %Loop.Body, label %Loop.End

Loop.Body:
; CHECK: 4 = MemoryPhi({entry,1},{Loop.End,3})
; CHECK-NEXT: 2 = MemoryDef(4)
; CHECK-NEXT: store i32 5
store i32 5, i32* %0, align 4
br i1 %a, label %Loop.End, label %Loop.End ; WhyDoWeEvenHaveThatLever.gif

Loop.End:
; CHECK: 3 = MemoryPhi({entry,1},{Loop.Body,2},{Loop.Body,2})
; CHECK-NEXT: MemoryUse(3)
; CHECK-NEXT: %1 = load
%1 = load i32, i32* %0, align 4
%2 = icmp eq i32 5, %1
br i1 %2, label %Ret, label %Loop.Body

Ret:
ret i32 %1
}
72 changes: 72 additions & 0 deletions llvm/test/Transforms/Util/MemorySSA/multiple-backedges-hal.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
; RUN: opt -basicaa -print-memoryssa -verify-memoryssa -analyze < %s 2>&1 | FileCheck %s

; hfinkel's case
; [entry]
; |
; .....
; (clobbering access - b)
; |
; .... ________________________________
; \ / |
; (x) |
; ...... |
; | |
; | ______________________ |
; \ / | |
; (starting access) | |
; ... | |
; (clobbering access - a) | |
; ... | |
; | | | |
; | |_______________________| |
; | |
; |_________________________________|
;
; More specifically, one access, with multiple clobbering accesses. One of
; which strictly dominates the access, the other of which has a backedge

; readnone so we don't have a 1:1 mapping of MemorySSA edges to Instructions.
declare void @doThingWithoutReading() readnone
declare i8 @getValue() readnone
declare i1 @getBool() readnone

define hidden void @testcase(i8* %Arg) {
Entry:
call void @doThingWithoutReading()
%Val.Entry = call i8 @getValue()
; CHECK: 1 = MemoryDef(liveOnEntry)
; CHECK-NEXT: store i8 %Val.Entry
store i8 %Val.Entry, i8* %Arg
call void @doThingWithoutReading()
br label %OuterLoop

OuterLoop:
; CHECK: 5 = MemoryPhi({Entry,1},{InnerLoop.Tail,3})
; CHECK-NEXT: %Val.Outer =
%Val.Outer = call i8 @getValue()
; CHECK: 2 = MemoryDef(5)
; CHECK-NEXT: store i8 %Val.Outer
store i8 %Val.Outer, i8* %Arg
call void @doThingWithoutReading()
br label %InnerLoop

InnerLoop:
; CHECK: 4 = MemoryPhi({OuterLoop,2},{InnerLoop,3})
; CHECK-NEXT: ; MemoryUse(4)
; CHECK-NEXT: %StartingAccess = load
%StartingAccess = load i8, i8* %Arg, align 4
%Val.Inner = call i8 @getValue()
; CHECK: 3 = MemoryDef(4)
; CHECK-NEXT: store i8 %Val.Inner
store i8 %Val.Inner, i8* %Arg
call void @doThingWithoutReading()
%KeepGoing = call i1 @getBool()
br i1 %KeepGoing, label %InnerLoop.Tail, label %InnerLoop

InnerLoop.Tail:
%KeepGoing.Tail = call i1 @getBool()
br i1 %KeepGoing.Tail, label %End, label %OuterLoop

End:
ret void
}
42 changes: 42 additions & 0 deletions llvm/test/Transforms/Util/MemorySSA/no-disconnected.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
; RUN: opt -basicaa -print-memoryssa -analyze -verify-memoryssa < %s 2>&1 | FileCheck %s
;
; This test ensures we don't end up with multiple reaching defs for a single
; use/phi edge If we were to optimize defs, we would end up with 2=
; MemoryDef(liveOnEntry) and 4 = MemoryDef(liveOnEntry) Both would mean both
; 1,2, and 3,4 would reach the phi node. Because the phi node can only have one
; entry on each edge, it would choose 2, 4 and disconnect 1 and 3 completely
; from the SSA graph, even though they are not dead

define void @sink_store(i32 %index, i32* %foo, i32* %bar) {
entry:
%cmp = trunc i32 %index to i1
br i1 %cmp, label %if.then, label %if.else

if.then: ; preds = %entry
; CHECK: 1 = MemoryDef(liveOnEntry)
; CHECK-NEXT: store i32 %index, i32* %foo, align 4
store i32 %index, i32* %foo, align 4
; CHECK: 2 = MemoryDef(1)
; CHECK-NEXT: store i32 %index, i32* %bar, align 4
store i32 %index, i32* %bar, align 4
br label %if.end

if.else: ; preds = %entry
; CHECK: 3 = MemoryDef(liveOnEntry)
; CHECK-NEXT: store i32 %index, i32* %foo, align 4
store i32 %index, i32* %foo, align 4
; CHECK: 4 = MemoryDef(3)
; CHECK-NEXT: store i32 %index, i32* %bar, align 4
store i32 %index, i32* %bar, align 4
br label %if.end

if.end: ; preds = %if.else, %if.then
; CHECK: 5 = MemoryPhi({if.then,2},{if.else,4})
; CHECK: MemoryUse(5)
; CHECK-NEXT: %c = load i32, i32* %foo
%c = load i32, i32* %foo
; CHECK: MemoryUse(5)
; CHECK-NEXT: %d = load i32, i32* %bar
%d = load i32, i32* %bar
ret void
}
36 changes: 36 additions & 0 deletions llvm/test/Transforms/Util/MemorySSA/optimize-use.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
; RUN: opt -basicaa -print-memoryssa -analyze -verify-memoryssa < %s 2>&1 | FileCheck %s

; Function Attrs: ssp uwtable
define i32 @main() {
entry:
; CHECK: 1 = MemoryDef(liveOnEntry)
; CHECK-NEXT: %call = call noalias i8* @_Znwm(i64 4)
%call = call noalias i8* @_Znwm(i64 4)
%0 = bitcast i8* %call to i32*
; CHECK: 2 = MemoryDef(1)
; CHECK-NEXT: %call1 = call noalias i8* @_Znwm(i64 4)
%call1 = call noalias i8* @_Znwm(i64 4)
%1 = bitcast i8* %call1 to i32*
; CHECK: 3 = MemoryDef(2)
; CHECK-NEXT: store i32 5, i32* %0, align 4
store i32 5, i32* %0, align 4
; CHECK: 4 = MemoryDef(3)
; CHECK-NEXT: store i32 7, i32* %1, align 4
store i32 7, i32* %1, align 4
; CHECK: MemoryUse(3)
; CHECK-NEXT: %2 = load i32, i32* %0, align 4
%2 = load i32, i32* %0, align 4
; CHECK: MemoryUse(4)
; CHECK-NEXT: %3 = load i32, i32* %1, align 4
%3 = load i32, i32* %1, align 4
; CHECK: MemoryUse(3)
; CHECK-NEXT: %4 = load i32, i32* %0, align 4
%4 = load i32, i32* %0, align 4
; CHECK: MemoryUse(4)
; CHECK-NEXT: %5 = load i32, i32* %1, align 4
%5 = load i32, i32* %1, align 4
%add = add nsw i32 %3, %5
ret i32 %add
}

declare noalias i8* @_Znwm(i64)
21 changes: 21 additions & 0 deletions llvm/test/Transforms/Util/MemorySSA/volatile-clobber.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
; RUN: opt -basicaa -print-memoryssa -verify-memoryssa -analyze < %s 2>&1 | FileCheck %s
;
; Ensures that volatile stores/loads count as MemoryDefs

define i32 @foo() {
%1 = alloca i32, align 4
; CHECK: 1 = MemoryDef(liveOnEntry)
; CHECK-NEXT: store volatile i32 4
store volatile i32 4, i32* %1, align 4
; CHECK: 2 = MemoryDef(1)
; CHECK-NEXT: store volatile i32 8
store volatile i32 8, i32* %1, align 4
; CHECK: 3 = MemoryDef(2)
; CHECK-NEXT: %2 = load volatile i32
%2 = load volatile i32, i32* %1, align 4
; CHECK: 4 = MemoryDef(3)
; CHECK-NEXT: %3 = load volatile i32
%3 = load volatile i32, i32* %1, align 4
%4 = add i32 %3, %2
ret i32 %4
}