598 lines · plain
1; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature2; RUN: opt < %s -S -passes=early-cse -earlycse-debug-hash | FileCheck %s --check-prefixes=CHECK,NO_ASSUME3; RUN: opt < %s -S -passes=early-cse --enable-knowledge-retention | FileCheck %s --check-prefixes=CHECK,USE_ASSUME4; RUN: opt < %s -S -passes=early-cse | FileCheck %s --check-prefixes=CHECK,NO_ASSUME5 6declare ptr @llvm.invariant.start.p0(i64, ptr nocapture) nounwind readonly7declare void @llvm.invariant.end.p0(ptr, i64, ptr nocapture) nounwind8 9; Check that we do load-load forwarding over invariant.start, since it does not10; clobber memory11define i8 @test_bypass1(ptr%P) {12; NO_ASSUME-LABEL: define {{[^@]+}}@test_bypass113; NO_ASSUME-SAME: (ptr [[P:%.*]]) {14; NO_ASSUME-NEXT: [[V1:%.*]] = load i8, ptr [[P]], align 115; NO_ASSUME-NEXT: [[I:%.*]] = call ptr @llvm.invariant.start.p0(i64 1, ptr [[P]])16; NO_ASSUME-NEXT: ret i8 017;18; USE_ASSUME-LABEL: define {{[^@]+}}@test_bypass119; USE_ASSUME-SAME: (ptr [[P:%.*]]) {20; USE_ASSUME-NEXT: [[V1:%.*]] = load i8, ptr [[P]], align 121; USE_ASSUME-NEXT: [[I:%.*]] = call ptr @llvm.invariant.start.p0(i64 1, ptr [[P]])22; USE_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(ptr [[P]], i64 1), "nonnull"(ptr [[P]]) ]23; USE_ASSUME-NEXT: ret i8 024;25 26 %V1 = load i8, ptr %P27 %i = call ptr @llvm.invariant.start.p0(i64 1, ptr %P)28 %V2 = load i8, ptr %P29 %Diff = sub i8 %V1, %V230 ret i8 %Diff31}32 33 34; Trivial Store->load forwarding over invariant.start35define i8 @test_bypass2(ptr%P) {36; NO_ASSUME-LABEL: define {{[^@]+}}@test_bypass237; NO_ASSUME-SAME: (ptr [[P:%.*]]) {38; NO_ASSUME-NEXT: store i8 42, ptr [[P]], align 139; NO_ASSUME-NEXT: [[I:%.*]] = call ptr @llvm.invariant.start.p0(i64 1, ptr [[P]])40; NO_ASSUME-NEXT: ret i8 4241;42; USE_ASSUME-LABEL: define {{[^@]+}}@test_bypass243; USE_ASSUME-SAME: (ptr [[P:%.*]]) {44; USE_ASSUME-NEXT: store i8 42, ptr [[P]], align 145; USE_ASSUME-NEXT: [[I:%.*]] = call ptr @llvm.invariant.start.p0(i64 1, ptr [[P]])46; USE_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(ptr [[P]], i64 1), "nonnull"(ptr [[P]]) ]47; USE_ASSUME-NEXT: ret i8 4248;49 50 store i8 42, ptr %P51 %i = call ptr @llvm.invariant.start.p0(i64 1, ptr %P)52 %V1 = load i8, ptr %P53 ret i8 %V154}55 56define i8 @test_bypass_store_load(ptr%P, ptr%P2) {57; NO_ASSUME-LABEL: define {{[^@]+}}@test_bypass_store_load58; NO_ASSUME-SAME: (ptr [[P:%.*]], ptr [[P2:%.*]]) {59; NO_ASSUME-NEXT: store i8 42, ptr [[P]], align 160; NO_ASSUME-NEXT: [[I:%.*]] = call ptr @llvm.invariant.start.p0(i64 1, ptr [[P]])61; NO_ASSUME-NEXT: store i8 0, ptr [[P2]], align 162; NO_ASSUME-NEXT: ret i8 4263;64; USE_ASSUME-LABEL: define {{[^@]+}}@test_bypass_store_load65; USE_ASSUME-SAME: (ptr [[P:%.*]], ptr [[P2:%.*]]) {66; USE_ASSUME-NEXT: store i8 42, ptr [[P]], align 167; USE_ASSUME-NEXT: [[I:%.*]] = call ptr @llvm.invariant.start.p0(i64 1, ptr [[P]])68; USE_ASSUME-NEXT: store i8 0, ptr [[P2]], align 169; USE_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(ptr [[P]], i64 1), "nonnull"(ptr [[P]]) ]70; USE_ASSUME-NEXT: ret i8 4271;72 73 store i8 42, ptr %P74 %i = call ptr @llvm.invariant.start.p0(i64 1, ptr %P)75 store i8 0, ptr %P276 %V1 = load i8, ptr %P77 ret i8 %V178}79 80define i8 @test_bypass_store_load_aatags_1(ptr%P, ptr%P2) {81; NO_ASSUME-LABEL: define {{[^@]+}}@test_bypass_store_load_aatags_182; NO_ASSUME-SAME: (ptr [[P:%.*]], ptr [[P2:%.*]]) {83; NO_ASSUME-NEXT: store i8 42, ptr [[P]], align 1, !tbaa [[TBAA0:![0-9]+]]84; NO_ASSUME-NEXT: [[I:%.*]] = call ptr @llvm.invariant.start.p0(i64 1, ptr [[P]])85; NO_ASSUME-NEXT: store i8 0, ptr [[P2]], align 186; NO_ASSUME-NEXT: ret i8 4287;88; USE_ASSUME-LABEL: define {{[^@]+}}@test_bypass_store_load_aatags_189; USE_ASSUME-SAME: (ptr [[P:%.*]], ptr [[P2:%.*]]) {90; USE_ASSUME-NEXT: store i8 42, ptr [[P]], align 1, !tbaa [[TBAA0:![0-9]+]]91; USE_ASSUME-NEXT: [[I:%.*]] = call ptr @llvm.invariant.start.p0(i64 1, ptr [[P]])92; USE_ASSUME-NEXT: store i8 0, ptr [[P2]], align 193; USE_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(ptr [[P]], i64 1), "nonnull"(ptr [[P]]) ]94; USE_ASSUME-NEXT: ret i8 4295;96 97 store i8 42, ptr %P, !tbaa !098 %i = call ptr @llvm.invariant.start.p0(i64 1, ptr %P)99 store i8 0, ptr %P2100 %V1 = load i8, ptr %P101 ret i8 %V1102}103 104; The test demonstrates a missed optimization opportunity in case when the load105; has AA tags that are different from the store tags.106define i8 @test_bypass_store_load_aatags_2(ptr%P, ptr%P2) {107; CHECK-LABEL: define {{[^@]+}}@test_bypass_store_load_aatags_2108; CHECK-SAME: (ptr [[P:%.*]], ptr [[P2:%.*]]) {109; CHECK-NEXT: store i8 42, ptr [[P]], align 1110; CHECK-NEXT: [[I:%.*]] = call ptr @llvm.invariant.start.p0(i64 1, ptr [[P]])111; CHECK-NEXT: store i8 0, ptr [[P2]], align 1112; CHECK-NEXT: [[V1:%.*]] = load i8, ptr [[P]], align 1, !tbaa [[TBAA0:![0-9]+]]113; CHECK-NEXT: ret i8 [[V1]]114;115 116 store i8 42, ptr %P117 %i = call ptr @llvm.invariant.start.p0(i64 1, ptr %P)118 store i8 0, ptr %P2119 %V1 = load i8, ptr %P, !tbaa !0120 ret i8 %V1121}122 123; We can DSE over invariant.start calls, since the first store to124; %P is valid, and the second store is actually unreachable based on semantics125; of invariant.start.126define void @test_bypass3(ptr %P) {127; NO_ASSUME-LABEL: define {{[^@]+}}@test_bypass3128; NO_ASSUME-SAME: (ptr [[P:%.*]]) {129; NO_ASSUME-NEXT: [[I:%.*]] = call ptr @llvm.invariant.start.p0(i64 1, ptr [[P]])130; NO_ASSUME-NEXT: store i8 60, ptr [[P]], align 1131; NO_ASSUME-NEXT: ret void132;133; USE_ASSUME-LABEL: define {{[^@]+}}@test_bypass3134; USE_ASSUME-SAME: (ptr [[P:%.*]]) {135; USE_ASSUME-NEXT: [[I:%.*]] = call ptr @llvm.invariant.start.p0(i64 1, ptr [[P]])136; USE_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(ptr [[P]], i64 1), "nonnull"(ptr [[P]]) ]137; USE_ASSUME-NEXT: store i8 60, ptr [[P]], align 1138; USE_ASSUME-NEXT: ret void139;140 141 store i8 50, ptr %P142 %i = call ptr @llvm.invariant.start.p0(i64 1, ptr %P)143 store i8 60, ptr %P144 ret void145}146 147 148; FIXME: Now the first store can actually be eliminated, since there is no read within149; the invariant region, between start and end.150define void @test_bypass4(ptr %P) {151; CHECK-LABEL: define {{[^@]+}}@test_bypass4152; CHECK-SAME: (ptr [[P:%.*]]) {153; CHECK-NEXT: store i8 50, ptr [[P]], align 1154; CHECK-NEXT: [[I:%.*]] = call ptr @llvm.invariant.start.p0(i64 1, ptr [[P]])155; CHECK-NEXT: call void @llvm.invariant.end.p0(ptr [[I]], i64 1, ptr [[P]])156; CHECK-NEXT: store i8 60, ptr [[P]], align 1157; CHECK-NEXT: ret void158;159 160 161 store i8 50, ptr %P162 %i = call ptr @llvm.invariant.start.p0(i64 1, ptr %P)163 call void @llvm.invariant.end.p0(ptr %i, i64 1, ptr %P)164 store i8 60, ptr %P165 ret void166}167 168 169declare void @clobber()170 171define i32 @test_before_load(ptr %p) {172; NO_ASSUME-LABEL: define {{[^@]+}}@test_before_load173; NO_ASSUME-SAME: (ptr [[P:%.*]]) {174; NO_ASSUME-NEXT: [[TMP1:%.*]] = call ptr @llvm.invariant.start.p0(i64 4, ptr [[P]])175; NO_ASSUME-NEXT: [[V1:%.*]] = load i32, ptr [[P]], align 4176; NO_ASSUME-NEXT: call void @clobber()177; NO_ASSUME-NEXT: ret i32 0178;179; USE_ASSUME-LABEL: define {{[^@]+}}@test_before_load180; USE_ASSUME-SAME: (ptr [[P:%.*]]) {181; USE_ASSUME-NEXT: [[TMP1:%.*]] = call ptr @llvm.invariant.start.p0(i64 4, ptr [[P]])182; USE_ASSUME-NEXT: [[V1:%.*]] = load i32, ptr [[P]], align 4183; USE_ASSUME-NEXT: call void @clobber()184; USE_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(ptr [[P]], i64 4), "nonnull"(ptr [[P]]), "align"(ptr [[P]], i64 4) ]185; USE_ASSUME-NEXT: ret i32 0186;187 call ptr @llvm.invariant.start.p0(i64 4, ptr %p)188 %v1 = load i32, ptr %p189 call void @clobber()190 %v2 = load i32, ptr %p191 %sub = sub i32 %v1, %v2192 ret i32 %sub193}194 195define i32 @test_before_clobber(ptr %p) {196; NO_ASSUME-LABEL: define {{[^@]+}}@test_before_clobber197; NO_ASSUME-SAME: (ptr [[P:%.*]]) {198; NO_ASSUME-NEXT: [[V1:%.*]] = load i32, ptr [[P]], align 4199; NO_ASSUME-NEXT: [[TMP1:%.*]] = call ptr @llvm.invariant.start.p0(i64 4, ptr [[P]])200; NO_ASSUME-NEXT: call void @clobber()201; NO_ASSUME-NEXT: ret i32 0202;203; USE_ASSUME-LABEL: define {{[^@]+}}@test_before_clobber204; USE_ASSUME-SAME: (ptr [[P:%.*]]) {205; USE_ASSUME-NEXT: [[V1:%.*]] = load i32, ptr [[P]], align 4206; USE_ASSUME-NEXT: [[TMP1:%.*]] = call ptr @llvm.invariant.start.p0(i64 4, ptr [[P]])207; USE_ASSUME-NEXT: call void @clobber()208; USE_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(ptr [[P]], i64 4), "nonnull"(ptr [[P]]), "align"(ptr [[P]], i64 4) ]209; USE_ASSUME-NEXT: ret i32 0210;211 %v1 = load i32, ptr %p212 call ptr @llvm.invariant.start.p0(i64 4, ptr %p)213 call void @clobber()214 %v2 = load i32, ptr %p215 %sub = sub i32 %v1, %v2216 ret i32 %sub217}218 219define i32 @test_duplicate_scope(ptr %p) {220; NO_ASSUME-LABEL: define {{[^@]+}}@test_duplicate_scope221; NO_ASSUME-SAME: (ptr [[P:%.*]]) {222; NO_ASSUME-NEXT: [[V1:%.*]] = load i32, ptr [[P]], align 4223; NO_ASSUME-NEXT: [[TMP1:%.*]] = call ptr @llvm.invariant.start.p0(i64 4, ptr [[P]])224; NO_ASSUME-NEXT: call void @clobber()225; NO_ASSUME-NEXT: [[TMP2:%.*]] = call ptr @llvm.invariant.start.p0(i64 4, ptr [[P]])226; NO_ASSUME-NEXT: ret i32 0227;228; USE_ASSUME-LABEL: define {{[^@]+}}@test_duplicate_scope229; USE_ASSUME-SAME: (ptr [[P:%.*]]) {230; USE_ASSUME-NEXT: [[V1:%.*]] = load i32, ptr [[P]], align 4231; USE_ASSUME-NEXT: [[TMP1:%.*]] = call ptr @llvm.invariant.start.p0(i64 4, ptr [[P]])232; USE_ASSUME-NEXT: call void @clobber()233; USE_ASSUME-NEXT: [[TMP2:%.*]] = call ptr @llvm.invariant.start.p0(i64 4, ptr [[P]])234; USE_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(ptr [[P]], i64 4), "nonnull"(ptr [[P]]), "align"(ptr [[P]], i64 4) ]235; USE_ASSUME-NEXT: ret i32 0236;237 %v1 = load i32, ptr %p238 call ptr @llvm.invariant.start.p0(i64 4, ptr %p)239 call void @clobber()240 call ptr @llvm.invariant.start.p0(i64 4, ptr %p)241 %v2 = load i32, ptr %p242 %sub = sub i32 %v1, %v2243 ret i32 %sub244}245 246define i32 @test_unanalzyable_load(ptr %p) {247; NO_ASSUME-LABEL: define {{[^@]+}}@test_unanalzyable_load248; NO_ASSUME-SAME: (ptr [[P:%.*]]) {249; NO_ASSUME-NEXT: [[TMP1:%.*]] = call ptr @llvm.invariant.start.p0(i64 4, ptr [[P]])250; NO_ASSUME-NEXT: call void @clobber()251; NO_ASSUME-NEXT: [[V1:%.*]] = load i32, ptr [[P]], align 4252; NO_ASSUME-NEXT: call void @clobber()253; NO_ASSUME-NEXT: ret i32 0254;255; USE_ASSUME-LABEL: define {{[^@]+}}@test_unanalzyable_load256; USE_ASSUME-SAME: (ptr [[P:%.*]]) {257; USE_ASSUME-NEXT: [[TMP1:%.*]] = call ptr @llvm.invariant.start.p0(i64 4, ptr [[P]])258; USE_ASSUME-NEXT: call void @clobber()259; USE_ASSUME-NEXT: [[V1:%.*]] = load i32, ptr [[P]], align 4260; USE_ASSUME-NEXT: call void @clobber()261; USE_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(ptr [[P]], i64 4), "nonnull"(ptr [[P]]), "align"(ptr [[P]], i64 4) ]262; USE_ASSUME-NEXT: ret i32 0263;264 call ptr @llvm.invariant.start.p0(i64 4, ptr %p)265 call void @clobber()266 %v1 = load i32, ptr %p267 call void @clobber()268 %v2 = load i32, ptr %p269 %sub = sub i32 %v1, %v2270 ret i32 %sub271}272 273define i32 @test_negative_after_clobber(ptr %p) {274; CHECK-LABEL: define {{[^@]+}}@test_negative_after_clobber275; CHECK-SAME: (ptr [[P:%.*]]) {276; CHECK-NEXT: [[V1:%.*]] = load i32, ptr [[P]], align 4277; CHECK-NEXT: call void @clobber()278; CHECK-NEXT: [[TMP1:%.*]] = call ptr @llvm.invariant.start.p0(i64 4, ptr [[P]])279; CHECK-NEXT: [[V2:%.*]] = load i32, ptr [[P]], align 4280; CHECK-NEXT: [[SUB:%.*]] = sub i32 [[V1]], [[V2]]281; CHECK-NEXT: ret i32 [[SUB]]282;283 %v1 = load i32, ptr %p284 call void @clobber()285 call ptr @llvm.invariant.start.p0(i64 4, ptr %p)286 %v2 = load i32, ptr %p287 %sub = sub i32 %v1, %v2288 ret i32 %sub289}290 291define i32 @test_merge(ptr %p, i1 %cnd) {292; NO_ASSUME-LABEL: define {{[^@]+}}@test_merge293; NO_ASSUME-SAME: (ptr [[P:%.*]], i1 [[CND:%.*]]) {294; NO_ASSUME-NEXT: [[V1:%.*]] = load i32, ptr [[P]], align 4295; NO_ASSUME-NEXT: [[TMP1:%.*]] = call ptr @llvm.invariant.start.p0(i64 4, ptr [[P]])296; NO_ASSUME-NEXT: br i1 [[CND]], label [[MERGE:%.*]], label [[TAKEN:%.*]]297; NO_ASSUME: taken:298; NO_ASSUME-NEXT: call void @clobber()299; NO_ASSUME-NEXT: br label [[MERGE]]300; NO_ASSUME: merge:301; NO_ASSUME-NEXT: ret i32 0302;303; USE_ASSUME-LABEL: define {{[^@]+}}@test_merge304; USE_ASSUME-SAME: (ptr [[P:%.*]], i1 [[CND:%.*]]) {305; USE_ASSUME-NEXT: [[V1:%.*]] = load i32, ptr [[P]], align 4306; USE_ASSUME-NEXT: [[TMP1:%.*]] = call ptr @llvm.invariant.start.p0(i64 4, ptr [[P]])307; USE_ASSUME-NEXT: br i1 [[CND]], label [[MERGE:%.*]], label [[TAKEN:%.*]]308; USE_ASSUME: taken:309; USE_ASSUME-NEXT: call void @clobber()310; USE_ASSUME-NEXT: br label [[MERGE]]311; USE_ASSUME: merge:312; USE_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(ptr [[P]], i64 4), "nonnull"(ptr [[P]]), "align"(ptr [[P]], i64 4) ]313; USE_ASSUME-NEXT: ret i32 0314;315 %v1 = load i32, ptr %p316 call ptr @llvm.invariant.start.p0(i64 4, ptr %p)317 br i1 %cnd, label %merge, label %taken318 319taken:320 call void @clobber()321 br label %merge322merge:323 %v2 = load i32, ptr %p324 %sub = sub i32 %v1, %v2325 ret i32 %sub326}327 328define i32 @test_negative_after_mergeclobber(ptr %p, i1 %cnd) {329; CHECK-LABEL: define {{[^@]+}}@test_negative_after_mergeclobber330; CHECK-SAME: (ptr [[P:%.*]], i1 [[CND:%.*]]) {331; CHECK-NEXT: [[V1:%.*]] = load i32, ptr [[P]], align 4332; CHECK-NEXT: br i1 [[CND]], label [[MERGE:%.*]], label [[TAKEN:%.*]]333; CHECK: taken:334; CHECK-NEXT: call void @clobber()335; CHECK-NEXT: br label [[MERGE]]336; CHECK: merge:337; CHECK-NEXT: [[TMP1:%.*]] = call ptr @llvm.invariant.start.p0(i64 4, ptr [[P]])338; CHECK-NEXT: [[V2:%.*]] = load i32, ptr [[P]], align 4339; CHECK-NEXT: [[SUB:%.*]] = sub i32 [[V1]], [[V2]]340; CHECK-NEXT: ret i32 [[SUB]]341;342 %v1 = load i32, ptr %p343 br i1 %cnd, label %merge, label %taken344 345taken:346 call void @clobber()347 br label %merge348merge:349 call ptr @llvm.invariant.start.p0(i64 4, ptr %p)350 %v2 = load i32, ptr %p351 %sub = sub i32 %v1, %v2352 ret i32 %sub353}354 355; In theory, this version could work, but earlycse is incapable of356; merging facts along distinct paths.357define i32 @test_false_negative_merge(ptr %p, i1 %cnd) {358; CHECK-LABEL: define {{[^@]+}}@test_false_negative_merge359; CHECK-SAME: (ptr [[P:%.*]], i1 [[CND:%.*]]) {360; CHECK-NEXT: [[V1:%.*]] = load i32, ptr [[P]], align 4361; CHECK-NEXT: br i1 [[CND]], label [[MERGE:%.*]], label [[TAKEN:%.*]]362; CHECK: taken:363; CHECK-NEXT: [[TMP1:%.*]] = call ptr @llvm.invariant.start.p0(i64 4, ptr [[P]])364; CHECK-NEXT: call void @clobber()365; CHECK-NEXT: br label [[MERGE]]366; CHECK: merge:367; CHECK-NEXT: [[V2:%.*]] = load i32, ptr [[P]], align 4368; CHECK-NEXT: [[SUB:%.*]] = sub i32 [[V1]], [[V2]]369; CHECK-NEXT: ret i32 [[SUB]]370;371 %v1 = load i32, ptr %p372 br i1 %cnd, label %merge, label %taken373 374taken:375 call ptr @llvm.invariant.start.p0(i64 4, ptr %p)376 call void @clobber()377 br label %merge378merge:379 %v2 = load i32, ptr %p380 %sub = sub i32 %v1, %v2381 ret i32 %sub382}383 384define i32 @test_merge_unanalyzable_load(ptr %p, i1 %cnd) {385; NO_ASSUME-LABEL: define {{[^@]+}}@test_merge_unanalyzable_load386; NO_ASSUME-SAME: (ptr [[P:%.*]], i1 [[CND:%.*]]) {387; NO_ASSUME-NEXT: [[TMP1:%.*]] = call ptr @llvm.invariant.start.p0(i64 4, ptr [[P]])388; NO_ASSUME-NEXT: call void @clobber()389; NO_ASSUME-NEXT: [[V1:%.*]] = load i32, ptr [[P]], align 4390; NO_ASSUME-NEXT: br i1 [[CND]], label [[MERGE:%.*]], label [[TAKEN:%.*]]391; NO_ASSUME: taken:392; NO_ASSUME-NEXT: call void @clobber()393; NO_ASSUME-NEXT: br label [[MERGE]]394; NO_ASSUME: merge:395; NO_ASSUME-NEXT: ret i32 0396;397; USE_ASSUME-LABEL: define {{[^@]+}}@test_merge_unanalyzable_load398; USE_ASSUME-SAME: (ptr [[P:%.*]], i1 [[CND:%.*]]) {399; USE_ASSUME-NEXT: [[TMP1:%.*]] = call ptr @llvm.invariant.start.p0(i64 4, ptr [[P]])400; USE_ASSUME-NEXT: call void @clobber()401; USE_ASSUME-NEXT: [[V1:%.*]] = load i32, ptr [[P]], align 4402; USE_ASSUME-NEXT: br i1 [[CND]], label [[MERGE:%.*]], label [[TAKEN:%.*]]403; USE_ASSUME: taken:404; USE_ASSUME-NEXT: call void @clobber()405; USE_ASSUME-NEXT: br label [[MERGE]]406; USE_ASSUME: merge:407; USE_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(ptr [[P]], i64 4), "nonnull"(ptr [[P]]), "align"(ptr [[P]], i64 4) ]408; USE_ASSUME-NEXT: ret i32 0409;410 call ptr @llvm.invariant.start.p0(i64 4, ptr %p)411 call void @clobber()412 %v1 = load i32, ptr %p413 br i1 %cnd, label %merge, label %taken414 415taken:416 call void @clobber()417 br label %merge418merge:419 %v2 = load i32, ptr %p420 %sub = sub i32 %v1, %v2421 ret i32 %sub422}423 424define void @test_dse_before_load(ptr %p, i1 %cnd) {425; NO_ASSUME-LABEL: define {{[^@]+}}@test_dse_before_load426; NO_ASSUME-SAME: (ptr [[P:%.*]], i1 [[CND:%.*]]) {427; NO_ASSUME-NEXT: [[TMP1:%.*]] = call ptr @llvm.invariant.start.p0(i64 4, ptr [[P]])428; NO_ASSUME-NEXT: [[V1:%.*]] = load i32, ptr [[P]], align 4429; NO_ASSUME-NEXT: call void @clobber()430; NO_ASSUME-NEXT: ret void431;432; USE_ASSUME-LABEL: define {{[^@]+}}@test_dse_before_load433; USE_ASSUME-SAME: (ptr [[P:%.*]], i1 [[CND:%.*]]) {434; USE_ASSUME-NEXT: [[TMP1:%.*]] = call ptr @llvm.invariant.start.p0(i64 4, ptr [[P]])435; USE_ASSUME-NEXT: [[V1:%.*]] = load i32, ptr [[P]], align 4436; USE_ASSUME-NEXT: call void @clobber()437; USE_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(ptr [[P]], i64 4), "nonnull"(ptr [[P]]), "align"(ptr [[P]], i64 4) ]438; USE_ASSUME-NEXT: ret void439;440 call ptr @llvm.invariant.start.p0(i64 4, ptr %p)441 %v1 = load i32, ptr %p442 call void @clobber()443 store i32 %v1, ptr %p444 ret void445}446 447define void @test_dse_after_load(ptr %p, i1 %cnd) {448; NO_ASSUME-LABEL: define {{[^@]+}}@test_dse_after_load449; NO_ASSUME-SAME: (ptr [[P:%.*]], i1 [[CND:%.*]]) {450; NO_ASSUME-NEXT: [[V1:%.*]] = load i32, ptr [[P]], align 4451; NO_ASSUME-NEXT: [[TMP1:%.*]] = call ptr @llvm.invariant.start.p0(i64 4, ptr [[P]])452; NO_ASSUME-NEXT: call void @clobber()453; NO_ASSUME-NEXT: ret void454;455; USE_ASSUME-LABEL: define {{[^@]+}}@test_dse_after_load456; USE_ASSUME-SAME: (ptr [[P:%.*]], i1 [[CND:%.*]]) {457; USE_ASSUME-NEXT: [[V1:%.*]] = load i32, ptr [[P]], align 4458; USE_ASSUME-NEXT: [[TMP1:%.*]] = call ptr @llvm.invariant.start.p0(i64 4, ptr [[P]])459; USE_ASSUME-NEXT: call void @clobber()460; USE_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(ptr [[P]], i64 4), "nonnull"(ptr [[P]]), "align"(ptr [[P]], i64 4) ]461; USE_ASSUME-NEXT: ret void462;463 %v1 = load i32, ptr %p464 call ptr @llvm.invariant.start.p0(i64 4, ptr %p)465 call void @clobber()466 store i32 %v1, ptr %p467 ret void468}469 470 471; In this case, we have a false negative since MemoryLocation is implicitly472; typed due to the user of a Value to represent the address. Note that other473; passes will canonicalize away the bitcasts in this example.474define i32 @test_false_negative_types(ptr %p) {475; CHECK-LABEL: define {{[^@]+}}@test_false_negative_types476; CHECK-SAME: (ptr [[P:%.*]]) {477; CHECK-NEXT: [[TMP1:%.*]] = call ptr @llvm.invariant.start.p0(i64 4, ptr [[P]])478; CHECK-NEXT: [[V1:%.*]] = load i32, ptr [[P]], align 4479; CHECK-NEXT: call void @clobber()480; CHECK-NEXT: [[V2F:%.*]] = load float, ptr [[P]], align 4481; CHECK-NEXT: [[V2:%.*]] = bitcast float [[V2F]] to i32482; CHECK-NEXT: [[SUB:%.*]] = sub i32 [[V1]], [[V2]]483; CHECK-NEXT: ret i32 [[SUB]]484;485 call ptr @llvm.invariant.start.p0(i64 4, ptr %p)486 %v1 = load i32, ptr %p487 call void @clobber()488 %v2f = load float, ptr %p489 %v2 = bitcast float %v2f to i32490 %sub = sub i32 %v1, %v2491 ret i32 %sub492}493 494define i32 @test_negative_size1(ptr %p) {495; CHECK-LABEL: define {{[^@]+}}@test_negative_size1496; CHECK-SAME: (ptr [[P:%.*]]) {497; CHECK-NEXT: [[TMP1:%.*]] = call ptr @llvm.invariant.start.p0(i64 3, ptr [[P]])498; CHECK-NEXT: [[V1:%.*]] = load i32, ptr [[P]], align 4499; CHECK-NEXT: call void @clobber()500; CHECK-NEXT: [[V2:%.*]] = load i32, ptr [[P]], align 4501; CHECK-NEXT: [[SUB:%.*]] = sub i32 [[V1]], [[V2]]502; CHECK-NEXT: ret i32 [[SUB]]503;504 call ptr @llvm.invariant.start.p0(i64 3, ptr %p)505 %v1 = load i32, ptr %p506 call void @clobber()507 %v2 = load i32, ptr %p508 %sub = sub i32 %v1, %v2509 ret i32 %sub510}511 512define i32 @test_negative_size2(ptr %p) {513; CHECK-LABEL: define {{[^@]+}}@test_negative_size2514; CHECK-SAME: (ptr [[P:%.*]]) {515; CHECK-NEXT: [[TMP1:%.*]] = call ptr @llvm.invariant.start.p0(i64 0, ptr [[P]])516; CHECK-NEXT: [[V1:%.*]] = load i32, ptr [[P]], align 4517; CHECK-NEXT: call void @clobber()518; CHECK-NEXT: [[V2:%.*]] = load i32, ptr [[P]], align 4519; CHECK-NEXT: [[SUB:%.*]] = sub i32 [[V1]], [[V2]]520; CHECK-NEXT: ret i32 [[SUB]]521;522 call ptr @llvm.invariant.start.p0(i64 0, ptr %p)523 %v1 = load i32, ptr %p524 call void @clobber()525 %v2 = load i32, ptr %p526 %sub = sub i32 %v1, %v2527 ret i32 %sub528}529 530define i32 @test_negative_scope(ptr %p) {531; CHECK-LABEL: define {{[^@]+}}@test_negative_scope532; CHECK-SAME: (ptr [[P:%.*]]) {533; CHECK-NEXT: [[SCOPE:%.*]] = call ptr @llvm.invariant.start.p0(i64 4, ptr [[P]])534; CHECK-NEXT: call void @llvm.invariant.end.p0(ptr [[SCOPE]], i64 4, ptr [[P]])535; CHECK-NEXT: [[V1:%.*]] = load i32, ptr [[P]], align 4536; CHECK-NEXT: call void @clobber()537; CHECK-NEXT: [[V2:%.*]] = load i32, ptr [[P]], align 4538; CHECK-NEXT: [[SUB:%.*]] = sub i32 [[V1]], [[V2]]539; CHECK-NEXT: ret i32 [[SUB]]540;541 %scope = call ptr @llvm.invariant.start.p0(i64 4, ptr %p)542 call void @llvm.invariant.end.p0(ptr %scope, i64 4, ptr %p)543 %v1 = load i32, ptr %p544 call void @clobber()545 %v2 = load i32, ptr %p546 %sub = sub i32 %v1, %v2547 ret i32 %sub548}549 550define i32 @test_false_negative_scope(ptr %p) {551; CHECK-LABEL: define {{[^@]+}}@test_false_negative_scope552; CHECK-SAME: (ptr [[P:%.*]]) {553; CHECK-NEXT: [[SCOPE:%.*]] = call ptr @llvm.invariant.start.p0(i64 4, ptr [[P]])554; CHECK-NEXT: [[V1:%.*]] = load i32, ptr [[P]], align 4555; CHECK-NEXT: call void @clobber()556; CHECK-NEXT: [[V2:%.*]] = load i32, ptr [[P]], align 4557; CHECK-NEXT: call void @llvm.invariant.end.p0(ptr [[SCOPE]], i64 4, ptr [[P]])558; CHECK-NEXT: [[SUB:%.*]] = sub i32 [[V1]], [[V2]]559; CHECK-NEXT: ret i32 [[SUB]]560;561 %scope = call ptr @llvm.invariant.start.p0(i64 4, ptr %p)562 %v1 = load i32, ptr %p563 call void @clobber()564 %v2 = load i32, ptr %p565 call void @llvm.invariant.end.p0(ptr %scope, i64 4, ptr %p)566 %sub = sub i32 %v1, %v2567 ret i32 %sub568}569 570; Invariant load defact starts an invariant.start scope of the appropriate size571define i32 @test_invariant_load_scope(ptr %p) {572; NO_ASSUME-LABEL: define {{[^@]+}}@test_invariant_load_scope573; NO_ASSUME-SAME: (ptr [[P:%.*]]) {574; NO_ASSUME-NEXT: [[V1:%.*]] = load i32, ptr [[P]], align 4, !invariant.load !4575; NO_ASSUME-NEXT: call void @clobber()576; NO_ASSUME-NEXT: ret i32 0577;578; USE_ASSUME-LABEL: define {{[^@]+}}@test_invariant_load_scope579; USE_ASSUME-SAME: (ptr [[P:%.*]]) {580; USE_ASSUME-NEXT: [[V1:%.*]] = load i32, ptr [[P]], align 4, !invariant.load !4581; USE_ASSUME-NEXT: call void @clobber()582; USE_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(ptr [[P]], i64 4), "nonnull"(ptr [[P]]), "align"(ptr [[P]], i64 4) ]583; USE_ASSUME-NEXT: ret i32 0584;585 %v1 = load i32, ptr %p, !invariant.load !{}586 call void @clobber()587 %v2 = load i32, ptr %p588 %sub = sub i32 %v1, %v2589 ret i32 %sub590}591 592; USE_ASSUME: declare void @llvm.assume(i1 noundef)593 594!0 = !{!1, !1, i64 0}595!1 = !{!"float", !2, i64 0}596!2 = !{!"omnipotent char", !3, i64 0}597!3 = !{!"Simple C/C++ TBAA"}598