Index: llvm/trunk/include/llvm/IR/IRBuilder.h =================================================================== --- llvm/trunk/include/llvm/IR/IRBuilder.h +++ llvm/trunk/include/llvm/IR/IRBuilder.h @@ -1476,14 +1476,19 @@ return CreateConstInBoundsGEP2_32(Ty, Ptr, 0, Idx, Name); } + Value *CreateStructGEP(Value *Ptr, unsigned Idx, const Twine &Name = "") { + return CreateConstInBoundsGEP2_32(nullptr, Ptr, 0, Idx, Name); + } + /// Same as CreateGlobalString, but return a pointer with "i8*" type /// instead of a pointer to array of i8. - Value *CreateGlobalStringPtr(StringRef Str, const Twine &Name = "", - unsigned AddressSpace = 0) { - GlobalVariable *gv = CreateGlobalString(Str, Name, AddressSpace); - Value *zero = ConstantInt::get(Type::getInt32Ty(Context), 0); - Value *Args[] = { zero, zero }; - return CreateInBoundsGEP(gv->getValueType(), gv, Args, Name); + Constant *CreateGlobalStringPtr(StringRef Str, const Twine &Name = "", + unsigned AddressSpace = 0) { + GlobalVariable *GV = CreateGlobalString(Str, Name, AddressSpace); + Constant *Zero = ConstantInt::get(Type::getInt32Ty(Context), 0); + Constant *Indices[] = {Zero, Zero}; + return ConstantExpr::getInBoundsGetElementPtr(GV->getValueType(), GV, + Indices); } //===--------------------------------------------------------------------===// Index: llvm/trunk/lib/Transforms/Instrumentation/GCOVProfiling.cpp =================================================================== --- llvm/trunk/lib/Transforms/Instrumentation/GCOVProfiling.cpp +++ llvm/trunk/lib/Transforms/Instrumentation/GCOVProfiling.cpp @@ -17,6 +17,7 @@ #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/Hashing.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/Sequence.h" #include "llvm/ADT/Statistic.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringMap.h" @@ -35,8 +36,8 @@ #include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" #include "llvm/Support/raw_ostream.h" -#include "llvm/Transforms/Instrumentation/GCOVProfiler.h" #include "llvm/Transforms/Instrumentation.h" +#include "llvm/Transforms/Instrumentation/GCOVProfiler.h" #include "llvm/Transforms/Utils/ModuleUtils.h" #include #include @@ -886,46 +887,196 @@ Constant *SummaryInfo = getSummaryInfoFunc(); Constant *EndFile = getEndFileFunc(); - NamedMDNode *CU_Nodes = M->getNamedMetadata("llvm.dbg.cu"); - if (CU_Nodes) { - for (unsigned i = 0, e = CU_Nodes->getNumOperands(); i != e; ++i) { - auto *CU = cast(CU_Nodes->getOperand(i)); + NamedMDNode *CUNodes = M->getNamedMetadata("llvm.dbg.cu"); + if (!CUNodes) { + Builder.CreateRetVoid(); + return WriteoutF; + } - // Skip module skeleton (and module) CUs. - if (CU->getDWOId()) - continue; + // Collect the relevant data into a large constant data structure that we can + // walk to write out everything. + StructType *StartFileCallArgsTy = StructType::create( + {Builder.getInt8PtrTy(), Builder.getInt8PtrTy(), Builder.getInt32Ty()}); + StructType *EmitFunctionCallArgsTy = StructType::create( + {Builder.getInt32Ty(), Builder.getInt8PtrTy(), Builder.getInt32Ty(), + Builder.getInt8Ty(), Builder.getInt32Ty()}); + StructType *EmitArcsCallArgsTy = StructType::create( + {Builder.getInt32Ty(), Builder.getInt64Ty()->getPointerTo()}); + StructType *FileInfoTy = + StructType::create({StartFileCallArgsTy, Builder.getInt32Ty(), + EmitFunctionCallArgsTy->getPointerTo(), + EmitArcsCallArgsTy->getPointerTo()}); + + Constant *Zero32 = Builder.getInt32(0); + + SmallVector FileInfos; + for (int i : llvm::seq(0, CUNodes->getNumOperands())) { + auto *CU = cast(CUNodes->getOperand(i)); - std::string FilenameGcda = mangleName(CU, GCovFileType::GCDA); - uint32_t CfgChecksum = FileChecksums.empty() ? 0 : FileChecksums[i]; - Builder.CreateCall(StartFile, - {Builder.CreateGlobalStringPtr(FilenameGcda), - Builder.CreateGlobalStringPtr(ReversedVersion), - Builder.getInt32(CfgChecksum)}); - for (unsigned j = 0, e = CountersBySP.size(); j != e; ++j) { - auto *SP = cast_or_null(CountersBySP[j].second); - uint32_t FuncChecksum = Funcs.empty() ? 0 : Funcs[j]->getFuncChecksum(); - Builder.CreateCall( - EmitFunction, - {Builder.getInt32(j), - Options.FunctionNamesInData - ? Builder.CreateGlobalStringPtr(getFunctionName(SP)) - : Constant::getNullValue(Builder.getInt8PtrTy()), - Builder.getInt32(FuncChecksum), - Builder.getInt8(Options.UseCfgChecksum), - Builder.getInt32(CfgChecksum)}); - - GlobalVariable *GV = CountersBySP[j].first; - unsigned Arcs = - cast(GV->getValueType())->getNumElements(); - Builder.CreateCall(EmitArcs, {Builder.getInt32(Arcs), - Builder.CreateConstGEP2_64(GV, 0, 0)}); - } - Builder.CreateCall(SummaryInfo, {}); - Builder.CreateCall(EndFile, {}); - } + // Skip module skeleton (and module) CUs. + if (CU->getDWOId()) + continue; + + std::string FilenameGcda = mangleName(CU, GCovFileType::GCDA); + uint32_t CfgChecksum = FileChecksums.empty() ? 0 : FileChecksums[i]; + auto *StartFileCallArgs = ConstantStruct::get( + StartFileCallArgsTy, {Builder.CreateGlobalStringPtr(FilenameGcda), + Builder.CreateGlobalStringPtr(ReversedVersion), + Builder.getInt32(CfgChecksum)}); + + SmallVector EmitFunctionCallArgsArray; + SmallVector EmitArcsCallArgsArray; + for (int j : llvm::seq(0, CountersBySP.size())) { + auto *SP = cast_or_null(CountersBySP[j].second); + uint32_t FuncChecksum = Funcs.empty() ? 0 : Funcs[j]->getFuncChecksum(); + EmitFunctionCallArgsArray.push_back(ConstantStruct::get( + EmitFunctionCallArgsTy, + {Builder.getInt32(j), + Options.FunctionNamesInData + ? Builder.CreateGlobalStringPtr(getFunctionName(SP)) + : Constant::getNullValue(Builder.getInt8PtrTy()), + Builder.getInt32(FuncChecksum), + Builder.getInt8(Options.UseCfgChecksum), + Builder.getInt32(CfgChecksum)})); + + GlobalVariable *GV = CountersBySP[j].first; + unsigned Arcs = cast(GV->getValueType())->getNumElements(); + EmitArcsCallArgsArray.push_back(ConstantStruct::get( + EmitArcsCallArgsTy, + {Builder.getInt32(Arcs), + ConstantExpr::getInBoundsGetElementPtr( + GV->getValueType(), GV, + makeArrayRef({Zero32, Zero32}))})); + } + // Create global arrays for the two emit calls. + int CountersSize = CountersBySP.size(); + assert(CountersSize == (int)EmitFunctionCallArgsArray.size() && + "Mismatched array size!"); + assert(CountersSize == (int)EmitArcsCallArgsArray.size() && + "Mismatched array size!"); + auto *EmitFunctionCallArgsArrayTy = + ArrayType::get(EmitFunctionCallArgsTy, CountersSize); + auto *EmitFunctionCallArgsArrayGV = new GlobalVariable( + *M, EmitFunctionCallArgsArrayTy, /*isConstant*/ true, + GlobalValue::InternalLinkage, + ConstantArray::get(EmitFunctionCallArgsArrayTy, + EmitFunctionCallArgsArray), + Twine("__llvm_internal_gcov_emit_function_args.") + Twine(i)); + auto *EmitArcsCallArgsArrayTy = + ArrayType::get(EmitArcsCallArgsTy, CountersSize); + EmitFunctionCallArgsArrayGV->setUnnamedAddr( + GlobalValue::UnnamedAddr::Global); + auto *EmitArcsCallArgsArrayGV = new GlobalVariable( + *M, EmitArcsCallArgsArrayTy, /*isConstant*/ true, + GlobalValue::InternalLinkage, + ConstantArray::get(EmitArcsCallArgsArrayTy, EmitArcsCallArgsArray), + Twine("__llvm_internal_gcov_emit_arcs_args.") + Twine(i)); + EmitArcsCallArgsArrayGV->setUnnamedAddr(GlobalValue::UnnamedAddr::Global); + + FileInfos.push_back(ConstantStruct::get( + FileInfoTy, + {StartFileCallArgs, Builder.getInt32(CountersSize), + ConstantExpr::getInBoundsGetElementPtr( + EmitFunctionCallArgsArrayTy, EmitFunctionCallArgsArrayGV, + makeArrayRef({Zero32, Zero32})), + ConstantExpr::getInBoundsGetElementPtr( + EmitArcsCallArgsArrayTy, EmitArcsCallArgsArrayGV, + makeArrayRef({Zero32, Zero32}))})); + } + + // If we didn't find anything to actually emit, bail on out. + if (FileInfos.empty()) { + Builder.CreateRetVoid(); + return WriteoutF; } + // To simplify code, we cap the number of file infos we write out to fit + // easily in a 32-bit signed integer. This gives consistent behavior between + // 32-bit and 64-bit systems without requiring (potentially very slow) 64-bit + // operations on 32-bit systems. It also seems unreasonable to try to handle + // more than 2 billion files. + if ((int64_t)FileInfos.size() > (int64_t)INT_MAX) + FileInfos.resize(INT_MAX); + + // Create a global for the entire data structure so we can walk it more + // easily. + auto *FileInfoArrayTy = ArrayType::get(FileInfoTy, FileInfos.size()); + auto *FileInfoArrayGV = new GlobalVariable( + *M, FileInfoArrayTy, /*isConstant*/ true, GlobalValue::InternalLinkage, + ConstantArray::get(FileInfoArrayTy, FileInfos), + "__llvm_internal_gcov_emit_file_info"); + FileInfoArrayGV->setUnnamedAddr(GlobalValue::UnnamedAddr::Global); + + // Create the CFG for walking this data structure. + auto *FileLoopHeader = + BasicBlock::Create(*Ctx, "file.loop.header", WriteoutF); + auto *CounterLoopHeader = + BasicBlock::Create(*Ctx, "counter.loop.header", WriteoutF); + auto *FileLoopLatch = BasicBlock::Create(*Ctx, "file.loop.latch", WriteoutF); + auto *ExitBB = BasicBlock::Create(*Ctx, "exit", WriteoutF); + + // We always have at least one file, so just branch to the header. + Builder.CreateBr(FileLoopHeader); + + // The index into the files structure is our loop induction variable. + Builder.SetInsertPoint(FileLoopHeader); + PHINode *IV = + Builder.CreatePHI(Builder.getInt32Ty(), /*NumReservedValues*/ 2); + IV->addIncoming(Builder.getInt32(0), BB); + auto *FileInfoPtr = + Builder.CreateInBoundsGEP(FileInfoArrayGV, {Builder.getInt32(0), IV}); + auto *StartFileCallArgsPtr = Builder.CreateStructGEP(FileInfoPtr, 0); + Builder.CreateCall( + StartFile, + {Builder.CreateLoad(Builder.CreateStructGEP(StartFileCallArgsPtr, 0)), + Builder.CreateLoad(Builder.CreateStructGEP(StartFileCallArgsPtr, 1)), + Builder.CreateLoad(Builder.CreateStructGEP(StartFileCallArgsPtr, 2))}); + auto *NumCounters = + Builder.CreateLoad(Builder.CreateStructGEP(FileInfoPtr, 1)); + auto *EmitFunctionCallArgsArray = + Builder.CreateLoad(Builder.CreateStructGEP(FileInfoPtr, 2)); + auto *EmitArcsCallArgsArray = + Builder.CreateLoad(Builder.CreateStructGEP(FileInfoPtr, 3)); + auto *EnterCounterLoopCond = + Builder.CreateICmpSLT(Builder.getInt32(0), NumCounters); + Builder.CreateCondBr(EnterCounterLoopCond, CounterLoopHeader, FileLoopLatch); + + Builder.SetInsertPoint(CounterLoopHeader); + auto *JV = Builder.CreatePHI(Builder.getInt32Ty(), /*NumReservedValues*/ 2); + JV->addIncoming(Builder.getInt32(0), FileLoopHeader); + auto *EmitFunctionCallArgsPtr = + Builder.CreateInBoundsGEP(EmitFunctionCallArgsArray, {JV}); + Builder.CreateCall( + EmitFunction, + {Builder.CreateLoad(Builder.CreateStructGEP(EmitFunctionCallArgsPtr, 0)), + Builder.CreateLoad(Builder.CreateStructGEP(EmitFunctionCallArgsPtr, 1)), + Builder.CreateLoad(Builder.CreateStructGEP(EmitFunctionCallArgsPtr, 2)), + Builder.CreateLoad(Builder.CreateStructGEP(EmitFunctionCallArgsPtr, 3)), + Builder.CreateLoad( + Builder.CreateStructGEP(EmitFunctionCallArgsPtr, 4))}); + auto *EmitArcsCallArgsPtr = + Builder.CreateInBoundsGEP(EmitArcsCallArgsArray, {JV}); + Builder.CreateCall( + EmitArcs, + {Builder.CreateLoad(Builder.CreateStructGEP(EmitArcsCallArgsPtr, 0)), + Builder.CreateLoad(Builder.CreateStructGEP(EmitArcsCallArgsPtr, 1))}); + auto *NextJV = Builder.CreateAdd(JV, Builder.getInt32(1)); + auto *CounterLoopCond = Builder.CreateICmpSLT(NextJV, NumCounters); + Builder.CreateCondBr(CounterLoopCond, CounterLoopHeader, FileLoopLatch); + JV->addIncoming(NextJV, CounterLoopHeader); + + Builder.SetInsertPoint(FileLoopLatch); + Builder.CreateCall(SummaryInfo, {}); + Builder.CreateCall(EndFile, {}); + auto *NextIV = Builder.CreateAdd(IV, Builder.getInt32(1)); + auto *FileLoopCond = + Builder.CreateICmpSLT(NextIV, Builder.getInt32(FileInfos.size())); + Builder.CreateCondBr(FileLoopCond, FileLoopHeader, ExitBB); + IV->addIncoming(NextIV, FileLoopLatch); + + Builder.SetInsertPoint(ExitBB); Builder.CreateRetVoid(); + return WriteoutF; } Index: llvm/trunk/test/Transforms/GCOVProfiling/function-numbering.ll =================================================================== --- llvm/trunk/test/Transforms/GCOVProfiling/function-numbering.ll +++ llvm/trunk/test/Transforms/GCOVProfiling/function-numbering.ll @@ -19,9 +19,72 @@ ; GCDA: @[[FOO:[0-9]+]] = private unnamed_addr constant [4 x i8] c"foo\00" ; GCDA-NOT: @{{[0-9]+}} = private unnamed_addr constant .* c"bar\00" ; GCDA: @[[BAZ:[0-9]+]] = private unnamed_addr constant [4 x i8] c"baz\00" -; GCDA: define internal void @__llvm_gcov_writeout() -; GCDA: call void @llvm_gcda_emit_function(i32 0, i8* getelementptr inbounds ([4 x i8], [4 x i8]* @[[FOO]] -; GCDA: call void @llvm_gcda_emit_function(i32 1, i8* getelementptr inbounds ([4 x i8], [4 x i8]* @[[BAZ]] +; GCDA: @__llvm_internal_gcov_emit_function_args.0 = internal unnamed_addr constant +; GCDA-SAME: { i32 0, i8* getelementptr inbounds ([4 x i8], [4 x i8]* @[[FOO]] +; GCDA-SAME: { i32 1, i8* getelementptr inbounds ([4 x i8], [4 x i8]* @[[BAZ]] +; +; GCDA-LABEL: define internal void @__llvm_gcov_writeout() {{.*}} { +; GCDA-NEXT: entry: +; GCDA-NEXT: br label %[[FILE_LOOP_HEADER:.*]] +; +; GCDA: [[FILE_LOOP_HEADER]]: +; GCDA-NEXT: %[[IV:.*]] = phi i32 [ 0, %entry ], [ %[[NEXT_IV:.*]], %[[FILE_LOOP_LATCH:.*]] ] +; GCDA-NEXT: %[[FILE_INFO:.*]] = getelementptr inbounds {{.*}}, {{.*}}* @__llvm_internal_gcov_emit_file_info, i32 0, i32 %[[IV]] +; GCDA-NEXT: %[[START_FILE_ARGS:.*]] = getelementptr inbounds {{.*}}, {{.*}}* %[[FILE_INFO]], i32 0, i32 0 +; GCDA-NEXT: %[[START_FILE_ARG_0_PTR:.*]] = getelementptr inbounds {{.*}}, {{.*}}* %[[START_FILE_ARGS]], i32 0, i32 0 +; GCDA-NEXT: %[[START_FILE_ARG_0:.*]] = load i8*, i8** %[[START_FILE_ARG_0_PTR]] +; GCDA-NEXT: %[[START_FILE_ARG_1_PTR:.*]] = getelementptr inbounds {{.*}}, {{.*}}* %[[START_FILE_ARGS]], i32 0, i32 1 +; GCDA-NEXT: %[[START_FILE_ARG_1:.*]] = load i8*, i8** %[[START_FILE_ARG_1_PTR]] +; GCDA-NEXT: %[[START_FILE_ARG_2_PTR:.*]] = getelementptr inbounds {{.*}}, {{.*}}* %[[START_FILE_ARGS]], i32 0, i32 2 +; GCDA-NEXT: %[[START_FILE_ARG_2:.*]] = load i32, i32* %[[START_FILE_ARG_2_PTR]] +; GCDA-NEXT: call void @llvm_gcda_start_file(i8* %[[START_FILE_ARG_0]], i8* %[[START_FILE_ARG_1]], i32 %[[START_FILE_ARG_2]]) +; GCDA-NEXT: %[[NUM_COUNTERS_PTR:.*]] = getelementptr inbounds {{.*}}, {{.*}}* %[[FILE_INFO]], i32 0, i32 1 +; GCDA-NEXT: %[[NUM_COUNTERS:.*]] = load i32, i32* %[[NUM_COUNTERS_PTR]] +; GCDA-NEXT: %[[EMIT_FUN_ARGS_ARRAY_PTR:.*]] = getelementptr inbounds {{.*}}, {{.*}}* %[[FILE_INFO]], i32 0, i32 2 +; GCDA-NEXT: %[[EMIT_FUN_ARGS_ARRAY:.*]] = load {{.*}}*, {{.*}}** %[[EMIT_FUN_ARGS_ARRAY_PTR]] +; GCDA-NEXT: %[[EMIT_ARCS_ARGS_ARRAY_PTR:.*]] = getelementptr inbounds {{.*}}, {{.*}}* %[[FILE_INFO]], i32 0, i32 3 +; GCDA-NEXT: %[[EMIT_ARCS_ARGS_ARRAY:.*]] = load {{.*}}*, {{.*}}** %[[EMIT_ARCS_ARGS_ARRAY_PTR]] +; GCDA-NEXT: %[[ENTER_COUNTER_LOOP_COND:.*]] = icmp slt i32 0, %[[NUM_COUNTERS]] +; GCDA-NEXT: br i1 %[[ENTER_COUNTER_LOOP_COND]], label %[[COUNTER_LOOP:.*]], label %[[FILE_LOOP_LATCH]] +; +; GCDA: [[COUNTER_LOOP]]: +; GCDA-NEXT: %[[JV:.*]] = phi i32 [ 0, %[[FILE_LOOP_HEADER]] ], [ %[[NEXT_JV:.*]], %[[COUNTER_LOOP]] ] +; GCDA-NEXT: %[[EMIT_FUN_ARGS:.*]] = getelementptr inbounds {{.*}}, {{.*}}* %[[EMIT_FUN_ARGS_ARRAY]], i32 %[[JV]] +; GCDA-NEXT: %[[EMIT_FUN_ARG_0_PTR:.*]] = getelementptr inbounds {{.*}}, {{.*}}* %[[EMIT_FUN_ARGS]], i32 0, i32 0 +; GCDA-NEXT: %[[EMIT_FUN_ARG_0:.*]] = load i32, i32* %[[EMIT_FUN_ARG_0_PTR]] +; GCDA-NEXT: %[[EMIT_FUN_ARG_1_PTR:.*]] = getelementptr inbounds {{.*}}, {{.*}}* %[[EMIT_FUN_ARGS]], i32 0, i32 1 +; GCDA-NEXT: %[[EMIT_FUN_ARG_1:.*]] = load i8*, i8** %[[EMIT_FUN_ARG_1_PTR]] +; GCDA-NEXT: %[[EMIT_FUN_ARG_2_PTR:.*]] = getelementptr inbounds {{.*}}, {{.*}}* %[[EMIT_FUN_ARGS]], i32 0, i32 2 +; GCDA-NEXT: %[[EMIT_FUN_ARG_2:.*]] = load i32, i32* %[[EMIT_FUN_ARG_2_PTR]] +; GCDA-NEXT: %[[EMIT_FUN_ARG_3_PTR:.*]] = getelementptr inbounds {{.*}}, {{.*}}* %[[EMIT_FUN_ARGS]], i32 0, i32 3 +; GCDA-NEXT: %[[EMIT_FUN_ARG_3:.*]] = load i8, i8* %[[EMIT_FUN_ARG_3_PTR]] +; GCDA-NEXT: %[[EMIT_FUN_ARG_4_PTR:.*]] = getelementptr inbounds {{.*}}, {{.*}}* %[[EMIT_FUN_ARGS]], i32 0, i32 4 +; GCDA-NEXT: %[[EMIT_FUN_ARG_4:.*]] = load i32, i32* %[[EMIT_FUN_ARG_4_PTR]] +; GCDA-NEXT: call void @llvm_gcda_emit_function(i32 %[[EMIT_FUN_ARG_0]], +; GCDA-SAME: i8* %[[EMIT_FUN_ARG_1]], +; GCDA-SAME: i32 %[[EMIT_FUN_ARG_2]], +; GCDA-SAME: i8 %[[EMIT_FUN_ARG_3]], +; GCDA-SAME: i32 %[[EMIT_FUN_ARG_4]]) +; GCDA-NEXT: %[[EMIT_ARCS_ARGS:.*]] = getelementptr inbounds {{.*}}, {{.*}}* %[[EMIT_ARCS_ARGS_ARRAY]], i32 %[[JV]] +; GCDA-NEXT: %[[EMIT_ARCS_ARG_0_PTR:.*]] = getelementptr inbounds {{.*}}, {{.*}}* %[[EMIT_ARCS_ARGS]], i32 0, i32 0 +; GCDA-NEXT: %[[EMIT_ARCS_ARG_0:.*]] = load i32, i32* %[[EMIT_ARCS_ARG_0_PTR]] +; GCDA-NEXT: %[[EMIT_ARCS_ARG_1_PTR:.*]] = getelementptr inbounds {{.*}}, {{.*}}* %[[EMIT_ARCS_ARGS]], i32 0, i32 1 +; GCDA-NEXT: %[[EMIT_ARCS_ARG_1:.*]] = load i64*, i64** %[[EMIT_ARCS_ARG_1_PTR]] +; GCDA-NEXT: call void @llvm_gcda_emit_arcs(i32 %[[EMIT_ARCS_ARG_0]], +; GCDA-SAME: i64* %[[EMIT_ARCS_ARG_1]]) +; GCDA-NEXT: %[[NEXT_JV]] = add i32 %[[JV]], 1 +; GCDA-NEXT: %[[COUNTER_LOOP_COND:.*]] = icmp slt i32 %[[NEXT_JV]], %[[NUM_COUNTERS]] +; GCDA-NEXT: br i1 %[[COUNTER_LOOP_COND]], label %[[COUNTER_LOOP]], label %[[FILE_LOOP_LATCH]] +; +; GCDA: [[FILE_LOOP_LATCH]]: +; GCDA-NEXT: call void @llvm_gcda_summary_info() +; GCDA-NEXT: call void @llvm_gcda_end_file() +; GCDA-NEXT: %[[NEXT_IV]] = add i32 %[[IV]], 1 +; GCDA-NEXT: %[[FILE_LOOP_COND:.*]] = icmp slt i32 %[[NEXT_IV]], 1 +; GCDA-NEXT: br i1 %[[FILE_LOOP_COND]], label %[[FILE_LOOP_HEADER]], label %[[EXIT:.*]] +; +; GCDA: [[EXIT]]: +; GCDA-NEXT: ret void ; GCNO: == foo (0) @ ; GCNO-NOT: == bar ({{[0-9]+}}) @