; 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,require,require,require,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 }