blob: 7b8cd1b23edea51f380a5b24cd7602fc8bd83417 [file] [edit]
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
; RUN: opt -S -passes='loop-unroll' < %s | FileCheck --check-prefix=DEFAULT %s
; RUN: opt -S -passes='loop-unroll<prepare-for-lto>' < %s | FileCheck --check-prefix=LTO %s
; The normal unroll pass fully unrolls this loop. In prepare-for-lto mode, keep
; it rolled so the external call can be inlined after linking.
declare i32 @extern_func(i32)
define i32 @extern_call(ptr %A) {
; DEFAULT-LABEL: @extern_call(
; DEFAULT-NEXT: entry:
; DEFAULT-NEXT: br label [[LOOP:%.*]]
; DEFAULT: loop:
; DEFAULT-NEXT: [[V:%.*]] = call i32 @extern_func(i32 0)
; DEFAULT-NEXT: [[V_1:%.*]] = call i32 @extern_func(i32 1)
; DEFAULT-NEXT: [[ADD_1:%.*]] = add i32 [[V]], [[V_1]]
; DEFAULT-NEXT: [[V_2:%.*]] = call i32 @extern_func(i32 2)
; DEFAULT-NEXT: [[ADD_2:%.*]] = add i32 [[ADD_1]], [[V_2]]
; DEFAULT-NEXT: [[V_3:%.*]] = call i32 @extern_func(i32 3)
; DEFAULT-NEXT: [[ADD_3:%.*]] = add i32 [[ADD_2]], [[V_3]]
; DEFAULT-NEXT: ret i32 [[ADD_3]]
;
; LTO-LABEL: @extern_call(
; LTO-NEXT: entry:
; LTO-NEXT: br label [[LOOP:%.*]]
; LTO: loop:
; LTO-NEXT: [[I:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[INC:%.*]], [[LOOP]] ]
; LTO-NEXT: [[SUM:%.*]] = phi i32 [ 0, [[ENTRY]] ], [ [[ADD:%.*]], [[LOOP]] ]
; LTO-NEXT: [[V:%.*]] = call i32 @extern_func(i32 [[I]])
; LTO-NEXT: [[ADD]] = add i32 [[SUM]], [[V]]
; LTO-NEXT: [[INC]] = add i32 [[I]], 1
; LTO-NEXT: [[CMP:%.*]] = icmp ult i32 [[INC]], 4
; LTO-NEXT: br i1 [[CMP]], label [[LOOP]], label [[EXIT:%.*]]
; LTO: exit:
; LTO-NEXT: [[ADD_LCSSA:%.*]] = phi i32 [ [[ADD]], [[LOOP]] ]
; LTO-NEXT: ret i32 [[ADD_LCSSA]]
;
entry:
br label %loop
loop:
%i = phi i32 [ 0, %entry ], [ %inc, %loop ]
%sum = phi i32 [ 0, %entry ], [ %add, %loop ]
%v = call i32 @extern_func(i32 %i)
%add = add i32 %sum, %v
%inc = add i32 %i, 1
%cmp = icmp ult i32 %inc, 4
br i1 %cmp, label %loop, label %exit
exit:
ret i32 %add
}
; A noinline call to an external function cannot benefit from waiting for LTO,
; so fully unroll this loop in both modes.
define i32 @extern_noinline_call(ptr %A) {
; DEFAULT-LABEL: @extern_noinline_call(
; DEFAULT-NEXT: entry:
; DEFAULT-NEXT: br label [[LOOP:%.*]]
; DEFAULT: loop:
; DEFAULT-NEXT: [[V:%.*]] = call i32 @extern_func(i32 0) #[[ATTR0:[0-9]+]]
; DEFAULT-NEXT: [[V_1:%.*]] = call i32 @extern_func(i32 1) #[[ATTR0]]
; DEFAULT-NEXT: [[ADD_1:%.*]] = add i32 [[V]], [[V_1]]
; DEFAULT-NEXT: [[V_2:%.*]] = call i32 @extern_func(i32 2) #[[ATTR0]]
; DEFAULT-NEXT: [[ADD_2:%.*]] = add i32 [[ADD_1]], [[V_2]]
; DEFAULT-NEXT: [[V_3:%.*]] = call i32 @extern_func(i32 3) #[[ATTR0]]
; DEFAULT-NEXT: [[ADD_3:%.*]] = add i32 [[ADD_2]], [[V_3]]
; DEFAULT-NEXT: ret i32 [[ADD_3]]
;
; LTO-LABEL: @extern_noinline_call(
; LTO-NEXT: entry:
; LTO-NEXT: br label [[LOOP:%.*]]
; LTO: loop:
; LTO-NEXT: [[V:%.*]] = call i32 @extern_func(i32 0) #[[ATTR0:[0-9]+]]
; LTO-NEXT: [[V_1:%.*]] = call i32 @extern_func(i32 1) #[[ATTR0]]
; LTO-NEXT: [[ADD_1:%.*]] = add i32 [[V]], [[V_1]]
; LTO-NEXT: [[V_2:%.*]] = call i32 @extern_func(i32 2) #[[ATTR0]]
; LTO-NEXT: [[ADD_2:%.*]] = add i32 [[ADD_1]], [[V_2]]
; LTO-NEXT: [[V_3:%.*]] = call i32 @extern_func(i32 3) #[[ATTR0]]
; LTO-NEXT: [[ADD_3:%.*]] = add i32 [[ADD_2]], [[V_3]]
; LTO-NEXT: ret i32 [[ADD_3]]
;
entry:
br label %loop
loop:
%i = phi i32 [ 0, %entry ], [ %inc, %loop ]
%sum = phi i32 [ 0, %entry ], [ %add, %loop ]
%v = call i32 @extern_func(i32 %i) #0
%add = add i32 %sum, %v
%inc = add i32 %i, 1
%cmp = icmp ult i32 %inc, 4
br i1 %cmp, label %loop, label %exit
exit:
ret i32 %add
}
; An indirect call may be resolved during LTO, so keep this loop rolled in
; prepare-for-lto mode. The normal pass still fully unrolls it.
define i32 @indirect_call(ptr %callee) {
; DEFAULT-LABEL: @indirect_call(
; DEFAULT-NEXT: entry:
; DEFAULT-NEXT: br label [[LOOP:%.*]]
; DEFAULT: loop:
; DEFAULT-NEXT: [[V:%.*]] = call i32 [[CALLEE:%.*]](i32 0)
; DEFAULT-NEXT: [[V_1:%.*]] = call i32 [[CALLEE]](i32 1)
; DEFAULT-NEXT: [[ADD_1:%.*]] = add i32 [[V]], [[V_1]]
; DEFAULT-NEXT: [[V_2:%.*]] = call i32 [[CALLEE]](i32 2)
; DEFAULT-NEXT: [[ADD_2:%.*]] = add i32 [[ADD_1]], [[V_2]]
; DEFAULT-NEXT: [[V_3:%.*]] = call i32 [[CALLEE]](i32 3)
; DEFAULT-NEXT: [[ADD_3:%.*]] = add i32 [[ADD_2]], [[V_3]]
; DEFAULT-NEXT: ret i32 [[ADD_3]]
;
; LTO-LABEL: @indirect_call(
; LTO-NEXT: entry:
; LTO-NEXT: br label [[LOOP:%.*]]
; LTO: loop:
; LTO-NEXT: [[I:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[INC:%.*]], [[LOOP]] ]
; LTO-NEXT: [[SUM:%.*]] = phi i32 [ 0, [[ENTRY]] ], [ [[ADD:%.*]], [[LOOP]] ]
; LTO-NEXT: [[V:%.*]] = call i32 [[CALLEE:%.*]](i32 [[I]])
; LTO-NEXT: [[ADD]] = add i32 [[SUM]], [[V]]
; LTO-NEXT: [[INC]] = add i32 [[I]], 1
; LTO-NEXT: [[CMP:%.*]] = icmp ult i32 [[INC]], 4
; LTO-NEXT: br i1 [[CMP]], label [[LOOP]], label [[EXIT:%.*]]
; LTO: exit:
; LTO-NEXT: [[ADD_LCSSA:%.*]] = phi i32 [ [[ADD]], [[LOOP]] ]
; LTO-NEXT: ret i32 [[ADD_LCSSA]]
;
entry:
br label %loop
loop:
%i = phi i32 [ 0, %entry ], [ %inc, %loop ]
%sum = phi i32 [ 0, %entry ], [ %add, %loop ]
%v = call i32 %callee(i32 %i)
%add = add i32 %sum, %v
%inc = add i32 %i, 1
%cmp = icmp ult i32 %inc, 4
br i1 %cmp, label %loop, label %exit
exit:
ret i32 %add
}
; Waiting for LTO cannot help a noinline call, so fully unroll this loop in
; both modes.
define i32 @indirect_noinline_call(ptr %callee) {
; DEFAULT-LABEL: @indirect_noinline_call(
; DEFAULT-NEXT: entry:
; DEFAULT-NEXT: br label [[LOOP:%.*]]
; DEFAULT: loop:
; DEFAULT-NEXT: [[V:%.*]] = call i32 [[CALLEE:%.*]](i32 0) #[[ATTR0]]
; DEFAULT-NEXT: [[V_1:%.*]] = call i32 [[CALLEE]](i32 1) #[[ATTR0]]
; DEFAULT-NEXT: [[ADD_1:%.*]] = add i32 [[V]], [[V_1]]
; DEFAULT-NEXT: [[V_2:%.*]] = call i32 [[CALLEE]](i32 2) #[[ATTR0]]
; DEFAULT-NEXT: [[ADD_2:%.*]] = add i32 [[ADD_1]], [[V_2]]
; DEFAULT-NEXT: [[V_3:%.*]] = call i32 [[CALLEE]](i32 3) #[[ATTR0]]
; DEFAULT-NEXT: [[ADD_3:%.*]] = add i32 [[ADD_2]], [[V_3]]
; DEFAULT-NEXT: ret i32 [[ADD_3]]
;
; LTO-LABEL: @indirect_noinline_call(
; LTO-NEXT: entry:
; LTO-NEXT: br label [[LOOP:%.*]]
; LTO: loop:
; LTO-NEXT: [[V:%.*]] = call i32 [[CALLEE:%.*]](i32 0) #[[ATTR0]]
; LTO-NEXT: [[V_1:%.*]] = call i32 [[CALLEE]](i32 1) #[[ATTR0]]
; LTO-NEXT: [[ADD_1:%.*]] = add i32 [[V]], [[V_1]]
; LTO-NEXT: [[V_2:%.*]] = call i32 [[CALLEE]](i32 2) #[[ATTR0]]
; LTO-NEXT: [[ADD_2:%.*]] = add i32 [[ADD_1]], [[V_2]]
; LTO-NEXT: [[V_3:%.*]] = call i32 [[CALLEE]](i32 3) #[[ATTR0]]
; LTO-NEXT: [[ADD_3:%.*]] = add i32 [[ADD_2]], [[V_3]]
; LTO-NEXT: ret i32 [[ADD_3]]
;
entry:
br label %loop
loop:
%i = phi i32 [ 0, %entry ], [ %inc, %loop ]
%sum = phi i32 [ 0, %entry ], [ %add, %loop ]
%v = call i32 %callee(i32 %i) #0
%add = add i32 %sum, %v
%inc = add i32 %i, 1
%cmp = icmp ult i32 %inc, 4
br i1 %cmp, label %loop, label %exit
exit:
ret i32 %add
}
; The single-use helper is already an inline candidate, so leave this loop
; rolled in both modes.
define internal i32 @helper(i32 %x) {
; DEFAULT-LABEL: @helper(
; DEFAULT-NEXT: [[ADD:%.*]] = add i32 [[X:%.*]], 42
; DEFAULT-NEXT: ret i32 [[ADD]]
;
; LTO-LABEL: @helper(
; LTO-NEXT: [[ADD:%.*]] = add i32 [[X:%.*]], 42
; LTO-NEXT: ret i32 [[ADD]]
;
%add = add i32 %x, 42
ret i32 %add
}
define i32 @internal_single_use_call(ptr %A) {
; DEFAULT-LABEL: @internal_single_use_call(
; DEFAULT-NEXT: entry:
; DEFAULT-NEXT: br label [[LOOP:%.*]]
; DEFAULT: loop:
; DEFAULT-NEXT: [[I:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[INC:%.*]], [[LOOP]] ]
; DEFAULT-NEXT: [[SUM:%.*]] = phi i32 [ 0, [[ENTRY]] ], [ [[ADD:%.*]], [[LOOP]] ]
; DEFAULT-NEXT: [[V:%.*]] = call i32 @helper(i32 [[I]])
; DEFAULT-NEXT: [[ADD]] = add i32 [[SUM]], [[V]]
; DEFAULT-NEXT: [[INC]] = add i32 [[I]], 1
; DEFAULT-NEXT: [[CMP:%.*]] = icmp ult i32 [[INC]], 4
; DEFAULT-NEXT: br i1 [[CMP]], label [[LOOP]], label [[EXIT:%.*]]
; DEFAULT: exit:
; DEFAULT-NEXT: [[ADD_LCSSA:%.*]] = phi i32 [ [[ADD]], [[LOOP]] ]
; DEFAULT-NEXT: ret i32 [[ADD_LCSSA]]
;
; LTO-LABEL: @internal_single_use_call(
; LTO-NEXT: entry:
; LTO-NEXT: br label [[LOOP:%.*]]
; LTO: loop:
; LTO-NEXT: [[I:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[INC:%.*]], [[LOOP]] ]
; LTO-NEXT: [[SUM:%.*]] = phi i32 [ 0, [[ENTRY]] ], [ [[ADD:%.*]], [[LOOP]] ]
; LTO-NEXT: [[V:%.*]] = call i32 @helper(i32 [[I]])
; LTO-NEXT: [[ADD]] = add i32 [[SUM]], [[V]]
; LTO-NEXT: [[INC]] = add i32 [[I]], 1
; LTO-NEXT: [[CMP:%.*]] = icmp ult i32 [[INC]], 4
; LTO-NEXT: br i1 [[CMP]], label [[LOOP]], label [[EXIT:%.*]]
; LTO: exit:
; LTO-NEXT: [[ADD_LCSSA:%.*]] = phi i32 [ [[ADD]], [[LOOP]] ]
; LTO-NEXT: ret i32 [[ADD_LCSSA]]
;
entry:
br label %loop
loop:
%i = phi i32 [ 0, %entry ], [ %inc, %loop ]
%sum = phi i32 [ 0, %entry ], [ %add, %loop ]
%v = call i32 @helper(i32 %i)
%add = add i32 %sum, %v
%inc = add i32 %i, 1
%cmp = icmp ult i32 %inc, 4
br i1 %cmp, label %loop, label %exit
exit:
ret i32 %add
}
; With no calls to inline, there is no reason to defer unrolling.
define i32 @no_call(ptr %A) {
; DEFAULT-LABEL: @no_call(
; DEFAULT-NEXT: entry:
; DEFAULT-NEXT: br label [[LOOP:%.*]]
; DEFAULT: loop:
; DEFAULT-NEXT: [[V:%.*]] = load i32, ptr [[A:%.*]], align 4
; DEFAULT-NEXT: [[ARRAYIDX_1:%.*]] = getelementptr inbounds i32, ptr [[A]], i32 1
; DEFAULT-NEXT: [[V_1:%.*]] = load i32, ptr [[ARRAYIDX_1]], align 4
; DEFAULT-NEXT: [[ADD_1:%.*]] = add i32 [[V]], [[V_1]]
; DEFAULT-NEXT: [[ARRAYIDX_2:%.*]] = getelementptr inbounds i32, ptr [[A]], i32 2
; DEFAULT-NEXT: [[V_2:%.*]] = load i32, ptr [[ARRAYIDX_2]], align 4
; DEFAULT-NEXT: [[ADD_2:%.*]] = add i32 [[ADD_1]], [[V_2]]
; DEFAULT-NEXT: [[ARRAYIDX_3:%.*]] = getelementptr inbounds i32, ptr [[A]], i32 3
; DEFAULT-NEXT: [[V_3:%.*]] = load i32, ptr [[ARRAYIDX_3]], align 4
; DEFAULT-NEXT: [[ADD_3:%.*]] = add i32 [[ADD_2]], [[V_3]]
; DEFAULT-NEXT: ret i32 [[ADD_3]]
;
; LTO-LABEL: @no_call(
; LTO-NEXT: entry:
; LTO-NEXT: br label [[LOOP:%.*]]
; LTO: loop:
; LTO-NEXT: [[V:%.*]] = load i32, ptr [[A:%.*]], align 4
; LTO-NEXT: [[ARRAYIDX_1:%.*]] = getelementptr inbounds i32, ptr [[A]], i32 1
; LTO-NEXT: [[V_1:%.*]] = load i32, ptr [[ARRAYIDX_1]], align 4
; LTO-NEXT: [[ADD_1:%.*]] = add i32 [[V]], [[V_1]]
; LTO-NEXT: [[ARRAYIDX_2:%.*]] = getelementptr inbounds i32, ptr [[A]], i32 2
; LTO-NEXT: [[V_2:%.*]] = load i32, ptr [[ARRAYIDX_2]], align 4
; LTO-NEXT: [[ADD_2:%.*]] = add i32 [[ADD_1]], [[V_2]]
; LTO-NEXT: [[ARRAYIDX_3:%.*]] = getelementptr inbounds i32, ptr [[A]], i32 3
; LTO-NEXT: [[V_3:%.*]] = load i32, ptr [[ARRAYIDX_3]], align 4
; LTO-NEXT: [[ADD_3:%.*]] = add i32 [[ADD_2]], [[V_3]]
; LTO-NEXT: ret i32 [[ADD_3]]
;
entry:
br label %loop
loop:
%i = phi i32 [ 0, %entry ], [ %inc, %loop ]
%sum = phi i32 [ 0, %entry ], [ %add, %loop ]
%arrayidx = getelementptr inbounds i32, ptr %A, i32 %i
%v = load i32, ptr %arrayidx
%add = add i32 %sum, %v
%inc = add i32 %i, 1
%cmp = icmp ult i32 %inc, 4
br i1 %cmp, label %loop, label %exit
exit:
ret i32 %add
}
attributes #0 = { noinline }