| ; 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) argmemonly readonly nounwind |
| |
| 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 void @neg_conflicting_read(ptr noalias %loc, ptr noalias %otherloc) { |
| ; CHECK-LABEL: define void @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: call void @load(i32 0, 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: ret void |
| ; |
| entry: |
| call void @store(i32 0, ptr %loc) |
| br label %loop |
| loop: |
| %iv = phi i32 [0, %entry], [%iv.next, %loop] |
| call void @load(i32 0, 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 void |
| } |
| |
| 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 |
| } |
| |
| define void @neg_ref(ptr %loc) { |
| ; CHECK-LABEL: define void @neg_ref( |
| ; CHECK-SAME: ptr [[LOC:%.*]]) { |
| ; CHECK-NEXT: [[ENTRY:.*]]: |
| ; CHECK-NEXT: br label %[[LOOP:.*]] |
| ; CHECK: [[LOOP]]: |
| ; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[BACKEDGE:.*]] ] |
| ; 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 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 void @neg_not_argmemonly(ptr %loc, ptr %loc2) { |
| ; CHECK-LABEL: define void @neg_not_argmemonly( |
| ; 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: call void @not_argmemonly(i32 0, ptr [[LOC]]) |
| ; CHECK-NEXT: call void @load(i32 0, ptr [[LOC2]]) |
| ; 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) |
| call void @load(i32 0, ptr %loc2) |
| %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 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 |
| } |
| |