| ; 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 |
| } |