Index: include/llvm/ProfileData/SampleProf.h =================================================================== --- include/llvm/ProfileData/SampleProf.h +++ include/llvm/ProfileData/SampleProf.h @@ -75,6 +75,18 @@ unsigned Discriminator; }; +/// Represents the relative location of a callsite. +/// +/// Callsite locations are specified by the line offset from the +/// beginning of the function (marked by the line where the function +/// head is), the discriminator value within that line, and the callee +/// function name. +struct CallsiteLocation : public LineLocation { + CallsiteLocation(int L, unsigned D, StringRef N) + : LineLocation(L, D), CalleeName(N) {} + StringRef CalleeName; +}; + } // End namespace sampleprof template <> struct DenseMapInfo { @@ -99,6 +111,31 @@ } }; +template <> struct DenseMapInfo { + typedef DenseMapInfo OffsetInfo; + typedef DenseMapInfo DiscriminatorInfo; + typedef DenseMapInfo CalleeNameInfo; + static inline sampleprof::CallsiteLocation getEmptyKey() { + return sampleprof::CallsiteLocation(OffsetInfo::getEmptyKey(), + DiscriminatorInfo::getEmptyKey(), ""); + } + static inline sampleprof::CallsiteLocation getTombstoneKey() { + return sampleprof::CallsiteLocation(OffsetInfo::getTombstoneKey(), + DiscriminatorInfo::getTombstoneKey(), + ""); + } + static inline unsigned getHashValue(sampleprof::CallsiteLocation Val) { + return DenseMapInfo>::getHashValue( + std::pair(Val.LineOffset, Val.Discriminator)); + } + static inline bool isEqual(sampleprof::CallsiteLocation LHS, + sampleprof::CallsiteLocation RHS) { + return LHS.LineOffset == RHS.LineOffset && + LHS.Discriminator == RHS.Discriminator && + LHS.CalleeName.equals(RHS.CalleeName); + } +}; + namespace sampleprof { /// Representation of a single sample record. @@ -159,6 +196,8 @@ }; typedef DenseMap BodySampleMap; +class FunctionSamples; +typedef DenseMap CallsiteSampleMap; /// Representation of the samples collected for a function. /// @@ -200,7 +239,34 @@ return sampleRecordAt(LineLocation(LineOffset, Discriminator)).getSamples(); } - bool empty() const { return BodySamples.empty(); } + /// Return the number of samples collected at the given location. + /// Each location is specified by \p LineOffset and \p Discriminator. + /// If the location is not found in profile, return 0. + unsigned findSamplesAt(int LineOffset, unsigned Discriminator) const { + const auto &ret = BodySamples.find(LineLocation(LineOffset, Discriminator)); + if (ret == BodySamples.end()) + return 0; + else + return ret->second.getSamples(); + } + + /// Return the function samples at the given callsite location. + FunctionSamples &functionSamplesAt(const CallsiteLocation &Loc) { + return CallsiteSamples[Loc]; + } + + /// Return a pointer to function samples at the given callsite location. + const FunctionSamples * + findFunctionSamplesAt(const CallsiteLocation &Loc) const { + auto iter = CallsiteSamples.find(Loc); + if (iter == CallsiteSamples.end()) { + return NULL; + } else { + return &iter->second; + } + } + + bool empty() const { return TotalSamples == 0; } /// Return the total number of samples collected inside the function. unsigned getTotalSamples() const { return TotalSamples; } @@ -212,6 +278,11 @@ /// Return all the samples collected in the body of the function. const BodySampleMap &getBodySamples() const { return BodySamples; } + /// Return all the callsite samples collected in the body of the function. + const CallsiteSampleMap &getCallsiteSamples() const { + return CallsiteSamples; + } + /// Merge the samples in \p Other into this one. void merge(const FunctionSamples &Other) { addTotalSamples(Other.getTotalSamples()); @@ -221,6 +292,11 @@ const SampleRecord &Rec = I.second; sampleRecordAt(Loc).merge(Rec); } + for (const auto &I : Other.getCallsiteSamples()) { + const CallsiteLocation &Loc = I.first; + const FunctionSamples &Rec = I.second; + functionSamplesAt(Loc).merge(Rec); + } } private: @@ -241,6 +317,8 @@ /// collected at the corresponding line offset. All line locations /// are an offset from the start of the function. BodySampleMap BodySamples; + + CallsiteSampleMap CallsiteSamples; }; } // End namespace sampleprof Index: lib/ProfileData/SampleProfReader.cpp =================================================================== --- lib/ProfileData/SampleProfReader.cpp +++ lib/ProfileData/SampleProfReader.cpp @@ -25,14 +25,22 @@ // Each section has the following format // // function1:total_samples:total_head_samples -// offset1[.discriminator]: number_of_samples [fn1:num fn2:num ... ] -// offset2[.discriminator]: number_of_samples [fn3:num fn4:num ... ] -// ... -// offsetN[.discriminator]: number_of_samples [fn5:num fn6:num ... ] -// -// The file may contain blank lines between sections and within a -// section. However, the spacing within a single line is fixed. Additional -// spaces will result in an error while reading the file. +// offset1[.discriminator]: number_of_samples [fn1:num fn2:num ... ] +// offset2[.discriminator]: number_of_samples [fn3:num fn4:num ... ] +// ... +// offsetN[.discriminator]: number_of_samples [fn5:num fn6:num ... ] +// offsetA[.discriminator]: fnA:num_of_total_samples +// offsetA1[.discriminator]: number_of_samples [fn7:num fn8:num ... ] +// ... +// +// This is a nested tree in which the identation represent the nest level +// of the inline stack. There is no blank line in the file. And the spacing +// within a single line is fixed. Additional spaces will result in an error +// while reading the file. +// +// Inline stack is a stack of source locations in which the tip of the stack +// represents the leaf function, and the bottom of the stack represents the +// actual symol in which the instruction belongs. // // Function names must be mangled in order for the profile loader to // match them in the current translation unit. The two numbers in the @@ -41,6 +49,11 @@ // in the prologue of the function (second number). This head sample // count provides an indicator of how frequently the function is invoked. // +// There are two types of lines in the function body. +// +// * Sampled line represents the profile information of a source location. +// * Callsite line represents the profile inofrmation of a callsite. +// // Each sampled line may contain several items. Some are optional (marked // below): // @@ -92,6 +105,16 @@ // instruction that calls one of ``foo()``, ``bar()`` and ``baz()``, // with ``baz()`` being the relatively more frequently called target. // +// Each callsite line may contain several items. Some are optional. +// +// a. Source line offset. This number represents the line number of the +// callsite that is inlined in the profiled binary. +// +// b. [OPTIONAL] Discriminator. Same as the discriminator for sampled line. +// +// c. Number of samples. This is an integer quantity representing the +// total number of samples collected for the inlined instance at this +// callsite //===----------------------------------------------------------------------===// #include "llvm/ProfileData/SampleProfReader.h" @@ -100,7 +123,8 @@ #include "llvm/Support/LEB128.h" #include "llvm/Support/LineIterator.h" #include "llvm/Support/MemoryBuffer.h" -#include "llvm/Support/Regex.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/SmallVector.h" using namespace llvm::sampleprof; using namespace llvm; @@ -143,6 +167,96 @@ dumpFunctionProfile(I.getKey(), OS); } +/// \brief Parse \p Input as function head. +/// +/// Parse one line of \p Input, and update function name in \p FName, +/// function's total sample count in \p NumSamples, function's entry +/// count in \p NumHeadSamples. +/// +/// \returns true if parsing is successful. +static bool ParseHead(const StringRef &Input, StringRef &FName, + unsigned &NumSamples, unsigned &NumHeadSamples) { + if (Input[0] == ' ') + return false; + size_t n1 = Input.find(':'); + FName = Input.substr(0, n1); + size_t n2 = Input.find(':', n1 + 1); + if (Input.substr(n1 + 1, n2 - n1 - 1).getAsInteger(10, NumSamples)) + return false; + if (Input.substr(n2 + 1).getAsInteger(10, NumHeadSamples)) + return false; + return true; +} + +/// \brief Parse \p Input as line sample. +/// +/// \param Input input line. +/// \param IsCallsite true if the line represents an inlined callsite. +/// \param Depth the depth of the inline stack. +/// \param NumSamples total samples of the line/inlined callsite. +/// \param LineOffset line offset to the start of the function. +/// \param Discriminator discriminator of the line. +/// \param TargetCountMap map from indirect call target to count. +/// +/// returns true if parsing is successful. +static bool ParseLine(const StringRef &Input, bool &IsCallsite, unsigned &Depth, + unsigned &NumSamples, unsigned &LineOffset, + unsigned &Discriminator, StringRef &CalleeName, + DenseMap &TargetCountMap) { + for (Depth = 0; Input[Depth] == ' '; Depth++) + ; + if (Depth == 0) + return false; + + size_t n1 = Input.find(':'); + StringRef Loc = Input.substr(Depth, n1 - Depth); + size_t n2 = Loc.find('.'); + if (n2 == StringRef::npos) { + if (Loc.getAsInteger(10, LineOffset)) + return false; + Discriminator = 0; + } else { + if (Loc.substr(0, n2).getAsInteger(10, LineOffset)) + return false; + if (Loc.substr(n2 + 1).getAsInteger(10, Discriminator)) + return false; + } + + StringRef Rest = Input.substr(n1 + 2); + if (Rest[0] >= '0' && Rest[0] <= '9') { + IsCallsite = false; + size_t n3 = Rest.find(' '); + if (n3 == StringRef::npos) { + if (Rest.getAsInteger(10, NumSamples)) + return false; + } else { + if (Rest.substr(0, n3).getAsInteger(10, NumSamples)) + return false; + } + while (n3 != StringRef::npos) { + n3 += Rest.substr(n3).find_first_not_of(' '); + Rest = Rest.substr(n3); + n3 = Rest.find(' '); + StringRef pair = Rest; + if (n3 != StringRef::npos) { + pair = Rest.substr(0, n3); + } + int n4 = pair.find(':'); + unsigned count; + if (pair.substr(n4 + 1).getAsInteger(10, count)) + return false; + TargetCountMap[pair.substr(0, n4)] = count; + } + } else { + IsCallsite = true; + int n3 = Rest.find_last_of(':'); + CalleeName = Rest.substr(0, n3); + if (Rest.substr(n3 + 1).getAsInteger(10, NumSamples)) + return false; + } + return true; +} + /// \brief Load samples from a text file. /// /// See the documentation at the top of the file for an explanation of @@ -152,12 +266,8 @@ std::error_code SampleProfileReaderText::read() { line_iterator LineIt(*Buffer, /*SkipBlanks=*/true, '#'); - // Read the profile of each function. Since each function may be - // mentioned more than once, and we are collecting flat profiles, - // accumulate samples as we parse them. - Regex HeadRE("^([^0-9].*):([0-9]+):([0-9]+)$"); - Regex LineSampleRE("^([0-9]+)\\.?([0-9]+)?: ([0-9]+)(.*)$"); - Regex CallSampleRE(" +([^0-9 ][^ ]*):([0-9]+)"); + SmallVector InlineStack; + while (!LineIt.is_at_eof()) { // Read the header of each function. // @@ -171,60 +281,54 @@ // // The only requirement we place on the identifier, then, is that it // should not begin with a number. - SmallVector Matches; - if (!HeadRE.match(*LineIt, &Matches)) { - reportError(LineIt.line_number(), - "Expected 'mangled_name:NUM:NUM', found " + *LineIt); - return sampleprof_error::malformed; - } - assert(Matches.size() == 4); - StringRef FName = Matches[1]; - unsigned NumSamples, NumHeadSamples; - Matches[2].getAsInteger(10, NumSamples); - Matches[3].getAsInteger(10, NumHeadSamples); - Profiles[FName] = FunctionSamples(); - FunctionSamples &FProfile = Profiles[FName]; - FProfile.addTotalSamples(NumSamples); - FProfile.addHeadSamples(NumHeadSamples); - ++LineIt; - - // Now read the body. The body of the function ends when we reach - // EOF or when we see the start of the next function. - while (!LineIt.is_at_eof() && isdigit((*LineIt)[0])) { - if (!LineSampleRE.match(*LineIt, &Matches)) { + if ((*LineIt)[0] != ' ') { + unsigned NumSamples, NumHeadSamples; + StringRef FName; + if (!ParseHead(*LineIt, FName, NumSamples, NumHeadSamples)) { + reportError(LineIt.line_number(), + "Expected 'mangled_name:NUM:NUM', found " + *LineIt); + return sampleprof_error::malformed; + } + Profiles[FName] = FunctionSamples(); + FunctionSamples &FProfile = Profiles[FName]; + FProfile.addTotalSamples(NumSamples); + FProfile.addHeadSamples(NumHeadSamples); + InlineStack.clear(); + InlineStack.push_back(&FProfile); + } else { + unsigned NumSamples; + StringRef FName; + DenseMap TargetCountMap; + bool IsCallsite; + unsigned Depth, LineOffset, Discriminator; + if (!ParseLine(*LineIt, IsCallsite, Depth, NumSamples, LineOffset, + Discriminator, FName, TargetCountMap)) { reportError(LineIt.line_number(), "Expected 'NUM[.NUM]: NUM[ mangled_name:NUM]*', found " + *LineIt); return sampleprof_error::malformed; } - assert(Matches.size() == 5); - unsigned LineOffset, NumSamples, Discriminator = 0; - Matches[1].getAsInteger(10, LineOffset); - if (Matches[2] != "") - Matches[2].getAsInteger(10, Discriminator); - Matches[3].getAsInteger(10, NumSamples); - - // If there are function calls in this line, generate a call sample - // entry for each call. - std::string CallsLine(Matches[4]); - while (CallsLine != "") { - SmallVector CallSample; - if (!CallSampleRE.match(CallsLine, &CallSample)) { - reportError(LineIt.line_number(), - "Expected 'mangled_name:NUM', found " + CallsLine); - return sampleprof_error::malformed; + if (IsCallsite) { + while (InlineStack.size() > Depth) { + InlineStack.pop_back(); + } + FunctionSamples &FSamples = InlineStack.back()->functionSamplesAt( + CallsiteLocation(LineOffset, Discriminator, FName)); + FSamples.addTotalSamples(NumSamples); + InlineStack.push_back(&FSamples); + } else { + while (InlineStack.size() > Depth) { + InlineStack.pop_back(); + } + FunctionSamples &FProfile = *InlineStack.back(); + for (const auto &name_count : TargetCountMap) { + FProfile.addCalledTargetSamples(LineOffset, Discriminator, + name_count.first, name_count.second); } - StringRef CalledFunction = CallSample[1]; - unsigned CalledFunctionSamples; - CallSample[2].getAsInteger(10, CalledFunctionSamples); - FProfile.addCalledTargetSamples(LineOffset, Discriminator, - CalledFunction, CalledFunctionSamples); - CallsLine = CallSampleRE.sub("", CallsLine); + FProfile.addBodySamples(LineOffset, Discriminator, NumSamples); } - - FProfile.addBodySamples(LineOffset, Discriminator, NumSamples); - ++LineIt; } + ++LineIt; } return sampleprof_error::success; @@ -474,7 +578,6 @@ return sampleprof_error::success; } - std::error_code SampleProfileReaderGCC::readOneFunctionProfile(const SourceStack &Stack, bool Update) { @@ -577,7 +680,6 @@ return sampleprof_error::not_implemented; } - /// \brief Read a GCC AutoFDO profile. /// /// This format is generated by the Linux Perf conversion tool at @@ -591,8 +693,8 @@ if (std::error_code EC = readFunctionProfiles()) return EC; - // FIXME(dnovillo) - Module groups and working set support are not - // yet implemented. +// FIXME(dnovillo) - Module groups and working set support are not +// yet implemented. #if 0 // Read the module group file. if (std::error_code EC = readModuleGroup()) Index: lib/Transforms/IPO/SampleProfile.cpp =================================================================== --- lib/Transforms/IPO/SampleProfile.cpp +++ lib/Transforms/IPO/SampleProfile.cpp @@ -45,6 +45,7 @@ #include "llvm/Support/Debug.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Transforms/IPO.h" +#include "llvm/Transforms/Utils/Cloning.h" #include using namespace llvm; @@ -103,6 +104,9 @@ bool emitAnnotations(Function &F); unsigned getInstWeight(Instruction &I); unsigned getBlockWeight(BasicBlock *BB); + const FunctionSamples *findCalleeFunctionSamples(const CallInst &I) const; + const FunctionSamples *findFunctionSamples(const Instruction &I) const; + bool inlineHotFunctions(Function &F); void printEdgeWeight(raw_ostream &OS, Edge E); void printBlockWeight(raw_ostream &OS, BasicBlock *BB); void printBlockEquivalence(raw_ostream &OS, BasicBlock *BB); @@ -223,7 +227,10 @@ const DILocation *DIL = DLoc; int LOffset = Lineno - HeaderLineno; unsigned Discriminator = DIL->getDiscriminator(); - unsigned Weight = Samples->samplesAt(LOffset, Discriminator); + const FunctionSamples *FS = findFunctionSamples(Inst); + unsigned Weight = 0; + if (FS) + Weight = FS->findSamplesAt(LOffset, Discriminator); DEBUG(dbgs() << " " << Lineno << "." << Discriminator << ":" << Inst << " (line offset: " << LOffset << "." << Discriminator << " - weight: " << Weight << ")\n"); @@ -275,6 +282,119 @@ return Changed; } +/// \brief Get the FunctionSamples for a call instruction. +/// +/// The FunctionSamples of a call instruction \p Inst is the inlined +/// instance in which that call instruction is calling to. It contains +/// all samples that resides in the inlined instance. We first find the +/// inlined instance in which the call instruction is from, then we +/// traverse its children to find the callsite with the matching +/// location and callee function name. +/// +/// \param Inst Call instruction to query. +/// +/// \returns The FunctionSamples pointer to the inlined instance. +const FunctionSamples * +SampleProfileLoader::findCalleeFunctionSamples(const CallInst &Inst) const { + const DILocation *DIL = Inst.getDebugLoc(); + if (!DIL) { + return nullptr; + } + DISubprogram *SP = DIL->getScope()->getSubprogram(); + if (!SP || DIL->getLine() < SP->getLine()) + return nullptr; + + Function *CalleeFunc = Inst.getCalledFunction(); + if (!CalleeFunc) { + return nullptr; + } + + StringRef CalleeName = CalleeFunc->getName(); + const FunctionSamples *FS = findFunctionSamples(Inst); + if (FS == nullptr) + return nullptr; + + return FS->findFunctionSamplesAt(CallsiteLocation( + DIL->getLine() - SP->getLine(), DIL->getDiscriminator(), CalleeName)); +} + +/// \brief Get the FunctionSamples for an instruction. +/// +/// The FunctionSamples of an instruction \p Inst is the inlined instance +/// in which that instruction is coming from. We traverse the inline stack +/// of that instruction, and match it with the tree nodes in the profile. +/// +/// \param Inst Instruction to query. +/// +/// \returns the FunctionSamples pointer to the inlined instance. +const FunctionSamples * +SampleProfileLoader::findFunctionSamples(const Instruction &Inst) const { + SmallVector S; + const DILocation *DIL = Inst.getDebugLoc(); + if (!DIL) { + return Samples; + } + StringRef CalleeName; + for (const DILocation *DIL = Inst.getDebugLoc(); DIL; + DIL = DIL->getInlinedAt()) { + DISubprogram *SP = DIL->getScope()->getSubprogram(); + if (!SP || DIL->getLine() < SP->getLine()) + return nullptr; + if (!CalleeName.empty()) { + S.push_back(CallsiteLocation(DIL->getLine() - SP->getLine(), + DIL->getDiscriminator(), CalleeName)); + } + CalleeName = SP->getLinkageName(); + } + if (S.size() == 0) + return Samples; + const FunctionSamples *FS = Samples; + for (int i = S.size() - 1; i >= 0 && FS != nullptr; i--) { + FS = FS->findFunctionSamplesAt(S[i]); + } + return FS; +} + +/// \brief Iteratively inline hot callsites of a function. +/// +/// Iteratively traverse all callsites of the function \p F, and find if +/// the corresponding inlined instance exists and is hot in profile. If +/// it is hot enough, inline the callsites and adds new callsites of the +/// callee into the caller. +/// +/// \param F function to perform iterative inlining. +/// +/// \returns True if there is any inline happened. +bool SampleProfileLoader::inlineHotFunctions(Function &F) { + bool Changed = false; + while (true) { + bool LocalChanged = false; + SmallVector CIS; + for (auto &BB : F) { + for (auto &I : BB.getInstList()) { + CallInst *CI = dyn_cast(&I); + if (CI) { + const FunctionSamples *FS = findCalleeFunctionSamples(*CI); + if (FS && FS->getTotalSamples() > 0) { + CIS.push_back(CI); + } + } + } + } + for (auto CI : CIS) { + InlineFunctionInfo IFI; + if (InlineFunction(CI, IFI)) + LocalChanged = true; + } + if (LocalChanged) { + Changed = true; + } else { + break; + } + } + return Changed; +} + /// \brief Find equivalence classes for the given block. /// /// This finds all the blocks that are guaranteed to execute the same @@ -725,6 +845,8 @@ DEBUG(dbgs() << "Line number for the first instruction in " << F.getName() << ": " << HeaderLineno << "\n"); + Changed |= inlineHotFunctions(F); + // Compute basic block weights. Changed |= computeBlockWeights(F); Index: test/Transforms/SampleProfile/Inputs/bad_discriminator_value.prof =================================================================== --- test/Transforms/SampleProfile/Inputs/bad_discriminator_value.prof +++ test/Transforms/SampleProfile/Inputs/bad_discriminator_value.prof @@ -1,2 +1,2 @@ empty:100:0 -1.-3: 10 + 1.-3: 10 Index: test/Transforms/SampleProfile/Inputs/bad_fn_header.prof =================================================================== --- test/Transforms/SampleProfile/Inputs/bad_fn_header.prof +++ test/Transforms/SampleProfile/Inputs/bad_fn_header.prof @@ -1,3 +1,3 @@ 3empty:100:BAD -0: 0 -1: 100 + 0: 0 + 1: 100 Index: test/Transforms/SampleProfile/Inputs/bad_mangle.prof =================================================================== --- test/Transforms/SampleProfile/Inputs/bad_mangle.prof +++ test/Transforms/SampleProfile/Inputs/bad_mangle.prof @@ -1,3 +1,3 @@ double convert(float):2909472:181842 -0: 181842 -1: 181842 + 0: 181842 + 1: 181842 Index: test/Transforms/SampleProfile/Inputs/bad_sample_line.prof =================================================================== --- test/Transforms/SampleProfile/Inputs/bad_sample_line.prof +++ test/Transforms/SampleProfile/Inputs/bad_sample_line.prof @@ -1,3 +1,3 @@ empty:100:0 -0: 0 -1: BAD + 0: 0 + 1: BAD Index: test/Transforms/SampleProfile/Inputs/bad_samples.prof =================================================================== --- test/Transforms/SampleProfile/Inputs/bad_samples.prof +++ test/Transforms/SampleProfile/Inputs/bad_samples.prof @@ -1,2 +1,2 @@ empty:100:0 -1.3: -10 + 1.3: -10 Index: test/Transforms/SampleProfile/Inputs/branch.prof =================================================================== --- test/Transforms/SampleProfile/Inputs/branch.prof +++ test/Transforms/SampleProfile/Inputs/branch.prof @@ -1,8 +1,8 @@ main:15680:0 -0: 0 -4: 0 -7: 0 -9: 10226 -10: 2243 -16: 0 -18: 0 + 0: 0 + 4: 0 + 7: 0 + 9: 10226 + 10: 2243 + 16: 0 + 18: 0 Index: test/Transforms/SampleProfile/Inputs/calls.prof =================================================================== --- test/Transforms/SampleProfile/Inputs/calls.prof +++ test/Transforms/SampleProfile/Inputs/calls.prof @@ -1,10 +1,8 @@ _Z3sumii:105580:5279 -0: 5279 -1: 5279 -2: 5279 + 0: 5279 + 1: 5279 + 2: 5279 main:225715:0 -2.1: 5553 -3: 5391 -# This indicates that at line 3 of this function, the 'then' branch -# of the conditional is taken (discriminator '1'). -3.1: 5752 _Z3sumii:5860 + 2.1: 5553 + 3: 5391 + 3.1: 5752 _Z3sumii:5860 Index: test/Transforms/SampleProfile/Inputs/discriminator.prof =================================================================== --- test/Transforms/SampleProfile/Inputs/discriminator.prof +++ test/Transforms/SampleProfile/Inputs/discriminator.prof @@ -1,8 +1,8 @@ foo:1000:0 -1: 1 -2: 1 -2.1: 100 -3: 100 -3.1: 5 -4: 100 -5: 1 + 1: 1 + 2: 1 + 2.1: 100 + 3: 100 + 3.1: 5 + 4: 100 + 5: 1 Index: test/Transforms/SampleProfile/Inputs/entry_counts.prof =================================================================== --- test/Transforms/SampleProfile/Inputs/entry_counts.prof +++ test/Transforms/SampleProfile/Inputs/entry_counts.prof @@ -1,3 +1,3 @@ empty:100:13293 -0: 0 -1: 100 + 0: 0 + 1: 100 Index: test/Transforms/SampleProfile/Inputs/fnptr.prof =================================================================== --- test/Transforms/SampleProfile/Inputs/fnptr.prof +++ test/Transforms/SampleProfile/Inputs/fnptr.prof @@ -1,12 +1,12 @@ _Z3fooi:7711:610 -1: 610 + 1: 610 _Z3bari:20301:1437 -1: 1437 + 1: 1437 main:184019:0 -4: 534 -6: 2080 -9: 2064 _Z3bari:1471 _Z3fooi:631 -5.1: 1075 -5: 1075 -7: 534 -4.2: 534 + 4: 534 + 6: 2080 + 9: 2064 _Z3bari:1471 _Z3fooi:631 + 5.1: 1075 + 5: 1075 + 7: 534 + 4.2: 534 Index: test/Transforms/SampleProfile/Inputs/inline.prof =================================================================== --- /dev/null +++ test/Transforms/SampleProfile/Inputs/inline.prof @@ -0,0 +1,7 @@ +main:225715:0 + 2.1: 5553 + 3: 5391 + 3.1: _Z3sumii:5860 + 0: 5279 + 1: 5279 + 2: 5279 Index: test/Transforms/SampleProfile/Inputs/propagate.prof =================================================================== --- test/Transforms/SampleProfile/Inputs/propagate.prof +++ test/Transforms/SampleProfile/Inputs/propagate.prof @@ -1,17 +1,17 @@ _Z3fooiil:58139:0 -0: 0 -1: 0 -2: 0 -4: 1 -5: 10 -6: 0 -7: 5 -8: 3 -9: 0 -10: 0 -11: 6339 -12: 16191 -13: 8141 -16: 1 -18: 0 -19: 0 + 0: 0 + 1: 0 + 2: 0 + 4: 1 + 5: 10 + 6: 0 + 7: 5 + 8: 3 + 9: 0 + 10: 0 + 11: 6339 + 12: 16191 + 13: 8141 + 16: 1 + 18: 0 + 19: 0 Index: test/Transforms/SampleProfile/Inputs/syntax.prof =================================================================== --- test/Transforms/SampleProfile/Inputs/syntax.prof +++ test/Transforms/SampleProfile/Inputs/syntax.prof @@ -1,3 +1,3 @@ empty:100:0 -0: 0 -1: 100 + 0: 0 + 1: 100 Index: test/Transforms/SampleProfile/inline.ll =================================================================== --- /dev/null +++ test/Transforms/SampleProfile/inline.ll @@ -0,0 +1,108 @@ +; RUN: opt < %s -sample-profile -sample-profile-file=%S/Inputs/inline.prof -S | FileCheck %s + +; Original C++ test case +; +; #include +; +; int sum(int x, int y) { +; return x + y; +; } +; +; int main() { +; int s, i = 0; +; while (i++ < 20000 * 20000) +; if (i != 100) s = sum(i, s); else s = 30; +; printf("sum is %d\n", s); +; return 0; +; } +; +@.str = private unnamed_addr constant [11 x i8] c"sum is %d\0A\00", align 1 + +; Function Attrs: nounwind uwtable +define i32 @_Z3sumii(i32 %x, i32 %y) { +entry: + %x.addr = alloca i32, align 4 + %y.addr = alloca i32, align 4 + store i32 %x, i32* %x.addr, align 4 + store i32 %y, i32* %y.addr, align 4 + %0 = load i32, i32* %x.addr, align 4, !dbg !11 + %1 = load i32, i32* %y.addr, align 4, !dbg !11 + %add = add nsw i32 %0, %1, !dbg !11 + ret i32 %add, !dbg !11 +} + +; Function Attrs: uwtable +define i32 @main() { +entry: + %retval = alloca i32, align 4 + %s = alloca i32, align 4 + %i = alloca i32, align 4 + store i32 0, i32* %retval + store i32 0, i32* %i, align 4, !dbg !12 + br label %while.cond, !dbg !13 + +while.cond: ; preds = %if.end, %entry + %0 = load i32, i32* %i, align 4, !dbg !14 + %inc = add nsw i32 %0, 1, !dbg !14 + store i32 %inc, i32* %i, align 4, !dbg !14 + %cmp = icmp slt i32 %0, 400000000, !dbg !14 + br i1 %cmp, label %while.body, label %while.end, !dbg !14 + +while.body: ; preds = %while.cond + %1 = load i32, i32* %i, align 4, !dbg !16 + %cmp1 = icmp ne i32 %1, 100, !dbg !16 + br i1 %cmp1, label %if.then, label %if.else, !dbg !16 + + +if.then: ; preds = %while.body + %2 = load i32, i32* %i, align 4, !dbg !18 + %3 = load i32, i32* %s, align 4, !dbg !18 + %call = call i32 @_Z3sumii(i32 %2, i32 %3), !dbg !18 +; CHECK-NOT: call i32 @_Z3sumii + store i32 %call, i32* %s, align 4, !dbg !18 + br label %if.end, !dbg !18 + +if.else: ; preds = %while.body + store i32 30, i32* %s, align 4, !dbg !20 + br label %if.end + +if.end: ; preds = %if.else, %if.then + br label %while.cond, !dbg !22 + +while.end: ; preds = %while.cond + %4 = load i32, i32* %s, align 4, !dbg !24 + %call2 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([11 x i8], [11 x i8]* @.str, i32 0, i32 0), i32 %4), !dbg !24 + ret i32 0, !dbg !25 +} + +declare i32 @printf(i8*, ...) #2 + +!llvm.module.flags = !{!8, !9} +!llvm.ident = !{!10} + +!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, producer: "clang version 3.5 ", isOptimized: false, emissionKind: 0, file: !1, enums: !2, retainedTypes: !2, subprograms: !3, globals: !2, imports: !2) +!1 = !DIFile(filename: "calls.cc", directory: ".") +!2 = !{} +!3 = !{!4, !7} +!4 = distinct !DISubprogram(name: "sum", line: 3, isLocal: false, isDefinition: true, virtualIndex: 6, flags: DIFlagPrototyped, isOptimized: false, scopeLine: 3, file: !1, scope: !5, type: !6, function: i32 (i32, i32)* @_Z3sumii, variables: !2) +!5 = !DIFile(filename: "calls.cc", directory: ".") +!6 = !DISubroutineType(types: !2) +!7 = distinct !DISubprogram(name: "main", line: 7, isLocal: false, isDefinition: true, virtualIndex: 6, flags: DIFlagPrototyped, isOptimized: false, scopeLine: 7, file: !1, scope: !5, type: !6, function: i32 ()* @main, variables: !2) +!8 = !{i32 2, !"Dwarf Version", i32 4} +!9 = !{i32 1, !"Debug Info Version", i32 3} +!10 = !{!"clang version 3.5 "} +!11 = !DILocation(line: 4, scope: !4) +!12 = !DILocation(line: 8, scope: !7) +!13 = !DILocation(line: 9, scope: !7) +!14 = !DILocation(line: 9, scope: !15) +!15 = !DILexicalBlockFile(discriminator: 1, file: !1, scope: !7) +!16 = !DILocation(line: 10, scope: !17) +!17 = distinct !DILexicalBlock(line: 10, column: 0, file: !1, scope: !7) +!18 = !DILocation(line: 10, scope: !19) +!19 = !DILexicalBlockFile(discriminator: 1, file: !1, scope: !17) +!20 = !DILocation(line: 10, scope: !21) +!21 = !DILexicalBlockFile(discriminator: 2, file: !1, scope: !17) +!22 = !DILocation(line: 10, scope: !23) +!23 = !DILexicalBlockFile(discriminator: 3, file: !1, scope: !17) +!24 = !DILocation(line: 11, scope: !7) +!25 = !DILocation(line: 12, scope: !7) Index: test/Transforms/SampleProfile/syntax.ll =================================================================== --- test/Transforms/SampleProfile/syntax.ll +++ test/Transforms/SampleProfile/syntax.ll @@ -5,7 +5,7 @@ ; RUN: not opt < %s -sample-profile -sample-profile-file=%S/Inputs/bad_line_values.prof 2>&1 | FileCheck -check-prefix=BAD-LINE-VALUES %s ; RUN: not opt < %s -sample-profile -sample-profile-file=%S/Inputs/bad_discriminator_value.prof 2>&1 | FileCheck -check-prefix=BAD-DISCRIMINATOR-VALUE %s ; RUN: not opt < %s -sample-profile -sample-profile-file=%S/Inputs/bad_samples.prof 2>&1 | FileCheck -check-prefix=BAD-SAMPLES %s -; RUN: opt < %s -sample-profile -sample-profile-file=%S/Inputs/bad_mangle.prof 2>&1 >/dev/null +; RUN: not opt < %s -sample-profile -sample-profile-file=%S/Inputs/bad_mangle.prof 2>&1 | FileCheck -check-prefix=BAD-MANGLE %s define void @empty() { entry: @@ -18,3 +18,4 @@ ; BAD-LINE-VALUES: error: {{.*}}bad_line_values.prof:2: Expected 'mangled_name:NUM:NUM', found -1: 10 ; BAD-DISCRIMINATOR-VALUE: error: {{.*}}bad_discriminator_value.prof:2: Expected 'NUM[.NUM]: NUM[ mangled_name:NUM]*', found 1.-3: 10 ; BAD-SAMPLES: error: {{.*}}bad_samples.prof:2: Expected 'NUM[.NUM]: NUM[ mangled_name:NUM]*', found 1.3: -10 +; BAD-MANGLE: error: {{.*}}bad_mangle.prof:1: Expected 'mangled_name:NUM:NUM', found double convert(float):2909472:181842 Index: test/tools/llvm-profdata/Inputs/sample-profile.proftext =================================================================== --- test/tools/llvm-profdata/Inputs/sample-profile.proftext +++ test/tools/llvm-profdata/Inputs/sample-profile.proftext @@ -1,12 +1,12 @@ _Z3bari:20301:1437 -1: 1437 + 1: 1437 _Z3fooi:7711:610 -1: 610 + 1: 610 main:184019:0 -4: 534 -4.2: 534 -5: 1075 -5.1: 1075 -6: 2080 -7: 534 -9: 2064 _Z3bari:1471 _Z3fooi:631 + 4: 534 + 4.2: 534 + 5: 1075 + 5.1: 1075 + 6: 2080 + 7: 534 + 9: 2064 _Z3bari:1471 _Z3fooi:631