| ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6 | |
| ; RUN: opt -passes="default<O3>" -enable-loop-flatten -loop-flatten-cost-threshold=3 -S %s | FileCheck %s | |
| target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128" | |
| target triple = "aarch64" | |
| ; TODO: @foo_32_bit_iv is no longer flattened, because its i32 bit IV gets | |
| ; widened and LoopFlatten does not recognize the wide form. | |
| define void @foo_32_bit_iv(ptr %A, i32 %N, i32 %M) { | |
| ; CHECK-LABEL: define void @foo_32_bit_iv( | |
| ; CHECK-SAME: ptr nofree readonly captures(none) [[A:%.*]], i32 [[N:%.*]], i32 [[M:%.*]]) local_unnamed_addr { | |
| ; CHECK-NEXT: [[ENTRY:.*:]] | |
| ; CHECK-NEXT: [[CMP3:%.*]] = icmp sgt i32 [[N]], 0 | |
| ; CHECK-NEXT: [[CMP21:%.*]] = icmp sgt i32 [[M]], 0 | |
| ; CHECK-NEXT: [[OR_COND:%.*]] = select i1 [[CMP3]], i1 [[CMP21]], i1 false | |
| ; CHECK-NEXT: br i1 [[OR_COND]], label %[[INNER_HEADER_PREHEADER_LR_PH_SPLIT:.*]], label %[[EXIT:.*]] | |
| ; CHECK: [[INNER_HEADER_PREHEADER_LR_PH_SPLIT]]: | |
| ; CHECK-NEXT: [[TMP0:%.*]] = zext nneg i32 [[M]] to i64 | |
| ; CHECK-NEXT: [[TMP1:%.*]] = zext nneg i32 [[N]] to i64 | |
| ; CHECK-NEXT: br label %[[INNER_HEADER_PREHEADER:.*]] | |
| ; CHECK: [[INNER_HEADER_PREHEADER]]: | |
| ; CHECK-NEXT: [[INDVAR6:%.*]] = phi i64 [ 0, %[[INNER_HEADER_PREHEADER_LR_PH_SPLIT]] ], [ [[INDVAR_NEXT7:%.*]], %[[INNER_HEADER_OUTER_LATCH_CRIT_EDGE:.*]] ] | |
| ; CHECK-NEXT: [[TMP5:%.*]] = trunc i64 [[INDVAR6]] to i32 | |
| ; CHECK-NEXT: [[TMP3:%.*]] = mul i32 [[M]], [[TMP5]] | |
| ; CHECK-NEXT: [[TMP4:%.*]] = sext i32 [[TMP3]] to i64 | |
| ; CHECK-NEXT: [[INVARIANT_GEP:%.*]] = getelementptr [4 x i8], ptr [[A]], i64 [[TMP4]] | |
| ; CHECK-NEXT: br label %[[INNER_LATCH:.*]] | |
| ; CHECK: [[INNER_LATCH]]: | |
| ; CHECK-NEXT: [[INDVAR:%.*]] = phi i64 [ 0, %[[INNER_HEADER_PREHEADER]] ], [ [[INDVAR_NEXT:%.*]], %[[INNER_LATCH]] ] | |
| ; CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr [4 x i8], ptr [[INVARIANT_GEP]], i64 [[INDVAR]] | |
| ; CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[ARRAYIDX]], align 4 | |
| ; CHECK-NEXT: tail call void @_Z1fi(i32 [[TMP2]]) | |
| ; CHECK-NEXT: [[INDVAR_NEXT]] = add nuw nsw i64 [[INDVAR]], 1 | |
| ; CHECK-NEXT: [[EXITCOND_NOT1:%.*]] = icmp eq i64 [[INDVAR_NEXT]], [[TMP0]] | |
| ; CHECK-NEXT: br i1 [[EXITCOND_NOT1]], label %[[INNER_HEADER_OUTER_LATCH_CRIT_EDGE]], label %[[INNER_LATCH]] | |
| ; CHECK: [[INNER_HEADER_OUTER_LATCH_CRIT_EDGE]]: | |
| ; CHECK-NEXT: [[INDVAR_NEXT7]] = add nuw nsw i64 [[INDVAR6]], 1 | |
| ; CHECK-NEXT: [[EXITCOND_NOT:%.*]] = icmp eq i64 [[INDVAR_NEXT7]], [[TMP1]] | |
| ; CHECK-NEXT: br i1 [[EXITCOND_NOT]], label %[[EXIT]], label %[[INNER_HEADER_PREHEADER]] | |
| ; CHECK: [[EXIT]]: | |
| ; CHECK-NEXT: ret void | |
| ; | |
| entry: | |
| br label %outer.header | |
| outer.header: | |
| %outer.iv = phi i32 [ 0, %entry ], [ %outer.iv.next, %outer.latch ] | |
| %cmp = icmp slt i32 %outer.iv, %N | |
| br i1 %cmp, label %outer.body, label %exit | |
| outer.body: | |
| br label %inner.header | |
| inner.header: | |
| %inner.iv = phi i32 [ 0, %outer.body ], [ %inner.iv.next, %inner.latch ] | |
| %cmp2 = icmp slt i32 %inner.iv, %M | |
| br i1 %cmp2, label %inner.latch, label %outer.latch | |
| inner.latch: | |
| %mul = mul nsw i32 %outer.iv, %M | |
| %add = add nsw i32 %mul, %inner.iv | |
| %idxprom = sext i32 %add to i64 | |
| %arrayidx = getelementptr inbounds i32, ptr %A, i64 %idxprom | |
| %0 = load i32, ptr %arrayidx, align 4 | |
| call void @_Z1fi(i32 %0) | |
| %inner.iv.next = add nsw i32 %inner.iv, 1 | |
| br label %inner.header | |
| outer.latch: | |
| %outer.iv.next = add nsw i32 %outer.iv, 1 | |
| br label %outer.header | |
| exit: | |
| ret void | |
| } | |
| declare dso_local void @_Z1fi(i32) #2 | |
| ; Same nest, but with i64 induction variables. | |
| define dso_local void @foo_64_bit_iv(ptr %A, i64 %N, i64 %M) { | |
| ; CHECK-LABEL: define dso_local void @foo_64_bit_iv( | |
| ; CHECK-SAME: ptr nofree readonly captures(none) [[A:%.*]], i64 [[N:%.*]], i64 [[M:%.*]]) local_unnamed_addr { | |
| ; CHECK-NEXT: [[ENTRY:.*:]] | |
| ; CHECK-NEXT: [[CMP3:%.*]] = icmp sgt i64 [[N]], 0 | |
| ; CHECK-NEXT: [[CMP21:%.*]] = icmp sgt i64 [[M]], 0 | |
| ; CHECK-NEXT: [[OR_COND:%.*]] = select i1 [[CMP3]], i1 [[CMP21]], i1 false | |
| ; CHECK-NEXT: br i1 [[OR_COND]], label %[[INNER_HEADER_PREHEADER_LVER_CHECK:.*]], label %[[EXIT:.*]] | |
| ; CHECK: [[INNER_HEADER_PREHEADER_LVER_CHECK]]: | |
| ; CHECK-NEXT: [[FLATTEN_MUL:%.*]] = tail call { i64, i1 } @llvm.umul.with.overflow.i64(i64 [[N]], i64 [[M]]) | |
| ; CHECK-NEXT: [[FLATTEN_OVERFLOW:%.*]] = extractvalue { i64, i1 } [[FLATTEN_MUL]], 1 | |
| ; CHECK-NEXT: br i1 [[FLATTEN_OVERFLOW]], label %[[INNER_HEADER_PREHEADER_LVER_ORIG:.*]], label %[[INNER_HEADER_PREHEADER_PREHEADER:.*]] | |
| ; CHECK: [[INNER_HEADER_PREHEADER_PREHEADER]]: | |
| ; CHECK-NEXT: [[FLATTEN_TRIPCOUNT:%.*]] = extractvalue { i64, i1 } [[FLATTEN_MUL]], 0 | |
| ; CHECK-NEXT: [[SMAX:%.*]] = tail call i64 @llvm.smax.i64(i64 [[FLATTEN_TRIPCOUNT]], i64 1) | |
| ; CHECK-NEXT: br label %[[INNER_HEADER_PREHEADER:.*]] | |
| ; CHECK: [[INNER_HEADER_PREHEADER_LVER_ORIG]]: | |
| ; CHECK-NEXT: [[OUTER_IV4_LVER_ORIG:%.*]] = phi i64 [ [[OUTER_IV_NEXT_LVER_ORIG:%.*]], %[[INNER_HEADER_OUTER_LATCH_CRIT_EDGE_LVER_ORIG:.*]] ], [ 0, %[[INNER_HEADER_PREHEADER_LVER_CHECK]] ] | |
| ; CHECK-NEXT: [[MUL_LVER_ORIG:%.*]] = mul nuw nsw i64 [[OUTER_IV4_LVER_ORIG]], [[M]] | |
| ; CHECK-NEXT: [[TMP0:%.*]] = getelementptr [4 x i8], ptr [[A]], i64 [[MUL_LVER_ORIG]] | |
| ; CHECK-NEXT: br label %[[INNER_LATCH_LVER_ORIG:.*]] | |
| ; CHECK: [[INNER_LATCH_LVER_ORIG]]: | |
| ; CHECK-NEXT: [[INNER_IV2_LVER_ORIG:%.*]] = phi i64 [ 0, %[[INNER_HEADER_PREHEADER_LVER_ORIG]] ], [ [[INNER_IV_NEXT_LVER_ORIG:%.*]], %[[INNER_LATCH_LVER_ORIG]] ] | |
| ; CHECK-NEXT: [[ARRAYIDX_LVER_ORIG:%.*]] = getelementptr [4 x i8], ptr [[TMP0]], i64 [[INNER_IV2_LVER_ORIG]] | |
| ; CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[ARRAYIDX_LVER_ORIG]], align 4 | |
| ; CHECK-NEXT: tail call void @_Z1fi(i32 [[TMP1]]) | |
| ; CHECK-NEXT: [[INNER_IV_NEXT_LVER_ORIG]] = add nuw nsw i64 [[INNER_IV2_LVER_ORIG]], 1 | |
| ; CHECK-NEXT: [[EXITCOND7_NOT:%.*]] = icmp eq i64 [[INNER_IV_NEXT_LVER_ORIG]], [[M]] | |
| ; CHECK-NEXT: br i1 [[EXITCOND7_NOT]], label %[[INNER_HEADER_OUTER_LATCH_CRIT_EDGE_LVER_ORIG]], label %[[INNER_LATCH_LVER_ORIG]] | |
| ; CHECK: [[INNER_HEADER_OUTER_LATCH_CRIT_EDGE_LVER_ORIG]]: | |
| ; CHECK-NEXT: [[OUTER_IV_NEXT_LVER_ORIG]] = add nuw nsw i64 [[OUTER_IV4_LVER_ORIG]], 1 | |
| ; CHECK-NEXT: [[EXITCOND8_NOT:%.*]] = icmp eq i64 [[OUTER_IV_NEXT_LVER_ORIG]], [[N]] | |
| ; CHECK-NEXT: br i1 [[EXITCOND8_NOT]], label %[[EXIT]], label %[[INNER_HEADER_PREHEADER_LVER_ORIG]] | |
| ; CHECK: [[INNER_HEADER_PREHEADER]]: | |
| ; CHECK-NEXT: [[OUTER_IV4:%.*]] = phi i64 [ [[OUTER_IV_NEXT:%.*]], %[[INNER_HEADER_PREHEADER]] ], [ 0, %[[INNER_HEADER_PREHEADER_PREHEADER]] ] | |
| ; CHECK-NEXT: [[FLATTEN_ARRAYIDX:%.*]] = getelementptr [4 x i8], ptr [[A]], i64 [[OUTER_IV4]] | |
| ; CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[FLATTEN_ARRAYIDX]], align 4 | |
| ; CHECK-NEXT: tail call void @_Z1fi(i32 [[TMP2]]) | |
| ; CHECK-NEXT: [[OUTER_IV_NEXT]] = add nuw nsw i64 [[OUTER_IV4]], 1 | |
| ; CHECK-NEXT: [[EXITCOND_NOT:%.*]] = icmp eq i64 [[OUTER_IV_NEXT]], [[SMAX]] | |
| ; CHECK-NEXT: br i1 [[EXITCOND_NOT]], label %[[EXIT]], label %[[INNER_HEADER_PREHEADER]] | |
| ; CHECK: [[EXIT]]: | |
| ; CHECK-NEXT: ret void | |
| ; | |
| entry: | |
| br label %outer.header | |
| outer.header: | |
| %outer.iv = phi i64 [ 0, %entry ], [ %outer.iv.next, %outer.latch ] | |
| %cmp = icmp slt i64 %outer.iv, %N | |
| br i1 %cmp, label %outer.body, label %exit | |
| outer.body: | |
| br label %inner.header | |
| inner.header: | |
| %inner.iv = phi i64 [ 0, %outer.body ], [ %inner.iv.next, %inner.latch ] | |
| %cmp2 = icmp slt i64 %inner.iv, %M | |
| br i1 %cmp2, label %inner.latch, label %outer.latch | |
| inner.latch: | |
| %mul = mul nsw i64 %outer.iv, %M | |
| %add = add nsw i64 %mul, %inner.iv | |
| %arrayidx = getelementptr inbounds i32, ptr %A, i64 %add | |
| %0 = load i32, ptr %arrayidx, align 4 | |
| call void @_Z1fi(i32 %0) | |
| %inner.iv.next = add nsw i64 %inner.iv, 1 | |
| br label %inner.header | |
| outer.latch: | |
| %outer.iv.next = add nsw i64 %outer.iv, 1 | |
| br label %outer.header | |
| exit: | |
| ret void | |
| } |