diff options
-rw-r--r-- | include/llvm/ADT/IntrusiveRefCntPtr.h | 2 | ||||
-rw-r--r-- | tools/Makefile | 2 | ||||
-rw-r--r-- | tools/llvmc2/Common.td | 63 | ||||
-rw-r--r-- | tools/llvmc2/Core.cpp | 115 | ||||
-rw-r--r-- | tools/llvmc2/Core.h | 83 | ||||
-rw-r--r-- | tools/llvmc2/Example.td | 24 | ||||
-rw-r--r-- | tools/llvmc2/ExampleWithOpt.td | 24 | ||||
-rw-r--r-- | tools/llvmc2/Makefile | 32 | ||||
-rw-r--r-- | tools/llvmc2/Tools.cpp | 28 | ||||
-rw-r--r-- | tools/llvmc2/Tools.h | 26 | ||||
-rw-r--r-- | tools/llvmc2/Tools.td | 87 | ||||
-rw-r--r-- | tools/llvmc2/Utility.cpp | 39 | ||||
-rw-r--r-- | tools/llvmc2/Utility.h | 27 | ||||
-rw-r--r-- | tools/llvmc2/doc/LLVMC-Enhancements.rst | 270 | ||||
-rw-r--r-- | tools/llvmc2/doc/LLVMCC-Tutorial.rst | 153 | ||||
-rw-r--r-- | tools/llvmc2/llvmcc.cpp | 73 | ||||
-rw-r--r-- | utils/TableGen/LLVMCCConfigurationEmitter.cpp | 935 | ||||
-rw-r--r-- | utils/TableGen/LLVMCCConfigurationEmitter.h | 30 | ||||
-rw-r--r-- | utils/TableGen/TableGen.cpp | 9 |
19 files changed, 2019 insertions, 3 deletions
diff --git a/include/llvm/ADT/IntrusiveRefCntPtr.h b/include/llvm/ADT/IntrusiveRefCntPtr.h index 8804f2b20f..4709d45b2a 100644 --- a/include/llvm/ADT/IntrusiveRefCntPtr.h +++ b/include/llvm/ADT/IntrusiveRefCntPtr.h @@ -222,4 +222,4 @@ namespace llvm { } // end namespace llvm -#endif // LLVM_ADT_INTRUSIVE_REF_CNT_PTR
\ No newline at end of file +#endif // LLVM_ADT_INTRUSIVE_REF_CNT_PTR diff --git a/tools/Makefile b/tools/Makefile index dc9ac44064..d1491af5bf 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -16,7 +16,7 @@ PARALLEL_DIRS := llvm-config \ llc llvm-ranlib llvm-ar llvm-nm \ llvm-ld llvmc llvm-prof llvm-link \ lli gccas gccld llvm-extract llvm-db llvm2cpp \ - bugpoint llvm-bcanalyzer llvm-stub + bugpoint llvm-bcanalyzer llvm-stub llvmc2 include $(LEVEL)/Makefile.config diff --git a/tools/llvmc2/Common.td b/tools/llvmc2/Common.td new file mode 100644 index 0000000000..15b9264d88 --- /dev/null +++ b/tools/llvmc2/Common.td @@ -0,0 +1,63 @@ +//===- Tools.td - Common definitions for LLVMCC -----------*- tablegen -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains common definitions used in llvmcc tool description files. +// +//===----------------------------------------------------------------------===// + +class Tool<list<dag> l> { + list<dag> properties = l; +} + +// Possible Tool properties + +def in_language; +def out_language; +def output_suffix; +def cmd_line; +def join; +def sink; + +// Possible option types + +def switch_option; +def parameter_option; +def parameter_list_option; +def prefix_option; +def prefix_list_option; + +// Possible option properties + +def append_cmd; +def forward; +def stop_compilation; +def unpack_values; +def help; +def required; + +// Map from suffixes to language names + +class LangToSuffixes<string str, list<string> lst> { + string lang = str; + list<string> suffixes = lst; +} + +class LanguageMap<list<LangToSuffixes> lst> { + list<LangToSuffixes> map = lst; +} + +// Toolchain classes + +class ToolChain <list<Tool> lst> { + list <Tool> tools = lst; +} + +class ToolChains <list<ToolChain> lst> { + list<ToolChain> chains = lst; +} diff --git a/tools/llvmc2/Core.cpp b/tools/llvmc2/Core.cpp new file mode 100644 index 0000000000..d08ee7c00f --- /dev/null +++ b/tools/llvmc2/Core.cpp @@ -0,0 +1,115 @@ +//===--- Core.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. +// +//===----------------------------------------------------------------------===// +// +// Core driver abstractions. +// +//===----------------------------------------------------------------------===// + +#include "Core.h" +#include "Utility.h" + +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/Support/CommandLine.h" + +#include <algorithm> +#include <iostream> +#include <stdexcept> + +using namespace llvm; +using namespace llvmcc; + +extern cl::list<std::string> InputFilenames; +extern cl::opt<std::string> OutputFilename; +extern cl::opt<bool> VerboseMode; + +namespace { + void print_string (const std::string& str) { + std::cerr << str << ' '; + } +} + +int llvmcc::Action::Execute() { + if (VerboseMode) { + std::cerr << Command_ << " "; + std::for_each(Args_.begin(), Args_.end(), print_string); + std::cerr << '\n'; + } + return ExecuteProgram(Command_, Args_); +} + +int llvmcc::CompilationGraph::Build (const sys::Path& tempDir) const { + sys::Path In(InputFilenames.at(0)), Out; + + // Find out which language corresponds to the suffix of the first input file + LanguageMap::const_iterator Lang = ExtsToLangs.find(In.getSuffix()); + if (Lang == ExtsToLangs.end()) + throw std::runtime_error("Unknown suffix!"); + + // Find the toolchain corresponding to this language + ToolChainMap::const_iterator ToolsIt = ToolChains.find(Lang->second); + if (ToolsIt == ToolChains.end()) + throw std::runtime_error("Unknown language!"); + ToolChain Tools = ToolsIt->second; + + PathVector JoinList; + + for (cl::list<std::string>::const_iterator B = InputFilenames.begin(), + E = InputFilenames.end(); B != E; ++B) { + In = sys::Path(*B); + + // Pass input file through the toolchain + for (ToolChain::const_iterator B = Tools.begin(), E = Tools.end(); + B != E; ++B) { + + const Tool* CurTool = B->getPtr(); + + // Is this the last step in the chain? + if (llvm::next(B) == E || CurTool->IsLast()) { + JoinList.push_back(In); + break; + } + else { + Out = tempDir; + Out.appendComponent(In.getBasename()); + Out.appendSuffix(CurTool->OutputSuffix()); + Out.makeUnique(true, NULL); + Out.eraseFromDisk(); + } + + if (CurTool->GenerateAction(In, Out).Execute() != 0) + throw std::runtime_error("Tool returned error code!"); + + In = Out; Out.clear(); + } + } + + // Pass .o files to linker + const Tool* JoinNode = (--Tools.end())->getPtr(); + + // If the final output name is empty, set it to "a.out" + if (!OutputFilename.empty()) { + Out = sys::Path(OutputFilename); + } + else { + Out = sys::Path("a"); + Out.appendSuffix(JoinNode->OutputSuffix()); + } + + if (JoinNode->GenerateAction(JoinList, Out).Execute() != 0) + throw std::runtime_error("Tool returned error code!"); + + return 0; +} + +void llvmcc::Tool::UnpackValues (const std::string& from, + std::vector<std::string>& to) const { + SplitString(from, to, ","); +} + diff --git a/tools/llvmc2/Core.h b/tools/llvmc2/Core.h new file mode 100644 index 0000000000..f82e0fa98e --- /dev/null +++ b/tools/llvmc2/Core.h @@ -0,0 +1,83 @@ +//===--- Core.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. +// +//===----------------------------------------------------------------------===// +// +// Core driver abstractions. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVMCC_CORE_H +#define LLVM_TOOLS_LLVMCC_CORE_H + +#include "Utility.h" + +#include "llvm/ADT/IntrusiveRefCntPtr.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/System/Path.h" + +#include <stdexcept> +#include <string> +#include <vector> + +// Core functionality + +namespace llvmcc { + + typedef std::vector<llvm::sys::Path> PathVector; + typedef llvm::StringMap<std::string> LanguageMap; + + class Action { + std::string Command_; + std::vector<std::string> Args_; + public: + Action (std::string const& C, + std::vector<std::string> const& A) + : Command_(C), Args_(A) + {} + + int Execute(); + }; + + class Tool : public llvm::RefCountedBaseVPTR<Tool> { + public: + virtual Action GenerateAction (PathVector const& inFiles, + llvm::sys::Path const& outFile) const = 0; + + virtual Action GenerateAction (llvm::sys::Path const& inFile, + llvm::sys::Path const& outFile) const = 0; + + virtual std::string Name() const = 0; + virtual std::string InputLanguage() const = 0; + virtual std::string OutputLanguage() const = 0; + virtual std::string OutputSuffix() const = 0; + + virtual bool IsLast() const = 0; + virtual bool IsJoin() const = 0; + + // Helper function that is called by the auto-generated code + // Splits strings of the form ",-foo,-bar,-baz" + // TOFIX: find a better name + void UnpackValues (std::string const& from, + std::vector<std::string>& to) const; + + virtual ~Tool() + {} + }; + + typedef std::vector<llvm::IntrusiveRefCntPtr<Tool> > ToolChain; + typedef llvm::StringMap<ToolChain> ToolChainMap; + + struct CompilationGraph { + ToolChainMap ToolChains; + LanguageMap ExtsToLangs; + + int Build(llvm::sys::Path const& tempDir) const; + }; +} + +#endif // LLVM_TOOLS_LLVMCC_CORE_H diff --git a/tools/llvmc2/Example.td b/tools/llvmc2/Example.td new file mode 100644 index 0000000000..68212c3010 --- /dev/null +++ b/tools/llvmc2/Example.td @@ -0,0 +1,24 @@ +//===- Example.td - LLVMCC toolchain descriptions ---------*- tablegen -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains toolchain descriptions used by llvmcc. +// +//===----------------------------------------------------------------------===// + +include "Common.td" +include "Tools.td" + +// Toolchains + +def ToolChains : ToolChains<[ + ToolChain<[llvm_gcc_c, llc, llvm_gcc_assembler, llvm_gcc_linker]>, + ToolChain<[llvm_gcc_cpp, llc, llvm_gcc_assembler, llvm_gcc_linker]>, + ToolChain<[llvm_as, llc, llvm_gcc_assembler, llvm_gcc_linker]>, + ToolChain<[llvm_gcc_assembler, llvm_gcc_linker]> + ]>; diff --git a/tools/llvmc2/ExampleWithOpt.td b/tools/llvmc2/ExampleWithOpt.td new file mode 100644 index 0000000000..9128d311c5 --- /dev/null +++ b/tools/llvmc2/ExampleWithOpt.td @@ -0,0 +1,24 @@ +//===- ExampleWithOpt.td - LLVMCC toolchain descriptions --*- tablegen -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains toolchain descriptions used by llvmcc. +// +//===----------------------------------------------------------------------===// + +include "Common.td" +include "Tools.td" + +// Toolchains + +def ToolChains : ToolChains<[ + ToolChain<[llvm_gcc_c, opt, llc, llvm_gcc_assembler, llvm_gcc_linker]>, + ToolChain<[llvm_gcc_cpp, opt, llc, llvm_gcc_assembler, llvm_gcc_linker]>, + ToolChain<[llvm_as, opt, llc, llvm_gcc_assembler, llvm_gcc_linker]>, + ToolChain<[llvm_gcc_assembler, llvm_gcc_linker]> + ]>; diff --git a/tools/llvmc2/Makefile b/tools/llvmc2/Makefile new file mode 100644 index 0000000000..d0c1588e11 --- /dev/null +++ b/tools/llvmc2/Makefile @@ -0,0 +1,32 @@ +##===- tools/llvmcc/Makefile -------------------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open +# Source License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## +LEVEL = ../.. +TOOLNAME = llvmc2 +BUILT_SOURCES = Tools.inc +LINK_COMPONENTS = support system +REQUIRES_EH := 1 + +include $(LEVEL)/Makefile.common + +TOOLS_TARGET=default +ifeq ($(TOOLS_TARGET), default) + TOOLS_SOURCE=Example.td +else + TOOLS_SOURCE=ExampleWithOpt.td +endif + +# TOFIX: integrate this part into Makefile.rules? +# The degree of horrorshowness in that file is too much for me atm. +$(ObjDir)/Tools.inc.tmp: $(TOOLS_SOURCE) $(ObjDir)/.dir + $(Echo) "Building LLVMCC configuration library with tblgen" + $(Verb) $(TableGen) -gen-llvmcc -o $@ $< + +Tools.inc : $(ObjDir)/Tools.inc.tmp + $(Verb) $(CMP) -s $@ $< || $(CP) $< $@ + diff --git a/tools/llvmc2/Tools.cpp b/tools/llvmc2/Tools.cpp new file mode 100644 index 0000000000..7a9921c54b --- /dev/null +++ b/tools/llvmc2/Tools.cpp @@ -0,0 +1,28 @@ +//===--- Tools.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. +// +//===----------------------------------------------------------------------===// +// +// Auto-generated tool descriptions. +// +//===----------------------------------------------------------------------===// + +#include "Tools.h" +#include "Core.h" + +#include "llvm/ADT/IntrusiveRefCntPtr.h" +#include "llvm/Support/CommandLine.h" + +#include <stdexcept> +#include <string> +#include <vector> + +using namespace llvm; +using namespace llvmcc; + +// Include the auto-generated file +#include "Tools.inc" diff --git a/tools/llvmc2/Tools.h b/tools/llvmc2/Tools.h new file mode 100644 index 0000000000..ba8f06dbd6 --- /dev/null +++ b/tools/llvmc2/Tools.h @@ -0,0 +1,26 @@ +//===--- Tools.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. +// +//===----------------------------------------------------------------------===// +// +// Auto-generated tool descriptions. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVMCC_TOOLS_H +#define LLVM_TOOLS_LLVMCC_TOOLS_H + +#include "Core.h" + +namespace llvmcc { + + void PopulateLanguageMap(LanguageMap& language_map); + void PopulateCompilationGraph(CompilationGraph& tools); + +} + +#endif //LLVM_TOOLS_LLVMCC_TOOLS_H diff --git a/tools/llvmc2/Tools.td b/tools/llvmc2/Tools.td new file mode 100644 index 0000000000..f69c290dff --- /dev/null +++ b/tools/llvmc2/Tools.td @@ -0,0 +1,87 @@ +//===- Tools.td - Tools description for the LLVMCC --------*- tablegen -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains descriptions of the various build tools run by llvmcc. +// +//===----------------------------------------------------------------------===// + +// Open issue: should we use DAG lists in Tool specifications +// or change to something like +// def LLVMGccC : < Tool< +// [ InLanguage<"c">, +// PrefixListOption<"Wl", [UnpackValues, PropertyName<Arg>, ...]> +// ...] ? +// DAG lists look more aesthetically pleasing to me. + +def llvm_gcc_c : Tool< +[(in_language "c"), + (out_language "llvm-assembler"), + (output_suffix "bc"), + (cmd_line "llvm-gcc -c $INFILE -o $OUTFILE -emit-llvm"), + (sink) +]>; + +def llvm_gcc_cpp : Tool< +[(in_language "c++"), + (out_language "llvm-assembler"), + (output_suffix "bc"), + (cmd_line "llvm-g++ -c $INFILE -o $OUTFILE -emit-llvm"), + (sink) +]>; + +def opt : Tool< +[(in_language "llvm-bitcode"), + (out_language "llvm-bitcode"), + (output_suffix "bc"), + (cmd_line "opt $INFILE -o $OUTFILE") +]>; + +def llvm_as : Tool< +[(in_language "llvm-assembler"), + (out_language "llvm-bitcode"), + (output_suffix "bc"), + (cmd_line "llvm-as $INFILE -o $OUTFILE") +]>; + +def llc : Tool< +[(in_language "llvm-bitcode"), + (out_language "assembler"), + (output_suffix "s"), + (cmd_line "llc $INFILE -o $OUTFILE") +]>; + +def llvm_gcc_assembler : Tool< +[(in_language "assembler"), + (out_language "object-code"), + (output_suffix "o"), + (cmd_line "llvm-gcc -c $INFILE -o $OUTFILE"), + (prefix_list_option "Wa", (unpack_values), (help "pass options to assembler")) +]>; + +def llvm_gcc_linker : Tool< +[(in_language "object-code"), + (out_language "executable"), + (output_suffix "out"), + (cmd_line "llvm-gcc $INFILE -o $OUTFILE"), + (join), + (prefix_list_option "L", (forward), (help "add a directory to link path")), + (prefix_list_option "l", (forward), (help "search a library when linking")), + (prefix_list_option "Wl", (unpack_values), (help "pass options to linker")) +]>; + +// Language map + +def LanguageMap : LanguageMap< + [LangToSuffixes<"c++", ["cc", "cp", "cxx", "cpp", "CPP", "c++", "C"]>, + LangToSuffixes<"c", ["c"]>, + LangToSuffixes<"assembler", ["s"]>, + LangToSuffixes<"llvm-assembler", ["ll"]>, + LangToSuffixes<"llvm-bitcode", ["bc"]>, + LangToSuffixes<"object-code", ["o"]>, + LangToSuffixes<"executable", ["out"]>]>; diff --git a/tools/llvmc2/Utility.cpp b/tools/llvmc2/Utility.cpp new file mode 100644 index 0000000000..c53578ad44 --- /dev/null +++ b/tools/llvmc2/Utility.cpp @@ -0,0 +1,39 @@ +//===--- Utility.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. +// +//===----------------------------------------------------------------------===// +// +// Various helper and utility functions - implementation. +// +//===----------------------------------------------------------------------===// + +#include "Utility.h" + +#include "llvm/System/Program.h" + +#include <stdexcept> + +using namespace llvm; + +int llvmcc::ExecuteProgram(const std::string& name, + const std::vector<std::string>& 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."); + + // Invoke the program + std::vector<const char*> argv((args.size()+2)); + argv[0] = name.c_str(); + for (unsigned i = 1; i <= args.size(); ++i) + argv[i] = args[i-1].c_str(); + argv[args.size()+1] = 0; // null terminate list. + + return sys::Program::ExecuteAndWait(prog, &argv[0]); +} diff --git a/tools/llvmc2/Utility.h b/tools/llvmc2/Utility.h new file mode 100644 index 0000000000..3c985a49fc --- /dev/null +++ b/tools/llvmc2/Utility.h @@ -0,0 +1,27 @@ +//===--- Utility.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. +// +//===----------------------------------------------------------------------===// +// +// Various helper and utility functions. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVMCC_UTILITY_H +#define LLVM_TOOLS_LLVMCC_UTILITY_H + +#include <string> +#include <vector> + +namespace llvmcc { + + int ExecuteProgram (const std::string& name, + const std::vector<std::string>& arguments); + +} + +#endif // LLVM_TOOLS_LLVMCC_UTILITY_H diff --git a/tools/llvmc2/doc/LLVMC-Enhancements.rst b/tools/llvmc2/doc/LLVMC-Enhancements.rst new file mode 100644 index 0000000000..a831ea06f8 --- /dev/null +++ b/tools/llvmc2/doc/LLVMC-Enhancements.rst @@ -0,0 +1,270 @@ +Introduction +============ + +A complete rewrite of the LLVMC compiler driver is proposed, aimed at +making it more configurable and useful. + +Motivation +========== + +As it stands, current version of LLVMC does not meet its stated goals +of configurability and extensibility and is therefore not used +much. The need for enhancements in LLVMC is also reflected in [1]_. The +proposed rewrite will fix the aforementioned deficiences and provide +an extensible, future-proof solution. + +Design +====== + +A compiler driver's job is essentially to find a way how to transform +a set of input files into a set of targets, depending on the +user-provided options. Since several ways of transformation can exist +potentially, it's natural to use a directed graph to represent all of +them. In this graph, nodes are tools (for example, ``gcc -S`` is a tool +that generates assembly from C language files) and edges between them +mean that the output of one tool can be given as input to another (as +in ``gcc -S -o - file.c | as``). We'll call this graph the compilation +graph. + +The proposed design revolves around the compilation graph and the +following core abstractions: + +- Target - An (intermediate) compilation target. + +- Action - A shell command template that represents basic compilation + transformation(example: ``gcc -S $INPUT_FILE -o $OUTPUT_FILE``). + +- Tool - Encapsulates information about a concrete tool used in the + compilation process, produces Actions. Operation depends on + command-line options provided by the user. + +- GraphBuilder - Constructs the compilation graph, operation depends + on command-line options. + +- GraphTraverser - Traverses the compilation graph and constructs a + sequence of Actions needed to build the target file, operation + depends on command-line options. + +A high-level view of the compilation process: + + 1. Configuration libraries (see below) are loaded in and the + compilation graph is constructed from the tool descriptions. + + 2. Information about possible options is gathered from (the nodes of) + the compilation graph. + + 3. Options are parsed based on data gathered in step 2. + + 4. A sequence of Actions needed to build the target is constructed + using the compilation graph and provided options. + + 5. The resulting action sequence is executed. + +Extensibility +============== + +To make this design extensible, TableGen [2]_ will be used for +automatic generation of the Tool classes. Users wanting to customize +LLVMC will need to write a configuration library consisting of a set +of TableGen descriptions of compilation tools plus a number of hooks +that influence compilation graph construction and traversal. LLVMC +will have the ability to load user configuration libraries at runtime; +in fact, its own basic functionality will be implemented as a +configuration library. + +TableGen specification example +------------------------------ + +This small example specifies a Tool that converts C source to object +files. Note that it is only a mock-up of inteded functionality, not a +final specification:: + + def GCC : Tool< + GCCProperties, // Properties of this tool + GCCOptions // Options description for this tool + >; + + def GCCProperties : ToolProperties<[ + ToolName<"GCC">, + InputLanguageName<"C">, + OutputLanguageName<"Object-Code"> + InputFileExtension<"c">, + OutputFileExtension<"o">, + CommandFormat<"gcc -c $OPTIONS $FILES"> + ]>; + + def GCCOptions : ToolOptions<[ + Option< + "-Wall", // Option name + [None], // Allowed values + [AddOption<"-Wall">]>, // Action + + Option< + "-Wextra", // Option name + [None], // Allowed values + [AddOption<"-Wextra">]>, // Action + + Option< + "-W", // Option name + [None], // Allowed values + [AddOption<"-W">]>, // Action + + Option< + "-D", // Option name + [AnyString], // Allowed values + + [AddOptionWithArgument<"-D",GetOptionArgument<"-D">>] + // Action: + // If the driver was given option "-D<argument>", add + // option "-D" with the same argument to the invocation string of + // this tool. + > + + ]>; + +Example of generated code +------------------------- + +The specification above compiles to the following code (again, it's a +mock-up):: + + class GCC : public Tool { + + public: + + GCC() { //... } + + // Properties + + static const char* ToolName = "GCC"; + static const char* InputLanguageName = "C"; + static const char* OutputLanguageName = "Object-Code"; + static const char* InputFileExtension = "c"; + static const char* OutputFileExtension = "o"; + static const char* CommandFormat = "gcc -c $OPTIONS $FILES"; + + // Options + + OptionsDescription SupportedOptions() { + OptionsDescription supportedOptions; + + supportedOptions.Add(Option("-Wall")); + supportedOptions.Add(Option("-Wextra")); + supportedOptions.Add(Option("-W")); + supportedOptions.Add(Option("-D", AllowedArgs::ANY_STRING)); + + return supportedOptions; + } + + Action GenerateAction(Options providedOptions) { + Action generatedAction(CommandFormat); Option curOpt; + + curOpt = providedOptions.Get("-D"); + if (curOpt) { + assert(curOpt.HasArgument()); + generatedAction.AddOption(Option("-D", curOpt.GetArgument())); + } + + curOpt = providedOptions.Get("-Wall"); + if (curOpt) + generatedAction.AddOption(Option("-Wall")); + + curOpt = providedOptions.Get("-Wextra"); + if (curOpt) + generatedAction.AddOption(Option("-Wall")); + + curOpt = providedOptions.Get("-W"); + if (curOpt) + generatedAction.AddOption(Option("-Wall")); } + + return generatedAction; + } + + }; + + // defined somewhere... + + class Action { public: void AddOption(const Option& opt) {...} + int Run(const Filenames& fnms) {...} + + } + +Option handling +=============== + +Since one of the main tasks of the compiler driver is to correctly +handle user-provided options, it is important to define this process +in exact way. The intent of the proposed scheme is to function as a +drop-in replacement for GCC. + +Option syntax +------------- + +Option syntax is specified by the following formal grammar:: + + <command-line> ::= <option>* + <option> ::= <positional-option> | <named-option> + <named-option> ::= -[-]<option-name>[<delimeter><option-argument>] + <delimeter> ::= ',' | '=' | ' ' + <positional-option> ::= <string> + <option-name> ::= <string> + <option-argument> ::= <string> + +This roughly corresponds to the GCC option syntax. Note that grouping +of short options(as in ``ls -la``) is forbidden. + +Example:: + + llvmc -O3 -Wa,-foo,-bar -pedantic -std=c++0x a.c b.c c.c + +Option arguments can also have special forms: for example, an argument +can be a comma-separated list (like in -Wa,-foo,-bar). In such cases, +it's up to the option handler to parse the argument. + +Option semantics +---------------- + +According to their meaning, options are classified into following +categories: + +- Global options - Options that influence compilation graph + construction/traversal. Example: -E (stop after preprocessing). + +- Local options - Options that influence one or several Actions in + the generated action sequence. Example: -O3 (turn on optimization). + +- Prefix options - Options that influence meaning of the following + command-line arguments. Example: -x language (specify language for + the input files explicitly). Prefix options can be local or global. + +- Built-in options - Options that are hard-coded into + driver. Examples: --help, -o file/-pipe (redirect output). Can be + local or global. + +Naming +====== + +Since the compiler driver, as a single point of access to the LLVM +tool set, is a very often used tool, it would be desirable to make its name +as short and easy to type as possible. Some possible names are 'llcc' or +'lcc', by analogy with gcc. + + +Issues +====== + +1. Should global-options-influencing hooks be written by hand or + auto-generated from TableGen specifications? + +2. More? + +References +========== + +.. [1] LLVM Bug#686 + + http://llvm.org/bugs/show_bug.cgi?id=686 + +.. [2] TableGen Fundamentals + + http://llvm.org/docs/TableGenFundamentals.html diff --git a/tools/llvmc2/doc/LLVMCC-Tutorial.rst b/tools/llvmc2/doc/LLVMCC-Tutorial.rst new file mode 100644 index 0000000000..8374cad768 --- /dev/null +++ b/tools/llvmc2/doc/LLVMCC-Tutorial.rst @@ -0,0 +1,153 @@ +Tutorial - Writing LLVMCC Configuration files +============================================= + +LLVMCC is a generic compiler driver(just like ``gcc``), designed to be +customizable and extensible. Its job is essentially to transform a set +of input files into a set of targets, depending on configuration rules +and user options. This tutorial describes how one can write +configuration files for ``llvmcc``. + +Since LLVMCC uses TableGen [1]_ as the language of its configuration +files, you need to be familiar with it. + +Describing a toolchain +---------------------- + +The main concept that ``llvmcc`` operates with is a *toolchain*, which +is just a list of tools that process input files in a pipeline-like +fashion. Toolchain definitions look like this:: + + def ToolChains : ToolChains<[ + ToolChain<[llvm_gcc_c, llc, llvm_gcc_assembler, llvm_gcc_linker]>, + ToolChain<[llvm_gcc_cpp, llc, llvm_gcc_assembler, llvm_gcc_linker]>, + ... + ]>; + +Every configuration file should have a single toolchains list called +``ToolChains``. + +At the time of writing, ``llvmcc`` does not support mixing various +toolchains together - in other words, all input files should be in the +same language. + +Another temporary limitation is that every toolchain should end with a +"join" node - a linker-like program that combines its inputs into a +single output file. + +Describing a tool +----------------- + +A single element of a toolchain is a tool. A tool definition looks +like this (taken from the Tools.td file):: + + def llvm_gcc_cpp : Tool<[ + (in_language "c++"), + (out_language "llvm-assembler"), + (output_suffix "bc"), + (cmd_line "llvm-g++ -c $INFILE -o $OUTFILE -emit-llvm"), + (sink) + ]>; + +This defines a new tool called ``llvm_gcc_cpp``, which is an alias for +``llvm-g++``. As you can see, a tool definition is just a list of +properties; most of them should be self-evident. The ``sink`` property +means that this tool should be passed all command-line options that +aren't handled by the other tools. + +The complete list of the currently implemented tool properties follows: + +* Possible tool properties: + - in_language - input language name. + + - out_language - output language name. + + - output_suffix - output file suffix. + + - cmd_line - the actual command used to run the tool. You can use + ``$INFILE`` and ``$OUTFILE`` variables. + + - join - this tool is a "join node" in the graph, i.e. it gets a + list of input files and joins them together. Used for linkers. + + - sink - all command-line options that are not handled by other + tools are passed to this tool. + +The next tool definition is slightly more complex:: + + def llvm_gcc_linker : Tool<[ + (in_language "object-code"), + (out_language "executable"), + (output_suffix "out"), + (cmd_line "llvm-gcc $INFILE -o $OUTFILE"), + (join), + (prefix_list_option "L", (forward), (help "add a directory to link path")), + (prefix_list_option "l", (forward), (help "search a library when linking")), + (prefix_list_option "Wl", (unpack_values), (help "pass options to linker")) + ]>; + +This tool has a "join" property, which means that it behaves like a +linker (because of that this tool should be the last in the +toolchain). This tool also defines several command-line options: ``-l``, +``-L`` and ``-Wl`` which have their usual meaning. An option has two +attributes: a name and a (possibly empty) list of properties. All +currently implemented option types and properties are described below: + +* Possible option types: + - switch_option - a simple boolean switch, for example ``-time``. + + - parameter_option - option that takes an argument, for example ``-std=c99``; + + - parameter_list_option - same as the above, but more than one + occurence of the option is allowed. + + - prefix_option - same as the parameter_option, but the option name + and parameter value are not separated. + + - prefix_list_option - same as the above, but more than one + occurence of the option is allowed; example: ``-lm -lpthread``. + +* Possible option properties: + - append_cmd - append a string to the tool invocation command. + + - forward - forward this option unchanged. + + - stop_compilation - stop compilation after this phase. + + - unpack_values - used for for splitting and forwarding + comma-separated lists of options, e.g. ``-Wa,-foo=bar,-baz`` is + converted to ``-foo=bar -baz`` and appended to the tool invocation + command. + + - help - help string associated with this option. + + - required - this option is obligatory. + +Language map +------------ + +One last bit that you probably should change is the language map, +which defines mappings between language names and file extensions. It +is used internally to choose the proper toolchain based on the names +of the input files. Language map definition is located in the file +``Tools.td`` and looks like this:: + + def LanguageMap : LanguageMap< + [LangToSuffixes<"c++", ["cc", "cp", "cxx", "cpp", "CPP", "c++", "C"]>, + LangToSuffixes<"c", ["c"]>, + ... + ]>; + + +Putting it all together +----------------------- + +Since at the time of writing LLVMCC does not support on-the-fly +reloading of the configuration, the only way to test your changes is +to recompile the program. To do this, ``cd`` to the source code +directory and run ``make``. + +References +========== + +.. [1] TableGen Fundamentals + http://llvm.cs.uiuc.edu/docs/TableGenFundamentals.html diff --git a/tools/llvmc2/llvmcc.cpp b/tools/llvmc2/llvmcc.cpp new file mode 100644 index 0000000000..c5edac9606 --- /dev/null +++ b/tools/llvmc2/llvmcc.cpp @@ -0,0 +1,73 @@ +//===--- llvmcc.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 "Core.h" +#include "Utility.h" +#include "Tools.h" + +#include "llvm/System/Path.h" +#include "llvm/Support/CommandLine.h" + +#include <iostream> +#include <stdexcept> +#include <string> + +using namespace llvm; +using namespace llvmcc; + +// These variables are also used in Core.cpp, +// so they should have external linkage. +cl::list<std::string> InputFilenames(cl::Positional, + cl::desc("<input file>"), cl::OneOrMore); +cl::opt<std::string> OutputFilename("o", cl::desc("Output file name"), + cl::value_desc("file")); +cl::opt<bool> VerboseMode("v", cl::desc("Enable verbose mode")); + + +namespace { + int BuildTargets(const CompilationGraph& graph) { + int ret; + sys::Path tempDir(sys::Path::GetTemporaryDirectory()); + + try { + ret = graph.Build(tempDir); + } + catch(...) { + tempDir.eraseFromDisk(true); + throw; + } + + tempDir.eraseFromDisk(true); + return ret; + } +} + +int main(int argc, char** argv) { + try { + CompilationGraph graph; + + cl::ParseCommandLineOptions(argc, argv, + "LLVM Compiler Driver(Work In Progress)"); + PopulateCompilationGraph(graph); + return BuildTargets(graph); + } + catch(const std::exception& ex) { + std::cerr << ex.what() << '\n'; + } + catch(...) { + std::cerr << "Unknown error!\n"; + } +} diff --git a/utils/TableGen/LLVMCCConfigurationEmitter.cpp b/utils/TableGen/LLVMCCConfigurationEmitter.cpp new file mode 100644 index 0000000000..9431fe1784 --- /dev/null +++ b/utils/TableGen/LLVMCCConfigurationEmitter.cpp @@ -0,0 +1,935 @@ +//===- LLVMCConfigurationEmitter.cpp - Generate LLVMCC config -------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open +// Source License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This tablegen backend is responsible for emitting LLVMCC configuration code. +// +//===----------------------------------------------------------------------===// + +#include "LLVMCCConfigurationEmitter.h" +#include "Record.h" + +#include "llvm/ADT/IntrusiveRefCntPtr.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/Support/Streams.h" + +#include <algorithm> +#include <cassert> +#include <functional> +#include <string> + +using namespace llvm; + +namespace { + +//===----------------------------------------------------------------------===// +/// Typedefs + +typedef std::vector<Record*> RecordVector; +typedef std::vector<std::string> StrVector; + +//===----------------------------------------------------------------------===// +/// Constants + +// Indentation strings +const char * Indent1 = " "; +const char * Indent2 = " "; +const char * Indent3 = " "; +const char * Indent4 = " "; + +// Default help string +const char * DefaultHelpString = "NO HELP MESSAGE PROVIDED"; + +// Name for the "sink" option +const char * SinkOptionName = "AutoGeneratedSinkOption"; + +//===----------------------------------------------------------------------===// +/// Helper functions + +std::string InitPtrToString(Init* ptr) { + StringInit& val = dynamic_cast<StringInit&>(*ptr); + return val.getValue(); +} + +//===----------------------------------------------------------------------===// +/// Back-end specific code + +// A command-line option can have one of the following types: +// +// Switch - a simple switch w/o arguments, e.g. -O2 +// +// Parameter - an option that takes one(and only one) argument, e.g. -o file, +// --output=file +// +// ParameterList - same as Parameter, but more than one occurence +// of the option is allowed, e.g. -lm -lpthread +// +// Prefix - argument is everything after the prefix, +// e.g. -Wa,-foo,-bar, -DNAME=VALUE +// +// PrefixList - same as Prefix, but more than one option occurence is +// allowed + +namespace OptionType { + enum OptionType { Switch, Parameter, ParameterList, Prefix, PrefixList}; +} + +bool IsListOptionType (OptionType::OptionType t) { + return (t == OptionType::ParameterList || t == OptionType::PrefixList); +} + +// Code duplication here is necessary because one option can affect +// several tools and those tools may have different actions associated +// with this option. GlobalOptionDescriptions are used to generate +// the option registration code, while ToolOptionDescriptions are used +// to generate tool-specific code. + +// Base class for option descriptions + +struct OptionDescription { + OptionType::OptionType Type; + std::string Name; + + OptionDescription(OptionType::OptionType t = OptionType::Switch, + const std::string& n = "") + : Type(t), Name(n) + {} + + const char* GenTypeDeclaration() const { + switch (Type) { + case OptionType::PrefixList: + case OptionType::ParameterList: + return "cl::list<std::string>"; + case OptionType::Switch: + return "cl::opt<bool>"; + case OptionType::Parameter: + case OptionType::Prefix: + default: + return "cl::opt<std::string>"; + } + } + + std::string GenVariableName() const { + switch (Type) { + case OptionType::Switch: + return "AutoGeneratedSwitch" + Name; + case OptionType::Prefix: + return "AutoGeneratedPrefix" + Name; + case OptionType::PrefixList: + return "AutoGeneratedPrefixList" + Name; + case OptionType::Parameter: + return "AutoGeneratedParameter" + Name; + case OptionType::ParameterList: + default: + return "AutoGeneratedParameterList" + Name; + } + } + +}; + +// Global option description + +namespace GlobalOptionDescriptionFlags { + enum GlobalOptionDescriptionFlags { Required = 0x1 }; +} + +struct GlobalOptionDescription : public OptionDescription { + std::string Help; + unsigned Flags; + + // StringMap can only store DefaultConstructible objects + GlobalOptionDescription() : OptionDescription(), Flags(0) + {} + + GlobalOptionDescription (OptionType::OptionType t, const std::string& n) + : OptionDescription(t, n), Help(DefaultHelpString), Flags(0) + {} + + bool isRequired() const { + return Flags & GlobalOptionDescriptionFlags::Required; + } + void setRequired() { + Flags |= GlobalOptionDescriptionFlags::Required; + } + + // Merge two option descriptions + void Merge (const GlobalOptionDescription& other) + { + if (other.Type != Type) + throw "Conflicting definitions for the option " + Name + "!"; + + if (Help.empty() && !other.Help.empty()) + Help = other.Help; + else if (!Help.empty() && !other.Help.empty()) + cerr << "Warning: more than one help string defined for option " + + Name + "\n"; + + Flags |= other.Flags; + } +}; + +// A GlobalOptionDescription array +// + some flags affecting generation of option declarations +struct GlobalOptionDescriptions { + typedef StringMap<GlobalOptionDescription> container_type; + typedef container_type::const_iterator const_iterator; + + // A list of GlobalOptionDescriptions + container_type Descriptions; + // Should the emitter generate a "cl::sink" option? + bool HasSink; + + // Support for STL-style iteration + const_iterator begin() const { return Descriptions.begin(); } + const_iterator end() const { return Descriptions.end(); } +}; + + +// Tool-local option description + +// Properties without arguments are implemented as flags +namespace ToolOptionDescriptionFlags { + enum ToolOptionDescriptionFlags { StopCompilation = 0x1, + Forward = 0x2, UnpackValues = 0x4}; +} +namespace OptionPropertyType { + enum OptionPropertyType { AppendCmd }; +} + +typedef std::pair<OptionPropertyType::OptionPropertyType, std::string> +OptionProperty; +typedef SmallVector<OptionProperty, 4> OptionPropertyList; + +struct ToolOptionDescription : public OptionDescription { + unsigned Flags; + OptionPropertyList Props; + + // StringMap can only store DefaultConstructible objects + ToolOptionDescription() : OptionDescription() {} + + ToolOptionDescription (OptionType::OptionType t, const std::string& n) + : OptionDescription(t, n) + {} + + // Various boolean properties + bool isStopCompilation() const { + return Flags & ToolOptionDescriptionFlags::StopCompilation; + } + void setStopCompilation() { + Flags |= ToolOptionDescriptionFlags::StopCompilation; + } + + bool isForward() const { + return Flags & ToolOptionDescriptionFlags::Forward; + } + void setForward() { + Flags |= ToolOptionDescriptionFlags::Forward; + } + + bool isUnpackValues() const { + return Flags & ToolOptionDescriptionFlags::UnpackValues; + } + void setUnpackValues() { + Flags |= ToolOptionDescriptionFlags::UnpackValues; + } + + void AddProperty (OptionPropertyType::OptionPropertyType t, + const std::string& val) + { + Props.push_back(std::make_pair(t, val)); + } +}; + +typedef StringMap<ToolOptionDescription> ToolOptionDescriptions; + +// Tool information record + +namespace ToolFlags { + enum ToolFlags { Join = 0x1, Sink = 0x2 }; +} + +struct ToolProperties : public RefCountedBase<ToolProperties> { + std::string Name; + StrVector CmdLine; + std::string InLanguage; + std::string OutLanguage; + std::string OutputSuffix; + unsigned Flags; + ToolOptionDescriptions OptDescs; + + // Various boolean properties + void setSink() { Flags |= ToolFlags::Sink; } + bool isSink() const { return Flags & ToolFlags::Sink; } + void setJoin() { Flags |= ToolFlags::Join; } + bool isJoin() const { return Flags & ToolFlags::Join; } + + // Default ctor here is needed because StringMap can only store + // DefaultConstructible objects + ToolProperties() {} + ToolProperties (const std::string& n) : Name(n) {} +}; + + +// A list of Tool information records +// IntrusiveRefCntPtrs are used because StringMap has no copy constructor +// (and we want to avoid copying ToolProperties anyway) +typedef std::vector<IntrusiveRefCntPtr<ToolProperties> > ToolPropertiesList; + + +// Function object for iterating over a list of tool property records +class CollectProperties { +private: + + /// Implementation details + + // "Property handler" - a function that extracts information + // about a given tool property from its DAG representation + typedef void (CollectProperties::*PropertyHandler)(DagInit*); + + // Map from property names -> property handlers + typedef StringMap<PropertyHandler> PropertyHandlerMap; + + // "Option property handler" - a function that extracts information + // about a given option property from its DAG representation + typedef void (CollectProperties::* + OptionPropertyHandler)(DagInit*, GlobalOptionDescription &); + + // Map from option property names -> option property handlers + typedef StringMap<OptionPropertyHandler> OptionPropertyHandlerMap; + + // Static maps from strings to CollectProperties methods("handlers") + static PropertyHandlerMap propertyHandlers_; + static OptionPropertyHandlerMap optionPropertyHandlers_; + static bool staticMembersInitialized_; + + + /// This is where the information is stored + + // Current Tool properties + ToolProperties& toolProps_; + // OptionDescriptions table(used to register options globally) + GlobalOptionDescriptions& optDescs_; + +public: + + explicit CollectProperties (ToolProperties& p, GlobalOptionDescriptions& d) + : toolProps_(p), optDescs_(d) + { + if (!staticMembersInitialized_) { + // Init tool property handlers + propertyHandlers_["cmd_line"] = &CollectProperties::onCmdLine; + propertyHandlers_["in_language"] = &CollectProperties::onInLanguage; + propertyHandlers_["join"] = &CollectProperties::onJoin; + propertyHandlers_["out_language"] = &CollectProperties::onOutLanguage; + propertyHandlers_["output_suffix"] = &CollectProperties::onOutputSuffix; + propertyHandlers_["parameter_option"] + = &CollectProperties::onParameter; + propertyHandlers_["parameter_list_option"] = + &CollectProperties::onParameterList; + propertyHandlers_["prefix_option"] = &CollectProperties::onPrefix; + propertyHandlers_["prefix_list_option"] = + &CollectProperties::onPrefixList; + propertyHandlers_["sink"] = &CollectProperties::onSink; + propertyHandlers_["switch_option"] = &CollectProperties::onSwitch; + + // Init option property handlers + optionPropertyHandlers_["append_cmd"] = &CollectProperties::onAppendCmd; + optionPropertyHandlers_["forward"] = &CollectProperties::onForward; + optionPropertyHandlers_["help"] = &CollectProperties::onHelp; + optionPropertyHandlers_["required"] = &CollectProperties::onRequired; + optionPropertyHandlers_["stop_compilation"] = + &CollectProperties::onStopCompilation; + optionPropertyHandlers_["unpack_values"] = + &CollectProperties::onUnpackValues; + + staticMembersInitialized_ = true; + } + } + + // Gets called for every tool property; + // Just forwards to the corresponding property handler. + void operator() (Init* i) { + DagInit& d = dynamic_cast<DagInit&>(*i); + std::string property_name = d.getOperator()->getAsString(); + PropertyHandlerMap::iterator method + = propertyHandlers_.find(property_name); + + if (method != propertyHandlers_.end()) { + PropertyHandler h = method->second; + (this->*h)(&d); + } + else { + throw "Unknown tool property: " + property_name + "!"; + } + } + +private: + + /// Property handlers -- + /// Functions that extract information about tool properties from + /// DAG representation. + + void onCmdLine (DagInit* d) { + checkNumberOfArguments(d, 1); + SplitString(InitPtrToString(d->getArg(0)), toolProps_.CmdLine); + if (toolProps_.CmdLine.empty()) + throw std::string("Tool " + toolProps_.Name + " has empty command line!"); + } + + void onInLanguage (DagInit* d) { + checkNumberOfArguments(d, 1); + toolProps_.InLanguage = InitPtrToString(d->getArg(0)); + } + + void onJoin (DagInit* d) { + checkNumberOfArguments(d, 0); + toolProps_.setJoin(); + } + + void onOutLanguage (DagInit* d) { + checkNumberOfArguments(d, 1); + toolProps_.OutLanguage = InitPtrToString(d->getArg(0)); + } + + void onOutputSuffix (DagInit* d) { + checkNumberOfArguments(d, 1); + toolProps_.OutputSuffix = InitPtrToString(d->getArg(0)); + } + + void onSink (DagInit* d) { + checkNumberOfArguments(d, 0); + optDescs_.HasSink = true; + toolProps_.setSink(); + } + + void onSwitch (DagInit* d) { addOption(d, OptionType::Switch); } + void onParameter (DagInit* d) { addOption(d, OptionType::Parameter); } + void onParameterList (DagInit* d) { addOption(d, OptionType::ParameterList); } + void onPrefix (DagInit* d) { addOption(d, OptionType::Prefix); } + void onPrefixList (DagInit* d) { addOption(d, OptionType::PrefixList); } + + /// Option property handlers -- + /// Methods that handle properties that are common for all types of + /// options (like append_cmd, stop_compilation) + + void onAppendCmd (DagInit* d, GlobalOptionDescription& o) { + checkNumberOfArguments(d, 1); + std::string const& cmd = InitPtrToString(d->getArg(0)); + + toolProps_.OptDescs[o.Name].AddProperty(OptionPropertyType::AppendCmd, cmd); + } + + void onForward (DagInit* d, GlobalOptionDescription& o) { + checkNumberOfArguments(d, 0); + toolProps_.OptDescs[o.Name].setForward(); + } + + void onHelp (DagInit* d, GlobalOptionDescription& o) { + checkNumberOfArguments(d, 1); + const std::string& help_message = InitPtrToString(d->getArg(0)); + + o.Help = help_message; + } + + void onRequired (DagInit* d, GlobalOptionDescription& o) { + checkNumberOfArguments(d, 0); + o.setRequired(); + } + + void onStopCompilation (DagInit* d, GlobalOptionDescription& o) { + checkNumberOfArguments(d, 0); + if (o.Type != OptionType::Switch) + throw std::string("Only options of type Switch can stop compilation!"); + toolProps_.OptDescs[o.Name].setStopCompilation(); + } + + void onUnpackValues (DagInit* d, GlobalOptionDescription& o) { + checkNumberOfArguments(d, 0); + toolProps_.OptDescs[o.Name].setUnpackValues(); + } + + /// Helper functions + + // Add an option of type t + void addOption (DagInit* d, OptionType::OptionType t) { + checkNumberOfArguments(d, 2); + const std::string& name = InitPtrToString(d->getArg(0)); + + GlobalOptionDescription o(t, name); + toolProps_.OptDescs[name].Type = t; + toolProps_.OptDescs[name].Name = name; + processOptionProperties(d, o); + insertDescription(o); + } + + // Ensure that the number of args in d is <= min_arguments, + // throw exception otherwise + void checkNumberOfArguments (DagInit* d, unsigned min_arguments) { + if (d->getNumArgs() < min_arguments) + throw "Property " + d->getOperator()->getAsString() + + " has too few arguments!"; + } + + // Insert new GlobalOptionDescription into GlobalOptionDescriptions list + void insertDescription (const GlobalOptionDescription& o) + { + if (optDescs_.Descriptions.count(o.Name)) { + GlobalOptionDescription& D = optDescs_.Descriptions[o.Name]; + D.Merge(o); + } + else { + optDescs_.Descriptions[o.Name] = o; + } + } + + // Go through the list of option properties and call a corresponding + // handler for each. + // + // Parameters: + // name - option name + // d - option property list + void processOptionProperties (DagInit* d, GlobalOptionDescription& o) { + // First argument is option name + checkNumberOfArguments(d, 2); + + for (unsigned B = 1, E = d->getNumArgs(); B!=E; ++B) { + DagInit& option_property + = dynamic_cast<DagInit&>(*d->getArg(B)); + const std::string& option_property_name + = option_property.getOperator()->getAsString(); + OptionPropertyHandlerMap::iterator method + = optionPropertyHandlers_.find(option_property_name); + + if (method != optionPropertyHandlers_.end()) { + OptionPropertyHandler h = method->second; + (this->*h)(&option_property, o); + } + else { + throw "Unknown option property: " + option_property_name + "!"; + } + } + } +}; + +// Static members of CollectProperties +CollectProperties::PropertyHandlerMap +CollectProperties::propertyHandlers_; + +CollectProperties::OptionPropertyHandlerMap +CollectProperties::optionPropertyHandlers_; + +bool CollectProperties::staticMembersInitialized_ = false; + + +// Gather information from the parsed TableGen data +// (Basically a wrapper for CollectProperties) +void CollectToolProperties (RecordVector::const_iterator B, + RecordVector::const_iterator E, + ToolPropertiesList& TPList, + GlobalOptionDescriptions& OptDescs) +{ + // Iterate over a properties list of every Tool definition + for (;B!=E;++B) { + RecordVector::value_type T = *B; + ListInit* PropList = T->getValueAsListInit("properties"); + if (!PropList) + throw std::string("Tool has no property list!"); + + IntrusiveRefCntPtr<ToolProperties> + ToolProps(new ToolProperties(T->getName())); + + std::for_each(PropList->begin(), PropList->end(), + CollectProperties(*ToolProps, OptDescs)); + TPList.push_back(ToolProps); + } +} + +// Used by EmitGenerateActionMethod +void EmitOptionPropertyHandlingCode (const ToolProperties& P, + const ToolOptionDescription& D, + std::ostream& O) +{ + // if clause + O << Indent2 << "if ("; + if (D.Type == OptionType::Switch) + O << D.GenVariableName(); + else + O << '!' << D.GenVariableName() << ".empty()"; + + O <<") {\n"; + + // Handle option properties that take an argument + for (OptionPropertyList::const_iterator B = D.Props.begin(), + E = D.Props.end(); B!=E; ++B) { + const OptionProperty& val = *B; + + switch (val.first) { + // (append_cmd cmd) property + case OptionPropertyType::AppendCmd: + O << Indent3 << "vec.push_back(\"" << val.second << "\");\n"; + break; + // Other properties with argument + default: + break; + } + } + + // Handle flags + + // (forward) property + if (D.isForward()) { + switch (D.Type) { + case OptionType::Switch: + O << Indent3 << "vec.push_back(\"-" << D.Name << "\");\n"; + break; + case OptionType::Parameter: + O << Indent3 << "vec.push_back(\"-" << D.Name << "\");\n"; + O << Indent3 << "vec.push_back(" << D.GenVariableName() << ");\n"; + break; + case OptionType::Prefix: + O << Indent3 << "vec.push_back(\"-" << D.Name << "\" + " + << D.GenVariableName() << ");\n"; + break; + case OptionType::PrefixList: + O << Indent3 << "for (" << D.GenTypeDeclaration() + << "::iterator B = " << D.GenVariableName() << ".begin(),\n" + << Indent3 << "E = " << D.GenVariableName() << ".end(); B != E; ++B)\n" + << Indent4 << "vec.push_back(\"-" << D.Name << "\" + " + << "*B);\n"; + break; + case OptionType::ParameterList: + O << Indent3 << "for (" << D.GenTypeDeclaration() + << "::iterator B = " << D.GenVariableName() << ".begin(),\n" + << Indent3 << "E = " << D.GenVariableName() + << ".end() ; B != E; ++B) {\n" + << Indent4 << "vec.push_back(\"-" << D.Name << "\");\n" + << Indent4 << "vec.push_back(*B);\n" + << Indent3 << "}\n"; + break; + } + } + + // (unpack_values) property + if (D.isUnpackValues()) { + if (IsListOptionType(D.Type)) { + O << Indent3 << "for (" << D.GenTypeDeclaration() + << "::iterator B = " << D.GenVariableName() << ".begin(),\n" + << Indent3 << "E = " << D.GenVariableName() + << ".end(); B != E; ++B)\n" + << Indent4 << "UnpackValues(*B, vec);\n"; + } + else if (D.Type == OptionType::Prefix || D.Type == OptionType::Parameter){ + O << Indent3 << "UnpackValues(" + << D.GenVariableName() << ", vec);\n"; + } + else { + // TOFIX: move this to the type-checking phase + throw std::string("Switches can't have unpack_values property!"); + } + } + + // close if clause + O << Indent2 << "}\n"; +} + +// Emite one of two versions of GenerateAction method +void EmitGenerateActionMethod (const ToolProperties& P, int V, std::ostream& O) +{ + assert(V==1 || V==2); + if (V==1) + O << Indent1 << "Action GenerateAction(const PathVector& inFiles,\n"; + else + O << Indent1 << "Action GenerateAction(const sys::Path& inFile,\n"; + + O << Indent2 << "const sys::Path& outFile) const\n" + << Indent1 << "{\n" + << Indent2 << "std::vector<std::string> vec;\n"; + + // Parse CmdLine tool property + StrVector::const_iterator I = P.CmdLine.begin(); + ++I; + for (StrVector::const_iterator E = P.CmdLine.end(); I != E; ++I) { + const std::string& cmd = *I; + O << Indent2; + if (cmd == "$INFILE") { + if (V==1) + O << "for (PathVector::const_iterator B = inFiles.begin()" + << ", E = inFiles.end();\n" + << Indent2 << "B != E; ++B)\n" + << Indent3 << "vec.push_back(B->toString());\n"; + else + O << "vec.push_back(inFile.toString());\n"; + } + else if (cmd == "$OUTFILE") { + O << "vec.push_back(outFile.toString());\n"; + } + else { + O << "vec.push_back(\"" << cmd << "\");\n"; + } + } + + // For every understood option, emit handling code + for (ToolOptionDescriptions::const_iterator B = P.OptDescs.begin(), + E = P.OptDescs.end(); B != E; ++B) { + const ToolOptionDescription& val = B->second; + EmitOptionPropertyHandlingCode(P, val, O); + } + + // Handle Sink property + if (P.isSink()) { + O << Indent2 << "if (!" << SinkOptionName << ".empty()) {\n" + << Indent3 << "vec.insert(vec.end(), " + << SinkOptionName << ".begin(), " << SinkOptionName << ".end());\n" + << Indent2 << "}\n"; + } + + O << Indent2 << "return Action(\"" << P.CmdLine.at(0) << "\", vec);\n" + << Indent1 << "}\n\n"; +} + +// Emit GenerateAction methods for Tool classes +void EmitGenerateActionMethods (const ToolProperties& P, std::ostream& O) { + + if (!P.isJoin()) + O << Indent1 << "Action GenerateAction(const PathVector& inFiles,\n" + << Indent2 << "const llvm::sys::Path& outFile) const\n" + << Indent1 << "{\n" + << Indent2 << "throw std::runtime_error(\"" << P.Name + << " is not a Join tool!\");\n" + << Indent1 << "}\n\n"; + else + EmitGenerateActionMethod(P, 1, O); + + EmitGenerateActionMethod(P, 2, O); +} + +// Emit IsLast() method for Tool classes +void EmitIsLastMethod (const ToolProperties& P, std::ostream& O) { + O << Indent1 << "bool IsLast() const {\n" + << Indent2 << "bool last = false;\n"; + + for (ToolOptionDescriptions::const_iterator B = P.OptDescs.begin(), + E = P.OptDescs.end(); B != E; ++B) { + const ToolOptionDescription& val = B->second; + + if (val.isStopCompilation()) + O << Indent2 + << "if (" << val.GenVariableName() + << ")\n" << Indent3 << "last = true;\n"; + } + + O << Indent2 << "return last;\n" + << Indent1 << "}\n\n"; +} + +// Emit static [Input,Output]Language() methods for Tool classes +void EmitInOutLanguageMethods (const ToolProperties& P, std::ostream& O) { + O << Indent1 << "std::string InputLanguage() const {\n" + << Indent2 << "return \"" << P.InLanguage << "\";\n" + << Indent1 << "}\n\n"; + + O << Indent1 << "std::string OutputLanguage() const {\n" + << Indent2 << "return \"" << P.OutLanguage << "\";\n" + << Indent1 << "}\n\n"; +} + +// Emit static [Input,Output]Language() methods for Tool classes +void EmitOutputSuffixMethod (const ToolProperties& P, std::ostream& O) { + O << Indent1 << "std::string OutputSuffix() const {\n" + << Indent2 << "return \"" << P.OutputSuffix << "\";\n" + << Indent1 << "}\n\n"; +} + +// Emit static Name() method for Tool classes +void EmitNameMethod (const ToolProperties& P, std::ostream& O) { + O << Indent1 << "std::string Name() const {\n" + << Indent2 << "return \"" << P.Name << "\";\n" + << Indent1 << "}\n\n"; +} + +// Emit static Name() method for Tool classes +void EmitIsJoinMethod (const ToolProperties& P, std::ostream& O) { + O << Indent1 << "bool IsJoin() const {\n"; + if (P.isJoin()) + O << Indent2 << "return true;\n"; + else + O << Indent2 << "return false;\n"; + O << Indent1 << "}\n\n"; +} + +// Emit a Tool class definition +void EmitToolClassDefinition (const ToolProperties& P, std::ostream& O) { + // Header + O << "class " << P.Name << " : public Tool {\n" + << "public:\n"; + + EmitNameMethod(P, O); + EmitInOutLanguageMethods(P, O); + EmitOutputSuffixMethod(P, O); + EmitIsJoinMethod(P, O); + EmitGenerateActionMethods(P, O); + EmitIsLastMethod(P, O); + + // Close class definition + O << "};\n\n"; +} + +// Iterate over a list of option descriptions and emit registration code +void EmitOptionDescriptions (const GlobalOptionDescriptions& descs, + std::ostream& O) +{ + // Emit static cl::Option variables + for (GlobalOptionDescriptions::const_iterator B = descs.begin(), + E = descs.end(); B!=E; ++B) { + const GlobalOptionDescription& val = B->second; + + O << val.GenTypeDeclaration() << ' ' + << val.GenVariableName() + << "(\"" << val.Name << '\"'; + + if (val.Type == OptionType::Prefix || val.Type == OptionType::PrefixList) + O << ", cl::Prefix"; + + if (val.isRequired()) { + switch (val.Type) { + case OptionType::PrefixList: + case OptionType::ParameterList: + O << ", cl::OneOrMore"; + break; + default: + O << ", cl::Required"; + } + } + + O << ", cl::desc(\"" << val.Help << "\"));\n"; + } + + if (descs.HasSink) + O << "cl::list<std::string> " << SinkOptionName << "(cl::Sink);\n"; + + O << '\n'; +} + +void EmitPopulateLanguageMap (const RecordKeeper& Records, std::ostream& O) +{ + // Get the relevant field out of RecordKeeper + Record* LangMapRecord = Records.getDef("LanguageMap"); + if (!LangMapRecord) + throw std::string("Language map definition not found!"); + + ListInit* LangsToSuffixesList = LangMapRecord->getValueAsListInit("map"); + if (!LangsToSuffixesList) + throw std::string("Error in the language map definition!"); + + // Generate code + O << "void llvmcc::PopulateLanguageMap(LanguageMap& language_map) {\n"; + + for (unsigned i = 0; i < LangsToSuffixesList->size(); ++i) { + Record* LangToSuffixes = LangsToSuffixesList->getElementAsRecord(i); + + const std::string& Lang = LangToSuffixes->getValueAsString("lang"); + const ListInit* Suffixes = LangToSuffixes->getValueAsListInit("suffixes"); + + for (unsigned i = 0; i < Suffixes->size(); ++i) + O << Indent1 << "language_map[\"" + << InitPtrToString(Suffixes->getElement(i)) + << "\"] = \"" << Lang << "\";\n"; + } + + O << "}\n\n"; +} + +void EmitPopulateCompilationGraph (const RecordKeeper& Records, + StringMap<std::string>& ToolToLang, + std::ostream& O) +{ + // Get the relevant field out of RecordKeeper + Record* ToolChains = Records.getDef("ToolChains"); + if (!ToolChains) + throw std::string("No ToolChains specification found!"); + ListInit* chains = ToolChains->getValueAsListInit("chains"); + if (!chains) + throw std::string("Error in toolchain list definition!"); + + // Generate code + O << "void llvmcc::PopulateCompilationGraph(CompilationGraph& G) {\n" + << Indent1 << "PopulateLanguageMap(G.ExtsToLangs);\n" + << Indent1 << "std::vector<IntrusiveRefCntPtr<Tool> > vec;\n\n"; + + for (unsigned i = 0; i < chains->size(); ++i) { + Record* ToolChain = chains->getElementAsRecord(i); + ListInit* Tools = ToolChain->getValueAsListInit("tools"); + + // Get name of the first tool in the list + const std::string& firstTool = + dynamic_cast<DefInit&>(**Tools->begin()).getDef()->getName(); + + for (ListInit::iterator B = Tools->begin(), + E = Tools->end(); B != E; ++B) { + Record* val = dynamic_cast<DefInit&>(**B).getDef(); + O << Indent1 << "vec.push_back(IntrusiveRefCntPtr<Tool>(new " + << val->getName() << "()));\n"; + } + O << Indent1 << "G.ToolChains[\"" << ToolToLang[firstTool] + << "\"] = vec;\n"; + O << Indent1 << "vec.clear();\n\n"; + } + + O << "}\n\n"; +} + +void FillInToolToLang (const ToolPropertiesList& T, + StringMap<std::string>& M) { + for (ToolPropertiesList::const_iterator B = T.begin(), E = T.end(); + B != E; ++B) { + const ToolProperties& P = *(*B); + M[P.Name] = P.InLanguage; + } +} + +// End of anonymous namespace +} + +// Back-end entry point +void LLVMCCConfigurationEmitter::run (std::ostream &O) { + // Emit file header + EmitSourceFileHeader("LLVMCC Configuration Library", O); + + // Get a list of all defined Tools + RecordVector Tools = Records.getAllDerivedDefinitions("Tool"); + if (Tools.empty()) + throw std::string("No tool definitions found!"); + + // Gather information from the Tool descriptions + ToolPropertiesList tool_props; + GlobalOptionDescriptions opt_descs; + CollectToolProperties(Tools.begin(), Tools.end(), tool_props, opt_descs); + + // Emit global option registration code + EmitOptionDescriptions(opt_descs, O); + + // Emit PopulateLanguageMap function + // (a language map maps from file extensions to language names) + EmitPopulateLanguageMap(Records, O); + + // Emit Tool classes + for (ToolPropertiesList::const_iterator B = tool_props.begin(), + E = tool_props.end(); B!=E; ++B) + EmitToolClassDefinition(*(*B), O); + + // Fill in table that maps tool names to languages + StringMap<std::string> ToolToLang; + FillInToolToLang(tool_props, ToolToLang); + + // Emit PopulateCompilationGraph function + EmitPopulateCompilationGraph(Records, ToolToLang, O); + + // EOF +} diff --git a/utils/TableGen/LLVMCCConfigurationEmitter.h b/utils/TableGen/LLVMCCConfigurationEmitter.h new file mode 100644 index 0000000000..849f054013 --- /dev/null +++ b/utils/TableGen/LLVMCCConfigurationEmitter.h @@ -0,0 +1,30 @@ +//===- LLVMCConfigurationEmitter.cpp - Generate LLVMCC config -------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open +// Source License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This tablegen backend is responsible for emitting LLVMCC configuration code. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVMCCCONF_EMITTER_H +#define LLVMCCCONF_EMITTER_H + +#include "TableGenBackend.h" + +namespace llvm { + class LLVMCCConfigurationEmitter : public TableGenBackend { + RecordKeeper &Records; + public: + explicit LLVMCCConfigurationEmitter(RecordKeeper &R) : Records(R) {} + + // run - Output the asmwriter, returning true on failure. + void run(std::ostream &o); + }; +} + +#endif //LLVMCCCONF_EMITTER_H diff --git a/utils/TableGen/TableGen.cpp b/utils/TableGen/TableGen.cpp index b174ff8903..7f8987d5e6 100644 --- a/utils/TableGen/TableGen.cpp +++ b/utils/TableGen/TableGen.cpp @@ -31,6 +31,7 @@ #include "DAGISelEmitter.h" #include "SubtargetEmitter.h" #include "IntrinsicEmitter.h" +#include "LLVMCCConfigurationEmitter.h" #include <algorithm> #include <cstdio> #include <fstream> @@ -41,11 +42,12 @@ enum ActionType { PrintRecords, GenEmitter, GenRegisterEnums, GenRegister, GenRegisterHeader, - GenInstrEnums, GenInstrs, GenAsmWriter, + GenInstrEnums, GenInstrs, GenAsmWriter, GenCallingConv, GenDAGISel, GenSubtarget, GenIntrinsic, + GenLLVMCCConf, PrintEnums }; @@ -76,6 +78,8 @@ namespace { "Generate subtarget enumerations"), clEnumValN(GenIntrinsic, "gen-intrinsic", "Generate intrinsic information"), + clEnumValN(GenLLVMCCConf, "gen-llvmcc", + "Generate LLVMCC configuration library"), clEnumValN(PrintEnums, "print-enums", "Print enum values for a class"), clEnumValEnd)); @@ -180,6 +184,9 @@ int main(int argc, char **argv) { case GenIntrinsic: IntrinsicEmitter(Records).run(*Out); break; + case GenLLVMCCConf: + LLVMCCConfigurationEmitter(Records).run(*Out); + break; case PrintEnums: { std::vector<Record*> Recs = Records.getAllDerivedDefinitions(Class); |