From: John McCall Date: Wed, 13 Jul 2011 06:10:41 +0000 (+0000) Subject: Convert the standard default-construction loops to use phis and X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=c3c0766277cd64bf117450a1519c9cf762d994d4;p=clang Convert the standard default-construction loops to use phis and partial destruction. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@135033 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/CodeGen/CGClass.cpp b/lib/CodeGen/CGClass.cpp index 222d87b44c..47d4fcbae3 100644 --- a/lib/CodeGen/CGClass.cpp +++ b/lib/CodeGen/CGClass.cpp @@ -1033,73 +1033,68 @@ void CodeGenFunction::EnterDtorCleanups(const CXXDestructorDecl *DD, } } -/// EmitCXXAggrConstructorCall - This routine essentially creates a (nested) -/// for-loop to call the default constructor on individual members of the -/// array. -/// 'D' is the default constructor for elements of the array, 'ArrayTy' is the -/// array type and 'ArrayPtr' points to the beginning fo the array. -/// It is assumed that all relevant checks have been made by the caller. +/// EmitCXXAggrConstructorCall - Emit a loop to call a particular +/// constructor for each of several members of an array. /// -/// \param ZeroInitialization True if each element should be zero-initialized -/// before it is constructed. +/// \param ctor the constructor to call for each element +/// \param argBegin,argEnd the arguments to evaluate and pass to the +/// constructor +/// \param arrayType the type of the array to initialize +/// \param arrayBegin an arrayType* +/// \param zeroInitialize true if each element should be +/// zero-initialized before it is constructed void -CodeGenFunction::EmitCXXAggrConstructorCall(const CXXConstructorDecl *D, - const ConstantArrayType *ArrayTy, - llvm::Value *ArrayPtr, - CallExpr::const_arg_iterator ArgBeg, - CallExpr::const_arg_iterator ArgEnd, - bool ZeroInitialization) { - - const llvm::Type *SizeTy = ConvertType(getContext().getSizeType()); - llvm::Value * NumElements = - llvm::ConstantInt::get(SizeTy, - getContext().getConstantArrayElementCount(ArrayTy)); - - EmitCXXAggrConstructorCall(D, NumElements, ArrayPtr, ArgBeg, ArgEnd, - ZeroInitialization); +CodeGenFunction::EmitCXXAggrConstructorCall(const CXXConstructorDecl *ctor, + const ConstantArrayType *arrayType, + llvm::Value *arrayBegin, + CallExpr::const_arg_iterator argBegin, + CallExpr::const_arg_iterator argEnd, + bool zeroInitialize) { + QualType elementType; + llvm::Value *numElements = + emitArrayLength(arrayType, elementType, arrayBegin); + + EmitCXXAggrConstructorCall(ctor, numElements, arrayBegin, + argBegin, argEnd, zeroInitialize); } +/// EmitCXXAggrConstructorCall - Emit a loop to call a particular +/// constructor for each of several members of an array. +/// +/// \param ctor the constructor to call for each element +/// \param numElements the number of elements in the array; +/// assumed to be non-zero +/// \param argBegin,argEnd the arguments to evaluate and pass to the +/// constructor +/// \param arrayBegin a T*, where T is the type constructed by ctor +/// \param zeroInitialize true if each element should be +/// zero-initialized before it is constructed void -CodeGenFunction::EmitCXXAggrConstructorCall(const CXXConstructorDecl *D, - llvm::Value *NumElements, - llvm::Value *ArrayPtr, - CallExpr::const_arg_iterator ArgBeg, - CallExpr::const_arg_iterator ArgEnd, - bool ZeroInitialization) { - const llvm::Type *SizeTy = ConvertType(getContext().getSizeType()); - - // Create a temporary for the loop index and initialize it with 0. - llvm::Value *IndexPtr = CreateTempAlloca(SizeTy, "loop.index"); - llvm::Value *Zero = llvm::Constant::getNullValue(SizeTy); - Builder.CreateStore(Zero, IndexPtr); - - // Start the loop with a block that tests the condition. - llvm::BasicBlock *CondBlock = createBasicBlock("for.cond"); - llvm::BasicBlock *AfterFor = createBasicBlock("for.end"); - - EmitBlock(CondBlock); - - llvm::BasicBlock *ForBody = createBasicBlock("for.body"); - - // Generate: if (loop-index < number-of-elements fall to the loop body, - // otherwise, go to the block after the for-loop. - llvm::Value *Counter = Builder.CreateLoad(IndexPtr); - llvm::Value *IsLess = Builder.CreateICmpULT(Counter, NumElements, "isless"); - // If the condition is true, execute the body. - Builder.CreateCondBr(IsLess, ForBody, AfterFor); +CodeGenFunction::EmitCXXAggrConstructorCall(const CXXConstructorDecl *ctor, + llvm::Value *numElements, + llvm::Value *arrayBegin, + CallExpr::const_arg_iterator argBegin, + CallExpr::const_arg_iterator argEnd, + bool zeroInitialize) { + // Find the end of the array. + llvm::Value *arrayEnd = Builder.CreateInBoundsGEP(arrayBegin, numElements, + "arrayctor.end"); + + // Enter the loop, setting up a phi for the current location to initialize. + llvm::BasicBlock *entryBB = Builder.GetInsertBlock(); + llvm::BasicBlock *loopBB = createBasicBlock("arrayctor.loop"); + EmitBlock(loopBB); + llvm::PHINode *cur = Builder.CreatePHI(arrayBegin->getType(), 2, + "arrayctor.cur"); + cur->addIncoming(arrayBegin, entryBB); - EmitBlock(ForBody); - - llvm::BasicBlock *ContinueBlock = createBasicBlock("for.inc"); // Inside the loop body, emit the constructor call on the array element. - Counter = Builder.CreateLoad(IndexPtr); - llvm::Value *Address = Builder.CreateInBoundsGEP(ArrayPtr, Counter, - "arrayidx"); + + QualType type = getContext().getTypeDeclType(ctor->getParent()); // Zero initialize the storage, if requested. - if (ZeroInitialization) - EmitNullInitialization(Address, - getContext().getTypeDeclType(D->getParent())); + if (zeroInitialize) + EmitNullInitialization(cur, type); // C++ [class.temporary]p4: // There are two contexts in which temporaries are destroyed at a different @@ -1109,27 +1104,33 @@ CodeGenFunction::EmitCXXAggrConstructorCall(const CXXConstructorDecl *D, // every temporary created in a default argument expression is sequenced // before the construction of the next array element, if any. - // Keep track of the current number of live temporaries. { RunCleanupsScope Scope(*this); - EmitCXXConstructorCall(D, Ctor_Complete, /*ForVirtualBase=*/false, Address, - ArgBeg, ArgEnd); - } + // Evaluate the constructor and its arguments in a regular + // partial-destroy cleanup. + if (getLangOptions().Exceptions && + !ctor->getParent()->hasTrivialDestructor()) { + Destroyer *destroyer = destroyCXXObject; + pushRegularPartialArrayCleanup(arrayBegin, cur, type, *destroyer); + } - EmitBlock(ContinueBlock); + EmitCXXConstructorCall(ctor, Ctor_Complete, /*ForVirtualBase=*/ false, + cur, argBegin, argEnd); + } - // Emit the increment of the loop counter. - llvm::Value *NextVal = llvm::ConstantInt::get(SizeTy, 1); - Counter = Builder.CreateLoad(IndexPtr); - NextVal = Builder.CreateAdd(Counter, NextVal, "inc"); - Builder.CreateStore(NextVal, IndexPtr); + // Go to the next element. + llvm::Value *next = + Builder.CreateInBoundsGEP(cur, llvm::ConstantInt::get(SizeTy, 1), + "arrayctor.next"); + cur->addIncoming(next, Builder.GetInsertBlock()); - // Finally, branch back up to the condition for the next iteration. - EmitBranch(CondBlock); + // Check whether that's the end of the loop. + llvm::Value *done = Builder.CreateICmpEQ(next, arrayEnd, "arrayctor.done"); + llvm::BasicBlock *contBB = createBasicBlock("arrayctor.cont"); + Builder.CreateCondBr(done, contBB, loopBB); - // Emit the fall-through block. - EmitBlock(AfterFor, true); + EmitBlock(contBB); } void CodeGenFunction::destroyCXXObject(CodeGenFunction &CGF, diff --git a/lib/CodeGen/CGExprCXX.cpp b/lib/CodeGen/CGExprCXX.cpp index 07997e4391..49aab66268 100644 --- a/lib/CodeGen/CGExprCXX.cpp +++ b/lib/CodeGen/CGExprCXX.cpp @@ -379,19 +379,11 @@ CodeGenFunction::EmitCXXConstructExpr(const CXXConstructExpr *E, } } - const ConstantArrayType *Array - = getContext().getAsConstantArrayType(E->getType()); - if (Array) { - QualType BaseElementTy = getContext().getBaseElementType(Array); - const llvm::Type *BasePtr = ConvertType(BaseElementTy); - BasePtr = llvm::PointerType::getUnqual(BasePtr); - llvm::Value *BaseAddrPtr = - Builder.CreateBitCast(Dest.getAddr(), BasePtr); - - EmitCXXAggrConstructorCall(CD, Array, BaseAddrPtr, + if (const ConstantArrayType *arrayType + = getContext().getAsConstantArrayType(E->getType())) { + EmitCXXAggrConstructorCall(CD, arrayType, Dest.getAddr(), E->arg_begin(), E->arg_end()); - } - else { + } else { CXXCtorType Type = Ctor_Complete; bool ForVirtualBase = false; @@ -805,11 +797,37 @@ static void EmitNewInitializer(CodeGenFunction &CGF, const CXXNewExpr *E, RequiresZeroInitialization = true; } + + // It's legal for NumElements to be zero, but + // EmitCXXAggrConstructorCall doesn't handle that, so we need to. + llvm::BranchInst *br = 0; + + // Optimize for a constant count. + llvm::ConstantInt *constantCount + = dyn_cast(NumElements); + if (constantCount) { + // Just skip out if the constant count is zero. + if (constantCount->isZero()) return; + + // Otherwise, emit the check. + } else { + llvm::BasicBlock *loopBB = CGF.createBasicBlock("new.ctorloop"); + llvm::Value *iszero = CGF.Builder.CreateIsNull(NumElements, "isempty"); + br = CGF.Builder.CreateCondBr(iszero, loopBB, loopBB); + CGF.EmitBlock(loopBB); + } CGF.EmitCXXAggrConstructorCall(Ctor, NumElements, NewPtr, E->constructor_arg_begin(), E->constructor_arg_end(), RequiresZeroInitialization); + + // Patch the earlier check to skip over the loop. + if (br) { + assert(CGF.Builder.GetInsertBlock()->empty()); + br->setSuccessor(0, CGF.Builder.GetInsertBlock()); + } + return; } else if (E->getNumConstructorArgs() == 1 && isa(E->getConstructorArg(0))) { diff --git a/test/CodeGenCXX/new-overflow.cpp b/test/CodeGenCXX/new-overflow.cpp index fd56d5e902..68f89c35b6 100644 --- a/test/CodeGenCXX/new-overflow.cpp +++ b/test/CodeGenCXX/new-overflow.cpp @@ -18,7 +18,7 @@ namespace test0 { // CHECK-NEXT: [[T2:%.*]] = extractvalue { i32, i1 } [[T0]], 0 // CHECK-NEXT: [[T3:%.*]] = select i1 [[T1]], i32 -1, i32 [[T2]] // CHECK-NEXT: call noalias i8* @_Znaj(i32 [[T3]]) - // CHECK: icmp ult i32 {{.*}}, [[N]] + // CHECK: getelementptr inbounds {{.*}}, i32 [[N]] elt *test(short s) { return new elt[s]; } @@ -41,7 +41,7 @@ namespace test1 { // CHECK-NEXT: [[T3:%.*]] = mul i32 [[N]], 100 // CHECK-NEXT: [[T4:%.*]] = select i1 [[T1]], i32 -1, i32 [[T2]] // CHECK-NEXT: call noalias i8* @_Znaj(i32 [[T4]]) - // CHECK: icmp ult i32 {{.*}}, [[T3]] + // CHECK: getelementptr inbounds {{.*}}, i32 [[T3]] elt *test(short s) { return new elt[s]; } @@ -69,7 +69,7 @@ namespace test2 { // CHECK-NEXT: [[T7:%.*]] = extractvalue { i32, i1 } [[T4]], 0 // CHECK-NEXT: [[T8:%.*]] = select i1 [[T6]], i32 -1, i32 [[T7]] // CHECK-NEXT: call noalias i8* @_Znaj(i32 [[T8]]) - // CHECK: icmp ult i32 {{.*}}, [[T3]] + // CHECK: getelementptr inbounds {{.*}}, i32 [[T3]] elt *test(short s) { return new elt[s]; } @@ -88,7 +88,7 @@ namespace test4 { // CHECK-NEXT: [[T0:%.*]] = icmp slt i32 [[N]], 0 // CHECK-NEXT: [[T1:%.*]] = select i1 [[T0]], i32 -1, i32 [[N]] // CHECK-NEXT: call noalias i8* @_Znaj(i32 [[T1]]) - // CHECK: icmp ult i32 {{.*}}, [[N]] + // CHECK: getelementptr inbounds {{.*}}, i32 [[N]] elt *test(short s) { return new elt[s]; } @@ -107,7 +107,7 @@ namespace test5 { // CHECK-NEXT: [[T0:%.*]] = icmp slt i32 [[N]], 0 // CHECK-NEXT: [[T1:%.*]] = select i1 [[T0]], i32 -1, i32 [[N]] // CHECK-NEXT: call noalias i8* @_Znaj(i32 [[T1]]) - // CHECK: icmp ult i32 {{.*}}, [[N]] + // CHECK: getelementptr inbounds {{.*}}, i32 [[N]] elt *test(int s) { return new elt[s]; } @@ -129,7 +129,7 @@ namespace test6 { // CHECK-NEXT: [[T2:%.*]] = extractvalue { i32, i1 } [[T0]], 0 // CHECK-NEXT: [[T3:%.*]] = select i1 [[T1]], i32 -1, i32 [[T2]] // CHECK-NEXT: call noalias i8* @_Znaj(i32 [[T3]]) - // CHECK: icmp ult i32 {{.*}}, [[N]] + // CHECK: getelementptr inbounds {{.*}}, i32 [[N]] elt *test(unsigned short s) { return new elt[s]; } @@ -152,7 +152,7 @@ namespace test7 { // CHECK-NEXT: [[T3:%.*]] = mul i32 [[N]], 100 // CHECK-NEXT: [[T4:%.*]] = select i1 [[T1]], i32 -1, i32 [[T2]] // CHECK-NEXT: call noalias i8* @_Znaj(i32 [[T4]]) - // CHECK: icmp ult i32 {{.*}}, [[T3]] + // CHECK: getelementptr inbounds {{.*}}, i32 [[T3]] elt *test(unsigned short s) { return new elt[s]; } @@ -177,7 +177,7 @@ namespace test8 { // CHECK-NEXT: [[T5:%.*]] = extractvalue { i32, i1 } [[T2]], 0 // CHECK-NEXT: [[T6:%.*]] = select i1 [[T4]], i32 -1, i32 [[T5]] // CHECK-NEXT: call noalias i8* @_Znaj(i32 [[T6]]) - // CHECK: icmp ult i32 {{.*}}, [[T1]] + // CHECK: getelementptr inbounds {{.*}}, i32 [[T1]] elt *test(long long s) { return new elt[s]; } @@ -202,7 +202,7 @@ namespace test9 { // CHECK-NEXT: [[T5:%.*]] = extractvalue { i32, i1 } [[T2]], 0 // CHECK-NEXT: [[T6:%.*]] = select i1 [[T4]], i32 -1, i32 [[T5]] // CHECK-NEXT: call noalias i8* @_Znaj(i32 [[T6]]) - // CHECK: icmp ult i32 {{.*}}, [[T1]] + // CHECK: getelementptr inbounds {{.*}}, i32 [[T1]] elt *test(unsigned long long s) { return new elt[s]; } diff --git a/test/CodeGenCXX/new.cpp b/test/CodeGenCXX/new.cpp index bd307f1086..3a72bb84e7 100644 --- a/test/CodeGenCXX/new.cpp +++ b/test/CodeGenCXX/new.cpp @@ -182,12 +182,17 @@ namespace test15 { } // CHECK: define void @_ZN6test155test1EPv( - // CHECK: [[P:%.*]] = load i8* + // CHECK: [[P:%.*]] = load i8** // CHECK-NEXT: icmp eq i8* [[P]], null // CHECK-NEXT: br i1 - // CHECK: [[T0:%.*]] = bitcast i8* [[P]] to [[A:%.*]]* - // CHECK: [[T1:%.*]] = getelementptr inbounds [[A]]* [[T0]], - // CHECK-NEXT: call void @_ZN6test151AC1Ev([[A]]* [[T1]]) + // CHECK: [[BEGIN:%.*]] = bitcast i8* [[P]] to [[A:%.*]]* + // CHECK-NEXT: [[END:%.*]] = getelementptr inbounds [[A]]* [[BEGIN]], i64 5 + // CHECK-NEXT: br label + // CHECK: [[CUR:%.*]] = phi [[A]]* [ [[BEGIN]], {{%.*}} ], [ [[NEXT:%.*]], {{%.*}} ] + // CHECK-NEXT: call void @_ZN6test151AC1Ev([[A]]* [[CUR]]) + // CHECK-NEXT: [[NEXT]] = getelementptr inbounds [[A]]* [[CUR]], i64 1 + // CHECK-NEXT: [[DONE:%.*]] = icmp eq [[A]]* [[NEXT]], [[END]] + // CHECK-NEXT: br i1 [[DONE]] void test1(void *p) { new (p) A[5]; } @@ -202,9 +207,13 @@ namespace test15 { // CHECK-NEXT: [[P:%.*]] = load i8* // CHECK-NEXT: icmp eq i8* [[P]], null // CHECK-NEXT: br i1 - // CHECK: [[T0:%.*]] = bitcast i8* [[P]] to [[A:%.*]]* - // CHECK: [[T1:%.*]] = getelementptr inbounds [[A]]* [[T0]], - // CHECK-NEXT: call void @_ZN6test151AC1Ev([[A]]* [[T1]]) + // CHECK: [[BEGIN:%.*]] = bitcast i8* [[P]] to [[A:%.*]]* + // CHECK-NEXT: [[ISEMPTY:%.*]] = icmp eq i64 [[T0]], 0 + // CHECK-NEXT: br i1 [[ISEMPTY]], + // CHECK: [[END:%.*]] = getelementptr inbounds [[A]]* [[BEGIN]], i64 [[T0]] + // CHECK-NEXT: br label + // CHECK: [[CUR:%.*]] = phi [[A]]* [ [[BEGIN]], + // CHECK-NEXT: call void @_ZN6test151AC1Ev([[A]]* [[CUR]]) void test2(void *p, int n) { new (p) A[n]; } diff --git a/test/CodeGenCXX/partial-destruction.cpp b/test/CodeGenCXX/partial-destruction.cpp index 52995523bf..f929d2d75e 100644 --- a/test/CodeGenCXX/partial-destruction.cpp +++ b/test/CodeGenCXX/partial-destruction.cpp @@ -118,3 +118,38 @@ namespace test1 { // CHECK: invoke void @_ZN5test11AD1Ev([[A]]* [[Y]]) // CHECK: invoke void @_ZN5test11AD1Ev([[A]]* [[X]]) } + +namespace test2 { + struct A { A(); ~A(); }; + + void test() { + A v[4][7]; + + // CHECK: define void @_ZN5test24testEv() + // CHECK: [[V:%.*]] = alloca [4 x [7 x [[A:%.*]]]], align 1 + // CHECK-NEXT: alloca i8* + // CHECK-NEXT: alloca i32 + // CHECK-NEXT: alloca i32 + + // Main initialization loop. + // CHECK-NEXT: [[BEGIN:%.*]] = getelementptr inbounds [4 x [7 x [[A]]]]* [[V]], i32 0, i32 0, i32 0 + // CHECK-NEXT: [[END:%.*]] = getelementptr inbounds [[A]]* [[BEGIN]], i64 28 + // CHECK-NEXT: br label + // CHECK: [[CUR:%.*]] = phi [[A]]* [ [[BEGIN]], {{%.*}} ], [ [[NEXT:%.*]], {{%.*}} ] + // CHECK-NEXT: invoke void @_ZN5test21AC1Ev([[A]]* [[CUR]]) + // CHECK: [[NEXT:%.*]] = getelementptr inbounds [[A]]* [[CUR]], i64 1 + // CHECK-NEXT: [[DONE:%.*]] = icmp eq [[A]]* [[NEXT]], [[END]] + // CHECK-NEXT: br i1 [[DONE]], + + // Partial destruction landing pad. + // CHECK: llvm.eh.exception() + // CHECK: [[EMPTY:%.*]] = icmp eq [[A]]* [[BEGIN]], [[CUR]] + // CHECK-NEXT: br i1 [[EMPTY]], + // CHECK: [[PAST:%.*]] = phi [[A]]* [ [[CUR]], {{%.*}} ], [ [[DEL:%.*]], {{%.*}} ] + // CHECK-NEXT: [[DEL]] = getelementptr inbounds [[A]]* [[PAST]], i64 -1 + // CHECK-NEXT: invoke void @_ZN5test21AD1Ev([[A]]* [[DEL]]) + // CHECK: [[T0:%.*]] = icmp eq [[A]]* [[DEL]], [[BEGIN]] + // CHECK-NEXT: br i1 [[T0]], + } + +} diff --git a/test/CodeGenCXX/value-init.cpp b/test/CodeGenCXX/value-init.cpp index 6178c24e94..04a18b3fa8 100644 --- a/test/CodeGenCXX/value-init.cpp +++ b/test/CodeGenCXX/value-init.cpp @@ -204,7 +204,6 @@ namespace test6 { }; // CHECK: define void @_ZN5test64testEv() // CHECK: [[ARR:%.*]] = alloca [10 x [20 x [[A:%.*]]]], - // CHECK-NEXT: [[IDX:%.*]] = alloca i64 // CHECK-NEXT: [[INNER:%.*]] = getelementptr inbounds [10 x [20 x [[A]]]]* [[ARR]], i64 0, i64 0 // CHECK-NEXT: [[T0:%.*]] = getelementptr inbounds [20 x [[A]]]* [[INNER]], i64 0, i64 0 @@ -222,23 +221,17 @@ namespace test6 { // CHECK-NEXT: [[END:%.*]] = getelementptr inbounds [20 x [[A]]]* [[INNER]], i64 10 // CHECK-NEXT: br label // CHECK: [[CUR:%.*]] = phi [20 x [[A]]]* [ [[BEGIN]], {{%.*}} ], [ [[NEXT:%.*]], {{%.*}} ] - // CHECK-NEXT: [[FIRST:%.*]] = bitcast [20 x [[A]]]* [[CUR]] to [[A]]* - // TODO: this loop should use phis, too, and for preference would be - // merged with the outer loop. - // CHECK-NEXT: store i64 0, i64* [[IDX]] - // CHECK-NEXT: br label - // CHECK: [[T0:%.*]] = load i64* [[IDX]] - // CHECK-NEXT: [[T1:%.*]] = icmp ult i64 [[T0]], 20 - // CHECK-NEXT: br i1 [[T1]] - // CHECK: [[T0:%.*]] = load i64* [[IDX]] - // CHECK-NEXT: [[T1:%.*]] = getelementptr inbounds [[A]]* [[FIRST]], i64 [[T0]] - // CHECK-NEXT: call void @_ZN5test61AC1Ev([[A]]* [[T1]]) - // CHECK-NEXT: br label - // CHECK: [[T0:%.*]] = load i64* [[IDX]] - // CHECK-NEXT: [[T1:%.*]] = add i64 [[T0]], 1 - // CHECK-NEXT: store i64 [[T1]], i64* [[IDX]] + // Inner loop. + // CHECK-NEXT: [[IBEGIN:%.*]] = getelementptr inbounds [20 x [[A]]]* [[CUR]], i32 0, i32 0 + // CHECK-NEXT: [[IEND:%.*]] = getelementptr inbounds [[A]]* [[IBEGIN]], i64 20 // CHECK-NEXT: br label + // CHECK: [[ICUR:%.*]] = phi [[A]]* [ [[IBEGIN]], {{%.*}} ], [ [[INEXT:%.*]], {{%.*}} ] + // CHECK-NEXT: call void @_ZN5test61AC1Ev([[A]]* [[ICUR]]) + // CHECK-NEXT: [[INEXT:%.*]] = getelementptr inbounds [[A]]* [[ICUR]], i64 1 + // CHECK-NEXT: [[T0:%.*]] = icmp eq [[A]]* [[INEXT]], [[IEND]] + // CHECK-NEXT: br i1 [[T0]], + // CHECK: [[NEXT]] = getelementptr inbounds [20 x [[A]]]* [[CUR]], i64 1 // CHECK-NEXT: [[T0:%.*]] = icmp eq [20 x [[A]]]* [[NEXT]], [[END]] // CHECK-NEXT: br i1 [[T0]]