265 lines · plain
1; NOTE: Assertions have been autogenerated by utils/update_test_checks.py2; RUN: opt -S -passes='early-cse' -earlycse-debug-hash < %s | FileCheck %s --check-prefixes=CHECK,NO_ASSUME3; RUN: opt -S -passes='early-cse<memssa>' < %s | FileCheck %s --check-prefixes=CHECK,NO_ASSUME4; RUN: opt -S -passes='early-cse<memssa>' --enable-knowledge-retention < %s | FileCheck %s --check-prefixes=CHECK,USE_ASSUME5 6declare void @clobber_and_use(i32)7declare void @clobber_and_combine(i32, i32)8 9define void @f_0(ptr %ptr) {10; NO_ASSUME-LABEL: @f_0(11; NO_ASSUME-NEXT: [[VAL0:%.*]] = load i32, ptr [[PTR:%.*]], align 4, !invariant.load !012; NO_ASSUME-NEXT: call void @clobber_and_use(i32 [[VAL0]])13; NO_ASSUME-NEXT: call void @clobber_and_use(i32 [[VAL0]])14; NO_ASSUME-NEXT: call void @clobber_and_use(i32 [[VAL0]])15; NO_ASSUME-NEXT: ret void16;17; USE_ASSUME-LABEL: @f_0(18; USE_ASSUME-NEXT: [[VAL0:%.*]] = load i32, ptr [[PTR:%.*]], align 4, !invariant.load !019; USE_ASSUME-NEXT: call void @clobber_and_use(i32 [[VAL0]])20; USE_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(ptr [[PTR]], i64 4), "nonnull"(ptr [[PTR]]), "align"(ptr [[PTR]], i64 4) ]21; USE_ASSUME-NEXT: call void @clobber_and_use(i32 [[VAL0]])22; USE_ASSUME-NEXT: call void @clobber_and_use(i32 [[VAL0]])23; USE_ASSUME-NEXT: ret void24;25 26 %val0 = load i32, ptr %ptr, !invariant.load !{}27 call void @clobber_and_use(i32 %val0)28 %val1 = load i32, ptr %ptr, !invariant.load !{}29 call void @clobber_and_use(i32 %val1)30 %val2 = load i32, ptr %ptr, !invariant.load !{}31 call void @clobber_and_use(i32 %val2)32 ret void33}34 35define void @f_1(ptr %ptr) {36; We can forward invariant loads to non-invariant loads.37; NO_ASSUME-LABEL: @f_1(38; NO_ASSUME-NEXT: [[VAL0:%.*]] = load i32, ptr [[PTR:%.*]], align 4, !invariant.load !039; NO_ASSUME-NEXT: call void @clobber_and_use(i32 [[VAL0]])40; NO_ASSUME-NEXT: call void @clobber_and_use(i32 [[VAL0]])41; NO_ASSUME-NEXT: ret void42;43; USE_ASSUME-LABEL: @f_1(44; USE_ASSUME-NEXT: [[VAL0:%.*]] = load i32, ptr [[PTR:%.*]], align 4, !invariant.load !045; USE_ASSUME-NEXT: call void @clobber_and_use(i32 [[VAL0]])46; USE_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(ptr [[PTR]], i64 4), "nonnull"(ptr [[PTR]]), "align"(ptr [[PTR]], i64 4) ]47; USE_ASSUME-NEXT: call void @clobber_and_use(i32 [[VAL0]])48; USE_ASSUME-NEXT: ret void49;50 51 %val0 = load i32, ptr %ptr, !invariant.load !{}52 call void @clobber_and_use(i32 %val0)53 %val1 = load i32, ptr %ptr54 call void @clobber_and_use(i32 %val1)55 ret void56}57 58define void @f_2(ptr %ptr) {59; We can forward a non-invariant load into an invariant load.60; NO_ASSUME-LABEL: @f_2(61; NO_ASSUME-NEXT: [[VAL0:%.*]] = load i32, ptr [[PTR:%.*]], align 462; NO_ASSUME-NEXT: call void @clobber_and_use(i32 [[VAL0]])63; NO_ASSUME-NEXT: call void @clobber_and_use(i32 [[VAL0]])64; NO_ASSUME-NEXT: ret void65;66; USE_ASSUME-LABEL: @f_2(67; USE_ASSUME-NEXT: [[VAL0:%.*]] = load i32, ptr [[PTR:%.*]], align 468; USE_ASSUME-NEXT: call void @clobber_and_use(i32 [[VAL0]])69; USE_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(ptr [[PTR]], i64 4), "nonnull"(ptr [[PTR]]), "align"(ptr [[PTR]], i64 4) ]70; USE_ASSUME-NEXT: call void @clobber_and_use(i32 [[VAL0]])71; USE_ASSUME-NEXT: ret void72;73 74 %val0 = load i32, ptr %ptr75 call void @clobber_and_use(i32 %val0)76 %val1 = load i32, ptr %ptr, !invariant.load !{}77 call void @clobber_and_use(i32 %val1)78 ret void79}80 81define void @f_3(i1 %cond, ptr %ptr) {82; NO_ASSUME-LABEL: @f_3(83; NO_ASSUME-NEXT: [[VAL0:%.*]] = load i32, ptr [[PTR:%.*]], align 4, !invariant.load !084; NO_ASSUME-NEXT: call void @clobber_and_use(i32 [[VAL0]])85; NO_ASSUME-NEXT: br i1 [[COND:%.*]], label [[LEFT:%.*]], label [[RIGHT:%.*]]86; NO_ASSUME: left:87; NO_ASSUME-NEXT: call void @clobber_and_use(i32 [[VAL0]])88; NO_ASSUME-NEXT: ret void89; NO_ASSUME: right:90; NO_ASSUME-NEXT: ret void91;92; USE_ASSUME-LABEL: @f_3(93; USE_ASSUME-NEXT: [[VAL0:%.*]] = load i32, ptr [[PTR:%.*]], align 4, !invariant.load !094; USE_ASSUME-NEXT: call void @clobber_and_use(i32 [[VAL0]])95; USE_ASSUME-NEXT: br i1 [[COND:%.*]], label [[LEFT:%.*]], label [[RIGHT:%.*]]96; USE_ASSUME: left:97; USE_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(ptr [[PTR]], i64 4), "nonnull"(ptr [[PTR]]), "align"(ptr [[PTR]], i64 4) ]98; USE_ASSUME-NEXT: call void @clobber_and_use(i32 [[VAL0]])99; USE_ASSUME-NEXT: ret void100; USE_ASSUME: right:101; USE_ASSUME-NEXT: ret void102;103 %val0 = load i32, ptr %ptr, !invariant.load !{}104 call void @clobber_and_use(i32 %val0)105 br i1 %cond, label %left, label %right106 107 108left:109 %val1 = load i32, ptr %ptr110 call void @clobber_and_use(i32 %val1)111 ret void112 113right:114 ret void115}116 117define void @f_4(i1 %cond, ptr %ptr) {118; Negative test -- can't forward %val0 to %va1 because that'll break119; def-dominates-use.120; CHECK-LABEL: @f_4(121; CHECK-NEXT: br i1 [[COND:%.*]], label [[LEFT:%.*]], label [[MERGE:%.*]]122; CHECK: left:123; CHECK-NEXT: [[VAL0:%.*]] = load i32, ptr [[PTR:%.*]], align 4, !invariant.load !0124; CHECK-NEXT: call void @clobber_and_use(i32 [[VAL0]])125; CHECK-NEXT: br label [[MERGE]]126; CHECK: merge:127; CHECK-NEXT: [[VAL1:%.*]] = load i32, ptr [[PTR]], align 4128; CHECK-NEXT: call void @clobber_and_use(i32 [[VAL1]])129; CHECK-NEXT: ret void130;131 br i1 %cond, label %left, label %merge132 133left:134 135 %val0 = load i32, ptr %ptr, !invariant.load !{}136 call void @clobber_and_use(i32 %val0)137 br label %merge138 139merge:140 141 %val1 = load i32, ptr %ptr142 call void @clobber_and_use(i32 %val1)143 ret void144}145 146; By assumption, the call can't change contents of p147; LangRef is a bit unclear about whether the store is reachable, so148; for the moment we chose to be conservative and just assume it's valid149; to restore the same unchanging value.150define void @test_dse1(ptr %p) {151; NO_ASSUME-LABEL: @test_dse1(152; NO_ASSUME-NEXT: [[V1:%.*]] = load i32, ptr [[P:%.*]], align 4, !invariant.load !0153; NO_ASSUME-NEXT: call void @clobber_and_use(i32 [[V1]])154; NO_ASSUME-NEXT: ret void155;156; USE_ASSUME-LABEL: @test_dse1(157; USE_ASSUME-NEXT: [[V1:%.*]] = load i32, ptr [[P:%.*]], align 4, !invariant.load !0158; USE_ASSUME-NEXT: call void @clobber_and_use(i32 [[V1]])159; USE_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(ptr [[P]], i64 4), "nonnull"(ptr [[P]]), "align"(ptr [[P]], i64 4) ]160; USE_ASSUME-NEXT: ret void161;162 %v1 = load i32, ptr %p, !invariant.load !{}163 call void @clobber_and_use(i32 %v1)164 store i32 %v1, ptr %p165 ret void166}167 168; Extending @test_dse1, the call can't change the contents of p.invariant.169; Moreover, the contents of p.invariant cannot change from the store to p.170define void @test_dse2(ptr noalias %p, ptr noalias %p.invariant) {171; NO_ASSUME-LABEL: @test_dse2(172; NO_ASSUME-NEXT: [[V:%.*]] = load i32, ptr [[P:%.*]], align 4173; NO_ASSUME-NEXT: [[V_INVARIANT:%.*]] = load i32, ptr [[P_INVARIANT:%.*]], align 4, !invariant.load !0174; NO_ASSUME-NEXT: [[V_COMB:%.*]] = call i32 @clobber_and_combine(i32 [[V]], i32 [[V_INVARIANT]])175; NO_ASSUME-NEXT: store i32 [[V_COMB]], ptr [[P]], align 4176; NO_ASSUME-NEXT: ret void177;178; USE_ASSUME-LABEL: @test_dse2(179; USE_ASSUME-NEXT: [[V:%.*]] = load i32, ptr [[P:%.*]], align 4180; USE_ASSUME-NEXT: [[V_INVARIANT:%.*]] = load i32, ptr [[P_INVARIANT:%.*]], align 4, !invariant.load !0181; USE_ASSUME-NEXT: [[V_COMB:%.*]] = call i32 @clobber_and_combine(i32 [[V]], i32 [[V_INVARIANT]])182; USE_ASSUME-NEXT: store i32 [[V_COMB]], ptr [[P]], align 4183; USE_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(ptr [[P_INVARIANT]], i64 4), "nonnull"(ptr [[P_INVARIANT]]), "align"(ptr [[P_INVARIANT]], i64 4) ]184; USE_ASSUME-NEXT: ret void185;186 %v = load i32, ptr %p187 %v.invariant = load i32, ptr %p.invariant, !invariant.load !{}188 %v.comb = call i32 @clobber_and_combine(i32 %v, i32 %v.invariant)189 store i32 %v.comb, ptr %p190 store i32 %v.invariant, ptr %p.invariant191 ret void192}193 194; By assumption, v1 must equal v2 (TODO)195define void @test_false_negative_dse2(ptr %p, i32 %v2) {196; CHECK-LABEL: @test_false_negative_dse2(197; CHECK-NEXT: [[V1:%.*]] = load i32, ptr [[P:%.*]], align 4, !invariant.load !0198; CHECK-NEXT: call void @clobber_and_use(i32 [[V1]])199; CHECK-NEXT: store i32 [[V2:%.*]], ptr [[P]], align 4200; CHECK-NEXT: ret void201;202 %v1 = load i32, ptr %p, !invariant.load !{}203 call void @clobber_and_use(i32 %v1)204 store i32 %v2, ptr %p205 ret void206}207 208; If we remove the load, we still start an invariant scope since209; it lets us remove later loads not explicitly marked invariant210define void @test_scope_start_without_load(ptr %p) {211; NO_ASSUME-LABEL: @test_scope_start_without_load(212; NO_ASSUME-NEXT: [[V1:%.*]] = load i32, ptr [[P:%.*]], align 4213; NO_ASSUME-NEXT: [[ADD:%.*]] = add i32 [[V1]], [[V1]]214; NO_ASSUME-NEXT: call void @clobber_and_use(i32 [[ADD]])215; NO_ASSUME-NEXT: call void @clobber_and_use(i32 [[V1]])216; NO_ASSUME-NEXT: ret void217;218; USE_ASSUME-LABEL: @test_scope_start_without_load(219; USE_ASSUME-NEXT: [[V1:%.*]] = load i32, ptr [[P:%.*]], align 4220; USE_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(ptr [[P]], i64 4), "nonnull"(ptr [[P]]), "align"(ptr [[P]], i64 4) ]221; USE_ASSUME-NEXT: [[ADD:%.*]] = add i32 [[V1]], [[V1]]222; USE_ASSUME-NEXT: call void @clobber_and_use(i32 [[ADD]])223; USE_ASSUME-NEXT: call void @clobber_and_use(i32 [[V1]])224; USE_ASSUME-NEXT: ret void225;226 %v1 = load i32, ptr %p227 %v2 = load i32, ptr %p, !invariant.load !{}228 %add = add i32 %v1, %v2229 call void @clobber_and_use(i32 %add)230 %v3 = load i32, ptr %p231 call void @clobber_and_use(i32 %v3)232 ret void233}234 235; If we already have an invariant scope, don't want to start a new one236; with a potentially greater generation. This hides the earlier invariant237; load238define void @test_scope_restart(ptr %p) {239; NO_ASSUME-LABEL: @test_scope_restart(240; NO_ASSUME-NEXT: [[V1:%.*]] = load i32, ptr [[P:%.*]], align 4, !invariant.load !0241; NO_ASSUME-NEXT: call void @clobber_and_use(i32 [[V1]])242; NO_ASSUME-NEXT: [[ADD:%.*]] = add i32 [[V1]], [[V1]]243; NO_ASSUME-NEXT: call void @clobber_and_use(i32 [[ADD]])244; NO_ASSUME-NEXT: call void @clobber_and_use(i32 [[V1]])245; NO_ASSUME-NEXT: ret void246;247; USE_ASSUME-LABEL: @test_scope_restart(248; USE_ASSUME-NEXT: [[V1:%.*]] = load i32, ptr [[P:%.*]], align 4, !invariant.load !0249; USE_ASSUME-NEXT: call void @clobber_and_use(i32 [[V1]])250; USE_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(ptr [[P]], i64 4), "nonnull"(ptr [[P]]), "align"(ptr [[P]], i64 4) ]251; USE_ASSUME-NEXT: [[ADD:%.*]] = add i32 [[V1]], [[V1]]252; USE_ASSUME-NEXT: call void @clobber_and_use(i32 [[ADD]])253; USE_ASSUME-NEXT: call void @clobber_and_use(i32 [[V1]])254; USE_ASSUME-NEXT: ret void255;256 %v1 = load i32, ptr %p, !invariant.load !{}257 call void @clobber_and_use(i32 %v1)258 %v2 = load i32, ptr %p, !invariant.load !{}259 %add = add i32 %v1, %v2260 call void @clobber_and_use(i32 %add)261 %v3 = load i32, ptr %p262 call void @clobber_and_use(i32 %v3)263 ret void264}265