blob: eeb0ef9c72c7fa3b08aceb386cc56951e46c1736 [file]
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
; RUN: opt -S -passes=loop-fusion -verify-loop-info -verify-dom-info < %s | FileCheck %s
@B = common global [1024 x i32] zeroinitializer, align 16
; The skip edge of the first loop guard goes through an empty block (%skip)
; that passes like JumpThreading can leave behind. Loop fusion must fold the
; block away so that the guard is recognized and the two loops are fused.
define void @guard_skip_empty_block(ptr noalias %A, i64 %N) {
; CHECK-LABEL: define void @guard_skip_empty_block(
; CHECK-SAME: ptr noalias [[A:%.*]], i64 [[N:%.*]]) {
; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: [[CMP4:%.*]] = icmp slt i64 0, [[N]]
; CHECK-NEXT: [[CMP31:%.*]] = icmp slt i64 0, [[N]]
; CHECK-NEXT: br i1 [[CMP4]], label %[[BB3:.*]], label %[[BB12:.*]]
; CHECK: [[BB3]]:
; CHECK-NEXT: br label %[[BB5:.*]]
; CHECK: [[BB5]]:
; CHECK-NEXT: [[I_05:%.*]] = phi i64 [ [[INC:%.*]], %[[BB5]] ], [ 0, %[[BB3]] ]
; CHECK-NEXT: [[I1_02:%.*]] = phi i64 [ [[INC14:%.*]], %[[BB5]] ], [ 0, %[[BB3]] ]
; CHECK-NEXT: [[SUB:%.*]] = sub nsw i64 [[I_05]], 3
; CHECK-NEXT: [[ADD:%.*]] = add nsw i64 [[I_05]], 3
; CHECK-NEXT: [[MUL:%.*]] = mul nsw i64 [[SUB]], [[ADD]]
; CHECK-NEXT: [[REM:%.*]] = srem i64 [[MUL]], [[I_05]]
; CHECK-NEXT: [[CONV:%.*]] = trunc i64 [[REM]] to i32
; CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds i32, ptr [[A]], i64 [[I_05]]
; CHECK-NEXT: store i32 [[CONV]], ptr [[ARRAYIDX]], align 4
; CHECK-NEXT: [[INC]] = add nsw i64 [[I_05]], 1
; CHECK-NEXT: [[CMP:%.*]] = icmp slt i64 [[INC]], [[N]]
; CHECK-NEXT: [[SUB7:%.*]] = sub nsw i64 [[I1_02]], 3
; CHECK-NEXT: [[ADD8:%.*]] = add nsw i64 [[I1_02]], 3
; CHECK-NEXT: [[MUL9:%.*]] = mul nsw i64 [[SUB7]], [[ADD8]]
; CHECK-NEXT: [[REM10:%.*]] = srem i64 [[MUL9]], [[I1_02]]
; CHECK-NEXT: [[CONV11:%.*]] = trunc i64 [[REM10]] to i32
; CHECK-NEXT: [[ARRAYIDX12:%.*]] = getelementptr inbounds [1024 x i32], ptr @B, i64 0, i64 [[I1_02]]
; CHECK-NEXT: store i32 [[CONV11]], ptr [[ARRAYIDX12]], align 4
; CHECK-NEXT: [[INC14]] = add nsw i64 [[I1_02]], 1
; CHECK-NEXT: [[CMP3:%.*]] = icmp slt i64 [[INC14]], [[N]]
; CHECK-NEXT: br i1 [[CMP3]], label %[[BB5]], label %[[BB15:.*]]
; CHECK: [[BB15]]:
; CHECK-NEXT: br label %[[BB12]]
; CHECK: [[BB12]]:
; CHECK-NEXT: ret void
;
entry:
%cmp4 = icmp slt i64 0, %N
br i1 %cmp4, label %blk3, label %skip
skip:
br label %blk14
blk3:
br label %blk5
blk5:
%i.05 = phi i64 [ %inc, %blk5 ], [ 0, %blk3 ]
%sub = sub nsw i64 %i.05, 3
%add = add nsw i64 %i.05, 3
%mul = mul nsw i64 %sub, %add
%rem = srem i64 %mul, %i.05
%conv = trunc i64 %rem to i32
%arrayidx = getelementptr inbounds i32, ptr %A, i64 %i.05
store i32 %conv, ptr %arrayidx, align 4
%inc = add nsw i64 %i.05, 1
%cmp = icmp slt i64 %inc, %N
br i1 %cmp, label %blk5, label %blk10
blk10:
br label %blk14
blk14:
%cmp31 = icmp slt i64 0, %N
br i1 %cmp31, label %blk8, label %blk12
blk8:
br label %blk9
blk9:
%i1.02 = phi i64 [ %inc14, %blk9 ], [ 0, %blk8 ]
%sub7 = sub nsw i64 %i1.02, 3
%add8 = add nsw i64 %i1.02, 3
%mul9 = mul nsw i64 %sub7, %add8
%rem10 = srem i64 %mul9, %i1.02
%conv11 = trunc i64 %rem10 to i32
%arrayidx12 = getelementptr inbounds [1024 x i32], ptr @B, i64 0, i64 %i1.02
store i32 %conv11, ptr %arrayidx12, align 4
%inc14 = add nsw i64 %i1.02, 1
%cmp3 = icmp slt i64 %inc14, %N
br i1 %cmp3, label %blk9, label %blk15
blk15:
br label %blk12
blk12:
ret void
}
; Same shape inside an outer loop: the skip block belongs to the outer loop
; and must be removed from LoopInfo as well.
define void @guard_skip_empty_block_nested(ptr noalias %A, i64 %N, i64 %M) {
; CHECK-LABEL: define void @guard_skip_empty_block_nested(
; CHECK-SAME: ptr noalias [[A:%.*]], i64 [[N:%.*]], i64 [[M:%.*]]) {
; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: [[CMPM:%.*]] = icmp slt i64 0, [[M]]
; CHECK-NEXT: br i1 [[CMPM]], label %[[OUTER_PH:.*]], label %[[EXIT:.*]]
; CHECK: [[OUTER_PH]]:
; CHECK-NEXT: br label %[[OUTER:.*]]
; CHECK: [[OUTER]]:
; CHECK-NEXT: [[J:%.*]] = phi i64 [ 0, %[[OUTER_PH]] ], [ [[J_INC:%.*]], %[[OUTER_LATCH:.*]] ]
; CHECK-NEXT: [[CMP4:%.*]] = icmp slt i64 0, [[N]]
; CHECK-NEXT: [[CMP31:%.*]] = icmp slt i64 0, [[N]]
; CHECK-NEXT: br i1 [[CMP4]], label %[[BB3:.*]], label %[[OUTER_LATCH]]
; CHECK: [[BB3]]:
; CHECK-NEXT: br label %[[BB5:.*]]
; CHECK: [[BB5]]:
; CHECK-NEXT: [[I_05:%.*]] = phi i64 [ [[INC:%.*]], %[[BB5]] ], [ 0, %[[BB3]] ]
; CHECK-NEXT: [[I1_02:%.*]] = phi i64 [ [[INC14:%.*]], %[[BB5]] ], [ 0, %[[BB3]] ]
; CHECK-NEXT: [[SUB:%.*]] = sub nsw i64 [[I_05]], 3
; CHECK-NEXT: [[ADD:%.*]] = add nsw i64 [[I_05]], 3
; CHECK-NEXT: [[MUL:%.*]] = mul nsw i64 [[SUB]], [[ADD]]
; CHECK-NEXT: [[REM:%.*]] = srem i64 [[MUL]], [[I_05]]
; CHECK-NEXT: [[CONV:%.*]] = trunc i64 [[REM]] to i32
; CHECK-NEXT: [[IDX:%.*]] = add nsw i64 [[I_05]], [[J]]
; CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds i32, ptr [[A]], i64 [[IDX]]
; CHECK-NEXT: store i32 [[CONV]], ptr [[ARRAYIDX]], align 4
; CHECK-NEXT: [[INC]] = add nsw i64 [[I_05]], 1
; CHECK-NEXT: [[CMP:%.*]] = icmp slt i64 [[INC]], [[N]]
; CHECK-NEXT: [[SUB7:%.*]] = sub nsw i64 [[I1_02]], 3
; CHECK-NEXT: [[ADD8:%.*]] = add nsw i64 [[I1_02]], 3
; CHECK-NEXT: [[MUL9:%.*]] = mul nsw i64 [[SUB7]], [[ADD8]]
; CHECK-NEXT: [[REM10:%.*]] = srem i64 [[MUL9]], [[I1_02]]
; CHECK-NEXT: [[CONV11:%.*]] = trunc i64 [[REM10]] to i32
; CHECK-NEXT: [[ARRAYIDX12:%.*]] = getelementptr inbounds [1024 x i32], ptr @B, i64 0, i64 [[I1_02]]
; CHECK-NEXT: store i32 [[CONV11]], ptr [[ARRAYIDX12]], align 4
; CHECK-NEXT: [[INC14]] = add nsw i64 [[I1_02]], 1
; CHECK-NEXT: [[CMP3:%.*]] = icmp slt i64 [[INC14]], [[N]]
; CHECK-NEXT: br i1 [[CMP3]], label %[[BB5]], label %[[BB15:.*]]
; CHECK: [[BB15]]:
; CHECK-NEXT: br label %[[OUTER_LATCH]]
; CHECK: [[OUTER_LATCH]]:
; CHECK-NEXT: [[J_INC]] = add nsw i64 [[J]], 1
; CHECK-NEXT: [[CMPJ:%.*]] = icmp slt i64 [[J_INC]], [[M]]
; CHECK-NEXT: br i1 [[CMPJ]], label %[[OUTER]], label %[[EXIT_LOOPEXIT:.*]]
; CHECK: [[EXIT_LOOPEXIT]]:
; CHECK-NEXT: br label %[[EXIT]]
; CHECK: [[EXIT]]:
; CHECK-NEXT: ret void
;
entry:
%cmpM = icmp slt i64 0, %M
br i1 %cmpM, label %outer.ph, label %exit
outer.ph:
br label %outer
outer:
%j = phi i64 [ 0, %outer.ph ], [ %j.inc, %outer.latch ]
%cmp4 = icmp slt i64 0, %N
br i1 %cmp4, label %blk3, label %skip
skip:
br label %blk14
blk3:
br label %blk5
blk5:
%i.05 = phi i64 [ %inc, %blk5 ], [ 0, %blk3 ]
%sub = sub nsw i64 %i.05, 3
%add = add nsw i64 %i.05, 3
%mul = mul nsw i64 %sub, %add
%rem = srem i64 %mul, %i.05
%conv = trunc i64 %rem to i32
%idx = add nsw i64 %i.05, %j
%arrayidx = getelementptr inbounds i32, ptr %A, i64 %idx
store i32 %conv, ptr %arrayidx, align 4
%inc = add nsw i64 %i.05, 1
%cmp = icmp slt i64 %inc, %N
br i1 %cmp, label %blk5, label %blk10
blk10:
br label %blk14
blk14:
%cmp31 = icmp slt i64 0, %N
br i1 %cmp31, label %blk8, label %outer.latch
blk8:
br label %blk9
blk9:
%i1.02 = phi i64 [ %inc14, %blk9 ], [ 0, %blk8 ]
%sub7 = sub nsw i64 %i1.02, 3
%add8 = add nsw i64 %i1.02, 3
%mul9 = mul nsw i64 %sub7, %add8
%rem10 = srem i64 %mul9, %i1.02
%conv11 = trunc i64 %rem10 to i32
%arrayidx12 = getelementptr inbounds [1024 x i32], ptr @B, i64 0, i64 %i1.02
store i32 %conv11, ptr %arrayidx12, align 4
%inc14 = add nsw i64 %i1.02, 1
%cmp3 = icmp slt i64 %inc14, %N
br i1 %cmp3, label %blk9, label %blk15
blk15:
br label %outer.latch
outer.latch:
%j.inc = add nsw i64 %j, 1
%cmpj = icmp slt i64 %j.inc, %M
br i1 %cmpj, label %outer, label %exit.loopexit
exit.loopexit:
br label %exit
exit:
ret void
}
; The loops have different trip counts and are not fused, but the empty skip
; block is still folded and the PHI in the merge block is updated.
define i32 @guard_skip_empty_block_phi(ptr noalias %A, i64 %N, i64 %M) {
; CHECK-LABEL: define i32 @guard_skip_empty_block_phi(
; CHECK-SAME: ptr noalias [[A:%.*]], i64 [[N:%.*]], i64 [[M:%.*]]) {
; CHECK-NEXT: [[ENTRY:.*]]:
; CHECK-NEXT: [[CMP4:%.*]] = icmp slt i64 0, [[N]]
; CHECK-NEXT: br i1 [[CMP4]], label %[[BB3:.*]], label %[[BB14:.*]]
; CHECK: [[BB3]]:
; CHECK-NEXT: br label %[[BB5:.*]]
; CHECK: [[BB5]]:
; CHECK-NEXT: [[I_05:%.*]] = phi i64 [ [[INC:%.*]], %[[BB5]] ], [ 0, %[[BB3]] ]
; CHECK-NEXT: [[CONV:%.*]] = trunc i64 [[I_05]] to i32
; CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds i32, ptr [[A]], i64 [[I_05]]
; CHECK-NEXT: store i32 [[CONV]], ptr [[ARRAYIDX]], align 4
; CHECK-NEXT: [[INC]] = add nsw i64 [[I_05]], 1
; CHECK-NEXT: [[CMP:%.*]] = icmp slt i64 [[INC]], [[N]]
; CHECK-NEXT: br i1 [[CMP]], label %[[BB5]], label %[[BB10:.*]]
; CHECK: [[BB10]]:
; CHECK-NEXT: br label %[[BB14]]
; CHECK: [[BB14]]:
; CHECK-NEXT: [[P:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ 1, %[[BB10]] ]
; CHECK-NEXT: [[CMP31:%.*]] = icmp slt i64 0, [[M]]
; CHECK-NEXT: br i1 [[CMP31]], label %[[BB8:.*]], label %[[BB12:.*]]
; CHECK: [[BB8]]:
; CHECK-NEXT: br label %[[BB9:.*]]
; CHECK: [[BB9]]:
; CHECK-NEXT: [[I1_02:%.*]] = phi i64 [ [[INC14:%.*]], %[[BB9]] ], [ 0, %[[BB8]] ]
; CHECK-NEXT: [[CONV11:%.*]] = trunc i64 [[I1_02]] to i32
; CHECK-NEXT: [[ARRAYIDX12:%.*]] = getelementptr inbounds [1024 x i32], ptr @B, i64 0, i64 [[I1_02]]
; CHECK-NEXT: store i32 [[CONV11]], ptr [[ARRAYIDX12]], align 4
; CHECK-NEXT: [[INC14]] = add nsw i64 [[I1_02]], 1
; CHECK-NEXT: [[CMP3:%.*]] = icmp slt i64 [[INC14]], [[M]]
; CHECK-NEXT: br i1 [[CMP3]], label %[[BB9]], label %[[BB15:.*]]
; CHECK: [[BB15]]:
; CHECK-NEXT: br label %[[BB12]]
; CHECK: [[BB12]]:
; CHECK-NEXT: ret i32 [[P]]
;
entry:
%cmp4 = icmp slt i64 0, %N
br i1 %cmp4, label %blk3, label %skip
skip:
br label %blk14
blk3:
br label %blk5
blk5:
%i.05 = phi i64 [ %inc, %blk5 ], [ 0, %blk3 ]
%conv = trunc i64 %i.05 to i32
%arrayidx = getelementptr inbounds i32, ptr %A, i64 %i.05
store i32 %conv, ptr %arrayidx, align 4
%inc = add nsw i64 %i.05, 1
%cmp = icmp slt i64 %inc, %N
br i1 %cmp, label %blk5, label %blk10
blk10:
br label %blk14
blk14:
%p = phi i32 [ 0, %skip ], [ 1, %blk10 ]
%cmp31 = icmp slt i64 0, %M
br i1 %cmp31, label %blk8, label %blk12
blk8:
br label %blk9
blk9:
%i1.02 = phi i64 [ %inc14, %blk9 ], [ 0, %blk8 ]
%conv11 = trunc i64 %i1.02 to i32
%arrayidx12 = getelementptr inbounds [1024 x i32], ptr @B, i64 0, i64 %i1.02
store i32 %conv11, ptr %arrayidx12, align 4
%inc14 = add nsw i64 %i1.02, 1
%cmp3 = icmp slt i64 %inc14, %M
br i1 %cmp3, label %blk9, label %blk15
blk15:
br label %blk12
blk12:
ret i32 %p
}
; The skip block is not empty: it must be left alone, so the first loop stays
; unguarded and the loops are not fused.
define void @guard_skip_block_not_empty(ptr noalias %A, i64 %N) {
; CHECK-LABEL: define void @guard_skip_block_not_empty(
; CHECK-SAME: ptr noalias [[A:%.*]], i64 [[N:%.*]]) {
; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: [[CMP4:%.*]] = icmp slt i64 0, [[N]]
; CHECK-NEXT: br i1 [[CMP4]], label %[[BB3:.*]], label %[[SKIP:.*]]
; CHECK: [[SKIP]]:
; CHECK-NEXT: store i32 0, ptr [[A]], align 4
; CHECK-NEXT: br label %[[BB14:.*]]
; CHECK: [[BB3]]:
; CHECK-NEXT: br label %[[BB5:.*]]
; CHECK: [[BB5]]:
; CHECK-NEXT: [[I_05:%.*]] = phi i64 [ [[INC:%.*]], %[[BB5]] ], [ 0, %[[BB3]] ]
; CHECK-NEXT: [[CONV:%.*]] = trunc i64 [[I_05]] to i32
; CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds i32, ptr [[A]], i64 [[I_05]]
; CHECK-NEXT: store i32 [[CONV]], ptr [[ARRAYIDX]], align 4
; CHECK-NEXT: [[INC]] = add nsw i64 [[I_05]], 1
; CHECK-NEXT: [[CMP:%.*]] = icmp slt i64 [[INC]], [[N]]
; CHECK-NEXT: br i1 [[CMP]], label %[[BB5]], label %[[BB10:.*]]
; CHECK: [[BB10]]:
; CHECK-NEXT: br label %[[BB14]]
; CHECK: [[BB14]]:
; CHECK-NEXT: [[CMP31:%.*]] = icmp slt i64 0, [[N]]
; CHECK-NEXT: br i1 [[CMP31]], label %[[BB8:.*]], label %[[BB12:.*]]
; CHECK: [[BB8]]:
; CHECK-NEXT: br label %[[BB9:.*]]
; CHECK: [[BB9]]:
; CHECK-NEXT: [[I1_02:%.*]] = phi i64 [ [[INC14:%.*]], %[[BB9]] ], [ 0, %[[BB8]] ]
; CHECK-NEXT: [[CONV11:%.*]] = trunc i64 [[I1_02]] to i32
; CHECK-NEXT: [[ARRAYIDX12:%.*]] = getelementptr inbounds [1024 x i32], ptr @B, i64 0, i64 [[I1_02]]
; CHECK-NEXT: store i32 [[CONV11]], ptr [[ARRAYIDX12]], align 4
; CHECK-NEXT: [[INC14]] = add nsw i64 [[I1_02]], 1
; CHECK-NEXT: [[CMP3:%.*]] = icmp slt i64 [[INC14]], [[N]]
; CHECK-NEXT: br i1 [[CMP3]], label %[[BB9]], label %[[BB15:.*]]
; CHECK: [[BB15]]:
; CHECK-NEXT: br label %[[BB12]]
; CHECK: [[BB12]]:
; CHECK-NEXT: ret void
;
entry:
%cmp4 = icmp slt i64 0, %N
br i1 %cmp4, label %blk3, label %skip
skip:
store i32 0, ptr %A, align 4
br label %blk14
blk3:
br label %blk5
blk5:
%i.05 = phi i64 [ %inc, %blk5 ], [ 0, %blk3 ]
%conv = trunc i64 %i.05 to i32
%arrayidx = getelementptr inbounds i32, ptr %A, i64 %i.05
store i32 %conv, ptr %arrayidx, align 4
%inc = add nsw i64 %i.05, 1
%cmp = icmp slt i64 %inc, %N
br i1 %cmp, label %blk5, label %blk10
blk10:
br label %blk14
blk14:
%cmp31 = icmp slt i64 0, %N
br i1 %cmp31, label %blk8, label %blk12
blk8:
br label %blk9
blk9:
%i1.02 = phi i64 [ %inc14, %blk9 ], [ 0, %blk8 ]
%conv11 = trunc i64 %i1.02 to i32
%arrayidx12 = getelementptr inbounds [1024 x i32], ptr @B, i64 0, i64 %i1.02
store i32 %conv11, ptr %arrayidx12, align 4
%inc14 = add nsw i64 %i1.02, 1
%cmp3 = icmp slt i64 %inc14, %N
br i1 %cmp3, label %blk9, label %blk15
blk15:
br label %blk12
blk12:
ret void
}