| ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5 | |
| ; RUN: opt -S -passes=licm %s | FileCheck %s | |
| ; RUN: opt -aa-pipeline=basic-aa -passes='require<aa>,require<target-ir>,require<scalar-evolution>,require<opt-remark-emit>,loop-mssa(licm)' < %s -S | FileCheck %s | |
| declare i32 @load(ptr %p) memory(argmem: read) nounwind willreturn | |
| define void @test_load(ptr noalias %loc, ptr noalias %sink) { | |
| ; CHECK-LABEL: define void @test_load( | |
| ; CHECK-SAME: ptr noalias [[LOC:%.*]], ptr noalias [[SINK:%.*]]) { | |
| ; CHECK-NEXT: [[ENTRY:.*]]: | |
| ; CHECK-NEXT: [[RET:%.*]] = call i32 @load(ptr [[LOC]]) | |
| ; CHECK-NEXT: br label %[[LOOP:.*]] | |
| ; CHECK: [[LOOP]]: | |
| ; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP]] ] | |
| ; CHECK-NEXT: store volatile i32 [[RET]], ptr [[SINK]], 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] | |
| %ret = call i32 @load(ptr %loc) | |
| store volatile i32 %ret, ptr %sink | |
| %iv.next = add i32 %iv, 1 | |
| %cmp = icmp slt i32 %iv, 200 | |
| br i1 %cmp, label %loop, label %exit | |
| exit: | |
| ret void | |
| } | |
| declare i32 @spec(ptr %p, ptr %q) readonly argmemonly nounwind speculatable | |
| ; We should strip the dereferenceable callsite attribute on spec call's argument since it is | |
| ; can cause UB in the speculatable call when hoisted to preheader. | |
| ; However, we need not strip the nonnull attribute since it just propagates | |
| ; poison if the parameter was indeed null. | |
| define void @test_strip_attribute(ptr noalias %loc, ptr noalias %sink, ptr %q) { | |
| ; CHECK-LABEL: define void @test_strip_attribute( | |
| ; CHECK-SAME: ptr noalias [[LOC:%.*]], ptr noalias [[SINK:%.*]], ptr [[Q:%.*]]) { | |
| ; CHECK-NEXT: [[ENTRY:.*]]: | |
| ; CHECK-NEXT: [[RET:%.*]] = call i32 @load(ptr [[LOC]]) | |
| ; CHECK-NEXT: [[NULLCHK:%.*]] = icmp eq ptr [[Q]], null | |
| ; CHECK-NEXT: [[RET2:%.*]] = call i32 @spec(ptr nonnull [[Q]], ptr [[LOC]]) | |
| ; CHECK-NEXT: br label %[[LOOP:.*]] | |
| ; CHECK: [[LOOP]]: | |
| ; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[ISNULL:.*]] ] | |
| ; CHECK-NEXT: br i1 [[NULLCHK]], label %[[ISNULL]], label %[[NONNULLBB:.*]] | |
| ; CHECK: [[NONNULLBB]]: | |
| ; CHECK-NEXT: br label %[[ISNULL]] | |
| ; CHECK: [[ISNULL]]: | |
| ; CHECK-NEXT: store volatile i32 [[RET]], ptr [[SINK]], 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, %isnull ] | |
| %ret = call i32 @load(ptr %loc) | |
| %nullchk = icmp eq ptr %q, null | |
| br i1 %nullchk, label %isnull, label %nonnullbb | |
| nonnullbb: | |
| %ret2 = call i32 @spec(ptr nonnull %q, ptr dereferenceable(12) %loc) | |
| br label %isnull | |
| isnull: | |
| store volatile i32 %ret, ptr %sink | |
| %iv.next = add i32 %iv, 1 | |
| %cmp = icmp slt i32 %iv, 200 | |
| br i1 %cmp, label %loop, label %exit | |
| exit: | |
| ret void | |
| } | |
| declare i32 @load_not_argmemonly() readonly nounwind willreturn | |
| define void @test_load_not_argmemonly(ptr noalias %sink) { | |
| ; CHECK-LABEL: define void @test_load_not_argmemonly( | |
| ; CHECK-SAME: ptr noalias [[SINK:%.*]]) { | |
| ; CHECK-NEXT: [[ENTRY:.*]]: | |
| ; CHECK-NEXT: [[RET:%.*]] = call i32 @load_not_argmemonly() | |
| ; CHECK-NEXT: store i32 [[RET]], ptr [[SINK]], 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] | |
| %ret = call i32 @load_not_argmemonly() | |
| store i32 %ret, ptr %sink | |
| %iv.next = add i32 %iv, 1 | |
| %cmp = icmp slt i32 %iv, 200 | |
| br i1 %cmp, label %loop, label %exit | |
| exit: | |
| ret void | |
| } | |
| declare void @store(i32 %val, ptr %p) argmemonly writeonly nounwind | |
| ; loop invariant calls to writeonly functions such as the above | |
| ; should be hoisted | |
| define void @test(ptr %loc) { | |
| ; CHECK-LABEL: define void @test( | |
| ; CHECK-SAME: ptr [[LOC:%.*]]) { | |
| ; CHECK-NEXT: [[ENTRY:.*]]: | |
| ; CHECK-NEXT: call void @store(i32 0, ptr [[LOC]]) | |
| ; 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] | |
| call void @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: call void @store(i32 0, ptr [[LOC]]) | |
| ; 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] | |
| call void @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 | |
| } | |
| ; cannot be hoisted because the two pointers can alias one another | |
| define void @neg_two_pointer(ptr %loc, ptr %otherloc) { | |
| ; CHECK-LABEL: define void @neg_two_pointer( | |
| ; CHECK-SAME: ptr [[LOC:%.*]], ptr [[OTHERLOC:%.*]]) { | |
| ; CHECK-NEXT: [[ENTRY:.*]]: | |
| ; CHECK-NEXT: br label %[[LOOP:.*]] | |
| ; CHECK: [[LOOP]]: | |
| ; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP]] ] | |
| ; CHECK-NEXT: call void @store(i32 0, ptr [[LOC]]) | |
| ; CHECK-NEXT: call void @store(i32 1, ptr [[OTHERLOC]]) | |
| ; 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 @store(i32 0, ptr %loc) | |
| call void @store(i32 1, ptr %otherloc) | |
| %iv.next = add i32 %iv, 1 | |
| %cmp = icmp slt i32 %iv, 200 | |
| br i1 %cmp, label %loop, label %exit | |
| exit: | |
| ret void | |
| } | |
| ; hoisted due to pointers not aliasing | |
| define void @two_pointer_noalias(ptr noalias %loc, ptr noalias %otherloc) { | |
| ; CHECK-LABEL: define void @two_pointer_noalias( | |
| ; CHECK-SAME: ptr noalias [[LOC:%.*]], ptr noalias [[OTHERLOC:%.*]]) { | |
| ; CHECK-NEXT: [[ENTRY:.*]]: | |
| ; CHECK-NEXT: call void @store(i32 0, ptr [[LOC]]) | |
| ; CHECK-NEXT: call void @store(i32 1, ptr [[OTHERLOC]]) | |
| ; 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] | |
| call void @store(i32 0, ptr %loc) | |
| call void @store(i32 1, ptr %otherloc) | |
| %iv.next = add i32 %iv, 1 | |
| %cmp = icmp slt i32 %iv, 200 | |
| br i1 %cmp, label %loop, label %exit | |
| exit: | |
| ret void | |
| } | |
| ; when there's a conflicting read, store call should not be hoisted | |
| define i32 @neg_conflicting_read(ptr noalias %loc, ptr noalias %otherloc) { | |
| ; CHECK-LABEL: define i32 @neg_conflicting_read( | |
| ; CHECK-SAME: ptr noalias [[LOC:%.*]], ptr noalias [[OTHERLOC:%.*]]) { | |
| ; CHECK-NEXT: [[ENTRY:.*]]: | |
| ; CHECK-NEXT: call void @store(i32 0, ptr [[LOC]]) | |
| ; CHECK-NEXT: br label %[[LOOP:.*]] | |
| ; CHECK: [[LOOP]]: | |
| ; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP]] ] | |
| ; CHECK-NEXT: [[VAL:%.*]] = call i32 @load(ptr [[LOC]]) | |
| ; CHECK-NEXT: call void @store(i32 0, ptr [[LOC]]) | |
| ; 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: [[VAL_LCSSA:%.*]] = phi i32 [ [[VAL]], %[[LOOP]] ] | |
| ; CHECK-NEXT: ret i32 [[VAL_LCSSA]] | |
| ; | |
| entry: | |
| call void @store(i32 0, ptr %loc) | |
| br label %loop | |
| loop: | |
| %iv = phi i32 [0, %entry], [%iv.next, %loop] | |
| %val = call i32 @load(ptr %loc) | |
| call void @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 %val | |
| } | |
| define i32 @unrelated_read(ptr noalias %loc, ptr noalias %otherloc) { | |
| ; CHECK-LABEL: define i32 @unrelated_read( | |
| ; CHECK-SAME: ptr noalias [[LOC:%.*]], ptr noalias [[OTHERLOC:%.*]]) { | |
| ; CHECK-NEXT: [[ENTRY:.*]]: | |
| ; CHECK-NEXT: call void @store(i32 0, ptr [[LOC]]) | |
| ; CHECK-NEXT: br label %[[LOOP:.*]] | |
| ; CHECK: [[LOOP]]: | |
| ; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP]] ] | |
| ; CHECK-NEXT: [[OTHERLOC_GEP:%.*]] = getelementptr i32, ptr [[OTHERLOC]], i32 [[IV]] | |
| ; CHECK-NEXT: [[VAL:%.*]] = call i32 @load(ptr [[OTHERLOC_GEP]]) | |
| ; 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: [[VAL_LCSSA:%.*]] = phi i32 [ [[VAL]], %[[LOOP]] ] | |
| ; CHECK-NEXT: ret i32 [[VAL_LCSSA]] | |
| ; | |
| entry: | |
| br label %loop | |
| loop: | |
| %iv = phi i32 [0, %entry], [%iv.next, %loop] | |
| %otherloc.gep = getelementptr i32, ptr %otherloc, i32 %iv | |
| %val = call i32 @load(ptr %otherloc.gep) | |
| call void @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 %val | |
| } | |
| 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: call void @store(i32 [[IV]], ptr [[LOC]]) | |
| ; 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 @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: call void @store(i32 0, ptr [[P]]) | |
| ; 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 | |
| call void @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: call void @store(i32 0, ptr [[LOC]]) | |
| ; CHECK-NEXT: store i32 [[IV]], 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 @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 | |
| } | |
| ;; The store can be hoisted because the its value is loop-invariant. | |
| ;; Does not depend on values modified inside the loop. | |
| define void @ref(ptr %loc) { | |
| ; CHECK-LABEL: define void @ref( | |
| ; CHECK-SAME: ptr [[LOC:%.*]]) { | |
| ; CHECK-NEXT: [[ENTRY:.*]]: | |
| ; CHECK-NEXT: call void @store(i32 0, ptr [[LOC]]) | |
| ; 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] | |
| call void @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 | |
| } | |
| 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: call void @store(i32 0, ptr [[LOC]]) | |
| ; 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] | |
| call void @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: call void @store(i32 0, ptr [[LOC]]) | |
| ; 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] | |
| call void @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 | |
| } | |
| declare void @not_nounwind(i32 %v, ptr %p) writeonly argmemonly | |
| declare void @not_argmemonly(i32 %v, ptr %p) writeonly nounwind | |
| declare void @not_writeonly(i32 %v, ptr %p) argmemonly nounwind | |
| define void @neg_not_nounwind(ptr %loc) { | |
| ; CHECK-LABEL: define void @neg_not_nounwind( | |
| ; 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 @not_nounwind(i32 0, ptr [[LOC]]) | |
| ; 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 @not_nounwind(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 | |
| } | |
| ; when the call is not argmemonly and is not the only memory access we | |
| ; do not hoist | |
| define i32 @neg_not_argmemonly(ptr %loc, ptr %loc2) { | |
| ; CHECK-LABEL: define i32 @neg_not_argmemonly( | |
| ; CHECK-SAME: ptr [[LOC:%.*]], ptr [[LOC2:%.*]]) { | |
| ; CHECK-NEXT: [[ENTRY:.*]]: | |
| ; CHECK-NEXT: call void @not_argmemonly(i32 0, ptr [[LOC]]) | |
| ; 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: [[VAL_LE:%.*]] = call i32 @load(ptr [[LOC2]]) | |
| ; CHECK-NEXT: ret i32 [[VAL_LE]] | |
| ; | |
| entry: | |
| br label %loop | |
| loop: | |
| %iv = phi i32 [0, %entry], [%iv.next, %loop] | |
| call void @not_argmemonly(i32 0, ptr %loc) | |
| %val = call i32 @load(ptr %loc2) | |
| %iv.next = add i32 %iv, 1 | |
| %cmp = icmp slt i32 %iv, 200 | |
| br i1 %cmp, label %loop, label %exit | |
| exit: | |
| ret i32 %val | |
| } | |
| ; when the call is not argmemonly and is only memory access we hoist it | |
| define void @not_argmemonly_hoisted(ptr %loc, ptr %loc2) { | |
| ; CHECK-LABEL: define void @not_argmemonly_hoisted( | |
| ; CHECK-SAME: ptr [[LOC:%.*]], ptr [[LOC2:%.*]]) { | |
| ; CHECK-NEXT: [[ENTRY:.*]]: | |
| ; CHECK-NEXT: call void @not_argmemonly(i32 0, ptr [[LOC]]) | |
| ; 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] | |
| call void @not_argmemonly(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_not_writeonly(ptr %loc) { | |
| ; CHECK-LABEL: define void @neg_not_writeonly( | |
| ; 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 @not_writeonly(i32 0, ptr [[LOC]]) | |
| ; 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 @not_writeonly(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 | |
| } | |