diff --git a/llvm/include/llvm/CodeGen/BasicBlockSectionsProfileReader.h b/llvm/include/llvm/CodeGen/BasicBlockSectionsProfileReader.h index ad26eee642ead..6e01dfd11ee6d 100644 --- a/llvm/include/llvm/CodeGen/BasicBlockSectionsProfileReader.h +++ b/llvm/include/llvm/CodeGen/BasicBlockSectionsProfileReader.h @@ -28,17 +28,60 @@ namespace llvm { -// The cluster information for a machine basic block. -struct BBClusterInfo { - // Unique ID for this basic block. +// This structure represents a unique ID for every block specified in the +// input profile. +struct ProfileBBID { + // Basic block id associated with `MachineBasicBlock::BBID`. unsigned BBID; + // The clone id associated with the block. This is zero for the original + // block. For the cloned ones, it is equal to 1 + index of the associated + // path in `FunctionPathAndClusterInfo::ClonePaths`. + unsigned CloneID; +}; + +// This struct represents the cluster information for a machine basic block, +// which is specifed by a unique ID. This templated struct is used for both the +// raw input profile (as `BBClusterInfo`) and the processed profile +// after applying the clonings (as `BBClusterInfo`). +template struct BBClusterInfo { + // Basic block ID. + BBIDType BasicBlockID; // Cluster ID this basic block belongs to. unsigned ClusterID; // Position of basic block within the cluster. unsigned PositionInCluster; }; -using ProgramBBClusterInfoMapTy = StringMap>; +// This represents the raw input profile for one function. +struct FunctionPathAndClusterInfo { + // BB Cluster information specified by `ProfileBBID`s (before cloning). + SmallVector> ClusterInfo; + // Paths to clone. A path a -> b -> c -> d implies cloning b, c, and d along + // the edge a -> b (a is not cloned). The index of the path in this vector + // determines the `ProfileBBID::CloneID` of the cloned blocks in that path. + SmallVector> ClonePaths; +}; + +// Provides DenseMapInfo for ProfileBBID. +template <> struct DenseMapInfo { + static inline ProfileBBID getEmptyKey() { + unsigned EmptyKey = DenseMapInfo::getEmptyKey(); + return ProfileBBID{EmptyKey, EmptyKey}; + } + static inline ProfileBBID getTombstoneKey() { + unsigned TombstoneKey = DenseMapInfo::getTombstoneKey(); + return ProfileBBID{TombstoneKey, TombstoneKey}; + } + static unsigned getHashValue(const ProfileBBID &Val) { + std::pair PairVal = + std::make_pair(Val.BBID, Val.CloneID); + return DenseMapInfo>::getHashValue(PairVal); + } + static bool isEqual(const ProfileBBID &LHS, const ProfileBBID &RHS) { + return DenseMapInfo::isEqual(LHS.BBID, RHS.BBID) && + DenseMapInfo::isEqual(LHS.CloneID, RHS.CloneID); + } +}; class BasicBlockSectionsProfileReader : public ImmutablePass { public: @@ -70,11 +113,11 @@ class BasicBlockSectionsProfileReader : public ImmutablePass { // function. If the first element is true and the second element is empty, it // means unique basic block sections are desired for all basic blocks of the // function. - std::pair> - getBBClusterInfoForFunction(StringRef FuncName) const; + std::pair + getPathAndClusterInfoForFunction(StringRef FuncName) const; // Initializes the FunctionNameToDIFilename map for the current module and - // then reads the profile for matching functions. + // then reads the profile for the matching functions. bool doInitialization(Module &M) override; private: @@ -91,6 +134,12 @@ class BasicBlockSectionsProfileReader : public ImmutablePass { inconvertibleErrorCode()); } + // Parses a `ProfileBBID` from `S`. `S` must be in the form "" + // (representing an original block) or "." (representing a + // cloned block) where bbid is a non-negative integer and cloneid is a + // positive integer. + Expected parseProfileBBID(StringRef S) const; + // Reads the basic block sections profile for functions in this module. Error ReadProfile(); @@ -111,16 +160,16 @@ class BasicBlockSectionsProfileReader : public ImmutablePass { // empty string if no debug info is available. StringMap> FunctionNameToDIFilename; - // This encapsulates the BB cluster information for the whole program. + // This contains the BB cluster information for the whole program. // - // For every function name, it contains the cluster information for (all or - // some of) its basic blocks. The cluster information for every basic block - // includes its cluster ID along with the position of the basic block in that - // cluster. - ProgramBBClusterInfoMapTy ProgramBBClusterInfo; + // For every function name, it contains the cloning and cluster information + // for (all or some of) its basic blocks. The cluster information for every + // basic block includes its cluster ID along with the position of the basic + // block in that cluster. + StringMap ProgramPathAndClusterInfo; // Some functions have alias names. We use this map to find the main alias - // name for which we have mapping in ProgramBBClusterInfo. + // name which appears in ProgramPathAndClusterInfo as a key. StringMap FuncAliasMap; }; diff --git a/llvm/lib/CodeGen/BasicBlockSections.cpp b/llvm/lib/CodeGen/BasicBlockSections.cpp index de7c17082fa4b..632fd68d88b5c 100644 --- a/llvm/lib/CodeGen/BasicBlockSections.cpp +++ b/llvm/lib/CodeGen/BasicBlockSections.cpp @@ -168,31 +168,6 @@ updateBranches(MachineFunction &MF, } } -// This function provides the BBCluster information associated with a function. -// Returns true if a valid association exists and false otherwise. -bool getBBClusterInfoForFunction( - const MachineFunction &MF, - BasicBlockSectionsProfileReader *BBSectionsProfileReader, - DenseMap &V) { - - // Find the assoicated cluster information. - std::pair> P = - BBSectionsProfileReader->getBBClusterInfoForFunction(MF.getName()); - if (!P.first) - return false; - - if (P.second.empty()) { - // This indicates that sections are desired for all basic blocks of this - // function. We clear the BBClusterInfo vector to denote this. - V.clear(); - return true; - } - - for (const BBClusterInfo &BBCI : P.second) - V[BBCI.BBID] = BBCI; - return true; -} - // This function sorts basic blocks according to the cluster's information. // All explicitly specified clusters of basic blocks will be ordered // accordingly. All non-specified BBs go into a separate "Cold" section. @@ -200,12 +175,12 @@ bool getBBClusterInfoForFunction( // clusters, they are moved into a single "Exception" section. Eventually, // clusters are ordered in increasing order of their IDs, with the "Exception" // and "Cold" succeeding all other clusters. -// FuncBBClusterInfo represent the cluster information for basic blocks. It +// ClusterInfoByBBID represents the cluster information for basic blocks. It // maps from BBID of basic blocks to their cluster information. If this is // empty, it means unique sections for all basic blocks in the function. -static void -assignSections(MachineFunction &MF, - const DenseMap &FuncBBClusterInfo) { +static void assignSections( + MachineFunction &MF, + const DenseMap> &ClusterInfoByBBID) { assert(MF.hasBBSections() && "BB Sections is not set for function."); // This variable stores the section ID of the cluster containing eh_pads (if // all eh_pads are one cluster). If more than one cluster contain eh_pads, we @@ -216,17 +191,17 @@ assignSections(MachineFunction &MF, // With the 'all' option, every basic block is placed in a unique section. // With the 'list' option, every basic block is placed in a section // associated with its cluster, unless we want individual unique sections - // for every basic block in this function (if FuncBBClusterInfo is empty). + // for every basic block in this function (if ClusterInfoByBBID is empty). if (MF.getTarget().getBBSectionsType() == llvm::BasicBlockSection::All || - FuncBBClusterInfo.empty()) { + ClusterInfoByBBID.empty()) { // If unique sections are desired for all basic blocks of the function, we // set every basic block's section ID equal to its original position in // the layout (which is equal to its number). This ensures that basic // blocks are ordered canonically. MBB.setSectionID(MBB.getNumber()); } else { - auto I = FuncBBClusterInfo.find(*MBB.getBBID()); - if (I != FuncBBClusterInfo.end()) { + auto I = ClusterInfoByBBID.find(*MBB.getBBID()); + if (I != ClusterInfoByBBID.end()) { MBB.setSectionID(I->second.ClusterID); } else { // BB goes into the special cold section if it is not specified in the @@ -333,16 +308,28 @@ bool BasicBlockSections::runOnMachineFunction(MachineFunction &MF) { return true; } - BBSectionsProfileReader = &getAnalysis(); + DenseMap> ClusterInfoByBBID; + if (BBSectionsType == BasicBlockSection::List) { + auto [HasProfile, PathAndClusterInfo] = + getAnalysis() + .getPathAndClusterInfoForFunction(MF.getName()); + if (!HasProfile) + return true; + for (const BBClusterInfo &BBP : + PathAndClusterInfo.ClusterInfo) { + // TODO: Apply the path cloning profile. + assert(!BBP.BasicBlockID.CloneID && "Path cloning is not supported yet"); + const auto [I, Inserted] = ClusterInfoByBBID.try_emplace( + BBP.BasicBlockID.BBID, + BBClusterInfo{BBP.BasicBlockID.BBID, BBP.ClusterID, + BBP.PositionInCluster}); + (void)I; + assert(Inserted && "Duplicate BBID found in profile"); + } + } - // Map from BBID of blocks to their cluster information. - DenseMap FuncBBClusterInfo; - if (BBSectionsType == BasicBlockSection::List && - !getBBClusterInfoForFunction(MF, BBSectionsProfileReader, - FuncBBClusterInfo)) - return true; MF.setBBSectionsType(BBSectionsType); - assignSections(MF, FuncBBClusterInfo); + assignSections(MF, ClusterInfoByBBID); // We make sure that the cluster including the entry basic block precedes all // other clusters. @@ -376,8 +363,8 @@ bool BasicBlockSections::runOnMachineFunction(MachineFunction &MF) { // If the two basic block are in the same section, the order is decided by // their position within the section. if (XSectionID.Type == MBBSectionID::SectionType::Default) - return FuncBBClusterInfo.lookup(*X.getBBID()).PositionInCluster < - FuncBBClusterInfo.lookup(*Y.getBBID()).PositionInCluster; + return ClusterInfoByBBID.lookup(*X.getBBID()).PositionInCluster < + ClusterInfoByBBID.lookup(*Y.getBBID()).PositionInCluster; return X.getNumber() < Y.getNumber(); }; diff --git a/llvm/lib/CodeGen/BasicBlockSectionsProfileReader.cpp b/llvm/lib/CodeGen/BasicBlockSectionsProfileReader.cpp index ef5f1251f5324..6bb412a6c7534 100644 --- a/llvm/lib/CodeGen/BasicBlockSectionsProfileReader.cpp +++ b/llvm/lib/CodeGen/BasicBlockSectionsProfileReader.cpp @@ -13,6 +13,7 @@ //===----------------------------------------------------------------------===// #include "llvm/CodeGen/BasicBlockSectionsProfileReader.h" +#include "llvm/ADT/DenseSet.h" #include "llvm/ADT/SmallSet.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/SmallVector.h" @@ -34,17 +35,37 @@ INITIALIZE_PASS(BasicBlockSectionsProfileReader, "bbsections-profile-reader", "Reads and parses a basic block sections profile.", false, false) +Expected +BasicBlockSectionsProfileReader::parseProfileBBID(StringRef S) const { + SmallVector Parts; + S.split(Parts, '.'); + if (Parts.size() > 2) + return createProfileParseError(Twine("unable to parse basic block id: '") + + S + "'"); + unsigned long long BBID; + if (getAsUnsignedInteger(Parts[0], 10, BBID)) + return createProfileParseError( + Twine("unable to parse BB id: '" + Parts[0]) + + "': unsigned integer expected"); + unsigned long long CloneID = 0; + if (Parts.size() > 1 && getAsUnsignedInteger(Parts[1], 10, CloneID)) + return createProfileParseError(Twine("unable to parse clone id: '") + + Parts[1] + "': unsigned integer expected"); + return ProfileBBID{static_cast(BBID), + static_cast(CloneID)}; +} + bool BasicBlockSectionsProfileReader::isFunctionHot(StringRef FuncName) const { - return getBBClusterInfoForFunction(FuncName).first; + return getPathAndClusterInfoForFunction(FuncName).first; } -std::pair> -BasicBlockSectionsProfileReader::getBBClusterInfoForFunction( +std::pair +BasicBlockSectionsProfileReader::getPathAndClusterInfoForFunction( StringRef FuncName) const { - auto R = ProgramBBClusterInfo.find(getAliasName(FuncName)); - return R != ProgramBBClusterInfo.end() + auto R = ProgramPathAndClusterInfo.find(getAliasName(FuncName)); + return R != ProgramPathAndClusterInfo.end() ? std::pair(true, R->second) - : std::pair(false, SmallVector{}); + : std::pair(false, FunctionPathAndClusterInfo()); } // Reads the version 1 basic block sections profile. Profile for each function @@ -61,8 +82,49 @@ BasicBlockSectionsProfileReader::getBBClusterInfoForFunction( // aliases. Basic block clusters are specified by 'c' and specify the cluster of // basic blocks, and the internal order in which they must be placed in the same // section. +// This profile can also specify cloning paths which instruct the compiler to +// clone basic blocks along a path. The cloned blocks are then specified in the +// cluster information. +// The following profile lists two cloning paths (starting with 'p') for +// function bar and places the total 9 blocks within two clusters. The first two +// blocks of a cloning path specify the edge along which the path is cloned. For +// instance, path 1 (1 -> 3 -> 4) instructs that 3 and 4 must be cloned along +// the edge 1->3. Within the given clusters, each cloned block is identified by +// ".". For instance, 3.1 represents the first +// clone of block 3. Original blocks are specified just with their block ids. A +// block cloned multiple times appears with distinct clone ids. The CFG for bar +// is shown below before and after cloning with its final clusters labeled. +// +// f main +// f bar +// p 1 3 4 # cloning path 1 +// p 4 2 # cloning path 2 +// c 1 3.1 4.1 6 # basic block cluster 1 +// c 0 2 3 4 2.1 5 # basic block cluster 2 +// **************************************************************************** +// function bar before and after cloning with basic block clusters shown. +// **************************************************************************** +// .... .............. +// 0 -------+ : 0 :---->: 1 ---> 3.1 : +// | | : | : :........ | : +// v v : v : : v : +// +--> 2 --> 5 1 ~~~~~~> +---: 2 : : 4.1: clsuter 1 +// | | | | : | : : | : +// | v | | : v ....... : v : +// | 3 <------+ | : 3 <--+ : : 6 : +// | | | : | | : :....: +// | v | : v | : +// +--- 4 ---> 6 | : 4 | : +// | : | | : +// | : v | : +// | :2.1---+ : cluster 2 +// | : | ......: +// | : v : +// +-->: 5 : +// .... +// **************************************************************************** Error BasicBlockSectionsProfileReader::ReadV1Profile() { - auto FI = ProgramBBClusterInfo.end(); + auto FI = ProgramPathAndClusterInfo.end(); // Current cluster ID corresponding to this function. unsigned CurrentCluster = 0; @@ -71,7 +133,7 @@ Error BasicBlockSectionsProfileReader::ReadV1Profile() { // Temporary set to ensure every basic block ID appears once in the clusters // of a function. - SmallSet FuncBBIDs; + DenseSet FuncBBIDs; // Debug-info-based module filename for the current function. Empty string // means no filename. @@ -85,7 +147,7 @@ Error BasicBlockSectionsProfileReader::ReadV1Profile() { S.split(Values, ' '); switch (Specifier) { case '@': - break; + continue; case 'm': // Module name speicifer. if (Values.size() != 1) { return createProfileParseError(Twine("invalid module name value: '") + @@ -106,7 +168,7 @@ Error BasicBlockSectionsProfileReader::ReadV1Profile() { if (!FunctionFound) { // Skip the following profile by setting the profile iterator (FI) to // the past-the-end element. - FI = ProgramBBClusterInfo.end(); + FI = ProgramPathAndClusterInfo.end(); DIFilename = ""; continue; } @@ -115,7 +177,7 @@ Error BasicBlockSectionsProfileReader::ReadV1Profile() { // Prepare for parsing clusters of this function name. // Start a new cluster map for this function name. - auto R = ProgramBBClusterInfo.try_emplace(Values.front()); + auto R = ProgramPathAndClusterInfo.try_emplace(Values.front()); // Report error when multiple profiles have been specified for the same // function. if (!R.second) @@ -132,38 +194,55 @@ Error BasicBlockSectionsProfileReader::ReadV1Profile() { case 'c': // Basic block cluster specifier. // Skip the profile when we the profile iterator (FI) refers to the // past-the-end element. - if (FI == ProgramBBClusterInfo.end()) - break; + if (FI == ProgramPathAndClusterInfo.end()) + continue; // Reset current cluster position. CurrentPosition = 0; - for (auto BBIDStr : Values) { - unsigned long long BBID; - if (getAsUnsignedInteger(BBIDStr, 10, BBID)) - return createProfileParseError(Twine("unsigned integer expected: '") + - BBIDStr + "'"); - if (!FuncBBIDs.insert(BBID).second) + for (auto BasicBlockIDStr : Values) { + auto BasicBlockID = parseProfileBBID(BasicBlockIDStr); + if (!BasicBlockID) + return BasicBlockID.takeError(); + if (!FuncBBIDs.insert(*BasicBlockID).second) return createProfileParseError( - Twine("duplicate basic block id found '") + BBIDStr + "'"); - if (BBID == 0 && CurrentPosition) + Twine("duplicate basic block id found '") + BasicBlockIDStr + + "'"); + + if (!BasicBlockID->BBID && CurrentPosition) return createProfileParseError( - "entry BB (0) does not begin a cluster"); + "entry BB (0) does not begin a cluster."); - FI->second.emplace_back( - BBClusterInfo{((unsigned)BBID), CurrentCluster, CurrentPosition++}); + FI->second.ClusterInfo.emplace_back(BBClusterInfo{ + *std::move(BasicBlockID), CurrentCluster, CurrentPosition++}); } CurrentCluster++; continue; + case 'p': { // Basic block cloning path specifier. + SmallSet BBsInPath; + FI->second.ClonePaths.push_back({}); + for (size_t I = 0; I < Values.size(); ++I) { + auto BBIDStr = Values[I]; + unsigned long long BBID = 0; + if (getAsUnsignedInteger(BBIDStr, 10, BBID)) + return createProfileParseError(Twine("unsigned integer expected: '") + + BBIDStr + "'"); + if (I != 0 && !BBsInPath.insert(BBID).second) + return createProfileParseError( + Twine("duplicate cloned block in path: '") + BBIDStr + "'"); + FI->second.ClonePaths.back().push_back(BBID); + } + continue; + } default: return createProfileParseError(Twine("invalid specifier: '") + Twine(Specifier) + "'"); } + llvm_unreachable("should not break from this switch statement"); } return Error::success(); } Error BasicBlockSectionsProfileReader::ReadV0Profile() { - auto FI = ProgramBBClusterInfo.end(); - + auto FI = ProgramPathAndClusterInfo.end(); // Current cluster ID corresponding to this function. unsigned CurrentCluster = 0; // Current position in the current cluster. @@ -184,7 +263,7 @@ Error BasicBlockSectionsProfileReader::ReadV0Profile() { if (S.consume_front("!")) { // Skip the profile when we the profile iterator (FI) refers to the // past-the-end element. - if (FI == ProgramBBClusterInfo.end()) + if (FI == ProgramPathAndClusterInfo.end()) continue; SmallVector BBIDs; S.split(BBIDs, ' '); @@ -202,8 +281,10 @@ Error BasicBlockSectionsProfileReader::ReadV0Profile() { return createProfileParseError( "entry BB (0) does not begin a cluster"); - FI->second.emplace_back( - BBClusterInfo{((unsigned)BBID), CurrentCluster, CurrentPosition++}); + FI->second.ClusterInfo.emplace_back( + BBClusterInfo({{static_cast(BBID), 0}, + CurrentCluster, + CurrentPosition++})); } CurrentCluster++; } else { @@ -237,7 +318,7 @@ Error BasicBlockSectionsProfileReader::ReadV0Profile() { if (!FunctionFound) { // Skip the following profile by setting the profile iterator (FI) to // the past-the-end element. - FI = ProgramBBClusterInfo.end(); + FI = ProgramPathAndClusterInfo.end(); continue; } for (size_t i = 1; i < Aliases.size(); ++i) @@ -245,7 +326,7 @@ Error BasicBlockSectionsProfileReader::ReadV0Profile() { // Prepare for parsing clusters of this function name. // Start a new cluster map for this function name. - auto R = ProgramBBClusterInfo.try_emplace(Aliases.front()); + auto R = ProgramPathAndClusterInfo.try_emplace(Aliases.front()); // Report error when multiple profiles have been specified for the same // function. if (!R.second) @@ -261,7 +342,7 @@ Error BasicBlockSectionsProfileReader::ReadV0Profile() { // Basic Block Sections can be enabled for a subset of machine basic blocks. // This is done by passing a file containing names of functions for which basic -// block sections are desired. Additionally, machine basic block ids of the +// block sections are desired. Additionally, machine basic block ids of the // functions can also be specified for a finer granularity. Moreover, a cluster // of basic blocks could be assigned to the same section. // Optionally, a debug-info filename can be specified for each function to allow diff --git a/llvm/test/CodeGen/X86/basic-block-sections-clusters-error.ll b/llvm/test/CodeGen/X86/basic-block-sections-clusters-error.ll index 5577601c02cfd..597d8f6707ecc 100644 --- a/llvm/test/CodeGen/X86/basic-block-sections-clusters-error.ll +++ b/llvm/test/CodeGen/X86/basic-block-sections-clusters-error.ll @@ -32,13 +32,35 @@ ; RUN: echo '!dummy1' >> %t8 ; RUN: not --crash llc < %s -O0 -mtriple=x86_64 -function-sections -basic-block-sections=%t8 2>&1 | FileCheck %s --check-prefix=CHECK-ERROR8 ; CHECK-ERROR8: LLVM ERROR: invalid profile {{.*}} at line 2: invalid specifier: '!' -; RUN: echo 'v1' > %t0 -; RUN: echo 'm dummy1/module1 dummy1/module2' +; RUN: echo 'v1' > %t9 +; RUN: echo 'm dummy1/module1 dummy1/module2' >> %t9 ; RUN: echo 'f dummy1' >> %t9 -; RUN: not --crash llc < %s -O0 -mtriple=x86_64 -function-sections -basic-block-sections=%t8 2>&1 | FileCheck %s --check-prefix=CHECK-ERROR8 -; CHECK-ERROR9: LLVM ERROR: invalid profile {{.*}} at line 2: invalid module name value: 'dummy1/module dummy1/module2' - - +; RUN: not --crash llc < %s -O0 -mtriple=x86_64 -function-sections -basic-block-sections=%t9 2>&1 | FileCheck %s --check-prefix=CHECK-ERROR9 +; CHECK-ERROR9: LLVM ERROR: invalid profile {{.*}} at line 2: invalid module name value: 'dummy1/module1 dummy1/module2' +;; +;; Error handling for version 1, cloning paths. +; RUN: echo 'v1' > %t10 +; RUN: echo 'f dummy1' >> %t10 +; RUN: echo 'c 0 1.1.1' >> %t10 +; RUN: not --crash llc < %s -O0 -mtriple=x86_64 -function-sections -basic-block-sections=%t10 2>&1 | FileCheck %s --check-prefix=CHECK-ERROR10 +; CHECK-ERROR10: LLVM ERROR: invalid profile {{.*}} at line 3: unable to parse basic block id: '1.1.1' +; RUN: echo 'v1' > %t11 +; RUN: echo 'f dummy1' >> %t11 +; RUN: echo 'c 0 1.a' >> %t11 +; RUN: not --crash llc < %s -O0 -mtriple=x86_64 -function-sections -basic-block-sections=%t11 2>&1 | FileCheck %s --check-prefix=CHECK-ERROR11 +; CHECK-ERROR11: LLVM ERROR: invalid profile {{.*}} at line 3: unable to parse clone id: 'a' +; RUN: echo 'v1' > %t12 +; RUN: echo 'f dummy1' >> %t12 +; RUN: echo 'c 0 1' >> %t12 +; RUN: echo 'p 1 2.1' >> %t12 +; RUN: not --crash llc < %s -O0 -mtriple=x86_64 -function-sections -basic-block-sections=%t12 2>&1 | FileCheck %s --check-prefix=CHECK-ERROR12 +; CHECK-ERROR12: LLVM ERROR: invalid profile {{.*}} at line 4: unsigned integer expected: '2.1' +; RUN: echo 'v1' > %t13 +; RUN: echo 'f dummy1' >> %t13 +; RUN: echo 'c 0 1' >> %t13 +; RUN: echo 'p 1 2 3 2' >> %t13 +; RUN: not --crash llc < %s -O0 -mtriple=x86_64 -function-sections -basic-block-sections=%t13 2>&1 | FileCheck %s --check-prefix=CHECK-ERROR13 +; CHECK-ERROR13: LLVM ERROR: invalid profile {{.*}} at line 4: duplicate cloned block in path: '2' define i32 @dummy1(i32 %x, i32 %y, i32 %z) { entry: @@ -63,4 +85,3 @@ define i32 @dummy2(i32 %x, i32 %y, i32 %z) !dbg !4 { !3 = !{i32 2, !"Debug Info Version", i32 3} !4 = distinct !DISubprogram(name: "dummy1", scope: !1, unit: !0) -