[Inliner] Handle `mustprogress` functions

When inlining `mustprogress` functions, if the caller or the callee has
the attribute, we drop the function attribute. The loops that have the
`llvm.loop.mustprogress` metadata keep their metadata. We do not need to
add new loop metadata to inlined functions because the patch in D86841
already adds the relevant loop metadata in all of the necessary places.

Reviewed By: jdoerfert

Differential Revision: https://reviews.llvm.org/D87262
This commit is contained in:
Atmn Patel 2020-11-06 00:10:23 -05:00
parent 1ca7f055ad
commit 46a29e9c6e
2 changed files with 529 additions and 0 deletions

View File

@ -294,3 +294,4 @@ def : MergeRule<"adjustCallerStackProbes">;
def : MergeRule<"adjustCallerStackProbeSize">;
def : MergeRule<"adjustMinLegalVectorWidth">;
def : MergeRule<"adjustNullPointerValidAttr">;
def : MergeRule<"setAND<MustProgressAttr>">;

View File

@ -0,0 +1,528 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --check-attributes --force-update
; RUN: opt < %s -S -passes="inline" | FileCheck %s
define void @callee(i32 %a, i32 %b) #0 {
entry:
br label %for.cond
for.cond:
%cmp = icmp slt i32 %a, %b
br i1 %cmp, label %for.body, label %for.end
for.body:
br label %for.cond, !llvm.loop !0
for.end:
br label %while.body
while.body:
br label %while.body
}
define void @caller(i32 %a, i32 %b) #1 {
; CHECK: Function Attrs: noinline
; CHECK-LABEL: define {{[^@]+}}@caller
; CHECK-SAME: (i32 [[A:%.*]], i32 [[B:%.*]]) [[ATTR1:#.*]] {
; CHECK-NEXT: entry:
; CHECK-NEXT: br label [[FOR_COND:%.*]]
; CHECK: for.cond:
; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[A]], [[B]]
; CHECK-NEXT: br i1 [[CMP]], label [[FOR_BODY:%.*]], label [[FOR_END:%.*]]
; CHECK: for.body:
; CHECK-NEXT: br label [[FOR_COND]]
; CHECK: for.end:
; CHECK-NEXT: br label [[FOR_COND_I:%.*]]
; CHECK: for.cond.i:
; CHECK-NEXT: br label [[FOR_COND_I]], !llvm.loop [[LOOP0:![0-9]+]]
; CHECK: callee.exit:
; CHECK-NEXT: ret void
;
entry:
br label %for.cond
for.cond:
%cmp = icmp slt i32 %a, %b
br i1 %cmp, label %for.body, label %for.end
for.body:
br label %for.cond
for.end:
call void @callee(i32 0, i32 5)
ret void
}
define void @callee_no_metadata(i32 %a, i32 %b) {
entry:
br label %for.cond
for.cond:
%cmp = icmp slt i32 %a, %b
br i1 %cmp, label %for.body, label %for.end
for.body:
br label %for.cond
for.end:
br label %while.body
while.body:
br label %while.body
}
define void @caller_no_metadata(i32 %a, i32 %b) {
; CHECK-LABEL: define {{[^@]+}}@caller_no_metadata
; CHECK-SAME: (i32 [[A:%.*]], i32 [[B:%.*]]) {
; CHECK-NEXT: entry:
; CHECK-NEXT: br label [[FOR_COND:%.*]]
; CHECK: for.cond:
; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[A]], [[B]]
; CHECK-NEXT: br i1 [[CMP]], label [[FOR_BODY:%.*]], label [[FOR_END:%.*]]
; CHECK: for.body:
; CHECK-NEXT: br label [[FOR_COND]]
; CHECK: for.end:
; CHECK-NEXT: br label [[FOR_COND_I:%.*]]
; CHECK: for.cond.i:
; CHECK-NEXT: br label [[FOR_COND_I]]
; CHECK: callee_no_metadata.exit:
; CHECK-NEXT: ret void
;
entry:
br label %for.cond
for.cond:
%cmp = icmp slt i32 %a, %b
br i1 %cmp, label %for.body, label %for.end
for.body:
br label %for.cond
for.end:
call void @callee_no_metadata(i32 0, i32 5)
ret void
}
define void @callee_mustprogress(i32 %a, i32 %b) #0 {
entry:
br label %for.cond
for.cond:
%cmp = icmp slt i32 %a, %b
br i1 %cmp, label %for.body, label %for.end
for.body:
br label %for.cond
for.end:
br label %while.body
while.body:
br label %while.body
}
define void @caller_mustprogress(i32 %a, i32 %b) #0 {
; CHECK: Function Attrs: mustprogress
; CHECK-LABEL: define {{[^@]+}}@caller_mustprogress
; CHECK-SAME: (i32 [[A:%.*]], i32 [[B:%.*]]) [[ATTR0:#[0-9]+]] {
; CHECK-NEXT: entry:
; CHECK-NEXT: br label [[FOR_COND:%.*]]
; CHECK: for.cond:
; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[A]], [[B]]
; CHECK-NEXT: br i1 [[CMP]], label [[FOR_BODY:%.*]], label [[FOR_END:%.*]]
; CHECK: for.body:
; CHECK-NEXT: br label [[FOR_COND]]
; CHECK: for.end:
; CHECK-NEXT: br label [[FOR_COND_I:%.*]]
; CHECK: for.cond.i:
; CHECK-NEXT: br label [[FOR_COND_I]]
; CHECK: callee_mustprogress.exit:
; CHECK-NEXT: ret void
;
entry:
br label %for.cond
for.cond:
%cmp = icmp slt i32 %a, %b
br i1 %cmp, label %for.body, label %for.end
for.body:
br label %for.cond
for.end:
call void @callee_mustprogress(i32 0, i32 5)
ret void
}
define void @caller_mustprogress_callee_no_metadata(i32 %a, i32 %b) #0 {
; CHECK-LABEL: define {{[^@]+}}@caller_mustprogress_callee_no_metadata
; CHECK-SAME: (i32 [[A:%.*]], i32 [[B:%.*]]) {
; CHECK-NEXT: entry:
; CHECK-NEXT: br label [[FOR_COND:%.*]]
; CHECK: for.cond:
; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[A]], [[B]]
; CHECK-NEXT: br i1 [[CMP]], label [[FOR_BODY:%.*]], label [[FOR_END:%.*]]
; CHECK: for.body:
; CHECK-NEXT: br label [[FOR_COND]]
; CHECK: for.end:
; CHECK-NEXT: br label [[FOR_COND_I:%.*]]
; CHECK: for.cond.i:
; CHECK-NEXT: br label [[FOR_COND_I]]
; CHECK: callee_no_metadata.exit:
; CHECK-NEXT: ret void
;
entry:
br label %for.cond
for.cond:
%cmp = icmp slt i32 %a, %b
br i1 %cmp, label %for.body, label %for.end
for.body:
br label %for.cond
for.end:
call void @callee_no_metadata(i32 0, i32 5)
ret void
}
define void @callee_multiple(i32 %a, i32 %b) #0 {
entry:
%a.addr = alloca i32, align 4
%b.addr = alloca i32, align 4
%i = alloca i32, align 4
store i32 %a, i32* %a.addr, align 4
store i32 %b, i32* %b.addr, align 4
br label %for.cond
for.cond:
%0 = load i32, i32* %a.addr, align 4
%1 = load i32, i32* %b.addr, align 4
%cmp = icmp slt i32 %0, %1
br i1 %cmp, label %for.body, label %for.end
for.body:
br label %for.cond, !llvm.loop !2
for.end:
store i32 0, i32* %i, align 4
br label %for.cond1
for.cond1:
%2 = load i32, i32* %i, align 4
%cmp2 = icmp slt i32 %2, 10
br i1 %cmp2, label %for.body3, label %for.end4
for.body3:
br label %for.inc
for.inc:
%3 = load i32, i32* %i, align 4
%inc = add nsw i32 %3, 1
store i32 %inc, i32* %i, align 4
br label %for.cond1, !llvm.loop !4
for.end4:
br label %while.body
while.body:
br label %while.body
}
define void @caller_multiple(i32 %a, i32 %b) #1 {
; CHECK: Function Attrs: noinline
; CHECK-LABEL: define {{[^@]+}}@caller_multiple
; CHECK-SAME: (i32 [[A:%.*]], i32 [[B:%.*]]) [[ATTR1]] {
; CHECK-NEXT: entry:
; CHECK-NEXT: [[A_ADDR_I:%.*]] = alloca i32, align 4
; CHECK-NEXT: [[B_ADDR_I:%.*]] = alloca i32, align 4
; CHECK-NEXT: [[I_I:%.*]] = alloca i32, align 4
; CHECK-NEXT: [[A_ADDR:%.*]] = alloca i32, align 4
; CHECK-NEXT: [[B_ADDR:%.*]] = alloca i32, align 4
; CHECK-NEXT: [[I:%.*]] = alloca i32, align 4
; CHECK-NEXT: store i32 [[A]], i32* [[A_ADDR]], align 4
; CHECK-NEXT: store i32 [[B]], i32* [[B_ADDR]], align 4
; CHECK-NEXT: br label [[FOR_COND:%.*]]
; CHECK: for.cond:
; CHECK-NEXT: [[TMP0:%.*]] = load i32, i32* [[A_ADDR]], align 4
; CHECK-NEXT: [[TMP1:%.*]] = load i32, i32* [[B_ADDR]], align 4
; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[TMP0]], [[TMP1]]
; CHECK-NEXT: br i1 [[CMP]], label [[FOR_BODY:%.*]], label [[FOR_END:%.*]]
; CHECK: for.body:
; CHECK-NEXT: br label [[FOR_COND]]
; CHECK: for.end:
; CHECK-NEXT: store i32 0, i32* [[I]], align 4
; CHECK-NEXT: br label [[FOR_COND1:%.*]]
; CHECK: for.cond1:
; CHECK-NEXT: [[TMP2:%.*]] = load i32, i32* [[I]], align 4
; CHECK-NEXT: [[CMP2:%.*]] = icmp slt i32 [[TMP2]], 10
; CHECK-NEXT: br i1 [[CMP2]], label [[FOR_BODY3:%.*]], label [[FOR_END4:%.*]]
; CHECK: for.body3:
; CHECK-NEXT: br label [[FOR_INC:%.*]]
; CHECK: for.inc:
; CHECK-NEXT: [[TMP3:%.*]] = load i32, i32* [[I]], align 4
; CHECK-NEXT: [[INC:%.*]] = add nsw i32 [[TMP3]], 1
; CHECK-NEXT: store i32 [[INC]], i32* [[I]], align 4
; CHECK-NEXT: br label [[FOR_COND1]]
; CHECK: for.end4:
; CHECK-NEXT: [[TMP4:%.*]] = bitcast i32* [[A_ADDR_I]] to i8*
; CHECK-NEXT: call void @llvm.lifetime.start.p0i8(i64 4, i8* [[TMP4]])
; CHECK-NEXT: [[TMP5:%.*]] = bitcast i32* [[B_ADDR_I]] to i8*
; CHECK-NEXT: call void @llvm.lifetime.start.p0i8(i64 4, i8* [[TMP5]])
; CHECK-NEXT: [[TMP6:%.*]] = bitcast i32* [[I_I]] to i8*
; CHECK-NEXT: call void @llvm.lifetime.start.p0i8(i64 4, i8* [[TMP6]])
; CHECK-NEXT: store i32 0, i32* [[A_ADDR_I]], align 4
; CHECK-NEXT: store i32 5, i32* [[B_ADDR_I]], align 4
; CHECK-NEXT: br label [[FOR_COND_I:%.*]]
; CHECK: for.cond.i:
; CHECK-NEXT: [[TMP7:%.*]] = load i32, i32* [[A_ADDR_I]], align 4
; CHECK-NEXT: [[TMP8:%.*]] = load i32, i32* [[B_ADDR_I]], align 4
; CHECK-NEXT: [[CMP_I:%.*]] = icmp slt i32 [[TMP7]], [[TMP8]]
; CHECK-NEXT: br i1 [[CMP_I]], label [[FOR_BODY_I:%.*]], label [[FOR_END_I:%.*]]
; CHECK: for.body.i:
; CHECK-NEXT: br label [[FOR_COND_I]], !llvm.loop [[LOOP2:![0-9]+]]
; CHECK: for.end.i:
; CHECK-NEXT: store i32 0, i32* [[I_I]], align 4
; CHECK-NEXT: br label [[FOR_COND1_I:%.*]]
; CHECK: for.cond1.i:
; CHECK-NEXT: [[TMP9:%.*]] = load i32, i32* [[I_I]], align 4
; CHECK-NEXT: [[CMP2_I:%.*]] = icmp slt i32 [[TMP9]], 10
; CHECK-NEXT: br i1 [[CMP2_I]], label [[FOR_BODY3_I:%.*]], label [[FOR_END4_I:%.*]]
; CHECK: for.body3.i:
; CHECK-NEXT: [[TMP10:%.*]] = load i32, i32* [[I_I]], align 4
; CHECK-NEXT: [[INC_I:%.*]] = add nsw i32 [[TMP10]], 1
; CHECK-NEXT: store i32 [[INC_I]], i32* [[I_I]], align 4
; CHECK-NEXT: br label [[FOR_COND1_I]], !llvm.loop [[LOOP3:![0-9]+]]
; CHECK: for.end4.i:
; CHECK-NEXT: br label [[WHILE_BODY_I:%.*]]
; CHECK: while.body.i:
; CHECK-NEXT: br label [[WHILE_BODY_I]]
; CHECK: callee_multiple.exit:
; CHECK-NEXT: ret void
;
entry:
%a.addr = alloca i32, align 4
%b.addr = alloca i32, align 4
%i = alloca i32, align 4
store i32 %a, i32* %a.addr, align 4
store i32 %b, i32* %b.addr, align 4
br label %for.cond
for.cond:
%0 = load i32, i32* %a.addr, align 4
%1 = load i32, i32* %b.addr, align 4
%cmp = icmp slt i32 %0, %1
br i1 %cmp, label %for.body, label %for.end
for.body:
br label %for.cond
for.end:
store i32 0, i32* %i, align 4
br label %for.cond1
for.cond1:
%2 = load i32, i32* %i, align 4
%cmp2 = icmp slt i32 %2, 10
br i1 %cmp2, label %for.body3, label %for.end4
for.body3:
br label %for.inc
for.inc:
%3 = load i32, i32* %i, align 4
%inc = add nsw i32 %3, 1
store i32 %inc, i32* %i, align 4
br label %for.cond1
for.end4:
call void @callee_multiple(i32 0, i32 5)
ret void
}
define void @callee_nested(i32 %a, i32 %b) #0 {
entry:
%a.addr = alloca i32, align 4
%b.addr = alloca i32, align 4
%i = alloca i32, align 4
store i32 %a, i32* %a.addr, align 4
store i32 %b, i32* %b.addr, align 4
br label %for.cond
for.cond:
%0 = load i32, i32* %a.addr, align 4
%1 = load i32, i32* %b.addr, align 4
%cmp = icmp slt i32 %0, %1
br i1 %cmp, label %for.body, label %for.end
for.body:
br label %for.cond, !llvm.loop !0
for.end:
store i32 0, i32* %i, align 4
br label %for.cond1
for.cond1:
%2 = load i32, i32* %i, align 4
%cmp2 = icmp slt i32 %2, 10
br i1 %cmp2, label %for.body3, label %for.end8
for.body3:
br label %for.cond4
for.cond4:
%3 = load i32, i32* %b.addr, align 4
%4 = load i32, i32* %a.addr, align 4
%cmp5 = icmp slt i32 %3, %4
br i1 %cmp5, label %for.body6, label %for.end7
for.body6:
br label %for.cond4, !llvm.loop !2
for.end7:
br label %for.inc
for.inc:
%5 = load i32, i32* %i, align 4
%inc = add nsw i32 %5, 1
store i32 %inc, i32* %i, align 4
br label %for.cond1, !llvm.loop !3
for.end8:
br label %while.body
while.body:
br label %while.body
}
define void @caller_nested(i32 %a, i32 %b) #1 {
; CHECK: Function Attrs: noinline
; CHECK-LABEL: define {{[^@]+}}@caller_nested
; CHECK-SAME: (i32 [[A:%.*]], i32 [[B:%.*]]) [[ATTR1]] {
; CHECK-NEXT: entry:
; CHECK-NEXT: [[A_ADDR_I:%.*]] = alloca i32, align 4
; CHECK-NEXT: [[B_ADDR_I:%.*]] = alloca i32, align 4
; CHECK-NEXT: [[I_I:%.*]] = alloca i32, align 4
; CHECK-NEXT: [[A_ADDR:%.*]] = alloca i32, align 4
; CHECK-NEXT: [[B_ADDR:%.*]] = alloca i32, align 4
; CHECK-NEXT: [[I:%.*]] = alloca i32, align 4
; CHECK-NEXT: [[I9:%.*]] = alloca i32, align 4
; CHECK-NEXT: store i32 [[A]], i32* [[A_ADDR]], align 4
; CHECK-NEXT: store i32 [[B]], i32* [[B_ADDR]], align 4
; CHECK-NEXT: br label [[FOR_COND:%.*]]
; CHECK: for.cond:
; CHECK-NEXT: [[TMP0:%.*]] = load i32, i32* [[A_ADDR]], align 4
; CHECK-NEXT: [[TMP1:%.*]] = load i32, i32* [[B_ADDR]], align 4
; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[TMP0]], [[TMP1]]
; CHECK-NEXT: br i1 [[CMP]], label [[FOR_BODY:%.*]], label [[FOR_END8:%.*]]
; CHECK: for.body:
; CHECK-NEXT: store i32 0, i32* [[I]], align 4
; CHECK-NEXT: br label [[FOR_COND1:%.*]]
; CHECK: for.cond1:
; CHECK-NEXT: [[TMP2:%.*]] = load i32, i32* [[I]], align 4
; CHECK-NEXT: [[CMP2:%.*]] = icmp slt i32 [[TMP2]], 10
; CHECK-NEXT: br i1 [[CMP2]], label [[FOR_BODY3:%.*]], label [[FOR_END7:%.*]]
; CHECK: for.body3:
; CHECK-NEXT: br label [[FOR_COND4:%.*]]
; CHECK: for.cond4:
; CHECK-NEXT: [[TMP3:%.*]] = load i32, i32* [[B_ADDR]], align 4
; CHECK-NEXT: [[TMP4:%.*]] = load i32, i32* [[A_ADDR]], align 4
; CHECK-NEXT: [[CMP5:%.*]] = icmp slt i32 [[TMP3]], [[TMP4]]
; CHECK-NEXT: br i1 [[CMP5]], label [[FOR_BODY6:%.*]], label [[FOR_END:%.*]]
; CHECK: for.body6:
; CHECK-NEXT: br label [[FOR_COND4]]
; CHECK: for.end:
; CHECK-NEXT: br label [[FOR_INC:%.*]]
; CHECK: for.inc:
; CHECK-NEXT: [[TMP5:%.*]] = load i32, i32* [[I]], align 4
; CHECK-NEXT: [[INC:%.*]] = add nsw i32 [[TMP5]], 1
; CHECK-NEXT: store i32 [[INC]], i32* [[I]], align 4
; CHECK-NEXT: br label [[FOR_COND1]]
; CHECK: for.end7:
; CHECK-NEXT: br label [[FOR_COND]]
; CHECK: for.end8:
; CHECK-NEXT: store i32 0, i32* [[I9]], align 4
; CHECK-NEXT: br label [[FOR_COND10:%.*]]
; CHECK: for.cond10:
; CHECK-NEXT: [[TMP6:%.*]] = load i32, i32* [[I9]], align 4
; CHECK-NEXT: [[CMP11:%.*]] = icmp slt i32 [[TMP6]], 10
; CHECK-NEXT: br i1 [[CMP11]], label [[FOR_BODY12:%.*]], label [[FOR_END15:%.*]]
; CHECK: for.body12:
; CHECK-NEXT: br label [[FOR_INC13:%.*]]
; CHECK: for.inc13:
; CHECK-NEXT: [[TMP7:%.*]] = load i32, i32* [[I9]], align 4
; CHECK-NEXT: [[INC14:%.*]] = add nsw i32 [[TMP7]], 1
; CHECK-NEXT: store i32 [[INC14]], i32* [[I9]], align 4
; CHECK-NEXT: br label [[FOR_COND10]]
; CHECK: for.end15:
; CHECK-NEXT: [[TMP8:%.*]] = bitcast i32* [[A_ADDR_I]] to i8*
; CHECK-NEXT: call void @llvm.lifetime.start.p0i8(i64 4, i8* [[TMP8]])
; CHECK-NEXT: [[TMP9:%.*]] = bitcast i32* [[B_ADDR_I]] to i8*
; CHECK-NEXT: call void @llvm.lifetime.start.p0i8(i64 4, i8* [[TMP9]])
; CHECK-NEXT: [[TMP10:%.*]] = bitcast i32* [[I_I]] to i8*
; CHECK-NEXT: call void @llvm.lifetime.start.p0i8(i64 4, i8* [[TMP10]])
; CHECK-NEXT: store i32 0, i32* [[A_ADDR_I]], align 4
; CHECK-NEXT: store i32 5, i32* [[B_ADDR_I]], align 4
; CHECK-NEXT: br label [[FOR_COND_I:%.*]]
; CHECK: for.cond.i:
; CHECK-NEXT: [[TMP11:%.*]] = load i32, i32* [[A_ADDR_I]], align 4
; CHECK-NEXT: [[TMP12:%.*]] = load i32, i32* [[B_ADDR_I]], align 4
; CHECK-NEXT: [[CMP_I:%.*]] = icmp slt i32 [[TMP11]], [[TMP12]]
; CHECK-NEXT: br i1 [[CMP_I]], label [[FOR_BODY_I:%.*]], label [[FOR_END_I:%.*]]
; CHECK: for.body.i:
; CHECK-NEXT: br label [[FOR_COND_I]], !llvm.loop [[LOOP0]]
; CHECK: for.end.i:
; CHECK-NEXT: store i32 0, i32* [[I_I]], align 4
; CHECK-NEXT: br label [[FOR_COND1_I:%.*]]
; CHECK: for.cond1.i:
; CHECK-NEXT: [[TMP13:%.*]] = load i32, i32* [[I_I]], align 4
; CHECK-NEXT: [[CMP2_I:%.*]] = icmp slt i32 [[TMP13]], 10
; CHECK-NEXT: br i1 [[CMP2_I]], label [[FOR_BODY3_I:%.*]], label [[FOR_END8_I:%.*]]
; CHECK: for.body3.i:
; CHECK-NEXT: br label [[FOR_COND4_I:%.*]]
; CHECK: for.cond4.i:
; CHECK-NEXT: [[TMP14:%.*]] = load i32, i32* [[B_ADDR_I]], align 4
; CHECK-NEXT: [[TMP15:%.*]] = load i32, i32* [[A_ADDR_I]], align 4
; CHECK-NEXT: [[CMP5_I:%.*]] = icmp slt i32 [[TMP14]], [[TMP15]]
; CHECK-NEXT: br i1 [[CMP5_I]], label [[FOR_BODY6_I:%.*]], label [[FOR_END7_I:%.*]]
; CHECK: for.body6.i:
; CHECK-NEXT: br label [[FOR_COND4_I]], !llvm.loop [[LOOP2:![0-9]+]]
; CHECK: for.end7.i:
; CHECK-NEXT: [[TMP16:%.*]] = load i32, i32* [[I_I]], align 4
; CHECK-NEXT: [[INC_I:%.*]] = add nsw i32 [[TMP16]], 1
; CHECK-NEXT: store i32 [[INC_I]], i32* [[I_I]], align 4
; CHECK-NEXT: br label [[FOR_COND1_I]], !llvm.loop [[LOOP4:![0-9]+]]
; CHECK: for.end8.i:
; CHECK-NEXT: br label [[WHILE_BODY_I:%.*]]
; CHECK: while.body.i:
; CHECK-NEXT: br label [[WHILE_BODY_I]]
; CHECK: callee_nested.exit:
; CHECK-NEXT: ret void
;
entry:
%a.addr = alloca i32, align 4
%b.addr = alloca i32, align 4
%i = alloca i32, align 4
%i9 = alloca i32, align 4
store i32 %a, i32* %a.addr, align 4
store i32 %b, i32* %b.addr, align 4
br label %for.cond
for.cond:
%0 = load i32, i32* %a.addr, align 4
%1 = load i32, i32* %b.addr, align 4
%cmp = icmp slt i32 %0, %1
br i1 %cmp, label %for.body, label %for.end8
for.body:
store i32 0, i32* %i, align 4
br label %for.cond1
for.cond1:
%2 = load i32, i32* %i, align 4
%cmp2 = icmp slt i32 %2, 10
br i1 %cmp2, label %for.body3, label %for.end7
for.body3:
br label %for.cond4
for.cond4:
%3 = load i32, i32* %b.addr, align 4
%4 = load i32, i32* %a.addr, align 4
%cmp5 = icmp slt i32 %3, %4
br i1 %cmp5, label %for.body6, label %for.end
for.body6:
br label %for.cond4
for.end:
br label %for.inc
for.inc:
%5 = load i32, i32* %i, align 4
%inc = add nsw i32 %5, 1
store i32 %inc, i32* %i, align 4
br label %for.cond1
for.end7:
br label %for.cond
for.end8:
store i32 0, i32* %i9, align 4
br label %for.cond10
for.cond10:
%6 = load i32, i32* %i9, align 4
%cmp11 = icmp slt i32 %6, 10
br i1 %cmp11, label %for.body12, label %for.end15
for.body12:
br label %for.inc13
for.inc13:
%7 = load i32, i32* %i9, align 4
%inc14 = add nsw i32 %7, 1
store i32 %inc14, i32* %i9, align 4
br label %for.cond10
for.end15:
call void @callee_nested(i32 0, i32 5)
ret void
}
; CHECK: attributes [[ATTR0]] = { mustprogress }
; CHECK: attributes [[ATTR1]] = { noinline }
; CHECK: [[LOOP0]] = distinct !{[[LOOP0]], [[GEN1:!.*]]}
; CHECK: [[GEN1]] = !{!"llvm.loop.mustprogress"}
; CHECK: [[LOOP2]] = distinct !{[[LOOP2]], [[GEN1:!.*]]}
; CHECK: [[LOOP3]] = distinct !{[[LOOP3]], [[GEN1:!.*]]}
; CHECK: [[LOOP4]] = distinct !{[[LOOP4]], [[GEN1:!.*]]}
attributes #0 = { mustprogress }
attributes #1 = { noinline }
attributes #2 = { noinline mustprogress }
!0 = distinct !{!0, !1}
!1 = !{!"llvm.loop.mustprogress"}
!2 = distinct !{!2, !1}
!3 = distinct !{!3, !1}
!4 = distinct !{!4, !1}
!5 = distinct !{!5, !1}
!6 = distinct !{!6, !1}
!7 = distinct !{!7, !1}