From: Mikhail Glushenkov Date: Mon, 2 Mar 2009 09:01:14 +0000 (+0000) Subject: Reorganize llvmc code. X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=f188178a2f8e6452d3e161acdad9a79e1f36c43f;p=oota-llvm.git Reorganize llvmc code. Move the code from 'llvmc/driver' into a new CompilerDriver library, and change the build system accordingly. Makes it easier for projects using LLVM to build their own llvmc-based drivers. Tested with objdir != srcdir. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@65821 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/llvm/CompilerDriver/Error.h b/include/llvm/CompilerDriver/Error.h new file mode 100644 index 00000000000..c0aaff1a724 --- /dev/null +++ b/include/llvm/CompilerDriver/Error.h @@ -0,0 +1,33 @@ +//===--- Error.h - The LLVM Compiler Driver ---------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open +// Source License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Exception classes for LLVMC. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVMC2_ERROR_H +#define LLVM_TOOLS_LLVMC2_ERROR_H + +#include + +namespace llvmc { + + class error_code: public std::runtime_error { + int Code_; + public: + error_code (int c) + : std::runtime_error("Tool returned error code"), Code_(c) + {} + + int code() const { return Code_; } + }; + +} + +#endif //LLVM_TOOLS_LLVMC2_ERROR_H diff --git a/lib/CompilerDriver/Action.cpp b/lib/CompilerDriver/Action.cpp new file mode 100644 index 00000000000..c0a1b849bcd --- /dev/null +++ b/lib/CompilerDriver/Action.cpp @@ -0,0 +1,78 @@ +//===--- Action.cpp - The LLVM Compiler Driver ------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open +// Source License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Action class - implementation and auxiliary functions. +// +//===----------------------------------------------------------------------===// + +#include "llvm/CompilerDriver/Action.h" + +#include "llvm/Support/CommandLine.h" +#include "llvm/System/Program.h" + +#include +#include + +using namespace llvm; +using namespace llvmc; + +extern cl::opt DryRun; +extern cl::opt VerboseMode; + +namespace { + int ExecuteProgram(const std::string& name, + const StrVector& args) { + sys::Path prog = sys::Program::FindProgramByName(name); + + if (prog.isEmpty()) + throw std::runtime_error("Can't find program '" + name + "'"); + if (!prog.canExecute()) + throw std::runtime_error("Program '" + name + "' is not executable."); + + // Build the command line vector and the redirects array. + const sys::Path* redirects[3] = {0,0,0}; + sys::Path stdout_redirect; + + std::vector argv; + argv.reserve((args.size()+2)); + argv.push_back(name.c_str()); + + for (StrVector::const_iterator B = args.begin(), E = args.end(); + B!=E; ++B) { + if (*B == ">") { + ++B; + stdout_redirect.set(*B); + redirects[1] = &stdout_redirect; + } + else { + argv.push_back((*B).c_str()); + } + } + argv.push_back(0); // null terminate list. + + // Invoke the program. + return sys::Program::ExecuteAndWait(prog, &argv[0], 0, &redirects[0]); + } + + void print_string (const std::string& str) { + std::cerr << str << ' '; + } +} + +int llvmc::Action::Execute() const { + if (DryRun || VerboseMode) { + std::cerr << Command_ << " "; + std::for_each(Args_.begin(), Args_.end(), print_string); + std::cerr << '\n'; + } + if (DryRun) + return 0; + else + return ExecuteProgram(Command_, Args_); +} diff --git a/lib/CompilerDriver/CMakeLists.txt b/lib/CompilerDriver/CMakeLists.txt new file mode 100644 index 00000000000..153dd443cbf --- /dev/null +++ b/lib/CompilerDriver/CMakeLists.txt @@ -0,0 +1,10 @@ +set(LLVM_LINK_COMPONENTS support system) +set(LLVM_REQUIRES_EH 1) + +add_llvm_tool(llvmc + Action.cpp + CompilationGraph.cpp + llvmc.cpp + Plugin.cpp + Tool.cpp + ) diff --git a/lib/CompilerDriver/CompilationGraph.cpp b/lib/CompilerDriver/CompilationGraph.cpp new file mode 100644 index 00000000000..b8a66e17f53 --- /dev/null +++ b/lib/CompilerDriver/CompilationGraph.cpp @@ -0,0 +1,534 @@ +//===--- CompilationGraph.cpp - The LLVM Compiler Driver --------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open +// Source License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Compilation graph - implementation. +// +//===----------------------------------------------------------------------===// + +#include "llvm/CompilerDriver/CompilationGraph.h" +#include "llvm/CompilerDriver/Error.h" + +#include "llvm/ADT/STLExtras.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/DOTGraphTraits.h" +#include "llvm/Support/GraphWriter.h" + +#include +#include +#include +#include +#include +#include +#include + +using namespace llvm; +using namespace llvmc; + +extern cl::list InputFilenames; +extern cl::list Languages; + +namespace llvmc { + + const std::string& LanguageMap::GetLanguage(const sys::Path& File) const { + LanguageMap::const_iterator Lang = this->find(File.getSuffix()); + if (Lang == this->end()) + throw std::runtime_error("Unknown suffix: " + File.getSuffix()); + return Lang->second; + } +} + +namespace { + + /// ChooseEdge - Return the edge with the maximum weight. + template + const Edge* ChooseEdge(const C& EdgesContainer, + const InputLanguagesSet& InLangs, + const std::string& NodeName = "root") { + const Edge* MaxEdge = 0; + unsigned MaxWeight = 0; + bool SingleMax = true; + + for (typename C::const_iterator B = EdgesContainer.begin(), + E = EdgesContainer.end(); B != E; ++B) { + const Edge* e = B->getPtr(); + unsigned EW = e->Weight(InLangs); + if (EW > MaxWeight) { + MaxEdge = e; + MaxWeight = EW; + SingleMax = true; + } else if (EW == MaxWeight) { + SingleMax = false; + } + } + + if (!SingleMax) + throw std::runtime_error("Node " + NodeName + + ": multiple maximal outward edges found!" + " Most probably a specification error."); + if (!MaxEdge) + throw std::runtime_error("Node " + NodeName + + ": no maximal outward edge found!" + " Most probably a specification error."); + return MaxEdge; + } + +} + +void Node::AddEdge(Edge* Edg) { + // If there already was an edge between two nodes, modify it instead + // of adding a new edge. + const std::string& ToolName = Edg->ToolName(); + for (container_type::iterator B = OutEdges.begin(), E = OutEdges.end(); + B != E; ++B) { + if ((*B)->ToolName() == ToolName) { + llvm::IntrusiveRefCntPtr(Edg).swap(*B); + return; + } + } + OutEdges.push_back(llvm::IntrusiveRefCntPtr(Edg)); +} + +CompilationGraph::CompilationGraph() { + NodesMap["root"] = Node(this); +} + +Node& CompilationGraph::getNode(const std::string& ToolName) { + nodes_map_type::iterator I = NodesMap.find(ToolName); + if (I == NodesMap.end()) + throw std::runtime_error("Node " + ToolName + " is not in the graph"); + return I->second; +} + +const Node& CompilationGraph::getNode(const std::string& ToolName) const { + nodes_map_type::const_iterator I = NodesMap.find(ToolName); + if (I == NodesMap.end()) + throw std::runtime_error("Node " + ToolName + " is not in the graph!"); + return I->second; +} + +// Find the tools list corresponding to the given language name. +const CompilationGraph::tools_vector_type& +CompilationGraph::getToolsVector(const std::string& LangName) const +{ + tools_map_type::const_iterator I = ToolsMap.find(LangName); + if (I == ToolsMap.end()) + throw std::runtime_error("No tool corresponding to the language " + + LangName + " found"); + return I->second; +} + +void CompilationGraph::insertNode(Tool* V) { + if (NodesMap.count(V->Name()) == 0) + NodesMap[V->Name()] = Node(this, V); +} + +void CompilationGraph::insertEdge(const std::string& A, Edge* Edg) { + Node& B = getNode(Edg->ToolName()); + if (A == "root") { + const char** InLangs = B.ToolPtr->InputLanguages(); + for (;*InLangs; ++InLangs) + ToolsMap[*InLangs].push_back(IntrusiveRefCntPtr(Edg)); + NodesMap["root"].AddEdge(Edg); + } + else { + Node& N = getNode(A); + N.AddEdge(Edg); + } + // Increase the inward edge counter. + B.IncrInEdges(); +} + +// Pass input file through the chain until we bump into a Join node or +// a node that says that it is the last. +void CompilationGraph::PassThroughGraph (const sys::Path& InFile, + const Node* StartNode, + const InputLanguagesSet& InLangs, + const sys::Path& TempDir, + const LanguageMap& LangMap) const { + sys::Path In = InFile; + const Node* CurNode = StartNode; + + while(true) { + Tool* CurTool = CurNode->ToolPtr.getPtr(); + + if (CurTool->IsJoin()) { + JoinTool& JT = dynamic_cast(*CurTool); + JT.AddToJoinList(In); + break; + } + + Action CurAction = CurTool->GenerateAction(In, CurNode->HasChildren(), + TempDir, InLangs, LangMap); + + if (int ret = CurAction.Execute()) + throw error_code(ret); + + if (CurAction.StopCompilation()) + return; + + CurNode = &getNode(ChooseEdge(CurNode->OutEdges, + InLangs, + CurNode->Name())->ToolName()); + In = CurAction.OutFile(); + } +} + +// Find the head of the toolchain corresponding to the given file. +// Also, insert an input language into InLangs. +const Node* CompilationGraph:: +FindToolChain(const sys::Path& In, const std::string* ForceLanguage, + InputLanguagesSet& InLangs, const LanguageMap& LangMap) const { + + // Determine the input language. + const std::string& InLanguage = + ForceLanguage ? *ForceLanguage : LangMap.GetLanguage(In); + + // Add the current input language to the input language set. + InLangs.insert(InLanguage); + + // Find the toolchain for the input language. + const tools_vector_type& TV = getToolsVector(InLanguage); + if (TV.empty()) + throw std::runtime_error("No toolchain corresponding to language " + + InLanguage + " found"); + return &getNode(ChooseEdge(TV, InLangs)->ToolName()); +} + +// Helper function used by Build(). +// Traverses initial portions of the toolchains (up to the first Join node). +// This function is also responsible for handling the -x option. +void CompilationGraph::BuildInitial (InputLanguagesSet& InLangs, + const sys::Path& TempDir, + const LanguageMap& LangMap) { + // This is related to -x option handling. + cl::list::const_iterator xIter = Languages.begin(), + xBegin = xIter, xEnd = Languages.end(); + bool xEmpty = true; + const std::string* xLanguage = 0; + unsigned xPos = 0, xPosNext = 0, filePos = 0; + + if (xIter != xEnd) { + xEmpty = false; + xPos = Languages.getPosition(xIter - xBegin); + cl::list::const_iterator xNext = llvm::next(xIter); + xPosNext = (xNext == xEnd) ? std::numeric_limits::max() + : Languages.getPosition(xNext - xBegin); + xLanguage = (*xIter == "none") ? 0 : &(*xIter); + } + + // For each input file: + for (cl::list::const_iterator B = InputFilenames.begin(), + CB = B, E = InputFilenames.end(); B != E; ++B) { + sys::Path In = sys::Path(*B); + + // Code for handling the -x option. + // Output: std::string* xLanguage (can be NULL). + if (!xEmpty) { + filePos = InputFilenames.getPosition(B - CB); + + if (xPos < filePos) { + if (filePos < xPosNext) { + xLanguage = (*xIter == "none") ? 0 : &(*xIter); + } + else { // filePos >= xPosNext + // Skip xIters while filePos > xPosNext + while (filePos > xPosNext) { + ++xIter; + xPos = xPosNext; + + cl::list::const_iterator xNext = llvm::next(xIter); + if (xNext == xEnd) + xPosNext = std::numeric_limits::max(); + else + xPosNext = Languages.getPosition(xNext - xBegin); + xLanguage = (*xIter == "none") ? 0 : &(*xIter); + } + } + } + } + + // Find the toolchain corresponding to this file. + const Node* N = FindToolChain(In, xLanguage, InLangs, LangMap); + // Pass file through the chain starting at head. + PassThroughGraph(In, N, InLangs, TempDir, LangMap); + } +} + +// Sort the nodes in topological order. +void CompilationGraph::TopologicalSort(std::vector& Out) { + std::queue Q; + Q.push(&getNode("root")); + + while (!Q.empty()) { + const Node* A = Q.front(); + Q.pop(); + Out.push_back(A); + for (Node::const_iterator EB = A->EdgesBegin(), EE = A->EdgesEnd(); + EB != EE; ++EB) { + Node* B = &getNode((*EB)->ToolName()); + B->DecrInEdges(); + if (B->HasNoInEdges()) + Q.push(B); + } + } +} + +namespace { + bool NotJoinNode(const Node* N) { + return N->ToolPtr ? !N->ToolPtr->IsJoin() : true; + } +} + +// Call TopologicalSort and filter the resulting list to include +// only Join nodes. +void CompilationGraph:: +TopologicalSortFilterJoinNodes(std::vector& Out) { + std::vector TopSorted; + TopologicalSort(TopSorted); + std::remove_copy_if(TopSorted.begin(), TopSorted.end(), + std::back_inserter(Out), NotJoinNode); +} + +int CompilationGraph::Build (const sys::Path& TempDir, + const LanguageMap& LangMap) { + + InputLanguagesSet InLangs; + + // Traverse initial parts of the toolchains and fill in InLangs. + BuildInitial(InLangs, TempDir, LangMap); + + std::vector JTV; + TopologicalSortFilterJoinNodes(JTV); + + // For all join nodes in topological order: + for (std::vector::iterator B = JTV.begin(), E = JTV.end(); + B != E; ++B) { + + const Node* CurNode = *B; + JoinTool* JT = &dynamic_cast(*CurNode->ToolPtr.getPtr()); + + // Are there any files in the join list? + if (JT->JoinListEmpty()) + continue; + + Action CurAction = JT->GenerateAction(CurNode->HasChildren(), + TempDir, InLangs, LangMap); + + if (int ret = CurAction.Execute()) + throw error_code(ret); + + if (CurAction.StopCompilation()) + return 0; + + const Node* NextNode = &getNode(ChooseEdge(CurNode->OutEdges, InLangs, + CurNode->Name())->ToolName()); + PassThroughGraph(sys::Path(CurAction.OutFile()), NextNode, + InLangs, TempDir, LangMap); + } + + return 0; +} + +int CompilationGraph::CheckLanguageNames() const { + int ret = 0; + // Check that names for output and input languages on all edges do match. + for (const_nodes_iterator B = this->NodesMap.begin(), + E = this->NodesMap.end(); B != E; ++B) { + + const Node & N1 = B->second; + if (N1.ToolPtr) { + for (Node::const_iterator EB = N1.EdgesBegin(), EE = N1.EdgesEnd(); + EB != EE; ++EB) { + const Node& N2 = this->getNode((*EB)->ToolName()); + + if (!N2.ToolPtr) { + ++ret; + std::cerr << "Error: there is an edge from '" << N1.ToolPtr->Name() + << "' back to the root!\n\n"; + continue; + } + + const char* OutLang = N1.ToolPtr->OutputLanguage(); + const char** InLangs = N2.ToolPtr->InputLanguages(); + bool eq = false; + for (;*InLangs; ++InLangs) { + if (std::strcmp(OutLang, *InLangs) == 0) { + eq = true; + break; + } + } + + if (!eq) { + ++ret; + std::cerr << "Error: Output->input language mismatch in the edge '" << + N1.ToolPtr->Name() << "' -> '" << N2.ToolPtr->Name() << "'!\n"; + + std::cerr << "Expected one of { "; + + InLangs = N2.ToolPtr->InputLanguages(); + for (;*InLangs; ++InLangs) { + std::cerr << '\'' << *InLangs << (*(InLangs+1) ? "', " : "'"); + } + + std::cerr << " }, but got '" << OutLang << "'!\n\n"; + } + + } + } + } + + return ret; +} + +int CompilationGraph::CheckMultipleDefaultEdges() const { + int ret = 0; + InputLanguagesSet Dummy; + + // For all nodes, just iterate over the outgoing edges and check if there is + // more than one edge with maximum weight. + for (const_nodes_iterator B = this->NodesMap.begin(), + E = this->NodesMap.end(); B != E; ++B) { + const Node& N = B->second; + unsigned MaxWeight = 0; + + // Ignore the root node. + if (!N.ToolPtr) + continue; + + for (Node::const_iterator EB = N.EdgesBegin(), EE = N.EdgesEnd(); + EB != EE; ++EB) { + unsigned EdgeWeight = (*EB)->Weight(Dummy); + if (EdgeWeight > MaxWeight) { + MaxWeight = EdgeWeight; + } + else if (EdgeWeight == MaxWeight) { + ++ret; + std::cerr + << "Error: there are multiple maximal edges stemming from the '" + << N.ToolPtr->Name() << "' node!\n\n"; + break; + } + } + } + + return ret; +} + +int CompilationGraph::CheckCycles() { + unsigned deleted = 0; + std::queue Q; + Q.push(&getNode("root")); + + // Try to delete all nodes that have no ingoing edges, starting from the + // root. If there are any nodes left after this operation, then we have a + // cycle. This relies on '--check-graph' not performing the topological sort. + while (!Q.empty()) { + Node* A = Q.front(); + Q.pop(); + ++deleted; + + for (Node::iterator EB = A->EdgesBegin(), EE = A->EdgesEnd(); + EB != EE; ++EB) { + Node* B = &getNode((*EB)->ToolName()); + B->DecrInEdges(); + if (B->HasNoInEdges()) + Q.push(B); + } + } + + if (deleted != NodesMap.size()) { + std::cerr << "Error: there are cycles in the compilation graph!\n" + << "Try inspecting the diagram produced by " + "'llvmc --view-graph'.\n\n"; + return 1; + } + + return 0; +} + +int CompilationGraph::Check () { + // We try to catch as many errors as we can in one go. + int ret = 0; + + // Check that output/input language names match. + ret += this->CheckLanguageNames(); + + // Check for multiple default edges. + ret += this->CheckMultipleDefaultEdges(); + + // Check for cycles. + ret += this->CheckCycles(); + + return ret; +} + +// Code related to graph visualization. + +namespace llvm { + template <> + struct DOTGraphTraits + : public DefaultDOTGraphTraits + { + + template + static std::string getNodeLabel(const Node* N, const GraphType&) + { + if (N->ToolPtr) + if (N->ToolPtr->IsJoin()) + return N->Name() + "\n (join" + + (N->HasChildren() ? ")" + : std::string(": ") + N->ToolPtr->OutputLanguage() + ')'); + else + return N->Name(); + else + return "root"; + } + + template + static std::string getEdgeSourceLabel(const Node* N, EdgeIter I) { + if (N->ToolPtr) { + return N->ToolPtr->OutputLanguage(); + } + else { + const char** InLangs = I->ToolPtr->InputLanguages(); + std::string ret; + + for (; *InLangs; ++InLangs) { + if (*(InLangs + 1)) { + ret += *InLangs; + ret += ", "; + } + else { + ret += *InLangs; + } + } + + return ret; + } + } + }; + +} + +void CompilationGraph::writeGraph() { + std::ofstream O("compilation-graph.dot"); + + if (O.good()) { + llvm::WriteGraph(this, "compilation-graph"); + O.close(); + } + else { + throw std::runtime_error("Error opening file 'compilation-graph.dot'" + " for writing!"); + } +} + +void CompilationGraph::viewGraph() { + llvm::ViewGraph(this, "compilation-graph"); +} diff --git a/lib/CompilerDriver/Makefile b/lib/CompilerDriver/Makefile new file mode 100644 index 00000000000..e5bf3e10a79 --- /dev/null +++ b/lib/CompilerDriver/Makefile @@ -0,0 +1,19 @@ +##===- lib/CompilerDriver/Makefile -------------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open +# Source License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LEVEL = ../.. + +# We don't want this library to appear in `llvm-config --libs` output, so its +# name doesn't start with "LLVM". + +LIBRARYNAME = CompilerDriver +LINK_COMPONENTS = support system +REQUIRES_EH := 1 + +include $(LEVEL)/Makefile.common diff --git a/lib/CompilerDriver/Plugin.cpp b/lib/CompilerDriver/Plugin.cpp new file mode 100644 index 00000000000..75abbd041d3 --- /dev/null +++ b/lib/CompilerDriver/Plugin.cpp @@ -0,0 +1,73 @@ +//===--- Plugin.cpp - The LLVM Compiler Driver ------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open +// Source License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Plugin support. +// +//===----------------------------------------------------------------------===// + +#include "llvm/CompilerDriver/Plugin.h" + +#include +#include + +namespace { + + // Registry::Add<> does not do lifetime management (probably issues + // with static constructor/destructor ordering), so we have to + // implement it here. + // + // All this static registration/life-before-main model seems + // unnecessary convoluted to me. + + static bool pluginListInitialized = false; + typedef std::vector PluginList; + static PluginList Plugins; + + struct ByPriority { + bool operator()(const llvmc::BasePlugin* lhs, + const llvmc::BasePlugin* rhs) { + return lhs->Priority() < rhs->Priority(); + } + }; +} + +namespace llvmc { + + PluginLoader::PluginLoader() { + if (!pluginListInitialized) { + for (PluginRegistry::iterator B = PluginRegistry::begin(), + E = PluginRegistry::end(); B != E; ++B) + Plugins.push_back(B->instantiate()); + std::sort(Plugins.begin(), Plugins.end(), ByPriority()); + } + pluginListInitialized = true; + } + + PluginLoader::~PluginLoader() { + if (pluginListInitialized) { + for (PluginList::iterator B = Plugins.begin(), E = Plugins.end(); + B != E; ++B) + delete (*B); + } + pluginListInitialized = false; + } + + void PluginLoader::PopulateLanguageMap(LanguageMap& langMap) { + for (PluginList::iterator B = Plugins.begin(), E = Plugins.end(); + B != E; ++B) + (*B)->PopulateLanguageMap(langMap); + } + + void PluginLoader::PopulateCompilationGraph(CompilationGraph& graph) { + for (PluginList::iterator B = Plugins.begin(), E = Plugins.end(); + B != E; ++B) + (*B)->PopulateCompilationGraph(graph); + } + +} diff --git a/lib/CompilerDriver/Tool.cpp b/lib/CompilerDriver/Tool.cpp new file mode 100644 index 00000000000..886b26b5d71 --- /dev/null +++ b/lib/CompilerDriver/Tool.cpp @@ -0,0 +1,74 @@ +//===--- Tool.cpp - The LLVM Compiler Driver --------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open +// Source License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Tool base class - implementation details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/CompilerDriver/Tool.h" + +#include "llvm/System/Path.h" +#include "llvm/Support/CommandLine.h" + +using namespace llvm; +using namespace llvmc; + +extern cl::opt OutputFilename; + +namespace { + sys::Path MakeTempFile(const sys::Path& TempDir, const std::string& BaseName, + const std::string& Suffix) { + sys::Path Out; + + // Make sure we don't end up with path names like '/file.o' if the + // TempDir is empty. + if (TempDir.empty()) { + Out.set(BaseName); + } + else { + Out = TempDir; + Out.appendComponent(BaseName); + } + Out.appendSuffix(Suffix); + // NOTE: makeUnique always *creates* a unique temporary file, + // which is good, since there will be no races. However, some + // tools do not like it when the output file already exists, so + // they have to be placated with -f or something like that. + Out.makeUnique(true, NULL); + return Out; + } +} + +sys::Path Tool::OutFilename(const sys::Path& In, + const sys::Path& TempDir, + bool StopCompilation, + const char* OutputSuffix) const { + sys::Path Out; + + if (StopCompilation) { + if (!OutputFilename.empty()) { + Out.set(OutputFilename); + } + else if (IsJoin()) { + Out.set("a"); + Out.appendSuffix(OutputSuffix); + } + else { + Out.set(In.getBasename()); + Out.appendSuffix(OutputSuffix); + } + } + else { + if (IsJoin()) + Out = MakeTempFile(TempDir, "tmp", OutputSuffix); + else + Out = MakeTempFile(TempDir, In.getBasename(), OutputSuffix); + } + return Out; +} diff --git a/lib/Makefile b/lib/Makefile index 4ad0763f97a..8dd67d9957d 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -9,7 +9,7 @@ LEVEL = .. PARALLEL_DIRS = VMCore AsmParser Bitcode Archive Analysis Transforms CodeGen \ - Target ExecutionEngine Debugger Linker + Target ExecutionEngine Debugger Linker CompilerDriver include $(LEVEL)/Makefile.common diff --git a/tools/llvmc/CMakeLists.txt b/tools/llvmc/CMakeLists.txt index f8204199041..bebaaebca8d 100644 --- a/tools/llvmc/CMakeLists.txt +++ b/tools/llvmc/CMakeLists.txt @@ -1,4 +1,4 @@ -add_subdirectory(driver) +# add_subdirectory(driver) # TODO: support plugins and user-configured builds. # See ./doc/LLVMC-Reference.rst "Customizing LLVMC: the compilation graph" diff --git a/tools/llvmc/Makefile b/tools/llvmc/Makefile index 4354c387d40..60961769d19 100644 --- a/tools/llvmc/Makefile +++ b/tools/llvmc/Makefile @@ -9,11 +9,12 @@ LEVEL = ../.. -BUILTIN_PLUGINS = Base Clang -DRIVER_NAME = llvmc -DIRS = plugins driver +# The current plan is to make the user copy the skeleton project and change only +# this file (and plugins/UserPlugin, of course). + +export LLVMC_BASED_DRIVER_NAME = llvmc +export LLVMC_BUILTIN_PLUGINS = Base Clang -export BUILTIN_PLUGINS -export DRIVER_NAME +DIRS = plugins driver include $(LEVEL)/Makefile.common diff --git a/tools/llvmc/Makefile.llvmc b/tools/llvmc/Makefile.llvmc new file mode 100644 index 00000000000..79ec6426e8d --- /dev/null +++ b/tools/llvmc/Makefile.llvmc @@ -0,0 +1,49 @@ +##===- tools/llvmc/Makefile.llvmc --------------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open +# Source License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +# TODO: This must be eventually merged into Makefile.rules. + +ifdef LLVMC_PLUGIN + +# We are included from plugins/PluginName/Makefile... + +LEVEL = ../../../.. + +LIBRARYNAME := $(patsubst %,plugin_llvmc_%,$(LLVMC_PLUGIN)) +REQUIRES_EH := 1 + +# Build a dynamic library if the user runs `make` from plugins/PluginName +ifndef LLVMC_BUILTIN_PLUGIN +LOADABLE_MODULE = 1 +endif + +# TableGen stuff... +ifneq ($(BUILT_SOURCES),) +BUILD_AUTOGENERATED_INC=1 +endif + +include $(LEVEL)/Makefile.common + +ifdef BUILD_AUTOGENERATED_INC + +TOOLS_SOURCE := $(strip $(wildcard $(PROJ_SRC_DIR)/*.td)) + +TD_COMMON :=$(strip $(wildcard \ + $(LLVM_SRC_ROOT)/include/llvm/CompilerDriver/*.td)) + +$(ObjDir)/AutoGenerated.inc.tmp: $(TOOLS_SOURCE) $(ObjDir)/.dir \ + $(TBLGEN) $(TD_COMMON) + $(Echo) "Building LLVMC configuration library with tblgen" + $(Verb) $(TableGen) -gen-llvmc -o $(call SYSPATH, $@) $< + +AutoGenerated.inc : $(ObjDir)/AutoGenerated.inc.tmp + $(Verb) $(CMP) -s $@ $< || $(CP) $< $@ +endif # BUILD_AUTOGENERATED_INC + +endif # LLVMC_PLUGIN diff --git a/tools/llvmc/driver/Action.cpp b/tools/llvmc/driver/Action.cpp deleted file mode 100644 index c0a1b849bcd..00000000000 --- a/tools/llvmc/driver/Action.cpp +++ /dev/null @@ -1,78 +0,0 @@ -//===--- Action.cpp - The LLVM Compiler Driver ------------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open -// Source License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// Action class - implementation and auxiliary functions. -// -//===----------------------------------------------------------------------===// - -#include "llvm/CompilerDriver/Action.h" - -#include "llvm/Support/CommandLine.h" -#include "llvm/System/Program.h" - -#include -#include - -using namespace llvm; -using namespace llvmc; - -extern cl::opt DryRun; -extern cl::opt VerboseMode; - -namespace { - int ExecuteProgram(const std::string& name, - const StrVector& args) { - sys::Path prog = sys::Program::FindProgramByName(name); - - if (prog.isEmpty()) - throw std::runtime_error("Can't find program '" + name + "'"); - if (!prog.canExecute()) - throw std::runtime_error("Program '" + name + "' is not executable."); - - // Build the command line vector and the redirects array. - const sys::Path* redirects[3] = {0,0,0}; - sys::Path stdout_redirect; - - std::vector argv; - argv.reserve((args.size()+2)); - argv.push_back(name.c_str()); - - for (StrVector::const_iterator B = args.begin(), E = args.end(); - B!=E; ++B) { - if (*B == ">") { - ++B; - stdout_redirect.set(*B); - redirects[1] = &stdout_redirect; - } - else { - argv.push_back((*B).c_str()); - } - } - argv.push_back(0); // null terminate list. - - // Invoke the program. - return sys::Program::ExecuteAndWait(prog, &argv[0], 0, &redirects[0]); - } - - void print_string (const std::string& str) { - std::cerr << str << ' '; - } -} - -int llvmc::Action::Execute() const { - if (DryRun || VerboseMode) { - std::cerr << Command_ << " "; - std::for_each(Args_.begin(), Args_.end(), print_string); - std::cerr << '\n'; - } - if (DryRun) - return 0; - else - return ExecuteProgram(Command_, Args_); -} diff --git a/tools/llvmc/driver/CMakeLists.txt b/tools/llvmc/driver/CMakeLists.txt deleted file mode 100644 index 153dd443cbf..00000000000 --- a/tools/llvmc/driver/CMakeLists.txt +++ /dev/null @@ -1,10 +0,0 @@ -set(LLVM_LINK_COMPONENTS support system) -set(LLVM_REQUIRES_EH 1) - -add_llvm_tool(llvmc - Action.cpp - CompilationGraph.cpp - llvmc.cpp - Plugin.cpp - Tool.cpp - ) diff --git a/tools/llvmc/driver/CompilationGraph.cpp b/tools/llvmc/driver/CompilationGraph.cpp deleted file mode 100644 index 238ef6f42a7..00000000000 --- a/tools/llvmc/driver/CompilationGraph.cpp +++ /dev/null @@ -1,534 +0,0 @@ -//===--- CompilationGraph.cpp - The LLVM Compiler Driver --------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open -// Source License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// Compilation graph - implementation. -// -//===----------------------------------------------------------------------===// - -#include "Error.h" -#include "llvm/CompilerDriver/CompilationGraph.h" - -#include "llvm/ADT/STLExtras.h" -#include "llvm/Support/CommandLine.h" -#include "llvm/Support/DOTGraphTraits.h" -#include "llvm/Support/GraphWriter.h" - -#include -#include -#include -#include -#include -#include -#include - -using namespace llvm; -using namespace llvmc; - -extern cl::list InputFilenames; -extern cl::list Languages; - -namespace llvmc { - - const std::string& LanguageMap::GetLanguage(const sys::Path& File) const { - LanguageMap::const_iterator Lang = this->find(File.getSuffix()); - if (Lang == this->end()) - throw std::runtime_error("Unknown suffix: " + File.getSuffix()); - return Lang->second; - } -} - -namespace { - - /// ChooseEdge - Return the edge with the maximum weight. - template - const Edge* ChooseEdge(const C& EdgesContainer, - const InputLanguagesSet& InLangs, - const std::string& NodeName = "root") { - const Edge* MaxEdge = 0; - unsigned MaxWeight = 0; - bool SingleMax = true; - - for (typename C::const_iterator B = EdgesContainer.begin(), - E = EdgesContainer.end(); B != E; ++B) { - const Edge* e = B->getPtr(); - unsigned EW = e->Weight(InLangs); - if (EW > MaxWeight) { - MaxEdge = e; - MaxWeight = EW; - SingleMax = true; - } else if (EW == MaxWeight) { - SingleMax = false; - } - } - - if (!SingleMax) - throw std::runtime_error("Node " + NodeName + - ": multiple maximal outward edges found!" - " Most probably a specification error."); - if (!MaxEdge) - throw std::runtime_error("Node " + NodeName + - ": no maximal outward edge found!" - " Most probably a specification error."); - return MaxEdge; - } - -} - -void Node::AddEdge(Edge* Edg) { - // If there already was an edge between two nodes, modify it instead - // of adding a new edge. - const std::string& ToolName = Edg->ToolName(); - for (container_type::iterator B = OutEdges.begin(), E = OutEdges.end(); - B != E; ++B) { - if ((*B)->ToolName() == ToolName) { - llvm::IntrusiveRefCntPtr(Edg).swap(*B); - return; - } - } - OutEdges.push_back(llvm::IntrusiveRefCntPtr(Edg)); -} - -CompilationGraph::CompilationGraph() { - NodesMap["root"] = Node(this); -} - -Node& CompilationGraph::getNode(const std::string& ToolName) { - nodes_map_type::iterator I = NodesMap.find(ToolName); - if (I == NodesMap.end()) - throw std::runtime_error("Node " + ToolName + " is not in the graph"); - return I->second; -} - -const Node& CompilationGraph::getNode(const std::string& ToolName) const { - nodes_map_type::const_iterator I = NodesMap.find(ToolName); - if (I == NodesMap.end()) - throw std::runtime_error("Node " + ToolName + " is not in the graph!"); - return I->second; -} - -// Find the tools list corresponding to the given language name. -const CompilationGraph::tools_vector_type& -CompilationGraph::getToolsVector(const std::string& LangName) const -{ - tools_map_type::const_iterator I = ToolsMap.find(LangName); - if (I == ToolsMap.end()) - throw std::runtime_error("No tool corresponding to the language " - + LangName + " found"); - return I->second; -} - -void CompilationGraph::insertNode(Tool* V) { - if (NodesMap.count(V->Name()) == 0) - NodesMap[V->Name()] = Node(this, V); -} - -void CompilationGraph::insertEdge(const std::string& A, Edge* Edg) { - Node& B = getNode(Edg->ToolName()); - if (A == "root") { - const char** InLangs = B.ToolPtr->InputLanguages(); - for (;*InLangs; ++InLangs) - ToolsMap[*InLangs].push_back(IntrusiveRefCntPtr(Edg)); - NodesMap["root"].AddEdge(Edg); - } - else { - Node& N = getNode(A); - N.AddEdge(Edg); - } - // Increase the inward edge counter. - B.IncrInEdges(); -} - -// Pass input file through the chain until we bump into a Join node or -// a node that says that it is the last. -void CompilationGraph::PassThroughGraph (const sys::Path& InFile, - const Node* StartNode, - const InputLanguagesSet& InLangs, - const sys::Path& TempDir, - const LanguageMap& LangMap) const { - sys::Path In = InFile; - const Node* CurNode = StartNode; - - while(true) { - Tool* CurTool = CurNode->ToolPtr.getPtr(); - - if (CurTool->IsJoin()) { - JoinTool& JT = dynamic_cast(*CurTool); - JT.AddToJoinList(In); - break; - } - - Action CurAction = CurTool->GenerateAction(In, CurNode->HasChildren(), - TempDir, InLangs, LangMap); - - if (int ret = CurAction.Execute()) - throw error_code(ret); - - if (CurAction.StopCompilation()) - return; - - CurNode = &getNode(ChooseEdge(CurNode->OutEdges, - InLangs, - CurNode->Name())->ToolName()); - In = CurAction.OutFile(); - } -} - -// Find the head of the toolchain corresponding to the given file. -// Also, insert an input language into InLangs. -const Node* CompilationGraph:: -FindToolChain(const sys::Path& In, const std::string* ForceLanguage, - InputLanguagesSet& InLangs, const LanguageMap& LangMap) const { - - // Determine the input language. - const std::string& InLanguage = - ForceLanguage ? *ForceLanguage : LangMap.GetLanguage(In); - - // Add the current input language to the input language set. - InLangs.insert(InLanguage); - - // Find the toolchain for the input language. - const tools_vector_type& TV = getToolsVector(InLanguage); - if (TV.empty()) - throw std::runtime_error("No toolchain corresponding to language " - + InLanguage + " found"); - return &getNode(ChooseEdge(TV, InLangs)->ToolName()); -} - -// Helper function used by Build(). -// Traverses initial portions of the toolchains (up to the first Join node). -// This function is also responsible for handling the -x option. -void CompilationGraph::BuildInitial (InputLanguagesSet& InLangs, - const sys::Path& TempDir, - const LanguageMap& LangMap) { - // This is related to -x option handling. - cl::list::const_iterator xIter = Languages.begin(), - xBegin = xIter, xEnd = Languages.end(); - bool xEmpty = true; - const std::string* xLanguage = 0; - unsigned xPos = 0, xPosNext = 0, filePos = 0; - - if (xIter != xEnd) { - xEmpty = false; - xPos = Languages.getPosition(xIter - xBegin); - cl::list::const_iterator xNext = llvm::next(xIter); - xPosNext = (xNext == xEnd) ? std::numeric_limits::max() - : Languages.getPosition(xNext - xBegin); - xLanguage = (*xIter == "none") ? 0 : &(*xIter); - } - - // For each input file: - for (cl::list::const_iterator B = InputFilenames.begin(), - CB = B, E = InputFilenames.end(); B != E; ++B) { - sys::Path In = sys::Path(*B); - - // Code for handling the -x option. - // Output: std::string* xLanguage (can be NULL). - if (!xEmpty) { - filePos = InputFilenames.getPosition(B - CB); - - if (xPos < filePos) { - if (filePos < xPosNext) { - xLanguage = (*xIter == "none") ? 0 : &(*xIter); - } - else { // filePos >= xPosNext - // Skip xIters while filePos > xPosNext - while (filePos > xPosNext) { - ++xIter; - xPos = xPosNext; - - cl::list::const_iterator xNext = llvm::next(xIter); - if (xNext == xEnd) - xPosNext = std::numeric_limits::max(); - else - xPosNext = Languages.getPosition(xNext - xBegin); - xLanguage = (*xIter == "none") ? 0 : &(*xIter); - } - } - } - } - - // Find the toolchain corresponding to this file. - const Node* N = FindToolChain(In, xLanguage, InLangs, LangMap); - // Pass file through the chain starting at head. - PassThroughGraph(In, N, InLangs, TempDir, LangMap); - } -} - -// Sort the nodes in topological order. -void CompilationGraph::TopologicalSort(std::vector& Out) { - std::queue Q; - Q.push(&getNode("root")); - - while (!Q.empty()) { - const Node* A = Q.front(); - Q.pop(); - Out.push_back(A); - for (Node::const_iterator EB = A->EdgesBegin(), EE = A->EdgesEnd(); - EB != EE; ++EB) { - Node* B = &getNode((*EB)->ToolName()); - B->DecrInEdges(); - if (B->HasNoInEdges()) - Q.push(B); - } - } -} - -namespace { - bool NotJoinNode(const Node* N) { - return N->ToolPtr ? !N->ToolPtr->IsJoin() : true; - } -} - -// Call TopologicalSort and filter the resulting list to include -// only Join nodes. -void CompilationGraph:: -TopologicalSortFilterJoinNodes(std::vector& Out) { - std::vector TopSorted; - TopologicalSort(TopSorted); - std::remove_copy_if(TopSorted.begin(), TopSorted.end(), - std::back_inserter(Out), NotJoinNode); -} - -int CompilationGraph::Build (const sys::Path& TempDir, - const LanguageMap& LangMap) { - - InputLanguagesSet InLangs; - - // Traverse initial parts of the toolchains and fill in InLangs. - BuildInitial(InLangs, TempDir, LangMap); - - std::vector JTV; - TopologicalSortFilterJoinNodes(JTV); - - // For all join nodes in topological order: - for (std::vector::iterator B = JTV.begin(), E = JTV.end(); - B != E; ++B) { - - const Node* CurNode = *B; - JoinTool* JT = &dynamic_cast(*CurNode->ToolPtr.getPtr()); - - // Are there any files in the join list? - if (JT->JoinListEmpty()) - continue; - - Action CurAction = JT->GenerateAction(CurNode->HasChildren(), - TempDir, InLangs, LangMap); - - if (int ret = CurAction.Execute()) - throw error_code(ret); - - if (CurAction.StopCompilation()) - return 0; - - const Node* NextNode = &getNode(ChooseEdge(CurNode->OutEdges, InLangs, - CurNode->Name())->ToolName()); - PassThroughGraph(sys::Path(CurAction.OutFile()), NextNode, - InLangs, TempDir, LangMap); - } - - return 0; -} - -int CompilationGraph::CheckLanguageNames() const { - int ret = 0; - // Check that names for output and input languages on all edges do match. - for (const_nodes_iterator B = this->NodesMap.begin(), - E = this->NodesMap.end(); B != E; ++B) { - - const Node & N1 = B->second; - if (N1.ToolPtr) { - for (Node::const_iterator EB = N1.EdgesBegin(), EE = N1.EdgesEnd(); - EB != EE; ++EB) { - const Node& N2 = this->getNode((*EB)->ToolName()); - - if (!N2.ToolPtr) { - ++ret; - std::cerr << "Error: there is an edge from '" << N1.ToolPtr->Name() - << "' back to the root!\n\n"; - continue; - } - - const char* OutLang = N1.ToolPtr->OutputLanguage(); - const char** InLangs = N2.ToolPtr->InputLanguages(); - bool eq = false; - for (;*InLangs; ++InLangs) { - if (std::strcmp(OutLang, *InLangs) == 0) { - eq = true; - break; - } - } - - if (!eq) { - ++ret; - std::cerr << "Error: Output->input language mismatch in the edge '" << - N1.ToolPtr->Name() << "' -> '" << N2.ToolPtr->Name() << "'!\n"; - - std::cerr << "Expected one of { "; - - InLangs = N2.ToolPtr->InputLanguages(); - for (;*InLangs; ++InLangs) { - std::cerr << '\'' << *InLangs << (*(InLangs+1) ? "', " : "'"); - } - - std::cerr << " }, but got '" << OutLang << "'!\n\n"; - } - - } - } - } - - return ret; -} - -int CompilationGraph::CheckMultipleDefaultEdges() const { - int ret = 0; - InputLanguagesSet Dummy; - - // For all nodes, just iterate over the outgoing edges and check if there is - // more than one edge with maximum weight. - for (const_nodes_iterator B = this->NodesMap.begin(), - E = this->NodesMap.end(); B != E; ++B) { - const Node& N = B->second; - unsigned MaxWeight = 0; - - // Ignore the root node. - if (!N.ToolPtr) - continue; - - for (Node::const_iterator EB = N.EdgesBegin(), EE = N.EdgesEnd(); - EB != EE; ++EB) { - unsigned EdgeWeight = (*EB)->Weight(Dummy); - if (EdgeWeight > MaxWeight) { - MaxWeight = EdgeWeight; - } - else if (EdgeWeight == MaxWeight) { - ++ret; - std::cerr - << "Error: there are multiple maximal edges stemming from the '" - << N.ToolPtr->Name() << "' node!\n\n"; - break; - } - } - } - - return ret; -} - -int CompilationGraph::CheckCycles() { - unsigned deleted = 0; - std::queue Q; - Q.push(&getNode("root")); - - // Try to delete all nodes that have no ingoing edges, starting from the - // root. If there are any nodes left after this operation, then we have a - // cycle. This relies on '--check-graph' not performing the topological sort. - while (!Q.empty()) { - Node* A = Q.front(); - Q.pop(); - ++deleted; - - for (Node::iterator EB = A->EdgesBegin(), EE = A->EdgesEnd(); - EB != EE; ++EB) { - Node* B = &getNode((*EB)->ToolName()); - B->DecrInEdges(); - if (B->HasNoInEdges()) - Q.push(B); - } - } - - if (deleted != NodesMap.size()) { - std::cerr << "Error: there are cycles in the compilation graph!\n" - << "Try inspecting the diagram produced by " - "'llvmc --view-graph'.\n\n"; - return 1; - } - - return 0; -} - -int CompilationGraph::Check () { - // We try to catch as many errors as we can in one go. - int ret = 0; - - // Check that output/input language names match. - ret += this->CheckLanguageNames(); - - // Check for multiple default edges. - ret += this->CheckMultipleDefaultEdges(); - - // Check for cycles. - ret += this->CheckCycles(); - - return ret; -} - -// Code related to graph visualization. - -namespace llvm { - template <> - struct DOTGraphTraits - : public DefaultDOTGraphTraits - { - - template - static std::string getNodeLabel(const Node* N, const GraphType&) - { - if (N->ToolPtr) - if (N->ToolPtr->IsJoin()) - return N->Name() + "\n (join" + - (N->HasChildren() ? ")" - : std::string(": ") + N->ToolPtr->OutputLanguage() + ')'); - else - return N->Name(); - else - return "root"; - } - - template - static std::string getEdgeSourceLabel(const Node* N, EdgeIter I) { - if (N->ToolPtr) { - return N->ToolPtr->OutputLanguage(); - } - else { - const char** InLangs = I->ToolPtr->InputLanguages(); - std::string ret; - - for (; *InLangs; ++InLangs) { - if (*(InLangs + 1)) { - ret += *InLangs; - ret += ", "; - } - else { - ret += *InLangs; - } - } - - return ret; - } - } - }; - -} - -void CompilationGraph::writeGraph() { - std::ofstream O("compilation-graph.dot"); - - if (O.good()) { - llvm::WriteGraph(this, "compilation-graph"); - O.close(); - } - else { - throw std::runtime_error("Error opening file 'compilation-graph.dot'" - " for writing!"); - } -} - -void CompilationGraph::viewGraph() { - llvm::ViewGraph(this, "compilation-graph"); -} diff --git a/tools/llvmc/driver/Error.h b/tools/llvmc/driver/Error.h deleted file mode 100644 index c0aaff1a724..00000000000 --- a/tools/llvmc/driver/Error.h +++ /dev/null @@ -1,33 +0,0 @@ -//===--- Error.h - The LLVM Compiler Driver ---------------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open -// Source License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// Exception classes for LLVMC. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_TOOLS_LLVMC2_ERROR_H -#define LLVM_TOOLS_LLVMC2_ERROR_H - -#include - -namespace llvmc { - - class error_code: public std::runtime_error { - int Code_; - public: - error_code (int c) - : std::runtime_error("Tool returned error code"), Code_(c) - {} - - int code() const { return Code_; } - }; - -} - -#endif //LLVM_TOOLS_LLVMC2_ERROR_H diff --git a/tools/llvmc/driver/Main.cpp b/tools/llvmc/driver/Main.cpp new file mode 100644 index 00000000000..16b91a87c91 --- /dev/null +++ b/tools/llvmc/driver/Main.cpp @@ -0,0 +1,126 @@ +//===--- Main.cpp - The LLVM Compiler Driver -------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open +// Source License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This tool provides a single point of access to the LLVM +// compilation tools. It has many options. To discover the options +// supported please refer to the tools' manual page or run the tool +// with the --help option. +// +//===----------------------------------------------------------------------===// + +#include "llvm/CompilerDriver/CompilationGraph.h" +#include "llvm/CompilerDriver/Error.h" +#include "llvm/CompilerDriver/Plugin.h" + +#include "llvm/System/Path.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/PluginLoader.h" + +#include +#include +#include + +namespace cl = llvm::cl; +namespace sys = llvm::sys; +using namespace llvmc; + +// Built-in command-line options. +// External linkage here is intentional. + +cl::list InputFilenames(cl::Positional, cl::desc(""), + cl::ZeroOrMore); +cl::opt OutputFilename("o", cl::desc("Output file name"), + cl::value_desc("file"), cl::Prefix); +cl::list Languages("x", + cl::desc("Specify the language of the following input files"), + cl::ZeroOrMore); +cl::opt DryRun("dry-run", + cl::desc("Only pretend to run commands")); +cl::opt VerboseMode("v", + cl::desc("Enable verbose mode")); + +cl::opt CheckGraph("check-graph", + cl::desc("Check the compilation graph for errors"), + cl::Hidden); +cl::opt WriteGraph("write-graph", + cl::desc("Write compilation-graph.dot file"), + cl::Hidden); +cl::opt ViewGraph("view-graph", + cl::desc("Show compilation graph in GhostView"), + cl::Hidden); +cl::opt SaveTemps("save-temps", + cl::desc("Keep temporary files"), + cl::Hidden); + +namespace { + /// BuildTargets - A small wrapper for CompilationGraph::Build. + int BuildTargets(CompilationGraph& graph, const LanguageMap& langMap) { + int ret; + const sys::Path& tempDir = SaveTemps + ? sys::Path("") + : sys::Path(sys::Path::GetTemporaryDirectory()); + + try { + ret = graph.Build(tempDir, langMap); + } + catch(...) { + tempDir.eraseFromDisk(true); + throw; + } + + if (!SaveTemps) + tempDir.eraseFromDisk(true); + return ret; + } +} + +int main(int argc, char** argv) { + try { + LanguageMap langMap; + CompilationGraph graph; + + cl::ParseCommandLineOptions + (argc, argv, "LLVM Compiler Driver (Work In Progress)", true); + + PluginLoader Plugins; + Plugins.PopulateLanguageMap(langMap); + Plugins.PopulateCompilationGraph(graph); + + if (CheckGraph) { + return graph.Check(); + } + + if (ViewGraph) { + graph.viewGraph(); + if (!WriteGraph) + return 0; + } + + if (WriteGraph) { + graph.writeGraph(); + return 0; + } + + if (InputFilenames.empty()) { + throw std::runtime_error("no input files"); + } + + return BuildTargets(graph, langMap); + } + catch(llvmc::error_code& ec) { + return ec.code(); + } + catch(const std::exception& ex) { + std::cerr << argv[0] << ": " << ex.what() << '\n'; + } + catch(...) { + std::cerr << argv[0] << ": unknown error!\n"; + } + return 1; +} diff --git a/tools/llvmc/driver/Makefile b/tools/llvmc/driver/Makefile index 107f0dea385..0e32726fec2 100644 --- a/tools/llvmc/driver/Makefile +++ b/tools/llvmc/driver/Makefile @@ -8,12 +8,15 @@ ##===----------------------------------------------------------------------===## LEVEL = ../../.. -TOOLNAME = $(DRIVER_NAME) -LINK_COMPONENTS = support system -REQUIRES_EH := 1 -ifneq ($(BUILTIN_PLUGINS),) -USEDLIBS = $(patsubst %,plugin_llvmc_%,$(BUILTIN_PLUGINS)) +TOOLNAME = $(LLVMC_BASED_DRIVER_NAME) +USEDLIBS = CompilerDriver + +ifneq ($(LLVMC_BUILTIN_PLUGINS),) +USEDLIBS += $(patsubst %,plugin_llvmc_%,$(LLVMC_BUILTIN_PLUGINS)) endif +LINK_COMPONENTS = support system +REQUIRES_EH := 1 + include $(LEVEL)/Makefile.common diff --git a/tools/llvmc/driver/Plugin.cpp b/tools/llvmc/driver/Plugin.cpp deleted file mode 100644 index 75abbd041d3..00000000000 --- a/tools/llvmc/driver/Plugin.cpp +++ /dev/null @@ -1,73 +0,0 @@ -//===--- Plugin.cpp - The LLVM Compiler Driver ------------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open -// Source License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// Plugin support. -// -//===----------------------------------------------------------------------===// - -#include "llvm/CompilerDriver/Plugin.h" - -#include -#include - -namespace { - - // Registry::Add<> does not do lifetime management (probably issues - // with static constructor/destructor ordering), so we have to - // implement it here. - // - // All this static registration/life-before-main model seems - // unnecessary convoluted to me. - - static bool pluginListInitialized = false; - typedef std::vector PluginList; - static PluginList Plugins; - - struct ByPriority { - bool operator()(const llvmc::BasePlugin* lhs, - const llvmc::BasePlugin* rhs) { - return lhs->Priority() < rhs->Priority(); - } - }; -} - -namespace llvmc { - - PluginLoader::PluginLoader() { - if (!pluginListInitialized) { - for (PluginRegistry::iterator B = PluginRegistry::begin(), - E = PluginRegistry::end(); B != E; ++B) - Plugins.push_back(B->instantiate()); - std::sort(Plugins.begin(), Plugins.end(), ByPriority()); - } - pluginListInitialized = true; - } - - PluginLoader::~PluginLoader() { - if (pluginListInitialized) { - for (PluginList::iterator B = Plugins.begin(), E = Plugins.end(); - B != E; ++B) - delete (*B); - } - pluginListInitialized = false; - } - - void PluginLoader::PopulateLanguageMap(LanguageMap& langMap) { - for (PluginList::iterator B = Plugins.begin(), E = Plugins.end(); - B != E; ++B) - (*B)->PopulateLanguageMap(langMap); - } - - void PluginLoader::PopulateCompilationGraph(CompilationGraph& graph) { - for (PluginList::iterator B = Plugins.begin(), E = Plugins.end(); - B != E; ++B) - (*B)->PopulateCompilationGraph(graph); - } - -} diff --git a/tools/llvmc/driver/Tool.cpp b/tools/llvmc/driver/Tool.cpp deleted file mode 100644 index 886b26b5d71..00000000000 --- a/tools/llvmc/driver/Tool.cpp +++ /dev/null @@ -1,74 +0,0 @@ -//===--- Tool.cpp - The LLVM Compiler Driver --------------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open -// Source License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// Tool base class - implementation details. -// -//===----------------------------------------------------------------------===// - -#include "llvm/CompilerDriver/Tool.h" - -#include "llvm/System/Path.h" -#include "llvm/Support/CommandLine.h" - -using namespace llvm; -using namespace llvmc; - -extern cl::opt OutputFilename; - -namespace { - sys::Path MakeTempFile(const sys::Path& TempDir, const std::string& BaseName, - const std::string& Suffix) { - sys::Path Out; - - // Make sure we don't end up with path names like '/file.o' if the - // TempDir is empty. - if (TempDir.empty()) { - Out.set(BaseName); - } - else { - Out = TempDir; - Out.appendComponent(BaseName); - } - Out.appendSuffix(Suffix); - // NOTE: makeUnique always *creates* a unique temporary file, - // which is good, since there will be no races. However, some - // tools do not like it when the output file already exists, so - // they have to be placated with -f or something like that. - Out.makeUnique(true, NULL); - return Out; - } -} - -sys::Path Tool::OutFilename(const sys::Path& In, - const sys::Path& TempDir, - bool StopCompilation, - const char* OutputSuffix) const { - sys::Path Out; - - if (StopCompilation) { - if (!OutputFilename.empty()) { - Out.set(OutputFilename); - } - else if (IsJoin()) { - Out.set("a"); - Out.appendSuffix(OutputSuffix); - } - else { - Out.set(In.getBasename()); - Out.appendSuffix(OutputSuffix); - } - } - else { - if (IsJoin()) - Out = MakeTempFile(TempDir, "tmp", OutputSuffix); - else - Out = MakeTempFile(TempDir, In.getBasename(), OutputSuffix); - } - return Out; -} diff --git a/tools/llvmc/driver/llvmc.cpp b/tools/llvmc/driver/llvmc.cpp deleted file mode 100644 index 059e840eeb6..00000000000 --- a/tools/llvmc/driver/llvmc.cpp +++ /dev/null @@ -1,127 +0,0 @@ -//===--- llvmc.cpp - The LLVM Compiler Driver -------------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open -// Source License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This tool provides a single point of access to the LLVM -// compilation tools. It has many options. To discover the options -// supported please refer to the tools' manual page or run the tool -// with the --help option. -// -//===----------------------------------------------------------------------===// - -#include "Error.h" - -#include "llvm/CompilerDriver/CompilationGraph.h" -#include "llvm/CompilerDriver/Plugin.h" - -#include "llvm/System/Path.h" -#include "llvm/Support/CommandLine.h" -#include "llvm/Support/PluginLoader.h" - -#include -#include -#include - -namespace cl = llvm::cl; -namespace sys = llvm::sys; -using namespace llvmc; - -// Built-in command-line options. -// External linkage here is intentional. - -cl::list InputFilenames(cl::Positional, cl::desc(""), - cl::ZeroOrMore); -cl::opt OutputFilename("o", cl::desc("Output file name"), - cl::value_desc("file"), cl::Prefix); -cl::list Languages("x", - cl::desc("Specify the language of the following input files"), - cl::ZeroOrMore); -cl::opt DryRun("dry-run", - cl::desc("Only pretend to run commands")); -cl::opt VerboseMode("v", - cl::desc("Enable verbose mode")); - -cl::opt CheckGraph("check-graph", - cl::desc("Check the compilation graph for errors"), - cl::Hidden); -cl::opt WriteGraph("write-graph", - cl::desc("Write compilation-graph.dot file"), - cl::Hidden); -cl::opt ViewGraph("view-graph", - cl::desc("Show compilation graph in GhostView"), - cl::Hidden); -cl::opt SaveTemps("save-temps", - cl::desc("Keep temporary files"), - cl::Hidden); - -namespace { - /// BuildTargets - A small wrapper for CompilationGraph::Build. - int BuildTargets(CompilationGraph& graph, const LanguageMap& langMap) { - int ret; - const sys::Path& tempDir = SaveTemps - ? sys::Path("") - : sys::Path(sys::Path::GetTemporaryDirectory()); - - try { - ret = graph.Build(tempDir, langMap); - } - catch(...) { - tempDir.eraseFromDisk(true); - throw; - } - - if (!SaveTemps) - tempDir.eraseFromDisk(true); - return ret; - } -} - -int main(int argc, char** argv) { - try { - LanguageMap langMap; - CompilationGraph graph; - - cl::ParseCommandLineOptions - (argc, argv, "LLVM Compiler Driver (Work In Progress)", true); - - PluginLoader Plugins; - Plugins.PopulateLanguageMap(langMap); - Plugins.PopulateCompilationGraph(graph); - - if (CheckGraph) { - return graph.Check(); - } - - if (ViewGraph) { - graph.viewGraph(); - if (!WriteGraph) - return 0; - } - - if (WriteGraph) { - graph.writeGraph(); - return 0; - } - - if (InputFilenames.empty()) { - throw std::runtime_error("no input files"); - } - - return BuildTargets(graph, langMap); - } - catch(llvmc::error_code& ec) { - return ec.code(); - } - catch(const std::exception& ex) { - std::cerr << argv[0] << ": " << ex.what() << '\n'; - } - catch(...) { - std::cerr << argv[0] << ": unknown error!\n"; - } - return 1; -} diff --git a/tools/llvmc/plugins/Base/Makefile b/tools/llvmc/plugins/Base/Makefile index 5ca6048508a..eb6ed010c6f 100644 --- a/tools/llvmc/plugins/Base/Makefile +++ b/tools/llvmc/plugins/Base/Makefile @@ -7,7 +7,9 @@ # ##===----------------------------------------------------------------------===## +LEVEL = ../.. + LLVMC_PLUGIN = Base BUILT_SOURCES = AutoGenerated.inc -include ../Makefile +include $(LEVEL)/Makefile.llvmc diff --git a/tools/llvmc/plugins/Clang/Makefile b/tools/llvmc/plugins/Clang/Makefile index 32060138c23..a1b011e90db 100644 --- a/tools/llvmc/plugins/Clang/Makefile +++ b/tools/llvmc/plugins/Clang/Makefile @@ -7,7 +7,9 @@ # ##===----------------------------------------------------------------------===## +LEVEL = ../.. + LLVMC_PLUGIN = Clang BUILT_SOURCES = AutoGenerated.inc -include ../Makefile +include $(LEVEL)/Makefile.llvmc diff --git a/tools/llvmc/plugins/Hello/Makefile b/tools/llvmc/plugins/Hello/Makefile index 181dd0c6050..8e9fa6f3135 100644 --- a/tools/llvmc/plugins/Hello/Makefile +++ b/tools/llvmc/plugins/Hello/Makefile @@ -7,6 +7,8 @@ # ##===----------------------------------------------------------------------===## +LEVEL = ../.. + LLVMC_PLUGIN = Hello -include ../Makefile +include $(LEVEL)/Makefile.llvmc diff --git a/tools/llvmc/plugins/Makefile b/tools/llvmc/plugins/Makefile index 734323f50d7..37dac6f0ac0 100644 --- a/tools/llvmc/plugins/Makefile +++ b/tools/llvmc/plugins/Makefile @@ -1,4 +1,4 @@ -##===- tools/llvmc/plugins/Makefile.plugins ----------------*- Makefile -*-===## +##===- tools/llvmc/plugins/Makefile ------------------------*- Makefile -*-===## # # The LLVM Compiler Infrastructure # @@ -7,49 +7,12 @@ # ##===----------------------------------------------------------------------===## -ifndef LLVMC_PLUGIN - LEVEL = ../../.. -DIRS = $(BUILTIN_PLUGINS) - -# TOFIX: Should we also build DSO versions of plugins? -export BUILTIN_LLVMC_PLUGIN=1 - -include $(LEVEL)/Makefile.common -else # LLVMC_PLUGIN - -LEVEL = ../../../.. - -LIBRARYNAME := $(patsubst %,plugin_llvmc_%,$(LLVMC_PLUGIN)) -REQUIRES_EH = 1 - -ifndef BUILTIN_LLVMC_PLUGIN -LOADABLE_MODULE = 1 +ifneq ($(LLVMC_BUILTIN_PLUGINS),) +DIRS = $(LLVMC_BUILTIN_PLUGINS) endif -ifneq ($(BUILT_SOURCES),) -BUILD_AUTOGENERATED_INC=1 -endif +export LLVMC_BUILTIN_PLUGIN=1 include $(LEVEL)/Makefile.common - -# TOFIX: This probably should go into Makefile.rules - -ifdef BUILD_AUTOGENERATED_INC - -TOOLS_SOURCE := $(strip $(wildcard $(PROJ_SRC_DIR)/*.td)) - -TD_COMMON :=$(strip $(wildcard \ - $(LLVM_SRC_ROOT)/include/llvm/CompilerDriver/*.td)) - -$(ObjDir)/AutoGenerated.inc.tmp: $(TOOLS_SOURCE) $(ObjDir)/.dir \ - $(TBLGEN) $(TD_COMMON) - $(Echo) "Building LLVMC configuration library with tblgen" - $(Verb) $(TableGen) -gen-llvmc -o $(call SYSPATH, $@) $< - -AutoGenerated.inc : $(ObjDir)/AutoGenerated.inc.tmp - $(Verb) $(CMP) -s $@ $< || $(CP) $< $@ -endif # BUILD_AUTOGENERATED_INC - -endif # LLVMC_PLUGIN diff --git a/tools/llvmc/plugins/Simple/Makefile b/tools/llvmc/plugins/Simple/Makefile index 1cd5af7a15e..c38cda51719 100644 --- a/tools/llvmc/plugins/Simple/Makefile +++ b/tools/llvmc/plugins/Simple/Makefile @@ -7,7 +7,9 @@ # ##===----------------------------------------------------------------------===## +LEVEL = ../.. + LLVMC_PLUGIN = Simple BUILT_SOURCES = AutoGenerated.inc -include ../Makefile +include $(LEVEL)/Makefile.llvmc