1; NOTE: Assertions have been autogenerated by utils/update_test_checks.py 2; RUN: opt < %s -S -passes='early-cse' -earlycse-debug-hash | FileCheck %s --check-prefix=CHECK-NOMEMSSA 3; RUN: opt < %s -S -passes='early-cse<memssa>' | FileCheck %s 4; RUN: opt < %s -S -passes='early-cse' | FileCheck %s --check-prefix=CHECK-NOMEMSSA 5; RUN: opt < %s -S -aa-pipeline=basic-aa -passes='early-cse<memssa>' | FileCheck %s 6 7@G1 = global i32 zeroinitializer 8@G2 = global i32 zeroinitializer 9@G3 = global i32 zeroinitializer 10 11;; Simple load value numbering across non-clobbering store. 12define i32 @test1() { 13; CHECK-NOMEMSSA-LABEL: @test1( 14; CHECK-NOMEMSSA-NEXT: [[V1:%.*]] = load i32, ptr @G1, align 4 15; CHECK-NOMEMSSA-NEXT: store i32 0, ptr @G2, align 4 16; CHECK-NOMEMSSA-NEXT: [[V2:%.*]] = load i32, ptr @G1, align 4 17; CHECK-NOMEMSSA-NEXT: [[DIFF:%.*]] = sub i32 [[V1]], [[V2]] 18; CHECK-NOMEMSSA-NEXT: ret i32 [[DIFF]] 19; 20; CHECK-LABEL: @test1( 21; CHECK-NEXT: [[V1:%.*]] = load i32, ptr @G1, align 4 22; CHECK-NEXT: store i32 0, ptr @G2, align 4 23; CHECK-NEXT: ret i32 0 24; 25 %V1 = load i32, ptr @G1 26 store i32 0, ptr @G2 27 %V2 = load i32, ptr @G1 28 %Diff = sub i32 %V1, %V2 29 ret i32 %Diff 30} 31 32;; Simple dead store elimination across non-clobbering store. 33define void @test2() { 34; CHECK-NOMEMSSA-LABEL: @test2( 35; CHECK-NOMEMSSA-NEXT: entry: 36; CHECK-NOMEMSSA-NEXT: [[V1:%.*]] = load i32, ptr @G1, align 4 37; CHECK-NOMEMSSA-NEXT: store i32 0, ptr @G2, align 4 38; CHECK-NOMEMSSA-NEXT: store i32 [[V1]], ptr @G1, align 4 39; CHECK-NOMEMSSA-NEXT: ret void 40; 41; CHECK-LABEL: @test2( 42; CHECK-NEXT: entry: 43; CHECK-NEXT: [[V1:%.*]] = load i32, ptr @G1, align 4 44; CHECK-NEXT: store i32 0, ptr @G2, align 4 45; CHECK-NEXT: ret void 46; 47entry: 48 %V1 = load i32, ptr @G1 49 store i32 0, ptr @G2 50 store i32 %V1, ptr @G1 51 ret void 52} 53 54;; Check that memoryphi optimization happens during EarlyCSE, enabling 55;; more load CSE opportunities. 56define void @test_memphiopt(i1 %c, ptr %p) { 57; CHECK-NOMEMSSA-LABEL: @test_memphiopt( 58; CHECK-NOMEMSSA-NEXT: entry: 59; CHECK-NOMEMSSA-NEXT: [[V1:%.*]] = load i32, ptr @G1, align 4 60; CHECK-NOMEMSSA-NEXT: br i1 [[C:%.*]], label [[THEN:%.*]], label [[END:%.*]] 61; CHECK-NOMEMSSA: then: 62; CHECK-NOMEMSSA-NEXT: [[PV:%.*]] = load i32, ptr [[P:%.*]], align 4 63; CHECK-NOMEMSSA-NEXT: br label [[END]] 64; CHECK-NOMEMSSA: end: 65; CHECK-NOMEMSSA-NEXT: [[V2:%.*]] = load i32, ptr @G1, align 4 66; CHECK-NOMEMSSA-NEXT: [[SUM:%.*]] = add i32 [[V1]], [[V2]] 67; CHECK-NOMEMSSA-NEXT: store i32 [[SUM]], ptr @G2, align 4 68; CHECK-NOMEMSSA-NEXT: ret void 69; 70; CHECK-LABEL: @test_memphiopt( 71; CHECK-NEXT: entry: 72; CHECK-NEXT: [[V1:%.*]] = load i32, ptr @G1, align 4 73; CHECK-NEXT: br i1 [[C:%.*]], label [[THEN:%.*]], label [[END:%.*]] 74; CHECK: then: 75; CHECK-NEXT: [[PV:%.*]] = load i32, ptr [[P:%.*]], align 4 76; CHECK-NEXT: br label [[END]] 77; CHECK: end: 78; CHECK-NEXT: [[SUM:%.*]] = add i32 [[V1]], [[V1]] 79; CHECK-NEXT: store i32 [[SUM]], ptr @G2, align 4 80; CHECK-NEXT: ret void 81; 82entry: 83 %v1 = load i32, ptr @G1 84 br i1 %c, label %then, label %end 85 86then: 87 %pv = load i32, ptr %p 88 store i32 %pv, ptr %p 89 br label %end 90 91end: 92 %v2 = load i32, ptr @G1 93 %sum = add i32 %v1, %v2 94 store i32 %sum, ptr @G2 95 ret void 96} 97 98 99;; Check that MemoryPhi optimization and MemoryUse re-optimization 100;; happens during EarlyCSE, enabling more load CSE opportunities. 101define void @test_memphiopt2(i1 %c, ptr %p) { 102; CHECK-NOMEMSSA-LABEL: @test_memphiopt2( 103; CHECK-NOMEMSSA-NEXT: entry: 104; CHECK-NOMEMSSA-NEXT: [[V1:%.*]] = load i32, ptr @G1, align 4 105; CHECK-NOMEMSSA-NEXT: store i32 [[V1]], ptr @G2, align 4 106; CHECK-NOMEMSSA-NEXT: br i1 [[C:%.*]], label [[THEN:%.*]], label [[END:%.*]] 107; CHECK-NOMEMSSA: then: 108; CHECK-NOMEMSSA-NEXT: [[PV:%.*]] = load i32, ptr [[P:%.*]], align 4 109; CHECK-NOMEMSSA-NEXT: br label [[END]] 110; CHECK-NOMEMSSA: end: 111; CHECK-NOMEMSSA-NEXT: [[V2:%.*]] = load i32, ptr @G1, align 4 112; CHECK-NOMEMSSA-NEXT: store i32 [[V2]], ptr @G3, align 4 113; CHECK-NOMEMSSA-NEXT: ret void 114; 115; CHECK-LABEL: @test_memphiopt2( 116; CHECK-NEXT: entry: 117; CHECK-NEXT: [[V1:%.*]] = load i32, ptr @G1, align 4 118; CHECK-NEXT: store i32 [[V1]], ptr @G2, align 4 119; CHECK-NEXT: br i1 [[C:%.*]], label [[THEN:%.*]], label [[END:%.*]] 120; CHECK: then: 121; CHECK-NEXT: [[PV:%.*]] = load i32, ptr [[P:%.*]], align 4 122; CHECK-NEXT: br label [[END]] 123; CHECK: end: 124; CHECK-NEXT: store i32 [[V1]], ptr @G3, align 4 125; CHECK-NEXT: ret void 126; 127entry: 128 %v1 = load i32, ptr @G1 129 store i32 %v1, ptr @G2 130 br i1 %c, label %then, label %end 131 132then: 133 %pv = load i32, ptr %p 134 store i32 %pv, ptr %p 135 br label %end 136 137end: 138 %v2 = load i32, ptr @G1 139 store i32 %v2, ptr @G3 140 ret void 141} 142 143;; Check that we respect lifetime.start/lifetime.end intrinsics when deleting 144;; stores that, without the lifetime calls, would be writebacks. 145define void @test_writeback_lifetimes(ptr %p) { 146; CHECK-NOMEMSSA-LABEL: @test_writeback_lifetimes( 147; CHECK-NOMEMSSA-NEXT: entry: 148; CHECK-NOMEMSSA-NEXT: [[Q:%.*]] = getelementptr i32, ptr [[P:%.*]], i64 1 149; CHECK-NOMEMSSA-NEXT: [[PV:%.*]] = load i32, ptr [[P]], align 4 150; CHECK-NOMEMSSA-NEXT: [[QV:%.*]] = load i32, ptr [[Q]], align 4 151; CHECK-NOMEMSSA-NEXT: call void @llvm.lifetime.end.p0(i64 8, ptr [[P]]) 152; CHECK-NOMEMSSA-NEXT: call void @llvm.lifetime.start.p0(i64 8, ptr [[P]]) 153; CHECK-NOMEMSSA-NEXT: store i32 [[PV]], ptr [[P]], align 4 154; CHECK-NOMEMSSA-NEXT: store i32 [[QV]], ptr [[Q]], align 4 155; CHECK-NOMEMSSA-NEXT: ret void 156; 157; CHECK-LABEL: @test_writeback_lifetimes( 158; CHECK-NEXT: entry: 159; CHECK-NEXT: [[Q:%.*]] = getelementptr i32, ptr [[P:%.*]], i64 1 160; CHECK-NEXT: [[PV:%.*]] = load i32, ptr [[P]], align 4 161; CHECK-NEXT: [[QV:%.*]] = load i32, ptr [[Q]], align 4 162; CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 8, ptr [[P]]) 163; CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 8, ptr [[P]]) 164; CHECK-NEXT: store i32 [[PV]], ptr [[P]], align 4 165; CHECK-NEXT: store i32 [[QV]], ptr [[Q]], align 4 166; CHECK-NEXT: ret void 167; 168entry: 169 %q = getelementptr i32, ptr %p, i64 1 170 %pv = load i32, ptr %p 171 %qv = load i32, ptr %q 172 call void @llvm.lifetime.end.p0(i64 8, ptr %p) 173 call void @llvm.lifetime.start.p0(i64 8, ptr %p) 174 store i32 %pv, ptr %p 175 store i32 %qv, ptr %q 176 ret void 177} 178 179;; Check that we respect lifetime.start/lifetime.end intrinsics when deleting 180;; stores that, without the lifetime calls, would be writebacks. 181define void @test_writeback_lifetimes_multi_arg(ptr %p, ptr %q) { 182; CHECK-NOMEMSSA-LABEL: @test_writeback_lifetimes_multi_arg( 183; CHECK-NOMEMSSA-NEXT: entry: 184; CHECK-NOMEMSSA-NEXT: [[PV:%.*]] = load i32, ptr [[P:%.*]], align 4 185; CHECK-NOMEMSSA-NEXT: [[QV:%.*]] = load i32, ptr [[Q:%.*]], align 4 186; CHECK-NOMEMSSA-NEXT: call void @llvm.lifetime.end.p0(i64 8, ptr [[P]]) 187; CHECK-NOMEMSSA-NEXT: call void @llvm.lifetime.start.p0(i64 8, ptr [[P]]) 188; CHECK-NOMEMSSA-NEXT: store i32 [[PV]], ptr [[P]], align 4 189; CHECK-NOMEMSSA-NEXT: store i32 [[QV]], ptr [[Q]], align 4 190; CHECK-NOMEMSSA-NEXT: ret void 191; 192; CHECK-LABEL: @test_writeback_lifetimes_multi_arg( 193; CHECK-NEXT: entry: 194; CHECK-NEXT: [[PV:%.*]] = load i32, ptr [[P:%.*]], align 4 195; CHECK-NEXT: [[QV:%.*]] = load i32, ptr [[Q:%.*]], align 4 196; CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 8, ptr [[P]]) 197; CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 8, ptr [[P]]) 198; CHECK-NEXT: store i32 [[PV]], ptr [[P]], align 4 199; CHECK-NEXT: store i32 [[QV]], ptr [[Q]], align 4 200; CHECK-NEXT: ret void 201; 202entry: 203 %pv = load i32, ptr %p 204 %qv = load i32, ptr %q 205 call void @llvm.lifetime.end.p0(i64 8, ptr %p) 206 call void @llvm.lifetime.start.p0(i64 8, ptr %p) 207 store i32 %pv, ptr %p 208 store i32 %qv, ptr %q 209 ret void 210} 211 212declare void @llvm.lifetime.end.p0(i64, ptr) 213declare void @llvm.lifetime.start.p0(i64, ptr) 214