; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6 ; RUN: opt -passes=licm < %s -S | FileCheck %s define void @test(ptr %loc) { ; CHECK-LABEL: define void @test( ; CHECK-SAME: ptr [[LOC:%.*]]) { ; CHECK-NEXT: [[ENTRY:.*]]: ; CHECK-NEXT: store i32 0, ptr [[LOC]], align 4 ; CHECK-NEXT: br label %[[LOOP:.*]] ; CHECK: [[LOOP]]: ; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP]] ] ; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], 1 ; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[IV]], 200 ; CHECK-NEXT: br i1 [[CMP]], label %[[LOOP]], label %[[EXIT:.*]] ; CHECK: [[EXIT]]: ; CHECK-NEXT: ret void ; entry: br label %loop loop: %iv = phi i32 [0, %entry], [%iv.next, %loop] store i32 0, ptr %loc %iv.next = add i32 %iv, 1 %cmp = icmp slt i32 %iv, 200 br i1 %cmp, label %loop, label %exit exit: ret void } define void @test_multiexit(ptr %loc, i1 %earlycnd) { ; CHECK-LABEL: define void @test_multiexit( ; CHECK-SAME: ptr [[LOC:%.*]], i1 [[EARLYCND:%.*]]) { ; CHECK-NEXT: [[ENTRY:.*]]: ; CHECK-NEXT: store i32 0, ptr [[LOC]], align 4 ; CHECK-NEXT: br label %[[LOOP:.*]] ; CHECK: [[LOOP]]: ; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[BACKEDGE:.*]] ] ; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], 1 ; CHECK-NEXT: br i1 [[EARLYCND]], label %[[EXIT1:.*]], label %[[BACKEDGE]] ; CHECK: [[BACKEDGE]]: ; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[IV]], 200 ; CHECK-NEXT: br i1 [[CMP]], label %[[LOOP]], label %[[EXIT2:.*]] ; CHECK: [[EXIT1]]: ; CHECK-NEXT: ret void ; CHECK: [[EXIT2]]: ; CHECK-NEXT: ret void ; entry: br label %loop loop: %iv = phi i32 [0, %entry], [%iv.next, %backedge] store i32 0, ptr %loc %iv.next = add i32 %iv, 1 br i1 %earlycnd, label %exit1, label %backedge backedge: %cmp = icmp slt i32 %iv, 200 br i1 %cmp, label %loop, label %exit2 exit1: ret void exit2: ret void } define ptr @false_negative_2use(ptr %loc) { ; CHECK-LABEL: define ptr @false_negative_2use( ; CHECK-SAME: ptr [[LOC:%.*]]) { ; CHECK-NEXT: [[ENTRY:.*]]: ; CHECK-NEXT: store i32 0, ptr [[LOC]], align 4 ; CHECK-NEXT: br label %[[LOOP:.*]] ; CHECK: [[LOOP]]: ; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP]] ] ; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], 1 ; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[IV]], 200 ; CHECK-NEXT: br i1 [[CMP]], label %[[LOOP]], label %[[EXIT:.*]] ; CHECK: [[EXIT]]: ; CHECK-NEXT: ret ptr [[LOC]] ; entry: br label %loop loop: %iv = phi i32 [0, %entry], [%iv.next, %loop] store i32 0, ptr %loc %iv.next = add i32 %iv, 1 %cmp = icmp slt i32 %iv, 200 br i1 %cmp, label %loop, label %exit exit: ret ptr %loc } define void @neg_lv_value(ptr %loc) { ; CHECK-LABEL: define void @neg_lv_value( ; CHECK-SAME: ptr [[LOC:%.*]]) { ; CHECK-NEXT: [[ENTRY:.*]]: ; CHECK-NEXT: br label %[[LOOP:.*]] ; CHECK: [[LOOP]]: ; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP]] ] ; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], 1 ; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[IV]], 200 ; CHECK-NEXT: br i1 [[CMP]], label %[[LOOP]], label %[[EXIT:.*]] ; CHECK: [[EXIT]]: ; CHECK-NEXT: [[IV_LCSSA:%.*]] = phi i32 [ [[IV]], %[[LOOP]] ] ; CHECK-NEXT: store i32 [[IV_LCSSA]], ptr [[LOC]], align 4 ; CHECK-NEXT: ret void ; entry: br label %loop loop: %iv = phi i32 [0, %entry], [%iv.next, %loop] store i32 %iv, ptr %loc %iv.next = add i32 %iv, 1 %cmp = icmp slt i32 %iv, 200 br i1 %cmp, label %loop, label %exit exit: ret void } define void @neg_lv_addr(ptr %loc) { ; CHECK-LABEL: define void @neg_lv_addr( ; CHECK-SAME: ptr [[LOC:%.*]]) { ; CHECK-NEXT: [[ENTRY:.*]]: ; CHECK-NEXT: br label %[[LOOP:.*]] ; CHECK: [[LOOP]]: ; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP]] ] ; CHECK-NEXT: [[P:%.*]] = getelementptr i32, ptr [[LOC]], i32 [[IV]] ; CHECK-NEXT: store i32 0, ptr [[P]], align 4 ; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], 1 ; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[IV]], 200 ; CHECK-NEXT: br i1 [[CMP]], label %[[LOOP]], label %[[EXIT:.*]] ; CHECK: [[EXIT]]: ; CHECK-NEXT: ret void ; entry: br label %loop loop: %iv = phi i32 [0, %entry], [%iv.next, %loop] %p = getelementptr i32, ptr %loc, i32 %iv store i32 0, ptr %p %iv.next = add i32 %iv, 1 %cmp = icmp slt i32 %iv, 200 br i1 %cmp, label %loop, label %exit exit: ret void } define void @neg_mod(ptr %loc) { ; CHECK-LABEL: define void @neg_mod( ; CHECK-SAME: ptr [[LOC:%.*]]) { ; CHECK-NEXT: [[ENTRY:.*]]: ; CHECK-NEXT: br label %[[LOOP:.*]] ; CHECK: [[LOOP]]: ; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP]] ] ; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], 1 ; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[IV]], 200 ; CHECK-NEXT: br i1 [[CMP]], label %[[LOOP]], label %[[EXIT:.*]] ; CHECK: [[EXIT]]: ; CHECK-NEXT: [[IV_LCSSA:%.*]] = phi i32 [ [[IV]], %[[LOOP]] ] ; CHECK-NEXT: store i32 [[IV_LCSSA]], ptr [[LOC]], align 4 ; CHECK-NEXT: ret void ; entry: br label %loop loop: %iv = phi i32 [0, %entry], [%iv.next, %loop] store i32 0, ptr %loc store i32 %iv, ptr %loc %iv.next = add i32 %iv, 1 %cmp = icmp slt i32 %iv, 200 br i1 %cmp, label %loop, label %exit exit: ret void } ; Hoisting the store is actually valid here, as it dominates the load. define void @ref(ptr %loc) { ; CHECK-LABEL: define void @ref( ; CHECK-SAME: ptr [[LOC:%.*]]) { ; CHECK-NEXT: [[ENTRY:.*]]: ; CHECK-NEXT: store i32 0, ptr [[LOC]], align 4 ; CHECK-NEXT: [[V:%.*]] = load i32, ptr [[LOC]], align 4 ; CHECK-NEXT: [[EARLYCND:%.*]] = icmp eq i32 [[V]], 198 ; CHECK-NEXT: br label %[[LOOP:.*]] ; CHECK: [[LOOP]]: ; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[BACKEDGE:.*]] ] ; CHECK-NEXT: br i1 [[EARLYCND]], label %[[EXIT1:.*]], label %[[BACKEDGE]] ; CHECK: [[BACKEDGE]]: ; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], 1 ; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[IV]], 200 ; CHECK-NEXT: br i1 [[CMP]], label %[[LOOP]], label %[[EXIT2:.*]] ; CHECK: [[EXIT1]]: ; CHECK-NEXT: ret void ; CHECK: [[EXIT2]]: ; CHECK-NEXT: ret void ; entry: br label %loop loop: %iv = phi i32 [0, %entry], [%iv.next, %backedge] store i32 0, ptr %loc %v = load i32, ptr %loc %earlycnd = icmp eq i32 %v, 198 br i1 %earlycnd, label %exit1, label %backedge backedge: %iv.next = add i32 %iv, 1 %cmp = icmp slt i32 %iv, 200 br i1 %cmp, label %loop, label %exit2 exit1: ret void exit2: ret void } ; Hoisting the store here leads to a miscompile. define void @neg_ref2(ptr %loc) { ; CHECK-LABEL: define void @neg_ref2( ; CHECK-SAME: ptr [[LOC:%.*]]) { ; CHECK-NEXT: [[ENTRY:.*]]: ; CHECK-NEXT: store i32 198, ptr [[LOC]], align 4 ; CHECK-NEXT: [[LOC_PROMOTED:%.*]] = load i32, ptr [[LOC]], align 4 ; CHECK-NEXT: br label %[[LOOP:.*]] ; CHECK: [[LOOP]]: ; CHECK-NEXT: [[TMP0:%.*]] = phi i32 [ [[LOC_PROMOTED]], %[[ENTRY]] ], [ 0, %[[BACKEDGE:.*]] ] ; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[BACKEDGE]] ] ; CHECK-NEXT: [[EARLYCND:%.*]] = icmp eq i32 [[TMP0]], 198 ; CHECK-NEXT: br i1 [[EARLYCND]], label %[[EXIT1:.*]], label %[[BACKEDGE]] ; CHECK: [[BACKEDGE]]: ; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], 1 ; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[IV]], 200 ; CHECK-NEXT: br i1 [[CMP]], label %[[LOOP]], label %[[EXIT2:.*]] ; CHECK: [[EXIT1]]: ; CHECK-NEXT: store i32 0, ptr [[LOC]], align 4 ; CHECK-NEXT: ret void ; CHECK: [[EXIT2]]: ; CHECK-NEXT: store i32 0, ptr [[LOC]], align 4 ; CHECK-NEXT: ret void ; entry: store i32 198, ptr %loc br label %loop loop: %iv = phi i32 [0, %entry], [%iv.next, %backedge] %v = load i32, ptr %loc store i32 0, ptr %loc %earlycnd = icmp eq i32 %v, 198 br i1 %earlycnd, label %exit1, label %backedge backedge: %iv.next = add i32 %iv, 1 %cmp = icmp slt i32 %iv, 200 br i1 %cmp, label %loop, label %exit2 exit1: ret void exit2: ret void } declare void @modref() define void @neg_modref(ptr %loc) { ; CHECK-LABEL: define void @neg_modref( ; CHECK-SAME: ptr [[LOC:%.*]]) { ; CHECK-NEXT: [[ENTRY:.*]]: ; CHECK-NEXT: br label %[[LOOP:.*]] ; CHECK: [[LOOP]]: ; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP]] ] ; CHECK-NEXT: store i32 0, ptr [[LOC]], align 4 ; CHECK-NEXT: call void @modref() ; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], 1 ; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[IV]], 200 ; CHECK-NEXT: br i1 [[CMP]], label %[[LOOP]], label %[[EXIT:.*]] ; CHECK: [[EXIT]]: ; CHECK-NEXT: ret void ; entry: br label %loop loop: %iv = phi i32 [0, %entry], [%iv.next, %loop] store i32 0, ptr %loc call void @modref() %iv.next = add i32 %iv, 1 %cmp = icmp slt i32 %iv, 200 br i1 %cmp, label %loop, label %exit exit: ret void } define void @neg_fence(ptr %loc) { ; CHECK-LABEL: define void @neg_fence( ; CHECK-SAME: ptr [[LOC:%.*]]) { ; CHECK-NEXT: [[ENTRY:.*]]: ; CHECK-NEXT: br label %[[LOOP:.*]] ; CHECK: [[LOOP]]: ; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP]] ] ; CHECK-NEXT: store i32 0, ptr [[LOC]], align 4 ; CHECK-NEXT: fence seq_cst ; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], 1 ; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[IV]], 200 ; CHECK-NEXT: br i1 [[CMP]], label %[[LOOP]], label %[[EXIT:.*]] ; CHECK: [[EXIT]]: ; CHECK-NEXT: ret void ; entry: br label %loop loop: %iv = phi i32 [0, %entry], [%iv.next, %loop] store i32 0, ptr %loc fence seq_cst %iv.next = add i32 %iv, 1 %cmp = icmp slt i32 %iv, 200 br i1 %cmp, label %loop, label %exit exit: ret void } define void @neg_volatile(ptr %loc) { ; CHECK-LABEL: define void @neg_volatile( ; CHECK-SAME: ptr [[LOC:%.*]]) { ; CHECK-NEXT: [[ENTRY:.*]]: ; CHECK-NEXT: br label %[[LOOP:.*]] ; CHECK: [[LOOP]]: ; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP]] ] ; CHECK-NEXT: store volatile i32 0, ptr [[LOC]], align 4 ; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], 1 ; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[IV]], 200 ; CHECK-NEXT: br i1 [[CMP]], label %[[LOOP]], label %[[EXIT:.*]] ; CHECK: [[EXIT]]: ; CHECK-NEXT: ret void ; entry: br label %loop loop: %iv = phi i32 [0, %entry], [%iv.next, %loop] store volatile i32 0, ptr %loc %iv.next = add i32 %iv, 1 %cmp = icmp slt i32 %iv, 200 br i1 %cmp, label %loop, label %exit exit: ret void } define void @neg_release(ptr %loc) { ; CHECK-LABEL: define void @neg_release( ; CHECK-SAME: ptr [[LOC:%.*]]) { ; CHECK-NEXT: [[ENTRY:.*]]: ; CHECK-NEXT: br label %[[LOOP:.*]] ; CHECK: [[LOOP]]: ; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP]] ] ; CHECK-NEXT: store atomic i32 0, ptr [[LOC]] release, align 4 ; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], 1 ; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[IV]], 200 ; CHECK-NEXT: br i1 [[CMP]], label %[[LOOP]], label %[[EXIT:.*]] ; CHECK: [[EXIT]]: ; CHECK-NEXT: ret void ; entry: br label %loop loop: %iv = phi i32 [0, %entry], [%iv.next, %loop] store atomic i32 0, ptr %loc release, align 4 %iv.next = add i32 %iv, 1 %cmp = icmp slt i32 %iv, 200 br i1 %cmp, label %loop, label %exit exit: ret void } define void @neg_seq_cst(ptr %loc) { ; CHECK-LABEL: define void @neg_seq_cst( ; CHECK-SAME: ptr [[LOC:%.*]]) { ; CHECK-NEXT: [[ENTRY:.*]]: ; CHECK-NEXT: br label %[[LOOP:.*]] ; CHECK: [[LOOP]]: ; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP]] ] ; CHECK-NEXT: store atomic i32 0, ptr [[LOC]] seq_cst, align 4 ; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], 1 ; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[IV]], 200 ; CHECK-NEXT: br i1 [[CMP]], label %[[LOOP]], label %[[EXIT:.*]] ; CHECK: [[EXIT]]: ; CHECK-NEXT: ret void ; entry: br label %loop loop: %iv = phi i32 [0, %entry], [%iv.next, %loop] store atomic i32 0, ptr %loc seq_cst, align 4 %iv.next = add i32 %iv, 1 %cmp = icmp slt i32 %iv, 200 br i1 %cmp, label %loop, label %exit exit: ret void } declare void @maythrow() inaccessiblememonly define void @neg_early_exit(ptr %loc) { ; CHECK-LABEL: define void @neg_early_exit( ; CHECK-SAME: ptr [[LOC:%.*]]) { ; CHECK-NEXT: [[ENTRY:.*]]: ; CHECK-NEXT: [[IS_NULL:%.*]] = icmp eq ptr [[LOC]], null ; CHECK-NEXT: br label %[[LOOP:.*]] ; CHECK: [[LOOP]]: ; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[BODY:.*]] ] ; CHECK-NEXT: br i1 [[IS_NULL]], label %[[EXIT:.*]], label %[[BODY]] ; CHECK: [[BODY]]: ; CHECK-NEXT: call void @maythrow() ; CHECK-NEXT: store i32 0, ptr [[LOC]], align 4 ; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], 1 ; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[IV]], 200 ; CHECK-NEXT: br i1 [[CMP]], label %[[LOOP]], label %[[EXIT]] ; CHECK: [[EXIT]]: ; CHECK-NEXT: ret void ; entry: br label %loop loop: %iv = phi i32 [0, %entry], [%iv.next, %body] %is_null = icmp eq ptr %loc, null br i1 %is_null, label %exit, label %body body: call void @maythrow() store i32 0, ptr %loc %iv.next = add i32 %iv, 1 %cmp = icmp slt i32 %iv, 200 br i1 %cmp, label %loop, label %exit exit: ret void } define void @neg_early_throw(ptr %loc) { ; CHECK-LABEL: define void @neg_early_throw( ; CHECK-SAME: ptr [[LOC:%.*]]) { ; CHECK-NEXT: [[ENTRY:.*]]: ; CHECK-NEXT: br label %[[LOOP:.*]] ; CHECK: [[LOOP]]: ; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP]] ] ; CHECK-NEXT: call void @maythrow() ; CHECK-NEXT: store i32 0, ptr [[LOC]], align 4 ; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], 1 ; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[IV]], 200 ; CHECK-NEXT: br i1 [[CMP]], label %[[LOOP]], label %[[EXIT:.*]] ; CHECK: [[EXIT]]: ; CHECK-NEXT: ret void ; entry: br label %loop loop: %iv = phi i32 [0, %entry], [%iv.next, %loop] call void @maythrow() store i32 0, ptr %loc %iv.next = add i32 %iv, 1 %cmp = icmp slt i32 %iv, 200 br i1 %cmp, label %loop, label %exit exit: ret void } define void @test_late_throw(ptr %loc) { ; CHECK-LABEL: define void @test_late_throw( ; CHECK-SAME: ptr [[LOC:%.*]]) { ; CHECK-NEXT: [[ENTRY:.*]]: ; CHECK-NEXT: store i32 0, ptr [[LOC]], align 4 ; CHECK-NEXT: br label %[[LOOP:.*]] ; CHECK: [[LOOP]]: ; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP]] ] ; CHECK-NEXT: call void @maythrow() ; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], 1 ; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[IV]], 200 ; CHECK-NEXT: br i1 [[CMP]], label %[[LOOP]], label %[[EXIT:.*]] ; CHECK: [[EXIT]]: ; CHECK-NEXT: ret void ; entry: br label %loop loop: %iv = phi i32 [0, %entry], [%iv.next, %loop] store i32 0, ptr %loc call void @maythrow() %iv.next = add i32 %iv, 1 %cmp = icmp slt i32 %iv, 200 br i1 %cmp, label %loop, label %exit exit: ret void } ; TODO: could validly hoist the store here since we know what value ; the load must observe. define i32 @test_dominated_read(ptr %loc) { ; CHECK-LABEL: define i32 @test_dominated_read( ; CHECK-SAME: ptr [[LOC:%.*]]) { ; CHECK-NEXT: [[ENTRY:.*]]: ; CHECK-NEXT: store i32 0, ptr [[LOC]], align 4 ; CHECK-NEXT: br label %[[LOOP:.*]] ; CHECK: [[LOOP]]: ; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP]] ] ; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], 1 ; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[IV]], 200 ; CHECK-NEXT: br i1 [[CMP]], label %[[LOOP]], label %[[EXIT:.*]] ; CHECK: [[EXIT]]: ; CHECK-NEXT: [[RELOAD_LE:%.*]] = load i32, ptr [[LOC]], align 4 ; CHECK-NEXT: ret i32 [[RELOAD_LE]] ; entry: br label %loop loop: %iv = phi i32 [0, %entry], [%iv.next, %loop] store i32 0, ptr %loc %reload = load i32, ptr %loc %iv.next = add i32 %iv, 1 %cmp = icmp slt i32 %iv, 200 br i1 %cmp, label %loop, label %exit exit: ret i32 %reload } ; TODO: could validly hoist the store since we already hoisted the load and ; it's no longer in the loop. define i32 @test_dominating_read(ptr %loc) { ; CHECK-LABEL: define i32 @test_dominating_read( ; CHECK-SAME: ptr [[LOC:%.*]]) { ; CHECK-NEXT: [[ENTRY:.*]]: ; CHECK-NEXT: [[LOC_PROMOTED:%.*]] = load i32, ptr [[LOC]], align 4 ; CHECK-NEXT: br label %[[LOOP:.*]] ; CHECK: [[LOOP]]: ; CHECK-NEXT: [[TMP0:%.*]] = phi i32 [ [[LOC_PROMOTED]], %[[ENTRY]] ], [ 0, %[[LOOP]] ] ; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP]] ] ; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], 1 ; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[IV]], 200 ; CHECK-NEXT: br i1 [[CMP]], label %[[LOOP]], label %[[EXIT:.*]] ; CHECK: [[EXIT]]: ; CHECK-NEXT: [[RELOAD_LCSSA:%.*]] = phi i32 [ [[TMP0]], %[[LOOP]] ] ; CHECK-NEXT: store i32 0, ptr [[LOC]], align 4 ; CHECK-NEXT: ret i32 [[RELOAD_LCSSA]] ; entry: br label %loop loop: %iv = phi i32 [0, %entry], [%iv.next, %loop] %reload = load i32, ptr %loc store i32 0, ptr %loc %iv.next = add i32 %iv, 1 %cmp = icmp slt i32 %iv, 200 br i1 %cmp, label %loop, label %exit exit: ret i32 %reload } declare void @readonly() readonly define void @test_dominated_readonly(ptr %loc) { ; CHECK-LABEL: define void @test_dominated_readonly( ; CHECK-SAME: ptr [[LOC:%.*]]) { ; CHECK-NEXT: [[ENTRY:.*]]: ; CHECK-NEXT: store i32 0, ptr [[LOC]], align 4 ; CHECK-NEXT: br label %[[LOOP:.*]] ; CHECK: [[LOOP]]: ; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP]] ] ; CHECK-NEXT: call void @readonly() ; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], 1 ; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[IV]], 200 ; CHECK-NEXT: br i1 [[CMP]], label %[[LOOP]], label %[[EXIT:.*]] ; CHECK: [[EXIT]]: ; CHECK-NEXT: ret void ; entry: br label %loop loop: %iv = phi i32 [0, %entry], [%iv.next, %loop] store i32 0, ptr %loc call void @readonly() %iv.next = add i32 %iv, 1 %cmp = icmp slt i32 %iv, 200 br i1 %cmp, label %loop, label %exit exit: ret void } ; While technically possible to hoist the store to %loc, this runs across ; a funemental limitation of alias sets since both stores and the call are ; within the same alias set and we can't distinguish them cheaply. define void @test_aliasset_fn(ptr %loc, ptr %loc2) { ; CHECK-LABEL: define void @test_aliasset_fn( ; CHECK-SAME: ptr [[LOC:%.*]], ptr [[LOC2:%.*]]) { ; CHECK-NEXT: [[ENTRY:.*]]: ; CHECK-NEXT: br label %[[LOOP:.*]] ; CHECK: [[LOOP]]: ; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP]] ] ; CHECK-NEXT: store i32 0, ptr [[LOC]], align 4 ; CHECK-NEXT: call void @readonly() ; CHECK-NEXT: store i32 [[IV]], ptr [[LOC2]], align 4 ; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], 1 ; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[IV]], 200 ; CHECK-NEXT: br i1 [[CMP]], label %[[LOOP]], label %[[EXIT:.*]] ; CHECK: [[EXIT]]: ; CHECK-NEXT: ret void ; entry: br label %loop loop: %iv = phi i32 [0, %entry], [%iv.next, %loop] store i32 0, ptr %loc call void @readonly() store i32 %iv, ptr %loc2 %iv.next = add i32 %iv, 1 %cmp = icmp slt i32 %iv, 200 br i1 %cmp, label %loop, label %exit exit: ret void } ; If we can't tell if the value is read before the write, we can't hoist the ; write over the potential read (since we don't know the value read) define void @neg_may_read(ptr %loc, i1 %maybe) { ; CHECK-LABEL: define void @neg_may_read( ; CHECK-SAME: ptr [[LOC:%.*]], i1 [[MAYBE:%.*]]) { ; CHECK-NEXT: [[ENTRY:.*]]: ; CHECK-NEXT: br label %[[LOOP:.*]] ; CHECK: [[LOOP]]: ; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[MERGE:.*]] ] ; CHECK-NEXT: br i1 [[MAYBE]], label %[[TAKEN:.*]], label %[[MERGE]] ; CHECK: [[TAKEN]]: ; CHECK-NEXT: call void @readonly() ; CHECK-NEXT: br label %[[MERGE]] ; CHECK: [[MERGE]]: ; CHECK-NEXT: store i32 0, ptr [[LOC]], align 4 ; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], 1 ; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[IV]], 200 ; CHECK-NEXT: br i1 [[CMP]], label %[[LOOP]], label %[[EXIT:.*]] ; CHECK: [[EXIT]]: ; CHECK-NEXT: ret void ; entry: br label %loop loop: %iv = phi i32 [0, %entry], [%iv.next, %merge] ;; maybe is a placeholder for an unanalyzable condition br i1 %maybe, label %taken, label %merge taken: call void @readonly() br label %merge merge: store i32 0, ptr %loc %iv.next = add i32 %iv, 1 %cmp = icmp slt i32 %iv, 200 br i1 %cmp, label %loop, label %exit exit: ret void } @g1 = external global i8 @g2 = external global i8 ; The store should not be hoisted here, because the location is accessed by the ; preceding load. However, the clobbering memory access for that load is ; liveOnEntry, because the pointer on the backedge does not alias. As such, ; only checking whether the clobbering access is outside the loop is ; insufficient. define i8 @load_before_store_with_out_of_loop_def(i1 %c) { ; CHECK-LABEL: define i8 @load_before_store_with_out_of_loop_def( ; CHECK-SAME: i1 [[C:%.*]]) { ; CHECK-NEXT: [[ENTRY:.*]]: ; CHECK-NEXT: br label %[[LOOP:.*]] ; CHECK: [[LOOP]]: ; CHECK-NEXT: [[PHI:%.*]] = phi ptr [ @g1, %[[ENTRY]] ], [ @g2, %[[LOOP]] ] ; CHECK-NEXT: [[V:%.*]] = load i8, ptr [[PHI]], align 1 ; CHECK-NEXT: store i8 1, ptr @g1, align 1 ; CHECK-NEXT: br i1 [[C]], label %[[LOOP]], label %[[EXIT:.*]] ; CHECK: [[EXIT]]: ; CHECK-NEXT: [[V_LCSSA:%.*]] = phi i8 [ [[V]], %[[LOOP]] ] ; CHECK-NEXT: ret i8 [[V_LCSSA]] ; entry: br label %loop loop: %phi = phi ptr [ @g1, %entry ], [ @g2, %loop ] %v = load i8, ptr %phi store i8 1, ptr @g1 br i1 %c, label %loop, label %exit exit: ret i8 %v }