mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2025-01-31 20:51:52 +01:00
86590965e8
This patch makes LICM use `ICFLoopSafetyInfo` that is a smarter version of LoopSafetyInfo that leverages power of Implicit Control Flow Tracking to keep track of throwing instructions and give less pessimistic answers to queries related to throws. The ICFLoopSafetyInfo itself has been introduced in rL344601. This patch enables it in LICM only. Differential Revision: https://reviews.llvm.org/D50377 Reviewed By: apilipenko llvm-svn: 346201
606 lines
15 KiB
LLVM
606 lines
15 KiB
LLVM
; REQUIRES: asserts
|
|
; RUN: opt -S -basicaa -licm -ipt-expensive-asserts=true < %s | FileCheck %s
|
|
; RUN: opt -aa-pipeline=basic-aa -passes='require<opt-remark-emit>,loop(licm)' -ipt-expensive-asserts=true -S %s | FileCheck %s
|
|
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
|
|
target triple = "x86_64-unknown-linux-gnu"
|
|
|
|
declare void @f() nounwind
|
|
declare void @llvm.experimental.guard(i1,...)
|
|
|
|
; constant fold on first ieration
|
|
define i32 @test1(i32* noalias nocapture readonly %a) nounwind uwtable {
|
|
; CHECK-LABEL: @test1(
|
|
entry:
|
|
; CHECK: %i1 = load i32, i32* %a, align 4
|
|
; CHECK-NEXT: br label %for.body
|
|
br label %for.body
|
|
|
|
for.body:
|
|
%iv = phi i32 [ 0, %entry ], [ %inc, %continue ]
|
|
%acc = phi i32 [ 0, %entry ], [ %add, %continue ]
|
|
%r.chk = icmp ult i32 %iv, 2000
|
|
br i1 %r.chk, label %continue, label %fail
|
|
continue:
|
|
%i1 = load i32, i32* %a, align 4
|
|
%add = add nsw i32 %i1, %acc
|
|
%inc = add nuw nsw i32 %iv, 1
|
|
%exitcond = icmp eq i32 %inc, 1000
|
|
br i1 %exitcond, label %for.cond.cleanup, label %for.body
|
|
|
|
for.cond.cleanup:
|
|
ret i32 %add
|
|
|
|
fail:
|
|
call void @f()
|
|
ret i32 -1
|
|
}
|
|
|
|
; Same as test1, but with a floating point IR and fcmp
|
|
define i32 @test_fcmp(i32* noalias nocapture readonly %a) nounwind uwtable {
|
|
; CHECK-LABEL: @test_fcmp(
|
|
entry:
|
|
; CHECK: %i1 = load i32, i32* %a, align 4
|
|
; CHECK-NEXT: br label %for.body
|
|
br label %for.body
|
|
|
|
for.body:
|
|
%iv = phi float [ 0.0, %entry ], [ %inc, %continue ]
|
|
%acc = phi i32 [ 0, %entry ], [ %add, %continue ]
|
|
%r.chk = fcmp olt float %iv, 2000.0
|
|
br i1 %r.chk, label %continue, label %fail
|
|
continue:
|
|
%i1 = load i32, i32* %a, align 4
|
|
%add = add nsw i32 %i1, %acc
|
|
%inc = fadd float %iv, 1.0
|
|
%exitcond = fcmp ogt float %inc, 1000.0
|
|
br i1 %exitcond, label %for.cond.cleanup, label %for.body
|
|
|
|
for.cond.cleanup:
|
|
ret i32 %add
|
|
|
|
fail:
|
|
call void @f()
|
|
ret i32 -1
|
|
}
|
|
|
|
; Count down from a.length w/entry guard
|
|
; TODO: currently unable to prove the following:
|
|
; ule i32 (add nsw i32 %len, -1), %len where len is [0, 512]
|
|
define i32 @test2(i32* noalias nocapture readonly %a) nounwind uwtable {
|
|
; CHECK-LABEL: @test2(
|
|
entry:
|
|
%len = load i32, i32* %a, align 4, !range !{i32 0, i32 512}
|
|
%is.non.pos = icmp eq i32 %len, 0
|
|
br i1 %is.non.pos, label %fail, label %preheader
|
|
preheader:
|
|
%lenminusone = add nsw i32 %len, -1
|
|
br label %for.body
|
|
for.body:
|
|
%iv = phi i32 [ %lenminusone, %preheader ], [ %dec, %continue ]
|
|
%acc = phi i32 [ 0, %preheader ], [ %add, %continue ]
|
|
%r.chk = icmp ule i32 %iv, %len
|
|
br i1 %r.chk, label %continue, label %fail
|
|
continue:
|
|
; CHECK-LABEL: continue
|
|
; CHECK: %i1 = load i32, i32* %a, align 4
|
|
%i1 = load i32, i32* %a, align 4
|
|
%add = add nsw i32 %i1, %acc
|
|
%dec = add nsw i32 %iv, -1
|
|
%exitcond = icmp eq i32 %dec, 0
|
|
br i1 %exitcond, label %for.cond.cleanup, label %for.body
|
|
|
|
for.cond.cleanup:
|
|
ret i32 %add
|
|
|
|
fail:
|
|
call void @f()
|
|
ret i32 -1
|
|
}
|
|
|
|
; trivially true for zero
|
|
define i32 @test3(i32* noalias nocapture readonly %a) nounwind uwtable {
|
|
; CHECK-LABEL: @test3(
|
|
entry:
|
|
%len = load i32, i32* %a, align 4, !range !{i32 0, i32 512}
|
|
%is.zero = icmp eq i32 %len, 0
|
|
br i1 %is.zero, label %fail, label %preheader
|
|
preheader:
|
|
; CHECK: %i1 = load i32, i32* %a, align 4
|
|
; CHECK-NEXT: br label %for.body
|
|
br label %for.body
|
|
for.body:
|
|
%iv = phi i32 [ 0, %preheader ], [ %inc, %continue ]
|
|
%acc = phi i32 [ 0, %preheader ], [ %add, %continue ]
|
|
%r.chk = icmp ule i32 %iv, %len
|
|
br i1 %r.chk, label %continue, label %fail
|
|
continue:
|
|
%i1 = load i32, i32* %a, align 4
|
|
%add = add nsw i32 %i1, %acc
|
|
%inc = add nuw nsw i32 %iv, 1
|
|
%exitcond = icmp eq i32 %inc, 1000
|
|
br i1 %exitcond, label %for.cond.cleanup, label %for.body
|
|
|
|
for.cond.cleanup:
|
|
ret i32 %add
|
|
|
|
fail:
|
|
call void @f()
|
|
ret i32 -1
|
|
}
|
|
|
|
; requires fact length is non-zero
|
|
; TODO: IsKnownNonNullFromDominatingConditions is currently only be done for
|
|
; pointers; should handle integers too
|
|
define i32 @test4(i32* noalias nocapture readonly %a) nounwind uwtable {
|
|
; CHECK-LABEL: @test4(
|
|
entry:
|
|
%len = load i32, i32* %a, align 4, !range !{i32 0, i32 512}
|
|
%is.zero = icmp eq i32 %len, 0
|
|
br i1 %is.zero, label %fail, label %preheader
|
|
preheader:
|
|
br label %for.body
|
|
for.body:
|
|
%iv = phi i32 [ 0, %preheader ], [ %inc, %continue ]
|
|
%acc = phi i32 [ 0, %preheader ], [ %add, %continue ]
|
|
%r.chk = icmp ult i32 %iv, %len
|
|
br i1 %r.chk, label %continue, label %fail
|
|
continue:
|
|
; CHECK-LABEL: continue
|
|
; CHECK: %i1 = load i32, i32* %a, align 4
|
|
%i1 = load i32, i32* %a, align 4
|
|
%add = add nsw i32 %i1, %acc
|
|
%inc = add nuw nsw i32 %iv, 1
|
|
%exitcond = icmp eq i32 %inc, 1000
|
|
br i1 %exitcond, label %for.cond.cleanup, label %for.body
|
|
|
|
for.cond.cleanup:
|
|
ret i32 %add
|
|
|
|
fail:
|
|
call void @f()
|
|
ret i32 -1
|
|
}
|
|
|
|
; variation on test1 with branch swapped
|
|
define i32 @test-brswap(i32* noalias nocapture readonly %a) nounwind uwtable {
|
|
; CHECK-LABEL: @test-brswap(
|
|
entry:
|
|
; CHECK: %i1 = load i32, i32* %a, align 4
|
|
; CHECK-NEXT: br label %for.body
|
|
br label %for.body
|
|
|
|
for.body:
|
|
%iv = phi i32 [ 0, %entry ], [ %inc, %continue ]
|
|
%acc = phi i32 [ 0, %entry ], [ %add, %continue ]
|
|
%r.chk = icmp ugt i32 %iv, 2000
|
|
br i1 %r.chk, label %fail, label %continue
|
|
continue:
|
|
%i1 = load i32, i32* %a, align 4
|
|
%add = add nsw i32 %i1, %acc
|
|
%inc = add nuw nsw i32 %iv, 1
|
|
%exitcond = icmp eq i32 %inc, 1000
|
|
br i1 %exitcond, label %for.cond.cleanup, label %for.body
|
|
|
|
for.cond.cleanup:
|
|
ret i32 %add
|
|
|
|
fail:
|
|
call void @f()
|
|
ret i32 -1
|
|
}
|
|
|
|
define i32 @test-nonphi(i32* noalias nocapture readonly %a) nounwind uwtable {
|
|
; CHECK-LABEL: @test-nonphi(
|
|
entry:
|
|
br label %for.body
|
|
|
|
for.body:
|
|
; CHECK-LABEL: continue
|
|
; CHECK: %i1 = load i32, i32* %a, align 4
|
|
%iv = phi i32 [ 0, %entry ], [ %inc, %continue ]
|
|
%acc = phi i32 [ 0, %entry ], [ %add, %continue ]
|
|
%xor = xor i32 %iv, 72
|
|
%r.chk = icmp ugt i32 %xor, 2000
|
|
br i1 %r.chk, label %fail, label %continue
|
|
continue:
|
|
%i1 = load i32, i32* %a, align 4
|
|
%add = add nsw i32 %i1, %acc
|
|
%inc = add nuw nsw i32 %iv, 1
|
|
%exitcond = icmp eq i32 %inc, 1000
|
|
br i1 %exitcond, label %for.cond.cleanup, label %for.body
|
|
|
|
for.cond.cleanup:
|
|
ret i32 %add
|
|
|
|
fail:
|
|
call void @f()
|
|
ret i32 -1
|
|
}
|
|
|
|
define i32 @test-wrongphi(i32* noalias nocapture readonly %a) nounwind uwtable {
|
|
; CHECK-LABEL: @test-wrongphi(
|
|
entry:
|
|
br label %for.body
|
|
|
|
for.body:
|
|
%iv = phi i32 [ 0, %entry ], [ %inc, %continue ]
|
|
%acc = phi i32 [ 0, %entry ], [ %add, %continue ]
|
|
%cond = icmp ult i32 %iv, 500
|
|
br i1 %cond, label %dummy_block1, label %dummy_block2
|
|
|
|
dummy_block1:
|
|
br label %dummy_block2
|
|
|
|
dummy_block2:
|
|
%wrongphi = phi i32 [11, %for.body], [12, %dummy_block1]
|
|
%r.chk = icmp ugt i32 %wrongphi, 2000
|
|
br i1 %r.chk, label %fail, label %continue
|
|
continue:
|
|
; CHECK-LABEL: continue
|
|
; CHECK: %i1 = load i32, i32* %a, align 4
|
|
%i1 = load i32, i32* %a, align 4
|
|
%add = add nsw i32 %i1, %acc
|
|
%inc = add nuw nsw i32 %iv, 1
|
|
%exitcond = icmp eq i32 %inc, 1000
|
|
br i1 %exitcond, label %for.cond.cleanup, label %for.body
|
|
|
|
for.cond.cleanup:
|
|
ret i32 %add
|
|
|
|
fail:
|
|
call void @f()
|
|
ret i32 -1
|
|
}
|
|
|
|
; This works because loop-simplify is run implicitly, but test for it anyways
|
|
define i32 @test-multiple-latch(i32* noalias nocapture readonly %a) nounwind uwtable {
|
|
; CHECK-LABEL: @test-multiple-latch(
|
|
entry:
|
|
; CHECK: %i1 = load i32, i32* %a, align 4
|
|
; CHECK-NEXT: br label %for.body
|
|
br label %for.body
|
|
|
|
for.body:
|
|
%iv = phi i32 [ 0, %entry ], [ %inc, %continue1 ], [ %inc, %continue2 ]
|
|
%acc = phi i32 [ 0, %entry ], [ %add, %continue1 ], [ %add, %continue2 ]
|
|
%r.chk = icmp ult i32 %iv, 2000
|
|
br i1 %r.chk, label %continue1, label %fail
|
|
continue1:
|
|
%i1 = load i32, i32* %a, align 4
|
|
%add = add nsw i32 %i1, %acc
|
|
%inc = add nuw nsw i32 %iv, 1
|
|
%cmp = icmp eq i32 %add, 0
|
|
br i1 %cmp, label %continue2, label %for.body
|
|
continue2:
|
|
%exitcond = icmp eq i32 %inc, 1000
|
|
br i1 %exitcond, label %for.cond.cleanup, label %for.body
|
|
|
|
for.cond.cleanup:
|
|
ret i32 %add
|
|
|
|
fail:
|
|
call void @f()
|
|
ret i32 -1
|
|
}
|
|
|
|
define void @test-hoisting-in-presence-of-guards(i1 %c, i32* %p) {
|
|
|
|
; CHECK-LABEL: @test-hoisting-in-presence-of-guards
|
|
; CHECK: entry:
|
|
; CHECK: %a = load i32, i32* %p
|
|
; CHECK: %invariant_cond = icmp ne i32 %a, 100
|
|
; CHECK: loop:
|
|
|
|
entry:
|
|
br label %loop
|
|
|
|
loop:
|
|
%iv = phi i32 [ 0, %entry ], [ %iv.next, %loop ]
|
|
%iv.next = add i32 %iv, 1
|
|
%a = load i32, i32* %p
|
|
%invariant_cond = icmp ne i32 %a, 100
|
|
call void (i1, ...) @llvm.experimental.guard(i1 %invariant_cond) [ "deopt"() ]
|
|
%loop_cond = icmp slt i32 %iv.next, 1000
|
|
br i1 %loop_cond, label %loop, label %exit
|
|
|
|
exit:
|
|
ret void
|
|
}
|
|
|
|
|
|
declare void @may_throw() inaccessiblememonly
|
|
|
|
; Test that we can sink a mustexecute load from loop header even in presence of
|
|
; throwing instructions after it.
|
|
define void @test_hoist_from_header_01(i32* %p, i32 %n) {
|
|
|
|
; CHECK-LABEL: @test_hoist_from_header_01(
|
|
; CHECK: entry:
|
|
; CHECK-NEXT: %load = load i32, i32* %p
|
|
; CHECK-NOT: load i32
|
|
|
|
entry:
|
|
br label %loop
|
|
|
|
loop:
|
|
%iv = phi i32 [ 0, %entry ], [ %iv.next, %backedge ]
|
|
%dummy = phi i32 [ 0, %entry ], [ %merge, %backedge ]
|
|
%load = load i32, i32* %p
|
|
call void @may_throw()
|
|
%cond = icmp slt i32 %iv, %n
|
|
br i1 %cond, label %if.true, label %if.false
|
|
|
|
if.true:
|
|
%a = add i32 %iv, %iv
|
|
br label %backedge
|
|
|
|
if.false:
|
|
%b = mul i32 %iv, %iv
|
|
br label %backedge
|
|
|
|
backedge:
|
|
%merge = phi i32 [ %a, %if.true ], [ %b, %if.false ]
|
|
%iv.next = add i32 %iv, %merge
|
|
%loop.cond = icmp ult i32 %iv.next, %load
|
|
br i1 %loop.cond, label %loop, label %exit
|
|
|
|
exit:
|
|
ret void
|
|
}
|
|
|
|
define void @test_hoist_from_header_02(i32* %p, i32 %n) {
|
|
|
|
; CHECK-LABEL: @test_hoist_from_header_02(
|
|
; CHECK: entry:
|
|
; CHECK-NEXT: %load = load i32, i32* %p
|
|
; CHECK-NOT: load i32
|
|
|
|
entry:
|
|
br label %loop
|
|
|
|
loop:
|
|
%iv = phi i32 [ 0, %entry ], [ %iv.next, %backedge ]
|
|
%dummy = phi i32 [ 0, %entry ], [ %merge, %backedge ]
|
|
%load = load i32, i32* %p
|
|
%cond = icmp slt i32 %iv, %n
|
|
br i1 %cond, label %if.true, label %if.false
|
|
|
|
if.true:
|
|
call void @may_throw()
|
|
%a = add i32 %iv, %iv
|
|
br label %backedge
|
|
|
|
if.false:
|
|
%b = mul i32 %iv, %iv
|
|
br label %backedge
|
|
|
|
backedge:
|
|
%merge = phi i32 [ %a, %if.true ], [ %b, %if.false ]
|
|
%iv.next = add i32 %iv, %merge
|
|
%loop.cond = icmp ult i32 %iv.next, %load
|
|
br i1 %loop.cond, label %loop, label %exit
|
|
|
|
exit:
|
|
ret void
|
|
}
|
|
|
|
define void @test_hoist_from_header_03(i32* %p, i32 %n) {
|
|
|
|
; CHECK-LABEL: @test_hoist_from_header_03(
|
|
; CHECK: entry:
|
|
; CHECK-NEXT: %load = load i32, i32* %p
|
|
; CHECK-NOT: load i32
|
|
|
|
entry:
|
|
br label %loop
|
|
|
|
loop:
|
|
%iv = phi i32 [ 0, %entry ], [ %iv.next, %backedge ]
|
|
%dummy = phi i32 [ 0, %entry ], [ %merge, %backedge ]
|
|
%load = load i32, i32* %p
|
|
%cond = icmp slt i32 %iv, %n
|
|
br i1 %cond, label %if.true, label %if.false
|
|
|
|
if.true:
|
|
%a = add i32 %iv, %iv
|
|
br label %backedge
|
|
|
|
if.false:
|
|
%b = mul i32 %iv, %iv
|
|
br label %backedge
|
|
|
|
backedge:
|
|
%merge = phi i32 [ %a, %if.true ], [ %b, %if.false ]
|
|
call void @may_throw()
|
|
%iv.next = add i32 %iv, %merge
|
|
%loop.cond = icmp ult i32 %iv.next, %load
|
|
br i1 %loop.cond, label %loop, label %exit
|
|
|
|
exit:
|
|
ret void
|
|
}
|
|
|
|
; Check that a throwing instruction prohibits hoisting across it.
|
|
define void @test_hoist_from_header_04(i32* %p, i32 %n) {
|
|
|
|
; CHECK-LABEL: @test_hoist_from_header_04(
|
|
; CHECK: entry:
|
|
; CHECK: loop:
|
|
; CHECK: %load = load i32, i32* %p
|
|
|
|
entry:
|
|
br label %loop
|
|
|
|
loop:
|
|
%iv = phi i32 [ 0, %entry ], [ %iv.next, %backedge ]
|
|
%dummy = phi i32 [ 0, %entry ], [ %merge, %backedge ]
|
|
call void @may_throw()
|
|
%load = load i32, i32* %p
|
|
%cond = icmp slt i32 %iv, %n
|
|
br i1 %cond, label %if.true, label %if.false
|
|
|
|
if.true:
|
|
%a = add i32 %iv, %iv
|
|
br label %backedge
|
|
|
|
if.false:
|
|
%b = mul i32 %iv, %iv
|
|
br label %backedge
|
|
|
|
backedge:
|
|
%merge = phi i32 [ %a, %if.true ], [ %b, %if.false ]
|
|
%iv.next = add i32 %iv, %merge
|
|
%loop.cond = icmp ult i32 %iv.next, %load
|
|
br i1 %loop.cond, label %loop, label %exit
|
|
|
|
exit:
|
|
ret void
|
|
}
|
|
|
|
; Check that we can hoist a mustexecute load from backedge even if something
|
|
; throws after it.
|
|
define void @test_hoist_from_backedge_01(i32* %p, i32 %n) {
|
|
|
|
; CHECK-LABEL: @test_hoist_from_backedge_01(
|
|
; CHECK: entry:
|
|
; CHECK-NEXT: %load = load i32, i32* %p
|
|
; CHECK-NOT: load i32
|
|
|
|
entry:
|
|
br label %loop
|
|
|
|
loop:
|
|
%iv = phi i32 [ 0, %entry ], [ %iv.next, %backedge ]
|
|
%dummy = phi i32 [ 0, %entry ], [ %merge, %backedge ]
|
|
%cond = icmp slt i32 %iv, %n
|
|
br i1 %cond, label %if.true, label %if.false
|
|
|
|
if.true:
|
|
%a = add i32 %iv, %iv
|
|
br label %backedge
|
|
|
|
if.false:
|
|
%b = mul i32 %iv, %iv
|
|
br label %backedge
|
|
|
|
backedge:
|
|
%merge = phi i32 [ %a, %if.true ], [ %b, %if.false ]
|
|
%iv.next = add i32 %iv, %merge
|
|
%load = load i32, i32* %p
|
|
call void @may_throw()
|
|
%loop.cond = icmp ult i32 %iv.next, %load
|
|
br i1 %loop.cond, label %loop, label %exit
|
|
|
|
exit:
|
|
ret void
|
|
}
|
|
|
|
; Check that we don't hoist the load if something before it can throw.
|
|
define void @test_hoist_from_backedge_02(i32* %p, i32 %n) {
|
|
|
|
; CHECK-LABEL: @test_hoist_from_backedge_02(
|
|
; CHECK: entry:
|
|
; CHECK: loop:
|
|
; CHECK: %load = load i32, i32* %p
|
|
|
|
entry:
|
|
br label %loop
|
|
|
|
loop:
|
|
%iv = phi i32 [ 0, %entry ], [ %iv.next, %backedge ]
|
|
%dummy = phi i32 [ 0, %entry ], [ %merge, %backedge ]
|
|
%cond = icmp slt i32 %iv, %n
|
|
br i1 %cond, label %if.true, label %if.false
|
|
|
|
if.true:
|
|
%a = add i32 %iv, %iv
|
|
br label %backedge
|
|
|
|
if.false:
|
|
%b = mul i32 %iv, %iv
|
|
br label %backedge
|
|
|
|
backedge:
|
|
%merge = phi i32 [ %a, %if.true ], [ %b, %if.false ]
|
|
%iv.next = add i32 %iv, %merge
|
|
call void @may_throw()
|
|
%load = load i32, i32* %p
|
|
%loop.cond = icmp ult i32 %iv.next, %load
|
|
br i1 %loop.cond, label %loop, label %exit
|
|
|
|
exit:
|
|
ret void
|
|
}
|
|
|
|
define void @test_hoist_from_backedge_03(i32* %p, i32 %n) {
|
|
|
|
; CHECK-LABEL: @test_hoist_from_backedge_03(
|
|
; CHECK: entry:
|
|
; CHECK: loop:
|
|
; CHECK: %load = load i32, i32* %p
|
|
|
|
entry:
|
|
br label %loop
|
|
|
|
loop:
|
|
%iv = phi i32 [ 0, %entry ], [ %iv.next, %backedge ]
|
|
%dummy = phi i32 [ 0, %entry ], [ %merge, %backedge ]
|
|
%cond = icmp slt i32 %iv, %n
|
|
br i1 %cond, label %if.true, label %if.false
|
|
|
|
if.true:
|
|
%a = add i32 %iv, %iv
|
|
br label %backedge
|
|
|
|
if.false:
|
|
%b = mul i32 %iv, %iv
|
|
call void @may_throw()
|
|
br label %backedge
|
|
|
|
backedge:
|
|
%merge = phi i32 [ %a, %if.true ], [ %b, %if.false ]
|
|
%iv.next = add i32 %iv, %merge
|
|
%load = load i32, i32* %p
|
|
%loop.cond = icmp ult i32 %iv.next, %load
|
|
br i1 %loop.cond, label %loop, label %exit
|
|
|
|
exit:
|
|
ret void
|
|
}
|
|
|
|
define void @test_hoist_from_backedge_04(i32* %p, i32 %n) {
|
|
|
|
; CHECK-LABEL: @test_hoist_from_backedge_04(
|
|
; CHECK: entry:
|
|
; CHECK: loop:
|
|
; CHECK: %load = load i32, i32* %p
|
|
|
|
entry:
|
|
br label %loop
|
|
|
|
loop:
|
|
%iv = phi i32 [ 0, %entry ], [ %iv.next, %backedge ]
|
|
%dummy = phi i32 [ 0, %entry ], [ %merge, %backedge ]
|
|
call void @may_throw()
|
|
%cond = icmp slt i32 %iv, %n
|
|
br i1 %cond, label %if.true, label %if.false
|
|
|
|
if.true:
|
|
%a = add i32 %iv, %iv
|
|
br label %backedge
|
|
|
|
if.false:
|
|
%b = mul i32 %iv, %iv
|
|
br label %backedge
|
|
|
|
backedge:
|
|
%merge = phi i32 [ %a, %if.true ], [ %b, %if.false ]
|
|
%iv.next = add i32 %iv, %merge
|
|
%load = load i32, i32* %p
|
|
%loop.cond = icmp ult i32 %iv.next, %load
|
|
br i1 %loop.cond, label %loop, label %exit
|
|
|
|
exit:
|
|
ret void
|
|
}
|