[llvm] r245940 - Convert SampleProfile pass into a Module pass.
Diego Novillo via llvm-commits
llvm-commits at lists.llvm.org
Wed Aug 26 07:02:17 PDT 2015
Thanks, Alexey. I'll take a look.
On Tue, Aug 25, 2015 at 7:44 PM, Alexey Samsonov <vonosmas at gmail.com> wrote:
> ASan complaints about the memory leaks, which are likely related to this
> change:
> http://lab.llvm.org:8011/builders/sanitizer-x86_64-linux-bootstrap/builds/8251/steps/check-llvm%20asan/logs/stdio
>
> On Tue, Aug 25, 2015 at 8:25 AM, Diego Novillo via llvm-commits <
> llvm-commits at lists.llvm.org> wrote:
>
>> Author: dnovillo
>> Date: Tue Aug 25 10:25:11 2015
>> New Revision: 245940
>>
>> URL: http://llvm.org/viewvc/llvm-project?rev=245940&view=rev
>> Log:
>> Convert SampleProfile pass into a Module pass.
>>
>> Eventually, we will need sample profiles to be incorporated into the
>> inliner's cost models. To do this, we need the sample profile pass to
>> be a module pass.
>>
>> This patch makes no functional changes beyond the mechanical adjustments
>> needed to run SampleProfile as a module pass.
>>
>> Added:
>> llvm/trunk/lib/Transforms/IPO/SampleProfile.cpp
>> - copied, changed from r245925,
>> llvm/trunk/lib/Transforms/Scalar/SampleProfile.cpp
>> Removed:
>> llvm/trunk/lib/Transforms/Scalar/SampleProfile.cpp
>> Modified:
>> llvm/trunk/include/llvm/Transforms/IPO.h
>> llvm/trunk/include/llvm/Transforms/Scalar.h
>> llvm/trunk/lib/Transforms/IPO/CMakeLists.txt
>> llvm/trunk/lib/Transforms/IPO/IPO.cpp
>> llvm/trunk/lib/Transforms/Scalar/CMakeLists.txt
>> llvm/trunk/lib/Transforms/Scalar/Scalar.cpp
>>
>> Modified: llvm/trunk/include/llvm/Transforms/IPO.h
>> URL:
>> http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/Transforms/IPO.h?rev=245940&r1=245939&r2=245940&view=diff
>>
>> ==============================================================================
>> --- llvm/trunk/include/llvm/Transforms/IPO.h (original)
>> +++ llvm/trunk/include/llvm/Transforms/IPO.h Tue Aug 25 10:25:11 2015
>> @@ -16,6 +16,7 @@
>> #define LLVM_TRANSFORMS_IPO_H
>>
>> #include "llvm/ADT/ArrayRef.h"
>> +#include "llvm/ADT/StringRef.h"
>>
>> namespace llvm {
>>
>> @@ -209,6 +210,12 @@ ModulePass *createBarrierNoopPass();
>> /// to bitsets.
>> ModulePass *createLowerBitSetsPass();
>>
>>
>> +//===----------------------------------------------------------------------===//
>> +// SampleProfilePass - Loads sample profile data from disk and generates
>> +// IR metadata to reflect the profile.
>> +ModulePass *createSampleProfileLoaderPass();
>> +ModulePass *createSampleProfileLoaderPass(StringRef Name);
>> +
>> } // End llvm namespace
>>
>> #endif
>>
>> Modified: llvm/trunk/include/llvm/Transforms/Scalar.h
>> URL:
>> http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/Transforms/Scalar.h?rev=245940&r1=245939&r2=245940&view=diff
>>
>> ==============================================================================
>> --- llvm/trunk/include/llvm/Transforms/Scalar.h (original)
>> +++ llvm/trunk/include/llvm/Transforms/Scalar.h Tue Aug 25 10:25:11 2015
>> @@ -408,13 +408,6 @@ FunctionPass *createPartiallyInlineLibCa
>>
>>
>> //===----------------------------------------------------------------------===//
>> //
>> -// SampleProfilePass - Loads sample profile data from disk and generates
>> -// IR metadata to reflect the profile.
>> -FunctionPass *createSampleProfileLoaderPass();
>> -FunctionPass *createSampleProfileLoaderPass(StringRef Name);
>> -
>>
>> -//===----------------------------------------------------------------------===//
>> -//
>> // ScalarizerPass - Converts vector operations into scalar operations
>> //
>> FunctionPass *createScalarizerPass();
>>
>> Modified: llvm/trunk/lib/Transforms/IPO/CMakeLists.txt
>> URL:
>> http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Transforms/IPO/CMakeLists.txt?rev=245940&r1=245939&r2=245940&view=diff
>>
>> ==============================================================================
>> --- llvm/trunk/lib/Transforms/IPO/CMakeLists.txt (original)
>> +++ llvm/trunk/lib/Transforms/IPO/CMakeLists.txt Tue Aug 25 10:25:11 2015
>> @@ -20,6 +20,7 @@ add_llvm_library(LLVMipo
>> PartialInlining.cpp
>> PassManagerBuilder.cpp
>> PruneEH.cpp
>> + SampleProfile.cpp
>> StripDeadPrototypes.cpp
>> StripSymbols.cpp
>>
>>
>> Modified: llvm/trunk/lib/Transforms/IPO/IPO.cpp
>> URL:
>> http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Transforms/IPO/IPO.cpp?rev=245940&r1=245939&r2=245940&view=diff
>>
>> ==============================================================================
>> --- llvm/trunk/lib/Transforms/IPO/IPO.cpp (original)
>> +++ llvm/trunk/lib/Transforms/IPO/IPO.cpp Tue Aug 25 10:25:11 2015
>> @@ -47,6 +47,7 @@ void llvm::initializeIPO(PassRegistry &R
>> initializeStripNonDebugSymbolsPass(Registry);
>> initializeBarrierNoopPass(Registry);
>> initializeEliminateAvailableExternallyPass(Registry);
>> + initializeSampleProfileLoaderPass(Registry);
>> }
>>
>> void LLVMInitializeIPO(LLVMPassRegistryRef R) {
>>
>> Copied: llvm/trunk/lib/Transforms/IPO/SampleProfile.cpp (from r245925,
>> llvm/trunk/lib/Transforms/Scalar/SampleProfile.cpp)
>> URL:
>> http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Transforms/IPO/SampleProfile.cpp?p2=llvm/trunk/lib/Transforms/IPO/SampleProfile.cpp&p1=llvm/trunk/lib/Transforms/Scalar/SampleProfile.cpp&r1=245925&r2=245940&rev=245940&view=diff
>>
>> ==============================================================================
>> --- llvm/trunk/lib/Transforms/Scalar/SampleProfile.cpp (original)
>> +++ llvm/trunk/lib/Transforms/IPO/SampleProfile.cpp Tue Aug 25 10:25:11
>> 2015
>> @@ -22,7 +22,6 @@
>> //
>>
>> //===----------------------------------------------------------------------===//
>>
>> -#include "llvm/Transforms/Scalar.h"
>> #include "llvm/ADT/DenseMap.h"
>> #include "llvm/ADT/SmallPtrSet.h"
>> #include "llvm/ADT/SmallSet.h"
>> @@ -45,6 +44,7 @@
>> #include "llvm/Support/CommandLine.h"
>> #include "llvm/Support/Debug.h"
>> #include "llvm/Support/raw_ostream.h"
>> +#include "llvm/Transforms/IPO.h"
>> #include <cctype>
>>
>> using namespace llvm;
>> @@ -74,14 +74,15 @@ typedef DenseMap<BasicBlock *, SmallVect
>> /// This pass reads profile data from the file specified by
>> /// -sample-profile-file and annotates every affected function with the
>> /// profile information found in that file.
>> -class SampleProfileLoader : public FunctionPass {
>> +class SampleProfileLoader : public ModulePass {
>> public:
>> // Class identification, replacement for typeinfo
>> static char ID;
>>
>> SampleProfileLoader(StringRef Name = SampleProfileFile)
>> - : FunctionPass(ID), DT(nullptr), PDT(nullptr), LI(nullptr),
>> Ctx(nullptr),
>> - Reader(), Samples(nullptr), Filename(Name),
>> ProfileIsValid(false) {
>> + : ModulePass(ID), DT(nullptr), PDT(nullptr), LI(nullptr),
>> + Ctx(nullptr), Reader(), Samples(nullptr), Filename(Name),
>> + ProfileIsValid(false) {
>> initializeSampleProfileLoaderPass(*PassRegistry::getPassRegistry());
>> }
>>
>> @@ -91,16 +92,23 @@ public:
>>
>> const char *getPassName() const override { return "Sample profile
>> pass"; }
>>
>> - bool runOnFunction(Function &F) override;
>> + bool runOnModule(Module &M) override;
>>
>> void getAnalysisUsage(AnalysisUsage &AU) const override {
>> AU.setPreservesCFG();
>> +
>> AU.addRequired<LoopInfoWrapperPass>();
>> + AU.addPreserved<LoopInfoWrapperPass>();
>> +
>> AU.addRequired<DominatorTreeWrapperPass>();
>> + AU.addPreserved<DominatorTreeWrapperPass>();
>> +
>> AU.addRequired<PostDominatorTree>();
>> + AU.addPreserved<PostDominatorTree>();
>> }
>>
>> protected:
>> + bool runOnFunction(Function &F);
>> unsigned getFunctionLoc(Function &F);
>> bool emitAnnotations(Function &F);
>> unsigned getInstWeight(Instruction &I);
>> @@ -743,10 +751,11 @@ INITIALIZE_PASS_END(SampleProfileLoader,
>> "Sample Profile loader", false, false)
>>
>> bool SampleProfileLoader::doInitialization(Module &M) {
>> - auto ReaderOrErr = SampleProfileReader::create(Filename,
>> M.getContext());
>> + auto& Ctx = M.getContext();
>> + auto ReaderOrErr = SampleProfileReader::create(Filename, Ctx);
>> if (std::error_code EC = ReaderOrErr.getError()) {
>> std::string Msg = "Could not open profile: " + EC.message();
>> - M.getContext().diagnose(DiagnosticInfoSampleProfile(Filename.data(),
>> Msg));
>> + Ctx.diagnose(DiagnosticInfoSampleProfile(Filename.data(), Msg));
>> return false;
>> }
>> Reader = std::move(ReaderOrErr.get());
>> @@ -754,21 +763,29 @@ bool SampleProfileLoader::doInitializati
>> return true;
>> }
>>
>> -FunctionPass *llvm::createSampleProfileLoaderPass() {
>> +ModulePass *llvm::createSampleProfileLoaderPass() {
>> return new SampleProfileLoader(SampleProfileFile);
>> }
>>
>> -FunctionPass *llvm::createSampleProfileLoaderPass(StringRef Name) {
>> +ModulePass *llvm::createSampleProfileLoaderPass(StringRef Name) {
>> return new SampleProfileLoader(Name);
>> }
>>
>> +bool SampleProfileLoader::runOnModule(Module &M) {
>> + bool retval = false;
>> + for (auto &F : M)
>> + if (!F.isDeclaration())
>> + retval |= runOnFunction(F);
>> + return retval;
>> +}
>> +
>> bool SampleProfileLoader::runOnFunction(Function &F) {
>> if (!ProfileIsValid)
>> return false;
>>
>> - DT = &getAnalysis<DominatorTreeWrapperPass>().getDomTree();
>> - PDT = &getAnalysis<PostDominatorTree>();
>> - LI = &getAnalysis<LoopInfoWrapperPass>().getLoopInfo();
>> + DT = &getAnalysis<DominatorTreeWrapperPass>(F).getDomTree();
>> + PDT = &getAnalysis<PostDominatorTree>(F);
>> + LI = &getAnalysis<LoopInfoWrapperPass>(F).getLoopInfo();
>> Ctx = &F.getParent()->getContext();
>> Samples = Reader->getSamplesFor(F);
>> if (!Samples->empty())
>>
>> Modified: llvm/trunk/lib/Transforms/Scalar/CMakeLists.txt
>> URL:
>> http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Transforms/Scalar/CMakeLists.txt?rev=245940&r1=245939&r2=245940&view=diff
>>
>> ==============================================================================
>> --- llvm/trunk/lib/Transforms/Scalar/CMakeLists.txt (original)
>> +++ llvm/trunk/lib/Transforms/Scalar/CMakeLists.txt Tue Aug 25 10:25:11
>> 2015
>> @@ -38,7 +38,6 @@ add_llvm_library(LLVMScalarOpts
>> RewriteStatepointsForGC.cpp
>> SCCP.cpp
>> SROA.cpp
>> - SampleProfile.cpp
>> Scalar.cpp
>> ScalarReplAggregates.cpp
>> Scalarizer.cpp
>>
>> Removed: llvm/trunk/lib/Transforms/Scalar/SampleProfile.cpp
>> URL:
>> http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Transforms/Scalar/SampleProfile.cpp?rev=245939&view=auto
>>
>> ==============================================================================
>> --- llvm/trunk/lib/Transforms/Scalar/SampleProfile.cpp (original)
>> +++ llvm/trunk/lib/Transforms/Scalar/SampleProfile.cpp (removed)
>> @@ -1,777 +0,0 @@
>> -//===- SampleProfile.cpp - Incorporate sample profiles into the IR
>> --------===//
>> -//
>> -// The LLVM Compiler Infrastructure
>> -//
>> -// This file is distributed under the University of Illinois Open Source
>> -// License. See LICENSE.TXT for details.
>> -//
>>
>> -//===----------------------------------------------------------------------===//
>> -//
>> -// This file implements the SampleProfileLoader transformation. This pass
>> -// reads a profile file generated by a sampling profiler (e.g. Linux
>> Perf -
>> -// http://perf.wiki.kernel.org/) and generates IR metadata to reflect
>> the
>> -// profile information in the given profile.
>> -//
>> -// This pass generates branch weight annotations on the IR:
>> -//
>> -// - prof: Represents branch weights. This annotation is added to
>> branches
>> -// to indicate the weights of each edge coming out of the branch.
>> -// The weight of each edge is the weight of the target block for
>> -// that edge. The weight of a block B is computed as the maximum
>> -// number of samples found in B.
>> -//
>>
>> -//===----------------------------------------------------------------------===//
>> -
>> -#include "llvm/Transforms/Scalar.h"
>> -#include "llvm/ADT/DenseMap.h"
>> -#include "llvm/ADT/SmallPtrSet.h"
>> -#include "llvm/ADT/SmallSet.h"
>> -#include "llvm/ADT/StringRef.h"
>> -#include "llvm/Analysis/LoopInfo.h"
>> -#include "llvm/Analysis/PostDominators.h"
>> -#include "llvm/IR/Constants.h"
>> -#include "llvm/IR/DebugInfo.h"
>> -#include "llvm/IR/DiagnosticInfo.h"
>> -#include "llvm/IR/Dominators.h"
>> -#include "llvm/IR/Function.h"
>> -#include "llvm/IR/InstIterator.h"
>> -#include "llvm/IR/Instructions.h"
>> -#include "llvm/IR/LLVMContext.h"
>> -#include "llvm/IR/MDBuilder.h"
>> -#include "llvm/IR/Metadata.h"
>> -#include "llvm/IR/Module.h"
>> -#include "llvm/Pass.h"
>> -#include "llvm/ProfileData/SampleProfReader.h"
>> -#include "llvm/Support/CommandLine.h"
>> -#include "llvm/Support/Debug.h"
>> -#include "llvm/Support/raw_ostream.h"
>> -#include <cctype>
>> -
>> -using namespace llvm;
>> -using namespace sampleprof;
>> -
>> -#define DEBUG_TYPE "sample-profile"
>> -
>> -// Command line option to specify the file to read samples from. This is
>> -// mainly used for debugging.
>> -static cl::opt<std::string> SampleProfileFile(
>> - "sample-profile-file", cl::init(""), cl::value_desc("filename"),
>> - cl::desc("Profile file loaded by -sample-profile"), cl::Hidden);
>> -static cl::opt<unsigned> SampleProfileMaxPropagateIterations(
>> - "sample-profile-max-propagate-iterations", cl::init(100),
>> - cl::desc("Maximum number of iterations to go through when
>> propagating "
>> - "sample block/edge weights through the CFG."));
>> -
>> -namespace {
>> -typedef DenseMap<BasicBlock *, unsigned> BlockWeightMap;
>> -typedef DenseMap<BasicBlock *, BasicBlock *> EquivalenceClassMap;
>> -typedef std::pair<BasicBlock *, BasicBlock *> Edge;
>> -typedef DenseMap<Edge, unsigned> EdgeWeightMap;
>> -typedef DenseMap<BasicBlock *, SmallVector<BasicBlock *, 8>>
>> BlockEdgeMap;
>> -
>> -/// \brief Sample profile pass.
>> -///
>> -/// This pass reads profile data from the file specified by
>> -/// -sample-profile-file and annotates every affected function with the
>> -/// profile information found in that file.
>> -class SampleProfileLoader : public FunctionPass {
>> -public:
>> - // Class identification, replacement for typeinfo
>> - static char ID;
>> -
>> - SampleProfileLoader(StringRef Name = SampleProfileFile)
>> - : FunctionPass(ID), DT(nullptr), PDT(nullptr), LI(nullptr),
>> Ctx(nullptr),
>> - Reader(), Samples(nullptr), Filename(Name),
>> ProfileIsValid(false) {
>> - initializeSampleProfileLoaderPass(*PassRegistry::getPassRegistry());
>> - }
>> -
>> - bool doInitialization(Module &M) override;
>> -
>> - void dump() { Reader->dump(); }
>> -
>> - const char *getPassName() const override { return "Sample profile
>> pass"; }
>> -
>> - bool runOnFunction(Function &F) override;
>> -
>> - void getAnalysisUsage(AnalysisUsage &AU) const override {
>> - AU.setPreservesCFG();
>> - AU.addRequired<LoopInfoWrapperPass>();
>> - AU.addRequired<DominatorTreeWrapperPass>();
>> - AU.addRequired<PostDominatorTree>();
>> - }
>> -
>> -protected:
>> - unsigned getFunctionLoc(Function &F);
>> - bool emitAnnotations(Function &F);
>> - unsigned getInstWeight(Instruction &I);
>> - unsigned getBlockWeight(BasicBlock *BB);
>> - void printEdgeWeight(raw_ostream &OS, Edge E);
>> - void printBlockWeight(raw_ostream &OS, BasicBlock *BB);
>> - void printBlockEquivalence(raw_ostream &OS, BasicBlock *BB);
>> - bool computeBlockWeights(Function &F);
>> - void findEquivalenceClasses(Function &F);
>> - void findEquivalencesFor(BasicBlock *BB1,
>> - SmallVector<BasicBlock *, 8> Descendants,
>> - DominatorTreeBase<BasicBlock> *DomTree);
>> - void propagateWeights(Function &F);
>> - unsigned visitEdge(Edge E, unsigned *NumUnknownEdges, Edge
>> *UnknownEdge);
>> - void buildEdges(Function &F);
>> - bool propagateThroughEdges(Function &F);
>> -
>> - /// \brief Line number for the function header. Used to compute
>> absolute
>> - /// line numbers from the relative line numbers found in the profile.
>> - unsigned HeaderLineno;
>> -
>> - /// \brief Map basic blocks to their computed weights.
>> - ///
>> - /// The weight of a basic block is defined to be the maximum
>> - /// of all the instruction weights in that block.
>> - BlockWeightMap BlockWeights;
>> -
>> - /// \brief Map edges to their computed weights.
>> - ///
>> - /// Edge weights are computed by propagating basic block weights in
>> - /// SampleProfile::propagateWeights.
>> - EdgeWeightMap EdgeWeights;
>> -
>> - /// \brief Set of visited blocks during propagation.
>> - SmallPtrSet<BasicBlock *, 128> VisitedBlocks;
>> -
>> - /// \brief Set of visited edges during propagation.
>> - SmallSet<Edge, 128> VisitedEdges;
>> -
>> - /// \brief Equivalence classes for block weights.
>> - ///
>> - /// Two blocks BB1 and BB2 are in the same equivalence class if they
>> - /// dominate and post-dominate each other, and they are in the same
>> loop
>> - /// nest. When this happens, the two blocks are guaranteed to execute
>> - /// the same number of times.
>> - EquivalenceClassMap EquivalenceClass;
>> -
>> - /// \brief Dominance, post-dominance and loop information.
>> - DominatorTree *DT;
>> - PostDominatorTree *PDT;
>> - LoopInfo *LI;
>> -
>> - /// \brief Predecessors for each basic block in the CFG.
>> - BlockEdgeMap Predecessors;
>> -
>> - /// \brief Successors for each basic block in the CFG.
>> - BlockEdgeMap Successors;
>> -
>> - /// \brief LLVM context holding the debug data we need.
>> - LLVMContext *Ctx;
>> -
>> - /// \brief Profile reader object.
>> - std::unique_ptr<SampleProfileReader> Reader;
>> -
>> - /// \brief Samples collected for the body of this function.
>> - FunctionSamples *Samples;
>> -
>> - /// \brief Name of the profile file to load.
>> - StringRef Filename;
>> -
>> - /// \brief Flag indicating whether the profile input loaded
>> successfully.
>> - bool ProfileIsValid;
>> -};
>> -}
>> -
>> -/// \brief Print the weight of edge \p E on stream \p OS.
>> -///
>> -/// \param OS Stream to emit the output to.
>> -/// \param E Edge to print.
>> -void SampleProfileLoader::printEdgeWeight(raw_ostream &OS, Edge E) {
>> - OS << "weight[" << E.first->getName() << "->" << E.second->getName()
>> - << "]: " << EdgeWeights[E] << "\n";
>> -}
>> -
>> -/// \brief Print the equivalence class of block \p BB on stream \p OS.
>> -///
>> -/// \param OS Stream to emit the output to.
>> -/// \param BB Block to print.
>> -void SampleProfileLoader::printBlockEquivalence(raw_ostream &OS,
>> - BasicBlock *BB) {
>> - BasicBlock *Equiv = EquivalenceClass[BB];
>> - OS << "equivalence[" << BB->getName()
>> - << "]: " << ((Equiv) ? EquivalenceClass[BB]->getName() : "NONE") <<
>> "\n";
>> -}
>> -
>> -/// \brief Print the weight of block \p BB on stream \p OS.
>> -///
>> -/// \param OS Stream to emit the output to.
>> -/// \param BB Block to print.
>> -void SampleProfileLoader::printBlockWeight(raw_ostream &OS, BasicBlock
>> *BB) {
>> - OS << "weight[" << BB->getName() << "]: " << BlockWeights[BB] << "\n";
>> -}
>> -
>> -/// \brief Get the weight for an instruction.
>> -///
>> -/// The "weight" of an instruction \p Inst is the number of samples
>> -/// collected on that instruction at runtime. To retrieve it, we
>> -/// need to compute the line number of \p Inst relative to the start of
>> its
>> -/// function. We use HeaderLineno to compute the offset. We then
>> -/// look up the samples collected for \p Inst using BodySamples.
>> -///
>> -/// \param Inst Instruction to query.
>> -///
>> -/// \returns The profiled weight of I.
>> -unsigned SampleProfileLoader::getInstWeight(Instruction &Inst) {
>> - DebugLoc DLoc = Inst.getDebugLoc();
>> - if (!DLoc)
>> - return 0;
>> -
>> - unsigned Lineno = DLoc.getLine();
>> - if (Lineno < HeaderLineno)
>> - return 0;
>> -
>> - const DILocation *DIL = DLoc;
>> - int LOffset = Lineno - HeaderLineno;
>> - unsigned Discriminator = DIL->getDiscriminator();
>> - unsigned Weight = Samples->samplesAt(LOffset, Discriminator);
>> - DEBUG(dbgs() << " " << Lineno << "." << Discriminator << ":" << Inst
>> - << " (line offset: " << LOffset << "." << Discriminator
>> - << " - weight: " << Weight << ")\n");
>> - return Weight;
>> -}
>> -
>> -/// \brief Compute the weight of a basic block.
>> -///
>> -/// The weight of basic block \p BB is the maximum weight of all the
>> -/// instructions in BB. The weight of \p BB is computed and cached in
>> -/// the BlockWeights map.
>> -///
>> -/// \param BB The basic block to query.
>> -///
>> -/// \returns The computed weight of BB.
>> -unsigned SampleProfileLoader::getBlockWeight(BasicBlock *BB) {
>> - // If we've computed BB's weight before, return it.
>> - std::pair<BlockWeightMap::iterator, bool> Entry =
>> - BlockWeights.insert(std::make_pair(BB, 0));
>> - if (!Entry.second)
>> - return Entry.first->second;
>> -
>> - // Otherwise, compute and cache BB's weight.
>> - unsigned Weight = 0;
>> - for (auto &I : BB->getInstList()) {
>> - unsigned InstWeight = getInstWeight(I);
>> - if (InstWeight > Weight)
>> - Weight = InstWeight;
>> - }
>> - Entry.first->second = Weight;
>> - return Weight;
>> -}
>> -
>> -/// \brief Compute and store the weights of every basic block.
>> -///
>> -/// This populates the BlockWeights map by computing
>> -/// the weights of every basic block in the CFG.
>> -///
>> -/// \param F The function to query.
>> -bool SampleProfileLoader::computeBlockWeights(Function &F) {
>> - bool Changed = false;
>> - DEBUG(dbgs() << "Block weights\n");
>> - for (auto &BB : F) {
>> - unsigned Weight = getBlockWeight(&BB);
>> - Changed |= (Weight > 0);
>> - DEBUG(printBlockWeight(dbgs(), &BB));
>> - }
>> -
>> - return Changed;
>> -}
>> -
>> -/// \brief Find equivalence classes for the given block.
>> -///
>> -/// This finds all the blocks that are guaranteed to execute the same
>> -/// number of times as \p BB1. To do this, it traverses all the
>> -/// descendants of \p BB1 in the dominator or post-dominator tree.
>> -///
>> -/// A block BB2 will be in the same equivalence class as \p BB1 if
>> -/// the following holds:
>> -///
>> -/// 1- \p BB1 is a descendant of BB2 in the opposite tree. So, if BB2
>> -/// is a descendant of \p BB1 in the dominator tree, then BB2 should
>> -/// dominate BB1 in the post-dominator tree.
>> -///
>> -/// 2- Both BB2 and \p BB1 must be in the same loop.
>> -///
>> -/// For every block BB2 that meets those two requirements, we set BB2's
>> -/// equivalence class to \p BB1.
>> -///
>> -/// \param BB1 Block to check.
>> -/// \param Descendants Descendants of \p BB1 in either the dom or pdom
>> tree.
>> -/// \param DomTree Opposite dominator tree. If \p Descendants is filled
>> -/// with blocks from \p BB1's dominator tree, then
>> -/// this is the post-dominator tree, and vice versa.
>> -void SampleProfileLoader::findEquivalencesFor(
>> - BasicBlock *BB1, SmallVector<BasicBlock *, 8> Descendants,
>> - DominatorTreeBase<BasicBlock> *DomTree) {
>> - for (auto *BB2 : Descendants) {
>> - bool IsDomParent = DomTree->dominates(BB2, BB1);
>> - bool IsInSameLoop = LI->getLoopFor(BB1) == LI->getLoopFor(BB2);
>> - if (BB1 != BB2 && VisitedBlocks.insert(BB2).second && IsDomParent &&
>> - IsInSameLoop) {
>> - EquivalenceClass[BB2] = BB1;
>> -
>> - // If BB2 is heavier than BB1, make BB2 have the same weight
>> - // as BB1.
>> - //
>> - // Note that we don't worry about the opposite situation here
>> - // (when BB2 is lighter than BB1). We will deal with this
>> - // during the propagation phase. Right now, we just want to
>> - // make sure that BB1 has the largest weight of all the
>> - // members of its equivalence set.
>> - unsigned &BB1Weight = BlockWeights[BB1];
>> - unsigned &BB2Weight = BlockWeights[BB2];
>> - BB1Weight = std::max(BB1Weight, BB2Weight);
>> - }
>> - }
>> -}
>> -
>> -/// \brief Find equivalence classes.
>> -///
>> -/// Since samples may be missing from blocks, we can fill in the gaps by
>> setting
>> -/// the weights of all the blocks in the same equivalence class to the
>> same
>> -/// weight. To compute the concept of equivalence, we use dominance and
>> loop
>> -/// information. Two blocks B1 and B2 are in the same equivalence class
>> if B1
>> -/// dominates B2, B2 post-dominates B1 and both are in the same loop.
>> -///
>> -/// \param F The function to query.
>> -void SampleProfileLoader::findEquivalenceClasses(Function &F) {
>> - SmallVector<BasicBlock *, 8> DominatedBBs;
>> - DEBUG(dbgs() << "\nBlock equivalence classes\n");
>> - // Find equivalence sets based on dominance and post-dominance
>> information.
>> - for (auto &BB : F) {
>> - BasicBlock *BB1 = &BB;
>> -
>> - // Compute BB1's equivalence class once.
>> - if (EquivalenceClass.count(BB1)) {
>> - DEBUG(printBlockEquivalence(dbgs(), BB1));
>> - continue;
>> - }
>> -
>> - // By default, blocks are in their own equivalence class.
>> - EquivalenceClass[BB1] = BB1;
>> -
>> - // Traverse all the blocks dominated by BB1. We are looking for
>> - // every basic block BB2 such that:
>> - //
>> - // 1- BB1 dominates BB2.
>> - // 2- BB2 post-dominates BB1.
>> - // 3- BB1 and BB2 are in the same loop nest.
>> - //
>> - // If all those conditions hold, it means that BB2 is executed
>> - // as many times as BB1, so they are placed in the same equivalence
>> - // class by making BB2's equivalence class be BB1.
>> - DominatedBBs.clear();
>> - DT->getDescendants(BB1, DominatedBBs);
>> - findEquivalencesFor(BB1, DominatedBBs, PDT->DT);
>> -
>> - // Repeat the same logic for all the blocks post-dominated by BB1.
>> - // We are looking for every basic block BB2 such that:
>> - //
>> - // 1- BB1 post-dominates BB2.
>> - // 2- BB2 dominates BB1.
>> - // 3- BB1 and BB2 are in the same loop nest.
>> - //
>> - // If all those conditions hold, BB2's equivalence class is BB1.
>> - DominatedBBs.clear();
>> - PDT->getDescendants(BB1, DominatedBBs);
>> - findEquivalencesFor(BB1, DominatedBBs, DT);
>> -
>> - DEBUG(printBlockEquivalence(dbgs(), BB1));
>> - }
>> -
>> - // Assign weights to equivalence classes.
>> - //
>> - // All the basic blocks in the same equivalence class will execute
>> - // the same number of times. Since we know that the head block in
>> - // each equivalence class has the largest weight, assign that weight
>> - // to all the blocks in that equivalence class.
>> - DEBUG(dbgs() << "\nAssign the same weight to all blocks in the same
>> class\n");
>> - for (auto &BI : F) {
>> - BasicBlock *BB = &BI;
>> - BasicBlock *EquivBB = EquivalenceClass[BB];
>> - if (BB != EquivBB)
>> - BlockWeights[BB] = BlockWeights[EquivBB];
>> - DEBUG(printBlockWeight(dbgs(), BB));
>> - }
>> -}
>> -
>> -/// \brief Visit the given edge to decide if it has a valid weight.
>> -///
>> -/// If \p E has not been visited before, we copy to \p UnknownEdge
>> -/// and increment the count of unknown edges.
>> -///
>> -/// \param E Edge to visit.
>> -/// \param NumUnknownEdges Current number of unknown edges.
>> -/// \param UnknownEdge Set if E has not been visited before.
>> -///
>> -/// \returns E's weight, if known. Otherwise, return 0.
>> -unsigned SampleProfileLoader::visitEdge(Edge E, unsigned
>> *NumUnknownEdges,
>> - Edge *UnknownEdge) {
>> - if (!VisitedEdges.count(E)) {
>> - (*NumUnknownEdges)++;
>> - *UnknownEdge = E;
>> - return 0;
>> - }
>> -
>> - return EdgeWeights[E];
>> -}
>> -
>> -/// \brief Propagate weights through incoming/outgoing edges.
>> -///
>> -/// If the weight of a basic block is known, and there is only one edge
>> -/// with an unknown weight, we can calculate the weight of that edge.
>> -///
>> -/// Similarly, if all the edges have a known count, we can calculate the
>> -/// count of the basic block, if needed.
>> -///
>> -/// \param F Function to process.
>> -///
>> -/// \returns True if new weights were assigned to edges or blocks.
>> -bool SampleProfileLoader::propagateThroughEdges(Function &F) {
>> - bool Changed = false;
>> - DEBUG(dbgs() << "\nPropagation through edges\n");
>> - for (auto &BI : F) {
>> - BasicBlock *BB = &BI;
>> -
>> - // Visit all the predecessor and successor edges to determine
>> - // which ones have a weight assigned already. Note that it doesn't
>> - // matter that we only keep track of a single unknown edge. The
>> - // only case we are interested in handling is when only a single
>> - // edge is unknown (see setEdgeOrBlockWeight).
>> - for (unsigned i = 0; i < 2; i++) {
>> - unsigned TotalWeight = 0;
>> - unsigned NumUnknownEdges = 0;
>> - Edge UnknownEdge, SelfReferentialEdge;
>> -
>> - if (i == 0) {
>> - // First, visit all predecessor edges.
>> - for (auto *Pred : Predecessors[BB]) {
>> - Edge E = std::make_pair(Pred, BB);
>> - TotalWeight += visitEdge(E, &NumUnknownEdges, &UnknownEdge);
>> - if (E.first == E.second)
>> - SelfReferentialEdge = E;
>> - }
>> - } else {
>> - // On the second round, visit all successor edges.
>> - for (auto *Succ : Successors[BB]) {
>> - Edge E = std::make_pair(BB, Succ);
>> - TotalWeight += visitEdge(E, &NumUnknownEdges, &UnknownEdge);
>> - }
>> - }
>> -
>> - // After visiting all the edges, there are three cases that we
>> - // can handle immediately:
>> - //
>> - // - All the edge weights are known (i.e., NumUnknownEdges == 0).
>> - // In this case, we simply check that the sum of all the edges
>> - // is the same as BB's weight. If not, we change BB's weight
>> - // to match. Additionally, if BB had not been visited before,
>> - // we mark it visited.
>> - //
>> - // - Only one edge is unknown and BB has already been visited.
>> - // In this case, we can compute the weight of the edge by
>> - // subtracting the total block weight from all the known
>> - // edge weights. If the edges weight more than BB, then the
>> - // edge of the last remaining edge is set to zero.
>> - //
>> - // - There exists a self-referential edge and the weight of BB is
>> - // known. In this case, this edge can be based on BB's weight.
>> - // We add up all the other known edges and set the weight on
>> - // the self-referential edge as we did in the previous case.
>> - //
>> - // In any other case, we must continue iterating. Eventually,
>> - // all edges will get a weight, or iteration will stop when
>> - // it reaches SampleProfileMaxPropagateIterations.
>> - if (NumUnknownEdges <= 1) {
>> - unsigned &BBWeight = BlockWeights[BB];
>> - if (NumUnknownEdges == 0) {
>> - // If we already know the weight of all edges, the weight of
>> the
>> - // basic block can be computed. It should be no larger than
>> the sum
>> - // of all edge weights.
>> - if (TotalWeight > BBWeight) {
>> - BBWeight = TotalWeight;
>> - Changed = true;
>> - DEBUG(dbgs() << "All edge weights for " << BB->getName()
>> - << " known. Set weight for block: ";
>> - printBlockWeight(dbgs(), BB););
>> - }
>> - if (VisitedBlocks.insert(BB).second)
>> - Changed = true;
>> - } else if (NumUnknownEdges == 1 && VisitedBlocks.count(BB)) {
>> - // If there is a single unknown edge and the block has been
>> - // visited, then we can compute E's weight.
>> - if (BBWeight >= TotalWeight)
>> - EdgeWeights[UnknownEdge] = BBWeight - TotalWeight;
>> - else
>> - EdgeWeights[UnknownEdge] = 0;
>> - VisitedEdges.insert(UnknownEdge);
>> - Changed = true;
>> - DEBUG(dbgs() << "Set weight for edge: ";
>> - printEdgeWeight(dbgs(), UnknownEdge));
>> - }
>> - } else if (SelfReferentialEdge.first && VisitedBlocks.count(BB)) {
>> - unsigned &BBWeight = BlockWeights[BB];
>> - // We have a self-referential edge and the weight of BB is known.
>> - if (BBWeight >= TotalWeight)
>> - EdgeWeights[SelfReferentialEdge] = BBWeight - TotalWeight;
>> - else
>> - EdgeWeights[SelfReferentialEdge] = 0;
>> - VisitedEdges.insert(SelfReferentialEdge);
>> - Changed = true;
>> - DEBUG(dbgs() << "Set self-referential edge weight to: ";
>> - printEdgeWeight(dbgs(), SelfReferentialEdge));
>> - }
>> - }
>> - }
>> -
>> - return Changed;
>> -}
>> -
>> -/// \brief Build in/out edge lists for each basic block in the CFG.
>> -///
>> -/// We are interested in unique edges. If a block B1 has multiple
>> -/// edges to another block B2, we only add a single B1->B2 edge.
>> -void SampleProfileLoader::buildEdges(Function &F) {
>> - for (auto &BI : F) {
>> - BasicBlock *B1 = &BI;
>> -
>> - // Add predecessors for B1.
>> - SmallPtrSet<BasicBlock *, 16> Visited;
>> - if (!Predecessors[B1].empty())
>> - llvm_unreachable("Found a stale predecessors list in a basic
>> block.");
>> - for (pred_iterator PI = pred_begin(B1), PE = pred_end(B1); PI != PE;
>> ++PI) {
>> - BasicBlock *B2 = *PI;
>> - if (Visited.insert(B2).second)
>> - Predecessors[B1].push_back(B2);
>> - }
>> -
>> - // Add successors for B1.
>> - Visited.clear();
>> - if (!Successors[B1].empty())
>> - llvm_unreachable("Found a stale successors list in a basic
>> block.");
>> - for (succ_iterator SI = succ_begin(B1), SE = succ_end(B1); SI != SE;
>> ++SI) {
>> - BasicBlock *B2 = *SI;
>> - if (Visited.insert(B2).second)
>> - Successors[B1].push_back(B2);
>> - }
>> - }
>> -}
>> -
>> -/// \brief Propagate weights into edges
>> -///
>> -/// The following rules are applied to every block BB in the CFG:
>> -///
>> -/// - If BB has a single predecessor/successor, then the weight
>> -/// of that edge is the weight of the block.
>> -///
>> -/// - If all incoming or outgoing edges are known except one, and the
>> -/// weight of the block is already known, the weight of the unknown
>> -/// edge will be the weight of the block minus the sum of all the known
>> -/// edges. If the sum of all the known edges is larger than BB's
>> weight,
>> -/// we set the unknown edge weight to zero.
>> -///
>> -/// - If there is a self-referential edge, and the weight of the block is
>> -/// known, the weight for that edge is set to the weight of the block
>> -/// minus the weight of the other incoming edges to that block (if
>> -/// known).
>> -void SampleProfileLoader::propagateWeights(Function &F) {
>> - bool Changed = true;
>> - unsigned i = 0;
>> -
>> - // Add an entry count to the function using the samples gathered
>> - // at the function entry.
>> - F.setEntryCount(Samples->getHeadSamples());
>> -
>> - // Before propagation starts, build, for each block, a list of
>> - // unique predecessors and successors. This is necessary to handle
>> - // identical edges in multiway branches. Since we visit all blocks and
>> all
>> - // edges of the CFG, it is cleaner to build these lists once at the
>> start
>> - // of the pass.
>> - buildEdges(F);
>> -
>> - // Propagate until we converge or we go past the iteration limit.
>> - while (Changed && i++ < SampleProfileMaxPropagateIterations) {
>> - Changed = propagateThroughEdges(F);
>> - }
>> -
>> - // Generate MD_prof metadata for every branch instruction using the
>> - // edge weights computed during propagation.
>> - DEBUG(dbgs() << "\nPropagation complete. Setting branch weights\n");
>> - MDBuilder MDB(F.getContext());
>> - for (auto &BI : F) {
>> - BasicBlock *BB = &BI;
>> - TerminatorInst *TI = BB->getTerminator();
>> - if (TI->getNumSuccessors() == 1)
>> - continue;
>> - if (!isa<BranchInst>(TI) && !isa<SwitchInst>(TI))
>> - continue;
>> -
>> - DEBUG(dbgs() << "\nGetting weights for branch at line "
>> - << TI->getDebugLoc().getLine() << ".\n");
>> - SmallVector<unsigned, 4> Weights;
>> - bool AllWeightsZero = true;
>> - for (unsigned I = 0; I < TI->getNumSuccessors(); ++I) {
>> - BasicBlock *Succ = TI->getSuccessor(I);
>> - Edge E = std::make_pair(BB, Succ);
>> - unsigned Weight = EdgeWeights[E];
>> - DEBUG(dbgs() << "\t"; printEdgeWeight(dbgs(), E));
>> - Weights.push_back(Weight);
>> - if (Weight != 0)
>> - AllWeightsZero = false;
>> - }
>> -
>> - // Only set weights if there is at least one non-zero weight.
>> - // In any other case, let the analyzer set weights.
>> - if (!AllWeightsZero) {
>> - DEBUG(dbgs() << "SUCCESS. Found non-zero weights.\n");
>> - TI->setMetadata(llvm::LLVMContext::MD_prof,
>> - MDB.createBranchWeights(Weights));
>> - } else {
>> - DEBUG(dbgs() << "SKIPPED. All branch weights are zero.\n");
>> - }
>> - }
>> -}
>> -
>> -/// \brief Get the line number for the function header.
>> -///
>> -/// This looks up function \p F in the current compilation unit and
>> -/// retrieves the line number where the function is defined. This is
>> -/// line 0 for all the samples read from the profile file. Every line
>> -/// number is relative to this line.
>> -///
>> -/// \param F Function object to query.
>> -///
>> -/// \returns the line number where \p F is defined. If it returns 0,
>> -/// it means that there is no debug information available for
>> \p F.
>> -unsigned SampleProfileLoader::getFunctionLoc(Function &F) {
>> - if (DISubprogram *S = getDISubprogram(&F))
>> - return S->getLine();
>> -
>> - // If could not find the start of \p F, emit a diagnostic to inform
>> the user
>> - // about the missed opportunity.
>> - F.getContext().diagnose(DiagnosticInfoSampleProfile(
>> - "No debug information found in function " + F.getName() +
>> - ": Function profile not used",
>> - DS_Warning));
>> - return 0;
>> -}
>> -
>> -/// \brief Generate branch weight metadata for all branches in \p F.
>> -///
>> -/// Branch weights are computed out of instruction samples using a
>> -/// propagation heuristic. Propagation proceeds in 3 phases:
>> -///
>> -/// 1- Assignment of block weights. All the basic blocks in the function
>> -/// are initial assigned the same weight as their most frequently
>> -/// executed instruction.
>> -///
>> -/// 2- Creation of equivalence classes. Since samples may be missing from
>> -/// blocks, we can fill in the gaps by setting the weights of all the
>> -/// blocks in the same equivalence class to the same weight. To
>> compute
>> -/// the concept of equivalence, we use dominance and loop information.
>> -/// Two blocks B1 and B2 are in the same equivalence class if B1
>> -/// dominates B2, B2 post-dominates B1 and both are in the same loop.
>> -///
>> -/// 3- Propagation of block weights into edges. This uses a simple
>> -/// propagation heuristic. The following rules are applied to every
>> -/// block BB in the CFG:
>> -///
>> -/// - If BB has a single predecessor/successor, then the weight
>> -/// of that edge is the weight of the block.
>> -///
>> -/// - If all the edges are known except one, and the weight of the
>> -/// block is already known, the weight of the unknown edge will
>> -/// be the weight of the block minus the sum of all the known
>> -/// edges. If the sum of all the known edges is larger than BB's
>> weight,
>> -/// we set the unknown edge weight to zero.
>> -///
>> -/// - If there is a self-referential edge, and the weight of the
>> block is
>> -/// known, the weight for that edge is set to the weight of the
>> block
>> -/// minus the weight of the other incoming edges to that block (if
>> -/// known).
>> -///
>> -/// Since this propagation is not guaranteed to finalize for every CFG,
>> we
>> -/// only allow it to proceed for a limited number of iterations
>> (controlled
>> -/// by -sample-profile-max-propagate-iterations).
>> -///
>> -/// FIXME: Try to replace this propagation heuristic with a scheme
>> -/// that is guaranteed to finalize. A work-list approach similar to
>> -/// the standard value propagation algorithm used by SSA-CCP might
>> -/// work here.
>> -///
>> -/// Once all the branch weights are computed, we emit the MD_prof
>> -/// metadata on BB using the computed values for each of its branches.
>> -///
>> -/// \param F The function to query.
>> -///
>> -/// \returns true if \p F was modified. Returns false, otherwise.
>> -bool SampleProfileLoader::emitAnnotations(Function &F) {
>> - bool Changed = false;
>> -
>> - // Initialize invariants used during computation and propagation.
>> - HeaderLineno = getFunctionLoc(F);
>> - if (HeaderLineno == 0)
>> - return false;
>> -
>> - DEBUG(dbgs() << "Line number for the first instruction in " <<
>> F.getName()
>> - << ": " << HeaderLineno << "\n");
>> -
>> - // Compute basic block weights.
>> - Changed |= computeBlockWeights(F);
>> -
>> - if (Changed) {
>> - // Find equivalence classes.
>> - findEquivalenceClasses(F);
>> -
>> - // Propagate weights to all edges.
>> - propagateWeights(F);
>> - }
>> -
>> - return Changed;
>> -}
>> -
>> -char SampleProfileLoader::ID = 0;
>> -INITIALIZE_PASS_BEGIN(SampleProfileLoader, "sample-profile",
>> - "Sample Profile loader", false, false)
>> -INITIALIZE_PASS_DEPENDENCY(DominatorTreeWrapperPass)
>> -INITIALIZE_PASS_DEPENDENCY(PostDominatorTree)
>> -INITIALIZE_PASS_DEPENDENCY(LoopInfoWrapperPass)
>> -INITIALIZE_PASS_DEPENDENCY(AddDiscriminators)
>> -INITIALIZE_PASS_END(SampleProfileLoader, "sample-profile",
>> - "Sample Profile loader", false, false)
>> -
>> -bool SampleProfileLoader::doInitialization(Module &M) {
>> - auto ReaderOrErr = SampleProfileReader::create(Filename,
>> M.getContext());
>> - if (std::error_code EC = ReaderOrErr.getError()) {
>> - std::string Msg = "Could not open profile: " + EC.message();
>> - M.getContext().diagnose(DiagnosticInfoSampleProfile(Filename.data(),
>> Msg));
>> - return false;
>> - }
>> - Reader = std::move(ReaderOrErr.get());
>> - ProfileIsValid = (Reader->read() == sampleprof_error::success);
>> - return true;
>> -}
>> -
>> -FunctionPass *llvm::createSampleProfileLoaderPass() {
>> - return new SampleProfileLoader(SampleProfileFile);
>> -}
>> -
>> -FunctionPass *llvm::createSampleProfileLoaderPass(StringRef Name) {
>> - return new SampleProfileLoader(Name);
>> -}
>> -
>> -bool SampleProfileLoader::runOnFunction(Function &F) {
>> - if (!ProfileIsValid)
>> - return false;
>> -
>> - DT = &getAnalysis<DominatorTreeWrapperPass>().getDomTree();
>> - PDT = &getAnalysis<PostDominatorTree>();
>> - LI = &getAnalysis<LoopInfoWrapperPass>().getLoopInfo();
>> - Ctx = &F.getParent()->getContext();
>> - Samples = Reader->getSamplesFor(F);
>> - if (!Samples->empty())
>> - return emitAnnotations(F);
>> - return false;
>> -}
>>
>> Modified: llvm/trunk/lib/Transforms/Scalar/Scalar.cpp
>> URL:
>> http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Transforms/Scalar/Scalar.cpp?rev=245940&r1=245939&r2=245940&view=diff
>>
>> ==============================================================================
>> --- llvm/trunk/lib/Transforms/Scalar/Scalar.cpp (original)
>> +++ llvm/trunk/lib/Transforms/Scalar/Scalar.cpp Tue Aug 25 10:25:11 2015
>> @@ -33,7 +33,6 @@ void llvm::initializeScalarOpts(PassRegi
>> initializeADCEPass(Registry);
>> initializeBDCEPass(Registry);
>> initializeAlignmentFromAssumptionsPass(Registry);
>> - initializeSampleProfileLoaderPass(Registry);
>> initializeConstantHoistingPass(Registry);
>> initializeConstantPropagationPass(Registry);
>> initializeCorrelatedValuePropagationPass(Registry);
>>
>>
>> _______________________________________________
>> llvm-commits mailing list
>> llvm-commits at lists.llvm.org
>> http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-commits
>>
>
>
>
> --
> Alexey Samsonov
> vonosmas at gmail.com
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/llvm-commits/attachments/20150826/6ad1e21d/attachment.html>
More information about the llvm-commits
mailing list