1; RUN: opt -S -early-cse < %s | FileCheck %s 2; RUN: opt -S -basicaa -early-cse-memssa < %s | FileCheck %s 3 4declare void @clobber_and_use(i32) 5 6define void @f_0(i32* %ptr) { 7; CHECK-LABEL: @f_0( 8; CHECK: %val0 = load i32, i32* %ptr, !invariant.load !0 9; CHECK: call void @clobber_and_use(i32 %val0) 10; CHECK: call void @clobber_and_use(i32 %val0) 11; CHECK: call void @clobber_and_use(i32 %val0) 12; CHECK: ret void 13 14 %val0 = load i32, i32* %ptr, !invariant.load !{} 15 call void @clobber_and_use(i32 %val0) 16 %val1 = load i32, i32* %ptr, !invariant.load !{} 17 call void @clobber_and_use(i32 %val1) 18 %val2 = load i32, i32* %ptr, !invariant.load !{} 19 call void @clobber_and_use(i32 %val2) 20 ret void 21} 22 23define void @f_1(i32* %ptr) { 24; We can forward invariant loads to non-invariant loads. 25 26; CHECK-LABEL: @f_1( 27; CHECK: %val0 = load i32, i32* %ptr, !invariant.load !0 28; CHECK: call void @clobber_and_use(i32 %val0) 29; CHECK: call void @clobber_and_use(i32 %val0) 30 31 %val0 = load i32, i32* %ptr, !invariant.load !{} 32 call void @clobber_and_use(i32 %val0) 33 %val1 = load i32, i32* %ptr 34 call void @clobber_and_use(i32 %val1) 35 ret void 36} 37 38define void @f_2(i32* %ptr) { 39; We can forward a non-invariant load into an invariant load. 40 41; CHECK-LABEL: @f_2( 42; CHECK: %val0 = load i32, i32* %ptr 43; CHECK: call void @clobber_and_use(i32 %val0) 44; CHECK: call void @clobber_and_use(i32 %val0) 45 46 %val0 = load i32, i32* %ptr 47 call void @clobber_and_use(i32 %val0) 48 %val1 = load i32, i32* %ptr, !invariant.load !{} 49 call void @clobber_and_use(i32 %val1) 50 ret void 51} 52 53define void @f_3(i1 %cond, i32* %ptr) { 54; CHECK-LABEL: @f_3( 55 %val0 = load i32, i32* %ptr, !invariant.load !{} 56 call void @clobber_and_use(i32 %val0) 57 br i1 %cond, label %left, label %right 58 59; CHECK: %val0 = load i32, i32* %ptr, !invariant.load !0 60; CHECK: left: 61; CHECK-NEXT: call void @clobber_and_use(i32 %val0) 62 63left: 64 %val1 = load i32, i32* %ptr 65 call void @clobber_and_use(i32 %val1) 66 ret void 67 68right: 69 ret void 70} 71 72define void @f_4(i1 %cond, i32* %ptr) { 73; Negative test -- can't forward %val0 to %va1 because that'll break 74; def-dominates-use. 75 76; CHECK-LABEL: @f_4( 77 br i1 %cond, label %left, label %merge 78 79left: 80; CHECK: left: 81; CHECK-NEXT: %val0 = load i32, i32* %ptr, !invariant.load ! 82; CHECK-NEXT: call void @clobber_and_use(i32 %val0) 83 84 %val0 = load i32, i32* %ptr, !invariant.load !{} 85 call void @clobber_and_use(i32 %val0) 86 br label %merge 87 88merge: 89; CHECK: merge: 90; CHECK-NEXT: %val1 = load i32, i32* %ptr 91; CHECK-NEXT: call void @clobber_and_use(i32 %val1) 92 93 %val1 = load i32, i32* %ptr 94 call void @clobber_and_use(i32 %val1) 95 ret void 96} 97 98; By assumption, the call can't change contents of p 99; LangRef is a bit unclear about whether the store is reachable, so 100; for the moment we chose to be conservative and just assume it's valid 101; to restore the same unchanging value. 102define void @test_dse1(i32* %p) { 103; CHECK-LABEL: @test_dse1 104; CHECK-NOT: store 105 %v1 = load i32, i32* %p, !invariant.load !{} 106 call void @clobber_and_use(i32 %v1) 107 store i32 %v1, i32* %p 108 ret void 109} 110 111; By assumption, v1 must equal v2 (TODO) 112define void @test_false_negative_dse2(i32* %p, i32 %v2) { 113; CHECK-LABEL: @test_false_negative_dse2 114; CHECK: store 115 %v1 = load i32, i32* %p, !invariant.load !{} 116 call void @clobber_and_use(i32 %v1) 117 store i32 %v2, i32* %p 118 ret void 119} 120 121; If we remove the load, we still start an invariant scope since 122; it lets us remove later loads not explicitly marked invariant 123define void @test_scope_start_without_load(i32* %p) { 124; CHECK-LABEL: @test_scope_start_without_load 125; CHECK: %v1 = load i32, i32* %p 126; CHECK: %add = add i32 %v1, %v1 127; CHECK: call void @clobber_and_use(i32 %add) 128; CHECK: call void @clobber_and_use(i32 %v1) 129; CHECK: ret void 130 %v1 = load i32, i32* %p 131 %v2 = load i32, i32* %p, !invariant.load !{} 132 %add = add i32 %v1, %v2 133 call void @clobber_and_use(i32 %add) 134 %v3 = load i32, i32* %p 135 call void @clobber_and_use(i32 %v3) 136 ret void 137} 138 139; If we already have an invariant scope, don't want to start a new one 140; with a potentially greater generation. This hides the earlier invariant 141; load 142define void @test_scope_restart(i32* %p) { 143; CHECK-LABEL: @test_scope_restart 144; CHECK: %v1 = load i32, i32* %p 145; CHECK: call void @clobber_and_use(i32 %v1) 146; CHECK: %add = add i32 %v1, %v1 147; CHECK: call void @clobber_and_use(i32 %add) 148; CHECK: call void @clobber_and_use(i32 %v1) 149; CHECK: ret void 150 %v1 = load i32, i32* %p, !invariant.load !{} 151 call void @clobber_and_use(i32 %v1) 152 %v2 = load i32, i32* %p, !invariant.load !{} 153 %add = add i32 %v1, %v2 154 call void @clobber_and_use(i32 %add) 155 %v3 = load i32, i32* %p 156 call void @clobber_and_use(i32 %v3) 157 ret void 158} 159