| ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6 | |
| ; RUN: opt -S -passes=loop-fusion -loop-fusion-disable-cost-model -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 | |
| } |