diff options
author | ed <ed@FreeBSD.org> | 2009-06-02 17:52:33 +0000 |
---|---|---|
committer | ed <ed@FreeBSD.org> | 2009-06-02 17:52:33 +0000 |
commit | 3277b69d734b9c90b44ebde4ede005717e2c3b2e (patch) | |
tree | 64ba909838c23261cace781ece27d106134ea451 /tools | |
download | FreeBSD-src-3277b69d734b9c90b44ebde4ede005717e2c3b2e.zip FreeBSD-src-3277b69d734b9c90b44ebde4ede005717e2c3b2e.tar.gz |
Import LLVM, at r72732.
Diffstat (limited to 'tools')
116 files changed, 16157 insertions, 0 deletions
diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt new file mode 100644 index 0000000..113d987 --- /dev/null +++ b/tools/CMakeLists.txt @@ -0,0 +1,36 @@ +# NOTE: The tools are organized into five groups of four consisting of one +# large and three small executables. This is done to minimize memory load +# in parallel builds. Please retain this ordering. + +if( NOT MSVC ) + add_subdirectory(llvm-config) +endif( NOT MSVC ) + +add_subdirectory(opt) +add_subdirectory(llvm-as) +add_subdirectory(llvm-dis) + +add_subdirectory(llc) +add_subdirectory(llvm-ranlib) +add_subdirectory(llvm-ar) +add_subdirectory(llvm-nm) + +add_subdirectory(llvm-ld) +add_subdirectory(llvm-prof) +add_subdirectory(llvm-link) +add_subdirectory(lli) + +# gccas and gccld are deprecated: +# add_subdirectory(gccas) +# add_subdirectory(gccld) +add_subdirectory(llvm-extract) +add_subdirectory(llvm-db) + +add_subdirectory(bugpoint) +add_subdirectory(llvm-bcanalyzer) +add_subdirectory(llvm-stub) +add_subdirectory(llvmc) + +if( EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/clang/CMakeLists.txt ) + add_subdirectory( ${CMAKE_CURRENT_SOURCE_DIR}/clang ) +endif( EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/clang/CMakeLists.txt ) diff --git a/tools/Makefile b/tools/Makefile new file mode 100644 index 0000000..b3c015f --- /dev/null +++ b/tools/Makefile @@ -0,0 +1,45 @@ +##===- tools/Makefile --------------------------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LEVEL := .. + +# Build clang if present. +OPTIONAL_PARALLEL_DIRS := clang + +# NOTE: The tools are organized into five groups of four consisting of one +# large and three small executables. This is done to minimize memory load +# in parallel builds. Please retain this ordering. +DIRS := llvm-config +PARALLEL_DIRS := opt llvm-as llvm-dis \ + llc llvm-ranlib llvm-ar llvm-nm \ + llvm-ld llvm-prof llvm-link \ + lli gccas gccld llvm-extract llvm-db \ + bugpoint llvm-bcanalyzer llvm-stub llvmc + +# Let users override the set of tools to build from the command line. +ifdef ONLY_TOOLS + OPTIONAL_PARALLEL_DIRS := + PARALLEL_DIRS := $(ONLY_TOOLS) +endif + +include $(LEVEL)/Makefile.config + +ifeq ($(ENABLE_PIC),1) + DIRS += lto + ifdef BINUTILS_INCDIR + DIRS += gold + endif +endif + +# No support for lto / gold on windows targets +ifeq ($(OS), $(filter $(OS), Cygwin MingW)) + DIRS := $(filter-out lto gold, $(DIRS)) +endif + +include $(LEVEL)/Makefile.common diff --git a/tools/bugpoint/BugDriver.cpp b/tools/bugpoint/BugDriver.cpp new file mode 100644 index 0000000..d050b59 --- /dev/null +++ b/tools/bugpoint/BugDriver.cpp @@ -0,0 +1,241 @@ +//===- BugDriver.cpp - Top-Level BugPoint class implementation ------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This class contains all of the shared state and information that is used by +// the BugPoint tool to track down errors in optimizations. This class is the +// main driver class that invokes all sub-functionality. +// +//===----------------------------------------------------------------------===// + +#include "BugDriver.h" +#include "ToolRunner.h" +#include "llvm/Linker.h" +#include "llvm/Module.h" +#include "llvm/Pass.h" +#include "llvm/Assembly/Parser.h" +#include "llvm/Bitcode/ReaderWriter.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/FileUtilities.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/raw_ostream.h" +#include <iostream> +#include <memory> +using namespace llvm; + +// Anonymous namespace to define command line options for debugging. +// +namespace { + // Output - The user can specify a file containing the expected output of the + // program. If this filename is set, it is used as the reference diff source, + // otherwise the raw input run through an interpreter is used as the reference + // source. + // + cl::opt<std::string> + OutputFile("output", cl::desc("Specify a reference program output " + "(for miscompilation detection)")); +} + +/// setNewProgram - If we reduce or update the program somehow, call this method +/// to update bugdriver with it. This deletes the old module and sets the +/// specified one as the current program. +void BugDriver::setNewProgram(Module *M) { + delete Program; + Program = M; +} + + +/// getPassesString - Turn a list of passes into a string which indicates the +/// command line options that must be passed to add the passes. +/// +std::string llvm::getPassesString(const std::vector<const PassInfo*> &Passes) { + std::string Result; + for (unsigned i = 0, e = Passes.size(); i != e; ++i) { + if (i) Result += " "; + Result += "-"; + Result += Passes[i]->getPassArgument(); + } + return Result; +} + +BugDriver::BugDriver(const char *toolname, bool as_child, bool find_bugs, + unsigned timeout, unsigned memlimit) + : ToolName(toolname), ReferenceOutputFile(OutputFile), + Program(0), Interpreter(0), SafeInterpreter(0), gcc(0), + run_as_child(as_child), + run_find_bugs(find_bugs), Timeout(timeout), MemoryLimit(memlimit) {} + + +/// ParseInputFile - Given a bitcode or assembly input filename, parse and +/// return it, or return null if not possible. +/// +Module *llvm::ParseInputFile(const std::string &Filename) { + std::auto_ptr<MemoryBuffer> Buffer(MemoryBuffer::getFileOrSTDIN(Filename)); + Module *Result = 0; + if (Buffer.get()) + Result = ParseBitcodeFile(Buffer.get()); + + ParseError Err; + if (!Result && !(Result = ParseAssemblyFile(Filename, Err))) { + Err.PrintError("bugpoint", errs()); + Result = 0; + } + + return Result; +} + +// This method takes the specified list of LLVM input files, attempts to load +// them, either as assembly or bitcode, then link them together. It returns +// true on failure (if, for example, an input bitcode file could not be +// parsed), and false on success. +// +bool BugDriver::addSources(const std::vector<std::string> &Filenames) { + assert(Program == 0 && "Cannot call addSources multiple times!"); + assert(!Filenames.empty() && "Must specify at least on input filename!"); + + try { + // Load the first input file. + Program = ParseInputFile(Filenames[0]); + if (Program == 0) return true; + + if (!run_as_child) + std::cout << "Read input file : '" << Filenames[0] << "'\n"; + + for (unsigned i = 1, e = Filenames.size(); i != e; ++i) { + std::auto_ptr<Module> M(ParseInputFile(Filenames[i])); + if (M.get() == 0) return true; + + if (!run_as_child) + std::cout << "Linking in input file: '" << Filenames[i] << "'\n"; + std::string ErrorMessage; + if (Linker::LinkModules(Program, M.get(), &ErrorMessage)) { + std::cerr << ToolName << ": error linking in '" << Filenames[i] << "': " + << ErrorMessage << '\n'; + return true; + } + } + } catch (const std::string &Error) { + std::cerr << ToolName << ": error reading input '" << Error << "'\n"; + return true; + } + + if (!run_as_child) + std::cout << "*** All input ok\n"; + + // All input files read successfully! + return false; +} + + + +/// run - The top level method that is invoked after all of the instance +/// variables are set up from command line arguments. +/// +bool BugDriver::run() { + // The first thing to do is determine if we're running as a child. If we are, + // then what to do is very narrow. This form of invocation is only called + // from the runPasses method to actually run those passes in a child process. + if (run_as_child) { + // Execute the passes + return runPassesAsChild(PassesToRun); + } + + if (run_find_bugs) { + // Rearrange the passes and apply them to the program. Repeat this process + // until the user kills the program or we find a bug. + return runManyPasses(PassesToRun); + } + + // If we're not running as a child, the first thing that we must do is + // determine what the problem is. Does the optimization series crash the + // compiler, or does it produce illegal code? We make the top-level + // decision by trying to run all of the passes on the the input program, + // which should generate a bitcode file. If it does generate a bitcode + // file, then we know the compiler didn't crash, so try to diagnose a + // miscompilation. + if (!PassesToRun.empty()) { + std::cout << "Running selected passes on program to test for crash: "; + if (runPasses(PassesToRun)) + return debugOptimizerCrash(); + } + + // Set up the execution environment, selecting a method to run LLVM bitcode. + if (initializeExecutionEnvironment()) return true; + + // Test to see if we have a code generator crash. + std::cout << "Running the code generator to test for a crash: "; + try { + compileProgram(Program); + std::cout << '\n'; + } catch (ToolExecutionError &TEE) { + std::cout << TEE.what(); + return debugCodeGeneratorCrash(); + } + + + // Run the raw input to see where we are coming from. If a reference output + // was specified, make sure that the raw output matches it. If not, it's a + // problem in the front-end or the code generator. + // + bool CreatedOutput = false; + if (ReferenceOutputFile.empty()) { + std::cout << "Generating reference output from raw program: "; + if(!createReferenceFile(Program)){ + return debugCodeGeneratorCrash(); + } + CreatedOutput = true; + } + + // Make sure the reference output file gets deleted on exit from this + // function, if appropriate. + sys::Path ROF(ReferenceOutputFile); + FileRemover RemoverInstance(ROF, CreatedOutput); + + // Diff the output of the raw program against the reference output. If it + // matches, then we assume there is a miscompilation bug and try to + // diagnose it. + std::cout << "*** Checking the code generator...\n"; + try { + if (!diffProgram()) { + std::cout << "\n*** Debugging miscompilation!\n"; + return debugMiscompilation(); + } + } catch (ToolExecutionError &TEE) { + std::cerr << TEE.what(); + return debugCodeGeneratorCrash(); + } + + std::cout << "\n*** Input program does not match reference diff!\n"; + std::cout << "Debugging code generator problem!\n"; + try { + return debugCodeGenerator(); + } catch (ToolExecutionError &TEE) { + std::cerr << TEE.what(); + return debugCodeGeneratorCrash(); + } +} + +void llvm::PrintFunctionList(const std::vector<Function*> &Funcs) { + unsigned NumPrint = Funcs.size(); + if (NumPrint > 10) NumPrint = 10; + for (unsigned i = 0; i != NumPrint; ++i) + std::cout << " " << Funcs[i]->getName(); + if (NumPrint < Funcs.size()) + std::cout << "... <" << Funcs.size() << " total>"; + std::cout << std::flush; +} + +void llvm::PrintGlobalVariableList(const std::vector<GlobalVariable*> &GVs) { + unsigned NumPrint = GVs.size(); + if (NumPrint > 10) NumPrint = 10; + for (unsigned i = 0; i != NumPrint; ++i) + std::cout << " " << GVs[i]->getName(); + if (NumPrint < GVs.size()) + std::cout << "... <" << GVs.size() << " total>"; + std::cout << std::flush; +} diff --git a/tools/bugpoint/BugDriver.h b/tools/bugpoint/BugDriver.h new file mode 100644 index 0000000..96e9fb9 --- /dev/null +++ b/tools/bugpoint/BugDriver.h @@ -0,0 +1,322 @@ +//===- BugDriver.h - Top-Level BugPoint class -------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This class contains all of the shared state and information that is used by +// the BugPoint tool to track down errors in optimizations. This class is the +// main driver class that invokes all sub-functionality. +// +//===----------------------------------------------------------------------===// + +#ifndef BUGDRIVER_H +#define BUGDRIVER_H + +#include "llvm/ADT/DenseMap.h" +#include <vector> +#include <string> + +namespace llvm { + +class Value; +class PassInfo; +class Module; +class GlobalVariable; +class Function; +class BasicBlock; +class AbstractInterpreter; +class Instruction; + +class DebugCrashes; + +class GCC; + +extern bool DisableSimplifyCFG; + +/// BugpointIsInterrupted - Set to true when the user presses ctrl-c. +/// +extern bool BugpointIsInterrupted; + +class BugDriver { + const std::string ToolName; // Name of bugpoint + std::string ReferenceOutputFile; // Name of `good' output file + Module *Program; // The raw program, linked together + std::vector<const PassInfo*> PassesToRun; + AbstractInterpreter *Interpreter; // How to run the program + AbstractInterpreter *SafeInterpreter; // To generate reference output, etc. + GCC *gcc; + bool run_as_child; + bool run_find_bugs; + unsigned Timeout; + unsigned MemoryLimit; + + // FIXME: sort out public/private distinctions... + friend class ReducePassList; + friend class ReduceMisCodegenFunctions; + +public: + BugDriver(const char *toolname, bool as_child, bool find_bugs, + unsigned timeout, unsigned memlimit); + + const std::string &getToolName() const { return ToolName; } + + // Set up methods... these methods are used to copy information about the + // command line arguments into instance variables of BugDriver. + // + bool addSources(const std::vector<std::string> &FileNames); + template<class It> + void addPasses(It I, It E) { PassesToRun.insert(PassesToRun.end(), I, E); } + void setPassesToRun(const std::vector<const PassInfo*> &PTR) { + PassesToRun = PTR; + } + const std::vector<const PassInfo*> &getPassesToRun() const { + return PassesToRun; + } + + /// run - The top level method that is invoked after all of the instance + /// variables are set up from command line arguments. The \p as_child argument + /// indicates whether the driver is to run in parent mode or child mode. + /// + bool run(); + + /// debugOptimizerCrash - This method is called when some optimizer pass + /// crashes on input. It attempts to prune down the testcase to something + /// reasonable, and figure out exactly which pass is crashing. + /// + bool debugOptimizerCrash(const std::string &ID = "passes"); + + /// debugCodeGeneratorCrash - This method is called when the code generator + /// crashes on an input. It attempts to reduce the input as much as possible + /// while still causing the code generator to crash. + bool debugCodeGeneratorCrash(); + + /// debugMiscompilation - This method is used when the passes selected are not + /// crashing, but the generated output is semantically different from the + /// input. + bool debugMiscompilation(); + + /// debugPassMiscompilation - This method is called when the specified pass + /// miscompiles Program as input. It tries to reduce the testcase to + /// something that smaller that still miscompiles the program. + /// ReferenceOutput contains the filename of the file containing the output we + /// are to match. + /// + bool debugPassMiscompilation(const PassInfo *ThePass, + const std::string &ReferenceOutput); + + /// compileSharedObject - This method creates a SharedObject from a given + /// BitcodeFile for debugging a code generator. + /// + std::string compileSharedObject(const std::string &BitcodeFile); + + /// debugCodeGenerator - This method narrows down a module to a function or + /// set of functions, using the CBE as a ``safe'' code generator for other + /// functions that are not under consideration. + bool debugCodeGenerator(); + + /// isExecutingJIT - Returns true if bugpoint is currently testing the JIT + /// + bool isExecutingJIT(); + + /// runPasses - Run all of the passes in the "PassesToRun" list, discard the + /// output, and return true if any of the passes crashed. + bool runPasses(Module *M = 0) { + if (M == 0) M = Program; + std::swap(M, Program); + bool Result = runPasses(PassesToRun); + std::swap(M, Program); + return Result; + } + + Module *getProgram() const { return Program; } + + /// swapProgramIn - Set the current module to the specified module, returning + /// the old one. + Module *swapProgramIn(Module *M) { + Module *OldProgram = Program; + Program = M; + return OldProgram; + } + + AbstractInterpreter *switchToSafeInterpreter() { + AbstractInterpreter *Old = Interpreter; + Interpreter = (AbstractInterpreter*)SafeInterpreter; + return Old; + } + + void switchToInterpreter(AbstractInterpreter *AI) { + Interpreter = AI; + } + + /// setNewProgram - If we reduce or update the program somehow, call this + /// method to update bugdriver with it. This deletes the old module and sets + /// the specified one as the current program. + void setNewProgram(Module *M); + + /// compileProgram - Try to compile the specified module, throwing an + /// exception if an error occurs, or returning normally if not. This is used + /// for code generation crash testing. + /// + void compileProgram(Module *M); + + /// executeProgram - This method runs "Program", capturing the output of the + /// program to a file, returning the filename of the file. A recommended + /// filename may be optionally specified. If there is a problem with the code + /// generator (e.g., llc crashes), this will throw an exception. + /// + std::string executeProgram(std::string RequestedOutputFilename = "", + std::string Bitcode = "", + const std::string &SharedObjects = "", + AbstractInterpreter *AI = 0, + bool *ProgramExitedNonzero = 0); + + /// executeProgramSafely - Used to create reference output with the "safe" + /// backend, if reference output is not provided. If there is a problem with + /// the code generator (e.g., llc crashes), this will throw an exception. + /// + std::string executeProgramSafely(std::string OutputFile = ""); + + /// createReferenceFile - calls compileProgram and then records the output + /// into ReferenceOutputFile. Returns true if reference file created, false + /// otherwise. Note: initializeExecutionEnvironment should be called BEFORE + /// this function. + /// + bool createReferenceFile(Module *M, const std::string &Filename + = "bugpoint.reference.out"); + + /// diffProgram - This method executes the specified module and diffs the + /// output against the file specified by ReferenceOutputFile. If the output + /// is different, true is returned. If there is a problem with the code + /// generator (e.g., llc crashes), this will throw an exception. + /// + bool diffProgram(const std::string &BitcodeFile = "", + const std::string &SharedObj = "", + bool RemoveBitcode = false); + + /// EmitProgressBitcode - This function is used to output the current Program + /// to a file named "bugpoint-ID.bc". + /// + void EmitProgressBitcode(const std::string &ID, bool NoFlyer = false); + + /// deleteInstructionFromProgram - This method clones the current Program and + /// deletes the specified instruction from the cloned module. It then runs a + /// series of cleanup passes (ADCE and SimplifyCFG) to eliminate any code + /// which depends on the value. The modified module is then returned. + /// + Module *deleteInstructionFromProgram(const Instruction *I, unsigned Simp) + const; + + /// performFinalCleanups - This method clones the current Program and performs + /// a series of cleanups intended to get rid of extra cruft on the module. If + /// the MayModifySemantics argument is true, then the cleanups is allowed to + /// modify how the code behaves. + /// + Module *performFinalCleanups(Module *M, bool MayModifySemantics = false); + + /// ExtractLoop - Given a module, extract up to one loop from it into a new + /// function. This returns null if there are no extractable loops in the + /// program or if the loop extractor crashes. + Module *ExtractLoop(Module *M); + + /// ExtractMappedBlocksFromModule - Extract all but the specified basic blocks + /// into their own functions. The only detail is that M is actually a module + /// cloned from the one the BBs are in, so some mapping needs to be performed. + /// If this operation fails for some reason (ie the implementation is buggy), + /// this function should return null, otherwise it returns a new Module. + Module *ExtractMappedBlocksFromModule(const std::vector<BasicBlock*> &BBs, + Module *M); + + /// runPassesOn - Carefully run the specified set of pass on the specified + /// module, returning the transformed module on success, or a null pointer on + /// failure. If AutoDebugCrashes is set to true, then bugpoint will + /// automatically attempt to track down a crashing pass if one exists, and + /// this method will never return null. + Module *runPassesOn(Module *M, const std::vector<const PassInfo*> &Passes, + bool AutoDebugCrashes = false, unsigned NumExtraArgs = 0, + const char * const *ExtraArgs = NULL); + + /// runPasses - Run the specified passes on Program, outputting a bitcode + /// file and writting the filename into OutputFile if successful. If the + /// optimizations fail for some reason (optimizer crashes), return true, + /// otherwise return false. If DeleteOutput is set to true, the bitcode is + /// deleted on success, and the filename string is undefined. This prints to + /// cout a single line message indicating whether compilation was successful + /// or failed, unless Quiet is set. ExtraArgs specifies additional arguments + /// to pass to the child bugpoint instance. + /// + bool runPasses(const std::vector<const PassInfo*> &PassesToRun, + std::string &OutputFilename, bool DeleteOutput = false, + bool Quiet = false, unsigned NumExtraArgs = 0, + const char * const *ExtraArgs = NULL) const; + + /// runManyPasses - Take the specified pass list and create different + /// combinations of passes to compile the program with. Compile the program with + /// each set and mark test to see if it compiled correctly. If the passes + /// compiled correctly output nothing and rearrange the passes into a new order. + /// If the passes did not compile correctly, output the command required to + /// recreate the failure. This returns true if a compiler error is found. + /// + bool runManyPasses(const std::vector<const PassInfo*> &AllPasses); + + /// writeProgramToFile - This writes the current "Program" to the named + /// bitcode file. If an error occurs, true is returned. + /// + bool writeProgramToFile(const std::string &Filename, Module *M = 0) const; + +private: + /// runPasses - Just like the method above, but this just returns true or + /// false indicating whether or not the optimizer crashed on the specified + /// input (true = crashed). + /// + bool runPasses(const std::vector<const PassInfo*> &PassesToRun, + bool DeleteOutput = true) const { + std::string Filename; + return runPasses(PassesToRun, Filename, DeleteOutput); + } + + /// runAsChild - The actual "runPasses" guts that runs in a child process. + int runPassesAsChild(const std::vector<const PassInfo*> &PassesToRun); + + /// initializeExecutionEnvironment - This method is used to set up the + /// environment for executing LLVM programs. + /// + bool initializeExecutionEnvironment(); +}; + +/// ParseInputFile - Given a bitcode or assembly input filename, parse and +/// return it, or return null if not possible. +/// +Module *ParseInputFile(const std::string &InputFilename); + + +/// getPassesString - Turn a list of passes into a string which indicates the +/// command line options that must be passed to add the passes. +/// +std::string getPassesString(const std::vector<const PassInfo*> &Passes); + +/// PrintFunctionList - prints out list of problematic functions +/// +void PrintFunctionList(const std::vector<Function*> &Funcs); + +/// PrintGlobalVariableList - prints out list of problematic global variables +/// +void PrintGlobalVariableList(const std::vector<GlobalVariable*> &GVs); + +// DeleteFunctionBody - "Remove" the function by deleting all of it's basic +// blocks, making it external. +// +void DeleteFunctionBody(Function *F); + +/// SplitFunctionsOutOfModule - Given a module and a list of functions in the +/// module, split the functions OUT of the specified module, and place them in +/// the new module. +Module *SplitFunctionsOutOfModule(Module *M, const std::vector<Function*> &F, + DenseMap<const Value*, Value*> &ValueMap); + +} // End llvm namespace + +#endif diff --git a/tools/bugpoint/CMakeLists.txt b/tools/bugpoint/CMakeLists.txt new file mode 100644 index 0000000..90f24ba --- /dev/null +++ b/tools/bugpoint/CMakeLists.txt @@ -0,0 +1,16 @@ +set(LLVM_LINK_COMPONENTS asmparser instrumentation scalaropts ipo + linker bitreader bitwriter) +set(LLVM_REQUIRES_EH 1) + +add_llvm_tool(bugpoint + BugDriver.cpp + CrashDebugger.cpp + ExecutionDriver.cpp + ExtractFunction.cpp + FindBugs.cpp + Miscompilation.cpp + OptimizerDriver.cpp + TestPasses.cpp + ToolRunner.cpp + bugpoint.cpp + ) diff --git a/tools/bugpoint/CrashDebugger.cpp b/tools/bugpoint/CrashDebugger.cpp new file mode 100644 index 0000000..7daf57c --- /dev/null +++ b/tools/bugpoint/CrashDebugger.cpp @@ -0,0 +1,648 @@ +//===- CrashDebugger.cpp - Debug compilation crashes ----------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the bugpoint internals that narrow down compilation crashes +// +//===----------------------------------------------------------------------===// + +#include "BugDriver.h" +#include "ToolRunner.h" +#include "ListReducer.h" +#include "llvm/Constants.h" +#include "llvm/DerivedTypes.h" +#include "llvm/Instructions.h" +#include "llvm/Module.h" +#include "llvm/Pass.h" +#include "llvm/PassManager.h" +#include "llvm/ValueSymbolTable.h" +#include "llvm/ADT/SmallPtrSet.h" +#include "llvm/Analysis/Verifier.h" +#include "llvm/Support/CFG.h" +#include "llvm/Transforms/Scalar.h" +#include "llvm/Transforms/Utils/Cloning.h" +#include "llvm/Support/FileUtilities.h" +#include "llvm/Support/CommandLine.h" +#include <fstream> +#include <set> +using namespace llvm; + +namespace { + cl::opt<bool> + KeepMain("keep-main", + cl::desc("Force function reduction to keep main"), + cl::init(false)); + cl::opt<bool> + NoGlobalRM ("disable-global-remove", + cl::desc("Do not remove global variables"), + cl::init(false)); +} + +namespace llvm { + class ReducePassList : public ListReducer<const PassInfo*> { + BugDriver &BD; + public: + ReducePassList(BugDriver &bd) : BD(bd) {} + + // doTest - Return true iff running the "removed" passes succeeds, and + // running the "Kept" passes fail when run on the output of the "removed" + // passes. If we return true, we update the current module of bugpoint. + // + virtual TestResult doTest(std::vector<const PassInfo*> &Removed, + std::vector<const PassInfo*> &Kept); + }; +} + +ReducePassList::TestResult +ReducePassList::doTest(std::vector<const PassInfo*> &Prefix, + std::vector<const PassInfo*> &Suffix) { + sys::Path PrefixOutput; + Module *OrigProgram = 0; + if (!Prefix.empty()) { + std::cout << "Checking to see if these passes crash: " + << getPassesString(Prefix) << ": "; + std::string PfxOutput; + if (BD.runPasses(Prefix, PfxOutput)) + return KeepPrefix; + + PrefixOutput.set(PfxOutput); + OrigProgram = BD.Program; + + BD.Program = ParseInputFile(PrefixOutput.toString()); + if (BD.Program == 0) { + std::cerr << BD.getToolName() << ": Error reading bitcode file '" + << PrefixOutput << "'!\n"; + exit(1); + } + PrefixOutput.eraseFromDisk(); + } + + std::cout << "Checking to see if these passes crash: " + << getPassesString(Suffix) << ": "; + + if (BD.runPasses(Suffix)) { + delete OrigProgram; // The suffix crashes alone... + return KeepSuffix; + } + + // Nothing failed, restore state... + if (OrigProgram) { + delete BD.Program; + BD.Program = OrigProgram; + } + return NoFailure; +} + +namespace { + /// ReduceCrashingGlobalVariables - This works by removing the global + /// variable's initializer and seeing if the program still crashes. If it + /// does, then we keep that program and try again. + /// + class ReduceCrashingGlobalVariables : public ListReducer<GlobalVariable*> { + BugDriver &BD; + bool (*TestFn)(BugDriver &, Module *); + public: + ReduceCrashingGlobalVariables(BugDriver &bd, + bool (*testFn)(BugDriver&, Module*)) + : BD(bd), TestFn(testFn) {} + + virtual TestResult doTest(std::vector<GlobalVariable*>& Prefix, + std::vector<GlobalVariable*>& Kept) { + if (!Kept.empty() && TestGlobalVariables(Kept)) + return KeepSuffix; + + if (!Prefix.empty() && TestGlobalVariables(Prefix)) + return KeepPrefix; + + return NoFailure; + } + + bool TestGlobalVariables(std::vector<GlobalVariable*>& GVs); + }; +} + +bool +ReduceCrashingGlobalVariables::TestGlobalVariables( + std::vector<GlobalVariable*>& GVs) { + // Clone the program to try hacking it apart... + DenseMap<const Value*, Value*> ValueMap; + Module *M = CloneModule(BD.getProgram(), ValueMap); + + // Convert list to set for fast lookup... + std::set<GlobalVariable*> GVSet; + + for (unsigned i = 0, e = GVs.size(); i != e; ++i) { + GlobalVariable* CMGV = cast<GlobalVariable>(ValueMap[GVs[i]]); + assert(CMGV && "Global Variable not in module?!"); + GVSet.insert(CMGV); + } + + std::cout << "Checking for crash with only these global variables: "; + PrintGlobalVariableList(GVs); + std::cout << ": "; + + // Loop over and delete any global variables which we aren't supposed to be + // playing with... + for (Module::global_iterator I = M->global_begin(), E = M->global_end(); + I != E; ++I) + if (I->hasInitializer() && !GVSet.count(I)) { + I->setInitializer(0); + I->setLinkage(GlobalValue::ExternalLinkage); + } + + // Try running the hacked up program... + if (TestFn(BD, M)) { + BD.setNewProgram(M); // It crashed, keep the trimmed version... + + // Make sure to use global variable pointers that point into the now-current + // module. + GVs.assign(GVSet.begin(), GVSet.end()); + return true; + } + + delete M; + return false; +} + +namespace llvm { + /// ReduceCrashingFunctions reducer - This works by removing functions and + /// seeing if the program still crashes. If it does, then keep the newer, + /// smaller program. + /// + class ReduceCrashingFunctions : public ListReducer<Function*> { + BugDriver &BD; + bool (*TestFn)(BugDriver &, Module *); + public: + ReduceCrashingFunctions(BugDriver &bd, + bool (*testFn)(BugDriver &, Module *)) + : BD(bd), TestFn(testFn) {} + + virtual TestResult doTest(std::vector<Function*> &Prefix, + std::vector<Function*> &Kept) { + if (!Kept.empty() && TestFuncs(Kept)) + return KeepSuffix; + if (!Prefix.empty() && TestFuncs(Prefix)) + return KeepPrefix; + return NoFailure; + } + + bool TestFuncs(std::vector<Function*> &Prefix); + }; +} + +bool ReduceCrashingFunctions::TestFuncs(std::vector<Function*> &Funcs) { + + //if main isn't present, claim there is no problem + if (KeepMain && find(Funcs.begin(), Funcs.end(), + BD.getProgram()->getFunction("main")) == Funcs.end()) + return false; + + // Clone the program to try hacking it apart... + DenseMap<const Value*, Value*> ValueMap; + Module *M = CloneModule(BD.getProgram(), ValueMap); + + // Convert list to set for fast lookup... + std::set<Function*> Functions; + for (unsigned i = 0, e = Funcs.size(); i != e; ++i) { + Function *CMF = cast<Function>(ValueMap[Funcs[i]]); + assert(CMF && "Function not in module?!"); + assert(CMF->getFunctionType() == Funcs[i]->getFunctionType() && "wrong ty"); + assert(CMF->getName() == Funcs[i]->getName() && "wrong name"); + Functions.insert(CMF); + } + + std::cout << "Checking for crash with only these functions: "; + PrintFunctionList(Funcs); + std::cout << ": "; + + // Loop over and delete any functions which we aren't supposed to be playing + // with... + for (Module::iterator I = M->begin(), E = M->end(); I != E; ++I) + if (!I->isDeclaration() && !Functions.count(I)) + DeleteFunctionBody(I); + + // Try running the hacked up program... + if (TestFn(BD, M)) { + BD.setNewProgram(M); // It crashed, keep the trimmed version... + + // Make sure to use function pointers that point into the now-current + // module. + Funcs.assign(Functions.begin(), Functions.end()); + return true; + } + delete M; + return false; +} + + +namespace { + /// ReduceCrashingBlocks reducer - This works by setting the terminators of + /// all terminators except the specified basic blocks to a 'ret' instruction, + /// then running the simplify-cfg pass. This has the effect of chopping up + /// the CFG really fast which can reduce large functions quickly. + /// + class ReduceCrashingBlocks : public ListReducer<const BasicBlock*> { + BugDriver &BD; + bool (*TestFn)(BugDriver &, Module *); + public: + ReduceCrashingBlocks(BugDriver &bd, bool (*testFn)(BugDriver &, Module *)) + : BD(bd), TestFn(testFn) {} + + virtual TestResult doTest(std::vector<const BasicBlock*> &Prefix, + std::vector<const BasicBlock*> &Kept) { + if (!Kept.empty() && TestBlocks(Kept)) + return KeepSuffix; + if (!Prefix.empty() && TestBlocks(Prefix)) + return KeepPrefix; + return NoFailure; + } + + bool TestBlocks(std::vector<const BasicBlock*> &Prefix); + }; +} + +bool ReduceCrashingBlocks::TestBlocks(std::vector<const BasicBlock*> &BBs) { + // Clone the program to try hacking it apart... + DenseMap<const Value*, Value*> ValueMap; + Module *M = CloneModule(BD.getProgram(), ValueMap); + + // Convert list to set for fast lookup... + SmallPtrSet<BasicBlock*, 8> Blocks; + for (unsigned i = 0, e = BBs.size(); i != e; ++i) + Blocks.insert(cast<BasicBlock>(ValueMap[BBs[i]])); + + std::cout << "Checking for crash with only these blocks:"; + unsigned NumPrint = Blocks.size(); + if (NumPrint > 10) NumPrint = 10; + for (unsigned i = 0, e = NumPrint; i != e; ++i) + std::cout << " " << BBs[i]->getName(); + if (NumPrint < Blocks.size()) + std::cout << "... <" << Blocks.size() << " total>"; + std::cout << ": "; + + // Loop over and delete any hack up any blocks that are not listed... + for (Module::iterator I = M->begin(), E = M->end(); I != E; ++I) + for (Function::iterator BB = I->begin(), E = I->end(); BB != E; ++BB) + if (!Blocks.count(BB) && BB->getTerminator()->getNumSuccessors()) { + // Loop over all of the successors of this block, deleting any PHI nodes + // that might include it. + for (succ_iterator SI = succ_begin(BB), E = succ_end(BB); SI != E; ++SI) + (*SI)->removePredecessor(BB); + + TerminatorInst *BBTerm = BB->getTerminator(); + + if (isa<StructType>(BBTerm->getType())) + BBTerm->replaceAllUsesWith(UndefValue::get(BBTerm->getType())); + else if (BB->getTerminator()->getType() != Type::VoidTy) + BBTerm->replaceAllUsesWith(Constant::getNullValue(BBTerm->getType())); + + // Replace the old terminator instruction. + BB->getInstList().pop_back(); + new UnreachableInst(BB); + } + + // The CFG Simplifier pass may delete one of the basic blocks we are + // interested in. If it does we need to take the block out of the list. Make + // a "persistent mapping" by turning basic blocks into <function, name> pairs. + // This won't work well if blocks are unnamed, but that is just the risk we + // have to take. + std::vector<std::pair<Function*, std::string> > BlockInfo; + + for (SmallPtrSet<BasicBlock*, 8>::iterator I = Blocks.begin(), + E = Blocks.end(); I != E; ++I) + BlockInfo.push_back(std::make_pair((*I)->getParent(), (*I)->getName())); + + // Now run the CFG simplify pass on the function... + PassManager Passes; + Passes.add(createCFGSimplificationPass()); + Passes.add(createVerifierPass()); + Passes.run(*M); + + // Try running on the hacked up program... + if (TestFn(BD, M)) { + BD.setNewProgram(M); // It crashed, keep the trimmed version... + + // Make sure to use basic block pointers that point into the now-current + // module, and that they don't include any deleted blocks. + BBs.clear(); + for (unsigned i = 0, e = BlockInfo.size(); i != e; ++i) { + ValueSymbolTable &ST = BlockInfo[i].first->getValueSymbolTable(); + Value* V = ST.lookup(BlockInfo[i].second); + if (V && V->getType() == Type::LabelTy) + BBs.push_back(cast<BasicBlock>(V)); + } + return true; + } + delete M; // It didn't crash, try something else. + return false; +} + +namespace { + /// ReduceCrashingInstructions reducer - This works by removing the specified + /// non-terminator instructions and replacing them with undef. + /// + class ReduceCrashingInstructions : public ListReducer<const Instruction*> { + BugDriver &BD; + bool (*TestFn)(BugDriver &, Module *); + public: + ReduceCrashingInstructions(BugDriver &bd, bool (*testFn)(BugDriver &, + Module *)) + : BD(bd), TestFn(testFn) {} + + virtual TestResult doTest(std::vector<const Instruction*> &Prefix, + std::vector<const Instruction*> &Kept) { + if (!Kept.empty() && TestInsts(Kept)) + return KeepSuffix; + if (!Prefix.empty() && TestInsts(Prefix)) + return KeepPrefix; + return NoFailure; + } + + bool TestInsts(std::vector<const Instruction*> &Prefix); + }; +} + +bool ReduceCrashingInstructions::TestInsts(std::vector<const Instruction*> + &Insts) { + // Clone the program to try hacking it apart... + DenseMap<const Value*, Value*> ValueMap; + Module *M = CloneModule(BD.getProgram(), ValueMap); + + // Convert list to set for fast lookup... + SmallPtrSet<Instruction*, 64> Instructions; + for (unsigned i = 0, e = Insts.size(); i != e; ++i) { + assert(!isa<TerminatorInst>(Insts[i])); + Instructions.insert(cast<Instruction>(ValueMap[Insts[i]])); + } + + std::cout << "Checking for crash with only " << Instructions.size(); + if (Instructions.size() == 1) + std::cout << " instruction: "; + else + std::cout << " instructions: "; + + for (Module::iterator MI = M->begin(), ME = M->end(); MI != ME; ++MI) + for (Function::iterator FI = MI->begin(), FE = MI->end(); FI != FE; ++FI) + for (BasicBlock::iterator I = FI->begin(), E = FI->end(); I != E;) { + Instruction *Inst = I++; + if (!Instructions.count(Inst) && !isa<TerminatorInst>(Inst)) { + if (Inst->getType() != Type::VoidTy) + Inst->replaceAllUsesWith(UndefValue::get(Inst->getType())); + Inst->eraseFromParent(); + } + } + + // Verify that this is still valid. + PassManager Passes; + Passes.add(createVerifierPass()); + Passes.run(*M); + + // Try running on the hacked up program... + if (TestFn(BD, M)) { + BD.setNewProgram(M); // It crashed, keep the trimmed version... + + // Make sure to use instruction pointers that point into the now-current + // module, and that they don't include any deleted blocks. + Insts.clear(); + for (SmallPtrSet<Instruction*, 64>::const_iterator I = Instructions.begin(), + E = Instructions.end(); I != E; ++I) + Insts.push_back(*I); + return true; + } + delete M; // It didn't crash, try something else. + return false; +} + +/// DebugACrash - Given a predicate that determines whether a component crashes +/// on a program, try to destructively reduce the program while still keeping +/// the predicate true. +static bool DebugACrash(BugDriver &BD, bool (*TestFn)(BugDriver &, Module *)) { + // See if we can get away with nuking some of the global variable initializers + // in the program... + if (!NoGlobalRM && + BD.getProgram()->global_begin() != BD.getProgram()->global_end()) { + // Now try to reduce the number of global variable initializers in the + // module to something small. + Module *M = CloneModule(BD.getProgram()); + bool DeletedInit = false; + + for (Module::global_iterator I = M->global_begin(), E = M->global_end(); + I != E; ++I) + if (I->hasInitializer()) { + I->setInitializer(0); + I->setLinkage(GlobalValue::ExternalLinkage); + DeletedInit = true; + } + + if (!DeletedInit) { + delete M; // No change made... + } else { + // See if the program still causes a crash... + std::cout << "\nChecking to see if we can delete global inits: "; + + if (TestFn(BD, M)) { // Still crashes? + BD.setNewProgram(M); + std::cout << "\n*** Able to remove all global initializers!\n"; + } else { // No longer crashes? + std::cout << " - Removing all global inits hides problem!\n"; + delete M; + + std::vector<GlobalVariable*> GVs; + + for (Module::global_iterator I = BD.getProgram()->global_begin(), + E = BD.getProgram()->global_end(); I != E; ++I) + if (I->hasInitializer()) + GVs.push_back(I); + + if (GVs.size() > 1 && !BugpointIsInterrupted) { + std::cout << "\n*** Attempting to reduce the number of global " + << "variables in the testcase\n"; + + unsigned OldSize = GVs.size(); + ReduceCrashingGlobalVariables(BD, TestFn).reduceList(GVs); + + if (GVs.size() < OldSize) + BD.EmitProgressBitcode("reduced-global-variables"); + } + } + } + } + + // Now try to reduce the number of functions in the module to something small. + std::vector<Function*> Functions; + for (Module::iterator I = BD.getProgram()->begin(), + E = BD.getProgram()->end(); I != E; ++I) + if (!I->isDeclaration()) + Functions.push_back(I); + + if (Functions.size() > 1 && !BugpointIsInterrupted) { + std::cout << "\n*** Attempting to reduce the number of functions " + "in the testcase\n"; + + unsigned OldSize = Functions.size(); + ReduceCrashingFunctions(BD, TestFn).reduceList(Functions); + + if (Functions.size() < OldSize) + BD.EmitProgressBitcode("reduced-function"); + } + + // Attempt to delete entire basic blocks at a time to speed up + // convergence... this actually works by setting the terminator of the blocks + // to a return instruction then running simplifycfg, which can potentially + // shrinks the code dramatically quickly + // + if (!DisableSimplifyCFG && !BugpointIsInterrupted) { + std::vector<const BasicBlock*> Blocks; + for (Module::const_iterator I = BD.getProgram()->begin(), + E = BD.getProgram()->end(); I != E; ++I) + for (Function::const_iterator FI = I->begin(), E = I->end(); FI !=E; ++FI) + Blocks.push_back(FI); + unsigned OldSize = Blocks.size(); + ReduceCrashingBlocks(BD, TestFn).reduceList(Blocks); + if (Blocks.size() < OldSize) + BD.EmitProgressBitcode("reduced-blocks"); + } + + // Attempt to delete instructions using bisection. This should help out nasty + // cases with large basic blocks where the problem is at one end. + if (!BugpointIsInterrupted) { + std::vector<const Instruction*> Insts; + for (Module::const_iterator MI = BD.getProgram()->begin(), + ME = BD.getProgram()->end(); MI != ME; ++MI) + for (Function::const_iterator FI = MI->begin(), FE = MI->end(); FI != FE; + ++FI) + for (BasicBlock::const_iterator I = FI->begin(), E = FI->end(); + I != E; ++I) + if (!isa<TerminatorInst>(I)) + Insts.push_back(I); + + ReduceCrashingInstructions(BD, TestFn).reduceList(Insts); + } + + // FIXME: This should use the list reducer to converge faster by deleting + // larger chunks of instructions at a time! + unsigned Simplification = 2; + do { + if (BugpointIsInterrupted) break; + --Simplification; + std::cout << "\n*** Attempting to reduce testcase by deleting instruc" + << "tions: Simplification Level #" << Simplification << '\n'; + + // Now that we have deleted the functions that are unnecessary for the + // program, try to remove instructions that are not necessary to cause the + // crash. To do this, we loop through all of the instructions in the + // remaining functions, deleting them (replacing any values produced with + // nulls), and then running ADCE and SimplifyCFG. If the transformed input + // still triggers failure, keep deleting until we cannot trigger failure + // anymore. + // + unsigned InstructionsToSkipBeforeDeleting = 0; + TryAgain: + + // Loop over all of the (non-terminator) instructions remaining in the + // function, attempting to delete them. + unsigned CurInstructionNum = 0; + for (Module::const_iterator FI = BD.getProgram()->begin(), + E = BD.getProgram()->end(); FI != E; ++FI) + if (!FI->isDeclaration()) + for (Function::const_iterator BI = FI->begin(), E = FI->end(); BI != E; + ++BI) + for (BasicBlock::const_iterator I = BI->begin(), E = --BI->end(); + I != E; ++I, ++CurInstructionNum) + if (InstructionsToSkipBeforeDeleting) { + --InstructionsToSkipBeforeDeleting; + } else { + if (BugpointIsInterrupted) goto ExitLoops; + + std::cout << "Checking instruction: " << *I; + Module *M = BD.deleteInstructionFromProgram(I, Simplification); + + // Find out if the pass still crashes on this pass... + if (TestFn(BD, M)) { + // Yup, it does, we delete the old module, and continue trying + // to reduce the testcase... + BD.setNewProgram(M); + InstructionsToSkipBeforeDeleting = CurInstructionNum; + goto TryAgain; // I wish I had a multi-level break here! + } + + // This pass didn't crash without this instruction, try the next + // one. + delete M; + } + + if (InstructionsToSkipBeforeDeleting) { + InstructionsToSkipBeforeDeleting = 0; + goto TryAgain; + } + + } while (Simplification); +ExitLoops: + + // Try to clean up the testcase by running funcresolve and globaldce... + if (!BugpointIsInterrupted) { + std::cout << "\n*** Attempting to perform final cleanups: "; + Module *M = CloneModule(BD.getProgram()); + M = BD.performFinalCleanups(M, true); + + // Find out if the pass still crashes on the cleaned up program... + if (TestFn(BD, M)) { + BD.setNewProgram(M); // Yup, it does, keep the reduced version... + } else { + delete M; + } + } + + BD.EmitProgressBitcode("reduced-simplified"); + + return false; +} + +static bool TestForOptimizerCrash(BugDriver &BD, Module *M) { + return BD.runPasses(M); +} + +/// debugOptimizerCrash - This method is called when some pass crashes on input. +/// It attempts to prune down the testcase to something reasonable, and figure +/// out exactly which pass is crashing. +/// +bool BugDriver::debugOptimizerCrash(const std::string &ID) { + std::cout << "\n*** Debugging optimizer crash!\n"; + + // Reduce the list of passes which causes the optimizer to crash... + if (!BugpointIsInterrupted) + ReducePassList(*this).reduceList(PassesToRun); + + std::cout << "\n*** Found crashing pass" + << (PassesToRun.size() == 1 ? ": " : "es: ") + << getPassesString(PassesToRun) << '\n'; + + EmitProgressBitcode(ID); + + return DebugACrash(*this, TestForOptimizerCrash); +} + +static bool TestForCodeGenCrash(BugDriver &BD, Module *M) { + try { + BD.compileProgram(M); + std::cerr << '\n'; + return false; + } catch (ToolExecutionError &) { + std::cerr << "<crash>\n"; + return true; // Tool is still crashing. + } +} + +/// debugCodeGeneratorCrash - This method is called when the code generator +/// crashes on an input. It attempts to reduce the input as much as possible +/// while still causing the code generator to crash. +bool BugDriver::debugCodeGeneratorCrash() { + std::cerr << "*** Debugging code generator crash!\n"; + + return DebugACrash(*this, TestForCodeGenCrash); +} diff --git a/tools/bugpoint/ExecutionDriver.cpp b/tools/bugpoint/ExecutionDriver.cpp new file mode 100644 index 0000000..640fe28 --- /dev/null +++ b/tools/bugpoint/ExecutionDriver.cpp @@ -0,0 +1,473 @@ +//===- ExecutionDriver.cpp - Allow execution of LLVM program --------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains code used to execute the program utilizing one of the +// various ways of running LLVM bitcode. +// +//===----------------------------------------------------------------------===// + +#include "BugDriver.h" +#include "ToolRunner.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/FileUtilities.h" +#include "llvm/Support/SystemUtils.h" +#include <fstream> +#include <iostream> + +using namespace llvm; + +namespace { + // OutputType - Allow the user to specify the way code should be run, to test + // for miscompilation. + // + enum OutputType { + AutoPick, RunLLI, RunJIT, RunLLC, RunCBE, CBE_bug, LLC_Safe, Custom + }; + + cl::opt<double> + AbsTolerance("abs-tolerance", cl::desc("Absolute error tolerated"), + cl::init(0.0)); + cl::opt<double> + RelTolerance("rel-tolerance", cl::desc("Relative error tolerated"), + cl::init(0.0)); + + cl::opt<OutputType> + InterpreterSel(cl::desc("Specify the \"test\" i.e. suspect back-end:"), + cl::values(clEnumValN(AutoPick, "auto", "Use best guess"), + clEnumValN(RunLLI, "run-int", + "Execute with the interpreter"), + clEnumValN(RunJIT, "run-jit", "Execute with JIT"), + clEnumValN(RunLLC, "run-llc", "Compile with LLC"), + clEnumValN(RunCBE, "run-cbe", "Compile with CBE"), + clEnumValN(CBE_bug,"cbe-bug", "Find CBE bugs"), + clEnumValN(LLC_Safe, "llc-safe", "Use LLC for all"), + clEnumValN(Custom, "run-custom", + "Use -exec-command to define a command to execute " + "the bitcode. Useful for cross-compilation."), + clEnumValEnd), + cl::init(AutoPick)); + + cl::opt<OutputType> + SafeInterpreterSel(cl::desc("Specify \"safe\" i.e. known-good backend:"), + cl::values(clEnumValN(AutoPick, "safe-auto", "Use best guess"), + clEnumValN(RunLLC, "safe-run-llc", "Compile with LLC"), + clEnumValN(RunCBE, "safe-run-cbe", "Compile with CBE"), + clEnumValN(Custom, "safe-run-custom", + "Use -exec-command to define a command to execute " + "the bitcode. Useful for cross-compilation."), + clEnumValEnd), + cl::init(AutoPick)); + + cl::opt<std::string> + SafeInterpreterPath("safe-path", + cl::desc("Specify the path to the \"safe\" backend program"), + cl::init("")); + + cl::opt<bool> + AppendProgramExitCode("append-exit-code", + cl::desc("Append the exit code to the output so it gets diff'd too"), + cl::init(false)); + + cl::opt<std::string> + InputFile("input", cl::init("/dev/null"), + cl::desc("Filename to pipe in as stdin (default: /dev/null)")); + + cl::list<std::string> + AdditionalSOs("additional-so", + cl::desc("Additional shared objects to load " + "into executing programs")); + + cl::list<std::string> + AdditionalLinkerArgs("Xlinker", + cl::desc("Additional arguments to pass to the linker")); + + cl::opt<std::string> + CustomExecCommand("exec-command", cl::init("simulate"), + cl::desc("Command to execute the bitcode (use with -run-custom) " + "(default: simulate)")); +} + +namespace llvm { + // Anything specified after the --args option are taken as arguments to the + // program being debugged. + cl::list<std::string> + InputArgv("args", cl::Positional, cl::desc("<program arguments>..."), + cl::ZeroOrMore, cl::PositionalEatsArgs); +} + +namespace { + cl::list<std::string> + ToolArgv("tool-args", cl::Positional, cl::desc("<tool arguments>..."), + cl::ZeroOrMore, cl::PositionalEatsArgs); + + cl::list<std::string> + SafeToolArgv("safe-tool-args", cl::Positional, + cl::desc("<safe-tool arguments>..."), + cl::ZeroOrMore, cl::PositionalEatsArgs); + + cl::list<std::string> + GCCToolArgv("gcc-tool-args", cl::Positional, + cl::desc("<gcc-tool arguments>..."), + cl::ZeroOrMore, cl::PositionalEatsArgs); +} + +//===----------------------------------------------------------------------===// +// BugDriver method implementation +// + +/// initializeExecutionEnvironment - This method is used to set up the +/// environment for executing LLVM programs. +/// +bool BugDriver::initializeExecutionEnvironment() { + std::cout << "Initializing execution environment: "; + + // Create an instance of the AbstractInterpreter interface as specified on + // the command line + SafeInterpreter = 0; + std::string Message; + + switch (InterpreterSel) { + case AutoPick: + InterpreterSel = RunCBE; + Interpreter = + AbstractInterpreter::createCBE(getToolName(), Message, &ToolArgv, + &GCCToolArgv); + if (!Interpreter) { + InterpreterSel = RunJIT; + Interpreter = AbstractInterpreter::createJIT(getToolName(), Message, + &ToolArgv); + } + if (!Interpreter) { + InterpreterSel = RunLLC; + Interpreter = AbstractInterpreter::createLLC(getToolName(), Message, + &ToolArgv, &GCCToolArgv); + } + if (!Interpreter) { + InterpreterSel = RunLLI; + Interpreter = AbstractInterpreter::createLLI(getToolName(), Message, + &ToolArgv); + } + if (!Interpreter) { + InterpreterSel = AutoPick; + Message = "Sorry, I can't automatically select an interpreter!\n"; + } + break; + case RunLLI: + Interpreter = AbstractInterpreter::createLLI(getToolName(), Message, + &ToolArgv); + break; + case RunLLC: + case LLC_Safe: + Interpreter = AbstractInterpreter::createLLC(getToolName(), Message, + &ToolArgv, &GCCToolArgv); + break; + case RunJIT: + Interpreter = AbstractInterpreter::createJIT(getToolName(), Message, + &ToolArgv); + break; + case RunCBE: + case CBE_bug: + Interpreter = AbstractInterpreter::createCBE(getToolName(), Message, + &ToolArgv, &GCCToolArgv); + break; + case Custom: + Interpreter = AbstractInterpreter::createCustom(getToolName(), Message, + CustomExecCommand); + break; + default: + Message = "Sorry, this back-end is not supported by bugpoint right now!\n"; + break; + } + if (!Interpreter) + std::cerr << Message; + else // Display informational messages on stdout instead of stderr + std::cout << Message; + + std::string Path = SafeInterpreterPath; + if (Path.empty()) + Path = getToolName(); + std::vector<std::string> SafeToolArgs = SafeToolArgv; + switch (SafeInterpreterSel) { + case AutoPick: + // In "cbe-bug" mode, default to using LLC as the "safe" backend. + if (!SafeInterpreter && + InterpreterSel == CBE_bug) { + SafeInterpreterSel = RunLLC; + SafeToolArgs.push_back("--relocation-model=pic"); + SafeInterpreter = AbstractInterpreter::createLLC(Path, Message, + &SafeToolArgs, + &GCCToolArgv); + } + + // In "llc-safe" mode, default to using LLC as the "safe" backend. + if (!SafeInterpreter && + InterpreterSel == LLC_Safe) { + SafeInterpreterSel = RunLLC; + SafeToolArgs.push_back("--relocation-model=pic"); + SafeInterpreter = AbstractInterpreter::createLLC(Path, Message, + &SafeToolArgs, + &GCCToolArgv); + } + + // Pick a backend that's different from the test backend. The JIT and + // LLC backends share a lot of code, so prefer to use the CBE as the + // safe back-end when testing them. + if (!SafeInterpreter && + InterpreterSel != RunCBE) { + SafeInterpreterSel = RunCBE; + SafeInterpreter = AbstractInterpreter::createCBE(Path, Message, + &SafeToolArgs, + &GCCToolArgv); + } + if (!SafeInterpreter && + InterpreterSel != RunLLC && + InterpreterSel != RunJIT) { + SafeInterpreterSel = RunLLC; + SafeToolArgs.push_back("--relocation-model=pic"); + SafeInterpreter = AbstractInterpreter::createLLC(Path, Message, + &SafeToolArgs, + &GCCToolArgv); + } + if (!SafeInterpreter) { + SafeInterpreterSel = AutoPick; + Message = "Sorry, I can't automatically select an interpreter!\n"; + } + break; + case RunLLC: + SafeToolArgs.push_back("--relocation-model=pic"); + SafeInterpreter = AbstractInterpreter::createLLC(Path, Message, + &SafeToolArgs, + &GCCToolArgv); + break; + case RunCBE: + SafeInterpreter = AbstractInterpreter::createCBE(Path, Message, + &SafeToolArgs, + &GCCToolArgv); + break; + case Custom: + SafeInterpreter = AbstractInterpreter::createCustom(Path, Message, + CustomExecCommand); + break; + default: + Message = "Sorry, this back-end is not supported by bugpoint as the " + "\"safe\" backend right now!\n"; + break; + } + if (!SafeInterpreter) { std::cout << Message << "\nExiting.\n"; exit(1); } + + gcc = GCC::create(getToolName(), Message, &GCCToolArgv); + if (!gcc) { std::cout << Message << "\nExiting.\n"; exit(1); } + + // If there was an error creating the selected interpreter, quit with error. + return Interpreter == 0; +} + +/// compileProgram - Try to compile the specified module, throwing an exception +/// if an error occurs, or returning normally if not. This is used for code +/// generation crash testing. +/// +void BugDriver::compileProgram(Module *M) { + // Emit the program to a bitcode file... + sys::Path BitcodeFile ("bugpoint-test-program.bc"); + std::string ErrMsg; + if (BitcodeFile.makeUnique(true,&ErrMsg)) { + std::cerr << ToolName << ": Error making unique filename: " << ErrMsg + << "\n"; + exit(1); + } + if (writeProgramToFile(BitcodeFile.toString(), M)) { + std::cerr << ToolName << ": Error emitting bitcode to file '" + << BitcodeFile << "'!\n"; + exit(1); + } + + // Remove the temporary bitcode file when we are done. + FileRemover BitcodeFileRemover(BitcodeFile); + + // Actually compile the program! + Interpreter->compileProgram(BitcodeFile.toString()); +} + + +/// executeProgram - This method runs "Program", capturing the output of the +/// program to a file, returning the filename of the file. A recommended +/// filename may be optionally specified. +/// +std::string BugDriver::executeProgram(std::string OutputFile, + std::string BitcodeFile, + const std::string &SharedObj, + AbstractInterpreter *AI, + bool *ProgramExitedNonzero) { + if (AI == 0) AI = Interpreter; + assert(AI && "Interpreter should have been created already!"); + bool CreatedBitcode = false; + std::string ErrMsg; + if (BitcodeFile.empty()) { + // Emit the program to a bitcode file... + sys::Path uniqueFilename("bugpoint-test-program.bc"); + if (uniqueFilename.makeUnique(true, &ErrMsg)) { + std::cerr << ToolName << ": Error making unique filename: " + << ErrMsg << "!\n"; + exit(1); + } + BitcodeFile = uniqueFilename.toString(); + + if (writeProgramToFile(BitcodeFile, Program)) { + std::cerr << ToolName << ": Error emitting bitcode to file '" + << BitcodeFile << "'!\n"; + exit(1); + } + CreatedBitcode = true; + } + + // Remove the temporary bitcode file when we are done. + sys::Path BitcodePath (BitcodeFile); + FileRemover BitcodeFileRemover(BitcodePath, CreatedBitcode); + + if (OutputFile.empty()) OutputFile = "bugpoint-execution-output"; + + // Check to see if this is a valid output filename... + sys::Path uniqueFile(OutputFile); + if (uniqueFile.makeUnique(true, &ErrMsg)) { + std::cerr << ToolName << ": Error making unique filename: " + << ErrMsg << "\n"; + exit(1); + } + OutputFile = uniqueFile.toString(); + + // Figure out which shared objects to run, if any. + std::vector<std::string> SharedObjs(AdditionalSOs); + if (!SharedObj.empty()) + SharedObjs.push_back(SharedObj); + + int RetVal = AI->ExecuteProgram(BitcodeFile, InputArgv, InputFile, + OutputFile, AdditionalLinkerArgs, SharedObjs, + Timeout, MemoryLimit); + + if (RetVal == -1) { + std::cerr << "<timeout>"; + static bool FirstTimeout = true; + if (FirstTimeout) { + std::cout << "\n" + "*** Program execution timed out! This mechanism is designed to handle\n" + " programs stuck in infinite loops gracefully. The -timeout option\n" + " can be used to change the timeout threshold or disable it completely\n" + " (with -timeout=0). This message is only displayed once.\n"; + FirstTimeout = false; + } + } + + if (AppendProgramExitCode) { + std::ofstream outFile(OutputFile.c_str(), std::ios_base::app); + outFile << "exit " << RetVal << '\n'; + outFile.close(); + } + + if (ProgramExitedNonzero != 0) + *ProgramExitedNonzero = (RetVal != 0); + + // Return the filename we captured the output to. + return OutputFile; +} + +/// executeProgramSafely - Used to create reference output with the "safe" +/// backend, if reference output is not provided. +/// +std::string BugDriver::executeProgramSafely(std::string OutputFile) { + bool ProgramExitedNonzero; + std::string outFN = executeProgram(OutputFile, "", "", SafeInterpreter, + &ProgramExitedNonzero); + return outFN; +} + +std::string BugDriver::compileSharedObject(const std::string &BitcodeFile) { + assert(Interpreter && "Interpreter should have been created already!"); + sys::Path OutputFile; + + // Using the known-good backend. + GCC::FileType FT = SafeInterpreter->OutputCode(BitcodeFile, OutputFile); + + std::string SharedObjectFile; + if (gcc->MakeSharedObject(OutputFile.toString(), FT, + SharedObjectFile, AdditionalLinkerArgs)) + exit(1); + + // Remove the intermediate C file + OutputFile.eraseFromDisk(); + + return "./" + SharedObjectFile; +} + +/// createReferenceFile - calls compileProgram and then records the output +/// into ReferenceOutputFile. Returns true if reference file created, false +/// otherwise. Note: initializeExecutionEnvironment should be called BEFORE +/// this function. +/// +bool BugDriver::createReferenceFile(Module *M, const std::string &Filename) { + try { + compileProgram(Program); + } catch (ToolExecutionError &) { + return false; + } + try { + ReferenceOutputFile = executeProgramSafely(Filename); + std::cout << "\nReference output is: " << ReferenceOutputFile << "\n\n"; + } catch (ToolExecutionError &TEE) { + std::cerr << TEE.what(); + if (Interpreter != SafeInterpreter) { + std::cerr << "*** There is a bug running the \"safe\" backend. Either" + << " debug it (for example with the -run-cbe bugpoint option," + << " if CBE is being used as the \"safe\" backend), or fix the" + << " error some other way.\n"; + } + return false; + } + return true; +} + +/// diffProgram - This method executes the specified module and diffs the +/// output against the file specified by ReferenceOutputFile. If the output +/// is different, true is returned. If there is a problem with the code +/// generator (e.g., llc crashes), this will throw an exception. +/// +bool BugDriver::diffProgram(const std::string &BitcodeFile, + const std::string &SharedObject, + bool RemoveBitcode) { + bool ProgramExitedNonzero; + + // Execute the program, generating an output file... + sys::Path Output(executeProgram("", BitcodeFile, SharedObject, 0, + &ProgramExitedNonzero)); + + std::string Error; + bool FilesDifferent = false; + if (int Diff = DiffFilesWithTolerance(sys::Path(ReferenceOutputFile), + sys::Path(Output.toString()), + AbsTolerance, RelTolerance, &Error)) { + if (Diff == 2) { + std::cerr << "While diffing output: " << Error << '\n'; + exit(1); + } + FilesDifferent = true; + } + + // Remove the generated output. + Output.eraseFromDisk(); + + // Remove the bitcode file if we are supposed to. + if (RemoveBitcode) + sys::Path(BitcodeFile).eraseFromDisk(); + return FilesDifferent; +} + +bool BugDriver::isExecutingJIT() { + return InterpreterSel == RunJIT; +} + diff --git a/tools/bugpoint/ExtractFunction.cpp b/tools/bugpoint/ExtractFunction.cpp new file mode 100644 index 0000000..e4affbb --- /dev/null +++ b/tools/bugpoint/ExtractFunction.cpp @@ -0,0 +1,375 @@ +//===- ExtractFunction.cpp - Extract a function from Program --------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements several methods that are used to extract functions, +// loops, or portions of a module from the rest of the module. +// +//===----------------------------------------------------------------------===// + +#include "BugDriver.h" +#include "llvm/Constants.h" +#include "llvm/DerivedTypes.h" +#include "llvm/Module.h" +#include "llvm/PassManager.h" +#include "llvm/Pass.h" +#include "llvm/Analysis/Verifier.h" +#include "llvm/Transforms/IPO.h" +#include "llvm/Transforms/Scalar.h" +#include "llvm/Transforms/Utils/Cloning.h" +#include "llvm/Transforms/Utils/FunctionUtils.h" +#include "llvm/Target/TargetData.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/FileUtilities.h" +#include "llvm/System/Path.h" +#include "llvm/System/Signals.h" +#include <set> +#include <fstream> +#include <iostream> +using namespace llvm; + +namespace llvm { + bool DisableSimplifyCFG = false; +} // End llvm namespace + +namespace { + cl::opt<bool> + NoDCE ("disable-dce", + cl::desc("Do not use the -dce pass to reduce testcases")); + cl::opt<bool, true> + NoSCFG("disable-simplifycfg", cl::location(DisableSimplifyCFG), + cl::desc("Do not use the -simplifycfg pass to reduce testcases")); +} + +/// deleteInstructionFromProgram - This method clones the current Program and +/// deletes the specified instruction from the cloned module. It then runs a +/// series of cleanup passes (ADCE and SimplifyCFG) to eliminate any code which +/// depends on the value. The modified module is then returned. +/// +Module *BugDriver::deleteInstructionFromProgram(const Instruction *I, + unsigned Simplification) const { + Module *Result = CloneModule(Program); + + const BasicBlock *PBB = I->getParent(); + const Function *PF = PBB->getParent(); + + Module::iterator RFI = Result->begin(); // Get iterator to corresponding fn + std::advance(RFI, std::distance(PF->getParent()->begin(), + Module::const_iterator(PF))); + + Function::iterator RBI = RFI->begin(); // Get iterator to corresponding BB + std::advance(RBI, std::distance(PF->begin(), Function::const_iterator(PBB))); + + BasicBlock::iterator RI = RBI->begin(); // Get iterator to corresponding inst + std::advance(RI, std::distance(PBB->begin(), BasicBlock::const_iterator(I))); + Instruction *TheInst = RI; // Got the corresponding instruction! + + // If this instruction produces a value, replace any users with null values + if (isa<StructType>(TheInst->getType())) + TheInst->replaceAllUsesWith(UndefValue::get(TheInst->getType())); + else if (TheInst->getType() != Type::VoidTy) + TheInst->replaceAllUsesWith(Constant::getNullValue(TheInst->getType())); + + // Remove the instruction from the program. + TheInst->getParent()->getInstList().erase(TheInst); + + + //writeProgramToFile("current.bc", Result); + + // Spiff up the output a little bit. + PassManager Passes; + // Make sure that the appropriate target data is always used... + Passes.add(new TargetData(Result)); + + /// FIXME: If this used runPasses() like the methods below, we could get rid + /// of the -disable-* options! + if (Simplification > 1 && !NoDCE) + Passes.add(createDeadCodeEliminationPass()); + if (Simplification && !DisableSimplifyCFG) + Passes.add(createCFGSimplificationPass()); // Delete dead control flow + + Passes.add(createVerifierPass()); + Passes.run(*Result); + return Result; +} + +static const PassInfo *getPI(Pass *P) { + const PassInfo *PI = P->getPassInfo(); + delete P; + return PI; +} + +/// performFinalCleanups - This method clones the current Program and performs +/// a series of cleanups intended to get rid of extra cruft on the module +/// before handing it to the user. +/// +Module *BugDriver::performFinalCleanups(Module *M, bool MayModifySemantics) { + // Make all functions external, so GlobalDCE doesn't delete them... + for (Module::iterator I = M->begin(), E = M->end(); I != E; ++I) + I->setLinkage(GlobalValue::ExternalLinkage); + + std::vector<const PassInfo*> CleanupPasses; + CleanupPasses.push_back(getPI(createGlobalDCEPass())); + CleanupPasses.push_back(getPI(createDeadTypeEliminationPass())); + + if (MayModifySemantics) + CleanupPasses.push_back(getPI(createDeadArgHackingPass())); + else + CleanupPasses.push_back(getPI(createDeadArgEliminationPass())); + + Module *New = runPassesOn(M, CleanupPasses); + if (New == 0) { + std::cerr << "Final cleanups failed. Sorry. :( Please report a bug!\n"; + return M; + } + delete M; + return New; +} + + +/// ExtractLoop - Given a module, extract up to one loop from it into a new +/// function. This returns null if there are no extractable loops in the +/// program or if the loop extractor crashes. +Module *BugDriver::ExtractLoop(Module *M) { + std::vector<const PassInfo*> LoopExtractPasses; + LoopExtractPasses.push_back(getPI(createSingleLoopExtractorPass())); + + Module *NewM = runPassesOn(M, LoopExtractPasses); + if (NewM == 0) { + Module *Old = swapProgramIn(M); + std::cout << "*** Loop extraction failed: "; + EmitProgressBitcode("loopextraction", true); + std::cout << "*** Sorry. :( Please report a bug!\n"; + swapProgramIn(Old); + return 0; + } + + // Check to see if we created any new functions. If not, no loops were + // extracted and we should return null. Limit the number of loops we extract + // to avoid taking forever. + static unsigned NumExtracted = 32; + if (M->size() == NewM->size() || --NumExtracted == 0) { + delete NewM; + return 0; + } else { + assert(M->size() < NewM->size() && "Loop extract removed functions?"); + Module::iterator MI = NewM->begin(); + for (unsigned i = 0, e = M->size(); i != e; ++i) + ++MI; + } + + return NewM; +} + + +// DeleteFunctionBody - "Remove" the function by deleting all of its basic +// blocks, making it external. +// +void llvm::DeleteFunctionBody(Function *F) { + // delete the body of the function... + F->deleteBody(); + assert(F->isDeclaration() && "This didn't make the function external!"); +} + +/// GetTorInit - Given a list of entries for static ctors/dtors, return them +/// as a constant array. +static Constant *GetTorInit(std::vector<std::pair<Function*, int> > &TorList) { + assert(!TorList.empty() && "Don't create empty tor list!"); + std::vector<Constant*> ArrayElts; + for (unsigned i = 0, e = TorList.size(); i != e; ++i) { + std::vector<Constant*> Elts; + Elts.push_back(ConstantInt::get(Type::Int32Ty, TorList[i].second)); + Elts.push_back(TorList[i].first); + ArrayElts.push_back(ConstantStruct::get(Elts)); + } + return ConstantArray::get(ArrayType::get(ArrayElts[0]->getType(), + ArrayElts.size()), + ArrayElts); +} + +/// SplitStaticCtorDtor - A module was recently split into two parts, M1/M2, and +/// M1 has all of the global variables. If M2 contains any functions that are +/// static ctors/dtors, we need to add an llvm.global_[cd]tors global to M2, and +/// prune appropriate entries out of M1s list. +static void SplitStaticCtorDtor(const char *GlobalName, Module *M1, Module *M2, + DenseMap<const Value*, Value*> ValueMap) { + GlobalVariable *GV = M1->getNamedGlobal(GlobalName); + if (!GV || GV->isDeclaration() || GV->hasLocalLinkage() || + !GV->use_empty()) return; + + std::vector<std::pair<Function*, int> > M1Tors, M2Tors; + ConstantArray *InitList = dyn_cast<ConstantArray>(GV->getInitializer()); + if (!InitList) return; + + for (unsigned i = 0, e = InitList->getNumOperands(); i != e; ++i) { + if (ConstantStruct *CS = dyn_cast<ConstantStruct>(InitList->getOperand(i))){ + if (CS->getNumOperands() != 2) return; // Not array of 2-element structs. + + if (CS->getOperand(1)->isNullValue()) + break; // Found a null terminator, stop here. + + ConstantInt *CI = dyn_cast<ConstantInt>(CS->getOperand(0)); + int Priority = CI ? CI->getSExtValue() : 0; + + Constant *FP = CS->getOperand(1); + if (ConstantExpr *CE = dyn_cast<ConstantExpr>(FP)) + if (CE->isCast()) + FP = CE->getOperand(0); + if (Function *F = dyn_cast<Function>(FP)) { + if (!F->isDeclaration()) + M1Tors.push_back(std::make_pair(F, Priority)); + else { + // Map to M2's version of the function. + F = cast<Function>(ValueMap[F]); + M2Tors.push_back(std::make_pair(F, Priority)); + } + } + } + } + + GV->eraseFromParent(); + if (!M1Tors.empty()) { + Constant *M1Init = GetTorInit(M1Tors); + new GlobalVariable(M1Init->getType(), false, GlobalValue::AppendingLinkage, + M1Init, GlobalName, M1); + } + + GV = M2->getNamedGlobal(GlobalName); + assert(GV && "Not a clone of M1?"); + assert(GV->use_empty() && "llvm.ctors shouldn't have uses!"); + + GV->eraseFromParent(); + if (!M2Tors.empty()) { + Constant *M2Init = GetTorInit(M2Tors); + new GlobalVariable(M2Init->getType(), false, GlobalValue::AppendingLinkage, + M2Init, GlobalName, M2); + } +} + + +/// SplitFunctionsOutOfModule - Given a module and a list of functions in the +/// module, split the functions OUT of the specified module, and place them in +/// the new module. +Module * +llvm::SplitFunctionsOutOfModule(Module *M, + const std::vector<Function*> &F, + DenseMap<const Value*, Value*> &ValueMap) { + // Make sure functions & globals are all external so that linkage + // between the two modules will work. + for (Module::iterator I = M->begin(), E = M->end(); I != E; ++I) + I->setLinkage(GlobalValue::ExternalLinkage); + for (Module::global_iterator I = M->global_begin(), E = M->global_end(); + I != E; ++I) { + if (I->hasName() && *I->getNameStart() == '\01') + I->setName(I->getNameStart()+1, I->getNameLen()-1); + I->setLinkage(GlobalValue::ExternalLinkage); + } + + DenseMap<const Value*, Value*> NewValueMap; + Module *New = CloneModule(M, NewValueMap); + + // Make sure global initializers exist only in the safe module (CBE->.so) + for (Module::global_iterator I = New->global_begin(), E = New->global_end(); + I != E; ++I) + I->setInitializer(0); // Delete the initializer to make it external + + // Remove the Test functions from the Safe module + std::set<Function *> TestFunctions; + for (unsigned i = 0, e = F.size(); i != e; ++i) { + Function *TNOF = cast<Function>(ValueMap[F[i]]); + DEBUG(std::cerr << "Removing function "); + DEBUG(WriteAsOperand(std::cerr, TNOF, false)); + DEBUG(std::cerr << "\n"); + TestFunctions.insert(cast<Function>(NewValueMap[TNOF])); + DeleteFunctionBody(TNOF); // Function is now external in this module! + } + + + // Remove the Safe functions from the Test module + for (Module::iterator I = New->begin(), E = New->end(); I != E; ++I) + if (!TestFunctions.count(I)) + DeleteFunctionBody(I); + + + // Make sure that there is a global ctor/dtor array in both halves of the + // module if they both have static ctor/dtor functions. + SplitStaticCtorDtor("llvm.global_ctors", M, New, NewValueMap); + SplitStaticCtorDtor("llvm.global_dtors", M, New, NewValueMap); + + return New; +} + +//===----------------------------------------------------------------------===// +// Basic Block Extraction Code +//===----------------------------------------------------------------------===// + +/// ExtractMappedBlocksFromModule - Extract all but the specified basic blocks +/// into their own functions. The only detail is that M is actually a module +/// cloned from the one the BBs are in, so some mapping needs to be performed. +/// If this operation fails for some reason (ie the implementation is buggy), +/// this function should return null, otherwise it returns a new Module. +Module *BugDriver::ExtractMappedBlocksFromModule(const + std::vector<BasicBlock*> &BBs, + Module *M) { + char *ExtraArg = NULL; + + sys::Path uniqueFilename("bugpoint-extractblocks"); + std::string ErrMsg; + if (uniqueFilename.createTemporaryFileOnDisk(true, &ErrMsg)) { + std::cout << "*** Basic Block extraction failed!\n"; + std::cerr << "Error creating temporary file: " << ErrMsg << "\n"; + M = swapProgramIn(M); + EmitProgressBitcode("basicblockextractfail", true); + swapProgramIn(M); + return 0; + } + sys::RemoveFileOnSignal(uniqueFilename); + + std::ofstream BlocksToNotExtractFile(uniqueFilename.c_str()); + if (!BlocksToNotExtractFile) { + std::cout << "*** Basic Block extraction failed!\n"; + std::cerr << "Error writing list of blocks to not extract: " << ErrMsg + << "\n"; + M = swapProgramIn(M); + EmitProgressBitcode("basicblockextractfail", true); + swapProgramIn(M); + return 0; + } + for (std::vector<BasicBlock*>::const_iterator I = BBs.begin(), E = BBs.end(); + I != E; ++I) { + BasicBlock *BB = *I; + // If the BB doesn't have a name, give it one so we have something to key + // off of. + if (!BB->hasName()) BB->setName("tmpbb"); + BlocksToNotExtractFile << BB->getParent()->getName() << " " + << BB->getName() << "\n"; + } + BlocksToNotExtractFile.close(); + + const char *uniqueFN = uniqueFilename.c_str(); + ExtraArg = (char*)malloc(23 + strlen(uniqueFN)); + strcat(strcpy(ExtraArg, "--extract-blocks-file="), uniqueFN); + + std::vector<const PassInfo*> PI; + std::vector<BasicBlock *> EmptyBBs; // This parameter is ignored. + PI.push_back(getPI(createBlockExtractorPass(EmptyBBs))); + Module *Ret = runPassesOn(M, PI, false, 1, &ExtraArg); + + if (uniqueFilename.exists()) + uniqueFilename.eraseFromDisk(); // Free disk space + free(ExtraArg); + + if (Ret == 0) { + std::cout << "*** Basic Block extraction failed, please report a bug!\n"; + M = swapProgramIn(M); + EmitProgressBitcode("basicblockextractfail", true); + swapProgramIn(M); + } + return Ret; +} diff --git a/tools/bugpoint/FindBugs.cpp b/tools/bugpoint/FindBugs.cpp new file mode 100644 index 0000000..e42cce4 --- /dev/null +++ b/tools/bugpoint/FindBugs.cpp @@ -0,0 +1,112 @@ +//===-- FindBugs.cpp - Run Many Different Optimizations -------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines an interface that allows bugpoint to choose different +// combinations of optimizations to run on the selected input. Bugpoint will +// run these optimizations and record the success/failure of each. This way +// we can hopefully spot bugs in the optimizations. +// +//===----------------------------------------------------------------------===// + +#include "BugDriver.h" +#include "ToolRunner.h" +#include "llvm/Pass.h" +#include <algorithm> +#include <ctime> +#include <iostream> +using namespace llvm; + +/// runManyPasses - Take the specified pass list and create different +/// combinations of passes to compile the program with. Compile the program with +/// each set and mark test to see if it compiled correctly. If the passes +/// compiled correctly output nothing and rearrange the passes into a new order. +/// If the passes did not compile correctly, output the command required to +/// recreate the failure. This returns true if a compiler error is found. +/// +bool BugDriver::runManyPasses(const std::vector<const PassInfo*> &AllPasses) { + setPassesToRun(AllPasses); + std::cout << "Starting bug finding procedure...\n\n"; + + // Creating a reference output if necessary + if (initializeExecutionEnvironment()) return false; + + std::cout << "\n"; + if (ReferenceOutputFile.empty()) { + std::cout << "Generating reference output from raw program: \n"; + if (!createReferenceFile(Program)) + return false; + } + + srand(time(NULL)); + + unsigned num = 1; + while(1) { + // + // Step 1: Randomize the order of the optimizer passes. + // + std::random_shuffle(PassesToRun.begin(), PassesToRun.end()); + + // + // Step 2: Run optimizer passes on the program and check for success. + // + std::cout << "Running selected passes on program to test for crash: "; + for(int i = 0, e = PassesToRun.size(); i != e; i++) { + std::cout << "-" << PassesToRun[i]->getPassArgument( )<< " "; + } + + std::string Filename; + if(runPasses(PassesToRun, Filename, false)) { + std::cout << "\n"; + std::cout << "Optimizer passes caused failure!\n\n"; + debugOptimizerCrash(); + return true; + } else { + std::cout << "Combination " << num << " optimized successfully!\n"; + } + + // + // Step 3: Compile the optimized code. + // + std::cout << "Running the code generator to test for a crash: "; + try { + compileProgram(Program); + std::cout << '\n'; + } catch (ToolExecutionError &TEE) { + std::cout << "\n*** compileProgram threw an exception: "; + std::cout << TEE.what(); + return debugCodeGeneratorCrash(); + } + + // + // Step 4: Run the program and compare its output to the reference + // output (created above). + // + std::cout << "*** Checking if passes caused miscompliation:\n"; + try { + if (diffProgram(Filename, "", false)) { + std::cout << "\n*** diffProgram returned true!\n"; + debugMiscompilation(); + return true; + } else { + std::cout << "\n*** diff'd output matches!\n"; + } + } catch (ToolExecutionError &TEE) { + std::cerr << TEE.what(); + debugCodeGeneratorCrash(); + return true; + } + + sys::Path(Filename).eraseFromDisk(); + + std::cout << "\n\n"; + num++; + } //end while + + // Unreachable. +} diff --git a/tools/bugpoint/ListReducer.h b/tools/bugpoint/ListReducer.h new file mode 100644 index 0000000..de3f389 --- /dev/null +++ b/tools/bugpoint/ListReducer.h @@ -0,0 +1,189 @@ +//===- ListReducer.h - Trim down list while retaining property --*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This class is to be used as a base class for operations that want to zero in +// on a subset of the input which still causes the bug we are tracking. +// +//===----------------------------------------------------------------------===// + +#ifndef BUGPOINT_LIST_REDUCER_H +#define BUGPOINT_LIST_REDUCER_H + +#include <vector> +#include <iostream> +#include <cstdlib> +#include <algorithm> + +namespace llvm { + + extern bool BugpointIsInterrupted; + +template<typename ElTy> +struct ListReducer { + enum TestResult { + NoFailure, // No failure of the predicate was detected + KeepSuffix, // The suffix alone satisfies the predicate + KeepPrefix // The prefix alone satisfies the predicate + }; + + virtual ~ListReducer() {} + + // doTest - This virtual function should be overriden by subclasses to + // implement the test desired. The testcase is only required to test to see + // if the Kept list still satisfies the property, but if it is going to check + // the prefix anyway, it can. + // + virtual TestResult doTest(std::vector<ElTy> &Prefix, + std::vector<ElTy> &Kept) = 0; + + // reduceList - This function attempts to reduce the length of the specified + // list while still maintaining the "test" property. This is the core of the + // "work" that bugpoint does. + // + bool reduceList(std::vector<ElTy> &TheList) { + std::vector<ElTy> empty; + std::srand(0x6e5ea738); // Seed the random number generator + switch (doTest(TheList, empty)) { + case KeepPrefix: + if (TheList.size() == 1) // we are done, it's the base case and it fails + return true; + else + break; // there's definitely an error, but we need to narrow it down + + case KeepSuffix: + // cannot be reached! + std::cerr << "bugpoint ListReducer internal error: selected empty set.\n"; + abort(); + + case NoFailure: + return false; // there is no failure with the full set of passes/funcs! + } + + // Maximal number of allowed splitting iterations, + // before the elements are randomly shuffled. + const unsigned MaxIterationsWithoutProgress = 3; + bool ShufflingEnabled = true; + +Backjump: + unsigned MidTop = TheList.size(); + unsigned MaxIterations = MaxIterationsWithoutProgress; + unsigned NumOfIterationsWithoutProgress = 0; + while (MidTop > 1) { // Binary split reduction loop + // Halt if the user presses ctrl-c. + if (BugpointIsInterrupted) { + std::cerr << "\n\n*** Reduction Interrupted, cleaning up...\n\n"; + return true; + } + + // If the loop doesn't make satisfying progress, try shuffling. + // The purpose of shuffling is to avoid the heavy tails of the + // distribution (improving the speed of convergence). + if (ShufflingEnabled && + NumOfIterationsWithoutProgress > MaxIterations) { + std::vector<ElTy> ShuffledList(TheList); + std::random_shuffle(ShuffledList.begin(), ShuffledList.end()); + std::cerr << "\n\n*** Testing shuffled set...\n\n"; + // Check that random shuffle doesn't loose the bug + if (doTest(ShuffledList, empty) == KeepPrefix) { + // If the bug is still here, use the shuffled list. + TheList.swap(ShuffledList); + MidTop = TheList.size(); + // Must increase the shuffling treshold to avoid the small + // probability of inifinite looping without making progress. + MaxIterations += 2; + std::cerr << "\n\n*** Shuffling does not hide the bug...\n\n"; + } else { + ShufflingEnabled = false; // Disable shuffling further on + std::cerr << "\n\n*** Shuffling hides the bug...\n\n"; + } + NumOfIterationsWithoutProgress = 0; + } + + unsigned Mid = MidTop / 2; + std::vector<ElTy> Prefix(TheList.begin(), TheList.begin()+Mid); + std::vector<ElTy> Suffix(TheList.begin()+Mid, TheList.end()); + + switch (doTest(Prefix, Suffix)) { + case KeepSuffix: + // The property still holds. We can just drop the prefix elements, and + // shorten the list to the "kept" elements. + TheList.swap(Suffix); + MidTop = TheList.size(); + // Reset progress treshold and progress counter + MaxIterations = MaxIterationsWithoutProgress; + NumOfIterationsWithoutProgress = 0; + break; + case KeepPrefix: + // The predicate still holds, shorten the list to the prefix elements. + TheList.swap(Prefix); + MidTop = TheList.size(); + // Reset progress treshold and progress counter + MaxIterations = MaxIterationsWithoutProgress; + NumOfIterationsWithoutProgress = 0; + break; + case NoFailure: + // Otherwise the property doesn't hold. Some of the elements we removed + // must be necessary to maintain the property. + MidTop = Mid; + NumOfIterationsWithoutProgress++; + break; + } + } + + // Probability of backjumping from the trimming loop back to the binary + // split reduction loop. + const int BackjumpProbability = 10; + + // Okay, we trimmed as much off the top and the bottom of the list as we + // could. If there is more than two elements in the list, try deleting + // interior elements and testing that. + // + if (TheList.size() > 2) { + bool Changed = true; + std::vector<ElTy> EmptyList; + while (Changed) { // Trimming loop. + Changed = false; + + // If the binary split reduction loop made an unfortunate sequence of + // splits, the trimming loop might be left off with a huge number of + // remaining elements (large search space). Backjumping out of that + // search space and attempting a different split can significantly + // improve the convergence speed. + if (std::rand() % 100 < BackjumpProbability) + goto Backjump; + + for (unsigned i = 1; i < TheList.size()-1; ++i) { // Check interior elts + if (BugpointIsInterrupted) { + std::cerr << "\n\n*** Reduction Interrupted, cleaning up...\n\n"; + return true; + } + + std::vector<ElTy> TestList(TheList); + TestList.erase(TestList.begin()+i); + + if (doTest(EmptyList, TestList) == KeepSuffix) { + // We can trim down the list! + TheList.swap(TestList); + --i; // Don't skip an element of the list + Changed = true; + } + } + // This can take a long time if left uncontrolled. For now, don't + // iterate. + break; + } + } + + return true; // there are some failure and we've narrowed them down + } +}; + +} // End llvm namespace + +#endif diff --git a/tools/bugpoint/Makefile b/tools/bugpoint/Makefile new file mode 100644 index 0000000..b821b6c --- /dev/null +++ b/tools/bugpoint/Makefile @@ -0,0 +1,17 @@ +##===- tools/bugpoint/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 = bugpoint + +LINK_COMPONENTS := asmparser instrumentation scalaropts ipo \ + linker bitreader bitwriter +REQUIRES_EH := 1 + +include $(LEVEL)/Makefile.common diff --git a/tools/bugpoint/Miscompilation.cpp b/tools/bugpoint/Miscompilation.cpp new file mode 100644 index 0000000..7e8ff78 --- /dev/null +++ b/tools/bugpoint/Miscompilation.cpp @@ -0,0 +1,932 @@ +//===- Miscompilation.cpp - Debug program miscompilations -----------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements optimizer and code generation miscompilation debugging +// support. +// +//===----------------------------------------------------------------------===// + +#include "BugDriver.h" +#include "ListReducer.h" +#include "llvm/Constants.h" +#include "llvm/DerivedTypes.h" +#include "llvm/Instructions.h" +#include "llvm/Linker.h" +#include "llvm/Module.h" +#include "llvm/Pass.h" +#include "llvm/Analysis/Verifier.h" +#include "llvm/Support/Mangler.h" +#include "llvm/Transforms/Utils/Cloning.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/FileUtilities.h" +#include "llvm/Config/config.h" // for HAVE_LINK_R +using namespace llvm; + +namespace llvm { + extern cl::list<std::string> InputArgv; +} + +namespace { + static llvm::cl::opt<bool> + DisableLoopExtraction("disable-loop-extraction", + cl::desc("Don't extract loops when searching for miscompilations"), + cl::init(false)); + + class ReduceMiscompilingPasses : public ListReducer<const PassInfo*> { + BugDriver &BD; + public: + ReduceMiscompilingPasses(BugDriver &bd) : BD(bd) {} + + virtual TestResult doTest(std::vector<const PassInfo*> &Prefix, + std::vector<const PassInfo*> &Suffix); + }; +} + +/// TestResult - After passes have been split into a test group and a control +/// group, see if they still break the program. +/// +ReduceMiscompilingPasses::TestResult +ReduceMiscompilingPasses::doTest(std::vector<const PassInfo*> &Prefix, + std::vector<const PassInfo*> &Suffix) { + // First, run the program with just the Suffix passes. If it is still broken + // with JUST the kept passes, discard the prefix passes. + std::cout << "Checking to see if '" << getPassesString(Suffix) + << "' compile correctly: "; + + std::string BitcodeResult; + if (BD.runPasses(Suffix, BitcodeResult, false/*delete*/, true/*quiet*/)) { + std::cerr << " Error running this sequence of passes" + << " on the input program!\n"; + BD.setPassesToRun(Suffix); + BD.EmitProgressBitcode("pass-error", false); + exit(BD.debugOptimizerCrash()); + } + + // Check to see if the finished program matches the reference output... + if (BD.diffProgram(BitcodeResult, "", true /*delete bitcode*/)) { + std::cout << " nope.\n"; + if (Suffix.empty()) { + std::cerr << BD.getToolName() << ": I'm confused: the test fails when " + << "no passes are run, nondeterministic program?\n"; + exit(1); + } + return KeepSuffix; // Miscompilation detected! + } + std::cout << " yup.\n"; // No miscompilation! + + if (Prefix.empty()) return NoFailure; + + // Next, see if the program is broken if we run the "prefix" passes first, + // then separately run the "kept" passes. + std::cout << "Checking to see if '" << getPassesString(Prefix) + << "' compile correctly: "; + + // If it is not broken with the kept passes, it's possible that the prefix + // passes must be run before the kept passes to break it. If the program + // WORKS after the prefix passes, but then fails if running the prefix AND + // kept passes, we can update our bitcode file to include the result of the + // prefix passes, then discard the prefix passes. + // + if (BD.runPasses(Prefix, BitcodeResult, false/*delete*/, true/*quiet*/)) { + std::cerr << " Error running this sequence of passes" + << " on the input program!\n"; + BD.setPassesToRun(Prefix); + BD.EmitProgressBitcode("pass-error", false); + exit(BD.debugOptimizerCrash()); + } + + // If the prefix maintains the predicate by itself, only keep the prefix! + if (BD.diffProgram(BitcodeResult)) { + std::cout << " nope.\n"; + sys::Path(BitcodeResult).eraseFromDisk(); + return KeepPrefix; + } + std::cout << " yup.\n"; // No miscompilation! + + // Ok, so now we know that the prefix passes work, try running the suffix + // passes on the result of the prefix passes. + // + Module *PrefixOutput = ParseInputFile(BitcodeResult); + if (PrefixOutput == 0) { + std::cerr << BD.getToolName() << ": Error reading bitcode file '" + << BitcodeResult << "'!\n"; + exit(1); + } + sys::Path(BitcodeResult).eraseFromDisk(); // No longer need the file on disk + + // Don't check if there are no passes in the suffix. + if (Suffix.empty()) + return NoFailure; + + std::cout << "Checking to see if '" << getPassesString(Suffix) + << "' passes compile correctly after the '" + << getPassesString(Prefix) << "' passes: "; + + Module *OriginalInput = BD.swapProgramIn(PrefixOutput); + if (BD.runPasses(Suffix, BitcodeResult, false/*delete*/, true/*quiet*/)) { + std::cerr << " Error running this sequence of passes" + << " on the input program!\n"; + BD.setPassesToRun(Suffix); + BD.EmitProgressBitcode("pass-error", false); + exit(BD.debugOptimizerCrash()); + } + + // Run the result... + if (BD.diffProgram(BitcodeResult, "", true/*delete bitcode*/)) { + std::cout << " nope.\n"; + delete OriginalInput; // We pruned down the original input... + return KeepSuffix; + } + + // Otherwise, we must not be running the bad pass anymore. + std::cout << " yup.\n"; // No miscompilation! + delete BD.swapProgramIn(OriginalInput); // Restore orig program & free test + return NoFailure; +} + +namespace { + class ReduceMiscompilingFunctions : public ListReducer<Function*> { + BugDriver &BD; + bool (*TestFn)(BugDriver &, Module *, Module *); + public: + ReduceMiscompilingFunctions(BugDriver &bd, + bool (*F)(BugDriver &, Module *, Module *)) + : BD(bd), TestFn(F) {} + + virtual TestResult doTest(std::vector<Function*> &Prefix, + std::vector<Function*> &Suffix) { + if (!Suffix.empty() && TestFuncs(Suffix)) + return KeepSuffix; + if (!Prefix.empty() && TestFuncs(Prefix)) + return KeepPrefix; + return NoFailure; + } + + bool TestFuncs(const std::vector<Function*> &Prefix); + }; +} + +/// TestMergedProgram - Given two modules, link them together and run the +/// program, checking to see if the program matches the diff. If the diff +/// matches, return false, otherwise return true. If the DeleteInputs argument +/// is set to true then this function deletes both input modules before it +/// returns. +/// +static bool TestMergedProgram(BugDriver &BD, Module *M1, Module *M2, + bool DeleteInputs) { + // Link the two portions of the program back to together. + std::string ErrorMsg; + if (!DeleteInputs) { + M1 = CloneModule(M1); + M2 = CloneModule(M2); + } + if (Linker::LinkModules(M1, M2, &ErrorMsg)) { + std::cerr << BD.getToolName() << ": Error linking modules together:" + << ErrorMsg << '\n'; + exit(1); + } + delete M2; // We are done with this module. + + Module *OldProgram = BD.swapProgramIn(M1); + + // Execute the program. If it does not match the expected output, we must + // return true. + bool Broken = BD.diffProgram(); + + // Delete the linked module & restore the original + BD.swapProgramIn(OldProgram); + delete M1; + return Broken; +} + +/// TestFuncs - split functions in a Module into two groups: those that are +/// under consideration for miscompilation vs. those that are not, and test +/// accordingly. Each group of functions becomes a separate Module. +/// +bool ReduceMiscompilingFunctions::TestFuncs(const std::vector<Function*>&Funcs){ + // Test to see if the function is misoptimized if we ONLY run it on the + // functions listed in Funcs. + std::cout << "Checking to see if the program is misoptimized when " + << (Funcs.size()==1 ? "this function is" : "these functions are") + << " run through the pass" + << (BD.getPassesToRun().size() == 1 ? "" : "es") << ":"; + PrintFunctionList(Funcs); + std::cout << '\n'; + + // Split the module into the two halves of the program we want. + DenseMap<const Value*, Value*> ValueMap; + Module *ToNotOptimize = CloneModule(BD.getProgram(), ValueMap); + Module *ToOptimize = SplitFunctionsOutOfModule(ToNotOptimize, Funcs, + ValueMap); + + // Run the predicate, note that the predicate will delete both input modules. + return TestFn(BD, ToOptimize, ToNotOptimize); +} + +/// DisambiguateGlobalSymbols - Mangle symbols to guarantee uniqueness by +/// modifying predominantly internal symbols rather than external ones. +/// +static void DisambiguateGlobalSymbols(Module *M) { + // Try not to cause collisions by minimizing chances of renaming an + // already-external symbol, so take in external globals and functions as-is. + // The code should work correctly without disambiguation (assuming the same + // mangler is used by the two code generators), but having symbols with the + // same name causes warnings to be emitted by the code generator. + Mangler Mang(*M); + // Agree with the CBE on symbol naming + Mang.markCharUnacceptable('.'); + Mang.setPreserveAsmNames(true); + for (Module::global_iterator I = M->global_begin(), E = M->global_end(); + I != E; ++I) + I->setName(Mang.getValueName(I)); + for (Module::iterator I = M->begin(), E = M->end(); I != E; ++I) + I->setName(Mang.getValueName(I)); +} + +/// ExtractLoops - Given a reduced list of functions that still exposed the bug, +/// check to see if we can extract the loops in the region without obscuring the +/// bug. If so, it reduces the amount of code identified. +/// +static bool ExtractLoops(BugDriver &BD, + bool (*TestFn)(BugDriver &, Module *, Module *), + std::vector<Function*> &MiscompiledFunctions) { + bool MadeChange = false; + while (1) { + if (BugpointIsInterrupted) return MadeChange; + + DenseMap<const Value*, Value*> ValueMap; + Module *ToNotOptimize = CloneModule(BD.getProgram(), ValueMap); + Module *ToOptimize = SplitFunctionsOutOfModule(ToNotOptimize, + MiscompiledFunctions, + ValueMap); + Module *ToOptimizeLoopExtracted = BD.ExtractLoop(ToOptimize); + if (!ToOptimizeLoopExtracted) { + // If the loop extractor crashed or if there were no extractible loops, + // then this chapter of our odyssey is over with. + delete ToNotOptimize; + delete ToOptimize; + return MadeChange; + } + + std::cerr << "Extracted a loop from the breaking portion of the program.\n"; + + // Bugpoint is intentionally not very trusting of LLVM transformations. In + // particular, we're not going to assume that the loop extractor works, so + // we're going to test the newly loop extracted program to make sure nothing + // has broken. If something broke, then we'll inform the user and stop + // extraction. + AbstractInterpreter *AI = BD.switchToSafeInterpreter(); + if (TestMergedProgram(BD, ToOptimizeLoopExtracted, ToNotOptimize, false)) { + BD.switchToInterpreter(AI); + + // Merged program doesn't work anymore! + std::cerr << " *** ERROR: Loop extraction broke the program. :(" + << " Please report a bug!\n"; + std::cerr << " Continuing on with un-loop-extracted version.\n"; + + BD.writeProgramToFile("bugpoint-loop-extract-fail-tno.bc", ToNotOptimize); + BD.writeProgramToFile("bugpoint-loop-extract-fail-to.bc", ToOptimize); + BD.writeProgramToFile("bugpoint-loop-extract-fail-to-le.bc", + ToOptimizeLoopExtracted); + + std::cerr << "Please submit the bugpoint-loop-extract-fail-*.bc files.\n"; + delete ToOptimize; + delete ToNotOptimize; + delete ToOptimizeLoopExtracted; + return MadeChange; + } + delete ToOptimize; + BD.switchToInterpreter(AI); + + std::cout << " Testing after loop extraction:\n"; + // Clone modules, the tester function will free them. + Module *TOLEBackup = CloneModule(ToOptimizeLoopExtracted); + Module *TNOBackup = CloneModule(ToNotOptimize); + if (!TestFn(BD, ToOptimizeLoopExtracted, ToNotOptimize)) { + std::cout << "*** Loop extraction masked the problem. Undoing.\n"; + // If the program is not still broken, then loop extraction did something + // that masked the error. Stop loop extraction now. + delete TOLEBackup; + delete TNOBackup; + return MadeChange; + } + ToOptimizeLoopExtracted = TOLEBackup; + ToNotOptimize = TNOBackup; + + std::cout << "*** Loop extraction successful!\n"; + + std::vector<std::pair<std::string, const FunctionType*> > MisCompFunctions; + for (Module::iterator I = ToOptimizeLoopExtracted->begin(), + E = ToOptimizeLoopExtracted->end(); I != E; ++I) + if (!I->isDeclaration()) + MisCompFunctions.push_back(std::make_pair(I->getName(), + I->getFunctionType())); + + // Okay, great! Now we know that we extracted a loop and that loop + // extraction both didn't break the program, and didn't mask the problem. + // Replace the current program with the loop extracted version, and try to + // extract another loop. + std::string ErrorMsg; + if (Linker::LinkModules(ToNotOptimize, ToOptimizeLoopExtracted, &ErrorMsg)){ + std::cerr << BD.getToolName() << ": Error linking modules together:" + << ErrorMsg << '\n'; + exit(1); + } + delete ToOptimizeLoopExtracted; + + // All of the Function*'s in the MiscompiledFunctions list are in the old + // module. Update this list to include all of the functions in the + // optimized and loop extracted module. + MiscompiledFunctions.clear(); + for (unsigned i = 0, e = MisCompFunctions.size(); i != e; ++i) { + Function *NewF = ToNotOptimize->getFunction(MisCompFunctions[i].first); + + assert(NewF && "Function not found??"); + assert(NewF->getFunctionType() == MisCompFunctions[i].second && + "found wrong function type?"); + MiscompiledFunctions.push_back(NewF); + } + + BD.setNewProgram(ToNotOptimize); + MadeChange = true; + } +} + +namespace { + class ReduceMiscompiledBlocks : public ListReducer<BasicBlock*> { + BugDriver &BD; + bool (*TestFn)(BugDriver &, Module *, Module *); + std::vector<Function*> FunctionsBeingTested; + public: + ReduceMiscompiledBlocks(BugDriver &bd, + bool (*F)(BugDriver &, Module *, Module *), + const std::vector<Function*> &Fns) + : BD(bd), TestFn(F), FunctionsBeingTested(Fns) {} + + virtual TestResult doTest(std::vector<BasicBlock*> &Prefix, + std::vector<BasicBlock*> &Suffix) { + if (!Suffix.empty() && TestFuncs(Suffix)) + return KeepSuffix; + if (TestFuncs(Prefix)) + return KeepPrefix; + return NoFailure; + } + + bool TestFuncs(const std::vector<BasicBlock*> &Prefix); + }; +} + +/// TestFuncs - Extract all blocks for the miscompiled functions except for the +/// specified blocks. If the problem still exists, return true. +/// +bool ReduceMiscompiledBlocks::TestFuncs(const std::vector<BasicBlock*> &BBs) { + // Test to see if the function is misoptimized if we ONLY run it on the + // functions listed in Funcs. + std::cout << "Checking to see if the program is misoptimized when all "; + if (!BBs.empty()) { + std::cout << "but these " << BBs.size() << " blocks are extracted: "; + for (unsigned i = 0, e = BBs.size() < 10 ? BBs.size() : 10; i != e; ++i) + std::cout << BBs[i]->getName() << " "; + if (BBs.size() > 10) std::cout << "..."; + } else { + std::cout << "blocks are extracted."; + } + std::cout << '\n'; + + // Split the module into the two halves of the program we want. + DenseMap<const Value*, Value*> ValueMap; + Module *ToNotOptimize = CloneModule(BD.getProgram(), ValueMap); + Module *ToOptimize = SplitFunctionsOutOfModule(ToNotOptimize, + FunctionsBeingTested, + ValueMap); + + // Try the extraction. If it doesn't work, then the block extractor crashed + // or something, in which case bugpoint can't chase down this possibility. + if (Module *New = BD.ExtractMappedBlocksFromModule(BBs, ToOptimize)) { + delete ToOptimize; + // Run the predicate, not that the predicate will delete both input modules. + return TestFn(BD, New, ToNotOptimize); + } + delete ToOptimize; + delete ToNotOptimize; + return false; +} + + +/// ExtractBlocks - Given a reduced list of functions that still expose the bug, +/// extract as many basic blocks from the region as possible without obscuring +/// the bug. +/// +static bool ExtractBlocks(BugDriver &BD, + bool (*TestFn)(BugDriver &, Module *, Module *), + std::vector<Function*> &MiscompiledFunctions) { + if (BugpointIsInterrupted) return false; + + std::vector<BasicBlock*> Blocks; + for (unsigned i = 0, e = MiscompiledFunctions.size(); i != e; ++i) + for (Function::iterator I = MiscompiledFunctions[i]->begin(), + E = MiscompiledFunctions[i]->end(); I != E; ++I) + Blocks.push_back(I); + + // Use the list reducer to identify blocks that can be extracted without + // obscuring the bug. The Blocks list will end up containing blocks that must + // be retained from the original program. + unsigned OldSize = Blocks.size(); + + // Check to see if all blocks are extractible first. + if (ReduceMiscompiledBlocks(BD, TestFn, + MiscompiledFunctions).TestFuncs(std::vector<BasicBlock*>())) { + Blocks.clear(); + } else { + ReduceMiscompiledBlocks(BD, TestFn,MiscompiledFunctions).reduceList(Blocks); + if (Blocks.size() == OldSize) + return false; + } + + DenseMap<const Value*, Value*> ValueMap; + Module *ProgClone = CloneModule(BD.getProgram(), ValueMap); + Module *ToExtract = SplitFunctionsOutOfModule(ProgClone, + MiscompiledFunctions, + ValueMap); + Module *Extracted = BD.ExtractMappedBlocksFromModule(Blocks, ToExtract); + if (Extracted == 0) { + // Weird, extraction should have worked. + std::cerr << "Nondeterministic problem extracting blocks??\n"; + delete ProgClone; + delete ToExtract; + return false; + } + + // Otherwise, block extraction succeeded. Link the two program fragments back + // together. + delete ToExtract; + + std::vector<std::pair<std::string, const FunctionType*> > MisCompFunctions; + for (Module::iterator I = Extracted->begin(), E = Extracted->end(); + I != E; ++I) + if (!I->isDeclaration()) + MisCompFunctions.push_back(std::make_pair(I->getName(), + I->getFunctionType())); + + std::string ErrorMsg; + if (Linker::LinkModules(ProgClone, Extracted, &ErrorMsg)) { + std::cerr << BD.getToolName() << ": Error linking modules together:" + << ErrorMsg << '\n'; + exit(1); + } + delete Extracted; + + // Set the new program and delete the old one. + BD.setNewProgram(ProgClone); + + // Update the list of miscompiled functions. + MiscompiledFunctions.clear(); + + for (unsigned i = 0, e = MisCompFunctions.size(); i != e; ++i) { + Function *NewF = ProgClone->getFunction(MisCompFunctions[i].first); + assert(NewF && "Function not found??"); + assert(NewF->getFunctionType() == MisCompFunctions[i].second && + "Function has wrong type??"); + MiscompiledFunctions.push_back(NewF); + } + + return true; +} + + +/// DebugAMiscompilation - This is a generic driver to narrow down +/// miscompilations, either in an optimization or a code generator. +/// +static std::vector<Function*> +DebugAMiscompilation(BugDriver &BD, + bool (*TestFn)(BugDriver &, Module *, Module *)) { + // Okay, now that we have reduced the list of passes which are causing the + // failure, see if we can pin down which functions are being + // miscompiled... first build a list of all of the non-external functions in + // the program. + std::vector<Function*> MiscompiledFunctions; + Module *Prog = BD.getProgram(); + for (Module::iterator I = Prog->begin(), E = Prog->end(); I != E; ++I) + if (!I->isDeclaration()) + MiscompiledFunctions.push_back(I); + + // Do the reduction... + if (!BugpointIsInterrupted) + ReduceMiscompilingFunctions(BD, TestFn).reduceList(MiscompiledFunctions); + + std::cout << "\n*** The following function" + << (MiscompiledFunctions.size() == 1 ? " is" : "s are") + << " being miscompiled: "; + PrintFunctionList(MiscompiledFunctions); + std::cout << '\n'; + + // See if we can rip any loops out of the miscompiled functions and still + // trigger the problem. + + if (!BugpointIsInterrupted && !DisableLoopExtraction && + ExtractLoops(BD, TestFn, MiscompiledFunctions)) { + // Okay, we extracted some loops and the problem still appears. See if we + // can eliminate some of the created functions from being candidates. + + // Loop extraction can introduce functions with the same name (foo_code). + // Make sure to disambiguate the symbols so that when the program is split + // apart that we can link it back together again. + DisambiguateGlobalSymbols(BD.getProgram()); + + // Do the reduction... + if (!BugpointIsInterrupted) + ReduceMiscompilingFunctions(BD, TestFn).reduceList(MiscompiledFunctions); + + std::cout << "\n*** The following function" + << (MiscompiledFunctions.size() == 1 ? " is" : "s are") + << " being miscompiled: "; + PrintFunctionList(MiscompiledFunctions); + std::cout << '\n'; + } + + if (!BugpointIsInterrupted && + ExtractBlocks(BD, TestFn, MiscompiledFunctions)) { + // Okay, we extracted some blocks and the problem still appears. See if we + // can eliminate some of the created functions from being candidates. + + // Block extraction can introduce functions with the same name (foo_code). + // Make sure to disambiguate the symbols so that when the program is split + // apart that we can link it back together again. + DisambiguateGlobalSymbols(BD.getProgram()); + + // Do the reduction... + ReduceMiscompilingFunctions(BD, TestFn).reduceList(MiscompiledFunctions); + + std::cout << "\n*** The following function" + << (MiscompiledFunctions.size() == 1 ? " is" : "s are") + << " being miscompiled: "; + PrintFunctionList(MiscompiledFunctions); + std::cout << '\n'; + } + + return MiscompiledFunctions; +} + +/// TestOptimizer - This is the predicate function used to check to see if the +/// "Test" portion of the program is misoptimized. If so, return true. In any +/// case, both module arguments are deleted. +/// +static bool TestOptimizer(BugDriver &BD, Module *Test, Module *Safe) { + // Run the optimization passes on ToOptimize, producing a transformed version + // of the functions being tested. + std::cout << " Optimizing functions being tested: "; + Module *Optimized = BD.runPassesOn(Test, BD.getPassesToRun(), + /*AutoDebugCrashes*/true); + std::cout << "done.\n"; + delete Test; + + std::cout << " Checking to see if the merged program executes correctly: "; + bool Broken = TestMergedProgram(BD, Optimized, Safe, true); + std::cout << (Broken ? " nope.\n" : " yup.\n"); + return Broken; +} + + +/// debugMiscompilation - This method is used when the passes selected are not +/// crashing, but the generated output is semantically different from the +/// input. +/// +bool BugDriver::debugMiscompilation() { + // Make sure something was miscompiled... + if (!BugpointIsInterrupted) + if (!ReduceMiscompilingPasses(*this).reduceList(PassesToRun)) { + std::cerr << "*** Optimized program matches reference output! No problem" + << " detected...\nbugpoint can't help you with your problem!\n"; + return false; + } + + std::cout << "\n*** Found miscompiling pass" + << (getPassesToRun().size() == 1 ? "" : "es") << ": " + << getPassesString(getPassesToRun()) << '\n'; + EmitProgressBitcode("passinput"); + + std::vector<Function*> MiscompiledFunctions = + DebugAMiscompilation(*this, TestOptimizer); + + // Output a bunch of bitcode files for the user... + std::cout << "Outputting reduced bitcode files which expose the problem:\n"; + DenseMap<const Value*, Value*> ValueMap; + Module *ToNotOptimize = CloneModule(getProgram(), ValueMap); + Module *ToOptimize = SplitFunctionsOutOfModule(ToNotOptimize, + MiscompiledFunctions, + ValueMap); + + std::cout << " Non-optimized portion: "; + ToNotOptimize = swapProgramIn(ToNotOptimize); + EmitProgressBitcode("tonotoptimize", true); + setNewProgram(ToNotOptimize); // Delete hacked module. + + std::cout << " Portion that is input to optimizer: "; + ToOptimize = swapProgramIn(ToOptimize); + EmitProgressBitcode("tooptimize"); + setNewProgram(ToOptimize); // Delete hacked module. + + return false; +} + +/// CleanupAndPrepareModules - Get the specified modules ready for code +/// generator testing. +/// +static void CleanupAndPrepareModules(BugDriver &BD, Module *&Test, + Module *Safe) { + // Clean up the modules, removing extra cruft that we don't need anymore... + Test = BD.performFinalCleanups(Test); + + // If we are executing the JIT, we have several nasty issues to take care of. + if (!BD.isExecutingJIT()) return; + + // First, if the main function is in the Safe module, we must add a stub to + // the Test module to call into it. Thus, we create a new function `main' + // which just calls the old one. + if (Function *oldMain = Safe->getFunction("main")) + if (!oldMain->isDeclaration()) { + // Rename it + oldMain->setName("llvm_bugpoint_old_main"); + // Create a NEW `main' function with same type in the test module. + Function *newMain = Function::Create(oldMain->getFunctionType(), + GlobalValue::ExternalLinkage, + "main", Test); + // Create an `oldmain' prototype in the test module, which will + // corresponds to the real main function in the same module. + Function *oldMainProto = Function::Create(oldMain->getFunctionType(), + GlobalValue::ExternalLinkage, + oldMain->getName(), Test); + // Set up and remember the argument list for the main function. + std::vector<Value*> args; + for (Function::arg_iterator + I = newMain->arg_begin(), E = newMain->arg_end(), + OI = oldMain->arg_begin(); I != E; ++I, ++OI) { + I->setName(OI->getName()); // Copy argument names from oldMain + args.push_back(I); + } + + // Call the old main function and return its result + BasicBlock *BB = BasicBlock::Create("entry", newMain); + CallInst *call = CallInst::Create(oldMainProto, args.begin(), args.end(), + "", BB); + + // If the type of old function wasn't void, return value of call + ReturnInst::Create(call, BB); + } + + // The second nasty issue we must deal with in the JIT is that the Safe + // module cannot directly reference any functions defined in the test + // module. Instead, we use a JIT API call to dynamically resolve the + // symbol. + + // Add the resolver to the Safe module. + // Prototype: void *getPointerToNamedFunction(const char* Name) + Constant *resolverFunc = + Safe->getOrInsertFunction("getPointerToNamedFunction", + PointerType::getUnqual(Type::Int8Ty), + PointerType::getUnqual(Type::Int8Ty), (Type *)0); + + // Use the function we just added to get addresses of functions we need. + for (Module::iterator F = Safe->begin(), E = Safe->end(); F != E; ++F) { + if (F->isDeclaration() && !F->use_empty() && &*F != resolverFunc && + !F->isIntrinsic() /* ignore intrinsics */) { + Function *TestFn = Test->getFunction(F->getName()); + + // Don't forward functions which are external in the test module too. + if (TestFn && !TestFn->isDeclaration()) { + // 1. Add a string constant with its name to the global file + Constant *InitArray = ConstantArray::get(F->getName()); + GlobalVariable *funcName = + new GlobalVariable(InitArray->getType(), true /*isConstant*/, + GlobalValue::InternalLinkage, InitArray, + F->getName() + "_name", Safe); + + // 2. Use `GetElementPtr *funcName, 0, 0' to convert the string to an + // sbyte* so it matches the signature of the resolver function. + + // GetElementPtr *funcName, ulong 0, ulong 0 + std::vector<Constant*> GEPargs(2,Constant::getNullValue(Type::Int32Ty)); + Value *GEP = ConstantExpr::getGetElementPtr(funcName, &GEPargs[0], 2); + std::vector<Value*> ResolverArgs; + ResolverArgs.push_back(GEP); + + // Rewrite uses of F in global initializers, etc. to uses of a wrapper + // function that dynamically resolves the calls to F via our JIT API + if (!F->use_empty()) { + // Create a new global to hold the cached function pointer. + Constant *NullPtr = ConstantPointerNull::get(F->getType()); + GlobalVariable *Cache = + new GlobalVariable(F->getType(), false,GlobalValue::InternalLinkage, + NullPtr,F->getName()+".fpcache", F->getParent()); + + // Construct a new stub function that will re-route calls to F + const FunctionType *FuncTy = F->getFunctionType(); + Function *FuncWrapper = Function::Create(FuncTy, + GlobalValue::InternalLinkage, + F->getName() + "_wrapper", + F->getParent()); + BasicBlock *EntryBB = BasicBlock::Create("entry", FuncWrapper); + BasicBlock *DoCallBB = BasicBlock::Create("usecache", FuncWrapper); + BasicBlock *LookupBB = BasicBlock::Create("lookupfp", FuncWrapper); + + // Check to see if we already looked up the value. + Value *CachedVal = new LoadInst(Cache, "fpcache", EntryBB); + Value *IsNull = new ICmpInst(ICmpInst::ICMP_EQ, CachedVal, + NullPtr, "isNull", EntryBB); + BranchInst::Create(LookupBB, DoCallBB, IsNull, EntryBB); + + // Resolve the call to function F via the JIT API: + // + // call resolver(GetElementPtr...) + CallInst *Resolver = + CallInst::Create(resolverFunc, ResolverArgs.begin(), + ResolverArgs.end(), "resolver", LookupBB); + + // Cast the result from the resolver to correctly-typed function. + CastInst *CastedResolver = + new BitCastInst(Resolver, + PointerType::getUnqual(F->getFunctionType()), + "resolverCast", LookupBB); + + // Save the value in our cache. + new StoreInst(CastedResolver, Cache, LookupBB); + BranchInst::Create(DoCallBB, LookupBB); + + PHINode *FuncPtr = PHINode::Create(NullPtr->getType(), + "fp", DoCallBB); + FuncPtr->addIncoming(CastedResolver, LookupBB); + FuncPtr->addIncoming(CachedVal, EntryBB); + + // Save the argument list. + std::vector<Value*> Args; + for (Function::arg_iterator i = FuncWrapper->arg_begin(), + e = FuncWrapper->arg_end(); i != e; ++i) + Args.push_back(i); + + // Pass on the arguments to the real function, return its result + if (F->getReturnType() == Type::VoidTy) { + CallInst::Create(FuncPtr, Args.begin(), Args.end(), "", DoCallBB); + ReturnInst::Create(DoCallBB); + } else { + CallInst *Call = CallInst::Create(FuncPtr, Args.begin(), Args.end(), + "retval", DoCallBB); + ReturnInst::Create(Call, DoCallBB); + } + + // Use the wrapper function instead of the old function + F->replaceAllUsesWith(FuncWrapper); + } + } + } + } + + if (verifyModule(*Test) || verifyModule(*Safe)) { + std::cerr << "Bugpoint has a bug, which corrupted a module!!\n"; + abort(); + } +} + + + +/// TestCodeGenerator - This is the predicate function used to check to see if +/// the "Test" portion of the program is miscompiled by the code generator under +/// test. If so, return true. In any case, both module arguments are deleted. +/// +static bool TestCodeGenerator(BugDriver &BD, Module *Test, Module *Safe) { + CleanupAndPrepareModules(BD, Test, Safe); + + sys::Path TestModuleBC("bugpoint.test.bc"); + std::string ErrMsg; + if (TestModuleBC.makeUnique(true, &ErrMsg)) { + std::cerr << BD.getToolName() << "Error making unique filename: " + << ErrMsg << "\n"; + exit(1); + } + if (BD.writeProgramToFile(TestModuleBC.toString(), Test)) { + std::cerr << "Error writing bitcode to `" << TestModuleBC << "'\nExiting."; + exit(1); + } + delete Test; + + // Make the shared library + sys::Path SafeModuleBC("bugpoint.safe.bc"); + if (SafeModuleBC.makeUnique(true, &ErrMsg)) { + std::cerr << BD.getToolName() << "Error making unique filename: " + << ErrMsg << "\n"; + exit(1); + } + + if (BD.writeProgramToFile(SafeModuleBC.toString(), Safe)) { + std::cerr << "Error writing bitcode to `" << SafeModuleBC << "'\nExiting."; + exit(1); + } + std::string SharedObject = BD.compileSharedObject(SafeModuleBC.toString()); + delete Safe; + + // Run the code generator on the `Test' code, loading the shared library. + // The function returns whether or not the new output differs from reference. + int Result = BD.diffProgram(TestModuleBC.toString(), SharedObject, false); + + if (Result) + std::cerr << ": still failing!\n"; + else + std::cerr << ": didn't fail.\n"; + TestModuleBC.eraseFromDisk(); + SafeModuleBC.eraseFromDisk(); + sys::Path(SharedObject).eraseFromDisk(); + + return Result; +} + + +/// debugCodeGenerator - debug errors in LLC, LLI, or CBE. +/// +bool BugDriver::debugCodeGenerator() { + if ((void*)SafeInterpreter == (void*)Interpreter) { + std::string Result = executeProgramSafely("bugpoint.safe.out"); + std::cout << "\n*** The \"safe\" i.e. 'known good' backend cannot match " + << "the reference diff. This may be due to a\n front-end " + << "bug or a bug in the original program, but this can also " + << "happen if bugpoint isn't running the program with the " + << "right flags or input.\n I left the result of executing " + << "the program with the \"safe\" backend in this file for " + << "you: '" + << Result << "'.\n"; + return true; + } + + DisambiguateGlobalSymbols(Program); + + std::vector<Function*> Funcs = DebugAMiscompilation(*this, TestCodeGenerator); + + // Split the module into the two halves of the program we want. + DenseMap<const Value*, Value*> ValueMap; + Module *ToNotCodeGen = CloneModule(getProgram(), ValueMap); + Module *ToCodeGen = SplitFunctionsOutOfModule(ToNotCodeGen, Funcs, ValueMap); + + // Condition the modules + CleanupAndPrepareModules(*this, ToCodeGen, ToNotCodeGen); + + sys::Path TestModuleBC("bugpoint.test.bc"); + std::string ErrMsg; + if (TestModuleBC.makeUnique(true, &ErrMsg)) { + std::cerr << getToolName() << "Error making unique filename: " + << ErrMsg << "\n"; + exit(1); + } + + if (writeProgramToFile(TestModuleBC.toString(), ToCodeGen)) { + std::cerr << "Error writing bitcode to `" << TestModuleBC << "'\nExiting."; + exit(1); + } + delete ToCodeGen; + + // Make the shared library + sys::Path SafeModuleBC("bugpoint.safe.bc"); + if (SafeModuleBC.makeUnique(true, &ErrMsg)) { + std::cerr << getToolName() << "Error making unique filename: " + << ErrMsg << "\n"; + exit(1); + } + + if (writeProgramToFile(SafeModuleBC.toString(), ToNotCodeGen)) { + std::cerr << "Error writing bitcode to `" << SafeModuleBC << "'\nExiting."; + exit(1); + } + std::string SharedObject = compileSharedObject(SafeModuleBC.toString()); + delete ToNotCodeGen; + + std::cout << "You can reproduce the problem with the command line: \n"; + if (isExecutingJIT()) { + std::cout << " lli -load " << SharedObject << " " << TestModuleBC; + } else { + std::cout << " llc -f " << TestModuleBC << " -o " << TestModuleBC<< ".s\n"; + std::cout << " gcc " << SharedObject << " " << TestModuleBC + << ".s -o " << TestModuleBC << ".exe"; +#if defined (HAVE_LINK_R) + std::cout << " -Wl,-R."; +#endif + std::cout << "\n"; + std::cout << " " << TestModuleBC << ".exe"; + } + for (unsigned i=0, e = InputArgv.size(); i != e; ++i) + std::cout << " " << InputArgv[i]; + std::cout << '\n'; + std::cout << "The shared object was created with:\n llc -march=c " + << SafeModuleBC << " -o temporary.c\n" + << " gcc -xc temporary.c -O2 -o " << SharedObject +#if defined(sparc) || defined(__sparc__) || defined(__sparcv9) + << " -G" // Compile a shared library, `-G' for Sparc +#else + << " -fPIC -shared" // `-shared' for Linux/X86, maybe others +#endif + << " -fno-strict-aliasing\n"; + + return false; +} diff --git a/tools/bugpoint/OptimizerDriver.cpp b/tools/bugpoint/OptimizerDriver.cpp new file mode 100644 index 0000000..3ded5e8 --- /dev/null +++ b/tools/bugpoint/OptimizerDriver.cpp @@ -0,0 +1,266 @@ +//===- OptimizerDriver.cpp - Allow BugPoint to run passes safely ----------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines an interface that allows bugpoint to run various passes +// without the threat of a buggy pass corrupting bugpoint (of course, bugpoint +// may have its own bugs, but that's another story...). It achieves this by +// forking a copy of itself and having the child process do the optimizations. +// If this client dies, we can always fork a new one. :) +// +//===----------------------------------------------------------------------===// + +// Note: as a short term hack, the old Unix-specific code and platform- +// independent code co-exist via conditional compilation until it is verified +// that the new code works correctly on Unix. + +#include "BugDriver.h" +#include "llvm/Module.h" +#include "llvm/PassManager.h" +#include "llvm/Analysis/Verifier.h" +#include "llvm/Bitcode/ReaderWriter.h" +#include "llvm/Target/TargetData.h" +#include "llvm/Support/FileUtilities.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Streams.h" +#include "llvm/System/Path.h" +#include "llvm/System/Program.h" +#include "llvm/Config/alloca.h" + +#define DONT_GET_PLUGIN_LOADER_OPTION +#include "llvm/Support/PluginLoader.h" + +#include <fstream> +using namespace llvm; + + +namespace { + // ChildOutput - This option captures the name of the child output file that + // is set up by the parent bugpoint process + cl::opt<std::string> ChildOutput("child-output", cl::ReallyHidden); + cl::opt<bool> UseValgrind("enable-valgrind", + cl::desc("Run optimizations through valgrind")); +} + +/// writeProgramToFile - This writes the current "Program" to the named bitcode +/// file. If an error occurs, true is returned. +/// +bool BugDriver::writeProgramToFile(const std::string &Filename, + Module *M) const { + std::ios::openmode io_mode = std::ios::out | std::ios::trunc | + std::ios::binary; + std::ofstream Out(Filename.c_str(), io_mode); + if (!Out.good()) return true; + + WriteBitcodeToFile(M ? M : Program, Out); + return false; +} + + +/// EmitProgressBitcode - This function is used to output the current Program +/// to a file named "bugpoint-ID.bc". +/// +void BugDriver::EmitProgressBitcode(const std::string &ID, bool NoFlyer) { + // Output the input to the current pass to a bitcode file, emit a message + // telling the user how to reproduce it: opt -foo blah.bc + // + std::string Filename = "bugpoint-" + ID + ".bc"; + if (writeProgramToFile(Filename)) { + cerr << "Error opening file '" << Filename << "' for writing!\n"; + return; + } + + cout << "Emitted bitcode to '" << Filename << "'\n"; + if (NoFlyer || PassesToRun.empty()) return; + cout << "\n*** You can reproduce the problem with: "; + cout << "opt " << Filename << " "; + cout << getPassesString(PassesToRun) << "\n"; +} + +int BugDriver::runPassesAsChild(const std::vector<const PassInfo*> &Passes) { + + std::ios::openmode io_mode = std::ios::out | std::ios::trunc | + std::ios::binary; + std::ofstream OutFile(ChildOutput.c_str(), io_mode); + if (!OutFile.good()) { + cerr << "Error opening bitcode file: " << ChildOutput << "\n"; + return 1; + } + + PassManager PM; + // Make sure that the appropriate target data is always used... + PM.add(new TargetData(Program)); + + for (unsigned i = 0, e = Passes.size(); i != e; ++i) { + if (Passes[i]->getNormalCtor()) + PM.add(Passes[i]->getNormalCtor()()); + else + cerr << "Cannot create pass yet: " << Passes[i]->getPassName() << "\n"; + } + // Check that the module is well formed on completion of optimization + PM.add(createVerifierPass()); + + // Write bitcode out to disk as the last step... + PM.add(CreateBitcodeWriterPass(OutFile)); + + // Run all queued passes. + PM.run(*Program); + + return 0; +} + +cl::opt<bool> SilencePasses("silence-passes", cl::desc("Suppress output of running passes (both stdout and stderr)")); + +/// runPasses - Run the specified passes on Program, outputting a bitcode file +/// and writing the filename into OutputFile if successful. If the +/// optimizations fail for some reason (optimizer crashes), return true, +/// otherwise return false. If DeleteOutput is set to true, the bitcode is +/// deleted on success, and the filename string is undefined. This prints to +/// cout a single line message indicating whether compilation was successful or +/// failed. +/// +bool BugDriver::runPasses(const std::vector<const PassInfo*> &Passes, + std::string &OutputFilename, bool DeleteOutput, + bool Quiet, unsigned NumExtraArgs, + const char * const *ExtraArgs) const { + // setup the output file name + cout << std::flush; + sys::Path uniqueFilename("bugpoint-output.bc"); + std::string ErrMsg; + if (uniqueFilename.makeUnique(true, &ErrMsg)) { + cerr << getToolName() << ": Error making unique filename: " + << ErrMsg << "\n"; + return(1); + } + OutputFilename = uniqueFilename.toString(); + + // set up the input file name + sys::Path inputFilename("bugpoint-input.bc"); + if (inputFilename.makeUnique(true, &ErrMsg)) { + cerr << getToolName() << ": Error making unique filename: " + << ErrMsg << "\n"; + return(1); + } + std::ios::openmode io_mode = std::ios::out | std::ios::trunc | + std::ios::binary; + std::ofstream InFile(inputFilename.c_str(), io_mode); + if (!InFile.good()) { + cerr << "Error opening bitcode file: " << inputFilename << "\n"; + return(1); + } + WriteBitcodeToFile(Program, InFile); + InFile.close(); + + // setup the child process' arguments + const char** args = (const char**) + alloca(sizeof(const char*) * + (Passes.size()+13+2*PluginLoader::getNumPlugins()+NumExtraArgs)); + int n = 0; + sys::Path tool = sys::Program::FindProgramByName(ToolName); + if (UseValgrind) { + args[n++] = "valgrind"; + args[n++] = "--error-exitcode=1"; + args[n++] = "-q"; + args[n++] = tool.c_str(); + } else + args[n++] = ToolName.c_str(); + + args[n++] = "-as-child"; + args[n++] = "-child-output"; + args[n++] = OutputFilename.c_str(); + std::vector<std::string> pass_args; + for (unsigned i = 0, e = PluginLoader::getNumPlugins(); i != e; ++i) { + pass_args.push_back( std::string("-load")); + pass_args.push_back( PluginLoader::getPlugin(i)); + } + for (std::vector<const PassInfo*>::const_iterator I = Passes.begin(), + E = Passes.end(); I != E; ++I ) + pass_args.push_back( std::string("-") + (*I)->getPassArgument() ); + for (std::vector<std::string>::const_iterator I = pass_args.begin(), + E = pass_args.end(); I != E; ++I ) + args[n++] = I->c_str(); + args[n++] = inputFilename.c_str(); + for (unsigned i = 0; i < NumExtraArgs; ++i) + args[n++] = *ExtraArgs; + args[n++] = 0; + + sys::Path prog; + if (UseValgrind) + prog = sys::Program::FindProgramByName("valgrind"); + else + prog = tool; + + // Redirect stdout and stderr to nowhere if SilencePasses is given + sys::Path Nowhere; + const sys::Path *Redirects[3] = {0, &Nowhere, &Nowhere}; + + int result = sys::Program::ExecuteAndWait(prog, args, 0, (SilencePasses ? Redirects : 0), + Timeout, MemoryLimit, &ErrMsg); + + // If we are supposed to delete the bitcode file or if the passes crashed, + // remove it now. This may fail if the file was never created, but that's ok. + if (DeleteOutput || result != 0) + sys::Path(OutputFilename).eraseFromDisk(); + + // Remove the temporary input file as well + inputFilename.eraseFromDisk(); + + if (!Quiet) { + if (result == 0) + cout << "Success!\n"; + else if (result > 0) + cout << "Exited with error code '" << result << "'\n"; + else if (result < 0) { + if (result == -1) + cout << "Execute failed: " << ErrMsg << "\n"; + else + cout << "Crashed with signal #" << abs(result) << "\n"; + } + if (result & 0x01000000) + cout << "Dumped core\n"; + } + + // Was the child successful? + return result != 0; +} + + +/// runPassesOn - Carefully run the specified set of pass on the specified +/// module, returning the transformed module on success, or a null pointer on +/// failure. +Module *BugDriver::runPassesOn(Module *M, + const std::vector<const PassInfo*> &Passes, + bool AutoDebugCrashes, unsigned NumExtraArgs, + const char * const *ExtraArgs) { + Module *OldProgram = swapProgramIn(M); + std::string BitcodeResult; + if (runPasses(Passes, BitcodeResult, false/*delete*/, true/*quiet*/, + NumExtraArgs, ExtraArgs)) { + if (AutoDebugCrashes) { + cerr << " Error running this sequence of passes" + << " on the input program!\n"; + delete OldProgram; + EmitProgressBitcode("pass-error", false); + exit(debugOptimizerCrash()); + } + swapProgramIn(OldProgram); + return 0; + } + + // Restore the current program. + swapProgramIn(OldProgram); + + Module *Ret = ParseInputFile(BitcodeResult); + if (Ret == 0) { + cerr << getToolName() << ": Error reading bitcode file '" + << BitcodeResult << "'!\n"; + exit(1); + } + sys::Path(BitcodeResult).eraseFromDisk(); // No longer need the file on disk + return Ret; +} diff --git a/tools/bugpoint/TestPasses.cpp b/tools/bugpoint/TestPasses.cpp new file mode 100644 index 0000000..900bf63 --- /dev/null +++ b/tools/bugpoint/TestPasses.cpp @@ -0,0 +1,75 @@ +//===- TestPasses.cpp - "buggy" passes used to test bugpoint --------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains "buggy" passes that are used to test bugpoint, to check +// that it is narrowing down testcases correctly. +// +//===----------------------------------------------------------------------===// + +#include "llvm/BasicBlock.h" +#include "llvm/Constant.h" +#include "llvm/Instructions.h" +#include "llvm/Pass.h" +#include "llvm/Type.h" +#include "llvm/Support/InstVisitor.h" + +using namespace llvm; + +namespace { + /// CrashOnCalls - This pass is used to test bugpoint. It intentionally + /// crashes on any call instructions. + class CrashOnCalls : public BasicBlockPass { + public: + static char ID; // Pass ID, replacement for typeid + CrashOnCalls() : BasicBlockPass(&ID) {} + private: + virtual void getAnalysisUsage(AnalysisUsage &AU) const { + AU.setPreservesAll(); + } + + bool runOnBasicBlock(BasicBlock &BB) { + for (BasicBlock::iterator I = BB.begin(), E = BB.end(); I != E; ++I) + if (isa<CallInst>(*I)) + abort(); + + return false; + } + }; + + char CrashOnCalls::ID = 0; + RegisterPass<CrashOnCalls> + X("bugpoint-crashcalls", + "BugPoint Test Pass - Intentionally crash on CallInsts"); +} + +namespace { + /// DeleteCalls - This pass is used to test bugpoint. It intentionally + /// deletes some call instructions, "misoptimizing" the program. + class DeleteCalls : public BasicBlockPass { + public: + static char ID; // Pass ID, replacement for typeid + DeleteCalls() : BasicBlockPass(&ID) {} + private: + bool runOnBasicBlock(BasicBlock &BB) { + for (BasicBlock::iterator I = BB.begin(), E = BB.end(); I != E; ++I) + if (CallInst *CI = dyn_cast<CallInst>(I)) { + if (!CI->use_empty()) + CI->replaceAllUsesWith(Constant::getNullValue(CI->getType())); + CI->getParent()->getInstList().erase(CI); + break; + } + return false; + } + }; + + char DeleteCalls::ID = 0; + RegisterPass<DeleteCalls> + Y("bugpoint-deletecalls", + "BugPoint Test Pass - Intentionally 'misoptimize' CallInsts"); +} diff --git a/tools/bugpoint/ToolRunner.cpp b/tools/bugpoint/ToolRunner.cpp new file mode 100644 index 0000000..978e60b --- /dev/null +++ b/tools/bugpoint/ToolRunner.cpp @@ -0,0 +1,748 @@ +//===-- ToolRunner.cpp ----------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements the interfaces described in the ToolRunner.h file. +// +//===----------------------------------------------------------------------===// + +#define DEBUG_TYPE "toolrunner" +#include "ToolRunner.h" +#include "llvm/Config/config.h" // for HAVE_LINK_R +#include "llvm/System/Program.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/FileUtilities.h" +#include <fstream> +#include <sstream> +#include <iostream> +using namespace llvm; + +namespace { + cl::opt<std::string> + RemoteClient("remote-client", + cl::desc("Remote execution client (rsh/ssh)")); + + cl::opt<std::string> + RemoteHost("remote-host", + cl::desc("Remote execution (rsh/ssh) host")); + + cl::opt<std::string> + RemoteUser("remote-user", + cl::desc("Remote execution (rsh/ssh) user id")); + + cl::opt<std::string> + RemoteExtra("remote-extra-options", + cl::desc("Remote execution (rsh/ssh) extra options")); +} + +ToolExecutionError::~ToolExecutionError() throw() { } + +/// RunProgramWithTimeout - This function provides an alternate interface to the +/// sys::Program::ExecuteAndWait interface. +/// @see sys:Program::ExecuteAndWait +static int RunProgramWithTimeout(const sys::Path &ProgramPath, + const char **Args, + const sys::Path &StdInFile, + const sys::Path &StdOutFile, + const sys::Path &StdErrFile, + unsigned NumSeconds = 0, + unsigned MemoryLimit = 0) { + const sys::Path* redirects[3]; + redirects[0] = &StdInFile; + redirects[1] = &StdOutFile; + redirects[2] = &StdErrFile; + + if (0) { + std::cerr << "RUN:"; + for (unsigned i = 0; Args[i]; ++i) + std::cerr << " " << Args[i]; + std::cerr << "\n"; + } + + return + sys::Program::ExecuteAndWait(ProgramPath, Args, 0, redirects, + NumSeconds, MemoryLimit); +} + + + +static void ProcessFailure(sys::Path ProgPath, const char** Args) { + std::ostringstream OS; + OS << "\nError running tool:\n "; + for (const char **Arg = Args; *Arg; ++Arg) + OS << " " << *Arg; + OS << "\n"; + + // Rerun the compiler, capturing any error messages to print them. + sys::Path ErrorFilename("bugpoint.program_error_messages"); + std::string ErrMsg; + if (ErrorFilename.makeUnique(true, &ErrMsg)) { + std::cerr << "Error making unique filename: " << ErrMsg << "\n"; + exit(1); + } + RunProgramWithTimeout(ProgPath, Args, sys::Path(""), ErrorFilename, + ErrorFilename); // FIXME: check return code ? + + // Print out the error messages generated by GCC if possible... + std::ifstream ErrorFile(ErrorFilename.c_str()); + if (ErrorFile) { + std::copy(std::istreambuf_iterator<char>(ErrorFile), + std::istreambuf_iterator<char>(), + std::ostreambuf_iterator<char>(OS)); + ErrorFile.close(); + } + + ErrorFilename.eraseFromDisk(); + throw ToolExecutionError(OS.str()); +} + +//===---------------------------------------------------------------------===// +// LLI Implementation of AbstractIntepreter interface +// +namespace { + class LLI : public AbstractInterpreter { + std::string LLIPath; // The path to the LLI executable + std::vector<std::string> ToolArgs; // Args to pass to LLI + public: + LLI(const std::string &Path, const std::vector<std::string> *Args) + : LLIPath(Path) { + ToolArgs.clear (); + if (Args) { ToolArgs = *Args; } + } + + virtual int ExecuteProgram(const std::string &Bitcode, + const std::vector<std::string> &Args, + const std::string &InputFile, + const std::string &OutputFile, + const std::vector<std::string> &GCCArgs, + const std::vector<std::string> &SharedLibs = + std::vector<std::string>(), + unsigned Timeout = 0, + unsigned MemoryLimit = 0); + }; +} + +int LLI::ExecuteProgram(const std::string &Bitcode, + const std::vector<std::string> &Args, + const std::string &InputFile, + const std::string &OutputFile, + const std::vector<std::string> &GCCArgs, + const std::vector<std::string> &SharedLibs, + unsigned Timeout, + unsigned MemoryLimit) { + if (!SharedLibs.empty()) + throw ToolExecutionError("LLI currently does not support " + "loading shared libraries."); + + std::vector<const char*> LLIArgs; + LLIArgs.push_back(LLIPath.c_str()); + LLIArgs.push_back("-force-interpreter=true"); + + // Add any extra LLI args. + for (unsigned i = 0, e = ToolArgs.size(); i != e; ++i) + LLIArgs.push_back(ToolArgs[i].c_str()); + + LLIArgs.push_back(Bitcode.c_str()); + // Add optional parameters to the running program from Argv + for (unsigned i=0, e = Args.size(); i != e; ++i) + LLIArgs.push_back(Args[i].c_str()); + LLIArgs.push_back(0); + + std::cout << "<lli>" << std::flush; + DEBUG(std::cerr << "\nAbout to run:\t"; + for (unsigned i=0, e = LLIArgs.size()-1; i != e; ++i) + std::cerr << " " << LLIArgs[i]; + std::cerr << "\n"; + ); + return RunProgramWithTimeout(sys::Path(LLIPath), &LLIArgs[0], + sys::Path(InputFile), sys::Path(OutputFile), sys::Path(OutputFile), + Timeout, MemoryLimit); +} + +// LLI create method - Try to find the LLI executable +AbstractInterpreter *AbstractInterpreter::createLLI(const std::string &ProgPath, + std::string &Message, + const std::vector<std::string> *ToolArgs) { + std::string LLIPath = FindExecutable("lli", ProgPath).toString(); + if (!LLIPath.empty()) { + Message = "Found lli: " + LLIPath + "\n"; + return new LLI(LLIPath, ToolArgs); + } + + Message = "Cannot find `lli' in executable directory or PATH!\n"; + return 0; +} + +//===---------------------------------------------------------------------===// +// Custom execution command implementation of AbstractIntepreter interface +// +// Allows using a custom command for executing the bitcode, thus allows, +// for example, to invoke a cross compiler for code generation followed by +// a simulator that executes the generated binary. +namespace { + class CustomExecutor : public AbstractInterpreter { + std::string ExecutionCommand; + std::vector<std::string> ExecutorArgs; + public: + CustomExecutor( + const std::string &ExecutionCmd, std::vector<std::string> ExecArgs) : + ExecutionCommand(ExecutionCmd), ExecutorArgs(ExecArgs) {} + + virtual int ExecuteProgram(const std::string &Bitcode, + const std::vector<std::string> &Args, + const std::string &InputFile, + const std::string &OutputFile, + const std::vector<std::string> &GCCArgs, + const std::vector<std::string> &SharedLibs = + std::vector<std::string>(), + unsigned Timeout = 0, + unsigned MemoryLimit = 0); + }; +} + +int CustomExecutor::ExecuteProgram(const std::string &Bitcode, + const std::vector<std::string> &Args, + const std::string &InputFile, + const std::string &OutputFile, + const std::vector<std::string> &GCCArgs, + const std::vector<std::string> &SharedLibs, + unsigned Timeout, + unsigned MemoryLimit) { + + std::vector<const char*> ProgramArgs; + ProgramArgs.push_back(ExecutionCommand.c_str()); + + for (std::size_t i = 0; i < ExecutorArgs.size(); ++i) + ProgramArgs.push_back(ExecutorArgs.at(i).c_str()); + ProgramArgs.push_back(Bitcode.c_str()); + ProgramArgs.push_back(0); + + // Add optional parameters to the running program from Argv + for (unsigned i=0, e = Args.size(); i != e; ++i) + ProgramArgs.push_back(Args[i].c_str()); + + return RunProgramWithTimeout( + sys::Path(ExecutionCommand), + &ProgramArgs[0], sys::Path(InputFile), sys::Path(OutputFile), + sys::Path(OutputFile), Timeout, MemoryLimit); +} + +// Custom execution environment create method, takes the execution command +// as arguments +AbstractInterpreter *AbstractInterpreter::createCustom( + const std::string &ProgramPath, + std::string &Message, + const std::string &ExecCommandLine) { + + std::string Command = ""; + std::vector<std::string> Args; + std::string delimiters = " "; + + // Tokenize the ExecCommandLine to the command and the args to allow + // defining a full command line as the command instead of just the + // executed program. We cannot just pass the whole string after the command + // as a single argument because then program sees only a single + // command line argument (with spaces in it: "foo bar" instead + // of "foo" and "bar"). + + // code borrowed from: + // http://oopweb.com/CPP/Documents/CPPHOWTO/Volume/C++Programming-HOWTO-7.html + std::string::size_type lastPos = + ExecCommandLine.find_first_not_of(delimiters, 0); + std::string::size_type pos = + ExecCommandLine.find_first_of(delimiters, lastPos); + + while (std::string::npos != pos || std::string::npos != lastPos) { + std::string token = ExecCommandLine.substr(lastPos, pos - lastPos); + if (Command == "") + Command = token; + else + Args.push_back(token); + // Skip delimiters. Note the "not_of" + lastPos = ExecCommandLine.find_first_not_of(delimiters, pos); + // Find next "non-delimiter" + pos = ExecCommandLine.find_first_of(delimiters, lastPos); + } + + std::string CmdPath = FindExecutable(Command, ProgramPath).toString(); + if (CmdPath.empty()) { + Message = + std::string("Cannot find '") + Command + + "' in executable directory or PATH!\n"; + return 0; + } + + Message = "Found command in: " + CmdPath + "\n"; + + return new CustomExecutor(CmdPath, Args); +} + +//===----------------------------------------------------------------------===// +// LLC Implementation of AbstractIntepreter interface +// +GCC::FileType LLC::OutputCode(const std::string &Bitcode, + sys::Path &OutputAsmFile) { + sys::Path uniqueFile(Bitcode+".llc.s"); + std::string ErrMsg; + if (uniqueFile.makeUnique(true, &ErrMsg)) { + std::cerr << "Error making unique filename: " << ErrMsg << "\n"; + exit(1); + } + OutputAsmFile = uniqueFile; + std::vector<const char *> LLCArgs; + LLCArgs.push_back (LLCPath.c_str()); + + // Add any extra LLC args. + for (unsigned i = 0, e = ToolArgs.size(); i != e; ++i) + LLCArgs.push_back(ToolArgs[i].c_str()); + + LLCArgs.push_back ("-o"); + LLCArgs.push_back (OutputAsmFile.c_str()); // Output to the Asm file + LLCArgs.push_back ("-f"); // Overwrite as necessary... + LLCArgs.push_back (Bitcode.c_str()); // This is the input bitcode + LLCArgs.push_back (0); + + std::cout << "<llc>" << std::flush; + DEBUG(std::cerr << "\nAbout to run:\t"; + for (unsigned i=0, e = LLCArgs.size()-1; i != e; ++i) + std::cerr << " " << LLCArgs[i]; + std::cerr << "\n"; + ); + if (RunProgramWithTimeout(sys::Path(LLCPath), &LLCArgs[0], + sys::Path(), sys::Path(), sys::Path())) + ProcessFailure(sys::Path(LLCPath), &LLCArgs[0]); + + return GCC::AsmFile; +} + +void LLC::compileProgram(const std::string &Bitcode) { + sys::Path OutputAsmFile; + OutputCode(Bitcode, OutputAsmFile); + OutputAsmFile.eraseFromDisk(); +} + +int LLC::ExecuteProgram(const std::string &Bitcode, + const std::vector<std::string> &Args, + const std::string &InputFile, + const std::string &OutputFile, + const std::vector<std::string> &ArgsForGCC, + const std::vector<std::string> &SharedLibs, + unsigned Timeout, + unsigned MemoryLimit) { + + sys::Path OutputAsmFile; + OutputCode(Bitcode, OutputAsmFile); + FileRemover OutFileRemover(OutputAsmFile); + + std::vector<std::string> GCCArgs(ArgsForGCC); + GCCArgs.insert(GCCArgs.end(), SharedLibs.begin(), SharedLibs.end()); + GCCArgs.insert(GCCArgs.end(), gccArgs.begin(), gccArgs.end()); + + // Assuming LLC worked, compile the result with GCC and run it. + return gcc->ExecuteProgram(OutputAsmFile.toString(), Args, GCC::AsmFile, + InputFile, OutputFile, GCCArgs, + Timeout, MemoryLimit); +} + +/// createLLC - Try to find the LLC executable +/// +LLC *AbstractInterpreter::createLLC(const std::string &ProgramPath, + std::string &Message, + const std::vector<std::string> *Args, + const std::vector<std::string> *GCCArgs) { + std::string LLCPath = FindExecutable("llc", ProgramPath).toString(); + if (LLCPath.empty()) { + Message = "Cannot find `llc' in executable directory or PATH!\n"; + return 0; + } + + Message = "Found llc: " + LLCPath + "\n"; + GCC *gcc = GCC::create(ProgramPath, Message, GCCArgs); + if (!gcc) { + std::cerr << Message << "\n"; + exit(1); + } + return new LLC(LLCPath, gcc, Args, GCCArgs); +} + +//===---------------------------------------------------------------------===// +// JIT Implementation of AbstractIntepreter interface +// +namespace { + class JIT : public AbstractInterpreter { + std::string LLIPath; // The path to the LLI executable + std::vector<std::string> ToolArgs; // Args to pass to LLI + public: + JIT(const std::string &Path, const std::vector<std::string> *Args) + : LLIPath(Path) { + ToolArgs.clear (); + if (Args) { ToolArgs = *Args; } + } + + virtual int ExecuteProgram(const std::string &Bitcode, + const std::vector<std::string> &Args, + const std::string &InputFile, + const std::string &OutputFile, + const std::vector<std::string> &GCCArgs = + std::vector<std::string>(), + const std::vector<std::string> &SharedLibs = + std::vector<std::string>(), + unsigned Timeout =0, + unsigned MemoryLimit =0); + }; +} + +int JIT::ExecuteProgram(const std::string &Bitcode, + const std::vector<std::string> &Args, + const std::string &InputFile, + const std::string &OutputFile, + const std::vector<std::string> &GCCArgs, + const std::vector<std::string> &SharedLibs, + unsigned Timeout, + unsigned MemoryLimit) { + // Construct a vector of parameters, incorporating those from the command-line + std::vector<const char*> JITArgs; + JITArgs.push_back(LLIPath.c_str()); + JITArgs.push_back("-force-interpreter=false"); + + // Add any extra LLI args. + for (unsigned i = 0, e = ToolArgs.size(); i != e; ++i) + JITArgs.push_back(ToolArgs[i].c_str()); + + for (unsigned i = 0, e = SharedLibs.size(); i != e; ++i) { + JITArgs.push_back("-load"); + JITArgs.push_back(SharedLibs[i].c_str()); + } + JITArgs.push_back(Bitcode.c_str()); + // Add optional parameters to the running program from Argv + for (unsigned i=0, e = Args.size(); i != e; ++i) + JITArgs.push_back(Args[i].c_str()); + JITArgs.push_back(0); + + std::cout << "<jit>" << std::flush; + DEBUG(std::cerr << "\nAbout to run:\t"; + for (unsigned i=0, e = JITArgs.size()-1; i != e; ++i) + std::cerr << " " << JITArgs[i]; + std::cerr << "\n"; + ); + DEBUG(std::cerr << "\nSending output to " << OutputFile << "\n"); + return RunProgramWithTimeout(sys::Path(LLIPath), &JITArgs[0], + sys::Path(InputFile), sys::Path(OutputFile), sys::Path(OutputFile), + Timeout, MemoryLimit); +} + +/// createJIT - Try to find the LLI executable +/// +AbstractInterpreter *AbstractInterpreter::createJIT(const std::string &ProgPath, + std::string &Message, const std::vector<std::string> *Args) { + std::string LLIPath = FindExecutable("lli", ProgPath).toString(); + if (!LLIPath.empty()) { + Message = "Found lli: " + LLIPath + "\n"; + return new JIT(LLIPath, Args); + } + + Message = "Cannot find `lli' in executable directory or PATH!\n"; + return 0; +} + +GCC::FileType CBE::OutputCode(const std::string &Bitcode, + sys::Path &OutputCFile) { + sys::Path uniqueFile(Bitcode+".cbe.c"); + std::string ErrMsg; + if (uniqueFile.makeUnique(true, &ErrMsg)) { + std::cerr << "Error making unique filename: " << ErrMsg << "\n"; + exit(1); + } + OutputCFile = uniqueFile; + std::vector<const char *> LLCArgs; + LLCArgs.push_back (LLCPath.c_str()); + + // Add any extra LLC args. + for (unsigned i = 0, e = ToolArgs.size(); i != e; ++i) + LLCArgs.push_back(ToolArgs[i].c_str()); + + LLCArgs.push_back ("-o"); + LLCArgs.push_back (OutputCFile.c_str()); // Output to the C file + LLCArgs.push_back ("-march=c"); // Output C language + LLCArgs.push_back ("-f"); // Overwrite as necessary... + LLCArgs.push_back (Bitcode.c_str()); // This is the input bitcode + LLCArgs.push_back (0); + + std::cout << "<cbe>" << std::flush; + DEBUG(std::cerr << "\nAbout to run:\t"; + for (unsigned i=0, e = LLCArgs.size()-1; i != e; ++i) + std::cerr << " " << LLCArgs[i]; + std::cerr << "\n"; + ); + if (RunProgramWithTimeout(LLCPath, &LLCArgs[0], sys::Path(), sys::Path(), + sys::Path())) + ProcessFailure(LLCPath, &LLCArgs[0]); + return GCC::CFile; +} + +void CBE::compileProgram(const std::string &Bitcode) { + sys::Path OutputCFile; + OutputCode(Bitcode, OutputCFile); + OutputCFile.eraseFromDisk(); +} + +int CBE::ExecuteProgram(const std::string &Bitcode, + const std::vector<std::string> &Args, + const std::string &InputFile, + const std::string &OutputFile, + const std::vector<std::string> &ArgsForGCC, + const std::vector<std::string> &SharedLibs, + unsigned Timeout, + unsigned MemoryLimit) { + sys::Path OutputCFile; + OutputCode(Bitcode, OutputCFile); + + FileRemover CFileRemove(OutputCFile); + + std::vector<std::string> GCCArgs(ArgsForGCC); + GCCArgs.insert(GCCArgs.end(), SharedLibs.begin(), SharedLibs.end()); + + return gcc->ExecuteProgram(OutputCFile.toString(), Args, GCC::CFile, + InputFile, OutputFile, GCCArgs, + Timeout, MemoryLimit); +} + +/// createCBE - Try to find the 'llc' executable +/// +CBE *AbstractInterpreter::createCBE(const std::string &ProgramPath, + std::string &Message, + const std::vector<std::string> *Args, + const std::vector<std::string> *GCCArgs) { + sys::Path LLCPath = FindExecutable("llc", ProgramPath); + if (LLCPath.isEmpty()) { + Message = + "Cannot find `llc' in executable directory or PATH!\n"; + return 0; + } + + Message = "Found llc: " + LLCPath.toString() + "\n"; + GCC *gcc = GCC::create(ProgramPath, Message, GCCArgs); + if (!gcc) { + std::cerr << Message << "\n"; + exit(1); + } + return new CBE(LLCPath, gcc, Args); +} + +//===---------------------------------------------------------------------===// +// GCC abstraction +// +int GCC::ExecuteProgram(const std::string &ProgramFile, + const std::vector<std::string> &Args, + FileType fileType, + const std::string &InputFile, + const std::string &OutputFile, + const std::vector<std::string> &ArgsForGCC, + unsigned Timeout, + unsigned MemoryLimit) { + std::vector<const char*> GCCArgs; + + GCCArgs.push_back(GCCPath.c_str()); + + for (std::vector<std::string>::const_iterator + I = gccArgs.begin(), E = gccArgs.end(); I != E; ++I) + GCCArgs.push_back(I->c_str()); + + // Specify -x explicitly in case the extension is wonky + GCCArgs.push_back("-x"); + if (fileType == CFile) { + GCCArgs.push_back("c"); + GCCArgs.push_back("-fno-strict-aliasing"); + } else { + GCCArgs.push_back("assembler"); +#ifdef __APPLE__ + GCCArgs.push_back("-force_cpusubtype_ALL"); +#endif + } + GCCArgs.push_back(ProgramFile.c_str()); // Specify the input filename... + GCCArgs.push_back("-x"); + GCCArgs.push_back("none"); + GCCArgs.push_back("-o"); + sys::Path OutputBinary (ProgramFile+".gcc.exe"); + std::string ErrMsg; + if (OutputBinary.makeUnique(true, &ErrMsg)) { + std::cerr << "Error making unique filename: " << ErrMsg << "\n"; + exit(1); + } + GCCArgs.push_back(OutputBinary.c_str()); // Output to the right file... + + // Add any arguments intended for GCC. We locate them here because this is + // most likely -L and -l options that need to come before other libraries but + // after the source. Other options won't be sensitive to placement on the + // command line, so this should be safe. + for (unsigned i = 0, e = ArgsForGCC.size(); i != e; ++i) + GCCArgs.push_back(ArgsForGCC[i].c_str()); + + GCCArgs.push_back("-lm"); // Hard-code the math library... + GCCArgs.push_back("-O2"); // Optimize the program a bit... +#if defined (HAVE_LINK_R) + GCCArgs.push_back("-Wl,-R."); // Search this dir for .so files +#endif +#ifdef __sparc__ + GCCArgs.push_back("-mcpu=v9"); +#endif + GCCArgs.push_back(0); // NULL terminator + + std::cout << "<gcc>" << std::flush; + DEBUG(std::cerr << "\nAbout to run:\t"; + for (unsigned i=0, e = GCCArgs.size()-1; i != e; ++i) + std::cerr << " " << GCCArgs[i]; + std::cerr << "\n"; + ); + if (RunProgramWithTimeout(GCCPath, &GCCArgs[0], sys::Path(), sys::Path(), + sys::Path())) { + ProcessFailure(GCCPath, &GCCArgs[0]); + exit(1); + } + + std::vector<const char*> ProgramArgs; + + if (RemoteClientPath.isEmpty()) + ProgramArgs.push_back(OutputBinary.c_str()); + else { + ProgramArgs.push_back(RemoteClientPath.c_str()); + ProgramArgs.push_back(RemoteHost.c_str()); + ProgramArgs.push_back("-l"); + ProgramArgs.push_back(RemoteUser.c_str()); + if (!RemoteExtra.empty()) { + ProgramArgs.push_back(RemoteExtra.c_str()); + } + + char* env_pwd = getenv("PWD"); + std::string Exec = "cd "; + Exec += env_pwd; + Exec += "; ./"; + Exec += OutputBinary.c_str(); + ProgramArgs.push_back(Exec.c_str()); + } + + // Add optional parameters to the running program from Argv + for (unsigned i=0, e = Args.size(); i != e; ++i) + ProgramArgs.push_back(Args[i].c_str()); + ProgramArgs.push_back(0); // NULL terminator + + // Now that we have a binary, run it! + std::cout << "<program>" << std::flush; + DEBUG(std::cerr << "\nAbout to run:\t"; + for (unsigned i=0, e = ProgramArgs.size()-1; i != e; ++i) + std::cerr << " " << ProgramArgs[i]; + std::cerr << "\n"; + ); + + FileRemover OutputBinaryRemover(OutputBinary); + + if (RemoteClientPath.isEmpty()) + return RunProgramWithTimeout(OutputBinary, &ProgramArgs[0], + sys::Path(InputFile), sys::Path(OutputFile), sys::Path(OutputFile), + Timeout, MemoryLimit); + else + return RunProgramWithTimeout(sys::Path(RemoteClientPath), &ProgramArgs[0], + sys::Path(InputFile), sys::Path(OutputFile), sys::Path(OutputFile), + Timeout, MemoryLimit); +} + +int GCC::MakeSharedObject(const std::string &InputFile, FileType fileType, + std::string &OutputFile, + const std::vector<std::string> &ArgsForGCC) { + sys::Path uniqueFilename(InputFile+LTDL_SHLIB_EXT); + std::string ErrMsg; + if (uniqueFilename.makeUnique(true, &ErrMsg)) { + std::cerr << "Error making unique filename: " << ErrMsg << "\n"; + exit(1); + } + OutputFile = uniqueFilename.toString(); + + std::vector<const char*> GCCArgs; + + GCCArgs.push_back(GCCPath.c_str()); + + for (std::vector<std::string>::const_iterator + I = gccArgs.begin(), E = gccArgs.end(); I != E; ++I) + GCCArgs.push_back(I->c_str()); + + // Compile the C/asm file into a shared object + GCCArgs.push_back("-x"); + GCCArgs.push_back(fileType == AsmFile ? "assembler" : "c"); + GCCArgs.push_back("-fno-strict-aliasing"); + GCCArgs.push_back(InputFile.c_str()); // Specify the input filename. + GCCArgs.push_back("-x"); + GCCArgs.push_back("none"); +#if defined(sparc) || defined(__sparc__) || defined(__sparcv9) + GCCArgs.push_back("-G"); // Compile a shared library, `-G' for Sparc +#elif defined(__APPLE__) + // link all source files into a single module in data segment, rather than + // generating blocks. dynamic_lookup requires that you set + // MACOSX_DEPLOYMENT_TARGET=10.3 in your env. FIXME: it would be better for + // bugpoint to just pass that in the environment of GCC. + GCCArgs.push_back("-single_module"); + GCCArgs.push_back("-dynamiclib"); // `-dynamiclib' for MacOS X/PowerPC + GCCArgs.push_back("-undefined"); + GCCArgs.push_back("dynamic_lookup"); +#else + GCCArgs.push_back("-shared"); // `-shared' for Linux/X86, maybe others +#endif + +#if defined(__ia64__) || defined(__alpha__) || defined(__amd64__) + GCCArgs.push_back("-fPIC"); // Requires shared objs to contain PIC +#endif +#ifdef __sparc__ + GCCArgs.push_back("-mcpu=v9"); +#endif + GCCArgs.push_back("-o"); + GCCArgs.push_back(OutputFile.c_str()); // Output to the right filename. + GCCArgs.push_back("-O2"); // Optimize the program a bit. + + + + // Add any arguments intended for GCC. We locate them here because this is + // most likely -L and -l options that need to come before other libraries but + // after the source. Other options won't be sensitive to placement on the + // command line, so this should be safe. + for (unsigned i = 0, e = ArgsForGCC.size(); i != e; ++i) + GCCArgs.push_back(ArgsForGCC[i].c_str()); + GCCArgs.push_back(0); // NULL terminator + + + + std::cout << "<gcc>" << std::flush; + DEBUG(std::cerr << "\nAbout to run:\t"; + for (unsigned i=0, e = GCCArgs.size()-1; i != e; ++i) + std::cerr << " " << GCCArgs[i]; + std::cerr << "\n"; + ); + if (RunProgramWithTimeout(GCCPath, &GCCArgs[0], sys::Path(), sys::Path(), + sys::Path())) { + ProcessFailure(GCCPath, &GCCArgs[0]); + return 1; + } + return 0; +} + +/// create - Try to find the `gcc' executable +/// +GCC *GCC::create(const std::string &ProgramPath, std::string &Message, + const std::vector<std::string> *Args) { + sys::Path GCCPath = FindExecutable("gcc", ProgramPath); + if (GCCPath.isEmpty()) { + Message = "Cannot find `gcc' in executable directory or PATH!\n"; + return 0; + } + + sys::Path RemoteClientPath; + if (!RemoteClient.empty()) + RemoteClientPath = FindExecutable(RemoteClient.c_str(), ProgramPath); + + Message = "Found gcc: " + GCCPath.toString() + "\n"; + return new GCC(GCCPath, RemoteClientPath, Args); +} diff --git a/tools/bugpoint/ToolRunner.h b/tools/bugpoint/ToolRunner.h new file mode 100644 index 0000000..721f66c --- /dev/null +++ b/tools/bugpoint/ToolRunner.h @@ -0,0 +1,230 @@ +//===-- tools/bugpoint/ToolRunner.h -----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file exposes an abstraction around a platform C compiler, used to +// compile C and assembly code. It also exposes an "AbstractIntepreter" +// interface, which is used to execute code using one of the LLVM execution +// engines. +// +//===----------------------------------------------------------------------===// + +#ifndef BUGPOINT_TOOLRUNNER_H +#define BUGPOINT_TOOLRUNNER_H + +#include "llvm/Support/SystemUtils.h" +#include <exception> +#include <vector> + +namespace llvm { + +class CBE; +class LLC; + +/// ToolExecutionError - An instance of this class is thrown by the +/// AbstractInterpreter instances if there is an error running a tool (e.g., LLC +/// crashes) which prevents execution of the program. +/// +class ToolExecutionError : std::exception { + std::string Message; +public: + explicit ToolExecutionError(const std::string &M) : Message(M) {} + virtual ~ToolExecutionError() throw(); + virtual const char* what() const throw() { return Message.c_str(); } +}; + + +//===---------------------------------------------------------------------===// +// GCC abstraction +// +class GCC { + sys::Path GCCPath; // The path to the gcc executable. + sys::Path RemoteClientPath; // The path to the rsh / ssh executable. + std::vector<std::string> gccArgs; // GCC-specific arguments. + GCC(const sys::Path &gccPath, const sys::Path &RemotePath, + const std::vector<std::string> *GCCArgs) + : GCCPath(gccPath), RemoteClientPath(RemotePath) { + if (GCCArgs) gccArgs = *GCCArgs; + } +public: + enum FileType { AsmFile, CFile }; + + static GCC *create(const std::string &ProgramPath, std::string &Message, + const std::vector<std::string> *Args); + + /// ExecuteProgram - Execute the program specified by "ProgramFile" (which is + /// either a .s file, or a .c file, specified by FileType), with the specified + /// arguments. Standard input is specified with InputFile, and standard + /// Output is captured to the specified OutputFile location. The SharedLibs + /// option specifies optional native shared objects that can be loaded into + /// the program for execution. + /// + int ExecuteProgram(const std::string &ProgramFile, + const std::vector<std::string> &Args, + FileType fileType, + const std::string &InputFile, + const std::string &OutputFile, + const std::vector<std::string> &GCCArgs = + std::vector<std::string>(), + unsigned Timeout = 0, + unsigned MemoryLimit = 0); + + /// MakeSharedObject - This compiles the specified file (which is either a .c + /// file or a .s file) into a shared object. + /// + int MakeSharedObject(const std::string &InputFile, FileType fileType, + std::string &OutputFile, + const std::vector<std::string> &ArgsForGCC); +}; + + +//===---------------------------------------------------------------------===// +/// AbstractInterpreter Class - Subclasses of this class are used to execute +/// LLVM bitcode in a variety of ways. This abstract interface hides this +/// complexity behind a simple interface. +/// +class AbstractInterpreter { +public: + static CBE *createCBE(const std::string &ProgramPath, std::string &Message, + const std::vector<std::string> *Args = 0, + const std::vector<std::string> *GCCArgs = 0); + static LLC *createLLC(const std::string &ProgramPath, std::string &Message, + const std::vector<std::string> *Args = 0, + const std::vector<std::string> *GCCArgs = 0); + + static AbstractInterpreter* createLLI(const std::string &ProgramPath, + std::string &Message, + const std::vector<std::string> *Args=0); + + static AbstractInterpreter* createJIT(const std::string &ProgramPath, + std::string &Message, + const std::vector<std::string> *Args=0); + + static AbstractInterpreter* createCustom(const std::string &ProgramPath, + std::string &Message, + const std::string &ExecCommandLine); + + + virtual ~AbstractInterpreter() {} + + /// compileProgram - Compile the specified program from bitcode to executable + /// code. This does not produce any output, it is only used when debugging + /// the code generator. If the code generator fails, an exception should be + /// thrown, otherwise, this function will just return. + virtual void compileProgram(const std::string &Bitcode) {} + + /// OutputCode - Compile the specified program from bitcode to code + /// understood by the GCC driver (either C or asm). If the code generator + /// fails, an exception should be thrown, otherwise, this function returns the + /// type of code emitted. + virtual GCC::FileType OutputCode(const std::string &Bitcode, + sys::Path &OutFile) { + throw std::string("OutputCode not supported by this AbstractInterpreter!"); + } + + /// ExecuteProgram - Run the specified bitcode file, emitting output to the + /// specified filename. This returns the exit code of the program. + /// + virtual int ExecuteProgram(const std::string &Bitcode, + const std::vector<std::string> &Args, + const std::string &InputFile, + const std::string &OutputFile, + const std::vector<std::string> &GCCArgs = + std::vector<std::string>(), + const std::vector<std::string> &SharedLibs = + std::vector<std::string>(), + unsigned Timeout = 0, + unsigned MemoryLimit = 0) = 0; +}; + +//===---------------------------------------------------------------------===// +// CBE Implementation of AbstractIntepreter interface +// +class CBE : public AbstractInterpreter { + sys::Path LLCPath; // The path to the `llc' executable. + std::vector<std::string> ToolArgs; // Extra args to pass to LLC. + GCC *gcc; +public: + CBE(const sys::Path &llcPath, GCC *Gcc, + const std::vector<std::string> *Args) + : LLCPath(llcPath), gcc(Gcc) { + ToolArgs.clear (); + if (Args) ToolArgs = *Args; + } + ~CBE() { delete gcc; } + + /// compileProgram - Compile the specified program from bitcode to executable + /// code. This does not produce any output, it is only used when debugging + /// the code generator. If the code generator fails, an exception should be + /// thrown, otherwise, this function will just return. + virtual void compileProgram(const std::string &Bitcode); + + virtual int ExecuteProgram(const std::string &Bitcode, + const std::vector<std::string> &Args, + const std::string &InputFile, + const std::string &OutputFile, + const std::vector<std::string> &GCCArgs = + std::vector<std::string>(), + const std::vector<std::string> &SharedLibs = + std::vector<std::string>(), + unsigned Timeout = 0, + unsigned MemoryLimit = 0); + + /// OutputCode - Compile the specified program from bitcode to code + /// understood by the GCC driver (either C or asm). If the code generator + /// fails, an exception should be thrown, otherwise, this function returns the + /// type of code emitted. + virtual GCC::FileType OutputCode(const std::string &Bitcode, + sys::Path &OutFile); +}; + + +//===---------------------------------------------------------------------===// +// LLC Implementation of AbstractIntepreter interface +// +class LLC : public AbstractInterpreter { + std::string LLCPath; // The path to the LLC executable. + std::vector<std::string> ToolArgs; // Extra args to pass to LLC. + std::vector<std::string> gccArgs; // Extra args to pass to GCC. + GCC *gcc; +public: + LLC(const std::string &llcPath, GCC *Gcc, + const std::vector<std::string> *Args, + const std::vector<std::string> *GCCArgs) + : LLCPath(llcPath), gcc(Gcc) { + ToolArgs.clear(); + if (Args) ToolArgs = *Args; + if (GCCArgs) gccArgs = *GCCArgs; + } + ~LLC() { delete gcc; } + + /// compileProgram - Compile the specified program from bitcode to executable + /// code. This does not produce any output, it is only used when debugging + /// the code generator. If the code generator fails, an exception should be + /// thrown, otherwise, this function will just return. + virtual void compileProgram(const std::string &Bitcode); + + virtual int ExecuteProgram(const std::string &Bitcode, + const std::vector<std::string> &Args, + const std::string &InputFile, + const std::string &OutputFile, + const std::vector<std::string> &GCCArgs = + std::vector<std::string>(), + const std::vector<std::string> &SharedLibs = + std::vector<std::string>(), + unsigned Timeout = 0, + unsigned MemoryLimit = 0); + + virtual GCC::FileType OutputCode(const std::string &Bitcode, + sys::Path &OutFile); + +}; + +} // End llvm namespace + +#endif diff --git a/tools/bugpoint/bugpoint.cpp b/tools/bugpoint/bugpoint.cpp new file mode 100644 index 0000000..20f0e99 --- /dev/null +++ b/tools/bugpoint/bugpoint.cpp @@ -0,0 +1,104 @@ +//===- bugpoint.cpp - The LLVM Bugpoint utility ---------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This program is an automated compiler debugger tool. It is used to narrow +// down miscompilations and crash problems to a specific pass in the compiler, +// and the specific Module or Function input that is causing the problem. +// +//===----------------------------------------------------------------------===// + +#include "BugDriver.h" +#include "ToolRunner.h" +#include "llvm/LinkAllPasses.h" +#include "llvm/Support/PassNameParser.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/PluginLoader.h" +#include "llvm/Support/PrettyStackTrace.h" +#include "llvm/System/Process.h" +#include "llvm/System/Signals.h" +#include "llvm/LinkAllVMCore.h" +#include <iostream> +using namespace llvm; + +// AsChild - Specifies that this invocation of bugpoint is being generated +// from a parent process. It is not intended to be used by users so the +// option is hidden. +static cl::opt<bool> +AsChild("as-child", cl::desc("Run bugpoint as child process"), + cl::ReallyHidden); + +static cl::opt<bool> +FindBugs("find-bugs", cl::desc("Run many different optimization sequences " + "on program to find bugs"), cl::init(false)); + +static cl::list<std::string> +InputFilenames(cl::Positional, cl::OneOrMore, + cl::desc("<input llvm ll/bc files>")); + +static cl::opt<unsigned> +TimeoutValue("timeout", cl::init(300), cl::value_desc("seconds"), + cl::desc("Number of seconds program is allowed to run before it " + "is killed (default is 300s), 0 disables timeout")); + +static cl::opt<unsigned> +MemoryLimit("mlimit", cl::init(100), cl::value_desc("MBytes"), + cl::desc("Maximum amount of memory to use. 0 disables check.")); + +// The AnalysesList is automatically populated with registered Passes by the +// PassNameParser. +// +static cl::list<const PassInfo*, bool, PassNameParser> +PassList(cl::desc("Passes available:"), cl::ZeroOrMore); + +/// BugpointIsInterrupted - Set to true when the user presses ctrl-c. +bool llvm::BugpointIsInterrupted = false; + +static void BugpointInterruptFunction() { + BugpointIsInterrupted = true; +} + +int main(int argc, char **argv) { + llvm::sys::PrintStackTraceOnErrorSignal(); + llvm::PrettyStackTraceProgram X(argc, argv); + llvm_shutdown_obj Y; // Call llvm_shutdown() on exit. + cl::ParseCommandLineOptions(argc, argv, + "LLVM automatic testcase reducer. See\nhttp://" + "llvm.org/cmds/bugpoint.html" + " for more information.\n"); + sys::SetInterruptFunction(BugpointInterruptFunction); + + BugDriver D(argv[0], AsChild, FindBugs, TimeoutValue, MemoryLimit); + if (D.addSources(InputFilenames)) return 1; + D.addPasses(PassList.begin(), PassList.end()); + + // Bugpoint has the ability of generating a plethora of core files, so to + // avoid filling up the disk, we prevent it + sys::Process::PreventCoreFiles(); + + try { + return D.run(); + } catch (ToolExecutionError &TEE) { + std::cerr << "Tool execution error: " << TEE.what() << '\n'; + } catch (const std::string& msg) { + std::cerr << argv[0] << ": " << msg << "\n"; + } catch (const std::bad_alloc &e) { + std::cerr << "Oh no, a bugpoint process ran out of memory!\n" + "To increase the allocation limits for bugpoint child\n" + "processes, use the -mlimit option.\n"; + } catch (const std::exception &e) { + std::cerr << "Whoops, a std::exception leaked out of bugpoint: " + << e.what() << "\n" + << "This is a bug in bugpoint!\n"; + } catch (...) { + std::cerr << "Whoops, an exception leaked out of bugpoint. " + << "This is a bug in bugpoint!\n"; + } + return 1; +} diff --git a/tools/gccas/Makefile b/tools/gccas/Makefile new file mode 100644 index 0000000..ff84d96 --- /dev/null +++ b/tools/gccas/Makefile @@ -0,0 +1,28 @@ +##===- tools/gccas/Makefile --------------------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## +LEVEL = ../.. + +include $(LEVEL)/Makefile.common + +install-local:: $(PROJ_bindir)/gccas + +$(PROJ_bindir)/gccas : gccas.sh Makefile + $(Echo) Installing gccas shell script. + $(Verb) sed "s#@TOOLDIR@#$(PROJ_bindir)#" $< > $@ + $(Verb) chmod 0755 $@ + +all-local:: $(ToolDir)/gccas + +$(ToolDir)/gccas : gccas.sh Makefile + $(Echo) Making $(ToolDir)/gccas shell script. + $(Verb) sed "s#@TOOLDIR@#$(ToolDir)#" $< > $@ + $(Verb) chmod 0755 $@ + +clean-local:: + $(Verb)$(RM) -f $(ToolDir)/gccas diff --git a/tools/gccas/gccas.sh b/tools/gccas/gccas.sh new file mode 100644 index 0000000..9cacad4 --- /dev/null +++ b/tools/gccas/gccas.sh @@ -0,0 +1,64 @@ +#!/bin/sh +##===- tools/gccas.sh ------------------------------------------*- bash -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## +# +# Synopsis: This shell script is a replacement for the old "gccas" tool that +# existed in LLVM versions before 2.0. The functionality of gccas has +# now been moved to opt and llvm-as. This shell script provides +# backwards compatibility so build environments invoking gccas can +# still get the net effect of llvm-as/opt by running gccas. +# +# Syntax: gccas OPTIONS... [asm file] +# +##===----------------------------------------------------------------------===## +# +echo "gccas: This tool is deprecated, please use opt" 1>&2 +TOOLDIR=@TOOLDIR@ +OPTOPTS="-std-compile-opts -f" +ASOPTS="" +lastwasdasho=0 +for option in "$@" ; do + option=`echo "$option" | sed 's/^--/-/'` + case "$option" in + -disable-opt) + OPTOPTS="$OPTOPTS $option" + ;; + -disable-inlining) + OPTOPTS="$OPTOPTS $option" + ;; + -verify) + OPTOPTS="$OPTOPTS -verify-each" + ;; + -strip-debug) + OPTOPTS="$OPTOPTS $option" + ;; + -o) + OPTOPTS="$OPTOPTS -o" + lastwasdasho=1 + ;; + -disable-compression) + # ignore + ;; + -traditional-format) + # ignore + ;; + -*) + OPTOPTS="$OPTOPTS $option" + ;; + *) + if test $lastwasdasho -eq 1 ; then + OPTOPTS="$OPTOPTS $option" + lastwasdasho=0 + else + ASOPTS="$ASOPTS $option" + fi + ;; + esac +done +${TOOLDIR}/llvm-as $ASOPTS -o - | ${TOOLDIR}/opt $OPTOPTS diff --git a/tools/gccld/Makefile b/tools/gccld/Makefile new file mode 100644 index 0000000..b2d3f73 --- /dev/null +++ b/tools/gccld/Makefile @@ -0,0 +1,29 @@ +##===- tools/gccld/Makefile --------------------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LEVEL = ../.. + +include $(LEVEL)/Makefile.common + +install-local:: $(PROJ_bindir)/gccld + +$(PROJ_bindir)/gccld : gccld.sh Makefile + $(Echo) Installing gccld shell script. + $(Verb) sed "s#@TOOLDIR@#$(PROJ_bindir)#" $< > $@ + $(Verb) chmod 0755 $@ + +all-local:: $(ToolDir)/gccld + +$(ToolDir)/gccld : gccld.sh Makefile + $(Echo) Making $(ToolDir)/gccld shell script. + $(Verb) sed "s#@TOOLDIR@#$(ToolDir)#" $< > $@ + $(Verb) chmod 0755 $@ + +clean-local:: + $(Verb)$(RM) -f $(ToolDir)/gccld diff --git a/tools/gccld/gccld.sh b/tools/gccld/gccld.sh new file mode 100644 index 0000000..b092607 --- /dev/null +++ b/tools/gccld/gccld.sh @@ -0,0 +1,23 @@ +#!/bin/sh +##===- tools/gccld/gccld.sh ------------------------------------*- bash -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## +# +# Synopsis: This shell script is a replacement for the old "gccld" tool that +# existed in LLVM versions before 2.0. The functionality of gccld has +# now been moved to llvm-ld. This shell script provides backwards +# compatibility so build environments invoking gccld can still get +# link (under the covers) with llvm-ld. +# +# Syntax: gccld OPTIONS... (see llvm-ld for details) +# +##===----------------------------------------------------------------------===## +# +echo "gccld: This tool is deprecated, please use llvm-ld" 1>&2 +TOOLDIR=@TOOLDIR@ +$TOOLDIR/llvm-ld "$@" diff --git a/tools/gold/Makefile b/tools/gold/Makefile new file mode 100644 index 0000000..f282a35 --- /dev/null +++ b/tools/gold/Makefile @@ -0,0 +1,30 @@ +#===- tools/gold/Makefile ----------------------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LEVEL = ../.. +LIBRARYNAME = LLVMgold + +# Include this here so we can get the configuration of the targets +# that have been configured for construction. We have to do this +# early so we can set up LINK_COMPONENTS before including Makefile.rules +include $(LEVEL)/Makefile.config + +LINK_LIBS_IN_SHARED=1 +SHARED_LIBRARY = 1 +BUILD_ARCHIVE = 0 +DONT_BUILD_RELINKED = 1 + +LINK_COMPONENTS := +LIBS += -llto + +# Because off_t is used in the public API, the largefile parts are required for +# ABI compatibility. +CXXFLAGS+=-I$(BINUTILS_INCDIR) -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -lLTO + +include $(LEVEL)/Makefile.common diff --git a/tools/gold/README.txt b/tools/gold/README.txt new file mode 100644 index 0000000..92ee3d1 --- /dev/null +++ b/tools/gold/README.txt @@ -0,0 +1,21 @@ +This directory contains a plugin that is designed to work with binutils +gold linker. At present time, this is not the default linker in +binutils, and the default build of gold does not support plugins. + +Obtaining binutils: + + cvs -z 9 -d :pserver:anoncvs@sourceware.org:/cvs/src login + {enter "anoncvs" as the password} + cvs -z 9 -d :pserver:anoncvs@sourceware.org:/cvs/src co binutils + +This will create a src/ directory. Make a build/ directory and from +there configure binutils with "../src/configure --enable-gold --enable-plugins". +Then build binutils with "make all-gold". + +To build the LLVMgold plugin, configure LLVM with the option +--with-binutils-include=/path/to/binutils/src/include/ --enable-pic. To use the +plugin, run "ld-new --plugin /path/to/libLLVMgold.so". +Without PIC libLTO and libLLVMgold are not being built (because they would fail +link on x86-64 with a relocation error: PIC and non-PIC can't be combined). +As an alternative to passing --enable-pic, you can use 'make ENABLE_PIC=1' in +your entire LLVM build. diff --git a/tools/gold/gold-plugin.cpp b/tools/gold/gold-plugin.cpp new file mode 100644 index 0000000..86d3fd3 --- /dev/null +++ b/tools/gold/gold-plugin.cpp @@ -0,0 +1,394 @@ +//===-- gold-plugin.cpp - Plugin to gold for Link Time Optimization ------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This is a gold plugin for LLVM. It provides an LLVM implementation of the +// interface described in http://gcc.gnu.org/wiki/whopr/driver . +// +//===----------------------------------------------------------------------===// + +#include "plugin-api.h" + +#include "llvm-c/lto.h" + +#include "llvm/Support/raw_ostream.h" +#include "llvm/System/Path.h" + +#include <cerrno> +#include <cstdlib> +#include <cstring> +#include <fstream> +#include <list> +#include <vector> + +using namespace llvm; + +namespace { + ld_plugin_status discard_message(int level, const char *format, ...) { + // Die loudly. Recent versions of Gold pass ld_plugin_message as the first + // callback in the transfer vector. This should never be called. + abort(); + } + + ld_plugin_add_symbols add_symbols = NULL; + ld_plugin_get_symbols get_symbols = NULL; + ld_plugin_add_input_file add_input_file = NULL; + ld_plugin_message message = discard_message; + + int api_version = 0; + int gold_version = 0; + + bool generate_api_file = false; + const char *gcc_path = NULL; + + struct claimed_file { + lto_module_t M; + void *handle; + std::vector<ld_plugin_symbol> syms; + }; + + lto_codegen_model output_type = LTO_CODEGEN_PIC_MODEL_STATIC; + std::list<claimed_file> Modules; + std::vector<sys::Path> Cleanup; +} + +ld_plugin_status claim_file_hook(const ld_plugin_input_file *file, + int *claimed); +ld_plugin_status all_symbols_read_hook(void); +ld_plugin_status cleanup_hook(void); + +extern "C" ld_plugin_status onload(ld_plugin_tv *tv); +ld_plugin_status onload(ld_plugin_tv *tv) { + // We're given a pointer to the first transfer vector. We read through them + // until we find one where tv_tag == LDPT_NULL. The REGISTER_* tagged values + // contain pointers to functions that we need to call to register our own + // hooks. The others are addresses of functions we can use to call into gold + // for services. + + bool registeredClaimFile = false; + bool registeredAllSymbolsRead = false; + bool registeredCleanup = false; + + for (; tv->tv_tag != LDPT_NULL; ++tv) { + switch (tv->tv_tag) { + case LDPT_API_VERSION: + api_version = tv->tv_u.tv_val; + break; + case LDPT_GOLD_VERSION: // major * 100 + minor + gold_version = tv->tv_u.tv_val; + break; + case LDPT_LINKER_OUTPUT: + switch (tv->tv_u.tv_val) { + case LDPO_REL: // .o + case LDPO_DYN: // .so + output_type = LTO_CODEGEN_PIC_MODEL_DYNAMIC; + break; + case LDPO_EXEC: // .exe + output_type = LTO_CODEGEN_PIC_MODEL_STATIC; + break; + default: + (*message)(LDPL_ERROR, "Unknown output file type %d", + tv->tv_u.tv_val); + return LDPS_ERR; + } + // TODO: add an option to disable PIC. + //output_type = LTO_CODEGEN_PIC_MODEL_DYNAMIC_NO_PIC; + break; + case LDPT_OPTION: + if (strcmp("generate-api-file", tv->tv_u.tv_string) == 0) { + generate_api_file = true; + } else if (strncmp("gcc=", tv->tv_u.tv_string, 4) == 0) { + if (gcc_path) { + (*message)(LDPL_WARNING, "Path to gcc specified twice. " + "Discarding %s", tv->tv_u.tv_string); + } else { + gcc_path = strdup(tv->tv_u.tv_string + 4); + } + } else { + (*message)(LDPL_WARNING, "Ignoring flag %s", tv->tv_u.tv_string); + } + break; + case LDPT_REGISTER_CLAIM_FILE_HOOK: { + ld_plugin_register_claim_file callback; + callback = tv->tv_u.tv_register_claim_file; + + if ((*callback)(claim_file_hook) != LDPS_OK) + return LDPS_ERR; + + registeredClaimFile = true; + } break; + case LDPT_REGISTER_ALL_SYMBOLS_READ_HOOK: { + ld_plugin_register_all_symbols_read callback; + callback = tv->tv_u.tv_register_all_symbols_read; + + if ((*callback)(all_symbols_read_hook) != LDPS_OK) + return LDPS_ERR; + + registeredAllSymbolsRead = true; + } break; + case LDPT_REGISTER_CLEANUP_HOOK: { + ld_plugin_register_cleanup callback; + callback = tv->tv_u.tv_register_cleanup; + + if ((*callback)(cleanup_hook) != LDPS_OK) + return LDPS_ERR; + + registeredCleanup = true; + } break; + case LDPT_ADD_SYMBOLS: + add_symbols = tv->tv_u.tv_add_symbols; + break; + case LDPT_GET_SYMBOLS: + get_symbols = tv->tv_u.tv_get_symbols; + break; + case LDPT_ADD_INPUT_FILE: + add_input_file = tv->tv_u.tv_add_input_file; + break; + case LDPT_MESSAGE: + message = tv->tv_u.tv_message; + break; + default: + break; + } + } + + if (!registeredClaimFile) { + (*message)(LDPL_ERROR, "register_claim_file not passed to LLVMgold."); + return LDPS_ERR; + } + if (!add_symbols) { + (*message)(LDPL_ERROR, "add_symbols not passed to LLVMgold."); + return LDPS_ERR; + } + + return LDPS_OK; +} + +/// claim_file_hook - called by gold to see whether this file is one that +/// our plugin can handle. We'll try to open it and register all the symbols +/// with add_symbol if possible. +ld_plugin_status claim_file_hook(const ld_plugin_input_file *file, + int *claimed) { + void *buf = NULL; + if (file->offset) { + // Gold has found what might be IR part-way inside of a file, such as + // an .a archive. + if (lseek(file->fd, file->offset, SEEK_SET) == -1) { + (*message)(LDPL_ERROR, + "Failed to seek to archive member of %s at offset %d: %s\n", + file->name, + file->offset, strerror(errno)); + return LDPS_ERR; + } + buf = malloc(file->filesize); + if (!buf) { + (*message)(LDPL_ERROR, + "Failed to allocate buffer for archive member of size: %d\n", + file->filesize); + return LDPS_ERR; + } + if (read(file->fd, buf, file->filesize) != file->filesize) { + (*message)(LDPL_ERROR, + "Failed to read archive member of %s at offset %d: %s\n", + file->name, + file->offset, + strerror(errno)); + free(buf); + return LDPS_ERR; + } + if (!lto_module_is_object_file_in_memory(buf, file->filesize)) { + free(buf); + return LDPS_OK; + } + } else if (!lto_module_is_object_file(file->name)) + return LDPS_OK; + + *claimed = 1; + Modules.resize(Modules.size() + 1); + claimed_file &cf = Modules.back(); + + cf.M = buf ? lto_module_create_from_memory(buf, file->filesize) : + lto_module_create(file->name); + free(buf); + if (!cf.M) { + (*message)(LDPL_ERROR, "Failed to create LLVM module: %s", + lto_get_error_message()); + return LDPS_ERR; + } + cf.handle = file->handle; + unsigned sym_count = lto_module_get_num_symbols(cf.M); + cf.syms.reserve(sym_count); + + for (unsigned i = 0; i != sym_count; ++i) { + lto_symbol_attributes attrs = lto_module_get_symbol_attribute(cf.M, i); + if ((attrs & LTO_SYMBOL_SCOPE_MASK) == LTO_SYMBOL_SCOPE_INTERNAL) + continue; + + cf.syms.push_back(ld_plugin_symbol()); + ld_plugin_symbol &sym = cf.syms.back(); + sym.name = const_cast<char *>(lto_module_get_symbol_name(cf.M, i)); + sym.version = NULL; + + int scope = attrs & LTO_SYMBOL_SCOPE_MASK; + switch (scope) { + case LTO_SYMBOL_SCOPE_HIDDEN: + sym.visibility = LDPV_HIDDEN; + break; + case LTO_SYMBOL_SCOPE_PROTECTED: + sym.visibility = LDPV_PROTECTED; + break; + case 0: // extern + case LTO_SYMBOL_SCOPE_DEFAULT: + sym.visibility = LDPV_DEFAULT; + break; + default: + (*message)(LDPL_ERROR, "Unknown scope attribute: %d", scope); + return LDPS_ERR; + } + + int definition = attrs & LTO_SYMBOL_DEFINITION_MASK; + switch (definition) { + case LTO_SYMBOL_DEFINITION_REGULAR: + sym.def = LDPK_DEF; + break; + case LTO_SYMBOL_DEFINITION_UNDEFINED: + sym.def = LDPK_UNDEF; + break; + case LTO_SYMBOL_DEFINITION_TENTATIVE: + sym.def = LDPK_COMMON; + break; + case LTO_SYMBOL_DEFINITION_WEAK: + sym.def = LDPK_WEAKDEF; + break; + case LTO_SYMBOL_DEFINITION_WEAKUNDEF: + sym.def = LDPK_WEAKUNDEF; + break; + default: + (*message)(LDPL_ERROR, "Unknown definition attribute: %d", definition); + return LDPS_ERR; + } + + // LLVM never emits COMDAT. + sym.size = 0; + sym.comdat_key = NULL; + + sym.resolution = LDPR_UNKNOWN; + } + + cf.syms.reserve(cf.syms.size()); + + if (!cf.syms.empty()) { + if ((*add_symbols)(cf.handle, cf.syms.size(), &cf.syms[0]) != LDPS_OK) { + (*message)(LDPL_ERROR, "Unable to add symbols!"); + return LDPS_ERR; + } + } + + return LDPS_OK; +} + +/// all_symbols_read_hook - gold informs us that all symbols have been read. +/// At this point, we use get_symbols to see if any of our definitions have +/// been overridden by a native object file. Then, perform optimization and +/// codegen. +ld_plugin_status all_symbols_read_hook(void) { + lto_code_gen_t cg = lto_codegen_create(); + + for (std::list<claimed_file>::iterator I = Modules.begin(), + E = Modules.end(); I != E; ++I) + lto_codegen_add_module(cg, I->M); + + std::ofstream api_file; + if (generate_api_file) { + api_file.open("apifile.txt", std::ofstream::out | std::ofstream::trunc); + if (!api_file.is_open()) { + (*message)(LDPL_FATAL, "Unable to open apifile.txt for writing."); + abort(); + } + } + + // If we don't preserve any symbols, libLTO will assume that all symbols are + // needed. Keep all symbols unless we're producing a final executable. + if (output_type == LTO_CODEGEN_PIC_MODEL_STATIC) { + bool anySymbolsPreserved = false; + for (std::list<claimed_file>::iterator I = Modules.begin(), + E = Modules.end(); I != E; ++I) { + (*get_symbols)(I->handle, I->syms.size(), &I->syms[0]); + for (unsigned i = 0, e = I->syms.size(); i != e; i++) { + if (I->syms[i].resolution == LDPR_PREVAILING_DEF || + (I->syms[i].def == LDPK_COMMON && + I->syms[i].resolution == LDPR_RESOLVED_IR)) { + lto_codegen_add_must_preserve_symbol(cg, I->syms[i].name); + anySymbolsPreserved = true; + + if (generate_api_file) + api_file << I->syms[i].name << "\n"; + } + } + } + + if (generate_api_file) + api_file.close(); + + if (!anySymbolsPreserved) { + // This entire file is unnecessary! + lto_codegen_dispose(cg); + return LDPS_OK; + } + } + + lto_codegen_set_pic_model(cg, output_type); + lto_codegen_set_debug_model(cg, LTO_DEBUG_MODEL_DWARF); + if (gcc_path) + lto_codegen_set_gcc_path(cg, gcc_path); + + size_t bufsize = 0; + const char *buffer = static_cast<const char *>(lto_codegen_compile(cg, + &bufsize)); + + std::string ErrMsg; + + sys::Path uniqueObjPath("/tmp/llvmgold.o"); + if (uniqueObjPath.createTemporaryFileOnDisk(true, &ErrMsg)) { + (*message)(LDPL_ERROR, "%s", ErrMsg.c_str()); + return LDPS_ERR; + } + raw_fd_ostream *objFile = new raw_fd_ostream(uniqueObjPath.c_str(), true, + ErrMsg); + if (!ErrMsg.empty()) { + delete objFile; + (*message)(LDPL_ERROR, "%s", ErrMsg.c_str()); + return LDPS_ERR; + } + + objFile->write(buffer, bufsize); + objFile->close(); + + lto_codegen_dispose(cg); + + if ((*add_input_file)(const_cast<char*>(uniqueObjPath.c_str())) != LDPS_OK) { + (*message)(LDPL_ERROR, "Unable to add .o file to the link."); + (*message)(LDPL_ERROR, "File left behind in: %s", uniqueObjPath.c_str()); + return LDPS_ERR; + } + + Cleanup.push_back(uniqueObjPath); + + return LDPS_OK; +} + +ld_plugin_status cleanup_hook(void) { + std::string ErrMsg; + + for (int i = 0, e = Cleanup.size(); i != e; ++i) + if (Cleanup[i].eraseFromDisk(false, &ErrMsg)) + (*message)(LDPL_ERROR, "Failed to delete '%s': %s", Cleanup[i].c_str(), + ErrMsg.c_str()); + + return LDPS_OK; +} diff --git a/tools/llc/CMakeLists.txt b/tools/llc/CMakeLists.txt new file mode 100644 index 0000000..e98b5a2 --- /dev/null +++ b/tools/llc/CMakeLists.txt @@ -0,0 +1,5 @@ +set(LLVM_LINK_COMPONENTS ${LLVM_TARGETS_TO_BUILD} bitreader) + +add_llvm_tool(llc + llc.cpp + ) diff --git a/tools/llc/Makefile b/tools/llc/Makefile new file mode 100644 index 0000000..8514040 --- /dev/null +++ b/tools/llc/Makefile @@ -0,0 +1,21 @@ +#===- tools/llc/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 = llc + +# Include this here so we can get the configuration of the targets +# that have been configured for construction. We have to do this +# early so we can set up LINK_COMPONENTS before including Makefile.rules +include $(LEVEL)/Makefile.config + +LINK_COMPONENTS := $(TARGETS_TO_BUILD) bitreader + +include $(LLVM_SRC_ROOT)/Makefile.rules + diff --git a/tools/llc/llc.cpp b/tools/llc/llc.cpp new file mode 100644 index 0000000..4808f0e --- /dev/null +++ b/tools/llc/llc.cpp @@ -0,0 +1,349 @@ +//===-- llc.cpp - Implement the LLVM Native Code Generator ----------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This is the llc code generator driver. It provides a convenient +// command-line interface for generating native assembly-language code +// or C code, given LLVM bitcode. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Bitcode/ReaderWriter.h" +#include "llvm/CodeGen/FileWriters.h" +#include "llvm/CodeGen/LinkAllCodegenComponents.h" +#include "llvm/CodeGen/LinkAllAsmWriterComponents.h" +#include "llvm/Target/SubtargetFeature.h" +#include "llvm/Target/TargetData.h" +#include "llvm/Target/TargetMachine.h" +#include "llvm/Target/TargetMachineRegistry.h" +#include "llvm/Transforms/Scalar.h" +#include "llvm/Module.h" +#include "llvm/ModuleProvider.h" +#include "llvm/PassManager.h" +#include "llvm/Pass.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/FileUtilities.h" +#include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/PluginLoader.h" +#include "llvm/Support/PrettyStackTrace.h" +#include "llvm/Support/RegistryParser.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Analysis/Verifier.h" +#include "llvm/System/Signals.h" +#include "llvm/Config/config.h" +#include "llvm/LinkAllVMCore.h" +#include <fstream> +#include <iostream> +#include <memory> +using namespace llvm; + +// General options for llc. Other pass-specific options are specified +// within the corresponding llc passes, and target-specific options +// and back-end code generation options are specified with the target machine. +// +static cl::opt<std::string> +InputFilename(cl::Positional, cl::desc("<input bitcode>"), cl::init("-")); + +static cl::opt<std::string> +OutputFilename("o", cl::desc("Output filename"), cl::value_desc("filename")); + +static cl::opt<bool> Force("f", cl::desc("Overwrite output files")); + +// Determine optimization level. +static cl::opt<char> +OptLevel("O", + cl::desc("Optimization level. [-O0, -O1, -O2, or -O3] " + "(default = '-O2')"), + cl::Prefix, + cl::ZeroOrMore, + cl::init(' ')); + +static cl::opt<std::string> +TargetTriple("mtriple", cl::desc("Override target triple for module")); + +static cl::opt<const TargetMachineRegistry::entry*, false, + RegistryParser<TargetMachine> > +MArch("march", cl::desc("Architecture to generate code for:")); + +static cl::opt<std::string> +MCPU("mcpu", + cl::desc("Target a specific cpu type (-mcpu=help for details)"), + cl::value_desc("cpu-name"), + cl::init("")); + +static cl::list<std::string> +MAttrs("mattr", + cl::CommaSeparated, + cl::desc("Target specific attributes (-mattr=help for details)"), + cl::value_desc("a1,+a2,-a3,...")); + +cl::opt<TargetMachine::CodeGenFileType> +FileType("filetype", cl::init(TargetMachine::AssemblyFile), + cl::desc("Choose a file type (not all types are supported by all targets):"), + cl::values( + clEnumValN(TargetMachine::AssemblyFile, "asm", + "Emit an assembly ('.s') file"), + clEnumValN(TargetMachine::ObjectFile, "obj", + "Emit a native object ('.o') file [experimental]"), + clEnumValN(TargetMachine::DynamicLibrary, "dynlib", + "Emit a native dynamic library ('.so') file" + " [experimental]"), + clEnumValEnd)); + +cl::opt<bool> NoVerify("disable-verify", cl::Hidden, + cl::desc("Do not verify input module")); + + +// GetFileNameRoot - Helper function to get the basename of a filename. +static inline std::string +GetFileNameRoot(const std::string &InputFilename) { + std::string IFN = InputFilename; + std::string outputFilename; + int Len = IFN.length(); + if ((Len > 2) && + IFN[Len-3] == '.' && IFN[Len-2] == 'b' && IFN[Len-1] == 'c') { + outputFilename = std::string(IFN.begin(), IFN.end()-3); // s/.bc/.s/ + } else { + outputFilename = IFN; + } + return outputFilename; +} + +static raw_ostream *GetOutputStream(const char *ProgName) { + if (OutputFilename != "") { + if (OutputFilename == "-") + return &outs(); + + // Specified an output filename? + if (!Force && std::ifstream(OutputFilename.c_str())) { + // If force is not specified, make sure not to overwrite a file! + std::cerr << ProgName << ": error opening '" << OutputFilename + << "': file exists!\n" + << "Use -f command line argument to force output\n"; + return 0; + } + // Make sure that the Out file gets unlinked from the disk if we get a + // SIGINT + sys::RemoveFileOnSignal(sys::Path(OutputFilename)); + + std::string error; + raw_ostream *Out = new raw_fd_ostream(OutputFilename.c_str(), true, error); + if (!error.empty()) { + std::cerr << error << '\n'; + delete Out; + return 0; + } + + return Out; + } + + if (InputFilename == "-") { + OutputFilename = "-"; + return &outs(); + } + + OutputFilename = GetFileNameRoot(InputFilename); + + bool Binary = false; + switch (FileType) { + case TargetMachine::AssemblyFile: + if (MArch->Name[0] == 'c') { + if (MArch->Name[1] == 0) + OutputFilename += ".cbe.c"; + else if (MArch->Name[1] == 'p' && MArch->Name[2] == 'p') + OutputFilename += ".cpp"; + else + OutputFilename += ".s"; + } else + OutputFilename += ".s"; + break; + case TargetMachine::ObjectFile: + OutputFilename += ".o"; + Binary = true; + break; + case TargetMachine::DynamicLibrary: + OutputFilename += LTDL_SHLIB_EXT; + Binary = true; + break; + } + + if (!Force && std::ifstream(OutputFilename.c_str())) { + // If force is not specified, make sure not to overwrite a file! + std::cerr << ProgName << ": error opening '" << OutputFilename + << "': file exists!\n" + << "Use -f command line argument to force output\n"; + return 0; + } + + // Make sure that the Out file gets unlinked from the disk if we get a + // SIGINT + sys::RemoveFileOnSignal(sys::Path(OutputFilename)); + + std::string error; + raw_ostream *Out = new raw_fd_ostream(OutputFilename.c_str(), Binary, error); + if (!error.empty()) { + std::cerr << error << '\n'; + delete Out; + return 0; + } + + return Out; +} + +// main - Entry point for the llc compiler. +// +int main(int argc, char **argv) { + sys::PrintStackTraceOnErrorSignal(); + PrettyStackTraceProgram X(argc, argv); + llvm_shutdown_obj Y; // Call llvm_shutdown() on exit. + cl::ParseCommandLineOptions(argc, argv, "llvm system compiler\n"); + + // Load the module to be compiled... + std::string ErrorMessage; + std::auto_ptr<Module> M; + + std::auto_ptr<MemoryBuffer> Buffer( + MemoryBuffer::getFileOrSTDIN(InputFilename, &ErrorMessage)); + if (Buffer.get()) + M.reset(ParseBitcodeFile(Buffer.get(), &ErrorMessage)); + if (M.get() == 0) { + std::cerr << argv[0] << ": bitcode didn't read correctly.\n"; + std::cerr << "Reason: " << ErrorMessage << "\n"; + return 1; + } + Module &mod = *M.get(); + + // If we are supposed to override the target triple, do so now. + if (!TargetTriple.empty()) + mod.setTargetTriple(TargetTriple); + + // Allocate target machine. First, check whether the user has + // explicitly specified an architecture to compile for. + if (MArch == 0) { + std::string Err; + MArch = TargetMachineRegistry::getClosestStaticTargetForModule(mod, Err); + if (MArch == 0) { + std::cerr << argv[0] << ": error auto-selecting target for module '" + << Err << "'. Please use the -march option to explicitly " + << "pick a target.\n"; + return 1; + } + } + + // Package up features to be passed to target/subtarget + std::string FeaturesStr; + if (MCPU.size() || MAttrs.size()) { + SubtargetFeatures Features; + Features.setCPU(MCPU); + for (unsigned i = 0; i != MAttrs.size(); ++i) + Features.AddFeature(MAttrs[i]); + FeaturesStr = Features.getString(); + } + + std::auto_ptr<TargetMachine> target(MArch->CtorFn(mod, FeaturesStr)); + assert(target.get() && "Could not allocate target machine!"); + TargetMachine &Target = *target.get(); + + // Figure out where we are going to send the output... + raw_ostream *Out = GetOutputStream(argv[0]); + if (Out == 0) return 1; + + CodeGenOpt::Level OLvl = CodeGenOpt::Default; + switch (OptLevel) { + default: + std::cerr << argv[0] << ": invalid optimization level.\n"; + return 1; + case ' ': break; + case '0': OLvl = CodeGenOpt::None; break; + case '1': + case '2': OLvl = CodeGenOpt::Default; break; + case '3': OLvl = CodeGenOpt::Aggressive; break; + } + + // If this target requires addPassesToEmitWholeFile, do it now. This is + // used by strange things like the C backend. + if (Target.WantsWholeFile()) { + PassManager PM; + PM.add(new TargetData(*Target.getTargetData())); + if (!NoVerify) + PM.add(createVerifierPass()); + + // Ask the target to add backend passes as necessary. + if (Target.addPassesToEmitWholeFile(PM, *Out, FileType, OLvl)) { + std::cerr << argv[0] << ": target does not support generation of this" + << " file type!\n"; + if (Out != &outs()) delete Out; + // And the Out file is empty and useless, so remove it now. + sys::Path(OutputFilename).eraseFromDisk(); + return 1; + } + PM.run(mod); + } else { + // Build up all of the passes that we want to do to the module. + ExistingModuleProvider Provider(M.release()); + FunctionPassManager Passes(&Provider); + Passes.add(new TargetData(*Target.getTargetData())); + +#ifndef NDEBUG + if (!NoVerify) + Passes.add(createVerifierPass()); +#endif + + // Ask the target to add backend passes as necessary. + MachineCodeEmitter *MCE = 0; + + // Override default to generate verbose assembly. + Target.setAsmVerbosityDefault(true); + + switch (Target.addPassesToEmitFile(Passes, *Out, FileType, OLvl)) { + default: + assert(0 && "Invalid file model!"); + return 1; + case FileModel::Error: + std::cerr << argv[0] << ": target does not support generation of this" + << " file type!\n"; + if (Out != &outs()) delete Out; + // And the Out file is empty and useless, so remove it now. + sys::Path(OutputFilename).eraseFromDisk(); + return 1; + case FileModel::AsmFile: + break; + case FileModel::MachOFile: + MCE = AddMachOWriter(Passes, *Out, Target); + break; + case FileModel::ElfFile: + MCE = AddELFWriter(Passes, *Out, Target); + break; + } + + if (Target.addPassesToEmitFileFinish(Passes, MCE, OLvl)) { + std::cerr << argv[0] << ": target does not support generation of this" + << " file type!\n"; + if (Out != &outs()) delete Out; + // And the Out file is empty and useless, so remove it now. + sys::Path(OutputFilename).eraseFromDisk(); + return 1; + } + + Passes.doInitialization(); + + // Run our queue of passes all at once now, efficiently. + // TODO: this could lazily stream functions out of the module. + for (Module::iterator I = mod.begin(), E = mod.end(); I != E; ++I) + if (!I->isDeclaration()) + Passes.run(*I); + + Passes.doFinalization(); + } + + // Delete the ostream if it's not a stdout stream + if (Out != &outs()) delete Out; + + return 0; +} diff --git a/tools/lli/CMakeLists.txt b/tools/lli/CMakeLists.txt new file mode 100644 index 0000000..ce70d46e --- /dev/null +++ b/tools/lli/CMakeLists.txt @@ -0,0 +1,5 @@ +set(LLVM_LINK_COMPONENTS jit interpreter nativecodegen bitreader selectiondag) + +add_llvm_tool(lli + lli.cpp + ) diff --git a/tools/lli/Makefile b/tools/lli/Makefile new file mode 100644 index 0000000..8f6eeed --- /dev/null +++ b/tools/lli/Makefile @@ -0,0 +1,15 @@ +##===- tools/lli/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 := lli +LINK_COMPONENTS := jit interpreter nativecodegen bitreader selectiondag + +# Enable JIT support +include $(LEVEL)/Makefile.common diff --git a/tools/lli/lli.cpp b/tools/lli/lli.cpp new file mode 100644 index 0000000..6d3cbbc --- /dev/null +++ b/tools/lli/lli.cpp @@ -0,0 +1,214 @@ +//===- lli.cpp - LLVM Interpreter / Dynamic compiler ----------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This utility provides a simple wrapper around the LLVM Execution Engines, +// which allow the direct execution of LLVM programs through a Just-In-Time +// compiler, or through an intepreter if no JIT is available for this platform. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Module.h" +#include "llvm/ModuleProvider.h" +#include "llvm/Type.h" +#include "llvm/Bitcode/ReaderWriter.h" +#include "llvm/CodeGen/LinkAllCodegenComponents.h" +#include "llvm/ExecutionEngine/JIT.h" +#include "llvm/ExecutionEngine/Interpreter.h" +#include "llvm/ExecutionEngine/GenericValue.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/PluginLoader.h" +#include "llvm/Support/PrettyStackTrace.h" +#include "llvm/System/Process.h" +#include "llvm/System/Signals.h" +#include <iostream> +#include <cerrno> +using namespace llvm; + +namespace { + cl::opt<std::string> + InputFile(cl::desc("<input bitcode>"), cl::Positional, cl::init("-")); + + cl::list<std::string> + InputArgv(cl::ConsumeAfter, cl::desc("<program arguments>...")); + + cl::opt<bool> ForceInterpreter("force-interpreter", + cl::desc("Force interpretation: disable JIT"), + cl::init(false)); + + // Determine optimization level. + cl::opt<char> + OptLevel("O", + cl::desc("Optimization level. [-O0, -O1, -O2, or -O3] " + "(default = '-O2')"), + cl::Prefix, + cl::ZeroOrMore, + cl::init(' ')); + + cl::opt<std::string> + TargetTriple("mtriple", cl::desc("Override target triple for module")); + + cl::opt<std::string> + EntryFunc("entry-function", + cl::desc("Specify the entry function (default = 'main') " + "of the executable"), + cl::value_desc("function"), + cl::init("main")); + + cl::opt<std::string> + FakeArgv0("fake-argv0", + cl::desc("Override the 'argv[0]' value passed into the executing" + " program"), cl::value_desc("executable")); + + cl::opt<bool> + DisableCoreFiles("disable-core-files", cl::Hidden, + cl::desc("Disable emission of core files if possible")); + + cl::opt<bool> + NoLazyCompilation("disable-lazy-compilation", + cl::desc("Disable JIT lazy compilation"), + cl::init(false)); +} + +static ExecutionEngine *EE = 0; + +static void do_shutdown() { + delete EE; + llvm_shutdown(); +} + +//===----------------------------------------------------------------------===// +// main Driver function +// +int main(int argc, char **argv, char * const *envp) { + sys::PrintStackTraceOnErrorSignal(); + PrettyStackTraceProgram X(argc, argv); + + atexit(do_shutdown); // Call llvm_shutdown() on exit. + cl::ParseCommandLineOptions(argc, argv, + "llvm interpreter & dynamic compiler\n"); + + // If the user doesn't want core files, disable them. + if (DisableCoreFiles) + sys::Process::PreventCoreFiles(); + + // Load the bitcode... + std::string ErrorMsg; + ModuleProvider *MP = NULL; + if (MemoryBuffer *Buffer = MemoryBuffer::getFileOrSTDIN(InputFile,&ErrorMsg)) { + MP = getBitcodeModuleProvider(Buffer, &ErrorMsg); + if (!MP) delete Buffer; + } + + if (!MP) { + std::cerr << argv[0] << ": error loading program '" << InputFile << "': " + << ErrorMsg << "\n"; + exit(1); + } + + // Get the module as the MP could go away once EE takes over. + Module *Mod = NoLazyCompilation + ? MP->materializeModule(&ErrorMsg) : MP->getModule(); + if (!Mod) { + std::cerr << argv[0] << ": bitcode didn't read correctly.\n"; + std::cerr << "Reason: " << ErrorMsg << "\n"; + exit(1); + } + + // If we are supposed to override the target triple, do so now. + if (!TargetTriple.empty()) + Mod->setTargetTriple(TargetTriple); + + CodeGenOpt::Level OLvl = CodeGenOpt::Default; + switch (OptLevel) { + default: + std::cerr << argv[0] << ": invalid optimization level.\n"; + return 1; + case ' ': break; + case '0': OLvl = CodeGenOpt::None; break; + case '1': + case '2': OLvl = CodeGenOpt::Default; break; + case '3': OLvl = CodeGenOpt::Aggressive; break; + } + + EE = ExecutionEngine::create(MP, ForceInterpreter, &ErrorMsg, OLvl); + if (!EE && !ErrorMsg.empty()) { + std::cerr << argv[0] << ":error creating EE: " << ErrorMsg << "\n"; + exit(1); + } + + if (NoLazyCompilation) + EE->DisableLazyCompilation(); + + // If the user specifically requested an argv[0] to pass into the program, + // do it now. + if (!FakeArgv0.empty()) { + InputFile = FakeArgv0; + } else { + // Otherwise, if there is a .bc suffix on the executable strip it off, it + // might confuse the program. + if (InputFile.rfind(".bc") == InputFile.length() - 3) + InputFile.erase(InputFile.length() - 3); + } + + // Add the module's name to the start of the vector of arguments to main(). + InputArgv.insert(InputArgv.begin(), InputFile); + + // Call the main function from M as if its signature were: + // int main (int argc, char **argv, const char **envp) + // using the contents of Args to determine argc & argv, and the contents of + // EnvVars to determine envp. + // + Function *EntryFn = Mod->getFunction(EntryFunc); + if (!EntryFn) { + std::cerr << '\'' << EntryFunc << "\' function not found in module.\n"; + return -1; + } + + // If the program doesn't explicitly call exit, we will need the Exit + // function later on to make an explicit call, so get the function now. + Constant *Exit = Mod->getOrInsertFunction("exit", Type::VoidTy, + Type::Int32Ty, NULL); + + // Reset errno to zero on entry to main. + errno = 0; + + // Run static constructors. + EE->runStaticConstructorsDestructors(false); + + if (NoLazyCompilation) { + for (Module::iterator I = Mod->begin(), E = Mod->end(); I != E; ++I) { + Function *Fn = &*I; + if (Fn != EntryFn && !Fn->isDeclaration()) + EE->getPointerToFunction(Fn); + } + } + + // Run main. + int Result = EE->runFunctionAsMain(EntryFn, InputArgv, envp); + + // Run static destructors. + EE->runStaticConstructorsDestructors(true); + + // If the program didn't call exit explicitly, we should call it now. + // This ensures that any atexit handlers get called correctly. + if (Function *ExitF = dyn_cast<Function>(Exit)) { + std::vector<GenericValue> Args; + GenericValue ResultGV; + ResultGV.IntVal = APInt(32, Result); + Args.push_back(ResultGV); + EE->runFunction(ExitF, Args); + std::cerr << "ERROR: exit(" << Result << ") returned!\n"; + abort(); + } else { + std::cerr << "ERROR: exit defined with wrong prototype!\n"; + abort(); + } +} diff --git a/tools/llvm-ar/CMakeLists.txt b/tools/llvm-ar/CMakeLists.txt new file mode 100644 index 0000000..c8b0b72 --- /dev/null +++ b/tools/llvm-ar/CMakeLists.txt @@ -0,0 +1,8 @@ +set(LLVM_LINK_COMPONENTS archive) +set(LLVM_REQUIRES_EH 1) + +add_llvm_tool(llvm-ar + llvm-ar.cpp + ) + +# TODO: Support check-local. diff --git a/tools/llvm-ar/Makefile b/tools/llvm-ar/Makefile new file mode 100644 index 0000000..e4fe4e8 --- /dev/null +++ b/tools/llvm-ar/Makefile @@ -0,0 +1,25 @@ +##===- tools/llvm-ar/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 = llvm-ar +LINK_COMPONENTS = archive +REQUIRES_EH := 1 + +# This tool has no plugins, optimize startup time. +TOOL_NO_EXPORTS = 1 + +include $(LEVEL)/Makefile.common + +check-local:: + $(Echo) Checking llvm-ar + $(Verb) $(ToolDir)/llvm-ar zRrS nada.a . + $(Verb) $(ToolDir)/llvm-ar tv nada.a | \ + grep Debug/llvm-ar.d >/dev/null 2>&1 + $(Verb) $(RM) -f nada.a diff --git a/tools/llvm-ar/llvm-ar.cpp b/tools/llvm-ar/llvm-ar.cpp new file mode 100644 index 0000000..5d81fc7 --- /dev/null +++ b/tools/llvm-ar/llvm-ar.cpp @@ -0,0 +1,776 @@ +//===-- llvm-ar.cpp - LLVM archive librarian utility ----------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Builds up (relatively) standard unix archive files (.a) containing LLVM +// bitcode or other files. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Module.h" +#include "llvm/Bitcode/Archive.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/PrettyStackTrace.h" +#include "llvm/System/Signals.h" +#include <iostream> +#include <algorithm> +#include <iomanip> +#include <memory> +using namespace llvm; + +// Option for compatibility with AIX, not used but must allow it to be present. +static cl::opt<bool> +X32Option ("X32_64", cl::Hidden, + cl::desc("Ignored option for compatibility with AIX")); + +// llvm-ar operation code and modifier flags. This must come first. +static cl::opt<std::string> +Options(cl::Positional, cl::Required, cl::desc("{operation}[modifiers]...")); + +// llvm-ar remaining positional arguments. +static cl::list<std::string> +RestOfArgs(cl::Positional, cl::OneOrMore, + cl::desc("[relpos] [count] <archive-file> [members]...")); + +// MoreHelp - Provide additional help output explaining the operations and +// modifiers of llvm-ar. This object instructs the CommandLine library +// to print the text of the constructor when the --help option is given. +static cl::extrahelp MoreHelp( + "\nOPERATIONS:\n" + " d[NsS] - delete file(s) from the archive\n" + " m[abiSs] - move file(s) in the archive\n" + " p[kN] - print file(s) found in the archive\n" + " q[ufsS] - quick append file(s) to the archive\n" + " r[abfiuzRsS] - replace or insert file(s) into the archive\n" + " t - display contents of archive\n" + " x[No] - extract file(s) from the archive\n" + "\nMODIFIERS (operation specific):\n" + " [a] - put file(s) after [relpos]\n" + " [b] - put file(s) before [relpos] (same as [i])\n" + " [f] - truncate inserted file names\n" + " [i] - put file(s) before [relpos] (same as [b])\n" + " [k] - always print bitcode files (default is to skip them)\n" + " [N] - use instance [count] of name\n" + " [o] - preserve original dates\n" + " [P] - use full path names when matching\n" + " [R] - recurse through directories when inserting\n" + " [s] - create an archive index (cf. ranlib)\n" + " [S] - do not build a symbol table\n" + " [u] - update only files newer than archive contents\n" + " [z] - compress files before inserting/extracting\n" + "\nMODIFIERS (generic):\n" + " [c] - do not warn if the library had to be created\n" + " [v] - be verbose about actions taken\n" + " [V] - be *really* verbose about actions taken\n" +); + +// This enumeration delineates the kinds of operations on an archive +// that are permitted. +enum ArchiveOperation { + NoOperation, ///< An operation hasn't been specified + Print, ///< Print the contents of the archive + Delete, ///< Delete the specified members + Move, ///< Move members to end or as given by {a,b,i} modifiers + QuickAppend, ///< Quickly append to end of archive + ReplaceOrInsert, ///< Replace or Insert members + DisplayTable, ///< Display the table of contents + Extract ///< Extract files back to file system +}; + +// Modifiers to follow operation to vary behavior +bool AddAfter = false; ///< 'a' modifier +bool AddBefore = false; ///< 'b' modifier +bool Create = false; ///< 'c' modifier +bool TruncateNames = false; ///< 'f' modifier +bool InsertBefore = false; ///< 'i' modifier +bool DontSkipBitcode = false; ///< 'k' modifier +bool UseCount = false; ///< 'N' modifier +bool OriginalDates = false; ///< 'o' modifier +bool FullPath = false; ///< 'P' modifier +bool RecurseDirectories = false; ///< 'R' modifier +bool SymTable = true; ///< 's' & 'S' modifiers +bool OnlyUpdate = false; ///< 'u' modifier +bool Verbose = false; ///< 'v' modifier +bool ReallyVerbose = false; ///< 'V' modifier +bool Compression = false; ///< 'z' modifier + +// Relative Positional Argument (for insert/move). This variable holds +// the name of the archive member to which the 'a', 'b' or 'i' modifier +// refers. Only one of 'a', 'b' or 'i' can be specified so we only need +// one variable. +std::string RelPos; + +// Select which of multiple entries in the archive with the same name should be +// used (specified with -N) for the delete and extract operations. +int Count = 1; + +// This variable holds the name of the archive file as given on the +// command line. +std::string ArchiveName; + +// This variable holds the list of member files to proecess, as given +// on the command line. +std::vector<std::string> Members; + +// This variable holds the (possibly expanded) list of path objects that +// correspond to files we will +std::set<sys::Path> Paths; + +// The Archive object to which all the editing operations will be sent. +Archive* TheArchive = 0; + +// getRelPos - Extract the member filename from the command line for +// the [relpos] argument associated with a, b, and i modifiers +void getRelPos() { + if(RestOfArgs.size() > 0) { + RelPos = RestOfArgs[0]; + RestOfArgs.erase(RestOfArgs.begin()); + } + else + throw "Expected [relpos] for a, b, or i modifier"; +} + +// getCount - Extract the [count] argument associated with the N modifier +// from the command line and check its value. +void getCount() { + if(RestOfArgs.size() > 0) { + Count = atoi(RestOfArgs[0].c_str()); + RestOfArgs.erase(RestOfArgs.begin()); + } + else + throw "Expected [count] value with N modifier"; + + // Non-positive counts are not allowed + if (Count < 1) + throw "Invalid [count] value (not a positive integer)"; +} + +// getArchive - Get the archive file name from the command line +void getArchive() { + if(RestOfArgs.size() > 0) { + ArchiveName = RestOfArgs[0]; + RestOfArgs.erase(RestOfArgs.begin()); + } + else + throw "An archive name must be specified."; +} + +// getMembers - Copy over remaining items in RestOfArgs to our Members vector +// This is just for clarity. +void getMembers() { + if(RestOfArgs.size() > 0) + Members = std::vector<std::string>(RestOfArgs); +} + +// parseCommandLine - Parse the command line options as presented and return the +// operation specified. Process all modifiers and check to make sure that +// constraints on modifier/operation pairs have not been violated. +ArchiveOperation parseCommandLine() { + + // Keep track of number of operations. We can only specify one + // per execution. + unsigned NumOperations = 0; + + // Keep track of the number of positional modifiers (a,b,i). Only + // one can be specified. + unsigned NumPositional = 0; + + // Keep track of which operation was requested + ArchiveOperation Operation = NoOperation; + + for(unsigned i=0; i<Options.size(); ++i) { + switch(Options[i]) { + case 'd': ++NumOperations; Operation = Delete; break; + case 'm': ++NumOperations; Operation = Move ; break; + case 'p': ++NumOperations; Operation = Print; break; + case 'q': ++NumOperations; Operation = QuickAppend; break; + case 'r': ++NumOperations; Operation = ReplaceOrInsert; break; + case 't': ++NumOperations; Operation = DisplayTable; break; + case 'x': ++NumOperations; Operation = Extract; break; + case 'c': Create = true; break; + case 'f': TruncateNames = true; break; + case 'k': DontSkipBitcode = true; break; + case 'l': /* accepted but unused */ break; + case 'o': OriginalDates = true; break; + case 'P': FullPath = true; break; + case 'R': RecurseDirectories = true; break; + case 's': SymTable = true; break; + case 'S': SymTable = false; break; + case 'u': OnlyUpdate = true; break; + case 'v': Verbose = true; break; + case 'V': Verbose = ReallyVerbose = true; break; + case 'z': Compression = true; break; + case 'a': + getRelPos(); + AddAfter = true; + NumPositional++; + break; + case 'b': + getRelPos(); + AddBefore = true; + NumPositional++; + break; + case 'i': + getRelPos(); + InsertBefore = true; + NumPositional++; + break; + case 'N': + getCount(); + UseCount = true; + break; + default: + cl::PrintHelpMessage(); + } + } + + // At this point, the next thing on the command line must be + // the archive name. + getArchive(); + + // Everything on the command line at this point is a member. + getMembers(); + + // Perform various checks on the operation/modifier specification + // to make sure we are dealing with a legal request. + if (NumOperations == 0) + throw "You must specify at least one of the operations"; + if (NumOperations > 1) + throw "Only one operation may be specified"; + if (NumPositional > 1) + throw "You may only specify one of a, b, and i modifiers"; + if (AddAfter || AddBefore || InsertBefore) + if (Operation != Move && Operation != ReplaceOrInsert) + throw "The 'a', 'b' and 'i' modifiers can only be specified with " + "the 'm' or 'r' operations"; + if (RecurseDirectories && Operation != ReplaceOrInsert) + throw "The 'R' modifiers is only applicabe to the 'r' operation"; + if (OriginalDates && Operation != Extract) + throw "The 'o' modifier is only applicable to the 'x' operation"; + if (TruncateNames && Operation!=QuickAppend && Operation!=ReplaceOrInsert) + throw "The 'f' modifier is only applicable to the 'q' and 'r' operations"; + if (OnlyUpdate && Operation != ReplaceOrInsert) + throw "The 'u' modifier is only applicable to the 'r' operation"; + if (Compression && Operation!=ReplaceOrInsert && Operation!=Extract) + throw "The 'z' modifier is only applicable to the 'r' and 'x' operations"; + if (Count > 1 && Members.size() > 1) + throw "Only one member name may be specified with the 'N' modifier"; + + // Return the parsed operation to the caller + return Operation; +} + +// recurseDirectories - Implements the "R" modifier. This function scans through +// the Paths vector (built by buildPaths, below) and replaces any directories it +// finds with all the files in that directory (recursively). It uses the +// sys::Path::getDirectoryContent method to perform the actual directory scans. +bool +recurseDirectories(const sys::Path& path, + std::set<sys::Path>& result, std::string* ErrMsg) { + result.clear(); + if (RecurseDirectories) { + std::set<sys::Path> content; + if (path.getDirectoryContents(content, ErrMsg)) + return true; + + for (std::set<sys::Path>::iterator I = content.begin(), E = content.end(); + I != E; ++I) { + // Make sure it exists and is a directory + sys::PathWithStatus PwS(*I); + const sys::FileStatus *Status = PwS.getFileStatus(false, ErrMsg); + if (!Status) + return true; + if (Status->isDir) { + std::set<sys::Path> moreResults; + if (recurseDirectories(*I, moreResults, ErrMsg)) + return true; + result.insert(moreResults.begin(), moreResults.end()); + } else { + result.insert(*I); + } + } + } + return false; +} + +// buildPaths - Convert the strings in the Members vector to sys::Path objects +// and make sure they are valid and exist exist. This check is only needed for +// the operations that add/replace files to the archive ('q' and 'r') +bool buildPaths(bool checkExistence, std::string* ErrMsg) { + for (unsigned i = 0; i < Members.size(); i++) { + sys::Path aPath; + if (!aPath.set(Members[i])) + throw std::string("File member name invalid: ") + Members[i]; + if (checkExistence) { + if (!aPath.exists()) + throw std::string("File does not exist: ") + Members[i]; + std::string Err; + sys::PathWithStatus PwS(aPath); + const sys::FileStatus *si = PwS.getFileStatus(false, &Err); + if (!si) + throw Err; + if (si->isDir) { + std::set<sys::Path> dirpaths; + if (recurseDirectories(aPath, dirpaths, ErrMsg)) + return true; + Paths.insert(dirpaths.begin(),dirpaths.end()); + } else { + Paths.insert(aPath); + } + } else { + Paths.insert(aPath); + } + } + return false; +} + +// printSymbolTable - print out the archive's symbol table. +void printSymbolTable() { + std::cout << "\nArchive Symbol Table:\n"; + const Archive::SymTabType& symtab = TheArchive->getSymbolTable(); + for (Archive::SymTabType::const_iterator I=symtab.begin(), E=symtab.end(); + I != E; ++I ) { + unsigned offset = TheArchive->getFirstFileOffset() + I->second; + std::cout << " " << std::setw(9) << offset << "\t" << I->first <<"\n"; + } +} + +// doPrint - Implements the 'p' operation. This function traverses the archive +// looking for members that match the path list. It is careful to uncompress +// things that should be and to skip bitcode files unless the 'k' modifier was +// given. +bool doPrint(std::string* ErrMsg) { + if (buildPaths(false, ErrMsg)) + return true; + unsigned countDown = Count; + for (Archive::iterator I = TheArchive->begin(), E = TheArchive->end(); + I != E; ++I ) { + if (Paths.empty() || + (std::find(Paths.begin(), Paths.end(), I->getPath()) != Paths.end())) { + if (countDown == 1) { + const char* data = reinterpret_cast<const char*>(I->getData()); + + // Skip things that don't make sense to print + if (I->isLLVMSymbolTable() || I->isSVR4SymbolTable() || + I->isBSD4SymbolTable() || (!DontSkipBitcode && I->isBitcode())) + continue; + + if (Verbose) + std::cout << "Printing " << I->getPath().toString() << "\n"; + + unsigned len = I->getSize(); + std::cout.write(data, len); + } else { + countDown--; + } + } + } + return false; +} + +// putMode - utility function for printing out the file mode when the 't' +// operation is in verbose mode. +void +printMode(unsigned mode) { + if (mode & 004) + std::cout << "r"; + else + std::cout << "-"; + if (mode & 002) + std::cout << "w"; + else + std::cout << "-"; + if (mode & 001) + std::cout << "x"; + else + std::cout << "-"; +} + +// doDisplayTable - Implement the 't' operation. This function prints out just +// the file names of each of the members. However, if verbose mode is requested +// ('v' modifier) then the file type, permission mode, user, group, size, and +// modification time are also printed. +bool +doDisplayTable(std::string* ErrMsg) { + if (buildPaths(false, ErrMsg)) + return true; + for (Archive::iterator I = TheArchive->begin(), E = TheArchive->end(); + I != E; ++I ) { + if (Paths.empty() || + (std::find(Paths.begin(), Paths.end(), I->getPath()) != Paths.end())) { + if (Verbose) { + // FIXME: Output should be this format: + // Zrw-r--r-- 500/ 500 525 Nov 8 17:42 2004 Makefile + if (I->isBitcode()) + std::cout << "b"; + else if (I->isCompressed()) + std::cout << "Z"; + else + std::cout << " "; + unsigned mode = I->getMode(); + printMode((mode >> 6) & 007); + printMode((mode >> 3) & 007); + printMode(mode & 007); + std::cout << " " << std::setw(4) << I->getUser(); + std::cout << "/" << std::setw(4) << I->getGroup(); + std::cout << " " << std::setw(8) << I->getSize(); + std::cout << " " << std::setw(20) << + I->getModTime().toString().substr(4); + std::cout << " " << I->getPath().toString() << "\n"; + } else { + std::cout << I->getPath().toString() << "\n"; + } + } + } + if (ReallyVerbose) + printSymbolTable(); + return false; +} + +// doExtract - Implement the 'x' operation. This function extracts files back to +// the file system, making sure to uncompress any that were compressed +bool +doExtract(std::string* ErrMsg) { + if (buildPaths(false, ErrMsg)) + return true; + for (Archive::iterator I = TheArchive->begin(), E = TheArchive->end(); + I != E; ++I ) { + if (Paths.empty() || + (std::find(Paths.begin(), Paths.end(), I->getPath()) != Paths.end())) { + + // Make sure the intervening directories are created + if (I->hasPath()) { + sys::Path dirs(I->getPath()); + dirs.eraseComponent(); + if (dirs.createDirectoryOnDisk(/*create_parents=*/true, ErrMsg)) + return true; + } + + // Open up a file stream for writing + std::ios::openmode io_mode = std::ios::out | std::ios::trunc | + std::ios::binary; + std::ofstream file(I->getPath().c_str(), io_mode); + + // Get the data and its length + const char* data = reinterpret_cast<const char*>(I->getData()); + unsigned len = I->getSize(); + + // Write the data. + file.write(data,len); + file.close(); + + // If we're supposed to retain the original modification times, etc. do so + // now. + if (OriginalDates) + I->getPath().setStatusInfoOnDisk(I->getFileStatus()); + } + } + return false; +} + +// doDelete - Implement the delete operation. This function deletes zero or more +// members from the archive. Note that if the count is specified, there should +// be no more than one path in the Paths list or else this algorithm breaks. +// That check is enforced in parseCommandLine (above). +bool +doDelete(std::string* ErrMsg) { + if (buildPaths(false, ErrMsg)) + return true; + if (Paths.empty()) + return false; + unsigned countDown = Count; + for (Archive::iterator I = TheArchive->begin(), E = TheArchive->end(); + I != E; ) { + if (std::find(Paths.begin(), Paths.end(), I->getPath()) != Paths.end()) { + if (countDown == 1) { + Archive::iterator J = I; + ++I; + TheArchive->erase(J); + } else + countDown--; + } else { + ++I; + } + } + + // We're done editting, reconstruct the archive. + if (TheArchive->writeToDisk(SymTable,TruncateNames,Compression,ErrMsg)) + return true; + if (ReallyVerbose) + printSymbolTable(); + return false; +} + +// doMore - Implement the move operation. This function re-arranges just the +// order of the archive members so that when the archive is written the move +// of the members is accomplished. Note the use of the RelPos variable to +// determine where the items should be moved to. +bool +doMove(std::string* ErrMsg) { + if (buildPaths(false, ErrMsg)) + return true; + + // By default and convention the place to move members to is the end of the + // archive. + Archive::iterator moveto_spot = TheArchive->end(); + + // However, if the relative positioning modifiers were used, we need to scan + // the archive to find the member in question. If we don't find it, its no + // crime, we just move to the end. + if (AddBefore || InsertBefore || AddAfter) { + for (Archive::iterator I = TheArchive->begin(), E= TheArchive->end(); + I != E; ++I ) { + if (RelPos == I->getPath().toString()) { + if (AddAfter) { + moveto_spot = I; + moveto_spot++; + } else { + moveto_spot = I; + } + break; + } + } + } + + // Keep a list of the paths remaining to be moved + std::set<sys::Path> remaining(Paths); + + // Scan the archive again, this time looking for the members to move to the + // moveto_spot. + for (Archive::iterator I = TheArchive->begin(), E= TheArchive->end(); + I != E && !remaining.empty(); ++I ) { + std::set<sys::Path>::iterator found = + std::find(remaining.begin(),remaining.end(),I->getPath()); + if (found != remaining.end()) { + if (I != moveto_spot) + TheArchive->splice(moveto_spot,*TheArchive,I); + remaining.erase(found); + } + } + + // We're done editting, reconstruct the archive. + if (TheArchive->writeToDisk(SymTable,TruncateNames,Compression,ErrMsg)) + return true; + if (ReallyVerbose) + printSymbolTable(); + return false; +} + +// doQuickAppend - Implements the 'q' operation. This function just +// indiscriminantly adds the members to the archive and rebuilds it. +bool +doQuickAppend(std::string* ErrMsg) { + // Get the list of paths to append. + if (buildPaths(true, ErrMsg)) + return true; + if (Paths.empty()) + return false; + + // Append them quickly. + for (std::set<sys::Path>::iterator PI = Paths.begin(), PE = Paths.end(); + PI != PE; ++PI) { + if (TheArchive->addFileBefore(*PI,TheArchive->end(),ErrMsg)) + return true; + } + + // We're done editting, reconstruct the archive. + if (TheArchive->writeToDisk(SymTable,TruncateNames,Compression,ErrMsg)) + return true; + if (ReallyVerbose) + printSymbolTable(); + return false; +} + +// doReplaceOrInsert - Implements the 'r' operation. This function will replace +// any existing files or insert new ones into the archive. +bool +doReplaceOrInsert(std::string* ErrMsg) { + + // Build the list of files to be added/replaced. + if (buildPaths(true, ErrMsg)) + return true; + if (Paths.empty()) + return false; + + // Keep track of the paths that remain to be inserted. + std::set<sys::Path> remaining(Paths); + + // Default the insertion spot to the end of the archive + Archive::iterator insert_spot = TheArchive->end(); + + // Iterate over the archive contents + for (Archive::iterator I = TheArchive->begin(), E = TheArchive->end(); + I != E && !remaining.empty(); ++I ) { + + // Determine if this archive member matches one of the paths we're trying + // to replace. + + std::set<sys::Path>::iterator found = remaining.end(); + for (std::set<sys::Path>::iterator RI = remaining.begin(), + RE = remaining.end(); RI != RE; ++RI ) { + std::string compare(RI->toString()); + if (TruncateNames && compare.length() > 15) { + const char* nm = compare.c_str(); + unsigned len = compare.length(); + size_t slashpos = compare.rfind('/'); + if (slashpos != std::string::npos) { + nm += slashpos + 1; + len -= slashpos +1; + } + if (len > 15) + len = 15; + compare.assign(nm,len); + } + if (compare == I->getPath().toString()) { + found = RI; + break; + } + } + + if (found != remaining.end()) { + std::string Err; + sys::PathWithStatus PwS(*found); + const sys::FileStatus *si = PwS.getFileStatus(false, &Err); + if (!si) + return true; + if (!si->isDir) { + if (OnlyUpdate) { + // Replace the item only if it is newer. + if (si->modTime > I->getModTime()) + if (I->replaceWith(*found, ErrMsg)) + return true; + } else { + // Replace the item regardless of time stamp + if (I->replaceWith(*found, ErrMsg)) + return true; + } + } else { + // We purposefully ignore directories. + } + + // Remove it from our "to do" list + remaining.erase(found); + } + + // Determine if this is the place where we should insert + if ((AddBefore || InsertBefore) && (RelPos == I->getPath().toString())) + insert_spot = I; + else if (AddAfter && (RelPos == I->getPath().toString())) { + insert_spot = I; + insert_spot++; + } + } + + // If we didn't replace all the members, some will remain and need to be + // inserted at the previously computed insert-spot. + if (!remaining.empty()) { + for (std::set<sys::Path>::iterator PI = remaining.begin(), + PE = remaining.end(); PI != PE; ++PI) { + if (TheArchive->addFileBefore(*PI,insert_spot, ErrMsg)) + return true; + } + } + + // We're done editting, reconstruct the archive. + if (TheArchive->writeToDisk(SymTable,TruncateNames,Compression,ErrMsg)) + return true; + if (ReallyVerbose) + printSymbolTable(); + return false; +} + +// main - main program for llvm-ar .. see comments in the code +int main(int argc, char **argv) { + // Print a stack trace if we signal out. + sys::PrintStackTraceOnErrorSignal(); + PrettyStackTraceProgram X(argc, argv); + llvm_shutdown_obj Y; // Call llvm_shutdown() on exit. + + // Have the command line options parsed and handle things + // like --help and --version. + cl::ParseCommandLineOptions(argc, argv, + "LLVM Archiver (llvm-ar)\n\n" + " This program archives bitcode files into single libraries\n" + ); + + int exitCode = 0; + + // Make sure we don't exit with "unhandled exception". + try { + // Do our own parsing of the command line because the CommandLine utility + // can't handle the grouped positional parameters without a dash. + ArchiveOperation Operation = parseCommandLine(); + + // Check the path name of the archive + sys::Path ArchivePath; + if (!ArchivePath.set(ArchiveName)) + throw std::string("Archive name invalid: ") + ArchiveName; + + // Create or open the archive object. + if (!ArchivePath.exists()) { + // Produce a warning if we should and we're creating the archive + if (!Create) + std::cerr << argv[0] << ": creating " << ArchivePath.toString() << "\n"; + TheArchive = Archive::CreateEmpty(ArchivePath); + TheArchive->writeToDisk(); + } else { + std::string Error; + TheArchive = Archive::OpenAndLoad(ArchivePath, &Error); + if (TheArchive == 0) { + std::cerr << argv[0] << ": error loading '" << ArchivePath << "': " + << Error << "!\n"; + return 1; + } + } + + // Make sure we're not fooling ourselves. + assert(TheArchive && "Unable to instantiate the archive"); + + // Make sure we clean up the archive even on failure. + std::auto_ptr<Archive> AutoArchive(TheArchive); + + // Perform the operation + std::string ErrMsg; + bool haveError = false; + switch (Operation) { + case Print: haveError = doPrint(&ErrMsg); break; + case Delete: haveError = doDelete(&ErrMsg); break; + case Move: haveError = doMove(&ErrMsg); break; + case QuickAppend: haveError = doQuickAppend(&ErrMsg); break; + case ReplaceOrInsert: haveError = doReplaceOrInsert(&ErrMsg); break; + case DisplayTable: haveError = doDisplayTable(&ErrMsg); break; + case Extract: haveError = doExtract(&ErrMsg); break; + case NoOperation: + std::cerr << argv[0] << ": No operation was selected.\n"; + break; + } + if (haveError) { + std::cerr << argv[0] << ": " << ErrMsg << "\n"; + return 1; + } + } catch (const char*msg) { + // These errors are usage errors, thrown only by the various checks in the + // code above. + std::cerr << argv[0] << ": " << msg << "\n\n"; + cl::PrintHelpMessage(); + exitCode = 1; + } catch (const std::string& msg) { + // These errors are thrown by LLVM libraries (e.g. lib System) and represent + // a more serious error so we bump the exitCode and don't print the usage. + std::cerr << argv[0] << ": " << msg << "\n"; + exitCode = 2; + } catch (...) { + // This really shouldn't happen, but just in case .... + std::cerr << argv[0] << ": An unexpected unknown exception occurred.\n"; + exitCode = 3; + } + + // Return result code back to operating system. + return exitCode; +} diff --git a/tools/llvm-as/CMakeLists.txt b/tools/llvm-as/CMakeLists.txt new file mode 100644 index 0000000..eef4a13 --- /dev/null +++ b/tools/llvm-as/CMakeLists.txt @@ -0,0 +1,6 @@ +set(LLVM_LINK_COMPONENTS asmparser bitwriter) +set(LLVM_REQUIRES_EH 1) + +add_llvm_tool(llvm-as + llvm-as.cpp + ) diff --git a/tools/llvm-as/Makefile b/tools/llvm-as/Makefile new file mode 100644 index 0000000..ae449c4 --- /dev/null +++ b/tools/llvm-as/Makefile @@ -0,0 +1,18 @@ +##===- tools/llvm-as/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 = llvm-as +LINK_COMPONENTS := asmparser bitwriter +REQUIRES_EH := 1 + +# This tool has no plugins, optimize startup time. +TOOL_NO_EXPORTS = 1 + +include $(LEVEL)/Makefile.common diff --git a/tools/llvm-as/llvm-as.cpp b/tools/llvm-as/llvm-as.cpp new file mode 100644 index 0000000..79ece8f --- /dev/null +++ b/tools/llvm-as/llvm-as.cpp @@ -0,0 +1,149 @@ +//===--- llvm-as.cpp - The low-level LLVM assembler -----------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This utility may be invoked in the following manner: +// llvm-as --help - Output information about command line switches +// llvm-as [options] - Read LLVM asm from stdin, write bitcode to stdout +// llvm-as [options] x.ll - Read LLVM asm from the x.ll file, write bitcode +// to the x.bc file. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Module.h" +#include "llvm/Assembly/Parser.h" +#include "llvm/Analysis/Verifier.h" +#include "llvm/Bitcode/ReaderWriter.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/PrettyStackTrace.h" +#include "llvm/Support/Streams.h" +#include "llvm/Support/SystemUtils.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/System/Signals.h" +#include <fstream> +#include <iostream> +#include <memory> +using namespace llvm; + +static cl::opt<std::string> +InputFilename(cl::Positional, cl::desc("<input .llvm file>"), cl::init("-")); + +static cl::opt<std::string> +OutputFilename("o", cl::desc("Override output filename"), + cl::value_desc("filename")); + +static cl::opt<bool> +Force("f", cl::desc("Overwrite output files")); + +static cl::opt<bool> +DisableOutput("disable-output", cl::desc("Disable output"), cl::init(false)); + +static cl::opt<bool> +DumpAsm("d", cl::desc("Print assembly as parsed"), cl::Hidden); + +static cl::opt<bool> +DisableVerify("disable-verify", cl::Hidden, + cl::desc("Do not run verifier on input LLVM (dangerous!)")); + +int main(int argc, char **argv) { + // Print a stack trace if we signal out. + sys::PrintStackTraceOnErrorSignal(); + PrettyStackTraceProgram X(argc, argv); + llvm_shutdown_obj Y; // Call llvm_shutdown() on exit. + cl::ParseCommandLineOptions(argc, argv, "llvm .ll -> .bc assembler\n"); + + int exitCode = 0; + std::ostream *Out = 0; + try { + // Parse the file now... + ParseError Err; + std::auto_ptr<Module> M(ParseAssemblyFile(InputFilename, Err)); + if (M.get() == 0) { + Err.PrintError(argv[0], errs()); + return 1; + } + + if (!DisableVerify) { + std::string Err; + if (verifyModule(*M.get(), ReturnStatusAction, &Err)) { + cerr << argv[0] + << ": assembly parsed, but does not verify as correct!\n"; + cerr << Err; + return 1; + } + } + + if (DumpAsm) cerr << "Here's the assembly:\n" << *M.get(); + + if (OutputFilename != "") { // Specified an output filename? + if (OutputFilename != "-") { // Not stdout? + if (!Force && std::ifstream(OutputFilename.c_str())) { + // If force is not specified, make sure not to overwrite a file! + cerr << argv[0] << ": error opening '" << OutputFilename + << "': file exists!\n" + << "Use -f command line argument to force output\n"; + return 1; + } + Out = new std::ofstream(OutputFilename.c_str(), std::ios::out | + std::ios::trunc | std::ios::binary); + } else { // Specified stdout + // FIXME: cout is not binary! + Out = &std::cout; + } + } else { + if (InputFilename == "-") { + OutputFilename = "-"; + Out = &std::cout; + } else { + std::string IFN = InputFilename; + int Len = IFN.length(); + if (IFN[Len-3] == '.' && IFN[Len-2] == 'l' && IFN[Len-1] == 'l') { + // Source ends in .ll + OutputFilename = std::string(IFN.begin(), IFN.end()-3); + } else { + OutputFilename = IFN; // Append a .bc to it + } + OutputFilename += ".bc"; + + if (!Force && std::ifstream(OutputFilename.c_str())) { + // If force is not specified, make sure not to overwrite a file! + cerr << argv[0] << ": error opening '" << OutputFilename + << "': file exists!\n" + << "Use -f command line argument to force output\n"; + return 1; + } + + Out = new std::ofstream(OutputFilename.c_str(), std::ios::out | + std::ios::trunc | std::ios::binary); + // Make sure that the Out file gets unlinked from the disk if we get a + // SIGINT + sys::RemoveFileOnSignal(sys::Path(OutputFilename)); + } + } + + if (!Out->good()) { + cerr << argv[0] << ": error opening " << OutputFilename << "!\n"; + return 1; + } + + if (!DisableOutput) + if (Force || !CheckBitcodeOutputToConsole(Out,true)) + WriteBitcodeToFile(M.get(), *Out); + } catch (const std::string& msg) { + cerr << argv[0] << ": " << msg << "\n"; + exitCode = 1; + } catch (...) { + cerr << argv[0] << ": Unexpected unknown exception occurred.\n"; + exitCode = 1; + } + + if (Out != &std::cout) delete Out; + return exitCode; +} + diff --git a/tools/llvm-bcanalyzer/CMakeLists.txt b/tools/llvm-bcanalyzer/CMakeLists.txt new file mode 100644 index 0000000..732bc32 --- /dev/null +++ b/tools/llvm-bcanalyzer/CMakeLists.txt @@ -0,0 +1,6 @@ +set(LLVM_LINK_COMPONENTS bitreader) +set(LLVM_REQUIRES_EH 1) + +add_llvm_tool(llvm-bcanalyzer + llvm-bcanalyzer.cpp + ) diff --git a/tools/llvm-bcanalyzer/Makefile b/tools/llvm-bcanalyzer/Makefile new file mode 100644 index 0000000..251f758 --- /dev/null +++ b/tools/llvm-bcanalyzer/Makefile @@ -0,0 +1,18 @@ +##===- tools/llvm-bcanalyzer/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 = llvm-bcanalyzer +LINK_COMPONENTS := bitreader +REQUIRES_EH := 1 + +# This tool has no plugins, optimize startup time. +TOOL_NO_EXPORTS = 1 + +include $(LEVEL)/Makefile.common diff --git a/tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp b/tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp new file mode 100644 index 0000000..b401a21 --- /dev/null +++ b/tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp @@ -0,0 +1,600 @@ +//===-- llvm-bcanalyzer.cpp - Bitcode Analyzer --------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This tool may be invoked in the following manner: +// llvm-bcanalyzer [options] - Read LLVM bitcode from stdin +// llvm-bcanalyzer [options] x.bc - Read LLVM bitcode from the x.bc file +// +// Options: +// --help - Output information about command line switches +// --dump - Dump low-level bitcode structure in readable format +// +// This tool provides analytical information about a bitcode file. It is +// intended as an aid to developers of bitcode reading and writing software. It +// produces on std::out a summary of the bitcode file that shows various +// statistics about the contents of the file. By default this information is +// detailed and contains information about individual bitcode blocks and the +// functions in the module. +// The tool is also able to print a bitcode file in a straight forward text +// format that shows the containment and relationships of the information in +// the bitcode file (-dump option). +// +//===----------------------------------------------------------------------===// + +#include "llvm/Analysis/Verifier.h" +#include "llvm/Bitcode/BitstreamReader.h" +#include "llvm/Bitcode/LLVMBitCodes.h" +#include "llvm/Bitcode/ReaderWriter.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/PrettyStackTrace.h" +#include "llvm/System/Signals.h" +#include <map> +#include <fstream> +#include <iostream> +#include <algorithm> +using namespace llvm; + +static cl::opt<std::string> + InputFilename(cl::Positional, cl::desc("<input bitcode>"), cl::init("-")); + +static cl::opt<std::string> + OutputFilename("-o", cl::init("-"), cl::desc("<output file>")); + +static cl::opt<bool> Dump("dump", cl::desc("Dump low level bitcode trace")); + +//===----------------------------------------------------------------------===// +// Bitcode specific analysis. +//===----------------------------------------------------------------------===// + +static cl::opt<bool> NoHistogram("disable-histogram", + cl::desc("Do not print per-code histogram")); + +static cl::opt<bool> +NonSymbolic("non-symbolic", + cl::desc("Emit numberic info in dump even if" + " symbolic info is available")); + +/// CurStreamType - If we can sniff the flavor of this stream, we can produce +/// better dump info. +static enum { + UnknownBitstream, + LLVMIRBitstream +} CurStreamType; + + +/// GetBlockName - Return a symbolic block name if known, otherwise return +/// null. +static const char *GetBlockName(unsigned BlockID, + const BitstreamReader &StreamFile) { + // Standard blocks for all bitcode files. + if (BlockID < bitc::FIRST_APPLICATION_BLOCKID) { + if (BlockID == bitc::BLOCKINFO_BLOCK_ID) + return "BLOCKINFO_BLOCK"; + return 0; + } + + // Check to see if we have a blockinfo record for this block, with a name. + if (const BitstreamReader::BlockInfo *Info = + StreamFile.getBlockInfo(BlockID)) { + if (!Info->Name.empty()) + return Info->Name.c_str(); + } + + + if (CurStreamType != LLVMIRBitstream) return 0; + + switch (BlockID) { + default: return 0; + case bitc::MODULE_BLOCK_ID: return "MODULE_BLOCK"; + case bitc::PARAMATTR_BLOCK_ID: return "PARAMATTR_BLOCK"; + case bitc::TYPE_BLOCK_ID: return "TYPE_BLOCK"; + case bitc::CONSTANTS_BLOCK_ID: return "CONSTANTS_BLOCK"; + case bitc::FUNCTION_BLOCK_ID: return "FUNCTION_BLOCK"; + case bitc::TYPE_SYMTAB_BLOCK_ID: return "TYPE_SYMTAB"; + case bitc::VALUE_SYMTAB_BLOCK_ID: return "VALUE_SYMTAB"; + } +} + +/// GetCodeName - Return a symbolic code name if known, otherwise return +/// null. +static const char *GetCodeName(unsigned CodeID, unsigned BlockID, + const BitstreamReader &StreamFile) { + // Standard blocks for all bitcode files. + if (BlockID < bitc::FIRST_APPLICATION_BLOCKID) { + if (BlockID == bitc::BLOCKINFO_BLOCK_ID) { + switch (CodeID) { + default: return 0; + case bitc::BLOCKINFO_CODE_SETBID: return "SETBID"; + case bitc::BLOCKINFO_CODE_BLOCKNAME: return "BLOCKNAME"; + case bitc::BLOCKINFO_CODE_SETRECORDNAME: return "SETRECORDNAME"; + } + } + return 0; + } + + // Check to see if we have a blockinfo record for this record, with a name. + if (const BitstreamReader::BlockInfo *Info = + StreamFile.getBlockInfo(BlockID)) { + for (unsigned i = 0, e = Info->RecordNames.size(); i != e; ++i) + if (Info->RecordNames[i].first == CodeID) + return Info->RecordNames[i].second.c_str(); + } + + + if (CurStreamType != LLVMIRBitstream) return 0; + + switch (BlockID) { + default: return 0; + case bitc::MODULE_BLOCK_ID: + switch (CodeID) { + default: return 0; + case bitc::MODULE_CODE_VERSION: return "VERSION"; + case bitc::MODULE_CODE_TRIPLE: return "TRIPLE"; + case bitc::MODULE_CODE_DATALAYOUT: return "DATALAYOUT"; + case bitc::MODULE_CODE_ASM: return "ASM"; + case bitc::MODULE_CODE_SECTIONNAME: return "SECTIONNAME"; + case bitc::MODULE_CODE_DEPLIB: return "DEPLIB"; + case bitc::MODULE_CODE_GLOBALVAR: return "GLOBALVAR"; + case bitc::MODULE_CODE_FUNCTION: return "FUNCTION"; + case bitc::MODULE_CODE_ALIAS: return "ALIAS"; + case bitc::MODULE_CODE_PURGEVALS: return "PURGEVALS"; + case bitc::MODULE_CODE_GCNAME: return "GCNAME"; + } + case bitc::PARAMATTR_BLOCK_ID: + switch (CodeID) { + default: return 0; + case bitc::PARAMATTR_CODE_ENTRY: return "ENTRY"; + } + case bitc::TYPE_BLOCK_ID: + switch (CodeID) { + default: return 0; + case bitc::TYPE_CODE_NUMENTRY: return "NUMENTRY"; + case bitc::TYPE_CODE_VOID: return "VOID"; + case bitc::TYPE_CODE_FLOAT: return "FLOAT"; + case bitc::TYPE_CODE_DOUBLE: return "DOUBLE"; + case bitc::TYPE_CODE_LABEL: return "LABEL"; + case bitc::TYPE_CODE_OPAQUE: return "OPAQUE"; + case bitc::TYPE_CODE_INTEGER: return "INTEGER"; + case bitc::TYPE_CODE_POINTER: return "POINTER"; + case bitc::TYPE_CODE_FUNCTION: return "FUNCTION"; + case bitc::TYPE_CODE_STRUCT: return "STRUCT"; + case bitc::TYPE_CODE_ARRAY: return "ARRAY"; + case bitc::TYPE_CODE_VECTOR: return "VECTOR"; + case bitc::TYPE_CODE_X86_FP80: return "X86_FP80"; + case bitc::TYPE_CODE_FP128: return "FP128"; + case bitc::TYPE_CODE_PPC_FP128: return "PPC_FP128"; + case bitc::TYPE_CODE_METADATA: return "METADATA"; + } + + case bitc::CONSTANTS_BLOCK_ID: + switch (CodeID) { + default: return 0; + case bitc::CST_CODE_SETTYPE: return "SETTYPE"; + case bitc::CST_CODE_NULL: return "NULL"; + case bitc::CST_CODE_UNDEF: return "UNDEF"; + case bitc::CST_CODE_INTEGER: return "INTEGER"; + case bitc::CST_CODE_WIDE_INTEGER: return "WIDE_INTEGER"; + case bitc::CST_CODE_FLOAT: return "FLOAT"; + case bitc::CST_CODE_AGGREGATE: return "AGGREGATE"; + case bitc::CST_CODE_STRING: return "STRING"; + case bitc::CST_CODE_CSTRING: return "CSTRING"; + case bitc::CST_CODE_CE_BINOP: return "CE_BINOP"; + case bitc::CST_CODE_CE_CAST: return "CE_CAST"; + case bitc::CST_CODE_CE_GEP: return "CE_GEP"; + case bitc::CST_CODE_CE_SELECT: return "CE_SELECT"; + case bitc::CST_CODE_CE_EXTRACTELT: return "CE_EXTRACTELT"; + case bitc::CST_CODE_CE_INSERTELT: return "CE_INSERTELT"; + case bitc::CST_CODE_CE_SHUFFLEVEC: return "CE_SHUFFLEVEC"; + case bitc::CST_CODE_CE_CMP: return "CE_CMP"; + case bitc::CST_CODE_INLINEASM: return "INLINEASM"; + case bitc::CST_CODE_CE_SHUFVEC_EX: return "CE_SHUFVEC_EX"; + case bitc::CST_CODE_MDSTRING: return "MDSTRING"; + case bitc::CST_CODE_MDNODE: return "MDNODE"; + } + case bitc::FUNCTION_BLOCK_ID: + switch (CodeID) { + default: return 0; + case bitc::FUNC_CODE_DECLAREBLOCKS: return "DECLAREBLOCKS"; + + case bitc::FUNC_CODE_INST_BINOP: return "INST_BINOP"; + case bitc::FUNC_CODE_INST_CAST: return "INST_CAST"; + case bitc::FUNC_CODE_INST_GEP: return "INST_GEP"; + case bitc::FUNC_CODE_INST_SELECT: return "INST_SELECT"; + case bitc::FUNC_CODE_INST_EXTRACTELT: return "INST_EXTRACTELT"; + case bitc::FUNC_CODE_INST_INSERTELT: return "INST_INSERTELT"; + case bitc::FUNC_CODE_INST_SHUFFLEVEC: return "INST_SHUFFLEVEC"; + case bitc::FUNC_CODE_INST_CMP: return "INST_CMP"; + + case bitc::FUNC_CODE_INST_RET: return "INST_RET"; + case bitc::FUNC_CODE_INST_BR: return "INST_BR"; + case bitc::FUNC_CODE_INST_SWITCH: return "INST_SWITCH"; + case bitc::FUNC_CODE_INST_INVOKE: return "INST_INVOKE"; + case bitc::FUNC_CODE_INST_UNWIND: return "INST_UNWIND"; + case bitc::FUNC_CODE_INST_UNREACHABLE: return "INST_UNREACHABLE"; + + case bitc::FUNC_CODE_INST_PHI: return "INST_PHI"; + case bitc::FUNC_CODE_INST_MALLOC: return "INST_MALLOC"; + case bitc::FUNC_CODE_INST_FREE: return "INST_FREE"; + case bitc::FUNC_CODE_INST_ALLOCA: return "INST_ALLOCA"; + case bitc::FUNC_CODE_INST_LOAD: return "INST_LOAD"; + case bitc::FUNC_CODE_INST_STORE: return "INST_STORE"; + case bitc::FUNC_CODE_INST_CALL: return "INST_CALL"; + case bitc::FUNC_CODE_INST_VAARG: return "INST_VAARG"; + case bitc::FUNC_CODE_INST_STORE2: return "INST_STORE2"; + case bitc::FUNC_CODE_INST_GETRESULT: return "INST_GETRESULT"; + case bitc::FUNC_CODE_INST_EXTRACTVAL: return "INST_EXTRACTVAL"; + case bitc::FUNC_CODE_INST_INSERTVAL: return "INST_INSERTVAL"; + case bitc::FUNC_CODE_INST_CMP2: return "INST_CMP2"; + case bitc::FUNC_CODE_INST_VSELECT: return "INST_VSELECT"; + } + case bitc::TYPE_SYMTAB_BLOCK_ID: + switch (CodeID) { + default: return 0; + case bitc::TST_CODE_ENTRY: return "ENTRY"; + } + case bitc::VALUE_SYMTAB_BLOCK_ID: + switch (CodeID) { + default: return 0; + case bitc::VST_CODE_ENTRY: return "ENTRY"; + case bitc::VST_CODE_BBENTRY: return "BBENTRY"; + } + } +} + +struct PerRecordStats { + unsigned NumInstances; + unsigned NumAbbrev; + uint64_t TotalBits; + + PerRecordStats() : NumInstances(0), NumAbbrev(0), TotalBits(0) {} +}; + +struct PerBlockIDStats { + /// NumInstances - This the number of times this block ID has been seen. + unsigned NumInstances; + + /// NumBits - The total size in bits of all of these blocks. + uint64_t NumBits; + + /// NumSubBlocks - The total number of blocks these blocks contain. + unsigned NumSubBlocks; + + /// NumAbbrevs - The total number of abbreviations. + unsigned NumAbbrevs; + + /// NumRecords - The total number of records these blocks contain, and the + /// number that are abbreviated. + unsigned NumRecords, NumAbbreviatedRecords; + + /// CodeFreq - Keep track of the number of times we see each code. + std::vector<PerRecordStats> CodeFreq; + + PerBlockIDStats() + : NumInstances(0), NumBits(0), + NumSubBlocks(0), NumAbbrevs(0), NumRecords(0), NumAbbreviatedRecords(0) {} +}; + +static std::map<unsigned, PerBlockIDStats> BlockIDStats; + + + +/// Error - All bitcode analysis errors go through this function, making this a +/// good place to breakpoint if debugging. +static bool Error(const std::string &Err) { + std::cerr << Err << "\n"; + return true; +} + +/// ParseBlock - Read a block, updating statistics, etc. +static bool ParseBlock(BitstreamCursor &Stream, unsigned IndentLevel) { + std::string Indent(IndentLevel*2, ' '); + uint64_t BlockBitStart = Stream.GetCurrentBitNo(); + unsigned BlockID = Stream.ReadSubBlockID(); + + // Get the statistics for this BlockID. + PerBlockIDStats &BlockStats = BlockIDStats[BlockID]; + + BlockStats.NumInstances++; + + // BLOCKINFO is a special part of the stream. + if (BlockID == bitc::BLOCKINFO_BLOCK_ID) { + if (Dump) std::cerr << Indent << "<BLOCKINFO_BLOCK/>\n"; + if (Stream.ReadBlockInfoBlock()) + return Error("Malformed BlockInfoBlock"); + uint64_t BlockBitEnd = Stream.GetCurrentBitNo(); + BlockStats.NumBits += BlockBitEnd-BlockBitStart; + return false; + } + + unsigned NumWords = 0; + if (Stream.EnterSubBlock(BlockID, &NumWords)) + return Error("Malformed block record"); + + const char *BlockName = 0; + if (Dump) { + std::cerr << Indent << "<"; + if ((BlockName = GetBlockName(BlockID, *Stream.getBitStreamReader()))) + std::cerr << BlockName; + else + std::cerr << "UnknownBlock" << BlockID; + + if (NonSymbolic && BlockName) + std::cerr << " BlockID=" << BlockID; + + std::cerr << " NumWords=" << NumWords + << " BlockCodeSize=" << Stream.GetAbbrevIDWidth() << ">\n"; + } + + SmallVector<uint64_t, 64> Record; + + // Read all the records for this block. + while (1) { + if (Stream.AtEndOfStream()) + return Error("Premature end of bitstream"); + + uint64_t RecordStartBit = Stream.GetCurrentBitNo(); + + // Read the code for this record. + unsigned AbbrevID = Stream.ReadCode(); + switch (AbbrevID) { + case bitc::END_BLOCK: { + if (Stream.ReadBlockEnd()) + return Error("Error at end of block"); + uint64_t BlockBitEnd = Stream.GetCurrentBitNo(); + BlockStats.NumBits += BlockBitEnd-BlockBitStart; + if (Dump) { + std::cerr << Indent << "</"; + if (BlockName) + std::cerr << BlockName << ">\n"; + else + std::cerr << "UnknownBlock" << BlockID << ">\n"; + } + return false; + } + case bitc::ENTER_SUBBLOCK: { + uint64_t SubBlockBitStart = Stream.GetCurrentBitNo(); + if (ParseBlock(Stream, IndentLevel+1)) + return true; + ++BlockStats.NumSubBlocks; + uint64_t SubBlockBitEnd = Stream.GetCurrentBitNo(); + + // Don't include subblock sizes in the size of this block. + BlockBitStart += SubBlockBitEnd-SubBlockBitStart; + break; + } + case bitc::DEFINE_ABBREV: + Stream.ReadAbbrevRecord(); + ++BlockStats.NumAbbrevs; + break; + default: + Record.clear(); + + ++BlockStats.NumRecords; + if (AbbrevID != bitc::UNABBREV_RECORD) + ++BlockStats.NumAbbreviatedRecords; + + const char *BlobStart = 0; + unsigned BlobLen = 0; + unsigned Code = Stream.ReadRecord(AbbrevID, Record, BlobStart, BlobLen); + + + + // Increment the # occurrences of this code. + if (BlockStats.CodeFreq.size() <= Code) + BlockStats.CodeFreq.resize(Code+1); + BlockStats.CodeFreq[Code].NumInstances++; + BlockStats.CodeFreq[Code].TotalBits += + Stream.GetCurrentBitNo()-RecordStartBit; + if (AbbrevID != bitc::UNABBREV_RECORD) + BlockStats.CodeFreq[Code].NumAbbrev++; + + if (Dump) { + std::cerr << Indent << " <"; + if (const char *CodeName = + GetCodeName(Code, BlockID, *Stream.getBitStreamReader())) + std::cerr << CodeName; + else + std::cerr << "UnknownCode" << Code; + if (NonSymbolic && + GetCodeName(Code, BlockID, *Stream.getBitStreamReader())) + std::cerr << " codeid=" << Code; + if (AbbrevID != bitc::UNABBREV_RECORD) + std::cerr << " abbrevid=" << AbbrevID; + + for (unsigned i = 0, e = Record.size(); i != e; ++i) + std::cerr << " op" << i << "=" << (int64_t)Record[i]; + + std::cerr << "/>"; + + if (BlobStart) { + std::cerr << " blob data = "; + bool BlobIsPrintable = true; + for (unsigned i = 0; i != BlobLen; ++i) + if (!isprint(BlobStart[i])) { + BlobIsPrintable = false; + break; + } + + if (BlobIsPrintable) + std::cerr << "'" << std::string(BlobStart, BlobStart+BlobLen) <<"'"; + else + std::cerr << "unprintable, " << BlobLen << " bytes."; + } + + std::cerr << "\n"; + } + + break; + } + } +} + +static void PrintSize(double Bits) { + fprintf(stderr, "%.2f/%.2fB/%lluW", Bits, Bits/8,(unsigned long long)Bits/32); +} +static void PrintSize(uint64_t Bits) { + fprintf(stderr, "%llub/%.2fB/%lluW", (unsigned long long)Bits, + (double)Bits/8, (unsigned long long)Bits/32); +} + + +/// AnalyzeBitcode - Analyze the bitcode file specified by InputFilename. +static int AnalyzeBitcode() { + // Read the input file. + MemoryBuffer *MemBuf = MemoryBuffer::getFileOrSTDIN(InputFilename.c_str()); + + if (MemBuf == 0) + return Error("Error reading '" + InputFilename + "'."); + + if (MemBuf->getBufferSize() & 3) + return Error("Bitcode stream should be a multiple of 4 bytes in length"); + + unsigned char *BufPtr = (unsigned char *)MemBuf->getBufferStart(); + unsigned char *EndBufPtr = BufPtr+MemBuf->getBufferSize(); + + // If we have a wrapper header, parse it and ignore the non-bc file contents. + // The magic number is 0x0B17C0DE stored in little endian. + if (isBitcodeWrapper(BufPtr, EndBufPtr)) + if (SkipBitcodeWrapperHeader(BufPtr, EndBufPtr)) + return Error("Invalid bitcode wrapper header"); + + BitstreamReader StreamFile(BufPtr, EndBufPtr); + BitstreamCursor Stream(StreamFile); + StreamFile.CollectBlockInfoNames(); + + // Read the stream signature. + char Signature[6]; + Signature[0] = Stream.Read(8); + Signature[1] = Stream.Read(8); + Signature[2] = Stream.Read(4); + Signature[3] = Stream.Read(4); + Signature[4] = Stream.Read(4); + Signature[5] = Stream.Read(4); + + // Autodetect the file contents, if it is one we know. + CurStreamType = UnknownBitstream; + if (Signature[0] == 'B' && Signature[1] == 'C' && + Signature[2] == 0x0 && Signature[3] == 0xC && + Signature[4] == 0xE && Signature[5] == 0xD) + CurStreamType = LLVMIRBitstream; + + unsigned NumTopBlocks = 0; + + // Parse the top-level structure. We only allow blocks at the top-level. + while (!Stream.AtEndOfStream()) { + unsigned Code = Stream.ReadCode(); + if (Code != bitc::ENTER_SUBBLOCK) + return Error("Invalid record at top-level"); + + if (ParseBlock(Stream, 0)) + return true; + ++NumTopBlocks; + } + + if (Dump) std::cerr << "\n\n"; + + uint64_t BufferSizeBits = (EndBufPtr-BufPtr)*CHAR_BIT; + // Print a summary of the read file. + std::cerr << "Summary of " << InputFilename << ":\n"; + std::cerr << " Total size: "; + PrintSize(BufferSizeBits); + std::cerr << "\n"; + std::cerr << " Stream type: "; + switch (CurStreamType) { + default: assert(0 && "Unknown bitstream type"); + case UnknownBitstream: std::cerr << "unknown\n"; break; + case LLVMIRBitstream: std::cerr << "LLVM IR\n"; break; + } + std::cerr << " # Toplevel Blocks: " << NumTopBlocks << "\n"; + std::cerr << "\n"; + + // Emit per-block stats. + std::cerr << "Per-block Summary:\n"; + for (std::map<unsigned, PerBlockIDStats>::iterator I = BlockIDStats.begin(), + E = BlockIDStats.end(); I != E; ++I) { + std::cerr << " Block ID #" << I->first; + if (const char *BlockName = GetBlockName(I->first, StreamFile)) + std::cerr << " (" << BlockName << ")"; + std::cerr << ":\n"; + + const PerBlockIDStats &Stats = I->second; + std::cerr << " Num Instances: " << Stats.NumInstances << "\n"; + std::cerr << " Total Size: "; + PrintSize(Stats.NumBits); + std::cerr << "\n"; + std::cerr << " % of file: " + << Stats.NumBits/(double)BufferSizeBits*100 << "\n"; + if (Stats.NumInstances > 1) { + std::cerr << " Average Size: "; + PrintSize(Stats.NumBits/(double)Stats.NumInstances); + std::cerr << "\n"; + std::cerr << " Tot/Avg SubBlocks: " << Stats.NumSubBlocks << "/" + << Stats.NumSubBlocks/(double)Stats.NumInstances << "\n"; + std::cerr << " Tot/Avg Abbrevs: " << Stats.NumAbbrevs << "/" + << Stats.NumAbbrevs/(double)Stats.NumInstances << "\n"; + std::cerr << " Tot/Avg Records: " << Stats.NumRecords << "/" + << Stats.NumRecords/(double)Stats.NumInstances << "\n"; + } else { + std::cerr << " Num SubBlocks: " << Stats.NumSubBlocks << "\n"; + std::cerr << " Num Abbrevs: " << Stats.NumAbbrevs << "\n"; + std::cerr << " Num Records: " << Stats.NumRecords << "\n"; + } + if (Stats.NumRecords) + std::cerr << " % Abbrev Recs: " << (Stats.NumAbbreviatedRecords/ + (double)Stats.NumRecords)*100 << "\n"; + std::cerr << "\n"; + + // Print a histogram of the codes we see. + if (!NoHistogram && !Stats.CodeFreq.empty()) { + std::vector<std::pair<unsigned, unsigned> > FreqPairs; // <freq,code> + for (unsigned i = 0, e = Stats.CodeFreq.size(); i != e; ++i) + if (unsigned Freq = Stats.CodeFreq[i].NumInstances) + FreqPairs.push_back(std::make_pair(Freq, i)); + std::stable_sort(FreqPairs.begin(), FreqPairs.end()); + std::reverse(FreqPairs.begin(), FreqPairs.end()); + + std::cerr << "\tRecord Histogram:\n"; + fprintf(stderr, "\t\t Count # Bits %% Abv Record Kind\n"); + for (unsigned i = 0, e = FreqPairs.size(); i != e; ++i) { + const PerRecordStats &RecStats = Stats.CodeFreq[FreqPairs[i].second]; + + fprintf(stderr, "\t\t%7d %9llu ", RecStats.NumInstances, + (unsigned long long)RecStats.TotalBits); + + if (RecStats.NumAbbrev) + fprintf(stderr, "%7.2f ", + (double)RecStats.NumAbbrev/RecStats.NumInstances*100); + else + fprintf(stderr, " "); + + if (const char *CodeName = + GetCodeName(FreqPairs[i].second, I->first, StreamFile)) + fprintf(stderr, "%s\n", CodeName); + else + fprintf(stderr, "UnknownCode%d\n", FreqPairs[i].second); + } + std::cerr << "\n"; + + } + } + return 0; +} + + +int main(int argc, char **argv) { + // Print a stack trace if we signal out. + sys::PrintStackTraceOnErrorSignal(); + PrettyStackTraceProgram X(argc, argv); + llvm_shutdown_obj Y; // Call llvm_shutdown() on exit. + cl::ParseCommandLineOptions(argc, argv, "llvm-bcanalyzer file analyzer\n"); + + return AnalyzeBitcode(); +} diff --git a/tools/llvm-config/CMakeLists.txt b/tools/llvm-config/CMakeLists.txt new file mode 100644 index 0000000..29287ab --- /dev/null +++ b/tools/llvm-config/CMakeLists.txt @@ -0,0 +1,120 @@ +include(TestBigEndian) + +if( NOT PERL_FOUND ) + message(FATAL_ERROR "Perl required but not found!") +endif( NOT PERL_FOUND ) + +set(PERL ${PERL_EXECUTABLE}) +set(VERSION PACKAGE_VERSION) +set(PREFIX ${LLVM_BINARY_DIR}) # TODO: Root for `make install'. +execute_process(COMMAND date + OUTPUT_VARIABLE LLVM_CONFIGTIME + OUTPUT_STRIP_TRAILING_WHITESPACE) +# LLVM_ON_UNIX and LLVM_ON_WIN32 already set. +# those are set to blank by `autoconf' on MinGW, so it seems they are not required: +#set(LLVMGCCDIR "") +#set(LLVMGCC "") +#set(LLVMGXX "") +#set(LLVMGCC_VERSION "") +#set(LLVMGCC_MAJVERS "") +test_big_endian(IS_BIG_ENDIAN) +if( IS_BIG_ENDIAN ) + set(ENDIAN "big") +else( IS_BIG_ENDIAN ) + set(ENDIAN "little") +endif( IS_BIG_ENDIAN ) +set(SHLIBEXT ${LTDL_SHLIB_EXT}) +#EXEEXT already set. +set(OS "${CMAKE_SYSTEM}") +set(ARCH "X86") # TODO: This gives "i686" in Linux: "${CMAKE_SYSTEM_PROCESSOR}") + +get_system_libs(LLVM_SYSTEM_LIBS_LIST) +foreach(l ${LLVM_SYSTEM_LIBS_LIST}) + set(LLVM_SYSTEM_LIBS ${LLVM_SYSTEM_LIBS} "-l${l}") +endforeach() + +include(GetTargetTriple) +get_target_triple(target) + +foreach(c ${LLVM_TARGETS_TO_BUILD}) + set(TARGETS_BUILT "${TARGETS_BUILT} ${c}") +endforeach(c) +set(TARGETS_TO_BUILD ${TARGETS_BUILT}) +set(TARGET_HAS_JIT "1") # TODO + +# Avoids replacement at config-time: +set(LLVM_CPPFLAGS "@LLVM_CPPFLAGS@") +set(LLVM_CFLAGS "@LLVM_CFLAGS@") +set(LLVM_CXXFLAGS "@LLVM_CXXFLAGS@") +set(LLVM_LDFLAGS "@LLVM_LDFLAGS@") +set(LIBS "@LIBS@") +set(LLVM_BUILDMODE "@LLVM_BUILDMODE@") + +configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/llvm-config.in.in + ${CMAKE_CURRENT_BINARY_DIR}/llvm-config.in + @ONLY +) + +set(LIBDEPS ${CMAKE_CURRENT_BINARY_DIR}/LibDeps.txt) +set(LIBDEPS_TMP ${CMAKE_CURRENT_BINARY_DIR}/LibDeps.txt.tmp) +set(FINAL_LIBDEPS ${CMAKE_CURRENT_BINARY_DIR}/FinalLibDeps.txt) +set(LLVM_CONFIG ${LLVM_TOOLS_BINARY_DIR}/llvm-config) +set(LLVM_CONFIG_IN ${CMAKE_CURRENT_BINARY_DIR}/llvm-config.in) + +if( CMAKE_CROSSCOMPILING ) + set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM ONLY) +endif() + +find_program(NM_PATH nm PATH_SUFFIXES /bin) + +if( NOT NM_PATH ) + message(FATAL_ERROR "`nm' not found") +endif() + +add_custom_command(OUTPUT ${LIBDEPS_TMP} + COMMAND ${PERL_EXECUTABLE} ${LLVM_MAIN_SRC_DIR}/utils/GenLibDeps.pl -flat ${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}/${CMAKE_CFG_INTDIR} ${NM_PATH} > ${LIBDEPS_TMP} + DEPENDS ${llvm_libs} + COMMENT "Regenerating ${LIBDEPS_TMP}") + +add_custom_command(OUTPUT ${LIBDEPS} + COMMAND ${CMAKE_COMMAND} -E copy_if_different ${LIBDEPS_TMP} ${LIBDEPS} + DEPENDS ${LIBDEPS_TMP} + COMMENT "Updated ${LIBDEPS} because dependencies changed") + +add_custom_command(OUTPUT ${FINAL_LIBDEPS} + COMMAND ${PERL_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/find-cycles.pl < ${LIBDEPS} > ${FINAL_LIBDEPS} || ${CMAKE_COMMAND} -E remove -f ${FINAL_LIBDEPS} + DEPENDS ${LIBDEPS} + COMMENT "Checking for cyclic dependencies between LLVM libraries.") + +string(TOUPPER "${CMAKE_BUILD_TYPE}" uppercase_CMAKE_BUILD_TYPE) +set(C_FLGS "${CMAKE_C_FLAGS_${uppercase_CMAKE_BUILD_TYPE}} ${LLVM_DEFINITIONS}") +set(CXX_FLGS "${CMAKE_CXX_FLAGS_${uppercase_CMAKE_BUILD_TYPE}} ${LLVM_DEFINITIONS}") +set(CPP_FLGS "${CMAKE_CPP_FLAGS_${uppercase_CMAKE_BUILD_TYPE}} ${LLVM_DEFINITIONS}") + +add_custom_command(OUTPUT ${LLVM_CONFIG} + COMMAND echo 's!@LLVM_CPPFLAGS@!${CPP_FLGS}!' > temp.sed + COMMAND echo 's!@LLVM_CFLAGS@!${C_FLGS}!' >> temp.sed + COMMAND echo 's!@LLVM_CXXFLAGS@!${CXX_FLGS}!' >> temp.sed + # TODO: Use general flags for linking! not just for shared libs: + COMMAND echo 's!@LLVM_LDFLAGS@!${CMAKE_SHARED_LINKER_FLAGS}!' >> temp.sed + COMMAND echo 's!@LIBS@!${LLVM_SYSTEM_LIBS}!' >> temp.sed + COMMAND echo 's!@LLVM_BUILDMODE@!${CMAKE_BUILD_TYPE}!' >> temp.sed + COMMAND sed -f temp.sed < ${LLVM_CONFIG_IN} > ${LLVM_CONFIG} + COMMAND ${CMAKE_COMMAND} -E remove -f temp.sed + COMMAND cat ${FINAL_LIBDEPS} >> ${LLVM_CONFIG} + COMMAND chmod +x ${LLVM_CONFIG} + COMMAND cd ${CMAKE_BINARY_DIR} && ${CMAKE_COMMAND} -U HAVE_LLVM_CONFIG -D LLVM_BINARY_DIR="${LLVM_BINARY_DIR}" ${CMAKE_SOURCE_DIR} + DEPENDS ${FINAL_LIBDEPS} ${LLVM_CONFIG_IN} + COMMENT "Building llvm-config script." + ) + +add_custom_target(llvm-config.target ALL + DEPENDS ${LLVM_CONFIG}) + +add_dependencies(llvm-config.target ${llvm_lib_targets}) + +install(FILES ${LLVM_CONFIG} + PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE + WORLD_READ WORLD_EXECUTE + DESTINATION bin) diff --git a/tools/llvm-config/Makefile b/tools/llvm-config/Makefile new file mode 100644 index 0000000..6eedca0 --- /dev/null +++ b/tools/llvm-config/Makefile @@ -0,0 +1,92 @@ +##===- tools/llvm-config/Makefile --------------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LEVEL = ../.. + +EXTRA_DIST = LibDeps.txt FinalLibDeps.txt llvm-config.in.in find-cycles.pl +REQUIRES_EH := 1 + +include $(LEVEL)/Makefile.common + +# If we don't have Perl, we can't generate the library dependencies upon which +# llvm-config depends. Therefore, only if we detect perl will we do anything +# useful. +ifeq ($(HAVE_PERL),1) + +# Combine preprocessor flags (except for -I) and CXX flags. +SUB_CPPFLAGS = ${CPP.BaseFlags} +SUB_CFLAGS = ${CPP.BaseFlags} ${C.Flags} +SUB_CXXFLAGS = ${CPP.BaseFlags} ${CXX.Flags} + +# This is blank for now. We need to be careful about adding stuff here: +# LDFLAGS tend not to be portable, and we don't currently require the +# user to use libtool when linking against LLVM. +SUB_LDFLAGS = + +FinalLibDeps = $(PROJ_OBJ_DIR)/FinalLibDeps.txt +LibDeps = $(PROJ_OBJ_DIR)/LibDeps.txt +LibDepsTemp = $(PROJ_OBJ_DIR)/LibDeps.txt.tmp +GenLibDeps = $(PROJ_SRC_ROOT)/utils/GenLibDeps.pl + +$(LibDepsTemp): $(GenLibDeps) $(LibDir) $(wildcard $(LibDir)/*.a $(LibDir)/*.o) + $(Echo) "Regenerating LibDeps.txt.tmp" + $(Verb) $(PERL) $(GenLibDeps) -flat $(LibDir) "$(NM_PATH)" > $(LibDepsTemp) + +$(LibDeps): $(LibDepsTemp) + $(Verb) $(CMP) -s $@ $< || ( $(CP) $< $@ && \ + $(EchoCmd) Updated LibDeps.txt because dependencies changed ) + +# Find all the cyclic dependencies between various LLVM libraries, so we +# don't have to process them at runtime. +$(FinalLibDeps): find-cycles.pl $(LibDeps) + $(Echo) "Checking for cyclic dependencies between LLVM libraries." + $(Verb) $(PERL) $< < $(LibDeps) > $@ || rm -f $@ + +# Rerun our configure substitutions as needed. +ConfigInIn = $(PROJ_SRC_DIR)/llvm-config.in.in +llvm-config.in: $(ConfigInIn) $(ConfigStatusScript) + $(Verb) cd $(PROJ_OBJ_ROOT) ; \ + $(ConfigStatusScript) tools/llvm-config/llvm-config.in + +# Build our final script. +$(ToolDir)/llvm-config: llvm-config.in $(FinalLibDeps) + $(Echo) "Building llvm-config script." + $(Verb) $(ECHO) 's/@LLVM_CPPFLAGS@/$(subst /,\/,$(SUB_CPPFLAGS))/' \ + > temp.sed + $(Verb) $(ECHO) 's/@LLVM_CFLAGS@/$(subst /,\/,$(SUB_CFLAGS))/' \ + >> temp.sed + $(Verb) $(ECHO) 's/@LLVM_CXXFLAGS@/$(subst /,\/,$(SUB_CXXFLAGS))/' \ + >> temp.sed + $(Verb) $(ECHO) 's/@LLVM_LDFLAGS@/$(subst /,\/,$(SUB_LDFLAGS))/' \ + >> temp.sed + $(Verb) $(ECHO) 's/@LLVM_BUILDMODE@/$(subst /,\/,$(BuildMode))/' \ + >> temp.sed + $(Verb) $(SED) -f temp.sed < $< > $@ + $(Verb) $(RM) temp.sed + $(Verb) cat $(FinalLibDeps) >> $@ + $(Verb) chmod +x $@ + +else +# We don't have perl, just generate a dummy llvm-config +$(ToolDir)/llvm-config: + $(Echo) "Building place holder llvm-config script." + $(Verb) $(ECHO) 'echo llvm-config: Perl not found so llvm-config could not be generated' >> $@ + $(Verb) chmod +x $@ + +endif +# Hook into the standard Makefile rules. +all-local:: $(ToolDir)/llvm-config +clean-local:: + $(Verb) $(RM) -f $(ToolDir)/llvm-config llvm-config.in $(FinalLibDeps) \ + $(LibDeps) GenLibDeps.out +install-local:: all-local + $(Echo) Installing llvm-config + $(Verb) $(MKDIR) $(PROJ_bindir) + $(Verb) $(ScriptInstall) $(ToolDir)/llvm-config $(PROJ_bindir) + diff --git a/tools/llvm-config/find-cycles.pl b/tools/llvm-config/find-cycles.pl new file mode 100755 index 0000000..8156abd --- /dev/null +++ b/tools/llvm-config/find-cycles.pl @@ -0,0 +1,165 @@ +#!/usr/bin/perl +# +# Program: find-cycles.pl +# +# Synopsis: Given a list of possibly cyclic dependencies, merge all the +# cycles. This makes it possible to topologically sort the +# dependencies between different parts of LLVM. +# +# Syntax: find-cycles.pl < LibDeps.txt > FinalLibDeps.txt +# +# Input: cycmem1: cycmem2 dep1 dep2 +# cycmem2: cycmem1 dep3 dep4 +# boring: dep4 +# +# Output: cycmem1 cycmem2: dep1 dep2 dep3 dep4 +# boring: dep4 +# +# This file was written by Eric Kidd, and is placed into the public domain. +# + +use 5.006; +use strict; +use warnings; + +my %DEPS; +my @CYCLES; +sub find_all_cycles; + +# Read our dependency information. +while (<>) { + chomp; + my ($module, $dependency_str) = /^\s*([^:]+):\s*(.*)\s*$/; + die "Malformed data: $_" unless defined $dependency_str; + my @dependencies = split(/ /, $dependency_str); + $DEPS{$module} = \@dependencies; +} + +# Partition our raw dependencies into sets of cyclically-connected nodes. +find_all_cycles(); + +# Print out the finished cycles, with their dependencies. +my @output; +my $cycles_found = 0; +foreach my $cycle (@CYCLES) { + my @modules = sort keys %{$cycle}; + + # Merge the dependencies of all modules in this cycle. + my %dependencies; + foreach my $module (@modules) { + @dependencies{@{$DEPS{$module}}} = 1; + } + + # Prune the known cyclic dependencies. + foreach my $module (@modules) { + delete $dependencies{$module}; + } + + # Warn about possible linker problems. + my @archives = grep(/\.a$/, @modules); + if (@archives > 1) { + $cycles_found = $cycles_found + 1; + print STDERR "find-cycles.pl: Circular dependency between *.a files:\n"; + print STDERR "find-cycles.pl: ", join(' ', @archives), "\n"; + push @modules, @archives; # WORKAROUND: Duplicate *.a files. Ick. + } + + # Add to our output. (@modules is already as sorted as we need it to be.) + push @output, (join(' ', @modules) . ': ' . + join(' ', sort keys %dependencies) . "\n"); +} +print sort @output; + +exit $cycles_found; + +#========================================================================== +# Depedency Cycle Support +#========================================================================== +# For now, we have cycles in our dependency graph. Ideally, each cycle +# would be collapsed down to a single *.a file, saving us all this work. +# +# To understand this code, you'll need a working knowledge of Perl 5, +# and possibly some quality time with 'man perlref'. + +my %SEEN; +my %CYCLES; +sub find_cycles ($@); +sub found_cycles ($@); + +sub find_all_cycles { + # Find all multi-item cycles. + my @modules = sort keys %DEPS; + foreach my $module (@modules) { find_cycles($module); } + + # Build fake one-item "cycles" for the remaining modules, so we can + # treat them uniformly. + foreach my $module (@modules) { + unless (defined $CYCLES{$module}) { + my %cycle = ($module, 1); + $CYCLES{$module} = \%cycle; + } + } + + # Find all our unique cycles. We have to do this the hard way because + # we apparently can't store hash references as hash keys without making + # 'strict refs' sad. + my %seen; + foreach my $cycle (values %CYCLES) { + unless ($seen{$cycle}) { + $seen{$cycle} = 1; + push @CYCLES, $cycle; + } + } +} + +# Walk through our graph depth-first (keeping a trail in @path), and report +# any cycles we find. +sub find_cycles ($@) { + my ($module, @path) = @_; + if (str_in_list($module, @path)) { + found_cycle($module, @path); + } else { + return if defined $SEEN{$module}; + $SEEN{$module} = 1; + foreach my $dep (@{$DEPS{$module}}) { + find_cycles($dep, @path, $module); + } + } +} + +# Give a cycle, attempt to merge it with pre-existing cycle data. +sub found_cycle ($@) { + my ($module, @path) = @_; + + # Pop any modules which aren't part of our cycle. + while ($path[0] ne $module) { shift @path; } + #print join("->", @path, $module) . "\n"; + + # Collect the modules in our cycle into a hash. + my %cycle; + foreach my $item (@path) { + $cycle{$item} = 1; + if (defined $CYCLES{$item}) { + # Looks like we intersect with an existing cycle, so merge + # all those in, too. + foreach my $old_item (keys %{$CYCLES{$item}}) { + $cycle{$old_item} = 1; + } + } + } + + # Update our global cycle table. + my $cycle_ref = \%cycle; + foreach my $item (keys %cycle) { + $CYCLES{$item} = $cycle_ref; + } + #print join(":", sort keys %cycle) . "\n"; +} + +sub str_in_list ($@) { + my ($str, @list) = @_; + foreach my $item (@list) { + return 1 if ($item eq $str); + } + return 0; +} diff --git a/tools/llvm-config/llvm-config.in.in b/tools/llvm-config/llvm-config.in.in new file mode 100644 index 0000000..36b5112 --- /dev/null +++ b/tools/llvm-config/llvm-config.in.in @@ -0,0 +1,460 @@ +#!@PERL@ +##===- tools/llvm-config ---------------------------------------*- perl -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## +# +# Synopsis: Prints out compiler options needed to build against an installed +# copy of LLVM. +# +# Syntax: llvm-config OPTIONS... [COMPONENTS...] +# +##===----------------------------------------------------------------------===## + +use 5.006; +use strict; +use warnings; + +#---- begin autoconf values ---- +my $PACKAGE_NAME = q{@PACKAGE_NAME@}; +my $VERSION = q{@PACKAGE_VERSION@}; +my $PREFIX = q{@LLVM_PREFIX@}; +my $LLVM_CONFIGTIME = q{@LLVM_CONFIGTIME@}; +my $LLVM_SRC_ROOT = q{@abs_top_srcdir@}; +my $LLVM_OBJ_ROOT = q{@abs_top_builddir@}; +my $LLVM_ON_WIN32 = q{@LLVM_ON_WIN32@}; +my $LLVM_ON_UNIX = q{@LLVM_ON_UNIX@}; +my $LLVMGCCDIR = q{@LLVMGCCDIR@}; +my $LLVMGCC = q{@LLVMGCC@}; +my $LLVMGXX = q{@LLVMGXX@}; +my $LLVMGCC_VERSION = q{@LLVMGCC_VERSION@}; +my $LLVMGCC_MAJVERS = q{@LLVMGCC_MAJVERS@}; +my $ENDIAN = q{@ENDIAN@}; +my $SHLIBEXT = q{@SHLIBEXT@}; +my $EXEEXT = q{@EXEEXT@}; +my $OS = q{@OS@}; +my $ARCH = lc(q{@ARCH@}); +my $TARGET_TRIPLE = q{@target@}; +my $TARGETS_TO_BUILD = q{@TARGETS_TO_BUILD@}; +my $TARGET_HAS_JIT = q{@TARGET_HAS_JIT@}; +my @TARGETS_BUILT = map { lc($_) } qw{@TARGETS_TO_BUILD@}; +#---- end autoconf values ---- + +# Must pretend x86_64 architecture is really x86, otherwise the native backend +# won't get linked in. +$ARCH = "x86" if $ARCH eq "x86_64"; + +#---- begin Makefile values ---- +my $CPPFLAGS = q{@LLVM_CPPFLAGS@}; +my $CFLAGS = q{@LLVM_CFLAGS@}; +my $CXXFLAGS = q{@LLVM_CXXFLAGS@}; +my $LDFLAGS = q{@LLVM_LDFLAGS@}; +my $SYSTEM_LIBS = q{@LIBS@}; +my $LLVM_BUILDMODE = q{@LLVM_BUILDMODE@}; +#---- end Makefile values ---- + +# Figure out where llvm-config is being run from. Primarily, we care if it has +# been installed, or is running from the build directory, which changes the +# locations of some files. + +# Convert the current executable name into its directory (e.g. "."). +my ($RUN_DIR) = ($0 =~ /^(.*)\/.*$/); + +# Find the unix pwd program: we don't want to use the bash builtin, as it does +# not look through symlinks etc. +my $PWD = `which pwd`; +chomp($PWD); +$PWD = "pwd" if (!-e $PWD); + +# Turn the directory into an absolute directory on the file system, also pop up +# from "bin" into the build or prefix dir. +my $ABS_RUN_DIR = `cd $RUN_DIR/..; $PWD`; +chomp($ABS_RUN_DIR); + +# Compute the absolute object directory build, e.g. "foo/llvm/Debug". +my $ABS_OBJ_ROOT = "$LLVM_OBJ_ROOT/$LLVM_BUILDMODE"; +$ABS_OBJ_ROOT = `cd $ABS_OBJ_ROOT; $PWD` if (-d $ABS_OBJ_ROOT); +chomp($ABS_OBJ_ROOT); + +my $INCLUDEDIR = "$ABS_RUN_DIR/include"; +my $LIBDIR = "$ABS_RUN_DIR/lib"; +my $BINDIR = "$ABS_RUN_DIR/bin"; +if ($ABS_RUN_DIR eq $ABS_OBJ_ROOT) { + # If we are running out of the build directory, the include dir is in the + # srcdir. + $INCLUDEDIR = "$LLVM_SRC_ROOT/include"; +} else { + # If installed, ignore the prefix the tree was configured with, use the + # current prefix. + $PREFIX = $ABS_RUN_DIR; +} + +sub usage; +sub fix_library_names (@); +sub fix_library_files (@); +sub expand_dependencies (@); +sub name_map_entries; + +# Parse our command-line arguments. +usage if @ARGV == 0; +my @components; +my $has_opt = 0; +my $want_libs = 0; +my $want_libnames = 0; +my $want_libfiles = 0; +my $want_components = 0; +foreach my $arg (@ARGV) { + if ($arg =~ /^-/) { + if ($arg eq "--version") { + $has_opt = 1; print "$VERSION\n"; + } elsif ($arg eq "--prefix") { + $has_opt = 1; print "$PREFIX\n"; + } elsif ($arg eq "--bindir") { + $has_opt = 1; print "$BINDIR\n"; + } elsif ($arg eq "--includedir") { + $has_opt = 1; print "$INCLUDEDIR\n"; + } elsif ($arg eq "--libdir") { + $has_opt = 1; print "$LIBDIR\n"; + } elsif ($arg eq "--cppflags") { + $has_opt = 1; print "-I$INCLUDEDIR $CPPFLAGS\n"; + } elsif ($arg eq "--cflags") { + $has_opt = 1; print "-I$INCLUDEDIR $CFLAGS\n"; + } elsif ($arg eq "--cxxflags") { + $has_opt = 1; print "-I$INCLUDEDIR $CXXFLAGS\n"; + } elsif ($arg eq "--ldflags") { + $has_opt = 1; print "-L$LIBDIR $LDFLAGS $SYSTEM_LIBS\n"; + } elsif ($arg eq "--libs") { + $has_opt = 1; $want_libs = 1; + } elsif ($arg eq "--libnames") { + $has_opt = 1; $want_libnames = 1; + } elsif ($arg eq "--libfiles") { + $has_opt = 1; $want_libfiles = 1; + } elsif ($arg eq "--components") { + $has_opt = 1; print join(' ', name_map_entries), "\n"; + } elsif ($arg eq "--targets-built") { + $has_opt = 1; print join(' ', @TARGETS_BUILT), "\n"; + } elsif ($arg eq "--host-target") { + $has_opt = 1; print "$TARGET_TRIPLE\n"; + } elsif ($arg eq "--build-mode") { + $has_opt = 1; print "$LLVM_BUILDMODE\n"; + } elsif ($arg eq "--obj-root") { + $has_opt = 1; print `cd $LLVM_OBJ_ROOT/; $PWD`; + } elsif ($arg eq "--src-root") { + $has_opt = 1; print `cd $LLVM_SRC_ROOT/; $PWD`; + } else { + usage(); + } + } else { + push @components, $arg; + } +} + +# If no options were specified, fail. +usage unless $has_opt; + +# If no components were specified, default to 'all'. +if (@components == 0) { + push @components, 'all'; +} + +# Force component names to lower case. +@components = map lc, @components; + +# Handle any arguments which require building our dependency graph. +if ($want_libs || $want_libnames || $want_libfiles) { + my @libs = expand_dependencies(@components); + print join(' ', fix_library_names(@libs)), "\n" if ($want_libs); + print join(' ', @libs), "\n" if ($want_libnames); + print join(' ', fix_library_files(@libs)), "\n" if ($want_libfiles); +} + +exit 0; + +#========================================================================== +# Support Routines +#========================================================================== + +sub usage { + print STDERR <<__EOD__; +Usage: llvm-config <OPTION>... [<COMPONENT>...] + +Get various configuration information needed to compile programs which use +LLVM. Typically called from 'configure' scripts. Examples: + llvm-config --cxxflags + llvm-config --ldflags + llvm-config --libs engine bcreader scalaropts + +Options: + --version Print LLVM version. + --prefix Print the installation prefix. + --src-root Print the source root LLVM was built from. + --obj-root Print the object root used to build LLVM. + --bindir Directory containing LLVM executables. + --includedir Directory containing LLVM headers. + --libdir Directory containing LLVM libraries. + --cppflags C preprocessor flags for files that include LLVM headers. + --cflags C compiler flags for files that include LLVM headers. + --cxxflags C++ compiler flags for files that include LLVM headers. + --ldflags Print Linker flags. + --libs Libraries needed to link against LLVM components. + --libnames Bare library names for in-tree builds. + --libfiles Fully qualified library filenames for makefile depends. + --components List of all possible components. + --targets-built List of all targets currently built. + --host-target Target triple used to configure LLVM. + --build-mode Print build mode of LLVM tree (e.g. Debug or Release). +Typical components: + all All LLVM libraries (default). + backend Either a native backend or the C backend. + engine Either a native JIT or a bytecode interpreter. +__EOD__ + exit(1); +} + +# Use -lfoo instead of libfoo.a whenever possible, and add directories to +# files which can't be found using -L. +sub fix_library_names (@) { + my @libs = @_; + my @result; + foreach my $lib (@libs) { + # Transform the bare library name appropriately. + my ($basename) = ($lib =~ /^lib([^.]*)\.a/); + if (defined $basename) { + push @result, "-l$basename"; + } else { + push @result, "$LIBDIR/$lib"; + } + } + return @result; +} + +# Turn the list of libraries into a list of files. +sub fix_library_files(@) { + my @libs = @_; + my @result; + foreach my $lib (@libs) { + # Transform the bare library name into a filename. + push @result, "$LIBDIR/$lib"; + } + return @result; +} + +#========================================================================== +# Library Dependency Analysis +#========================================================================== +# Given a few human-readable library names, find all their dependencies +# and sort them into an order which the linker will like. If we packed +# our libraries into fewer archives, we could make the linker do much +# of this work for us. +# +# Libraries have two different types of names in this code: Human-friendly +# "component" names entered on the command-line, and the raw file names +# we use internally (and ultimately pass to the linker). +# +# To understand this code, you'll need a working knowledge of Perl 5, +# and possibly some quality time with 'man perlref'. + +sub load_dependencies; +sub build_name_map; +sub have_native_backend; +sub find_best_engine; +sub expand_names (@); +sub find_all_required_sets (@); +sub find_all_required_sets_helper ($$@); + +# Each "set" contains one or more libraries which must be included as a +# group (due to cyclic dependencies). Sets are represented as a Perl array +# reference pointing to a list of internal library names. +my @SETS; + +# Various mapping tables. +my %LIB_TO_SET_MAP; # Maps internal library names to their sets. +my %SET_DEPS; # Maps sets to a list of libraries they depend on. +my %NAME_MAP; # Maps human-entered names to internal names. + +# Have our dependencies been loaded yet? +my $DEPENDENCIES_LOADED = 0; + +# Given a list of human-friendly component names, translate them into a +# complete set of linker arguments. +sub expand_dependencies (@) { + my @libs = @_; + load_dependencies; + my @required_sets = find_all_required_sets(expand_names(@libs)); + my @sorted_sets = topologically_sort_sets(@required_sets); + + # Expand the library sets into libraries. + my @result; + foreach my $set (@sorted_sets) { push @result, @{$set}; } + return @result; +} + +# Load in the raw dependency data stored at the end of this file. +sub load_dependencies { + return if $DEPENDENCIES_LOADED; + $DEPENDENCIES_LOADED = 1; + while (<DATA>) { + # Parse our line. + my ($libs, $deps) = /^\s*([^:]+):\s*(.*)\s*$/; + die "Malformed dependency data" unless defined $deps; + my @libs = split(' ', $libs); + my @deps = split(' ', $deps); + + # Record our dependency data. + my $set = \@libs; + push @SETS, $set; + foreach my $lib (@libs) { $LIB_TO_SET_MAP{$lib} = $set; } + $SET_DEPS{$set} = \@deps; + } + build_name_map; +} + +# Build a map converting human-friendly component names into internal +# library names. +sub build_name_map { + # Add entries for all the actual libraries. + foreach my $set (@SETS) { + foreach my $lib (sort @$set) { + my $short_name = $lib; + $short_name =~ s/^(lib)?LLVM([^.]*)\..*$/$2/; + $short_name =~ tr/A-Z/a-z/; + $NAME_MAP{$short_name} = [$lib]; + } + } + + # Add target-specific entries + foreach my $target (@TARGETS_BUILT) { + # FIXME: Temporary, until we don't switch all targets + if (defined $NAME_MAP{$target.'asmprinter'}) { + $NAME_MAP{$target} = [$target.'asmprinter', $target.'codegen'] + } + } + + # Add virtual entries. + $NAME_MAP{'native'} = have_native_backend() ? [$ARCH] : []; + $NAME_MAP{'nativecodegen'} = have_native_backend() ? [$ARCH.'codegen'] : []; + $NAME_MAP{'backend'} = have_native_backend() ? ['native'] : ['cbackend']; + $NAME_MAP{'engine'} = find_best_engine; + $NAME_MAP{'all'} = [name_map_entries]; # Must be last. +} + +# Return true if we have a native backend to use. +sub have_native_backend { + my %BUILT; + foreach my $target (@TARGETS_BUILT) { $BUILT{$target} = 1; } + return defined $NAME_MAP{$ARCH} && defined $BUILT{$ARCH}; +} + +# Find a working subclass of ExecutionEngine for this platform. +sub find_best_engine { + if (have_native_backend && $TARGET_HAS_JIT) { + return ['jit', 'native']; + } else { + return ['interpreter']; + } +} + +# Get all the human-friendly component names. +sub name_map_entries { + load_dependencies; + return sort keys %NAME_MAP; +} + +# Map human-readable names to internal library names. +sub expand_names (@) { + my @names = @_; + my @result; + foreach my $name (@names) { + if (defined $LIB_TO_SET_MAP{$name}) { + # We've hit bottom: An actual library name. + push @result, $name; + } elsif (defined $NAME_MAP{$name}) { + # We've found a short name to expand. + push @result, expand_names(@{$NAME_MAP{$name}}); + } else { + print STDERR "llvm-config: unknown component name: $name\n"; + exit(1); + } + } + return @result; +} + +# Given a list of internal library names, return all sets of libraries which +# will need to be included by the linker (in no particular order). +sub find_all_required_sets (@) { + my @libs = @_; + my %sets_added; + my @result; + find_all_required_sets_helper(\%sets_added, \@result, @libs); + return @result; +} + +# Recursive closures are pretty broken in Perl, so we're going to separate +# this function from find_all_required_sets and pass in the state we need +# manually, as references. Yes, this is fairly unpleasant. +sub find_all_required_sets_helper ($$@) { + my ($sets_added, $result, @libs) = @_; + foreach my $lib (@libs) { + my $set = $LIB_TO_SET_MAP{$lib}; + next if defined $$sets_added{$set}; + $$sets_added{$set} = 1; + push @$result, $set; + find_all_required_sets_helper($sets_added, $result, @{$SET_DEPS{$set}}); + } +} + +# Print a list of sets, with a label. Used for debugging. +sub print_sets ($@) { + my ($label, @sets) = @_; + my @output; + foreach my $set (@sets) { push @output, join(',', @$set); } + print "$label: ", join(';', @output), "\n"; +} + +# Returns true if $lib is a key in $added. +sub has_lib_been_added ($$) { + my ($added, $lib) = @_; + return defined $$added{$LIB_TO_SET_MAP{$lib}}; +} + +# Returns true if all the dependencies of $set appear in $added. +sub have_all_deps_been_added ($$) { + my ($added, $set) = @_; + #print_sets(" Checking", $set); + #print_sets(" Wants", $SET_DEPS{$set}); + foreach my $lib (@{$SET_DEPS{$set}}) { + return 0 unless has_lib_been_added($added, $lib); + } + return 1; +} + +# Given a list of sets, topologically sort them using dependencies. +sub topologically_sort_sets (@) { + my @sets = @_; + my %added; + my @result; + SCAN: while (@sets) { # We'll delete items from @sets as we go. + #print_sets("So far", reverse(@result)); + #print_sets("Remaining", @sets); + for (my $i = 0; $i < @sets; ++$i) { + my $set = $sets[$i]; + if (have_all_deps_been_added(\%added, $set)) { + push @result, $set; + $added{$set} = 1; + #print "Removing $i.\n"; + splice(@sets, $i, 1); + next SCAN; # Restart our scan. + } + } + die "Can't find a library with no dependencies"; + } + return reverse(@result); +} + +# Our library dependency data will be added after the '__END__' token, and will +# be read through the magic <DATA> filehandle. +__END__ diff --git a/tools/llvm-db/CLICommand.h b/tools/llvm-db/CLICommand.h new file mode 100644 index 0000000..0beec91 --- /dev/null +++ b/tools/llvm-db/CLICommand.h @@ -0,0 +1,111 @@ +//===- CLICommand.h - Classes used to represent commands --------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines a small class hierarchy used to represent the various types +// of commands in the CLI debugger front-end. +// +//===----------------------------------------------------------------------===// + +#ifndef CLICOMMAND_H +#define CLICOMMAND_H + +#include <string> +#include <vector> +#include <cassert> + +namespace llvm { + class CLIDebugger; + + /// CLICommand - Base class of the hierarchy, used to provide the abstract + /// interface common to all commands. + /// + class CLICommand { + /// ShortHelp, LongHelp - The short and long helps strings printed for the + /// command. The ShortHelp string should be a single line of text without a + /// newline. The LongHelp string should be a full description with + /// terminating newline. + std::string ShortHelp, LongHelp; + + /// RefCount - This contains the number of entries in the CLIDebugger + /// CommandTable that points to this command. + unsigned RefCount; + + /// OptionNames - This contains a list of names for the option. Keeping + /// track of this is done just to make the help output more helpful. + /// + std::vector<std::string> OptionNames; + public: + CLICommand(const std::string &SH, const std::string &LH) + : ShortHelp(SH), LongHelp(LH), RefCount(0) {} + + virtual ~CLICommand() {} + + /// addRef/dropRef - Implement a simple reference counting scheme to make + /// sure we delete commands that are no longer used. + void addRef() { ++RefCount; } + void dropRef() { + if (--RefCount == 0) delete this; + } + + /// getPrimaryOptionName - Return the first name the option was added under. + /// This is the name we report for the option in the help output. + std::string getPrimaryOptionName() const { + return OptionNames.empty() ? "" : OptionNames[0]; + } + + /// getOptionName - Return all of the names the option is registered as. + /// + const std::vector<std::string> &getOptionNames() const { + return OptionNames; + } + + /// addOptionName - Add a name that this option is known as. + /// + void addOptionName(const std::string &Name) { + OptionNames.push_back(Name); + } + + /// removeOptionName - Eliminate one of the names for this option. + /// + void removeOptionName(const std::string &Name) { + unsigned i = 0; + for (; OptionNames[i] != Name; ++i) + assert(i+1 < OptionNames.size() && "Didn't find option name!"); + OptionNames.erase(OptionNames.begin()+i); + } + + + /// getShortHelp - Return the short help string for this command. + /// + const std::string &getShortHelp() { return ShortHelp; } + + /// getLongHelp - Return the long help string for this command, if it + /// exists. + const std::string &getLongHelp() { return LongHelp; } + + virtual void runCommand(CLIDebugger &D, std::string &Arguments) = 0; + }; + + /// BuiltinCLICommand - This class represents commands that are built directly + /// into the debugger. + class BuiltinCLICommand : public CLICommand { + // Impl - Pointer to the method that implements the command + void (CLIDebugger::*Impl)(std::string&); + public: + BuiltinCLICommand(const std::string &ShortHelp, const std::string &LongHelp, + void (CLIDebugger::*impl)(std::string&)) + : CLICommand(ShortHelp, LongHelp), Impl(impl) {} + + void runCommand(CLIDebugger &D, std::string &Arguments) { + (D.*Impl)(Arguments); + } + }; +} + +#endif diff --git a/tools/llvm-db/CLIDebugger.cpp b/tools/llvm-db/CLIDebugger.cpp new file mode 100644 index 0000000..1d2a838 --- /dev/null +++ b/tools/llvm-db/CLIDebugger.cpp @@ -0,0 +1,308 @@ +//===-- CLIDebugger.cpp - Command Line Interface to the Debugger ----------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains the main implementation of the Command Line Interface to +// the debugger. +// +//===----------------------------------------------------------------------===// + +#include "CLIDebugger.h" +#include "CLICommand.h" +#include "llvm/Debugger/SourceFile.h" +#include "llvm/ADT/StringExtras.h" +#include <iostream> +using namespace llvm; + +/// CLIDebugger constructor - This initializes the debugger to its default +/// state, and initializes the command table. +/// +CLIDebugger::CLIDebugger() + : TheProgramInfo(0), TheRuntimeInfo(0), Prompt("(llvm-db) "), ListSize(10) { + // Initialize instance variables + CurrentFile = 0; + LineListedStart = 1; + LineListedEnd = 1; + LastCurrentFrame = 0; + CurrentLanguage = 0; + + CLICommand *C; + //===--------------------------------------------------------------------===// + // Program startup and shutdown options + // + addCommand("file", new BuiltinCLICommand( + "Use specified file as the program to be debugged", + "The debugger looks in the current directory and the program $PATH for the" + " specified LLVM program. It then unloads the currently loaded program and" + " loads the specified program.\n", + &CLIDebugger::fileCommand)); + + addCommand("create", new BuiltinCLICommand( + "Start the program, halting its execution in main", + "This command creates an instance of the current program, but stops" + "\nexecution immediately.\n", + &CLIDebugger::createCommand)); + + addCommand("kill", new BuiltinCLICommand( + "Kills the execution of the current program being debugged", "", + &CLIDebugger::killCommand)); + + addCommand("quit", new BuiltinCLICommand( + "Exit the debugger", "", + &CLIDebugger::quitCommand)); + + //===--------------------------------------------------------------------===// + // Program execution commands + // + addCommand("run", C = new BuiltinCLICommand( + "Start the program running from the beginning", "", + &CLIDebugger::runCommand)); + addCommand("r", C); + + addCommand("cont", C = new BuiltinCLICommand( + "Continue program being debugged until the next stop point", "", + &CLIDebugger::contCommand)); + addCommand("c", C); addCommand("fg", C); + + addCommand("step", C = new BuiltinCLICommand( + "Step program until it reaches a new source line", "", + &CLIDebugger::stepCommand)); + addCommand("s", C); + + addCommand("next", C = new BuiltinCLICommand( + "Step program until it reaches a new source line, stepping over calls", "", + &CLIDebugger::nextCommand)); + addCommand("n", C); + + addCommand("finish", new BuiltinCLICommand( + "Execute until the selected stack frame returns", + "Upon return, the value returned is printed and put in the value history.\n", + &CLIDebugger::finishCommand)); + + //===--------------------------------------------------------------------===// + // Stack frame commands + // + addCommand("backtrace", C = new BuiltinCLICommand( + "Print backtrace of all stack frames, or innermost COUNT frames", + "FIXME: describe. Takes 'n', '-n' or 'full'\n", + &CLIDebugger::backtraceCommand)); + addCommand("bt", C); + + addCommand("up", new BuiltinCLICommand( + "Select and print stack frame that called this one", + "An argument says how many frames up to go.\n", + &CLIDebugger::upCommand)); + + addCommand("down", new BuiltinCLICommand( + "Select and print stack frame called by this one", + "An argument says how many frames down go.\n", + &CLIDebugger::downCommand)); + + addCommand("frame", C = new BuiltinCLICommand( + "Select and print a stack frame", + "With no argument, print the selected stack frame. (See also 'info frame').\n" + "An argument specifies the frame to select.\n", + &CLIDebugger::frameCommand)); + addCommand("f", C); + + //===--------------------------------------------------------------------===// + // Breakpoint related commands + // + addCommand("break", C = new BuiltinCLICommand( + "Set breakpoint at specified line or function", + "FIXME: describe.\n", + &CLIDebugger::breakCommand)); + addCommand("b", C); + + + //===--------------------------------------------------------------------===// + // Miscellaneous commands + // + addCommand("info", new BuiltinCLICommand( + "Generic command for showing things about the program being debugged", + "info functions: display information about functions in the program.\ninfo" + " source : display information about the current source file.\ninfo source" + "s : Display source file names for the program\ninfo target : print status" + " of inferior process\n", + &CLIDebugger::infoCommand)); + + addCommand("list", C = new BuiltinCLICommand( + "List specified function or line", + "FIXME: document\n", + &CLIDebugger::listCommand)); + addCommand("l", C); + + addCommand("set", new BuiltinCLICommand( + "Change program or debugger variable", + "FIXME: document\n", + &CLIDebugger::setCommand)); + + addCommand("show", new BuiltinCLICommand( + "Generic command for showing things about the debugger", + "FIXME: document\n", + &CLIDebugger::showCommand)); + + addCommand("help", C = new BuiltinCLICommand( + "Prints information about available commands", "", + &CLIDebugger::helpCommand)); + addCommand("h", C); +} + + +/// addCommand - Add a command to the CommandTable, potentially displacing a +/// preexisting command. +void CLIDebugger::addCommand(const std::string &Option, CLICommand *Cmd) { + assert(Cmd && "Cannot set a null command!"); + CLICommand *&CS = CommandTable[Option]; + if (CS == Cmd) return; // noop + + // If we already have a command, decrement the command's reference count. + if (CS) { + CS->removeOptionName(Option); + CS->dropRef(); + } + CS = Cmd; + + // Remember that we are using this command. + Cmd->addRef(); + Cmd->addOptionName(Option); +} + +static bool isValidPrefix(const std::string &Prefix, const std::string &Option){ + return Prefix.size() <= Option.size() && + Prefix == std::string(Option.begin(), Option.begin()+Prefix.size()); +} + +/// getCommand - This looks up the specified command using a fuzzy match. +/// If the string exactly matches a command or is an unambiguous prefix of a +/// command, it returns the command. Otherwise it throws an exception +/// indicating the possible ambiguous choices. +CLICommand *CLIDebugger::getCommand(const std::string &Command) { + + // Look up the command in the table. + std::map<std::string, CLICommand*>::iterator CI = + CommandTable.lower_bound(Command); + + if (Command == "") { + throw "Null command should not get here!"; + } else if (CI == CommandTable.end() || + !isValidPrefix(Command, CI->first)) { + // If this command has no relation to anything in the command table, + // print the error message. + throw "Unknown command: '" + Command + + "'. Use 'help' for list of commands."; + } else if (CI->first == Command) { + // We have an exact match on the command + return CI->second; + } else { + // Otherwise, we have a prefix match. Check to see if this is + // unambiguous, and if so, run it. + std::map<std::string, CLICommand*>::iterator CI2 = CI; + + // If the next command is a valid completion of this one, we are + // ambiguous. + if (++CI2 != CommandTable.end() && isValidPrefix(Command, CI2->first)) { + std::string ErrorMsg = + "Ambiguous command '" + Command + "'. Options: " + CI->first; + for (++CI; CI != CommandTable.end() && + isValidPrefix(Command, CI->first); ++CI) + ErrorMsg += ", " + CI->first; + throw ErrorMsg; + } else { + // It's an unambiguous prefix of a command, use it. + return CI->second; + } + } +} + + +/// run - Start the debugger, returning when the user exits the debugger. This +/// starts the main event loop of the CLI debugger. +/// +int CLIDebugger::run() { + std::string Command; + std::cout << Prompt; + + // Keep track of the last command issued, so that we can reissue it if the + // user hits enter as the command. + CLICommand *LastCommand = 0; + std::string LastArgs; + + // Continue reading commands until the end of file. + while (getline(std::cin, Command)) { + std::string Arguments = Command; + + // Split off the command from the arguments to the command. + Command = getToken(Arguments, " \t\n\v\f\r\\/;.*&"); + + try { + CLICommand *CurCommand; + + if (Command == "") { + CurCommand = LastCommand; + Arguments = LastArgs; + } else { + CurCommand = getCommand(Command); + } + + // Save the command we are running in case the user wants us to repeat it + // next time. + LastCommand = CurCommand; + LastArgs = Arguments; + + // Finally, execute the command. + if (CurCommand) + CurCommand->runCommand(*this, Arguments); + + } catch (int RetVal) { + // The quit command exits the command loop by throwing an integer return + // code. + return RetVal; + } catch (const std::string &Error) { + std::cout << "Error: " << Error << "\n"; + } catch (const char *Error) { + std::cout << "Error: " << Error << "\n"; + } catch (const NonErrorException &E) { + std::cout << E.getMessage() << "\n"; + } catch (...) { + std::cout << "ERROR: Debugger caught unexpected exception!\n"; + // Attempt to continue. + } + + // Write the prompt to get the next bit of user input + std::cout << Prompt; + } + + return 0; +} + + +/// askYesNo - Ask the user a question, and demand a yes/no response. If +/// the user says yes, return true. +/// +bool CLIDebugger::askYesNo(const std::string &Message) const { + std::string Answer; + std::cout << Message << " (y or n) " << std::flush; + while (getline(std::cin, Answer)) { + std::string Val = getToken(Answer); + if (getToken(Answer).empty()) { + if (Val == "yes" || Val == "y" || Val == "YES" || Val == "Y" || + Val == "Yes") + return true; + if (Val == "no" || Val == "n" || Val == "NO" || Val == "N" || + Val == "No") + return false; + } + + std::cout << "Please answer y or n.\n" << Message << " (y or n) " + << std::flush; + } + + // Ran out of input? + return false; +} diff --git a/tools/llvm-db/CLIDebugger.h b/tools/llvm-db/CLIDebugger.h new file mode 100644 index 0000000..56ea14d --- /dev/null +++ b/tools/llvm-db/CLIDebugger.h @@ -0,0 +1,205 @@ +//===- CLIDebugger.h - LLVM Command Line Interface Debugger -----*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the CLIDebugger class, which implements a command line +// interface to the LLVM Debugger library. +// +//===----------------------------------------------------------------------===// + +#ifndef CLIDEBUGGER_H +#define CLIDEBUGGER_H + +#include "llvm/Debugger/Debugger.h" +#include <map> + +namespace llvm { + class CLICommand; + class SourceFile; + class SourceLanguage; + class ProgramInfo; + class RuntimeInfo; + + /// CLIDebugger - This class implements the command line interface for the + /// LLVM debugger. + class CLIDebugger { + /// Dbg - The low-level LLVM debugger object that we use to do our dirty + /// work. + Debugger Dbg; + + /// CommandTable - This table contains a mapping from command names to the + /// CLICommand object that implements the command. + std::map<std::string, CLICommand*> CommandTable; + + //===------------------------------------------------------------------===// + // Data related to the program that is currently loaded. Note that the Dbg + // variable also captures some information about the loaded program. This + // pointer is non-null iff Dbg.isProgramLoaded() is true. + // + ProgramInfo *TheProgramInfo; + + //===------------------------------------------------------------------===// + // Data related to the program that is currently executing, but has stopped. + // Note that the Dbg variable also captures some information about the + // loaded program. This pointer is non-null iff Dbg.isProgramRunning() is + // true. + // + RuntimeInfo *TheRuntimeInfo; + + /// LastCurrentFrame - This variable holds the Frame ID of the top-level + /// stack frame from the last time that the program was executed. We keep + /// this because we only want to print the source location when the current + /// function changes. + void *LastCurrentFrame; + + //===------------------------------------------------------------------===// + // Data directly exposed through the debugger prompt + // + std::string Prompt; // set prompt, show prompt + unsigned ListSize; // set listsize, show listsize + + //===------------------------------------------------------------------===// + // Data to support user interaction + // + + /// CurrentFile - The current source file we are inspecting, or null if + /// none. + const SourceFile *CurrentFile; + unsigned LineListedStart, LineListedEnd; + + /// CurrentLanguage - This contains the source language in use, if one is + /// explicitly set by the user. If this is null (the default), the language + /// is automatically determined from the current stack frame. + /// + const SourceLanguage *CurrentLanguage; + + public: + CLIDebugger(); + + /// getDebugger - Return the current LLVM debugger implementation being + /// used. + Debugger &getDebugger() { return Dbg; } + + /// run - Start the debugger, returning when the user exits the debugger. + /// This starts the main event loop of the CLI debugger. + /// + int run(); + + /// addCommand - Add a command to the CommandTable, potentially displacing a + /// preexisting command. + void addCommand(const std::string &Option, CLICommand *Cmd); + + /// addSourceDirectory - Add a directory to search when looking for the + /// source code of the program. + void addSourceDirectory(const std::string &Dir) { + // FIXME: implement + } + + /// getCurrentLanguage - Return the current source language that the user is + /// playing around with. This is aquired from the current stack frame of a + /// running program if one exists, but this value can be explicitly set by + /// the user as well. + const SourceLanguage &getCurrentLanguage() const; + + /// getProgramInfo - Return a reference to the ProgramInfo object for the + /// currently loaded program. If there is no program loaded, throw an + /// exception. + ProgramInfo &getProgramInfo() const { + if (TheProgramInfo == 0) + throw "No program is loaded."; + return *TheProgramInfo; + } + + /// getRuntimeInfo - Return a reference to the current RuntimeInfo object. + /// If there is no program running, throw an exception. + RuntimeInfo &getRuntimeInfo() const { + if (TheRuntimeInfo == 0) + throw "No program is running."; + return *TheRuntimeInfo; + } + + private: // Internal implementation methods + + /// getCommand - This looks up the specified command using a fuzzy match. + /// If the string exactly matches a command or is an unambiguous prefix of a + /// command, it returns the command. Otherwise it throws an exception + /// indicating the possible ambiguous choices. + CLICommand *getCommand(const std::string &Command); + + /// askYesNo - Ask the user a question, and demand a yes/no response. If + /// the user says yes, return true. + bool askYesNo(const std::string &Message) const; + + /// printProgramLocation - Given a loaded and created child process that has + /// stopped, print its current source location. + void printProgramLocation(bool PrintLocation = true); + + /// eliminateRunInfo - We are about to run the program. Forget any state + /// about how the program used to be stopped. + void eliminateRunInfo(); + + /// programStoppedSuccessfully - This method updates internal data + /// structures to reflect the fact that the program just executed a while, + /// and has successfully stopped. + void programStoppedSuccessfully(); + + public: /// Builtin debugger commands, invokable by the user + // Program startup and shutdown options + void fileCommand(std::string &Options); // file + void createCommand(std::string &Options); // create + void killCommand(std::string &Options); // kill + void quitCommand(std::string &Options); // quit + + // Program execution commands + void runCommand(std::string &Options); // run|r + void contCommand(std::string &Options); // cont|c|fg + void stepCommand(std::string &Options); // step|s [count] + void nextCommand(std::string &Options); // next|n [count] + void finishCommand(std::string &Options); // finish + + // Stack frame commands + void backtraceCommand(std::string &Options); // backtrace|bt [count] + void upCommand(std::string &Options); // up + void downCommand(std::string &Options); // down + void frameCommand(std::string &Options); // frame + + + // Breakpoint related commands + void breakCommand(std::string &Options); // break|b <id> + + // Miscellaneous commands + void infoCommand(std::string &Options); // info + void listCommand(std::string &Options); // list + void setCommand(std::string &Options); // set + void showCommand(std::string &Options); // show + void helpCommand(std::string &Options); // help + + private: + /// startProgramRunning - If the program has been updated, reload it, then + /// start executing the program. + void startProgramRunning(); + + /// printSourceLine - Print the specified line of the current source file. + /// If the specified line is invalid (the source file could not be loaded or + /// the line number is out of range), don't print anything, but return true. + bool printSourceLine(unsigned LineNo); + + /// parseLineSpec - Parses a line specifier, for use by the 'list' command. + /// If SourceFile is returned as a void pointer, then it was not specified. + /// If the line specifier is invalid, an exception is thrown. + void parseLineSpec(std::string &LineSpec, const SourceFile *&SourceFile, + unsigned &LineNo); + + /// parseProgramOptions - This method parses the Options string and loads it + /// as options to be passed to the program. This is used by the run command + /// and by 'set args'. + void parseProgramOptions(std::string &Options); + }; +} + +#endif diff --git a/tools/llvm-db/CMakeLists.txt b/tools/llvm-db/CMakeLists.txt new file mode 100644 index 0000000..af64908 --- /dev/null +++ b/tools/llvm-db/CMakeLists.txt @@ -0,0 +1,8 @@ +set(LLVM_LINK_COMPONENTS debugger) +set(LLVM_REQUIRES_EH 1) + +add_llvm_tool(llvm-db + CLIDebugger.cpp + Commands.cpp + llvm-db.cpp + ) diff --git a/tools/llvm-db/Commands.cpp b/tools/llvm-db/Commands.cpp new file mode 100644 index 0000000..ffebdd5 --- /dev/null +++ b/tools/llvm-db/Commands.cpp @@ -0,0 +1,865 @@ +//===-- Commands.cpp - Implement various commands for the CLI -------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements many builtin user commands. +// +//===----------------------------------------------------------------------===// + +#include "CLIDebugger.h" +#include "CLICommand.h" +#include "llvm/Debugger/ProgramInfo.h" +#include "llvm/Debugger/RuntimeInfo.h" +#include "llvm/Debugger/SourceLanguage.h" +#include "llvm/Debugger/SourceFile.h" +#include "llvm/Debugger/InferiorProcess.h" +#include "llvm/Support/FileUtilities.h" +#include "llvm/ADT/StringExtras.h" +#include <iostream> +#include <cstdlib> +using namespace llvm; + +/// getCurrentLanguage - Return the current source language that the user is +/// playing around with. This is aquired from the current stack frame of a +/// running program if one exists, but this value can be explicitly set by the +/// user as well. +const SourceLanguage &CLIDebugger::getCurrentLanguage() const { + // If the user explicitly switched languages with 'set language', use what + // they asked for. + if (CurrentLanguage) { + return *CurrentLanguage; + } else if (Dbg.isProgramRunning()) { + // Otherwise, if the program is running, infer the current language from it. + const GlobalVariable *FuncDesc = + getRuntimeInfo().getCurrentFrame().getFunctionDesc(); + return getProgramInfo().getFunction(FuncDesc).getSourceFile().getLanguage(); + } else { + // Otherwise, default to C like GDB apparently does. + return SourceLanguage::getCFamilyInstance(); + } +} + +/// startProgramRunning - If the program has been updated, reload it, then +/// start executing the program. +void CLIDebugger::startProgramRunning() { + eliminateRunInfo(); + + // If the program has been modified, reload it! + sys::PathWithStatus Program(Dbg.getProgramPath()); + std::string Err; + const sys::FileStatus *Status = Program.getFileStatus(false, &Err); + if (!Status) + throw Err; + if (TheProgramInfo->getProgramTimeStamp() != Status->getTimestamp()) { + std::cout << "'" << Program << "' has changed; re-reading program.\n"; + + // Unload an existing program. This kills the program if necessary. + Dbg.unloadProgram(); + delete TheProgramInfo; + TheProgramInfo = 0; + CurrentFile = 0; + + Dbg.loadProgram(Program.toString()); + TheProgramInfo = new ProgramInfo(Dbg.getProgram()); + } + + std::cout << "Starting program: " << Dbg.getProgramPath() << "\n"; + Dbg.createProgram(); + + // There was no current frame. + LastCurrentFrame = 0; +} + +/// printSourceLine - Print the specified line of the current source file. +/// If the specified line is invalid (the source file could not be loaded or +/// the line number is out of range), don't print anything, but return true. +bool CLIDebugger::printSourceLine(unsigned LineNo) { + assert(CurrentFile && "There is no current source file to print!"); + const char *LineStart, *LineEnd; + CurrentFile->getSourceLine(LineNo-1, LineStart, LineEnd); + if (LineStart == 0) return true; + std::cout << LineNo; + + // If this is the line the program is currently stopped at, print a marker. + if (Dbg.isProgramRunning()) { + unsigned CurLineNo, CurColNo; + const SourceFileInfo *CurSFI; + getRuntimeInfo().getCurrentFrame().getSourceLocation(CurLineNo, CurColNo, + CurSFI); + + if (CurLineNo == LineNo && CurrentFile == &CurSFI->getSourceText()) + std::cout << " ->"; + } + + std::cout << "\t" << std::string(LineStart, LineEnd) << "\n"; + return false; +} + +/// printProgramLocation - Print a line of the place where the current stack +/// frame has stopped and the source line it is on. +/// +void CLIDebugger::printProgramLocation(bool PrintLocation) { + assert(Dbg.isProgramLoaded() && Dbg.isProgramRunning() && + "Error program is not loaded and running!"); + + // Figure out where the program stopped... + StackFrame &SF = getRuntimeInfo().getCurrentFrame(); + unsigned LineNo, ColNo; + const SourceFileInfo *FileDesc; + SF.getSourceLocation(LineNo, ColNo, FileDesc); + + // If requested, print out some program information about WHERE we are. + if (PrintLocation) { + // FIXME: print the current function arguments + if (const GlobalVariable *FuncDesc = SF.getFunctionDesc()) + std::cout << getProgramInfo().getFunction(FuncDesc).getSymbolicName(); + else + std::cout << "<unknown function>"; + + CurrentFile = &FileDesc->getSourceText(); + + std::cout << " at " << CurrentFile->getFilename() << ":" << LineNo; + if (ColNo) std::cout << ":" << ColNo; + std::cout << "\n"; + } + + if (printSourceLine(LineNo)) + std::cout << "<could not load source file>\n"; + else { + LineListedStart = LineNo-ListSize/2+1; + if ((int)LineListedStart < 1) LineListedStart = 1; + LineListedEnd = LineListedStart+1; + } +} + +/// eliminateRunInfo - We are about to run the program. Forget any state +/// about how the program used to be stopped. +void CLIDebugger::eliminateRunInfo() { + delete TheRuntimeInfo; + TheRuntimeInfo = 0; +} + +/// programStoppedSuccessfully - This method updates internal data +/// structures to reflect the fact that the program just executed a while, +/// and has successfully stopped. +void CLIDebugger::programStoppedSuccessfully() { + assert(TheRuntimeInfo==0 && "Someone forgot to release the old RuntimeInfo!"); + + TheRuntimeInfo = new RuntimeInfo(TheProgramInfo, Dbg.getRunningProcess()); + + // FIXME: if there are any breakpoints at the current location, print them as + // well. + + // Since the program as successfully stopped, print its location. + void *CurrentFrame = getRuntimeInfo().getCurrentFrame().getFrameID(); + printProgramLocation(CurrentFrame != LastCurrentFrame); + LastCurrentFrame = CurrentFrame; +} + + + +/// getUnsignedIntegerOption - Get an unsigned integer number from the Val +/// string. Check to make sure that the string contains an unsigned integer +/// token, and if not, throw an exception. If isOnlyOption is set, also throw +/// an exception if there is extra junk at the end of the string. +static unsigned getUnsignedIntegerOption(const char *Msg, std::string &Val, + bool isOnlyOption = true) { + std::string Tok = getToken(Val); + if (Tok.empty() || (isOnlyOption && !getToken(Val).empty())) + throw std::string(Msg) + " expects an unsigned integer argument."; + + char *EndPtr; + unsigned Result = strtoul(Tok.c_str(), &EndPtr, 0); + if (EndPtr != Tok.c_str()+Tok.size()) + throw std::string(Msg) + " expects an unsigned integer argument."; + + return Result; +} + +/// getOptionalUnsignedIntegerOption - This method is just like +/// getUnsignedIntegerOption, but if the argument value is not specified, a +/// default is returned instead of causing an error. +static unsigned +getOptionalUnsignedIntegerOption(const char *Msg, unsigned Default, + std::string &Val, bool isOnlyOption = true) { + // Check to see if the value was specified... + std::string TokVal = getToken(Val); + if (TokVal.empty()) return Default; + + // If it was specified, add it back to the value we are parsing... + Val = TokVal+Val; + + // And parse normally. + return getUnsignedIntegerOption(Msg, Val, isOnlyOption); +} + + +/// parseProgramOptions - This method parses the Options string and loads it +/// as options to be passed to the program. This is used by the run command +/// and by 'set args'. +void CLIDebugger::parseProgramOptions(std::string &Options) { + // FIXME: tokenizing by whitespace is clearly incorrect. Instead we should + // honor quotes and other things that a shell would. Also in the future we + // should support redirection of standard IO. + + std::vector<std::string> Arguments; + for (std::string A = getToken(Options); !A.empty(); A = getToken(Options)) + Arguments.push_back(A); + Dbg.setProgramArguments(Arguments.begin(), Arguments.end()); +} + + +//===----------------------------------------------------------------------===// +// Program startup and shutdown options +//===----------------------------------------------------------------------===// + + +/// file command - If the user specifies an option, search the PATH for the +/// specified program/bitcode file and load it. If the user does not specify +/// an option, unload the current program. +void CLIDebugger::fileCommand(std::string &Options) { + std::string Prog = getToken(Options); + if (!getToken(Options).empty()) + throw "file command takes at most one argument."; + + // Check to make sure the user knows what they are doing + if (Dbg.isProgramRunning() && + !askYesNo("A program is already loaded. Kill it?")) + return; + + // Unload an existing program. This kills the program if necessary. + eliminateRunInfo(); + delete TheProgramInfo; + TheProgramInfo = 0; + Dbg.unloadProgram(); + CurrentFile = 0; + + // If requested, start the new program. + if (Prog.empty()) { + std::cout << "Unloaded program.\n"; + } else { + std::cout << "Loading program... " << std::flush; + Dbg.loadProgram(Prog); + assert(Dbg.isProgramLoaded() && + "loadProgram succeeded, but not program loaded!"); + TheProgramInfo = new ProgramInfo(Dbg.getProgram()); + std::cout << "successfully loaded '" << Dbg.getProgramPath() << "'!\n"; + } +} + + +void CLIDebugger::createCommand(std::string &Options) { + if (!getToken(Options).empty()) + throw "create command does not take any arguments."; + if (!Dbg.isProgramLoaded()) throw "No program loaded."; + if (Dbg.isProgramRunning() && + !askYesNo("The program is already running. Restart from the beginning?")) + return; + + // Start the program running. + startProgramRunning(); + + // The program stopped! + programStoppedSuccessfully(); +} + +void CLIDebugger::killCommand(std::string &Options) { + if (!getToken(Options).empty()) + throw "kill command does not take any arguments."; + if (!Dbg.isProgramRunning()) + throw "No program is currently being run."; + + if (askYesNo("Kill the program being debugged?")) + Dbg.killProgram(); + eliminateRunInfo(); +} + +void CLIDebugger::quitCommand(std::string &Options) { + if (!getToken(Options).empty()) + throw "quit command does not take any arguments."; + + if (Dbg.isProgramRunning() && + !askYesNo("The program is running. Exit anyway?")) + return; + + // Throw exception to get out of the user-input loop. + throw 0; +} + + +//===----------------------------------------------------------------------===// +// Program execution commands +//===----------------------------------------------------------------------===// + +void CLIDebugger::runCommand(std::string &Options) { + if (!Dbg.isProgramLoaded()) throw "No program loaded."; + if (Dbg.isProgramRunning() && + !askYesNo("The program is already running. Restart from the beginning?")) + return; + + // Parse all of the options to the run command, which specify program + // arguments to run with. + parseProgramOptions(Options); + + eliminateRunInfo(); + + // Start the program running. + startProgramRunning(); + + // Start the program running... + Options = ""; + contCommand(Options); +} + +void CLIDebugger::contCommand(std::string &Options) { + if (!getToken(Options).empty()) throw "cont argument not supported yet."; + if (!Dbg.isProgramRunning()) throw "Program is not running."; + + eliminateRunInfo(); + + Dbg.contProgram(); + + // The program stopped! + programStoppedSuccessfully(); +} + +void CLIDebugger::stepCommand(std::string &Options) { + if (!Dbg.isProgramRunning()) throw "Program is not running."; + + // Figure out how many times to step. + unsigned Amount = + getOptionalUnsignedIntegerOption("'step' command", 1, Options); + + eliminateRunInfo(); + + // Step the specified number of times. + for (; Amount; --Amount) + Dbg.stepProgram(); + + // The program stopped! + programStoppedSuccessfully(); +} + +void CLIDebugger::nextCommand(std::string &Options) { + if (!Dbg.isProgramRunning()) throw "Program is not running."; + unsigned Amount = + getOptionalUnsignedIntegerOption("'next' command", 1, Options); + + eliminateRunInfo(); + + for (; Amount; --Amount) + Dbg.nextProgram(); + + // The program stopped! + programStoppedSuccessfully(); +} + +void CLIDebugger::finishCommand(std::string &Options) { + if (!getToken(Options).empty()) + throw "finish command does not take any arguments."; + if (!Dbg.isProgramRunning()) throw "Program is not running."; + + // Figure out where we are exactly. If the user requests that we return from + // a frame that is not the top frame, make sure we get it. + void *CurrentFrame = getRuntimeInfo().getCurrentFrame().getFrameID(); + + eliminateRunInfo(); + + Dbg.finishProgram(CurrentFrame); + + // The program stopped! + programStoppedSuccessfully(); +} + +//===----------------------------------------------------------------------===// +// Stack frame commands +//===----------------------------------------------------------------------===// + +void CLIDebugger::backtraceCommand(std::string &Options) { + // Accepts "full", n, -n + if (!getToken(Options).empty()) + throw "FIXME: bt command argument not implemented yet!"; + + RuntimeInfo &RI = getRuntimeInfo(); + ProgramInfo &PI = getProgramInfo(); + + try { + for (unsigned i = 0; ; ++i) { + StackFrame &SF = RI.getStackFrame(i); + std::cout << "#" << i; + if (i == RI.getCurrentFrameIdx()) + std::cout << " ->"; + std::cout << "\t" << SF.getFrameID() << " in "; + if (const GlobalVariable *G = SF.getFunctionDesc()) + std::cout << PI.getFunction(G).getSymbolicName(); + + unsigned LineNo, ColNo; + const SourceFileInfo *SFI; + SF.getSourceLocation(LineNo, ColNo, SFI); + if (!SFI->getBaseName().empty()) { + std::cout << " at " << SFI->getBaseName(); + if (LineNo) { + std::cout << ":" << LineNo; + if (ColNo) + std::cout << ":" << ColNo; + } + } + + // FIXME: when we support shared libraries, we should print ' from foo.so' + // if the stack frame is from a different object than the current one. + + std::cout << "\n"; + } + } catch (...) { + // Stop automatically when we run off the bottom of the stack. + } +} + +void CLIDebugger::upCommand(std::string &Options) { + unsigned Num = + getOptionalUnsignedIntegerOption("'up' command", 1, Options); + + RuntimeInfo &RI = getRuntimeInfo(); + unsigned CurFrame = RI.getCurrentFrameIdx(); + + // Check to see if we go can up the specified number of frames. + try { + RI.getStackFrame(CurFrame+Num); + } catch (...) { + if (Num == 1) + throw "Initial frame selected; you cannot go up."; + else + throw "Cannot go up " + utostr(Num) + " frames!"; + } + + RI.setCurrentFrameIdx(CurFrame+Num); + printProgramLocation(); +} + +void CLIDebugger::downCommand(std::string &Options) { + unsigned Num = + getOptionalUnsignedIntegerOption("'down' command", 1, Options); + + RuntimeInfo &RI = getRuntimeInfo(); + unsigned CurFrame = RI.getCurrentFrameIdx(); + + // Check to see if we can go up the specified number of frames. + if (CurFrame < Num) { + if (Num == 1) + throw "Bottom (i.e., innermost) frame selected; you cannot go down."; + else + throw "Cannot go down " + utostr(Num) + " frames!"; + } + + RI.setCurrentFrameIdx(CurFrame-Num); + printProgramLocation(); +} + +void CLIDebugger::frameCommand(std::string &Options) { + RuntimeInfo &RI = getRuntimeInfo(); + unsigned CurFrame = RI.getCurrentFrameIdx(); + + unsigned Num = + getOptionalUnsignedIntegerOption("'frame' command", CurFrame, Options); + + // Check to see if we go to the specified frame. + RI.getStackFrame(Num); + + RI.setCurrentFrameIdx(Num); + printProgramLocation(); +} + + +//===----------------------------------------------------------------------===// +// Breakpoint related commands +//===----------------------------------------------------------------------===// + +void CLIDebugger::breakCommand(std::string &Options) { + // Figure out where the user wants a breakpoint. + const SourceFile *File; + unsigned LineNo; + + // Check to see if the user specified a line specifier. + std::string Option = getToken(Options); // strip whitespace + if (!Option.empty()) { + Options = Option + Options; // reconstruct string + + // Parse the line specifier. + parseLineSpec(Options, File, LineNo); + } else { + // Build a line specifier for the current stack frame. + throw "FIXME: breaking at the current location is not implemented yet!"; + } + + if (!File) File = CurrentFile; + if (File == 0) + throw "Unknown file to place breakpoint!"; + + std::cerr << "Break: " << File->getFilename() << ":" << LineNo << "\n"; + + throw "breakpoints not implemented yet!"; +} + +//===----------------------------------------------------------------------===// +// Miscellaneous commands +//===----------------------------------------------------------------------===// + +void CLIDebugger::infoCommand(std::string &Options) { + std::string What = getToken(Options); + + if (What.empty() || !getToken(Options).empty()){ + std::string infoStr("info"); + helpCommand(infoStr); + return; + } + + if (What == "frame") { + } else if (What == "functions") { + const std::map<const GlobalVariable*, SourceFunctionInfo*> &Functions + = getProgramInfo().getSourceFunctions(); + std::cout << "All defined functions:\n"; + // FIXME: GDB groups these by source file. We could do that I guess. + for (std::map<const GlobalVariable*, SourceFunctionInfo*>::const_iterator + I = Functions.begin(), E = Functions.end(); I != E; ++I) { + std::cout << I->second->getSymbolicName() << "\n"; + } + + } else if (What == "source") { + if (CurrentFile == 0) + throw "No current source file."; + + // Get the SourceFile information for the current file. + const SourceFileInfo &SF = + getProgramInfo().getSourceFile(CurrentFile->getDescriptor()); + + std::cout << "Current source file is: " << SF.getBaseName() << "\n" + << "Compilation directory is: " << SF.getDirectory() << "\n"; + if (unsigned NL = CurrentFile->getNumLines()) + std::cout << "Located in: " << CurrentFile->getFilename() << "\n" + << "Contains " << NL << " lines\n"; + else + std::cout << "Could not find source file.\n"; + std::cout << "Source language is " + << SF.getLanguage().getSourceLanguageName() << "\n"; + + } else if (What == "sources") { + const std::map<const GlobalVariable*, SourceFileInfo*> &SourceFiles = + getProgramInfo().getSourceFiles(); + std::cout << "Source files for the program:\n"; + for (std::map<const GlobalVariable*, SourceFileInfo*>::const_iterator I = + SourceFiles.begin(), E = SourceFiles.end(); I != E;) { + std::cout << I->second->getDirectory() << "/" + << I->second->getBaseName(); + ++I; + if (I != E) std::cout << ", "; + } + std::cout << "\n"; + } else if (What == "target") { + std::cout << Dbg.getRunningProcess().getStatus(); + } else { + // See if this is something handled by the current language. + if (getCurrentLanguage().printInfo(What)) + return; + + throw "Unknown info command '" + What + "'. Try 'help info'."; + } +} + +/// parseLineSpec - Parses a line specifier, for use by the 'list' command. +/// If SourceFile is returned as a void pointer, then it was not specified. +/// If the line specifier is invalid, an exception is thrown. +void CLIDebugger::parseLineSpec(std::string &LineSpec, + const SourceFile *&SourceFile, + unsigned &LineNo) { + SourceFile = 0; + LineNo = 0; + + // First, check to see if we have a : separator. + std::string FirstPart = getToken(LineSpec, ":"); + std::string SecondPart = getToken(LineSpec, ":"); + if (!getToken(LineSpec).empty()) throw "Malformed line specification!"; + + // If there is no second part, we must have either "function", "number", + // "+offset", or "-offset". + if (SecondPart.empty()) { + if (FirstPart.empty()) throw "Malformed line specification!"; + if (FirstPart[0] == '+') { + FirstPart.erase(FirstPart.begin(), FirstPart.begin()+1); + // For +n, return LineListedEnd+n + LineNo = LineListedEnd + + getUnsignedIntegerOption("Line specifier '+'", FirstPart); + + } else if (FirstPart[0] == '-') { + FirstPart.erase(FirstPart.begin(), FirstPart.begin()+1); + // For -n, return LineListedEnd-n + LineNo = LineListedEnd - + getUnsignedIntegerOption("Line specifier '-'", FirstPart); + if ((int)LineNo < 1) LineNo = 1; + } else if (FirstPart[0] == '*') { + throw "Address expressions not supported as source locations!"; + } else { + // Ok, check to see if this is just a line number. + std::string Saved = FirstPart; + try { + LineNo = getUnsignedIntegerOption("", Saved); + } catch (...) { + // Ok, it's not a valid line number. It must be a source-language + // entity name. + std::string Name = getToken(FirstPart); + if (!getToken(FirstPart).empty()) + throw "Extra junk in line specifier after '" + Name + "'."; + SourceFunctionInfo *SFI = + getCurrentLanguage().lookupFunction(Name, getProgramInfo(), + TheRuntimeInfo); + if (SFI == 0) + throw "Unknown identifier '" + Name + "'."; + + unsigned L, C; + SFI->getSourceLocation(L, C); + if (L == 0) throw "Could not locate '" + Name + "'!"; + LineNo = L; + SourceFile = &SFI->getSourceFile().getSourceText(); + return; + } + } + + } else { + // Ok, this must be a filename qualified line number or function name. + // First, figure out the source filename. + std::string SourceFilename = getToken(FirstPart); + if (!getToken(FirstPart).empty()) + throw "Invalid filename qualified source location!"; + + // Next, check to see if this is just a line number. + std::string Saved = SecondPart; + try { + LineNo = getUnsignedIntegerOption("", Saved); + } catch (...) { + // Ok, it's not a valid line number. It must be a function name. + throw "FIXME: Filename qualified function names are not support " + "as line specifiers yet!"; + } + + // Ok, we got the line number. Now check out the source file name to make + // sure it's all good. If it is, return it. If not, throw exception. + SourceFile =&getProgramInfo().getSourceFile(SourceFilename).getSourceText(); + } +} + +void CLIDebugger::listCommand(std::string &Options) { + if (!Dbg.isProgramLoaded()) + throw "No program is loaded. Use the 'file' command."; + + // Handle "list foo," correctly, by returning " " as the second token + Options += " "; + + std::string FirstLineSpec = getToken(Options, ","); + std::string SecondLineSpec = getToken(Options, ","); + if (!getToken(Options, ",").empty()) + throw "list command only expects two source location specifiers!"; + + // StartLine, EndLine - The starting and ending line numbers to print. + unsigned StartLine = 0, EndLine = 0; + + if (SecondLineSpec.empty()) { // No second line specifier provided? + // Handle special forms like "", "+", "-", etc. + std::string TmpSpec = FirstLineSpec; + std::string Tok = getToken(TmpSpec); + if (getToken(TmpSpec).empty() && (Tok == "" || Tok == "+" || Tok == "-")) { + if (Tok == "+" || Tok == "") { + StartLine = LineListedEnd; + EndLine = StartLine + ListSize; + } else { + assert(Tok == "-"); + StartLine = LineListedStart-ListSize; + EndLine = LineListedStart; + if ((int)StartLine <= 0) StartLine = 1; + } + } else { + // Must be a normal line specifier. + const SourceFile *File; + unsigned LineNo; + parseLineSpec(FirstLineSpec, File, LineNo); + + // If the user only specified one file specifier, we should display + // ListSize lines centered at the specified line. + if (File != 0) CurrentFile = File; + StartLine = LineNo - (ListSize+1)/2; + if ((int)StartLine <= 0) StartLine = 1; + EndLine = StartLine + ListSize; + } + + } else { + // Parse two line specifiers... + const SourceFile *StartFile, *EndFile; + unsigned StartLineNo, EndLineNo; + parseLineSpec(FirstLineSpec, StartFile, StartLineNo); + unsigned SavedLLE = LineListedEnd; + LineListedEnd = StartLineNo; + try { + parseLineSpec(SecondLineSpec, EndFile, EndLineNo); + } catch (...) { + LineListedEnd = SavedLLE; + throw; + } + + // Inherit file specified by the first line spec if there was one. + if (EndFile == 0) EndFile = StartFile; + + if (StartFile != EndFile) + throw "Start and end line specifiers are in different files!"; + CurrentFile = StartFile; + StartLine = StartLineNo; + EndLine = EndLineNo+1; + } + + assert((int)StartLine > 0 && (int)EndLine > 0 && StartLine <= EndLine && + "Error reading line specifiers!"); + + // If there was no current file, and the user didn't specify one to list, we + // have an error. + if (CurrentFile == 0) + throw "There is no current file to list."; + + // Remember for next time. + LineListedStart = StartLine; + LineListedEnd = StartLine; + + for (unsigned LineNo = StartLine; LineNo != EndLine; ++LineNo) { + // Print the source line, unless it is invalid. + if (printSourceLine(LineNo)) + break; + LineListedEnd = LineNo+1; + } + + // If we didn't print any lines, find out why. + if (LineListedEnd == StartLine) { + // See if we can read line #0 from the file, if not, we couldn't load the + // file. + const char *LineStart, *LineEnd; + CurrentFile->getSourceLine(0, LineStart, LineEnd); + if (LineStart == 0) + throw "Could not load source file '" + CurrentFile->getFilename() + "'!"; + else + std::cout << "<end of file>\n"; + } +} + +void CLIDebugger::setCommand(std::string &Options) { + std::string What = getToken(Options); + + if (What.empty()) + throw "set command expects at least two arguments."; + if (What == "args") { + parseProgramOptions(Options); + } else if (What == "language") { + std::string Lang = getToken(Options); + if (!getToken(Options).empty()) + throw "set language expects one argument at most."; + if (Lang == "") { + std::cout << "The currently understood settings are:\n\n" + << "local or auto Automatic setting based on source file\n" + << "c Use the C language\n" + << "c++ Use the C++ language\n" + << "unknown Use when source language is not supported\n"; + } else if (Lang == "local" || Lang == "auto") { + CurrentLanguage = 0; + } else if (Lang == "c") { + CurrentLanguage = &SourceLanguage::getCFamilyInstance(); + } else if (Lang == "c++") { + CurrentLanguage = &SourceLanguage::getCPlusPlusInstance(); + } else if (Lang == "unknown") { + CurrentLanguage = &SourceLanguage::getUnknownLanguageInstance(); + } else { + throw "Unknown language '" + Lang + "'."; + } + + } else if (What == "listsize") { + ListSize = getUnsignedIntegerOption("'set prompt' command", Options); + } else if (What == "prompt") { + // Include any trailing whitespace or other tokens, but not leading + // whitespace. + Prompt = getToken(Options); // Strip leading whitespace + Prompt += Options; // Keep trailing whitespace or other stuff + } else { + // FIXME: Try to parse this as a source-language program expression. + throw "Don't know how to set '" + What + "'!"; + } +} + +void CLIDebugger::showCommand(std::string &Options) { + std::string What = getToken(Options); + + if (What.empty() || !getToken(Options).empty()) + throw "show command expects one argument."; + + if (What == "args") { + std::cout << "Argument list to give program when started is \""; + // FIXME: This doesn't print stuff correctly if the arguments have spaces in + // them, but currently the only way to get that is to use the --args command + // line argument. This should really handle escaping all hard characters as + // needed. + for (unsigned i = 0, e = Dbg.getNumProgramArguments(); i != e; ++i) + std::cout << (i ? " " : "") << Dbg.getProgramArgument(i); + std::cout << "\"\n"; + + } else if (What == "language") { + std::cout << "The current source language is '"; + if (CurrentLanguage) + std::cout << CurrentLanguage->getSourceLanguageName(); + else + std::cout << "auto; currently " + << getCurrentLanguage().getSourceLanguageName(); + std::cout << "'.\n"; + } else if (What == "listsize") { + std::cout << "Number of source lines llvm-db will list by default is " + << ListSize << ".\n"; + } else if (What == "prompt") { + std::cout << "llvm-db's prompt is \"" << Prompt << "\".\n"; + } else { + throw "Unknown show command '" + What + "'. Try 'help show'."; + } +} + +void CLIDebugger::helpCommand(std::string &Options) { + // Print out all of the commands in the CommandTable + std::string Command = getToken(Options); + if (!getToken(Options).empty()) + throw "help command takes at most one argument."; + + // Getting detailed help on a particular command? + if (!Command.empty()) { + CLICommand *C = getCommand(Command); + std::cout << C->getShortHelp() << ".\n" << C->getLongHelp(); + + // If there are aliases for this option, print them out. + const std::vector<std::string> &Names = C->getOptionNames(); + if (Names.size() > 1) { + std::cout << "The '" << Command << "' command is known as: '" + << Names[0] << "'"; + for (unsigned i = 1, e = Names.size(); i != e; ++i) + std::cout << ", '" << Names[i] << "'"; + std::cout << "\n"; + } + + } else { + unsigned MaxSize = 0; + for (std::map<std::string, CLICommand*>::iterator I = CommandTable.begin(), + E = CommandTable.end(); I != E; ++I) + if (I->first.size() > MaxSize && + I->first == I->second->getPrimaryOptionName()) + MaxSize = I->first.size(); + + // Loop over all of the commands, printing the short help version + for (std::map<std::string, CLICommand*>::iterator I = CommandTable.begin(), + E = CommandTable.end(); I != E; ++I) + if (I->first == I->second->getPrimaryOptionName()) + std::cout << I->first << std::string(MaxSize - I->first.size(), ' ') + << " - " << I->second->getShortHelp() << "\n"; + } +} diff --git a/tools/llvm-db/Makefile b/tools/llvm-db/Makefile new file mode 100644 index 0000000..df81649 --- /dev/null +++ b/tools/llvm-db/Makefile @@ -0,0 +1,15 @@ +##===- tools/llvm-db/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 = llvm-db +LINK_COMPONENTS := debugger +REQUIRES_EH := 1 + +include $(LEVEL)/Makefile.common diff --git a/tools/llvm-db/llvm-db.cpp b/tools/llvm-db/llvm-db.cpp new file mode 100644 index 0000000..04e6162 --- /dev/null +++ b/tools/llvm-db/llvm-db.cpp @@ -0,0 +1,100 @@ +//===- llvm-db.cpp - LLVM Debugger ----------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This utility implements a simple text-mode front-end to the LLVM debugger +// library. +// +//===----------------------------------------------------------------------===// + +#include "CLIDebugger.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/PrettyStackTrace.h" +#include "llvm/System/Signals.h" +#include <iostream> +using namespace llvm; + +namespace { + // Command line options for specifying the program to debug and options to use + cl::opt<std::string> + InputFile(cl::desc("<program>"), cl::Positional, cl::init("")); + + cl::list<std::string> + InputArgs("args", cl::Positional, cl::desc("<program and arguments>"), + cl::ZeroOrMore); + + // Command line options to control various directory related stuff + cl::list<std::string> + SourceDirectories("directory", cl::value_desc("directory"), + cl::desc("Add directory to the search for source files")); + cl::alias SDA("d", cl::desc("Alias for --directory"), + cl::aliasopt(SourceDirectories)); + + cl::opt<std::string> + WorkingDirectory("cd", cl::desc("Use directory as current working directory"), + cl::value_desc("directory")); + + // Command line options specific to the llvm-db debugger driver + cl::opt<bool> Quiet("quiet", cl::desc("Do not print introductory messages")); + cl::alias QA1("silent", cl::desc("Alias for -quiet"), cl::aliasopt(Quiet)); + cl::alias QA2("q", cl::desc("Alias for -quiet"), cl::aliasopt(Quiet)); +} + +//===----------------------------------------------------------------------===// +// main Driver function +// +int main(int argc, char **argv, char * const *envp) { + // Print a stack trace if we signal out. + sys::PrintStackTraceOnErrorSignal(); + PrettyStackTraceProgram X(argc, argv); + + llvm_shutdown_obj Y; // Call llvm_shutdown() on exit. + std::cout << "NOTE: llvm-db is known useless right now.\n"; + try { + cl::ParseCommandLineOptions(argc, argv, + "llvm source-level debugger\n"); + + if (!Quiet) + std::cout << "llvm-db: The LLVM source-level debugger\n"; + + // Merge Inputfile and InputArgs into the InputArgs list... + if (!InputFile.empty() && InputArgs.empty()) + InputArgs.push_back(InputFile); + + // Create the CLI debugger... + CLIDebugger D; + + // Initialize the debugger with the command line options we read... + Debugger &Dbg = D.getDebugger(); + + // Initialize the debugger environment. + Dbg.initializeEnvironment(envp); + Dbg.setWorkingDirectory(WorkingDirectory); + for (unsigned i = 0, e = SourceDirectories.size(); i != e; ++i) + D.addSourceDirectory(SourceDirectories[i]); + + if (!InputArgs.empty()) { + try { + D.fileCommand(InputArgs[0]); + } catch (const std::string &Error) { + std::cout << "Error: " << Error << "\n"; + } + + Dbg.setProgramArguments(InputArgs.begin()+1, InputArgs.end()); + } + + // Now that we have initialized the debugger, run it. + return D.run(); + } catch (const std::string& msg) { + std::cerr << argv[0] << ": " << msg << "\n"; + } catch (...) { + std::cerr << argv[0] << ": Unexpected unknown exception occurred.\n"; + } + return 1; +} diff --git a/tools/llvm-dis/CMakeLists.txt b/tools/llvm-dis/CMakeLists.txt new file mode 100644 index 0000000..d62a6b5 --- /dev/null +++ b/tools/llvm-dis/CMakeLists.txt @@ -0,0 +1,6 @@ +set(LLVM_LINK_COMPONENTS bitreader) +set(LLVM_REQUIRES_EH 1) + +add_llvm_tool(llvm-dis + llvm-dis.cpp + ) diff --git a/tools/llvm-dis/Makefile b/tools/llvm-dis/Makefile new file mode 100644 index 0000000..dfd5e34 --- /dev/null +++ b/tools/llvm-dis/Makefile @@ -0,0 +1,18 @@ +##===- tools/llvm-dis/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 = llvm-dis +LINK_COMPONENTS := bitreader +REQUIRES_EH := 1 + +# This tool has no plugins, optimize startup time. +TOOL_NO_EXPORTS = 1 + +include $(LEVEL)/Makefile.common diff --git a/tools/llvm-dis/llvm-dis.cpp b/tools/llvm-dis/llvm-dis.cpp new file mode 100644 index 0000000..471e5e2 --- /dev/null +++ b/tools/llvm-dis/llvm-dis.cpp @@ -0,0 +1,143 @@ +//===-- llvm-dis.cpp - The low-level LLVM disassembler --------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This utility may be invoked in the following manner: +// llvm-dis [options] - Read LLVM bitcode from stdin, write asm to stdout +// llvm-dis [options] x.bc - Read LLVM bitcode from the x.bc file, write asm +// to the x.ll file. +// Options: +// --help - Output information about command line switches +// +//===----------------------------------------------------------------------===// + +#include "llvm/Module.h" +#include "llvm/PassManager.h" +#include "llvm/Bitcode/ReaderWriter.h" +#include "llvm/Assembly/PrintModulePass.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/PrettyStackTrace.h" +#include "llvm/Support/Streams.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/System/Signals.h" +#include <iostream> +#include <fstream> +#include <memory> +using namespace llvm; + +static cl::opt<std::string> +InputFilename(cl::Positional, cl::desc("<input bitcode>"), cl::init("-")); + +static cl::opt<std::string> +OutputFilename("o", cl::desc("Override output filename"), + cl::value_desc("filename")); + +static cl::opt<bool> +Force("f", cl::desc("Overwrite output files")); + +static cl::opt<bool> +DontPrint("disable-output", cl::desc("Don't output the .ll file"), cl::Hidden); + +int main(int argc, char **argv) { + // Print a stack trace if we signal out. + sys::PrintStackTraceOnErrorSignal(); + PrettyStackTraceProgram X(argc, argv); + + llvm_shutdown_obj Y; // Call llvm_shutdown() on exit. + try { + cl::ParseCommandLineOptions(argc, argv, "llvm .bc -> .ll disassembler\n"); + + std::ostream *Out = &std::cout; // Default to printing to stdout. + std::string ErrorMessage; + + std::auto_ptr<Module> M; + + if (MemoryBuffer *Buffer + = MemoryBuffer::getFileOrSTDIN(InputFilename, &ErrorMessage)) { + M.reset(ParseBitcodeFile(Buffer, &ErrorMessage)); + delete Buffer; + } + + if (M.get() == 0) { + cerr << argv[0] << ": "; + if (ErrorMessage.size()) + cerr << ErrorMessage << "\n"; + else + cerr << "bitcode didn't read correctly.\n"; + return 1; + } + + if (DontPrint) { + // Just use stdout. We won't actually print anything on it. + } else if (OutputFilename != "") { // Specified an output filename? + if (OutputFilename != "-") { // Not stdout? + if (!Force && std::ifstream(OutputFilename.c_str())) { + // If force is not specified, make sure not to overwrite a file! + cerr << argv[0] << ": error opening '" << OutputFilename + << "': file exists! Sending to standard output.\n"; + } else { + Out = new std::ofstream(OutputFilename.c_str()); + } + } + } else { + if (InputFilename == "-") { + OutputFilename = "-"; + } else { + std::string IFN = InputFilename; + int Len = IFN.length(); + if (IFN[Len-3] == '.' && IFN[Len-2] == 'b' && IFN[Len-1] == 'c') { + // Source ends in .bc + OutputFilename = std::string(IFN.begin(), IFN.end()-3)+".ll"; + } else { + OutputFilename = IFN+".ll"; + } + + if (!Force && std::ifstream(OutputFilename.c_str())) { + // If force is not specified, make sure not to overwrite a file! + cerr << argv[0] << ": error opening '" << OutputFilename + << "': file exists! Sending to standard output.\n"; + } else { + Out = new std::ofstream(OutputFilename.c_str()); + + // Make sure that the Out file gets unlinked from the disk if we get a + // SIGINT + sys::RemoveFileOnSignal(sys::Path(OutputFilename)); + } + } + } + + if (!Out->good()) { + cerr << argv[0] << ": error opening " << OutputFilename + << ": sending to stdout instead!\n"; + Out = &std::cout; + } + + // All that llvm-dis does is write the assembly to a file. + if (!DontPrint) { + PassManager Passes; + raw_os_ostream L(*Out); + Passes.add(createPrintModulePass(&L)); + Passes.run(*M.get()); + } + + if (Out != &std::cout) { + ((std::ofstream*)Out)->close(); + delete Out; + } + return 0; + } catch (const std::string& msg) { + cerr << argv[0] << ": " << msg << "\n"; + } catch (...) { + cerr << argv[0] << ": Unexpected unknown exception occurred.\n"; + } + + return 1; +} + diff --git a/tools/llvm-extract/CMakeLists.txt b/tools/llvm-extract/CMakeLists.txt new file mode 100644 index 0000000..88e9343 --- /dev/null +++ b/tools/llvm-extract/CMakeLists.txt @@ -0,0 +1,5 @@ +set(LLVM_LINK_COMPONENTS ipo bitreader bitwriter) + +add_llvm_tool(llvm-extract + llvm-extract.cpp + ) diff --git a/tools/llvm-extract/Makefile b/tools/llvm-extract/Makefile new file mode 100644 index 0000000..2ef8841 --- /dev/null +++ b/tools/llvm-extract/Makefile @@ -0,0 +1,18 @@ +##===- tools/llvm-extract/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 = llvm-extract +LINK_COMPONENTS := ipo bitreader bitwriter + +# This tool has no plugins, optimize startup time. +TOOL_NO_EXPORTS = 1 + +include $(LEVEL)/Makefile.common diff --git a/tools/llvm-extract/llvm-extract.cpp b/tools/llvm-extract/llvm-extract.cpp new file mode 100644 index 0000000..46840f2 --- /dev/null +++ b/tools/llvm-extract/llvm-extract.cpp @@ -0,0 +1,136 @@ +//===- llvm-extract.cpp - LLVM function extraction utility ----------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This utility changes the input module to only contain a single function, +// which is primarily used for debugging transformations. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Module.h" +#include "llvm/PassManager.h" +#include "llvm/Bitcode/ReaderWriter.h" +#include "llvm/Transforms/IPO.h" +#include "llvm/Target/TargetData.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/PrettyStackTrace.h" +#include "llvm/System/Signals.h" +#include <iostream> +#include <memory> +#include <fstream> +using namespace llvm; + +// InputFilename - The filename to read from. +static cl::opt<std::string> +InputFilename(cl::Positional, cl::desc("<input bitcode file>"), + cl::init("-"), cl::value_desc("filename")); + +static cl::opt<std::string> +OutputFilename("o", cl::desc("Specify output filename"), + cl::value_desc("filename"), cl::init("-")); + +static cl::opt<bool> +Force("f", cl::desc("Overwrite output files")); + +static cl::opt<bool> +DeleteFn("delete", cl::desc("Delete specified Globals from Module")); + +static cl::opt<bool> +Relink("relink", + cl::desc("Turn external linkage for callees of function to delete")); + +// ExtractFunc - The function to extract from the module... +static cl::opt<std::string> +ExtractFunc("func", cl::desc("Specify function to extract"), cl::init(""), + cl::value_desc("function")); + +// ExtractGlobal - The global to extract from the module... +static cl::opt<std::string> +ExtractGlobal("glob", cl::desc("Specify global to extract"), cl::init(""), + cl::value_desc("global")); + +int main(int argc, char **argv) { + // Print a stack trace if we signal out. + sys::PrintStackTraceOnErrorSignal(); + PrettyStackTraceProgram X(argc, argv); + + llvm_shutdown_obj Y; // Call llvm_shutdown() on exit. + cl::ParseCommandLineOptions(argc, argv, "llvm extractor\n"); + + std::auto_ptr<Module> M; + + MemoryBuffer *Buffer = MemoryBuffer::getFileOrSTDIN(InputFilename); + if (Buffer == 0) { + cerr << argv[0] << ": Error reading file '" + InputFilename + "'\n"; + return 1; + } else { + M.reset(ParseBitcodeFile(Buffer)); + } + delete Buffer; + + if (M.get() == 0) { + cerr << argv[0] << ": bitcode didn't read correctly.\n"; + return 1; + } + + // Figure out which function we should extract + GlobalVariable *G = !ExtractGlobal.empty() ? + M.get()->getNamedGlobal(ExtractGlobal) : 0; + + // Figure out which function we should extract + if (ExtractFunc.empty() && ExtractGlobal.empty()) ExtractFunc = "main"; + Function *F = M.get()->getFunction(ExtractFunc); + + if (F == 0 && G == 0) { + cerr << argv[0] << ": program doesn't contain function named '" + << ExtractFunc << "' or a global named '" << ExtractGlobal << "'!\n"; + return 1; + } + + // In addition to deleting all other functions, we also want to spiff it + // up a little bit. Do this now. + PassManager Passes; + Passes.add(new TargetData(M.get())); // Use correct TargetData + // Either isolate the function or delete it from the Module + std::vector<GlobalValue*> GVs; + if (F) GVs.push_back(F); + if (G) GVs.push_back(G); + + Passes.add(createGVExtractionPass(GVs, DeleteFn, Relink)); + if (!DeleteFn) + Passes.add(createGlobalDCEPass()); // Delete unreachable globals + Passes.add(createDeadTypeEliminationPass()); // Remove dead types... + Passes.add(createStripDeadPrototypesPass()); // Remove dead func decls + + std::ostream *Out = 0; + + if (OutputFilename != "-") { // Not stdout? + if (!Force && std::ifstream(OutputFilename.c_str())) { + // If force is not specified, make sure not to overwrite a file! + cerr << argv[0] << ": error opening '" << OutputFilename + << "': file exists!\n" + << "Use -f command line argument to force output\n"; + return 1; + } + std::ios::openmode io_mode = std::ios::out | std::ios::trunc | + std::ios::binary; + Out = new std::ofstream(OutputFilename.c_str(), io_mode); + } else { // Specified stdout + // FIXME: cout is not binary! + Out = &std::cout; + } + + Passes.add(CreateBitcodeWriterPass(*Out)); + Passes.run(*M.get()); + + if (Out != &std::cout) + delete Out; + return 0; +} diff --git a/tools/llvm-ld/CMakeLists.txt b/tools/llvm-ld/CMakeLists.txt new file mode 100644 index 0000000..51f0dc1 --- /dev/null +++ b/tools/llvm-ld/CMakeLists.txt @@ -0,0 +1,7 @@ +set(LLVM_LINK_COMPONENTS ipo scalaropts linker archive bitwriter) +set(LLVM_REQUIRES_EH 1) + +add_llvm_tool(llvm-ld + Optimize.cpp + llvm-ld.cpp + ) diff --git a/tools/llvm-ld/Makefile b/tools/llvm-ld/Makefile new file mode 100644 index 0000000..92cac2a --- /dev/null +++ b/tools/llvm-ld/Makefile @@ -0,0 +1,16 @@ +##===- tools/llvm-ld/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 = llvm-ld +LINK_COMPONENTS = ipo scalaropts linker archive bitwriter +REQUIRES_EH := 1 + +include $(LEVEL)/Makefile.common diff --git a/tools/llvm-ld/Optimize.cpp b/tools/llvm-ld/Optimize.cpp new file mode 100644 index 0000000..f788f06 --- /dev/null +++ b/tools/llvm-ld/Optimize.cpp @@ -0,0 +1,194 @@ +//===- Optimize.cpp - Optimize a complete program -------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements all optimization of the linked module for llvm-ld. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Module.h" +#include "llvm/PassManager.h" +#include "llvm/Analysis/Passes.h" +#include "llvm/Analysis/LoopPass.h" +#include "llvm/Analysis/Verifier.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/System/DynamicLibrary.h" +#include "llvm/Target/TargetData.h" +#include "llvm/Target/TargetMachine.h" +#include "llvm/Transforms/IPO.h" +#include "llvm/Transforms/Scalar.h" +#include "llvm/Support/PassNameParser.h" +#include "llvm/Support/PluginLoader.h" +#include <iostream> +using namespace llvm; + +// Pass Name Options as generated by the PassNameParser +static cl::list<const PassInfo*, bool, PassNameParser> + OptimizationList(cl::desc("Optimizations available:")); + +//Don't verify at the end +static cl::opt<bool> DontVerify("disable-verify", cl::ReallyHidden); + +static cl::opt<bool> DisableInline("disable-inlining", + cl::desc("Do not run the inliner pass")); + +static cl::opt<bool> +DisableOptimizations("disable-opt", + cl::desc("Do not run any optimization passes")); + +static cl::opt<bool> DisableInternalize("disable-internalize", + cl::desc("Do not mark all symbols as internal")); + +static cl::opt<bool> VerifyEach("verify-each", + cl::desc("Verify intermediate results of all passes")); + +static cl::alias ExportDynamic("export-dynamic", + cl::aliasopt(DisableInternalize), + cl::desc("Alias for -disable-internalize")); + +static cl::opt<bool> Strip("strip-all", + cl::desc("Strip all symbol info from executable")); + +static cl::alias A0("s", cl::desc("Alias for --strip-all"), + cl::aliasopt(Strip)); + +static cl::opt<bool> StripDebug("strip-debug", + cl::desc("Strip debugger symbol info from executable")); + +static cl::alias A1("S", cl::desc("Alias for --strip-debug"), + cl::aliasopt(StripDebug)); + +// A utility function that adds a pass to the pass manager but will also add +// a verifier pass after if we're supposed to verify. +static inline void addPass(PassManager &PM, Pass *P) { + // Add the pass to the pass manager... + PM.add(P); + + // If we are verifying all of the intermediate steps, add the verifier... + if (VerifyEach) + PM.add(createVerifierPass()); +} + +namespace llvm { + +/// Optimize - Perform link time optimizations. This will run the scalar +/// optimizations, any loaded plugin-optimization modules, and then the +/// inter-procedural optimizations if applicable. +void Optimize(Module* M) { + + // Instantiate the pass manager to organize the passes. + PassManager Passes; + + // If we're verifying, start off with a verification pass. + if (VerifyEach) + Passes.add(createVerifierPass()); + + // Add an appropriate TargetData instance for this module... + addPass(Passes, new TargetData(M)); + + if (!DisableOptimizations) { + // Now that composite has been compiled, scan through the module, looking + // for a main function. If main is defined, mark all other functions + // internal. + if (!DisableInternalize) + addPass(Passes, createInternalizePass(true)); + + // Propagate constants at call sites into the functions they call. This + // opens opportunities for globalopt (and inlining) by substituting function + // pointers passed as arguments to direct uses of functions. + addPass(Passes, createIPSCCPPass()); + + // Now that we internalized some globals, see if we can hack on them! + addPass(Passes, createGlobalOptimizerPass()); + + // Linking modules together can lead to duplicated global constants, only + // keep one copy of each constant... + addPass(Passes, createConstantMergePass()); + + // Remove unused arguments from functions... + addPass(Passes, createDeadArgEliminationPass()); + + // Reduce the code after globalopt and ipsccp. Both can open up significant + // simplification opportunities, and both can propagate functions through + // function pointers. When this happens, we often have to resolve varargs + // calls, etc, so let instcombine do this. + addPass(Passes, createInstructionCombiningPass()); + + if (!DisableInline) + addPass(Passes, createFunctionInliningPass()); // Inline small functions + + addPass(Passes, createPruneEHPass()); // Remove dead EH info + addPass(Passes, createGlobalOptimizerPass()); // Optimize globals again. + addPass(Passes, createGlobalDCEPass()); // Remove dead functions + + // If we didn't decide to inline a function, check to see if we can + // transform it to pass arguments by value instead of by reference. + addPass(Passes, createArgumentPromotionPass()); + + // The IPO passes may leave cruft around. Clean up after them. + addPass(Passes, createInstructionCombiningPass()); + addPass(Passes, createJumpThreadingPass()); // Thread jumps. + addPass(Passes, createScalarReplAggregatesPass()); // Break up allocas + + // Run a few AA driven optimizations here and now, to cleanup the code. + addPass(Passes, createFunctionAttrsPass()); // Add nocapture + addPass(Passes, createGlobalsModRefPass()); // IP alias analysis + + addPass(Passes, createLICMPass()); // Hoist loop invariants + addPass(Passes, createGVNPass()); // Remove redundancies + addPass(Passes, createMemCpyOptPass()); // Remove dead memcpy's + addPass(Passes, createDeadStoreEliminationPass()); // Nuke dead stores + + // Cleanup and simplify the code after the scalar optimizations. + addPass(Passes, createInstructionCombiningPass()); + + addPass(Passes, createJumpThreadingPass()); // Thread jumps. + addPass(Passes, createPromoteMemoryToRegisterPass()); // Cleanup jumpthread. + + // Delete basic blocks, which optimization passes may have killed... + addPass(Passes, createCFGSimplificationPass()); + + // Now that we have optimized the program, discard unreachable functions... + addPass(Passes, createGlobalDCEPass()); + } + + // If the -s or -S command line options were specified, strip the symbols out + // of the resulting program to make it smaller. -s and -S are GNU ld options + // that we are supporting; they alias -strip-all and -strip-debug. + if (Strip || StripDebug) + addPass(Passes, createStripSymbolsPass(StripDebug && !Strip)); + + // Create a new optimization pass for each one specified on the command line + std::auto_ptr<TargetMachine> target; + for (unsigned i = 0; i < OptimizationList.size(); ++i) { + const PassInfo *Opt = OptimizationList[i]; + if (Opt->getNormalCtor()) + addPass(Passes, Opt->getNormalCtor()()); + else + std::cerr << "llvm-ld: cannot create pass: " << Opt->getPassName() + << "\n"; + } + + // The user's passes may leave cruft around. Clean up after them them but + // only if we haven't got DisableOptimizations set + if (!DisableOptimizations) { + addPass(Passes, createInstructionCombiningPass()); + addPass(Passes, createCFGSimplificationPass()); + addPass(Passes, createAggressiveDCEPass()); + addPass(Passes, createGlobalDCEPass()); + } + + // Make sure everything is still good. + if (!DontVerify) + Passes.add(createVerifierPass()); + + // Run our queue of passes all at once now, efficiently. + Passes.run(*M); +} + +} diff --git a/tools/llvm-ld/llvm-ld.cpp b/tools/llvm-ld/llvm-ld.cpp new file mode 100644 index 0000000..fd2e0f7 --- /dev/null +++ b/tools/llvm-ld/llvm-ld.cpp @@ -0,0 +1,708 @@ +//===- llvm-ld.cpp - LLVM 'ld' compatible linker --------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This utility is intended to be compatible with GCC, and follows standard +// system 'ld' conventions. As such, the default output file is ./a.out. +// Additionally, this program outputs a shell script that is used to invoke LLI +// to execute the program. In this manner, the generated executable (a.out for +// example), is directly executable, whereas the bitcode file actually lives in +// the a.out.bc file generated by this program. Also, Force is on by default. +// +// Note that if someone (or a script) deletes the executable program generated, +// the .bc file will be left around. Considering that this is a temporary hack, +// I'm not too worried about this. +// +//===----------------------------------------------------------------------===// + +#include "llvm/LinkAllVMCore.h" +#include "llvm/Linker.h" +#include "llvm/System/Program.h" +#include "llvm/Module.h" +#include "llvm/PassManager.h" +#include "llvm/Bitcode/ReaderWriter.h" +#include "llvm/Target/TargetData.h" +#include "llvm/Target/TargetMachine.h" +#include "llvm/Target/TargetMachineRegistry.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/FileUtilities.h" +#include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/PrettyStackTrace.h" +#include "llvm/Support/Streams.h" +#include "llvm/Support/SystemUtils.h" +#include "llvm/System/Signals.h" +#include "llvm/Config/config.h" +#include <fstream> +#include <memory> +#include <cstring> +using namespace llvm; + +// Input/Output Options +static cl::list<std::string> InputFilenames(cl::Positional, cl::OneOrMore, + cl::desc("<input bitcode files>")); + +static cl::opt<std::string> OutputFilename("o", cl::init("a.out"), + cl::desc("Override output filename"), + cl::value_desc("filename")); + +static cl::opt<bool> Verbose("v", + cl::desc("Print information about actions taken")); + +static cl::list<std::string> LibPaths("L", cl::Prefix, + cl::desc("Specify a library search path"), + cl::value_desc("directory")); + +static cl::list<std::string> FrameworkPaths("F", cl::Prefix, + cl::desc("Specify a framework search path"), + cl::value_desc("directory")); + +static cl::list<std::string> Libraries("l", cl::Prefix, + cl::desc("Specify libraries to link to"), + cl::value_desc("library prefix")); + +static cl::list<std::string> Frameworks("framework", + cl::desc("Specify frameworks to link to"), + cl::value_desc("framework")); + +// Options to control the linking, optimization, and code gen processes +static cl::opt<bool> LinkAsLibrary("link-as-library", + cl::desc("Link the .bc files together as a library, not an executable")); + +static cl::alias Relink("r", cl::aliasopt(LinkAsLibrary), + cl::desc("Alias for -link-as-library")); + +static cl::opt<bool> Native("native", + cl::desc("Generate a native binary instead of a shell script")); + +static cl::opt<bool>NativeCBE("native-cbe", + cl::desc("Generate a native binary with the C backend and GCC")); + +static cl::list<std::string> PostLinkOpts("post-link-opts", + cl::value_desc("path"), + cl::desc("Run one or more optimization programs after linking")); + +static cl::list<std::string> XLinker("Xlinker", cl::value_desc("option"), + cl::desc("Pass options to the system linker")); + +// Compatibility options that llvm-ld ignores but are supported for +// compatibility with LD +static cl::opt<std::string> CO3("soname", cl::Hidden, + cl::desc("Compatibility option: ignored")); + +static cl::opt<std::string> CO4("version-script", cl::Hidden, + cl::desc("Compatibility option: ignored")); + +static cl::opt<bool> CO5("eh-frame-hdr", cl::Hidden, + cl::desc("Compatibility option: ignored")); + +static cl::opt<std::string> CO6("h", cl::Hidden, + cl::desc("Compatibility option: ignored")); + +static cl::opt<bool> CO7("start-group", cl::Hidden, + cl::desc("Compatibility option: ignored")); + +static cl::opt<bool> CO8("end-group", cl::Hidden, + cl::desc("Compatibility option: ignored")); + +static cl::opt<std::string> CO9("m", cl::Hidden, + cl::desc("Compatibility option: ignored")); + +/// This is just for convenience so it doesn't have to be passed around +/// everywhere. +static std::string progname; + +/// PrintAndExit - Prints a message to standard error and exits with error code +/// +/// Inputs: +/// Message - The message to print to standard error. +/// +static void PrintAndExit(const std::string &Message, int errcode = 1) { + cerr << progname << ": " << Message << "\n"; + llvm_shutdown(); + exit(errcode); +} + +static void PrintCommand(const std::vector<const char*> &args) { + std::vector<const char*>::const_iterator I = args.begin(), E = args.end(); + for (; I != E; ++I) + if (*I) + cout << "'" << *I << "'" << " "; + cout << "\n" << std::flush; +} + +/// CopyEnv - This function takes an array of environment variables and makes a +/// copy of it. This copy can then be manipulated any way the caller likes +/// without affecting the process's real environment. +/// +/// Inputs: +/// envp - An array of C strings containing an environment. +/// +/// Return value: +/// NULL - An error occurred. +/// +/// Otherwise, a pointer to a new array of C strings is returned. Every string +/// in the array is a duplicate of the one in the original array (i.e. we do +/// not copy the char *'s from one array to another). +/// +static char ** CopyEnv(char ** const envp) { + // Count the number of entries in the old list; + unsigned entries; // The number of entries in the old environment list + for (entries = 0; envp[entries] != NULL; entries++) + /*empty*/; + + // Add one more entry for the NULL pointer that ends the list. + ++entries; + + // If there are no entries at all, just return NULL. + if (entries == 0) + return NULL; + + // Allocate a new environment list. + char **newenv = new char* [entries]; + if ((newenv = new char* [entries]) == NULL) + return NULL; + + // Make a copy of the list. Don't forget the NULL that ends the list. + entries = 0; + while (envp[entries] != NULL) { + newenv[entries] = new char[strlen (envp[entries]) + 1]; + strcpy (newenv[entries], envp[entries]); + ++entries; + } + newenv[entries] = NULL; + + return newenv; +} + + +/// RemoveEnv - Remove the specified environment variable from the environment +/// array. +/// +/// Inputs: +/// name - The name of the variable to remove. It cannot be NULL. +/// envp - The array of environment variables. It cannot be NULL. +/// +/// Notes: +/// This is mainly done because functions to remove items from the environment +/// are not available across all platforms. In particular, Solaris does not +/// seem to have an unsetenv() function or a setenv() function (or they are +/// undocumented if they do exist). +/// +static void RemoveEnv(const char * name, char ** const envp) { + for (unsigned index=0; envp[index] != NULL; index++) { + // Find the first equals sign in the array and make it an EOS character. + char *p = strchr (envp[index], '='); + if (p == NULL) + continue; + else + *p = '\0'; + + // Compare the two strings. If they are equal, zap this string. + // Otherwise, restore it. + if (!strcmp(name, envp[index])) + *envp[index] = '\0'; + else + *p = '='; + } + + return; +} + +/// GenerateBitcode - generates a bitcode file from the module provided +void GenerateBitcode(Module* M, const std::string& FileName) { + + if (Verbose) + cout << "Generating Bitcode To " << FileName << '\n'; + + // Create the output file. + std::ios::openmode io_mode = std::ios::out | std::ios::trunc | + std::ios::binary; + std::ofstream Out(FileName.c_str(), io_mode); + if (!Out.good()) + PrintAndExit("error opening '" + FileName + "' for writing!"); + + // Ensure that the bitcode file gets removed from the disk if we get a + // terminating signal. + sys::RemoveFileOnSignal(sys::Path(FileName)); + + // Write it out + WriteBitcodeToFile(M, Out); + + // Close the bitcode file. + Out.close(); +} + +/// GenerateAssembly - generates a native assembly language source file from the +/// specified bitcode file. +/// +/// Inputs: +/// InputFilename - The name of the input bitcode file. +/// OutputFilename - The name of the file to generate. +/// llc - The pathname to use for LLC. +/// envp - The environment to use when running LLC. +/// +/// Return non-zero value on error. +/// +static int GenerateAssembly(const std::string &OutputFilename, + const std::string &InputFilename, + const sys::Path &llc, + std::string &ErrMsg ) { + // Run LLC to convert the bitcode file into assembly code. + std::vector<const char*> args; + args.push_back(llc.c_str()); + // We will use GCC to assemble the program so set the assembly syntax to AT&T, + // regardless of what the target in the bitcode file is. + args.push_back("-x86-asm-syntax=att"); + args.push_back("-f"); + args.push_back("-o"); + args.push_back(OutputFilename.c_str()); + args.push_back(InputFilename.c_str()); + args.push_back(0); + + if (Verbose) { + cout << "Generating Assembly With: \n"; + PrintCommand(args); + } + + return sys::Program::ExecuteAndWait(llc, &args[0], 0, 0, 0, 0, &ErrMsg); +} + +/// GenerateCFile - generates a C source file from the specified bitcode file. +static int GenerateCFile(const std::string &OutputFile, + const std::string &InputFile, + const sys::Path &llc, + std::string& ErrMsg) { + // Run LLC to convert the bitcode file into C. + std::vector<const char*> args; + args.push_back(llc.c_str()); + args.push_back("-march=c"); + args.push_back("-f"); + args.push_back("-o"); + args.push_back(OutputFile.c_str()); + args.push_back(InputFile.c_str()); + args.push_back(0); + + if (Verbose) { + cout << "Generating C Source With: \n"; + PrintCommand(args); + } + + return sys::Program::ExecuteAndWait(llc, &args[0], 0, 0, 0, 0, &ErrMsg); +} + +/// GenerateNative - generates a native object file from the +/// specified bitcode file. +/// +/// Inputs: +/// InputFilename - The name of the input bitcode file. +/// OutputFilename - The name of the file to generate. +/// NativeLinkItems - The native libraries, files, code with which to link +/// LibPaths - The list of directories in which to find libraries. +/// FrameworksPaths - The list of directories in which to find frameworks. +/// Frameworks - The list of frameworks (dynamic libraries) +/// gcc - The pathname to use for GGC. +/// envp - A copy of the process's current environment. +/// +/// Outputs: +/// None. +/// +/// Returns non-zero value on error. +/// +static int GenerateNative(const std::string &OutputFilename, + const std::string &InputFilename, + const Linker::ItemList &LinkItems, + const sys::Path &gcc, char ** const envp, + std::string& ErrMsg) { + // Remove these environment variables from the environment of the + // programs that we will execute. It appears that GCC sets these + // environment variables so that the programs it uses can configure + // themselves identically. + // + // However, when we invoke GCC below, we want it to use its normal + // configuration. Hence, we must sanitize its environment. + char ** clean_env = CopyEnv(envp); + if (clean_env == NULL) + return 1; + RemoveEnv("LIBRARY_PATH", clean_env); + RemoveEnv("COLLECT_GCC_OPTIONS", clean_env); + RemoveEnv("GCC_EXEC_PREFIX", clean_env); + RemoveEnv("COMPILER_PATH", clean_env); + RemoveEnv("COLLECT_GCC", clean_env); + + + // Run GCC to assemble and link the program into native code. + // + // Note: + // We can't just assemble and link the file with the system assembler + // and linker because we don't know where to put the _start symbol. + // GCC mysteriously knows how to do it. + std::vector<std::string> args; + args.push_back(gcc.c_str()); + args.push_back("-fno-strict-aliasing"); + args.push_back("-O3"); + args.push_back("-o"); + args.push_back(OutputFilename); + args.push_back(InputFilename); + + // Add in the library and framework paths + for (unsigned index = 0; index < LibPaths.size(); index++) { + args.push_back("-L" + LibPaths[index]); + } + for (unsigned index = 0; index < FrameworkPaths.size(); index++) { + args.push_back("-F" + FrameworkPaths[index]); + } + + // Add the requested options + for (unsigned index = 0; index < XLinker.size(); index++) + args.push_back(XLinker[index]); + + // Add in the libraries to link. + for (unsigned index = 0; index < LinkItems.size(); index++) + if (LinkItems[index].first != "crtend") { + if (LinkItems[index].second) + args.push_back("-l" + LinkItems[index].first); + else + args.push_back(LinkItems[index].first); + } + + // Add in frameworks to link. + for (unsigned index = 0; index < Frameworks.size(); index++) { + args.push_back("-framework"); + args.push_back(Frameworks[index]); + } + + // Now that "args" owns all the std::strings for the arguments, call the c_str + // method to get the underlying string array. We do this game so that the + // std::string array is guaranteed to outlive the const char* array. + std::vector<const char *> Args; + for (unsigned i = 0, e = args.size(); i != e; ++i) + Args.push_back(args[i].c_str()); + Args.push_back(0); + + if (Verbose) { + cout << "Generating Native Executable With:\n"; + PrintCommand(Args); + } + + // Run the compiler to assembly and link together the program. + int R = sys::Program::ExecuteAndWait( + gcc, &Args[0], (const char**)clean_env, 0, 0, 0, &ErrMsg); + delete [] clean_env; + return R; +} + +/// EmitShellScript - Output the wrapper file that invokes the JIT on the LLVM +/// bitcode file for the program. +static void EmitShellScript(char **argv) { + if (Verbose) + cout << "Emitting Shell Script\n"; +#if defined(_WIN32) || defined(__CYGWIN__) + // Windows doesn't support #!/bin/sh style shell scripts in .exe files. To + // support windows systems, we copy the llvm-stub.exe executable from the + // build tree to the destination file. + std::string ErrMsg; + sys::Path llvmstub = FindExecutable("llvm-stub.exe", argv[0]); + if (llvmstub.isEmpty()) + PrintAndExit("Could not find llvm-stub.exe executable!"); + + if (0 != sys::CopyFile(sys::Path(OutputFilename), llvmstub, &ErrMsg)) + PrintAndExit(ErrMsg); + + return; +#endif + + // Output the script to start the program... + std::ofstream Out2(OutputFilename.c_str()); + if (!Out2.good()) + PrintAndExit("error opening '" + OutputFilename + "' for writing!"); + + Out2 << "#!/bin/sh\n"; + // Allow user to setenv LLVMINTERP if lli is not in their PATH. + Out2 << "lli=${LLVMINTERP-lli}\n"; + Out2 << "exec $lli \\\n"; + // gcc accepts -l<lib> and implicitly searches /lib and /usr/lib. + LibPaths.push_back("/lib"); + LibPaths.push_back("/usr/lib"); + LibPaths.push_back("/usr/X11R6/lib"); + // We don't need to link in libc! In fact, /usr/lib/libc.so may not be a + // shared object at all! See RH 8: plain text. + std::vector<std::string>::iterator libc = + std::find(Libraries.begin(), Libraries.end(), "c"); + if (libc != Libraries.end()) Libraries.erase(libc); + // List all the shared object (native) libraries this executable will need + // on the command line, so that we don't have to do this manually! + for (std::vector<std::string>::iterator i = Libraries.begin(), + e = Libraries.end(); i != e; ++i) { + // try explicit -L arguments first: + sys::Path FullLibraryPath; + for (cl::list<std::string>::const_iterator P = LibPaths.begin(), + E = LibPaths.end(); P != E; ++P) { + FullLibraryPath = *P; + FullLibraryPath.appendComponent("lib" + *i); + FullLibraryPath.appendSuffix(&(LTDL_SHLIB_EXT[1])); + if (!FullLibraryPath.isEmpty()) { + if (!FullLibraryPath.isDynamicLibrary()) { + // Not a native shared library; mark as invalid + FullLibraryPath = sys::Path(); + } else break; + } + } + if (FullLibraryPath.isEmpty()) + FullLibraryPath = sys::Path::FindLibrary(*i); + if (!FullLibraryPath.isEmpty()) + Out2 << " -load=" << FullLibraryPath.toString() << " \\\n"; + } + Out2 << " $0.bc ${1+\"$@\"}\n"; + Out2.close(); +} + +// BuildLinkItems -- This function generates a LinkItemList for the LinkItems +// linker function by combining the Files and Libraries in the order they were +// declared on the command line. +static void BuildLinkItems( + Linker::ItemList& Items, + const cl::list<std::string>& Files, + const cl::list<std::string>& Libraries) { + + // Build the list of linkage items for LinkItems. + + cl::list<std::string>::const_iterator fileIt = Files.begin(); + cl::list<std::string>::const_iterator libIt = Libraries.begin(); + + int libPos = -1, filePos = -1; + while ( libIt != Libraries.end() || fileIt != Files.end() ) { + if (libIt != Libraries.end()) + libPos = Libraries.getPosition(libIt - Libraries.begin()); + else + libPos = -1; + if (fileIt != Files.end()) + filePos = Files.getPosition(fileIt - Files.begin()); + else + filePos = -1; + + if (filePos != -1 && (libPos == -1 || filePos < libPos)) { + // Add a source file + Items.push_back(std::make_pair(*fileIt++, false)); + } else if (libPos != -1 && (filePos == -1 || libPos < filePos)) { + // Add a library + Items.push_back(std::make_pair(*libIt++, true)); + } + } +} + +// Rightly this should go in a header file but it just seems such a waste. +namespace llvm { +extern void Optimize(Module*); +} + +int main(int argc, char **argv, char **envp) { + // Print a stack trace if we signal out. + sys::PrintStackTraceOnErrorSignal(); + PrettyStackTraceProgram X(argc, argv); + + llvm_shutdown_obj Y; // Call llvm_shutdown() on exit. + try { + // Initial global variable above for convenience printing of program name. + progname = sys::Path(argv[0]).getBasename(); + + // Parse the command line options + cl::ParseCommandLineOptions(argc, argv, "llvm linker\n"); + + // Construct a Linker (now that Verbose is set) + Linker TheLinker(progname, OutputFilename, Verbose); + + // Keep track of the native link items (versus the bitcode items) + Linker::ItemList NativeLinkItems; + + // Add library paths to the linker + TheLinker.addPaths(LibPaths); + TheLinker.addSystemPaths(); + + // Remove any consecutive duplicates of the same library... + Libraries.erase(std::unique(Libraries.begin(), Libraries.end()), + Libraries.end()); + + if (LinkAsLibrary) { + std::vector<sys::Path> Files; + for (unsigned i = 0; i < InputFilenames.size(); ++i ) + Files.push_back(sys::Path(InputFilenames[i])); + if (TheLinker.LinkInFiles(Files)) + return 1; // Error already printed + + // The libraries aren't linked in but are noted as "dependent" in the + // module. + for (cl::list<std::string>::const_iterator I = Libraries.begin(), + E = Libraries.end(); I != E ; ++I) { + TheLinker.getModule()->addLibrary(*I); + } + } else { + // Build a list of the items from our command line + Linker::ItemList Items; + BuildLinkItems(Items, InputFilenames, Libraries); + + // Link all the items together + if (TheLinker.LinkInItems(Items, NativeLinkItems) ) + return 1; // Error already printed + } + + std::auto_ptr<Module> Composite(TheLinker.releaseModule()); + + // Optimize the module + Optimize(Composite.get()); + +#if defined(_WIN32) || defined(__CYGWIN__) + if (!LinkAsLibrary) { + // Default to "a.exe" instead of "a.out". + if (OutputFilename.getNumOccurrences() == 0) + OutputFilename = "a.exe"; + + // If there is no suffix add an "exe" one. + sys::Path ExeFile( OutputFilename ); + if (ExeFile.getSuffix() == "") { + ExeFile.appendSuffix("exe"); + OutputFilename = ExeFile.toString(); + } + } +#endif + + // Generate the bitcode for the optimized module. + std::string RealBitcodeOutput = OutputFilename; + + if (!LinkAsLibrary) RealBitcodeOutput += ".bc"; + GenerateBitcode(Composite.get(), RealBitcodeOutput); + + // If we are not linking a library, generate either a native executable + // or a JIT shell script, depending upon what the user wants. + if (!LinkAsLibrary) { + // If the user wants to run a post-link optimization, run it now. + if (!PostLinkOpts.empty()) { + std::vector<std::string> opts = PostLinkOpts; + for (std::vector<std::string>::iterator I = opts.begin(), + E = opts.end(); I != E; ++I) { + sys::Path prog(*I); + if (!prog.canExecute()) { + prog = sys::Program::FindProgramByName(*I); + if (prog.isEmpty()) + PrintAndExit(std::string("Optimization program '") + *I + + "' is not found or not executable."); + } + // Get the program arguments + sys::Path tmp_output("opt_result"); + std::string ErrMsg; + if (tmp_output.createTemporaryFileOnDisk(true, &ErrMsg)) + PrintAndExit(ErrMsg); + + const char* args[4]; + args[0] = I->c_str(); + args[1] = RealBitcodeOutput.c_str(); + args[2] = tmp_output.c_str(); + args[3] = 0; + if (0 == sys::Program::ExecuteAndWait(prog, args, 0,0,0,0, &ErrMsg)) { + if (tmp_output.isBitcodeFile() || tmp_output.isBitcodeFile()) { + sys::Path target(RealBitcodeOutput); + target.eraseFromDisk(); + if (tmp_output.renamePathOnDisk(target, &ErrMsg)) + PrintAndExit(ErrMsg, 2); + } else + PrintAndExit("Post-link optimization output is not bitcode"); + } else { + PrintAndExit(ErrMsg); + } + } + } + + // If the user wants to generate a native executable, compile it from the + // bitcode file. + // + // Otherwise, create a script that will run the bitcode through the JIT. + if (Native) { + // Name of the Assembly Language output file + sys::Path AssemblyFile ( OutputFilename); + AssemblyFile.appendSuffix("s"); + + // Mark the output files for removal if we get an interrupt. + sys::RemoveFileOnSignal(AssemblyFile); + sys::RemoveFileOnSignal(sys::Path(OutputFilename)); + + // Determine the locations of the llc and gcc programs. + sys::Path llc = FindExecutable("llc", argv[0]); + if (llc.isEmpty()) + PrintAndExit("Failed to find llc"); + + sys::Path gcc = FindExecutable("gcc", argv[0]); + if (gcc.isEmpty()) + PrintAndExit("Failed to find gcc"); + + // Generate an assembly language file for the bitcode. + std::string ErrMsg; + if (0 != GenerateAssembly(AssemblyFile.toString(), RealBitcodeOutput, + llc, ErrMsg)) + PrintAndExit(ErrMsg); + + if (0 != GenerateNative(OutputFilename, AssemblyFile.toString(), + NativeLinkItems, gcc, envp, ErrMsg)) + PrintAndExit(ErrMsg); + + // Remove the assembly language file. + AssemblyFile.eraseFromDisk(); + } else if (NativeCBE) { + sys::Path CFile (OutputFilename); + CFile.appendSuffix("cbe.c"); + + // Mark the output files for removal if we get an interrupt. + sys::RemoveFileOnSignal(CFile); + sys::RemoveFileOnSignal(sys::Path(OutputFilename)); + + // Determine the locations of the llc and gcc programs. + sys::Path llc = FindExecutable("llc", argv[0]); + if (llc.isEmpty()) + PrintAndExit("Failed to find llc"); + + sys::Path gcc = FindExecutable("gcc", argv[0]); + if (gcc.isEmpty()) + PrintAndExit("Failed to find gcc"); + + // Generate an assembly language file for the bitcode. + std::string ErrMsg; + if (0 != GenerateCFile( + CFile.toString(), RealBitcodeOutput, llc, ErrMsg)) + PrintAndExit(ErrMsg); + + if (0 != GenerateNative(OutputFilename, CFile.toString(), + NativeLinkItems, gcc, envp, ErrMsg)) + PrintAndExit(ErrMsg); + + // Remove the assembly language file. + CFile.eraseFromDisk(); + + } else { + EmitShellScript(argv); + } + + // Make the script executable... + std::string ErrMsg; + if (sys::Path(OutputFilename).makeExecutableOnDisk(&ErrMsg)) + PrintAndExit(ErrMsg); + + // Make the bitcode file readable and directly executable in LLEE as well + if (sys::Path(RealBitcodeOutput).makeExecutableOnDisk(&ErrMsg)) + PrintAndExit(ErrMsg); + + if (sys::Path(RealBitcodeOutput).makeReadableOnDisk(&ErrMsg)) + PrintAndExit(ErrMsg); + } + } catch (const std::string& msg) { + PrintAndExit(msg,2); + } catch (...) { + PrintAndExit("Unexpected unknown exception occurred.", 2); + } + + // Graceful exit + return 0; +} diff --git a/tools/llvm-link/CMakeLists.txt b/tools/llvm-link/CMakeLists.txt new file mode 100644 index 0000000..69a435e --- /dev/null +++ b/tools/llvm-link/CMakeLists.txt @@ -0,0 +1,5 @@ +set(LLVM_LINK_COMPONENTS linker bitreader bitwriter) + +add_llvm_tool(llvm-link + llvm-link.cpp + ) diff --git a/tools/llvm-link/Makefile b/tools/llvm-link/Makefile new file mode 100644 index 0000000..ddc7a59 --- /dev/null +++ b/tools/llvm-link/Makefile @@ -0,0 +1,17 @@ +##===- tools/llvm-link/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 = llvm-link +LINK_COMPONENTS = linker bitreader bitwriter + +# This tool has no plugins, optimize startup time. +TOOL_NO_EXPORTS = 1 + +include $(LEVEL)/Makefile.common diff --git a/tools/llvm-link/llvm-link.cpp b/tools/llvm-link/llvm-link.cpp new file mode 100644 index 0000000..15850f4 --- /dev/null +++ b/tools/llvm-link/llvm-link.cpp @@ -0,0 +1,154 @@ +//===- llvm-link.cpp - Low-level LLVM linker ------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This utility may be invoked in the following manner: +// llvm-link a.bc b.bc c.bc -o x.bc +// +//===----------------------------------------------------------------------===// + +#include "llvm/Linker.h" +#include "llvm/Module.h" +#include "llvm/Analysis/Verifier.h" +#include "llvm/Bitcode/ReaderWriter.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/PrettyStackTrace.h" +#include "llvm/Support/Streams.h" +#include "llvm/System/Signals.h" +#include "llvm/System/Path.h" +#include <fstream> +#include <iostream> +#include <memory> +using namespace llvm; + +static cl::list<std::string> +InputFilenames(cl::Positional, cl::OneOrMore, + cl::desc("<input bitcode files>")); + +static cl::opt<std::string> +OutputFilename("o", cl::desc("Override output filename"), cl::init("-"), + cl::value_desc("filename")); + +static cl::opt<bool> Force("f", cl::desc("Overwrite output files")); + +static cl::opt<bool> +Verbose("v", cl::desc("Print information about actions taken")); + +static cl::opt<bool> +DumpAsm("d", cl::desc("Print assembly as linked"), cl::Hidden); + +// LoadFile - Read the specified bitcode file in and return it. This routine +// searches the link path for the specified file to try to find it... +// +static inline std::auto_ptr<Module> LoadFile(const std::string &FN) { + sys::Path Filename; + if (!Filename.set(FN)) { + cerr << "Invalid file name: '" << FN << "'\n"; + return std::auto_ptr<Module>(); + } + + std::string ErrorMessage; + if (Filename.exists()) { + if (Verbose) cerr << "Loading '" << Filename.c_str() << "'\n"; + Module* Result = 0; + + const std::string &FNStr = Filename.toString(); + if (MemoryBuffer *Buffer = MemoryBuffer::getFileOrSTDIN(FNStr, + &ErrorMessage)) { + Result = ParseBitcodeFile(Buffer, &ErrorMessage); + delete Buffer; + } + if (Result) return std::auto_ptr<Module>(Result); // Load successful! + + if (Verbose) { + cerr << "Error opening bitcode file: '" << Filename.c_str() << "'"; + if (ErrorMessage.size()) cerr << ": " << ErrorMessage; + cerr << "\n"; + } + } else { + cerr << "Bitcode file: '" << Filename.c_str() << "' does not exist.\n"; + } + + return std::auto_ptr<Module>(); +} + +int main(int argc, char **argv) { + // Print a stack trace if we signal out. + sys::PrintStackTraceOnErrorSignal(); + PrettyStackTraceProgram X(argc, argv); + + llvm_shutdown_obj Y; // Call llvm_shutdown() on exit. + cl::ParseCommandLineOptions(argc, argv, "llvm linker\n"); + + unsigned BaseArg = 0; + std::string ErrorMessage; + + std::auto_ptr<Module> Composite(LoadFile(InputFilenames[BaseArg])); + if (Composite.get() == 0) { + cerr << argv[0] << ": error loading file '" + << InputFilenames[BaseArg] << "'\n"; + return 1; + } + + for (unsigned i = BaseArg+1; i < InputFilenames.size(); ++i) { + std::auto_ptr<Module> M(LoadFile(InputFilenames[i])); + if (M.get() == 0) { + cerr << argv[0] << ": error loading file '" <<InputFilenames[i]<< "'\n"; + return 1; + } + + if (Verbose) cerr << "Linking in '" << InputFilenames[i] << "'\n"; + + if (Linker::LinkModules(Composite.get(), M.get(), &ErrorMessage)) { + cerr << argv[0] << ": link error in '" << InputFilenames[i] + << "': " << ErrorMessage << "\n"; + return 1; + } + } + + // TODO: Iterate over the -l list and link in any modules containing + // global symbols that have not been resolved so far. + + if (DumpAsm) cerr << "Here's the assembly:\n" << *Composite.get(); + + // FIXME: cout is not binary! + std::ostream *Out = &std::cout; // Default to printing to stdout... + if (OutputFilename != "-") { + if (!Force && std::ifstream(OutputFilename.c_str())) { + // If force is not specified, make sure not to overwrite a file! + cerr << argv[0] << ": error opening '" << OutputFilename + << "': file exists!\n" + << "Use -f command line argument to force output\n"; + return 1; + } + std::ios::openmode io_mode = std::ios::out | std::ios::trunc | + std::ios::binary; + Out = new std::ofstream(OutputFilename.c_str(), io_mode); + if (!Out->good()) { + cerr << argv[0] << ": error opening '" << OutputFilename << "'!\n"; + return 1; + } + + // Make sure that the Out file gets unlinked from the disk if we get a + // SIGINT + sys::RemoveFileOnSignal(sys::Path(OutputFilename)); + } + + if (verifyModule(*Composite.get())) { + cerr << argv[0] << ": linked module is broken!\n"; + return 1; + } + + if (Verbose) cerr << "Writing bitcode...\n"; + WriteBitcodeToFile(Composite.get(), *Out); + + if (Out != &std::cout) delete Out; + return 0; +} diff --git a/tools/llvm-nm/CMakeLists.txt b/tools/llvm-nm/CMakeLists.txt new file mode 100644 index 0000000..45cf1b6 --- /dev/null +++ b/tools/llvm-nm/CMakeLists.txt @@ -0,0 +1,5 @@ +set(LLVM_LINK_COMPONENTS archive bitreader) + +add_llvm_tool(llvm-nm + llvm-nm.cpp + ) diff --git a/tools/llvm-nm/Makefile b/tools/llvm-nm/Makefile new file mode 100644 index 0000000..ecf5f8c --- /dev/null +++ b/tools/llvm-nm/Makefile @@ -0,0 +1,17 @@ +##===- tools/llvm-nm/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 = llvm-nm +LINK_COMPONENTS = archive bitreader + +# This tool has no plugins, optimize startup time. +TOOL_NO_EXPORTS = 1 + +include $(LEVEL)/Makefile.common diff --git a/tools/llvm-nm/llvm-nm.cpp b/tools/llvm-nm/llvm-nm.cpp new file mode 100644 index 0000000..324e0f6 --- /dev/null +++ b/tools/llvm-nm/llvm-nm.cpp @@ -0,0 +1,192 @@ +//===-- llvm-nm.cpp - Symbol table dumping utility for llvm ---------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This program is a utility that works like traditional Unix "nm", +// that is, it prints out the names of symbols in a bitcode file, +// along with some information about each symbol. +// +// This "nm" does not print symbols' addresses. It supports many of +// the features of GNU "nm", including its different output formats. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Module.h" +#include "llvm/Bitcode/ReaderWriter.h" +#include "llvm/Bitcode/Archive.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/PrettyStackTrace.h" +#include "llvm/System/Signals.h" +#include <algorithm> +#include <cctype> +#include <cerrno> +#include <cstring> +#include <iostream> +using namespace llvm; + +namespace { + enum OutputFormatTy { bsd, sysv, posix }; + cl::opt<OutputFormatTy> + OutputFormat("format", + cl::desc("Specify output format"), + cl::values(clEnumVal(bsd, "BSD format"), + clEnumVal(sysv, "System V format"), + clEnumVal(posix, "POSIX.2 format"), + clEnumValEnd), cl::init(bsd)); + cl::alias OutputFormat2("f", cl::desc("Alias for --format"), + cl::aliasopt(OutputFormat)); + + cl::list<std::string> + InputFilenames(cl::Positional, cl::desc("<input bitcode files>"), + cl::ZeroOrMore); + + cl::opt<bool> UndefinedOnly("undefined-only", + cl::desc("Show only undefined symbols")); + cl::alias UndefinedOnly2("u", cl::desc("Alias for --undefined-only"), + cl::aliasopt(UndefinedOnly)); + + cl::opt<bool> DefinedOnly("defined-only", + cl::desc("Show only defined symbols")); + + cl::opt<bool> ExternalOnly("extern-only", + cl::desc("Show only external symbols")); + cl::alias ExternalOnly2("g", cl::desc("Alias for --extern-only"), + cl::aliasopt(ExternalOnly)); + + cl::opt<bool> BSDFormat("B", cl::desc("Alias for --format=bsd")); + cl::opt<bool> POSIXFormat("P", cl::desc("Alias for --format=posix")); + + bool MultipleFiles = false; + + std::string ToolName; +} + +static char TypeCharForSymbol(GlobalValue &GV) { + if (GV.isDeclaration()) return 'U'; + if (GV.hasLinkOnceLinkage()) return 'C'; + if (GV.hasCommonLinkage()) return 'C'; + if (GV.hasWeakLinkage()) return 'W'; + if (isa<Function>(GV) && GV.hasInternalLinkage()) return 't'; + if (isa<Function>(GV)) return 'T'; + if (isa<GlobalVariable>(GV) && GV.hasInternalLinkage()) return 'd'; + if (isa<GlobalVariable>(GV)) return 'D'; + if (const GlobalAlias *GA = dyn_cast<GlobalAlias>(&GV)) { + const GlobalValue *AliasedGV = GA->getAliasedGlobal(); + if (isa<Function>(AliasedGV)) return 'T'; + if (isa<GlobalVariable>(AliasedGV)) return 'D'; + } + return '?'; +} + +static void DumpSymbolNameForGlobalValue(GlobalValue &GV) { + // Private linkage and available_externally linkage don't exist in symtab. + if (GV.hasPrivateLinkage() || GV.hasAvailableExternallyLinkage()) return; + + const std::string SymbolAddrStr = " "; // Not used yet... + char TypeChar = TypeCharForSymbol(GV); + if ((TypeChar != 'U') && UndefinedOnly) + return; + if ((TypeChar == 'U') && DefinedOnly) + return; + if (GV.hasLocalLinkage () && ExternalOnly) + return; + if (OutputFormat == posix) { + std::cout << GV.getName () << " " << TypeCharForSymbol(GV) << " " + << SymbolAddrStr << "\n"; + } else if (OutputFormat == bsd) { + std::cout << SymbolAddrStr << " " << TypeCharForSymbol(GV) << " " + << GV.getName () << "\n"; + } else if (OutputFormat == sysv) { + std::string PaddedName (GV.getName ()); + while (PaddedName.length () < 20) + PaddedName += " "; + std::cout << PaddedName << "|" << SymbolAddrStr << "| " + << TypeCharForSymbol(GV) + << " | | | |\n"; + } +} + +static void DumpSymbolNamesFromModule(Module *M) { + const std::string &Filename = M->getModuleIdentifier (); + if (OutputFormat == posix && MultipleFiles) { + std::cout << Filename << ":\n"; + } else if (OutputFormat == bsd && MultipleFiles) { + std::cout << "\n" << Filename << ":\n"; + } else if (OutputFormat == sysv) { + std::cout << "\n\nSymbols from " << Filename << ":\n\n" + << "Name Value Class Type" + << " Size Line Section\n"; + } + std::for_each (M->begin(), M->end(), DumpSymbolNameForGlobalValue); + std::for_each (M->global_begin(), M->global_end(), + DumpSymbolNameForGlobalValue); + std::for_each (M->alias_begin(), M->alias_end(), + DumpSymbolNameForGlobalValue); +} + +static void DumpSymbolNamesFromFile(std::string &Filename) { + std::string ErrorMessage; + sys::Path aPath(Filename); + // Note: Currently we do not support reading an archive from stdin. + if (Filename == "-" || aPath.isBitcodeFile()) { + std::auto_ptr<MemoryBuffer> Buffer( + MemoryBuffer::getFileOrSTDIN(Filename, &ErrorMessage)); + Module *Result = 0; + if (Buffer.get()) + Result = ParseBitcodeFile(Buffer.get(), &ErrorMessage); + + if (Result) + DumpSymbolNamesFromModule(Result); + else { + std::cerr << ToolName << ": " << Filename << ": " << ErrorMessage << "\n"; + return; + } + + } else if (aPath.isArchive()) { + std::string ErrMsg; + Archive* archive = Archive::OpenAndLoad(sys::Path(Filename), &ErrorMessage); + if (!archive) + std::cerr << ToolName << ": " << Filename << ": " << ErrorMessage << "\n"; + std::vector<Module *> Modules; + if (archive->getAllModules(Modules, &ErrorMessage)) { + std::cerr << ToolName << ": " << Filename << ": " << ErrorMessage << "\n"; + return; + } + MultipleFiles = true; + std::for_each (Modules.begin(), Modules.end(), DumpSymbolNamesFromModule); + } else { + std::cerr << ToolName << ": " << Filename << ": " + << "unrecognizable file type\n"; + return; + } +} + +int main(int argc, char **argv) { + // Print a stack trace if we signal out. + sys::PrintStackTraceOnErrorSignal(); + PrettyStackTraceProgram X(argc, argv); + + llvm_shutdown_obj Y; // Call llvm_shutdown() on exit. + cl::ParseCommandLineOptions(argc, argv, "llvm symbol table dumper\n"); + + ToolName = argv[0]; + if (BSDFormat) OutputFormat = bsd; + if (POSIXFormat) OutputFormat = posix; + + switch (InputFilenames.size()) { + case 0: InputFilenames.push_back("-"); + case 1: break; + default: MultipleFiles = true; + } + + std::for_each(InputFilenames.begin(), InputFilenames.end(), + DumpSymbolNamesFromFile); + return 0; +} diff --git a/tools/llvm-prof/CMakeLists.txt b/tools/llvm-prof/CMakeLists.txt new file mode 100644 index 0000000..9a51150 --- /dev/null +++ b/tools/llvm-prof/CMakeLists.txt @@ -0,0 +1,6 @@ +set(LLVM_LINK_COMPONENTS bitreader analysis) +set(LLVM_REQUIRES_EH 1) + +add_llvm_tool(llvm-prof + llvm-prof.cpp + ) diff --git a/tools/llvm-prof/Makefile b/tools/llvm-prof/Makefile new file mode 100644 index 0000000..3c4948e --- /dev/null +++ b/tools/llvm-prof/Makefile @@ -0,0 +1,18 @@ +##===- tools/llvm-prof/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 = llvm-prof +LINK_COMPONENTS = bitreader analysis +REQUIRES_EH := 1 + +# This tool has no plugins, optimize startup time. +TOOL_NO_EXPORTS = 1 + +include $(LEVEL)/Makefile.common diff --git a/tools/llvm-prof/llvm-prof.cpp b/tools/llvm-prof/llvm-prof.cpp new file mode 100644 index 0000000..119dc1a --- /dev/null +++ b/tools/llvm-prof/llvm-prof.cpp @@ -0,0 +1,256 @@ +//===- llvm-prof.cpp - Read in and process llvmprof.out data files --------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This tools is meant for use with the various LLVM profiling instrumentation +// passes. It reads in the data file produced by executing an instrumented +// program, and outputs a nice report. +// +//===----------------------------------------------------------------------===// + +#include "llvm/InstrTypes.h" +#include "llvm/Module.h" +#include "llvm/Assembly/AsmAnnotationWriter.h" +#include "llvm/Analysis/ProfileInfoLoader.h" +#include "llvm/Bitcode/ReaderWriter.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/PrettyStackTrace.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/System/Signals.h" +#include <algorithm> +#include <iostream> +#include <iomanip> +#include <map> +#include <set> + +using namespace llvm; + +namespace { + cl::opt<std::string> + BitcodeFile(cl::Positional, cl::desc("<program bitcode file>"), + cl::Required); + + cl::opt<std::string> + ProfileDataFile(cl::Positional, cl::desc("<llvmprof.out file>"), + cl::Optional, cl::init("llvmprof.out")); + + cl::opt<bool> + PrintAnnotatedLLVM("annotated-llvm", + cl::desc("Print LLVM code with frequency annotations")); + cl::alias PrintAnnotated2("A", cl::desc("Alias for --annotated-llvm"), + cl::aliasopt(PrintAnnotatedLLVM)); + cl::opt<bool> + PrintAllCode("print-all-code", + cl::desc("Print annotated code for the entire program")); +} + +// PairSecondSort - A sorting predicate to sort by the second element of a pair. +template<class T> +struct PairSecondSortReverse + : public std::binary_function<std::pair<T, unsigned>, + std::pair<T, unsigned>, bool> { + bool operator()(const std::pair<T, unsigned> &LHS, + const std::pair<T, unsigned> &RHS) const { + return LHS.second > RHS.second; + } +}; + +namespace { + class ProfileAnnotator : public AssemblyAnnotationWriter { + std::map<const Function *, unsigned> &FuncFreqs; + std::map<const BasicBlock*, unsigned> &BlockFreqs; + std::map<ProfileInfoLoader::Edge, unsigned> &EdgeFreqs; + public: + ProfileAnnotator(std::map<const Function *, unsigned> &FF, + std::map<const BasicBlock*, unsigned> &BF, + std::map<ProfileInfoLoader::Edge, unsigned> &EF) + : FuncFreqs(FF), BlockFreqs(BF), EdgeFreqs(EF) {} + + virtual void emitFunctionAnnot(const Function *F, raw_ostream &OS) { + OS << ";;; %" << F->getName() << " called " << FuncFreqs[F] + << " times.\n;;;\n"; + } + virtual void emitBasicBlockStartAnnot(const BasicBlock *BB, + raw_ostream &OS) { + if (BlockFreqs.empty()) return; + if (unsigned Count = BlockFreqs[BB]) + OS << "\t;;; Basic block executed " << Count << " times.\n"; + else + OS << "\t;;; Never executed!\n"; + } + + virtual void emitBasicBlockEndAnnot(const BasicBlock *BB, raw_ostream &OS) { + if (EdgeFreqs.empty()) return; + + // Figure out how many times each successor executed. + std::vector<std::pair<const BasicBlock*, unsigned> > SuccCounts; + const TerminatorInst *TI = BB->getTerminator(); + + std::map<ProfileInfoLoader::Edge, unsigned>::iterator I = + EdgeFreqs.lower_bound(std::make_pair(const_cast<BasicBlock*>(BB), 0U)); + for (; I != EdgeFreqs.end() && I->first.first == BB; ++I) + if (I->second) + SuccCounts.push_back(std::make_pair(TI->getSuccessor(I->first.second), + I->second)); + if (!SuccCounts.empty()) { + OS << "\t;;; Out-edge counts:"; + for (unsigned i = 0, e = SuccCounts.size(); i != e; ++i) + OS << " [" << SuccCounts[i].second << " -> " + << SuccCounts[i].first->getName() << "]"; + OS << "\n"; + } + } + }; +} + + +int main(int argc, char **argv) { + // Print a stack trace if we signal out. + sys::PrintStackTraceOnErrorSignal(); + PrettyStackTraceProgram X(argc, argv); + + llvm_shutdown_obj Y; // Call llvm_shutdown() on exit. + try { + cl::ParseCommandLineOptions(argc, argv, "llvm profile dump decoder\n"); + + // Read in the bitcode file... + std::string ErrorMessage; + Module *M = 0; + if (MemoryBuffer *Buffer = MemoryBuffer::getFileOrSTDIN(BitcodeFile, + &ErrorMessage)) { + M = ParseBitcodeFile(Buffer, &ErrorMessage); + delete Buffer; + } + if (M == 0) { + std::cerr << argv[0] << ": " << BitcodeFile << ": " + << ErrorMessage << "\n"; + return 1; + } + + // Read the profiling information + ProfileInfoLoader PI(argv[0], ProfileDataFile, *M); + + std::map<const Function *, unsigned> FuncFreqs; + std::map<const BasicBlock*, unsigned> BlockFreqs; + std::map<ProfileInfoLoader::Edge, unsigned> EdgeFreqs; + + // Output a report. Eventually, there will be multiple reports selectable on + // the command line, for now, just keep things simple. + + // Emit the most frequent function table... + std::vector<std::pair<Function*, unsigned> > FunctionCounts; + PI.getFunctionCounts(FunctionCounts); + FuncFreqs.insert(FunctionCounts.begin(), FunctionCounts.end()); + + // Sort by the frequency, backwards. + sort(FunctionCounts.begin(), FunctionCounts.end(), + PairSecondSortReverse<Function*>()); + + uint64_t TotalExecutions = 0; + for (unsigned i = 0, e = FunctionCounts.size(); i != e; ++i) + TotalExecutions += FunctionCounts[i].second; + + std::cout << "===" << std::string(73, '-') << "===\n" + << "LLVM profiling output for execution"; + if (PI.getNumExecutions() != 1) std::cout << "s"; + std::cout << ":\n"; + + for (unsigned i = 0, e = PI.getNumExecutions(); i != e; ++i) { + std::cout << " "; + if (e != 1) std::cout << i+1 << ". "; + std::cout << PI.getExecution(i) << "\n"; + } + + std::cout << "\n===" << std::string(73, '-') << "===\n"; + std::cout << "Function execution frequencies:\n\n"; + + // Print out the function frequencies... + std::cout << " ## Frequency\n"; + for (unsigned i = 0, e = FunctionCounts.size(); i != e; ++i) { + if (FunctionCounts[i].second == 0) { + std::cout << "\n NOTE: " << e-i << " function" << + (e-i-1 ? "s were" : " was") << " never executed!\n"; + break; + } + + std::cout << std::setw(3) << i+1 << ". " + << std::setw(5) << FunctionCounts[i].second << "/" + << TotalExecutions << " " + << FunctionCounts[i].first->getName().c_str() << "\n"; + } + + std::set<Function*> FunctionsToPrint; + + // If we have block count information, print out the LLVM module with + // frequency annotations. + if (PI.hasAccurateBlockCounts()) { + std::vector<std::pair<BasicBlock*, unsigned> > Counts; + PI.getBlockCounts(Counts); + + TotalExecutions = 0; + for (unsigned i = 0, e = Counts.size(); i != e; ++i) + TotalExecutions += Counts[i].second; + + // Sort by the frequency, backwards. + sort(Counts.begin(), Counts.end(), + PairSecondSortReverse<BasicBlock*>()); + + std::cout << "\n===" << std::string(73, '-') << "===\n"; + std::cout << "Top 20 most frequently executed basic blocks:\n\n"; + + // Print out the function frequencies... + std::cout <<" ## %% \tFrequency\n"; + unsigned BlocksToPrint = Counts.size(); + if (BlocksToPrint > 20) BlocksToPrint = 20; + for (unsigned i = 0; i != BlocksToPrint; ++i) { + if (Counts[i].second == 0) break; + Function *F = Counts[i].first->getParent(); + std::cout << std::setw(3) << i+1 << ". " + << std::setw(5) << std::setprecision(2) + << Counts[i].second/(double)TotalExecutions*100 << "% " + << std::setw(5) << Counts[i].second << "/" + << TotalExecutions << "\t" + << F->getName().c_str() << "() - " + << Counts[i].first->getName().c_str() << "\n"; + FunctionsToPrint.insert(F); + } + + BlockFreqs.insert(Counts.begin(), Counts.end()); + } + + if (PI.hasAccurateEdgeCounts()) { + std::vector<std::pair<ProfileInfoLoader::Edge, unsigned> > Counts; + PI.getEdgeCounts(Counts); + EdgeFreqs.insert(Counts.begin(), Counts.end()); + } + + if (PrintAnnotatedLLVM || PrintAllCode) { + std::cout << "\n===" << std::string(73, '-') << "===\n"; + std::cout << "Annotated LLVM code for the module:\n\n"; + + ProfileAnnotator PA(FuncFreqs, BlockFreqs, EdgeFreqs); + + if (FunctionsToPrint.empty() || PrintAllCode) + M->print(std::cout, &PA); + else + // Print just a subset of the functions. + for (std::set<Function*>::iterator I = FunctionsToPrint.begin(), + E = FunctionsToPrint.end(); I != E; ++I) + (*I)->print(std::cout, &PA); + } + + return 0; + } catch (const std::string& msg) { + std::cerr << argv[0] << ": " << msg << "\n"; + } catch (...) { + std::cerr << argv[0] << ": Unexpected unknown exception occurred.\n"; + } + return 1; +} diff --git a/tools/llvm-ranlib/CMakeLists.txt b/tools/llvm-ranlib/CMakeLists.txt new file mode 100644 index 0000000..3116d2e --- /dev/null +++ b/tools/llvm-ranlib/CMakeLists.txt @@ -0,0 +1,6 @@ +set(LLVM_LINK_COMPONENTS archive) +set(LLVM_REQUIRES_EH 1) + +add_llvm_tool(llvm-ranlib + llvm-ranlib.cpp + ) diff --git a/tools/llvm-ranlib/Makefile b/tools/llvm-ranlib/Makefile new file mode 100644 index 0000000..46a10e6 --- /dev/null +++ b/tools/llvm-ranlib/Makefile @@ -0,0 +1,18 @@ +##===- tools/llvm-ranlib/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 = llvm-ranlib +LINK_COMPONENTS = archive +REQUIRES_EH := 1 + +# This tool has no plugins, optimize startup time. +TOOL_NO_EXPORTS = 1 + +include $(LEVEL)/Makefile.common diff --git a/tools/llvm-ranlib/llvm-ranlib.cpp b/tools/llvm-ranlib/llvm-ranlib.cpp new file mode 100644 index 0000000..7210610 --- /dev/null +++ b/tools/llvm-ranlib/llvm-ranlib.cpp @@ -0,0 +1,98 @@ +//===-- llvm-ranlib.cpp - LLVM archive index generator --------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Adds or updates an index (symbol table) for an LLVM archive file. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Module.h" +#include "llvm/Bitcode/Archive.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/PrettyStackTrace.h" +#include "llvm/System/Signals.h" +#include <iostream> +#include <iomanip> +#include <memory> + +using namespace llvm; + +// llvm-ar operation code and modifier flags +static cl::opt<std::string> +ArchiveName(cl::Positional, cl::Optional, cl::desc("<archive-file>")); + +static cl::opt<bool> +Verbose("verbose",cl::Optional,cl::init(false), + cl::desc("Print the symbol table")); + +// printSymbolTable - print out the archive's symbol table. +void printSymbolTable(Archive* TheArchive) { + std::cout << "\nArchive Symbol Table:\n"; + const Archive::SymTabType& symtab = TheArchive->getSymbolTable(); + for (Archive::SymTabType::const_iterator I=symtab.begin(), E=symtab.end(); + I != E; ++I ) { + unsigned offset = TheArchive->getFirstFileOffset() + I->second; + std::cout << " " << std::setw(9) << offset << "\t" << I->first <<"\n"; + } +} + +int main(int argc, char **argv) { + // Print a stack trace if we signal out. + llvm::sys::PrintStackTraceOnErrorSignal(); + llvm::PrettyStackTraceProgram X(argc, argv); + + llvm_shutdown_obj Y; // Call llvm_shutdown() on exit. + + // Have the command line options parsed and handle things + // like --help and --version. + cl::ParseCommandLineOptions(argc, argv, + "LLVM Archive Index Generator (llvm-ranlib)\n\n" + " This program adds or updates an index of bitcode symbols\n" + " to an LLVM archive file." + ); + + int exitCode = 0; + + // Make sure we don't exit with "unhandled exception". + try { + + // Check the path name of the archive + sys::Path ArchivePath; + if (!ArchivePath.set(ArchiveName)) + throw std::string("Archive name invalid: ") + ArchiveName; + + // Make sure it exists, we don't create empty archives + if (!ArchivePath.exists()) + throw std::string("Archive file does not exist"); + + std::string err_msg; + std::auto_ptr<Archive> + AutoArchive(Archive::OpenAndLoad(ArchivePath,&err_msg)); + Archive* TheArchive = AutoArchive.get(); + if (!TheArchive) + throw err_msg; + + if (TheArchive->writeToDisk(true, false, false, &err_msg )) + throw err_msg; + + if (Verbose) + printSymbolTable(TheArchive); + + } catch (const char* msg) { + std::cerr << argv[0] << ": " << msg << "\n\n"; + exitCode = 1; + } catch (const std::string& msg) { + std::cerr << argv[0] << ": " << msg << "\n"; + exitCode = 2; + } catch (...) { + std::cerr << argv[0] << ": An unexpected unknown exception occurred.\n"; + exitCode = 3; + } + return exitCode; +} diff --git a/tools/llvm-stub/CMakeLists.txt b/tools/llvm-stub/CMakeLists.txt new file mode 100644 index 0000000..a98dc9e --- /dev/null +++ b/tools/llvm-stub/CMakeLists.txt @@ -0,0 +1,3 @@ +add_llvm_tool(llvm-stub + llvm-stub.c + ) diff --git a/tools/llvm-stub/Makefile b/tools/llvm-stub/Makefile new file mode 100644 index 0000000..7ffe149 --- /dev/null +++ b/tools/llvm-stub/Makefile @@ -0,0 +1,13 @@ +##===- tools/llvm-stub/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 = llvm-stub +include $(LEVEL)/Makefile.common + diff --git a/tools/llvm-stub/llvm-stub.c b/tools/llvm-stub/llvm-stub.c new file mode 100644 index 0000000..e5624a9 --- /dev/null +++ b/tools/llvm-stub/llvm-stub.c @@ -0,0 +1,74 @@ +/*===- llvm-stub.c - Stub executable to run llvm bitcode files ------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This tool is used by the gccld program to enable transparent execution of +// bitcode files by the user. Specifically, gccld outputs two files when asked +// to compile a <program> file: +// 1. It outputs the LLVM bitcode file to <program>.bc +// 2. It outputs a stub executable that runs lli on <program>.bc +// +// This allows the end user to just say ./<program> and have the JIT executed +// automatically. On unix, the stub executable emitted is actually a bourne +// shell script that does the forwarding. Windows does not like #!/bin/sh +// programs in .exe files, so we make it an actual program, defined here. +// +//===----------------------------------------------------------------------===*/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "llvm/Config/config.h" + +#if defined(HAVE_UNISTD_H) && !defined(_MSC_VER) +#include <unistd.h> +#endif + +#ifdef _WIN32 +#include <process.h> +#include <io.h> +#endif + +int main(int argc, char** argv) { + const char *Interp = getenv("LLVMINTERP"); + const char **Args; + if (Interp == 0) Interp = "lli"; + + /* Set up the command line options to pass to the JIT. */ + Args = (const char**)malloc(sizeof(char*) * (argc+2)); + /* argv[0] is the JIT */ + Args[0] = Interp; + +#ifdef LLVM_ON_WIN32 + { + int len = strlen(argv[0]); + if (len < 4 || strcmp(argv[0] + len - 4, ".exe") != 0) { + /* .exe suffix is stripped off of argv[0] if the executable was run on the + * command line without one. Put it back on. + */ + argv[0] = strcat(strcpy((char*)malloc(len + 5), argv[0]), ".exe"); + } + } +#endif + + /* argv[1] is argv[0] + ".bc". */ + Args[1] = strcat(strcpy((char*)malloc(strlen(argv[0])+4), argv[0]), ".bc"); + + /* The rest of the args are as before. */ + memcpy(Args+2, argv+1, sizeof(char*)*argc); + + /* Run the JIT. */ + execvp(Interp, (char *const*)Args); + + /* if _execv returns, the JIT could not be started. */ + fprintf(stderr, "Could not execute the LLVM JIT. Either add 'lli' to your" + " path, or set the\ninterpreter you want to use in the LLVMINTERP " + "environment variable.\n"); + return 1; +} diff --git a/tools/llvmc/CMakeLists.txt b/tools/llvmc/CMakeLists.txt new file mode 100644 index 0000000..bebaaeb --- /dev/null +++ b/tools/llvmc/CMakeLists.txt @@ -0,0 +1,4 @@ +# 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 new file mode 100644 index 0000000..df91728 --- /dev/null +++ b/tools/llvmc/Makefile @@ -0,0 +1,17 @@ +##===- tools/llvmc/Makefile --------------------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open +# Source License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LEVEL = ../.. + +export LLVMC_BASED_DRIVER_NAME = llvmc +export LLVMC_BUILTIN_PLUGINS = Base Clang + +DIRS = plugins driver + +include $(LEVEL)/Makefile.common diff --git a/tools/llvmc/doc/LLVMC-Reference.rst b/tools/llvmc/doc/LLVMC-Reference.rst new file mode 100644 index 0000000..d99fa0c --- /dev/null +++ b/tools/llvmc/doc/LLVMC-Reference.rst @@ -0,0 +1,667 @@ +=================================== +Customizing LLVMC: Reference Manual +=================================== +.. + This file was automatically generated by rst2html. + Please do not edit directly! + The ReST source lives in the directory 'tools/llvmc/doc'. + +.. contents:: + +.. raw:: html + + <div class="doc_author"> + <p>Written by <a href="mailto:foldr@codedgers.com">Mikhail Glushenkov</a></p> + </div> + +Introduction +============ + +LLVMC is a generic compiler driver, designed to be customizable and +extensible. It plays the same role for LLVM as the ``gcc`` program +does for GCC - LLVMC's job is essentially to transform a set of input +files into a set of targets depending on configuration rules and user +options. What makes LLVMC different is that these transformation rules +are completely customizable - in fact, LLVMC knows nothing about the +specifics of transformation (even the command-line options are mostly +not hard-coded) and regards the transformation structure as an +abstract graph. The structure of this graph is completely determined +by plugins, which can be either statically or dynamically linked. This +makes it possible to easily adapt LLVMC for other purposes - for +example, as a build tool for game resources. + +Because LLVMC employs TableGen_ as its configuration language, you +need to be familiar with it to customize LLVMC. + +.. _TableGen: http://llvm.cs.uiuc.edu/docs/TableGenFundamentals.html + + +Compiling with LLVMC +==================== + +LLVMC tries hard to be as compatible with ``gcc`` as possible, +although there are some small differences. Most of the time, however, +you shouldn't be able to notice them:: + + $ # This works as expected: + $ llvmc -O3 -Wall hello.cpp + $ ./a.out + hello + +One nice feature of LLVMC is that one doesn't have to distinguish +between different compilers for different languages (think ``g++`` and +``gcc``) - the right toolchain is chosen automatically based on input +language names (which are, in turn, determined from file +extensions). If you want to force files ending with ".c" to compile as +C++, use the ``-x`` option, just like you would do it with ``gcc``:: + + $ # hello.c is really a C++ file + $ llvmc -x c++ hello.c + $ ./a.out + hello + +On the other hand, when using LLVMC as a linker to combine several C++ +object files you should provide the ``--linker`` option since it's +impossible for LLVMC to choose the right linker in that case:: + + $ llvmc -c hello.cpp + $ llvmc hello.o + [A lot of link-time errors skipped] + $ llvmc --linker=c++ hello.o + $ ./a.out + hello + +By default, LLVMC uses ``llvm-gcc`` to compile the source code. It is +also possible to choose the work-in-progress ``clang`` compiler with +the ``-clang`` option. + + +Predefined options +================== + +LLVMC has some built-in options that can't be overridden in the +configuration libraries: + +* ``-o FILE`` - Output file name. + +* ``-x LANGUAGE`` - Specify the language of the following input files + until the next -x option. + +* ``-load PLUGIN_NAME`` - Load the specified plugin DLL. Example: + ``-load $LLVM_DIR/Release/lib/LLVMCSimple.so``. + +* ``-v`` - Enable verbose mode, i.e. print out all executed commands. + +* ``--check-graph`` - Check the compilation for common errors like mismatched + output/input language names, multiple default edges and cycles. Because of + plugins, these checks can't be performed at compile-time. Exit with code zero if + no errors were found, and return the number of found errors otherwise. Hidden + option, useful for debugging LLVMC plugins. + +* ``--view-graph`` - Show a graphical representation of the compilation graph + and exit. Requires that you have ``dot`` and ``gv`` programs installed. Hidden + option, useful for debugging LLVMC plugins. + +* ``--write-graph`` - Write a ``compilation-graph.dot`` file in the current + directory with the compilation graph description in Graphviz format (identical + to the file used by the ``--view-graph`` option). The ``-o`` option can be used + to set the output file name. Hidden option, useful for debugging LLVMC plugins. + +* ``--save-temps`` - Write temporary files to the current directory + and do not delete them on exit. Hidden option, useful for debugging. + +* ``--help``, ``--help-hidden``, ``--version`` - These options have + their standard meaning. + + +Compiling LLVMC plugins +======================= + +It's easiest to start working on your own LLVMC plugin by copying the +skeleton project which lives under ``$LLVMC_DIR/plugins/Simple``:: + + $ cd $LLVMC_DIR/plugins + $ cp -r Simple MyPlugin + $ cd MyPlugin + $ ls + Makefile PluginMain.cpp Simple.td + +As you can see, our basic plugin consists of only two files (not +counting the build script). ``Simple.td`` contains TableGen +description of the compilation graph; its format is documented in the +following sections. ``PluginMain.cpp`` is just a helper file used to +compile the auto-generated C++ code produced from TableGen source. It +can also contain hook definitions (see `below`__). + +__ hooks_ + +The first thing that you should do is to change the ``LLVMC_PLUGIN`` +variable in the ``Makefile`` to avoid conflicts (since this variable +is used to name the resulting library):: + + LLVMC_PLUGIN=MyPlugin + +It is also a good idea to rename ``Simple.td`` to something less +generic:: + + $ mv Simple.td MyPlugin.td + +Note that the plugin source directory must be placed under +``$LLVMC_DIR/plugins`` to make use of the existing build +infrastructure. To build a version of the LLVMC executable called +``mydriver`` with your plugin compiled in, use the following command:: + + $ cd $LLVMC_DIR + $ make BUILTIN_PLUGINS=MyPlugin DRIVER_NAME=mydriver + +To build your plugin as a dynamic library, just ``cd`` to its source +directory and run ``make``. The resulting file will be called +``LLVMC$(LLVMC_PLUGIN).$(DLL_EXTENSION)`` (in our case, +``LLVMCMyPlugin.so``). This library can be then loaded in with the +``-load`` option. Example:: + + $ cd $LLVMC_DIR/plugins/Simple + $ make + $ llvmc -load $LLVM_DIR/Release/lib/LLVMCSimple.so + +Sometimes, you will want a 'bare-bones' version of LLVMC that has no +built-in plugins. It can be compiled with the following command:: + + $ cd $LLVMC_DIR + $ make BUILTIN_PLUGINS="" + + +Customizing LLVMC: the compilation graph +======================================== + +Each TableGen configuration file should include the common +definitions:: + + include "llvm/CompilerDriver/Common.td" + +Internally, LLVMC stores information about possible source +transformations in form of a graph. Nodes in this graph represent +tools, and edges between two nodes represent a transformation path. A +special "root" node is used to mark entry points for the +transformations. LLVMC also assigns a weight to each edge (more on +this later) to choose between several alternative edges. + +The definition of the compilation graph (see file +``plugins/Base/Base.td`` for an example) is just a list of edges:: + + def CompilationGraph : CompilationGraph<[ + Edge<"root", "llvm_gcc_c">, + Edge<"root", "llvm_gcc_assembler">, + ... + + Edge<"llvm_gcc_c", "llc">, + Edge<"llvm_gcc_cpp", "llc">, + ... + + OptionalEdge<"llvm_gcc_c", "opt", (case (switch_on "opt"), + (inc_weight))>, + OptionalEdge<"llvm_gcc_cpp", "opt", (case (switch_on "opt"), + (inc_weight))>, + ... + + OptionalEdge<"llvm_gcc_assembler", "llvm_gcc_cpp_linker", + (case (input_languages_contain "c++"), (inc_weight), + (or (parameter_equals "linker", "g++"), + (parameter_equals "linker", "c++")), (inc_weight))>, + ... + + ]>; + +As you can see, the edges can be either default or optional, where +optional edges are differentiated by an additional ``case`` expression +used to calculate the weight of this edge. Notice also that we refer +to tools via their names (as strings). This makes it possible to add +edges to an existing compilation graph in plugins without having to +know about all tool definitions used in the graph. + +The default edges are assigned a weight of 1, and optional edges get a +weight of 0 + 2*N where N is the number of tests that evaluated to +true in the ``case`` expression. It is also possible to provide an +integer parameter to ``inc_weight`` and ``dec_weight`` - in this case, +the weight is increased (or decreased) by the provided value instead +of the default 2. It is also possible to change the default weight of +an optional edge by using the ``default`` clause of the ``case`` +construct. + +When passing an input file through the graph, LLVMC picks the edge +with the maximum weight. To avoid ambiguity, there should be only one +default edge between two nodes (with the exception of the root node, +which gets a special treatment - there you are allowed to specify one +default edge *per language*). + +When multiple plugins are loaded, their compilation graphs are merged +together. Since multiple edges that have the same end nodes are not +allowed (i.e. the graph is not a multigraph), an edge defined in +several plugins will be replaced by the definition from the plugin +that was loaded last. Plugin load order can be controlled by using the +plugin priority feature described above. + +To get a visual representation of the compilation graph (useful for +debugging), run ``llvmc --view-graph``. You will need ``dot`` and +``gsview`` installed for this to work properly. + +Describing options +================== + +Command-line options that the plugin supports are defined by using an +``OptionList``:: + + def Options : OptionList<[ + (switch_option "E", (help "Help string")), + (alias_option "quiet", "q") + ... + ]>; + +As you can see, the option list is just a list of DAGs, where each DAG +is an option description consisting of the option name and some +properties. A plugin can define more than one option list (they are +all merged together in the end), which can be handy if one wants to +separate option groups syntactically. + +* Possible option types: + + - ``switch_option`` - a simple boolean switch without arguments, for example + ``-O2`` or ``-time``. At most one occurrence is allowed. + + - ``parameter_option`` - option that takes one argument, for example + ``-std=c99``. It is also allowed to use spaces instead of the equality + sign: ``-std c99``. At most one occurrence is allowed. + + - ``parameter_list_option`` - same as the above, but more than one option + occurence is allowed. + + - ``prefix_option`` - same as the parameter_option, but the option name and + argument do not have to be separated. Example: ``-ofile``. This can be also + specified as ``-o file``; however, ``-o=file`` will be parsed incorrectly + (``=file`` will be interpreted as option value). At most one occurrence is + allowed. + + - ``prefix_list_option`` - same as the above, but more than one occurence of + the option is allowed; example: ``-lm -lpthread``. + + - ``alias_option`` - a special option type for creating aliases. Unlike other + option types, aliases are not allowed to have any properties besides the + aliased option name. Usage example: ``(alias_option "preprocess", "E")`` + + +* Possible option properties: + + - ``help`` - help string associated with this option. Used for ``--help`` + output. + + - ``required`` - this option must be specified exactly once (or, in case of + the list options without the ``multi_val`` property, at least + once). Incompatible with ``zero_or_one`` and ``one_or_more``. + + - ``one_or_more`` - the option must be specified at least one time. Useful + only for list options in conjunction with ``multi_val``; for ordinary lists + it is synonymous with ``required``. Incompatible with ``required`` and + ``zero_or_one``. + + - ``zero_or_one`` - the option can be specified zero or one times. Useful + only for list options in conjunction with ``multi_val``. Incompatible with + ``required`` and ``one_or_more``. + + - ``hidden`` - the description of this option will not appear in + the ``--help`` output (but will appear in the ``--help-hidden`` + output). + + - ``really_hidden`` - the option will not be mentioned in any help + output. + + - ``multi_val n`` - this option takes *n* arguments (can be useful in some + special cases). Usage example: ``(parameter_list_option "foo", (multi_val + 3))``. Only list options can have this attribute; you can, however, use + the ``one_or_more`` and ``zero_or_one`` properties. + + - ``extern`` - this option is defined in some other plugin, see below. + +External options +---------------- + +Sometimes, when linking several plugins together, one plugin needs to +access options defined in some other plugin. Because of the way +options are implemented, such options must be marked as +``extern``. This is what the ``extern`` option property is +for. Example:: + + ... + (switch_option "E", (extern)) + ... + +See also the section on plugin `priorities`__. + +__ priorities_ + +.. _case: + +Conditional evaluation +====================== + +The 'case' construct is the main means by which programmability is +achieved in LLVMC. It can be used to calculate edge weights, program +actions and modify the shell commands to be executed. The 'case' +expression is designed after the similarly-named construct in +functional languages and takes the form ``(case (test_1), statement_1, +(test_2), statement_2, ... (test_N), statement_N)``. The statements +are evaluated only if the corresponding tests evaluate to true. + +Examples:: + + // Edge weight calculation + + // Increases edge weight by 5 if "-A" is provided on the + // command-line, and by 5 more if "-B" is also provided. + (case + (switch_on "A"), (inc_weight 5), + (switch_on "B"), (inc_weight 5)) + + + // Tool command line specification + + // Evaluates to "cmdline1" if the option "-A" is provided on the + // command line; to "cmdline2" if "-B" is provided; + // otherwise to "cmdline3". + + (case + (switch_on "A"), "cmdline1", + (switch_on "B"), "cmdline2", + (default), "cmdline3") + +Note the slight difference in 'case' expression handling in contexts +of edge weights and command line specification - in the second example +the value of the ``"B"`` switch is never checked when switch ``"A"`` is +enabled, and the whole expression always evaluates to ``"cmdline1"`` in +that case. + +Case expressions can also be nested, i.e. the following is legal:: + + (case (switch_on "E"), (case (switch_on "o"), ..., (default), ...) + (default), ...) + +You should, however, try to avoid doing that because it hurts +readability. It is usually better to split tool descriptions and/or +use TableGen inheritance instead. + +* Possible tests are: + + - ``switch_on`` - Returns true if a given command-line switch is + provided by the user. Example: ``(switch_on "opt")``. + + - ``parameter_equals`` - Returns true if a command-line parameter equals + a given value. + Example: ``(parameter_equals "W", "all")``. + + - ``element_in_list`` - Returns true if a command-line parameter + list contains a given value. + Example: ``(parameter_in_list "l", "pthread")``. + + - ``input_languages_contain`` - Returns true if a given language + belongs to the current input language set. + Example: ``(input_languages_contain "c++")``. + + - ``in_language`` - Evaluates to true if the input file language + equals to the argument. At the moment works only with ``cmd_line`` + and ``actions`` (on non-join nodes). + Example: ``(in_language "c++")``. + + - ``not_empty`` - Returns true if a given option (which should be + either a parameter or a parameter list) is set by the + user. + Example: ``(not_empty "o")``. + + - ``empty`` - The opposite of ``not_empty``. Equivalent to ``(not (not_empty + X))``. Provided for convenience. + + - ``default`` - Always evaluates to true. Should always be the last + test in the ``case`` expression. + + - ``and`` - A standard logical combinator that returns true iff all + of its arguments return true. Used like this: ``(and (test1), + (test2), ... (testN))``. Nesting of ``and`` and ``or`` is allowed, + but not encouraged. + + - ``or`` - Another logical combinator that returns true only if any + one of its arguments returns true. Example: ``(or (test1), + (test2), ... (testN))``. + + +Writing a tool description +========================== + +As was said earlier, nodes in the compilation graph represent tools, +which are described separately. A tool definition looks like this +(taken from the ``include/llvm/CompilerDriver/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-explanatory. The ``sink`` +property means that this tool should be passed all command-line +options that aren't mentioned in the option list. + +The complete list of all currently implemented tool properties follows. + +* Possible tool properties: + + - ``in_language`` - input language name. Can be either a string or a + list, in case the tool supports multiple input languages. + + - ``out_language`` - output language name. Tools are not allowed to + have multiple output languages. + + - ``output_suffix`` - output file suffix. Can also be changed + dynamically, see documentation on actions. + + - ``cmd_line`` - the actual command used to run the tool. You can + use ``$INFILE`` and ``$OUTFILE`` variables, output redirection + with ``>``, hook invocations (``$CALL``), environment variables + (via ``$ENV``) and the ``case`` construct. + + - ``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. + + - ``actions`` - A single big ``case`` expression that specifies how + this tool reacts on command-line options (described in more detail + below). + +Actions +------- + +A tool often needs to react to command-line options, and this is +precisely what the ``actions`` property is for. The next example +illustrates this feature:: + + def llvm_gcc_linker : Tool<[ + (in_language "object-code"), + (out_language "executable"), + (output_suffix "out"), + (cmd_line "llvm-gcc $INFILE -o $OUTFILE"), + (join), + (actions (case (not_empty "L"), (forward "L"), + (not_empty "l"), (forward "l"), + (not_empty "dummy"), + [(append_cmd "-dummy1"), (append_cmd "-dummy2")]) + ]>; + +The ``actions`` tool property is implemented on top of the omnipresent +``case`` expression. It associates one or more different *actions* +with given conditions - in the example, the actions are ``forward``, +which forwards a given option unchanged, and ``append_cmd``, which +appends a given string to the tool execution command. Multiple actions +can be associated with a single condition by using a list of actions +(used in the example to append some dummy options). The same ``case`` +construct can also be used in the ``cmd_line`` property to modify the +tool command line. + +The "join" property used in the example means that this tool behaves +like a linker. + +The list of all possible actions follows. + +* Possible actions: + + - ``append_cmd`` - append a string to the tool invocation + command. + Example: ``(case (switch_on "pthread"), (append_cmd + "-lpthread"))`` + + - ``error` - exit with error. + Example: ``(error "Mixing -c and -S is not allowed!")``. + + - ``forward`` - forward an option unchanged. + Example: ``(forward "Wall")``. + + - ``forward_as`` - Change the name of an option, but forward the + argument unchanged. + Example: ``(forward_as "O0", "--disable-optimization")``. + + - ``output_suffix`` - modify the output suffix of this + tool. + Example: ``(output_suffix "i")``. + + - ``stop_compilation`` - stop compilation after this tool processes + its input. Used without arguments. + + - ``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. + Example: ``(unpack_values "Wa,")``. + +Language map +============ + +If you are adding support for a new language to LLVMC, you'll need to +modify the language map, which defines mappings from file extensions +to language names. It is used to choose the proper toolchain(s) for a +given input file set. Language map definition looks like this:: + + def LanguageMap : LanguageMap< + [LangToSuffixes<"c++", ["cc", "cp", "cxx", "cpp", "CPP", "c++", "C"]>, + LangToSuffixes<"c", ["c"]>, + ... + ]>; + +For example, without those definitions the following command wouldn't work:: + + $ llvmc hello.cpp + llvmc: Unknown suffix: cpp + +The language map entries should be added only for tools that are +linked with the root node. Since tools are not allowed to have +multiple output languages, for nodes "inside" the graph the input and +output languages should match. This is enforced at compile-time. + + +More advanced topics +==================== + +.. _hooks: + +Hooks and environment variables +------------------------------- + +Normally, LLVMC executes programs from the system ``PATH``. Sometimes, +this is not sufficient: for example, we may want to specify tool paths +or names in the configuration file. This can be easily achieved via +the hooks mechanism. To write your own hooks, just add their +definitions to the ``PluginMain.cpp`` or drop a ``.cpp`` file into the +your plugin directory. Hooks should live in the ``hooks`` namespace +and have the signature ``std::string hooks::MyHookName ([const char* +Arg0 [ const char* Arg2 [, ...]]])``. They can be used from the +``cmd_line`` tool property:: + + (cmd_line "$CALL(MyHook)/path/to/file -o $CALL(AnotherHook)") + +To pass arguments to hooks, use the following syntax:: + + (cmd_line "$CALL(MyHook, 'Arg1', 'Arg2', 'Arg # 3')/path/to/file -o1 -o2") + +It is also possible to use environment variables in the same manner:: + + (cmd_line "$ENV(VAR1)/path/to/file -o $ENV(VAR2)") + +To change the command line string based on user-provided options use +the ``case`` expression (documented `above`__):: + + (cmd_line + (case + (switch_on "E"), + "llvm-g++ -E -x c $INFILE -o $OUTFILE", + (default), + "llvm-g++ -c -x c $INFILE -o $OUTFILE -emit-llvm")) + +__ case_ + +.. _priorities: + +How plugins are loaded +---------------------- + +It is possible for LLVMC plugins to depend on each other. For example, +one can create edges between nodes defined in some other plugin. To +make this work, however, that plugin should be loaded first. To +achieve this, the concept of plugin priority was introduced. By +default, every plugin has priority zero; to specify the priority +explicitly, put the following line in your plugin's TableGen file:: + + def Priority : PluginPriority<$PRIORITY_VALUE>; + # Where PRIORITY_VALUE is some integer > 0 + +Plugins are loaded in order of their (increasing) priority, starting +with 0. Therefore, the plugin with the highest priority value will be +loaded last. + +Debugging +--------- + +When writing LLVMC plugins, it can be useful to get a visual view of +the resulting compilation graph. This can be achieved via the command +line option ``--view-graph``. This command assumes that Graphviz_ and +Ghostview_ are installed. There is also a ``--write-graph`` option that +creates a Graphviz source file (``compilation-graph.dot``) in the +current directory. + +Another useful ``llvmc`` option is ``--check-graph``. It checks the +compilation graph for common errors like mismatched output/input +language names, multiple default edges and cycles. These checks can't +be performed at compile-time because the plugins can load code +dynamically. When invoked with ``--check-graph``, ``llvmc`` doesn't +perform any compilation tasks and returns the number of encountered +errors as its status code. + +.. _Graphviz: http://www.graphviz.org/ +.. _Ghostview: http://pages.cs.wisc.edu/~ghost/ + +.. raw:: html + + <hr /> + <address> + <a href="http://jigsaw.w3.org/css-validator/check/referer"> + <img src="http://jigsaw.w3.org/css-validator/images/vcss-blue" + alt="Valid CSS" /></a> + <a href="http://validator.w3.org/check?uri=referer"> + <img src="http://www.w3.org/Icons/valid-xhtml10-blue" + alt="Valid XHTML 1.0 Transitional"/></a> + + <a href="mailto:foldr@codedgers.com">Mikhail Glushenkov</a><br /> + <a href="http://llvm.org">LLVM Compiler Infrastructure</a><br /> + + Last modified: $Date: 2008-12-11 11:34:48 -0600 (Thu, 11 Dec 2008) $ + </address> diff --git a/tools/llvmc/doc/LLVMC-Tutorial.rst b/tools/llvmc/doc/LLVMC-Tutorial.rst new file mode 100644 index 0000000..6f06477 --- /dev/null +++ b/tools/llvmc/doc/LLVMC-Tutorial.rst @@ -0,0 +1,124 @@ +====================== +Tutorial - Using LLVMC +====================== +.. + This file was automatically generated by rst2html. + Please do not edit directly! + The ReST source lives in the directory 'tools/llvmc/doc'. + +.. contents:: + +.. raw:: html + + <div class="doc_author"> + <p>Written by <a href="mailto:foldr@codedgers.com">Mikhail Glushenkov</a></p> + </div> + +Introduction +============ + +LLVMC is a generic compiler driver, which plays the same role for LLVM +as the ``gcc`` program does for GCC - the difference being that LLVMC +is designed to be more adaptable and easier to customize. Most of +LLVMC functionality is implemented via plugins, which can be loaded +dynamically or compiled in. This tutorial describes the basic usage +and configuration of LLVMC. + + +Compiling with LLVMC +==================== + +In general, LLVMC tries to be command-line compatible with ``gcc`` as +much as possible, so most of the familiar options work:: + + $ llvmc -O3 -Wall hello.cpp + $ ./a.out + hello + +This will invoke ``llvm-g++`` under the hood (you can see which +commands are executed by using the ``-v`` option). For further help on +command-line LLVMC usage, refer to the ``llvmc --help`` output. + + +Using LLVMC to generate toolchain drivers +========================================= + +LLVMC plugins are written mostly using TableGen_, so you need to +be familiar with it to get anything done. + +.. _TableGen: http://llvm.cs.uiuc.edu/docs/TableGenFundamentals.html + +Start by compiling ``plugins/Simple/Simple.td``, which is a primitive +wrapper for ``gcc``:: + + $ cd $LLVM_DIR/tools/llvmc + $ make DRIVER_NAME=mygcc BUILTIN_PLUGINS=Simple + $ cat > hello.c + [...] + $ mygcc hello.c + $ ./hello.out + Hello + +Here we link our plugin with the LLVMC core statically to form an +executable file called ``mygcc``. It is also possible to build our +plugin as a standalone dynamic library; this is described in the +reference manual. + +Contents of the file ``Simple.td`` look like this:: + + // Include common definitions + include "llvm/CompilerDriver/Common.td" + + // Tool descriptions + def gcc : Tool< + [(in_language "c"), + (out_language "executable"), + (output_suffix "out"), + (cmd_line "gcc $INFILE -o $OUTFILE"), + (sink) + ]>; + + // Language map + def LanguageMap : LanguageMap<[LangToSuffixes<"c", ["c"]>]>; + + // Compilation graph + def CompilationGraph : CompilationGraph<[Edge<"root", "gcc">]>; + +As you can see, this file consists of three parts: tool descriptions, +language map, and the compilation graph definition. + +At the heart of LLVMC is the idea of a compilation graph: vertices in +this graph are tools, and edges represent a transformation path +between two tools (for example, assembly source produced by the +compiler can be transformed into executable code by an assembler). The +compilation graph is basically a list of edges; a special node named +``root`` is used to mark graph entry points. + +Tool descriptions are represented as property lists: most properties +in the example above should be self-explanatory; the ``sink`` property +means that all options lacking an explicit description should be +forwarded to this tool. + +The ``LanguageMap`` associates a language name with a list of suffixes +and is used for deciding which toolchain corresponds to a given input +file. + +To learn more about LLVMC customization, refer to the reference +manual and plugin source code in the ``plugins`` directory. + +.. raw:: html + + <hr /> + <address> + <a href="http://jigsaw.w3.org/css-validator/check/referer"> + <img src="http://jigsaw.w3.org/css-validator/images/vcss-blue" + alt="Valid CSS" /></a> + <a href="http://validator.w3.org/check?uri=referer"> + <img src="http://www.w3.org/Icons/valid-xhtml10-blue" + alt="Valid XHTML 1.0 Transitional"/></a> + + <a href="mailto:foldr@codedgers.com">Mikhail Glushenkov</a><br /> + <a href="http://llvm.org">LLVM Compiler Infrastructure</a><br /> + + Last modified: $Date: 2008-12-11 11:34:48 -0600 (Thu, 11 Dec 2008) $ + </address> diff --git a/tools/llvmc/doc/Makefile b/tools/llvmc/doc/Makefile new file mode 100644 index 0000000..65e6b9b --- /dev/null +++ b/tools/llvmc/doc/Makefile @@ -0,0 +1,27 @@ +##===- tools/llvmc/doc/Makefile ----------------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LEVEL=../../.. +include $(LEVEL)/Makefile.config + +DOC_DIR=../../../docs +RST2HTML=rst2html --stylesheet=llvm.css --link-stylesheet + +all : LLVMC-Reference.html LLVMC-Tutorial.html + $(CP) LLVMC-Reference.html $(DOC_DIR)/CompilerDriver.html + $(CP) LLVMC-Tutorial.html $(DOC_DIR)/CompilerDriverTutorial.html + +LLVMC-Tutorial.html : LLVMC-Tutorial.rst + $(RST2HTML) $< $@ + +LLVMC-Reference.html : LLVMC-Reference.rst + $(RST2HTML) $< $@ + +clean : + $(RM) LLVMC-Tutorial.html LLVMC-Reference.html diff --git a/tools/llvmc/doc/img/lines.gif b/tools/llvmc/doc/img/lines.gif Binary files differnew file mode 100644 index 0000000..88f491e --- /dev/null +++ b/tools/llvmc/doc/img/lines.gif diff --git a/tools/llvmc/driver/Main.cpp b/tools/llvmc/driver/Main.cpp new file mode 100644 index 0000000..b1f5b67 --- /dev/null +++ b/tools/llvmc/driver/Main.cpp @@ -0,0 +1,14 @@ +//===--- 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. +// +//===----------------------------------------------------------------------===// +// +// Just include CompilerDriver/Main.inc. +// +//===----------------------------------------------------------------------===// + +#include "llvm/CompilerDriver/Main.inc" diff --git a/tools/llvmc/driver/Makefile b/tools/llvmc/driver/Makefile new file mode 100644 index 0000000..3dd373a --- /dev/null +++ b/tools/llvmc/driver/Makefile @@ -0,0 +1,22 @@ +##===- tools/llvmc/driver/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 = $(LLVMC_BASED_DRIVER_NAME) +LLVMLIBS = 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/example/Hello/Hello.cpp b/tools/llvmc/example/Hello/Hello.cpp new file mode 100644 index 0000000..23a13a5 --- /dev/null +++ b/tools/llvmc/example/Hello/Hello.cpp @@ -0,0 +1,30 @@ +//===- Hello.cpp - Example code from "Writing an LLVMC Plugin" ------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Test plugin for LLVMC. Shows how to write plugins without using TableGen. +// +//===----------------------------------------------------------------------===// + +#include "llvm/CompilerDriver/CompilationGraph.h" +#include "llvm/CompilerDriver/Plugin.h" + +#include <iostream> + +namespace { +struct MyPlugin : public llvmc::BasePlugin { + void PopulateLanguageMap(llvmc::LanguageMap&) const + { std::cout << "Hello!\n"; } + + void PopulateCompilationGraph(llvmc::CompilationGraph&) const + {} +}; + +static llvmc::RegisterPlugin<MyPlugin> RP("Hello", "Hello World plugin"); + +} diff --git a/tools/llvmc/example/Hello/Makefile b/tools/llvmc/example/Hello/Makefile new file mode 100644 index 0000000..10325e6 --- /dev/null +++ b/tools/llvmc/example/Hello/Makefile @@ -0,0 +1,14 @@ +##===- tools/llvmc/plugins/Hello/Makefile ------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LEVEL = ../../../.. + +LLVMC_PLUGIN = Hello + +include $(LEVEL)/Makefile.common diff --git a/tools/llvmc/example/Simple/Makefile b/tools/llvmc/example/Simple/Makefile new file mode 100644 index 0000000..d7adb5d --- /dev/null +++ b/tools/llvmc/example/Simple/Makefile @@ -0,0 +1,15 @@ +##===- tools/llvmc/plugins/Simple/Makefile -----------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LEVEL = ../../../.. + +LLVMC_PLUGIN = Simple +BUILT_SOURCES = AutoGenerated.inc + +include $(LEVEL)/Makefile.common diff --git a/tools/llvmc/example/Simple/PluginMain.cpp b/tools/llvmc/example/Simple/PluginMain.cpp new file mode 100644 index 0000000..add8acb --- /dev/null +++ b/tools/llvmc/example/Simple/PluginMain.cpp @@ -0,0 +1 @@ +#include "AutoGenerated.inc" diff --git a/tools/llvmc/example/Simple/Simple.td b/tools/llvmc/example/Simple/Simple.td new file mode 100644 index 0000000..87bc385 --- /dev/null +++ b/tools/llvmc/example/Simple/Simple.td @@ -0,0 +1,37 @@ +//===- Simple.td - A simple plugin for LLVMC ------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// A simple LLVMC-based gcc wrapper that shows how to write LLVMC plugins. +// +// To compile, use this command: +// +// $ cd $LLVMC_DIR/example/Simple +// $ make +// +// Run as: +// +// $ llvmc -load $LLVM_DIR/Release/lib/plugin_llvmc_Simple.so +// +// For instructions on how to build your own LLVMC-based driver, see +// the 'example/Skeleton' directory. +//===----------------------------------------------------------------------===// + +include "llvm/CompilerDriver/Common.td" + +def gcc : Tool< +[(in_language "c"), + (out_language "executable"), + (output_suffix "out"), + (cmd_line "gcc $INFILE -o $OUTFILE"), + (sink) +]>; + +def LanguageMap : LanguageMap<[LangToSuffixes<"c", ["c"]>]>; + +def CompilationGraph : CompilationGraph<[Edge<"root", "gcc">]>; diff --git a/tools/llvmc/example/Skeleton/Makefile b/tools/llvmc/example/Skeleton/Makefile new file mode 100644 index 0000000..2e4cbb9 --- /dev/null +++ b/tools/llvmc/example/Skeleton/Makefile @@ -0,0 +1,24 @@ +##===- llvmc/example/Skeleton/Makefile ---------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open +# Source License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +# Change this so that $(BASE_LEVEL)/Makefile.common refers to +# $LLVM_DIR/Makefile.common. +export LLVMC_BASE_LEVEL = ../../../.. + +# Change this to the name of your LLVMC-based driver. +export LLVMC_BASED_DRIVER_NAME = llvmc-skeleton + +# List your plugin names here +export LLVMC_BUILTIN_PLUGINS = # Plugin + +LEVEL = $(LLVMC_BASE_LEVEL) + +DIRS = plugins driver + +include $(LEVEL)/Makefile.common diff --git a/tools/llvmc/example/Skeleton/README b/tools/llvmc/example/Skeleton/README new file mode 100644 index 0000000..92216ae --- /dev/null +++ b/tools/llvmc/example/Skeleton/README @@ -0,0 +1,6 @@ + +This is a template that can be used to create your own LLVMC-based drivers. Just +copy the `Skeleton` directory to the location of your preference and edit +`Skeleton/Makefile` and `Skeleton/plugins/Plugin`. + +The build system assumes that your project is based on LLVM. diff --git a/tools/llvmc/example/Skeleton/driver/Main.cpp b/tools/llvmc/example/Skeleton/driver/Main.cpp new file mode 100644 index 0000000..b1f5b67 --- /dev/null +++ b/tools/llvmc/example/Skeleton/driver/Main.cpp @@ -0,0 +1,14 @@ +//===--- 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. +// +//===----------------------------------------------------------------------===// +// +// Just include CompilerDriver/Main.inc. +// +//===----------------------------------------------------------------------===// + +#include "llvm/CompilerDriver/Main.inc" diff --git a/tools/llvmc/example/Skeleton/driver/Makefile b/tools/llvmc/example/Skeleton/driver/Makefile new file mode 100644 index 0000000..bf6d7a5 --- /dev/null +++ b/tools/llvmc/example/Skeleton/driver/Makefile @@ -0,0 +1,22 @@ +##===- llvmc/example/Skeleton/driver/Makefile --------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open +# Source License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LEVEL = $(LLVMC_BASE_LEVEL)/.. + +TOOLNAME = $(LLVMC_BASED_DRIVER_NAME) +LLVMLIBS = 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/example/Skeleton/plugins/Makefile b/tools/llvmc/example/Skeleton/plugins/Makefile new file mode 100644 index 0000000..fb07f23 --- /dev/null +++ b/tools/llvmc/example/Skeleton/plugins/Makefile @@ -0,0 +1,18 @@ +##===- llvmc/example/Skeleton/plugins/Makefile -------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open +# Source License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LEVEL = $(LLVMC_BASE_LEVEL)/.. + +ifneq ($(LLVMC_BUILTIN_PLUGINS),) +DIRS = $(LLVMC_BUILTIN_PLUGINS) +endif + +export LLVMC_BUILTIN_PLUGIN=1 + +include $(LEVEL)/Makefile.common diff --git a/tools/llvmc/example/Skeleton/plugins/Plugin/Makefile b/tools/llvmc/example/Skeleton/plugins/Plugin/Makefile new file mode 100644 index 0000000..54f7221 --- /dev/null +++ b/tools/llvmc/example/Skeleton/plugins/Plugin/Makefile @@ -0,0 +1,17 @@ +##===- llvmc/example/Skeleton/plugins/Plugin/Makefile ------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LEVEL = $(LLVMC_BASE_LEVEL)/../.. + +# Change this to the name of your plugin. +LLVMC_PLUGIN = Plugin + +BUILT_SOURCES = AutoGenerated.inc + +include $(LEVEL)/Makefile.common diff --git a/tools/llvmc/example/Skeleton/plugins/Plugin/Plugin.td b/tools/llvmc/example/Skeleton/plugins/Plugin/Plugin.td new file mode 100644 index 0000000..febb9ad --- /dev/null +++ b/tools/llvmc/example/Skeleton/plugins/Plugin/Plugin.td @@ -0,0 +1,7 @@ +//===- Plugin.td - A skeleton plugin for LLVMC -------------*- tablegen -*-===// +// +// Write the code for your plugin here. +// +//===----------------------------------------------------------------------===// + +include "llvm/CompilerDriver/Common.td" diff --git a/tools/llvmc/example/Skeleton/plugins/Plugin/PluginMain.cpp b/tools/llvmc/example/Skeleton/plugins/Plugin/PluginMain.cpp new file mode 100644 index 0000000..add8acb --- /dev/null +++ b/tools/llvmc/example/Skeleton/plugins/Plugin/PluginMain.cpp @@ -0,0 +1 @@ +#include "AutoGenerated.inc" diff --git a/tools/llvmc/plugins/Base/Base.td.in b/tools/llvmc/plugins/Base/Base.td.in new file mode 100644 index 0000000..757078a --- /dev/null +++ b/tools/llvmc/plugins/Base/Base.td.in @@ -0,0 +1,202 @@ +//===- Base.td - LLVMC2 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 compilation graph description used by llvmc2. +// +//===----------------------------------------------------------------------===// + +include "llvm/CompilerDriver/Common.td" + +// Options + +def OptList : OptionList<[ + (switch_option "emit-llvm", + (help "Emit LLVM .ll files instead of native object files")), + (switch_option "E", + (help "Stop after the preprocessing stage, do not run the compiler")), + (switch_option "fsyntax-only", + (help "Stop after checking the input for syntax errors")), + (switch_option "opt", + (help "Enable opt")), + (switch_option "S", + (help "Stop after compilation, do not assemble")), + (switch_option "c", + (help "Compile and assemble, but do not link")), + (switch_option "pthread", + (help "Enable threads")), + (parameter_option "linker", + (help "Choose linker (possible values: gcc, g++)")), + (parameter_list_option "include", + (help "Include the named file prior to preprocessing")), + (prefix_list_option "I", + (help "Add a directory to include path")), + (prefix_list_option "Wa,", + (help "Pass options to assembler")), + (prefix_list_option "Wllc,", + (help "Pass options to llc")), + (prefix_list_option "L", + (help "Add a directory to link path")), + (prefix_list_option "l", + (help "Search a library when linking")), + (prefix_list_option "Wl,", + (help "Pass options to linker")), + (prefix_list_option "Wo,", + (help "Pass options to opt")) +]>; + +// Tools + +class llvm_gcc_based <string cmd_prefix, string in_lang, string E_ext> : Tool< +[(in_language in_lang), + (out_language "llvm-bitcode"), + (output_suffix "bc"), + (cmd_line (case + (switch_on "E"), + (case (not_empty "o"), + !strconcat(cmd_prefix, " -E $INFILE -o $OUTFILE"), + (default), + !strconcat(cmd_prefix, " -E $INFILE")), + (switch_on "fsyntax-only"), + !strconcat(cmd_prefix, " -fsyntax-only $INFILE"), + (and (switch_on "S"), (switch_on "emit-llvm")), + !strconcat(cmd_prefix, " -S $INFILE -o $OUTFILE -emit-llvm"), + (default), + !strconcat(cmd_prefix, " -c $INFILE -o $OUTFILE -emit-llvm"))), + (actions + (case + (switch_on "E"), [(stop_compilation), (output_suffix E_ext)], + (and (switch_on "emit-llvm"), (switch_on "S")), + [(output_suffix "ll"), (stop_compilation)], + (and (switch_on "emit-llvm"), (switch_on "c")), (stop_compilation), + (switch_on "fsyntax-only"), (stop_compilation), + (not_empty "include"), (forward "include"), + (not_empty "I"), (forward "I"))), + (sink) +]>; + +def llvm_gcc_c : llvm_gcc_based<"@LLVMGCCCOMMAND@ -x c", "c", "i">; +def llvm_gcc_cpp : llvm_gcc_based<"@LLVMGXXCOMMAND@ -x c++", "c++", "i">; +def llvm_gcc_m : llvm_gcc_based<"@LLVMGCCCOMMAND@ -x objective-c", "objective-c", "mi">; +def llvm_gcc_mxx : llvm_gcc_based<"@LLVMGCCCOMMAND@ -x objective-c++", + "objective-c++", "mi">; + +def opt : Tool< +[(in_language "llvm-bitcode"), + (out_language "llvm-bitcode"), + (output_suffix "bc"), + (actions (case (not_empty "Wo,"), (unpack_values "Wo,"))), + (cmd_line "opt -f $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 llvm_gcc_assembler : Tool< +[(in_language "assembler"), + (out_language "object-code"), + (output_suffix "o"), + (cmd_line "@LLVMGCCCOMMAND@ -c -x assembler $INFILE -o $OUTFILE"), + (actions (case + (switch_on "c"), (stop_compilation), + (not_empty "Wa,"), (unpack_values "Wa,"))) +]>; + +def llc : Tool< +[(in_language "llvm-bitcode"), + (out_language "assembler"), + (output_suffix "s"), + (cmd_line "llc -f $INFILE -o $OUTFILE"), + (actions (case + (switch_on "S"), (stop_compilation), + (not_empty "Wllc,"), (unpack_values "Wllc,"))) +]>; + +// Base class for linkers +class llvm_gcc_based_linker <string cmd_prefix> : Tool< +[(in_language "object-code"), + (out_language "executable"), + (output_suffix "out"), + (cmd_line !strconcat(cmd_prefix, " $INFILE -o $OUTFILE")), + (join), + (actions (case + (switch_on "pthread"), (append_cmd "-lpthread"), + (not_empty "L"), (forward "L"), + (not_empty "l"), (forward "l"), + (not_empty "Wl,"), (unpack_values "Wl,"))) +]>; + +// Default linker +def llvm_gcc_linker : llvm_gcc_based_linker<"@LLVMGCCCOMMAND@">; +// Alternative linker for C++ +def llvm_gcc_cpp_linker : llvm_gcc_based_linker<"@LLVMGXXCOMMAND@">; + +// Language map + +def LanguageMap : LanguageMap< + [LangToSuffixes<"c++", ["cc", "cp", "cxx", "cpp", "CPP", "c++", "C"]>, + LangToSuffixes<"c", ["c"]>, + LangToSuffixes<"c-cpp-output", ["i"]>, + LangToSuffixes<"objective-c-cpp-output", ["mi"]>, + LangToSuffixes<"objective-c++", ["mm"]>, + LangToSuffixes<"objective-c", ["m"]>, + LangToSuffixes<"assembler", ["s"]>, + LangToSuffixes<"assembler-with-cpp", ["S"]>, + LangToSuffixes<"llvm-assembler", ["ll"]>, + LangToSuffixes<"llvm-bitcode", ["bc"]>, + LangToSuffixes<"object-code", ["o"]>, + LangToSuffixes<"executable", ["out"]> + ]>; + +// Compilation graph + +def CompilationGraph : CompilationGraph<[ + Edge<"root", "llvm_gcc_c">, + Edge<"root", "llvm_gcc_assembler">, + Edge<"root", "llvm_gcc_cpp">, + Edge<"root", "llvm_gcc_m">, + Edge<"root", "llvm_gcc_mxx">, + Edge<"root", "llvm_as">, + + Edge<"llvm_gcc_c", "llc">, + Edge<"llvm_gcc_cpp", "llc">, + Edge<"llvm_gcc_m", "llc">, + Edge<"llvm_gcc_mxx", "llc">, + Edge<"llvm_as", "llc">, + + OptionalEdge<"llvm_gcc_c", "opt", (case (switch_on "opt"), (inc_weight))>, + OptionalEdge<"llvm_gcc_cpp", "opt", (case (switch_on "opt"), (inc_weight))>, + OptionalEdge<"llvm_gcc_m", "opt", (case (switch_on "opt"), (inc_weight))>, + OptionalEdge<"llvm_gcc_mxx", "opt", (case (switch_on "opt"), (inc_weight))>, + OptionalEdge<"llvm_as", "opt", (case (switch_on "opt"), (inc_weight))>, + Edge<"opt", "llc">, + + Edge<"llc", "llvm_gcc_assembler">, + Edge<"llvm_gcc_assembler", "llvm_gcc_linker">, + OptionalEdge<"llvm_gcc_assembler", "llvm_gcc_cpp_linker", + (case + (or (input_languages_contain "c++"), + (input_languages_contain "objective-c++")), + (inc_weight), + (or (parameter_equals "linker", "g++"), + (parameter_equals "linker", "c++")), (inc_weight))>, + + + Edge<"root", "llvm_gcc_linker">, + OptionalEdge<"root", "llvm_gcc_cpp_linker", + (case + (or (input_languages_contain "c++"), + (input_languages_contain "objective-c++")), + (inc_weight), + (or (parameter_equals "linker", "g++"), + (parameter_equals "linker", "c++")), (inc_weight))> + ]>; diff --git a/tools/llvmc/plugins/Base/Makefile b/tools/llvmc/plugins/Base/Makefile new file mode 100644 index 0000000..ebc4335 --- /dev/null +++ b/tools/llvmc/plugins/Base/Makefile @@ -0,0 +1,15 @@ +##===- tools/llvmc/plugins/Base/Makefile -------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LEVEL = ../../../.. + +LLVMC_PLUGIN = Base +BUILT_SOURCES = AutoGenerated.inc + +include $(LEVEL)/Makefile.common diff --git a/tools/llvmc/plugins/Base/PluginMain.cpp b/tools/llvmc/plugins/Base/PluginMain.cpp new file mode 100644 index 0000000..add8acb --- /dev/null +++ b/tools/llvmc/plugins/Base/PluginMain.cpp @@ -0,0 +1 @@ +#include "AutoGenerated.inc" diff --git a/tools/llvmc/plugins/Clang/Clang.td b/tools/llvmc/plugins/Clang/Clang.td new file mode 100644 index 0000000..a179c53 --- /dev/null +++ b/tools/llvmc/plugins/Clang/Clang.td @@ -0,0 +1,116 @@ +// A replacement for the Clang's ccc script. +// Depends on the Base plugin. +// To compile, use this command: +// +// cd $LLVMC2_DIR +// make DRIVER_NAME=ccc2 BUILTIN_PLUGINS=Clang +// +// Or just use the default llvmc, which now has this plugin enabled. + +include "llvm/CompilerDriver/Common.td" + +def Priority : PluginPriority<1>; + +def Options : OptionList<[ +// Extern options +(switch_option "E", (extern)), +(switch_option "S", (extern)), +(switch_option "c", (extern)), +(switch_option "fsyntax-only", (extern)), +(switch_option "emit-llvm", (extern)), +(switch_option "pthread", (extern)), +(parameter_list_option "I", (extern)), +(parameter_list_option "include", (extern)), +(parameter_list_option "L", (extern)), +(parameter_list_option "l", (extern)), +(prefix_list_option "Wa,", (extern)), +(prefix_list_option "Wl,", (extern)), + +(switch_option "clang", (help "Use Clang instead of llvm-gcc")) +]>; + +class clang_based<string language, string cmd, string ext_E> : Tool< +[(in_language language), + (out_language "llvm-bitcode"), + (output_suffix "bc"), + (cmd_line (case + (switch_on "E"), + (case + (not_empty "o"), + !strconcat(cmd, " -E $INFILE -o $OUTFILE"), + (default), + !strconcat(cmd, " -E $INFILE")), + (and (switch_on "S"), (switch_on "emit-llvm")), + !strconcat(cmd, " -emit-llvm $INFILE -o $OUTFILE"), + (default), + !strconcat(cmd, " -emit-llvm-bc $INFILE -o $OUTFILE"))), + (actions (case (switch_on "E"), + [(stop_compilation), (output_suffix ext_E)], + (switch_on "fsyntax-only"), (stop_compilation), + (and (switch_on "S"), (switch_on "emit-llvm")), + [(stop_compilation), (output_suffix "ll")], + (and (switch_on "c"), (switch_on "emit-llvm")), + (stop_compilation), + (not_empty "include"), (forward "include"), + (not_empty "I"), (forward "I"))), + (sink) +]>; + +def clang_c : clang_based<"c", "clang -x c", "i">; +def clang_cpp : clang_based<"c++", "clang -x c++", "i">; +def clang_objective_c : clang_based<"objective-c", + "clang -x objective-c", "mi">; +def clang_objective_cpp : clang_based<"objective-c++", + "clang -x objective-c++", "mi">; + +def as : Tool< +[(in_language "assembler"), + (out_language "object-code"), + (output_suffix "o"), + (cmd_line "as $INFILE -o $OUTFILE"), + (actions (case (not_empty "Wa,"), (unpack_values "Wa,"), + (switch_on "c"), (stop_compilation))) +]>; + +// Default linker +def llvm_ld : Tool< +[(in_language "object-code"), + (out_language "executable"), + (output_suffix "out"), + (cmd_line "llvm-ld -native -disable-internalize $INFILE -o $OUTFILE"), + (actions (case + (switch_on "pthread"), (append_cmd "-lpthread"), + (not_empty "L"), (forward "L"), + (not_empty "l"), (forward "l"), + (not_empty "Wl,"), (unpack_values "Wl,"))), + (join) +]>; + +// Language map + +def LanguageMap : LanguageMap<[ + LangToSuffixes<"c++", ["cc", "cp", "cxx", "cpp", "CPP", "c++", "C"]>, + LangToSuffixes<"c", ["c"]>, + LangToSuffixes<"objective-c", ["m"]>, + LangToSuffixes<"c-cpp-output", ["i"]>, + LangToSuffixes<"objective-c-cpp-output", ["mi"]> +]>; + +// Compilation graph + +def CompilationGraph : CompilationGraph<[ + OptionalEdge<"root", "clang_c", + (case (switch_on "clang"), (inc_weight))>, + OptionalEdge<"root", "clang_cpp", + (case (switch_on "clang"), (inc_weight))>, + OptionalEdge<"root", "clang_objective_c", + (case (switch_on "clang"), (inc_weight))>, + OptionalEdge<"root", "clang_objective_cpp", + (case (switch_on "clang"), (inc_weight))>, + Edge<"clang_c", "llc">, + Edge<"clang_cpp", "llc">, + Edge<"clang_objective_c", "llc">, + Edge<"clang_objective_cpp", "llc">, + OptionalEdge<"llc", "as", (case (switch_on "clang"), (inc_weight))>, + Edge<"as", "llvm_ld"> +]>; diff --git a/tools/llvmc/plugins/Clang/Makefile b/tools/llvmc/plugins/Clang/Makefile new file mode 100644 index 0000000..5e5b88a --- /dev/null +++ b/tools/llvmc/plugins/Clang/Makefile @@ -0,0 +1,15 @@ +##===- tools/llvmc/plugins/Clang/Makefile ------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LEVEL = ../../../.. + +LLVMC_PLUGIN = Clang +BUILT_SOURCES = AutoGenerated.inc + +include $(LEVEL)/Makefile.common diff --git a/tools/llvmc/plugins/Clang/PluginMain.cpp b/tools/llvmc/plugins/Clang/PluginMain.cpp new file mode 100644 index 0000000..add8acb --- /dev/null +++ b/tools/llvmc/plugins/Clang/PluginMain.cpp @@ -0,0 +1 @@ +#include "AutoGenerated.inc" diff --git a/tools/llvmc/plugins/Makefile b/tools/llvmc/plugins/Makefile new file mode 100644 index 0000000..37dac6f --- /dev/null +++ b/tools/llvmc/plugins/Makefile @@ -0,0 +1,18 @@ +##===- tools/llvmc/plugins/Makefile ------------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open +# Source License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LEVEL = ../../.. + +ifneq ($(LLVMC_BUILTIN_PLUGINS),) +DIRS = $(LLVMC_BUILTIN_PLUGINS) +endif + +export LLVMC_BUILTIN_PLUGIN=1 + +include $(LEVEL)/Makefile.common diff --git a/tools/lto/LTOCodeGenerator.cpp b/tools/lto/LTOCodeGenerator.cpp new file mode 100644 index 0000000..d3a3f7f --- /dev/null +++ b/tools/lto/LTOCodeGenerator.cpp @@ -0,0 +1,506 @@ +//===-LTOCodeGenerator.cpp - LLVM Link Time Optimizer ---------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements the Link Time Optimization library. This library is +// intended to be used by linker to optimize code at link time. +// +//===----------------------------------------------------------------------===// + +#include "LTOModule.h" +#include "LTOCodeGenerator.h" + + +#include "llvm/Module.h" +#include "llvm/PassManager.h" +#include "llvm/Linker.h" +#include "llvm/Constants.h" +#include "llvm/DerivedTypes.h" +#include "llvm/ModuleProvider.h" +#include "llvm/Bitcode/ReaderWriter.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/SystemUtils.h" +#include "llvm/Support/Mangler.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/System/Signals.h" +#include "llvm/Analysis/Passes.h" +#include "llvm/Analysis/LoopPass.h" +#include "llvm/Analysis/Verifier.h" +#include "llvm/CodeGen/FileWriters.h" +#include "llvm/Target/SubtargetFeature.h" +#include "llvm/Target/TargetOptions.h" +#include "llvm/Target/TargetData.h" +#include "llvm/Target/TargetMachine.h" +#include "llvm/Target/TargetMachineRegistry.h" +#include "llvm/Target/TargetAsmInfo.h" +#include "llvm/Transforms/IPO.h" +#include "llvm/Transforms/Scalar.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/Config/config.h" + + +#include <fstream> +#include <unistd.h> +#include <stdlib.h> +#include <fcntl.h> + + +using namespace llvm; + +static cl::opt<bool> DisableInline("disable-inlining", + cl::desc("Do not run the inliner pass")); + + +const char* LTOCodeGenerator::getVersionString() +{ +#ifdef LLVM_VERSION_INFO + return PACKAGE_NAME " version " PACKAGE_VERSION ", " LLVM_VERSION_INFO; +#else + return PACKAGE_NAME " version " PACKAGE_VERSION; +#endif +} + + +LTOCodeGenerator::LTOCodeGenerator() + : _linker("LinkTimeOptimizer", "ld-temp.o"), _target(NULL), + _emitDwarfDebugInfo(false), _scopeRestrictionsDone(false), + _codeModel(LTO_CODEGEN_PIC_MODEL_DYNAMIC), + _nativeObjectFile(NULL), _gccPath(NULL) +{ + +} + +LTOCodeGenerator::~LTOCodeGenerator() +{ + delete _target; + delete _nativeObjectFile; +} + + + +bool LTOCodeGenerator::addModule(LTOModule* mod, std::string& errMsg) +{ + return _linker.LinkInModule(mod->getLLVVMModule(), &errMsg); +} + + +bool LTOCodeGenerator::setDebugInfo(lto_debug_model debug, std::string& errMsg) +{ + switch (debug) { + case LTO_DEBUG_MODEL_NONE: + _emitDwarfDebugInfo = false; + return false; + + case LTO_DEBUG_MODEL_DWARF: + _emitDwarfDebugInfo = true; + return false; + } + errMsg = "unknown debug format"; + return true; +} + + +bool LTOCodeGenerator::setCodePICModel(lto_codegen_model model, + std::string& errMsg) +{ + switch (model) { + case LTO_CODEGEN_PIC_MODEL_STATIC: + case LTO_CODEGEN_PIC_MODEL_DYNAMIC: + case LTO_CODEGEN_PIC_MODEL_DYNAMIC_NO_PIC: + _codeModel = model; + return false; + } + errMsg = "unknown pic model"; + return true; +} + +void LTOCodeGenerator::setGccPath(const char* path) +{ + if ( _gccPath ) + delete _gccPath; + _gccPath = new sys::Path(path); +} + +void LTOCodeGenerator::addMustPreserveSymbol(const char* sym) +{ + _mustPreserveSymbols[sym] = 1; +} + + +bool LTOCodeGenerator::writeMergedModules(const char* path, std::string& errMsg) +{ + if ( this->determineTarget(errMsg) ) + return true; + + // mark which symbols can not be internalized + this->applyScopeRestrictions(); + + // create output file + std::ofstream out(path, std::ios_base::out|std::ios::trunc|std::ios::binary); + if ( out.fail() ) { + errMsg = "could not open bitcode file for writing: "; + errMsg += path; + return true; + } + + // write bitcode to it + WriteBitcodeToFile(_linker.getModule(), out); + if ( out.fail() ) { + errMsg = "could not write bitcode file: "; + errMsg += path; + return true; + } + + return false; +} + + +const void* LTOCodeGenerator::compile(size_t* length, std::string& errMsg) +{ + // make unique temp .s file to put generated assembly code + sys::Path uniqueAsmPath("lto-llvm.s"); + if ( uniqueAsmPath.createTemporaryFileOnDisk(true, &errMsg) ) + return NULL; + sys::RemoveFileOnSignal(uniqueAsmPath); + + // generate assembly code + bool genResult = false; + { + raw_fd_ostream asmFile(uniqueAsmPath.c_str(), false, errMsg); + if (!errMsg.empty()) + return NULL; + genResult = this->generateAssemblyCode(asmFile, errMsg); + } + if ( genResult ) { + if ( uniqueAsmPath.exists() ) + uniqueAsmPath.eraseFromDisk(); + return NULL; + } + + // make unique temp .o file to put generated object file + sys::PathWithStatus uniqueObjPath("lto-llvm.o"); + if ( uniqueObjPath.createTemporaryFileOnDisk(true, &errMsg) ) { + if ( uniqueAsmPath.exists() ) + uniqueAsmPath.eraseFromDisk(); + return NULL; + } + sys::RemoveFileOnSignal(uniqueObjPath); + + // assemble the assembly code + const std::string& uniqueObjStr = uniqueObjPath.toString(); + bool asmResult = this->assemble(uniqueAsmPath.toString(), + uniqueObjStr, errMsg); + if ( !asmResult ) { + // remove old buffer if compile() called twice + delete _nativeObjectFile; + + // read .o file into memory buffer + _nativeObjectFile = MemoryBuffer::getFile(uniqueObjStr.c_str(),&errMsg); + } + + // remove temp files + uniqueAsmPath.eraseFromDisk(); + uniqueObjPath.eraseFromDisk(); + + // return buffer, unless error + if ( _nativeObjectFile == NULL ) + return NULL; + *length = _nativeObjectFile->getBufferSize(); + return _nativeObjectFile->getBufferStart(); +} + + +bool LTOCodeGenerator::assemble(const std::string& asmPath, + const std::string& objPath, std::string& errMsg) +{ + sys::Path gcc; + if ( _gccPath ) { + gcc = *_gccPath; + } else { + // find compiler driver + gcc = sys::Program::FindProgramByName("gcc"); + if ( gcc.isEmpty() ) { + errMsg = "can't locate gcc"; + return true; + } + } + + // build argument list + std::vector<const char*> args; + std::string targetTriple = _linker.getModule()->getTargetTriple(); + args.push_back(gcc.c_str()); + if ( targetTriple.find("darwin") != targetTriple.size() ) { + if (strncmp(targetTriple.c_str(), "i386-apple-", 11) == 0) { + args.push_back("-arch"); + args.push_back("i386"); + } + else if (strncmp(targetTriple.c_str(), "x86_64-apple-", 13) == 0) { + args.push_back("-arch"); + args.push_back("x86_64"); + } + else if (strncmp(targetTriple.c_str(), "powerpc-apple-", 14) == 0) { + args.push_back("-arch"); + args.push_back("ppc"); + } + else if (strncmp(targetTriple.c_str(), "powerpc64-apple-", 16) == 0) { + args.push_back("-arch"); + args.push_back("ppc64"); + } + else if (strncmp(targetTriple.c_str(), "arm-apple-", 10) == 0) { + args.push_back("-arch"); + args.push_back("arm"); + } + else if ((strncmp(targetTriple.c_str(), "armv4t-apple-", 13) == 0) || + (strncmp(targetTriple.c_str(), "thumbv4t-apple-", 15) == 0)) { + args.push_back("-arch"); + args.push_back("armv4t"); + } + else if ((strncmp(targetTriple.c_str(), "armv5-apple-", 12) == 0) || + (strncmp(targetTriple.c_str(), "armv5e-apple-", 13) == 0) || + (strncmp(targetTriple.c_str(), "thumbv5-apple-", 14) == 0) || + (strncmp(targetTriple.c_str(), "thumbv5e-apple-", 15) == 0)) { + args.push_back("-arch"); + args.push_back("armv5"); + } + else if ((strncmp(targetTriple.c_str(), "armv6-apple-", 12) == 0) || + (strncmp(targetTriple.c_str(), "thumbv6-apple-", 14) == 0)) { + args.push_back("-arch"); + args.push_back("armv6"); + } + } + args.push_back("-c"); + args.push_back("-x"); + args.push_back("assembler"); + args.push_back("-o"); + args.push_back(objPath.c_str()); + args.push_back(asmPath.c_str()); + args.push_back(0); + + // invoke assembler + if ( sys::Program::ExecuteAndWait(gcc, &args[0], 0, 0, 0, 0, &errMsg) ) { + errMsg = "error in assembly"; + return true; + } + return false; // success +} + + + +bool LTOCodeGenerator::determineTarget(std::string& errMsg) +{ + if ( _target == NULL ) { + // create target machine from info for merged modules + Module* mergedModule = _linker.getModule(); + const TargetMachineRegistry::entry* march = + TargetMachineRegistry::getClosestStaticTargetForModule( + *mergedModule, errMsg); + if ( march == NULL ) + return true; + + // construct LTModule, hand over ownership of module and target + std::string FeatureStr = + getFeatureString(_linker.getModule()->getTargetTriple().c_str()); + _target = march->CtorFn(*mergedModule, FeatureStr.c_str()); + } + return false; +} + +void LTOCodeGenerator::applyScopeRestrictions() +{ + if ( !_scopeRestrictionsDone ) { + Module* mergedModule = _linker.getModule(); + + // Start off with a verification pass. + PassManager passes; + passes.add(createVerifierPass()); + + // mark which symbols can not be internalized + if ( !_mustPreserveSymbols.empty() ) { + Mangler mangler(*mergedModule, + _target->getTargetAsmInfo()->getGlobalPrefix()); + std::vector<const char*> mustPreserveList; + for (Module::iterator f = mergedModule->begin(), + e = mergedModule->end(); f != e; ++f) { + if ( !f->isDeclaration() + && _mustPreserveSymbols.count(mangler.getValueName(f)) ) + mustPreserveList.push_back(::strdup(f->getName().c_str())); + } + for (Module::global_iterator v = mergedModule->global_begin(), + e = mergedModule->global_end(); v != e; ++v) { + if ( !v->isDeclaration() + && _mustPreserveSymbols.count(mangler.getValueName(v)) ) + mustPreserveList.push_back(::strdup(v->getName().c_str())); + } + passes.add(createInternalizePass(mustPreserveList)); + } + // apply scope restrictions + passes.run(*mergedModule); + + _scopeRestrictionsDone = true; + } +} + +/// Optimize merged modules using various IPO passes +bool LTOCodeGenerator::generateAssemblyCode(raw_ostream& out, + std::string& errMsg) +{ + if ( this->determineTarget(errMsg) ) + return true; + + // mark which symbols can not be internalized + this->applyScopeRestrictions(); + + Module* mergedModule = _linker.getModule(); + + // If target supports exception handling then enable it now. + if ( _target->getTargetAsmInfo()->doesSupportExceptionHandling() ) + llvm::ExceptionHandling = true; + + // set codegen model + switch( _codeModel ) { + case LTO_CODEGEN_PIC_MODEL_STATIC: + _target->setRelocationModel(Reloc::Static); + break; + case LTO_CODEGEN_PIC_MODEL_DYNAMIC: + _target->setRelocationModel(Reloc::PIC_); + break; + case LTO_CODEGEN_PIC_MODEL_DYNAMIC_NO_PIC: + _target->setRelocationModel(Reloc::DynamicNoPIC); + break; + } + + // if options were requested, set them + if ( !_codegenOptions.empty() ) + cl::ParseCommandLineOptions(_codegenOptions.size(), + (char**)&_codegenOptions[0]); + + // Instantiate the pass manager to organize the passes. + PassManager passes; + + // Start off with a verification pass. + passes.add(createVerifierPass()); + + // Add an appropriate TargetData instance for this module... + passes.add(new TargetData(*_target->getTargetData())); + + // Propagate constants at call sites into the functions they call. This + // opens opportunities for globalopt (and inlining) by substituting function + // pointers passed as arguments to direct uses of functions. + passes.add(createIPSCCPPass()); + + // Now that we internalized some globals, see if we can hack on them! + passes.add(createGlobalOptimizerPass()); + + // Linking modules together can lead to duplicated global constants, only + // keep one copy of each constant... + passes.add(createConstantMergePass()); + + // Remove unused arguments from functions... + passes.add(createDeadArgEliminationPass()); + + // Reduce the code after globalopt and ipsccp. Both can open up significant + // simplification opportunities, and both can propagate functions through + // function pointers. When this happens, we often have to resolve varargs + // calls, etc, so let instcombine do this. + passes.add(createInstructionCombiningPass()); + if (!DisableInline) + passes.add(createFunctionInliningPass()); // Inline small functions + passes.add(createPruneEHPass()); // Remove dead EH info + passes.add(createGlobalDCEPass()); // Remove dead functions + + // If we didn't decide to inline a function, check to see if we can + // transform it to pass arguments by value instead of by reference. + passes.add(createArgumentPromotionPass()); + + // The IPO passes may leave cruft around. Clean up after them. + passes.add(createInstructionCombiningPass()); + passes.add(createJumpThreadingPass()); // Thread jumps. + passes.add(createScalarReplAggregatesPass()); // Break up allocas + + // Run a few AA driven optimizations here and now, to cleanup the code. + passes.add(createFunctionAttrsPass()); // Add nocapture + passes.add(createGlobalsModRefPass()); // IP alias analysis + passes.add(createLICMPass()); // Hoist loop invariants + passes.add(createGVNPass()); // Remove common subexprs + passes.add(createMemCpyOptPass()); // Remove dead memcpy's + passes.add(createDeadStoreEliminationPass()); // Nuke dead stores + + // Cleanup and simplify the code after the scalar optimizations. + passes.add(createInstructionCombiningPass()); + passes.add(createJumpThreadingPass()); // Thread jumps. + passes.add(createPromoteMemoryToRegisterPass()); // Cleanup after threading. + + + // Delete basic blocks, which optimization passes may have killed... + passes.add(createCFGSimplificationPass()); + + // Now that we have optimized the program, discard unreachable functions... + passes.add(createGlobalDCEPass()); + + // Make sure everything is still good. + passes.add(createVerifierPass()); + + FunctionPassManager* codeGenPasses = + new FunctionPassManager(new ExistingModuleProvider(mergedModule)); + + codeGenPasses->add(new TargetData(*_target->getTargetData())); + + MachineCodeEmitter* mce = NULL; + + switch (_target->addPassesToEmitFile(*codeGenPasses, out, + TargetMachine::AssemblyFile, + CodeGenOpt::Aggressive)) { + case FileModel::MachOFile: + mce = AddMachOWriter(*codeGenPasses, out, *_target); + break; + case FileModel::ElfFile: + mce = AddELFWriter(*codeGenPasses, out, *_target); + break; + case FileModel::AsmFile: + break; + case FileModel::Error: + case FileModel::None: + errMsg = "target file type not supported"; + return true; + } + + if (_target->addPassesToEmitFileFinish(*codeGenPasses, mce, + CodeGenOpt::Aggressive)) { + errMsg = "target does not support generation of this file type"; + return true; + } + + // Run our queue of passes all at once now, efficiently. + passes.run(*mergedModule); + + // Run the code generator, and write assembly file + codeGenPasses->doInitialization(); + + for (Module::iterator + it = mergedModule->begin(), e = mergedModule->end(); it != e; ++it) + if (!it->isDeclaration()) + codeGenPasses->run(*it); + + codeGenPasses->doFinalization(); + return false; // success +} + + +/// Optimize merged modules using various IPO passes +void LTOCodeGenerator::setCodeGenDebugOptions(const char* options) +{ + std::string ops(options); + for (std::string o = getToken(ops); !o.empty(); o = getToken(ops)) { + // ParseCommandLineOptions() expects argv[0] to be program name. + // Lazily add that. + if ( _codegenOptions.empty() ) + _codegenOptions.push_back("libLTO"); + _codegenOptions.push_back(strdup(o.c_str())); + } +} diff --git a/tools/lto/LTOCodeGenerator.h b/tools/lto/LTOCodeGenerator.h new file mode 100644 index 0000000..57398b0 --- /dev/null +++ b/tools/lto/LTOCodeGenerator.h @@ -0,0 +1,67 @@ +//===-LTOCodeGenerator.h - LLVM Link Time Optimizer -----------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file declares the LTOCodeGenerator class. +// +//===----------------------------------------------------------------------===// + + +#ifndef LTO_CODE_GENERATOR_H +#define LTO_CODE_GENERATOR_H + +#include "llvm/Linker.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/SmallVector.h" + +#include <string> + + +// +// C++ class which implements the opaque lto_code_gen_t +// + +class LTOCodeGenerator { +public: + static const char* getVersionString(); + + LTOCodeGenerator(); + ~LTOCodeGenerator(); + + bool addModule(class LTOModule*, std::string& errMsg); + bool setDebugInfo(lto_debug_model, std::string& errMsg); + bool setCodePICModel(lto_codegen_model, std::string& errMsg); + void setGccPath(const char* path); + void addMustPreserveSymbol(const char* sym); + bool writeMergedModules(const char* path, + std::string& errMsg); + const void* compile(size_t* length, std::string& errMsg); + void setCodeGenDebugOptions(const char *opts); +private: + bool generateAssemblyCode(llvm::raw_ostream& out, + std::string& errMsg); + bool assemble(const std::string& asmPath, + const std::string& objPath, std::string& errMsg); + void applyScopeRestrictions(); + bool determineTarget(std::string& errMsg); + + typedef llvm::StringMap<uint8_t> StringSet; + + llvm::Linker _linker; + llvm::TargetMachine* _target; + bool _emitDwarfDebugInfo; + bool _scopeRestrictionsDone; + lto_codegen_model _codeModel; + StringSet _mustPreserveSymbols; + llvm::MemoryBuffer* _nativeObjectFile; + std::vector<const char*> _codegenOptions; + llvm::sys::Path* _gccPath; +}; + +#endif // LTO_CODE_GENERATOR_H + diff --git a/tools/lto/LTOModule.cpp b/tools/lto/LTOModule.cpp new file mode 100644 index 0000000..939d0ea --- /dev/null +++ b/tools/lto/LTOModule.cpp @@ -0,0 +1,538 @@ +//===-LTOModule.cpp - LLVM Link Time Optimizer ----------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements the Link Time Optimization library. This library is +// intended to be used by linker to optimize code at link time. +// +//===----------------------------------------------------------------------===// + +#include "LTOModule.h" + +#include "llvm/Constants.h" +#include "llvm/Module.h" +#include "llvm/ModuleProvider.h" +#include "llvm/ADT/OwningPtr.h" +#include "llvm/Bitcode/ReaderWriter.h" +#include "llvm/Support/SystemUtils.h" +#include "llvm/Support/Mangler.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/MathExtras.h" +#include "llvm/System/Path.h" +#include "llvm/System/Process.h" +#include "llvm/Target/SubtargetFeature.h" +#include "llvm/Target/TargetMachine.h" +#include "llvm/Target/TargetMachineRegistry.h" +#include "llvm/Target/TargetAsmInfo.h" + +#include <fstream> + +using namespace llvm; + +bool LTOModule::isBitcodeFile(const void* mem, size_t length) +{ + return ( llvm::sys::IdentifyFileType((char*)mem, length) + == llvm::sys::Bitcode_FileType ); +} + +bool LTOModule::isBitcodeFile(const char* path) +{ + return llvm::sys::Path(path).isBitcodeFile(); +} + +bool LTOModule::isBitcodeFileForTarget(const void* mem, size_t length, + const char* triplePrefix) +{ + MemoryBuffer* buffer = makeBuffer(mem, length); + if ( buffer == NULL ) + return false; + return isTargetMatch(buffer, triplePrefix); +} + + +bool LTOModule::isBitcodeFileForTarget(const char* path, + const char* triplePrefix) +{ + MemoryBuffer *buffer = MemoryBuffer::getFile(path); + if (buffer == NULL) + return false; + return isTargetMatch(buffer, triplePrefix); +} + +// takes ownership of buffer +bool LTOModule::isTargetMatch(MemoryBuffer* buffer, const char* triplePrefix) +{ + OwningPtr<ModuleProvider> mp(getBitcodeModuleProvider(buffer)); + // on success, mp owns buffer and both are deleted at end of this method + if ( !mp ) { + delete buffer; + return false; + } + std::string actualTarget = mp->getModule()->getTargetTriple(); + return ( strncmp(actualTarget.c_str(), triplePrefix, + strlen(triplePrefix)) == 0); +} + + +LTOModule::LTOModule(Module* m, TargetMachine* t) + : _module(m), _target(t), _symbolsParsed(false) +{ +} + +LTOModule* LTOModule::makeLTOModule(const char* path, std::string& errMsg) +{ + OwningPtr<MemoryBuffer> buffer(MemoryBuffer::getFile(path, &errMsg)); + if ( !buffer ) + return NULL; + return makeLTOModule(buffer.get(), errMsg); +} + +/// makeBuffer - create a MemoryBuffer from a memory range. +/// MemoryBuffer requires the byte past end of the buffer to be a zero. +/// We might get lucky and already be that way, otherwise make a copy. +/// Also if next byte is on a different page, don't assume it is readable. +MemoryBuffer* LTOModule::makeBuffer(const void* mem, size_t length) +{ + const char* startPtr = (char*)mem; + const char* endPtr = startPtr+length; + if ( (((uintptr_t)endPtr & (sys::Process::GetPageSize()-1)) == 0) + || (*endPtr != 0) ) + return MemoryBuffer::getMemBufferCopy(startPtr, endPtr); + else + return MemoryBuffer::getMemBuffer(startPtr, endPtr); +} + + +LTOModule* LTOModule::makeLTOModule(const void* mem, size_t length, + std::string& errMsg) +{ + OwningPtr<MemoryBuffer> buffer(makeBuffer(mem, length)); + if ( !buffer ) + return NULL; + return makeLTOModule(buffer.get(), errMsg); +} + +/// getFeatureString - Return a string listing the features associated with the +/// target triple. +/// +/// FIXME: This is an inelegant way of specifying the features of a +/// subtarget. It would be better if we could encode this information into the +/// IR. See <rdar://5972456>. +std::string getFeatureString(const char *TargetTriple) { + SubtargetFeatures Features; + + if (strncmp(TargetTriple, "powerpc-apple-", 14) == 0) { + Features.AddFeature("altivec", true); + } else if (strncmp(TargetTriple, "powerpc64-apple-", 16) == 0) { + Features.AddFeature("64bit", true); + Features.AddFeature("altivec", true); + } + + return Features.getString(); +} + +LTOModule* LTOModule::makeLTOModule(MemoryBuffer* buffer, std::string& errMsg) +{ + // parse bitcode buffer + OwningPtr<Module> m(ParseBitcodeFile(buffer, &errMsg)); + if ( !m ) + return NULL; + // find machine architecture for this module + const TargetMachineRegistry::entry* march = + TargetMachineRegistry::getClosestStaticTargetForModule(*m, errMsg); + + if ( march == NULL ) + return NULL; + + // construct LTModule, hand over ownership of module and target + std::string FeatureStr = getFeatureString(m->getTargetTriple().c_str()); + TargetMachine* target = march->CtorFn(*m, FeatureStr); + return new LTOModule(m.take(), target); +} + + +const char* LTOModule::getTargetTriple() +{ + return _module->getTargetTriple().c_str(); +} + +void LTOModule::addDefinedFunctionSymbol(Function* f, Mangler &mangler) +{ + // add to list of defined symbols + addDefinedSymbol(f, mangler, true); + + // add external symbols referenced by this function. + for (Function::iterator b = f->begin(); b != f->end(); ++b) { + for (BasicBlock::iterator i = b->begin(); i != b->end(); ++i) { + for (unsigned count = 0, total = i->getNumOperands(); + count != total; ++count) { + findExternalRefs(i->getOperand(count), mangler); + } + } + } +} + +// get string that data pointer points to +bool LTOModule::objcClassNameFromExpression(Constant* c, std::string& name) +{ + if (ConstantExpr* ce = dyn_cast<ConstantExpr>(c)) { + Constant* op = ce->getOperand(0); + if (GlobalVariable* gvn = dyn_cast<GlobalVariable>(op)) { + Constant* cn = gvn->getInitializer(); + if (ConstantArray* ca = dyn_cast<ConstantArray>(cn)) { + if ( ca->isCString() ) { + name = ".objc_class_name_" + ca->getAsString(); + return true; + } + } + } + } + return false; +} + +// parse i386/ppc ObjC class data structure +void LTOModule::addObjCClass(GlobalVariable* clgv) +{ + if (ConstantStruct* c = dyn_cast<ConstantStruct>(clgv->getInitializer())) { + // second slot in __OBJC,__class is pointer to superclass name + std::string superclassName; + if ( objcClassNameFromExpression(c->getOperand(1), superclassName) ) { + NameAndAttributes info; + if ( _undefines.find(superclassName.c_str()) == _undefines.end() ) { + const char* symbolName = ::strdup(superclassName.c_str()); + info.name = ::strdup(symbolName); + info.attributes = LTO_SYMBOL_DEFINITION_UNDEFINED; + // string is owned by _undefines + _undefines[info.name] = info; + } + } + // third slot in __OBJC,__class is pointer to class name + std::string className; + if ( objcClassNameFromExpression(c->getOperand(2), className) ) { + const char* symbolName = ::strdup(className.c_str()); + NameAndAttributes info; + info.name = symbolName; + info.attributes = (lto_symbol_attributes) + (LTO_SYMBOL_PERMISSIONS_DATA | + LTO_SYMBOL_DEFINITION_REGULAR | + LTO_SYMBOL_SCOPE_DEFAULT); + _symbols.push_back(info); + _defines[info.name] = 1; + } + } +} + + +// parse i386/ppc ObjC category data structure +void LTOModule::addObjCCategory(GlobalVariable* clgv) +{ + if (ConstantStruct* c = dyn_cast<ConstantStruct>(clgv->getInitializer())) { + // second slot in __OBJC,__category is pointer to target class name + std::string targetclassName; + if ( objcClassNameFromExpression(c->getOperand(1), targetclassName) ) { + NameAndAttributes info; + if ( _undefines.find(targetclassName.c_str()) == _undefines.end() ){ + const char* symbolName = ::strdup(targetclassName.c_str()); + info.name = ::strdup(symbolName); + info.attributes = LTO_SYMBOL_DEFINITION_UNDEFINED; + // string is owned by _undefines + _undefines[info.name] = info; + } + } + } +} + + +// parse i386/ppc ObjC class list data structure +void LTOModule::addObjCClassRef(GlobalVariable* clgv) +{ + std::string targetclassName; + if ( objcClassNameFromExpression(clgv->getInitializer(), targetclassName) ){ + NameAndAttributes info; + if ( _undefines.find(targetclassName.c_str()) == _undefines.end() ) { + const char* symbolName = ::strdup(targetclassName.c_str()); + info.name = ::strdup(symbolName); + info.attributes = LTO_SYMBOL_DEFINITION_UNDEFINED; + // string is owned by _undefines + _undefines[info.name] = info; + } + } +} + + +void LTOModule::addDefinedDataSymbol(GlobalValue* v, Mangler& mangler) +{ + // add to list of defined symbols + addDefinedSymbol(v, mangler, false); + + // Special case i386/ppc ObjC data structures in magic sections: + // The issue is that the old ObjC object format did some strange + // contortions to avoid real linker symbols. For instance, the + // ObjC class data structure is allocated statically in the executable + // that defines that class. That data structures contains a pointer to + // its superclass. But instead of just initializing that part of the + // struct to the address of its superclass, and letting the static and + // dynamic linkers do the rest, the runtime works by having that field + // instead point to a C-string that is the name of the superclass. + // At runtime the objc initialization updates that pointer and sets + // it to point to the actual super class. As far as the linker + // knows it is just a pointer to a string. But then someone wanted the + // linker to issue errors at build time if the superclass was not found. + // So they figured out a way in mach-o object format to use an absolute + // symbols (.objc_class_name_Foo = 0) and a floating reference + // (.reference .objc_class_name_Bar) to cause the linker into erroring when + // a class was missing. + // The following synthesizes the implicit .objc_* symbols for the linker + // from the ObjC data structures generated by the front end. + if ( v->hasSection() /* && isTargetDarwin */ ) { + // special case if this data blob is an ObjC class definition + if ( v->getSection().compare(0, 15, "__OBJC,__class,") == 0 ) { + if (GlobalVariable* gv = dyn_cast<GlobalVariable>(v)) { + addObjCClass(gv); + } + } + + // special case if this data blob is an ObjC category definition + else if ( v->getSection().compare(0, 18, "__OBJC,__category,") == 0 ) { + if (GlobalVariable* gv = dyn_cast<GlobalVariable>(v)) { + addObjCCategory(gv); + } + } + + // special case if this data blob is the list of referenced classes + else if ( v->getSection().compare(0, 18, "__OBJC,__cls_refs,") == 0 ) { + if (GlobalVariable* gv = dyn_cast<GlobalVariable>(v)) { + addObjCClassRef(gv); + } + } + } + + // add external symbols referenced by this data. + for (unsigned count = 0, total = v->getNumOperands(); + count != total; ++count) { + findExternalRefs(v->getOperand(count), mangler); + } +} + + +void LTOModule::addDefinedSymbol(GlobalValue* def, Mangler &mangler, + bool isFunction) +{ + // ignore all llvm.* symbols + if ( strncmp(def->getNameStart(), "llvm.", 5) == 0 ) + return; + + // string is owned by _defines + const char* symbolName = ::strdup(mangler.getValueName(def).c_str()); + + // set alignment part log2() can have rounding errors + uint32_t align = def->getAlignment(); + uint32_t attr = align ? CountTrailingZeros_32(def->getAlignment()) : 0; + + // set permissions part + if ( isFunction ) + attr |= LTO_SYMBOL_PERMISSIONS_CODE; + else { + GlobalVariable* gv = dyn_cast<GlobalVariable>(def); + if ( (gv != NULL) && gv->isConstant() ) + attr |= LTO_SYMBOL_PERMISSIONS_RODATA; + else + attr |= LTO_SYMBOL_PERMISSIONS_DATA; + } + + // set definition part + if ( def->hasWeakLinkage() || def->hasLinkOnceLinkage() ) { + attr |= LTO_SYMBOL_DEFINITION_WEAK; + } + else if ( def->hasCommonLinkage()) { + attr |= LTO_SYMBOL_DEFINITION_TENTATIVE; + } + else { + attr |= LTO_SYMBOL_DEFINITION_REGULAR; + } + + // set scope part + if ( def->hasHiddenVisibility() ) + attr |= LTO_SYMBOL_SCOPE_HIDDEN; + else if ( def->hasProtectedVisibility() ) + attr |= LTO_SYMBOL_SCOPE_PROTECTED; + else if ( def->hasExternalLinkage() || def->hasWeakLinkage() + || def->hasLinkOnceLinkage() || def->hasCommonLinkage() ) + attr |= LTO_SYMBOL_SCOPE_DEFAULT; + else + attr |= LTO_SYMBOL_SCOPE_INTERNAL; + + // add to table of symbols + NameAndAttributes info; + info.name = symbolName; + info.attributes = (lto_symbol_attributes)attr; + _symbols.push_back(info); + _defines[info.name] = 1; +} + +void LTOModule::addAsmGlobalSymbol(const char *name) { + // only add new define if not already defined + if ( _defines.count(name, &name[strlen(name)+1]) == 0 ) + return; + + // string is owned by _defines + const char *symbolName = ::strdup(name); + uint32_t attr = LTO_SYMBOL_DEFINITION_REGULAR; + attr |= LTO_SYMBOL_SCOPE_DEFAULT; + NameAndAttributes info; + info.name = symbolName; + info.attributes = (lto_symbol_attributes)attr; + _symbols.push_back(info); + _defines[info.name] = 1; +} + +void LTOModule::addPotentialUndefinedSymbol(GlobalValue* decl, Mangler &mangler) +{ + // ignore all llvm.* symbols + if ( strncmp(decl->getNameStart(), "llvm.", 5) == 0 ) + return; + + const char* name = mangler.getValueName(decl).c_str(); + + // we already have the symbol + if (_undefines.find(name) != _undefines.end()) + return; + + NameAndAttributes info; + // string is owned by _undefines + info.name = ::strdup(name); + if (decl->hasExternalWeakLinkage()) + info.attributes = LTO_SYMBOL_DEFINITION_WEAKUNDEF; + else + info.attributes = LTO_SYMBOL_DEFINITION_UNDEFINED; + _undefines[name] = info; +} + + + +// Find exeternal symbols referenced by VALUE. This is a recursive function. +void LTOModule::findExternalRefs(Value* value, Mangler &mangler) { + + if (GlobalValue* gv = dyn_cast<GlobalValue>(value)) { + if ( !gv->hasExternalLinkage() ) + addPotentialUndefinedSymbol(gv, mangler); + // If this is a variable definition, do not recursively process + // initializer. It might contain a reference to this variable + // and cause an infinite loop. The initializer will be + // processed in addDefinedDataSymbol(). + return; + } + + // GlobalValue, even with InternalLinkage type, may have operands with + // ExternalLinkage type. Do not ignore these operands. + if (Constant* c = dyn_cast<Constant>(value)) { + // Handle ConstantExpr, ConstantStruct, ConstantArry etc.. + for (unsigned i = 0, e = c->getNumOperands(); i != e; ++i) + findExternalRefs(c->getOperand(i), mangler); + } +} + +void LTOModule::lazyParseSymbols() +{ + if ( !_symbolsParsed ) { + _symbolsParsed = true; + + // Use mangler to add GlobalPrefix to names to match linker names. + Mangler mangler(*_module, _target->getTargetAsmInfo()->getGlobalPrefix()); + // add chars used in ObjC method names so method names aren't mangled + mangler.markCharAcceptable('['); + mangler.markCharAcceptable(']'); + mangler.markCharAcceptable('('); + mangler.markCharAcceptable(')'); + mangler.markCharAcceptable('-'); + mangler.markCharAcceptable('+'); + mangler.markCharAcceptable(' '); + + // add functions + for (Module::iterator f = _module->begin(); f != _module->end(); ++f) { + if ( f->isDeclaration() ) + addPotentialUndefinedSymbol(f, mangler); + else + addDefinedFunctionSymbol(f, mangler); + } + + // add data + for (Module::global_iterator v = _module->global_begin(), + e = _module->global_end(); v != e; ++v) { + if ( v->isDeclaration() ) + addPotentialUndefinedSymbol(v, mangler); + else + addDefinedDataSymbol(v, mangler); + } + + // add asm globals + const std::string &inlineAsm = _module->getModuleInlineAsm(); + const std::string glbl = ".globl"; + std::string asmSymbolName; + std::string::size_type pos = inlineAsm.find(glbl, 0); + while (pos != std::string::npos) { + // eat .globl + pos = pos + 6; + + // skip white space between .globl and symbol name + std::string::size_type pbegin = inlineAsm.find_first_not_of(' ', pos); + if (pbegin == std::string::npos) + break; + + // find end-of-line + std::string::size_type pend = inlineAsm.find_first_of('\n', pbegin); + if (pend == std::string::npos) + break; + + asmSymbolName.assign(inlineAsm, pbegin, pend - pbegin); + addAsmGlobalSymbol(asmSymbolName.c_str()); + + // search next .globl + pos = inlineAsm.find(glbl, pend); + } + + // make symbols for all undefines + for (StringMap<NameAndAttributes>::iterator it=_undefines.begin(); + it != _undefines.end(); ++it) { + // if this symbol also has a definition, then don't make an undefine + // because it is a tentative definition + if ( _defines.count(it->getKeyData(), it->getKeyData()+ + it->getKeyLength()) == 0 ) { + NameAndAttributes info = it->getValue(); + _symbols.push_back(info); + } + } + } +} + + +uint32_t LTOModule::getSymbolCount() +{ + lazyParseSymbols(); + return _symbols.size(); +} + + +lto_symbol_attributes LTOModule::getSymbolAttributes(uint32_t index) +{ + lazyParseSymbols(); + if ( index < _symbols.size() ) + return _symbols[index].attributes; + else + return lto_symbol_attributes(0); +} + +const char* LTOModule::getSymbolName(uint32_t index) +{ + lazyParseSymbols(); + if ( index < _symbols.size() ) + return _symbols[index].name; + else + return NULL; +} + diff --git a/tools/lto/LTOModule.h b/tools/lto/LTOModule.h new file mode 100644 index 0000000..9de02a2 --- /dev/null +++ b/tools/lto/LTOModule.h @@ -0,0 +1,112 @@ +//===-LTOModule.h - LLVM Link Time Optimizer ------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file declares the LTOModule class. +// +//===----------------------------------------------------------------------===// + +#ifndef LTO_MODULE_H +#define LTO_MODULE_H + +#include "llvm/Module.h" +#include "llvm/ADT/OwningPtr.h" +#include "llvm/Target/TargetMachine.h" +#include "llvm/ADT/StringMap.h" + +#include "llvm-c/lto.h" + +#include <vector> +#include <string> + + +// forward references to llvm classes +namespace llvm { + class Mangler; + class MemoryBuffer; + class GlobalValue; + class Value; + class Function; +} + + +// +// C++ class which implements the opaque lto_module_t +// +class LTOModule { +public: + + static bool isBitcodeFile(const void* mem, size_t length); + static bool isBitcodeFile(const char* path); + + static bool isBitcodeFileForTarget(const void* mem, + size_t length, const char* triplePrefix); + + static bool isBitcodeFileForTarget(const char* path, + const char* triplePrefix); + + static LTOModule* makeLTOModule(const char* path, std::string& errMsg); + static LTOModule* makeLTOModule(const void* mem, size_t length, + std::string& errMsg); + + const char* getTargetTriple(); + uint32_t getSymbolCount(); + lto_symbol_attributes getSymbolAttributes(uint32_t index); + const char* getSymbolName(uint32_t index); + + llvm::Module * getLLVVMModule() { return _module.get(); } + +private: + LTOModule(llvm::Module* m, llvm::TargetMachine* t); + + void lazyParseSymbols(); + void addDefinedSymbol(llvm::GlobalValue* def, + llvm::Mangler& mangler, + bool isFunction); + void addPotentialUndefinedSymbol(llvm::GlobalValue* decl, + llvm::Mangler &mangler); + void findExternalRefs(llvm::Value* value, + llvm::Mangler& mangler); + void addDefinedFunctionSymbol(llvm::Function* f, + llvm::Mangler &mangler); + void addDefinedDataSymbol(llvm::GlobalValue* v, + llvm::Mangler &mangler); + void addAsmGlobalSymbol(const char *); + void addObjCClass(llvm::GlobalVariable* clgv); + void addObjCCategory(llvm::GlobalVariable* clgv); + void addObjCClassRef(llvm::GlobalVariable* clgv); + bool objcClassNameFromExpression(llvm::Constant* c, + std::string& name); + + static bool isTargetMatch(llvm::MemoryBuffer* memBuffer, + const char* triplePrefix); + + static LTOModule* makeLTOModule(llvm::MemoryBuffer* buffer, + std::string& errMsg); + static llvm::MemoryBuffer* makeBuffer(const void* mem, size_t length); + + typedef llvm::StringMap<uint8_t> StringSet; + + struct NameAndAttributes { + const char* name; + lto_symbol_attributes attributes; + }; + + llvm::OwningPtr<llvm::Module> _module; + llvm::OwningPtr<llvm::TargetMachine> _target; + bool _symbolsParsed; + std::vector<NameAndAttributes> _symbols; + // _defines and _undefines only needed to disambiguate tentative definitions + StringSet _defines; + llvm::StringMap<NameAndAttributes> _undefines; +}; + +extern std::string getFeatureString(const char *TargetTriple); + +#endif // LTO_MODULE_H + diff --git a/tools/lto/Makefile b/tools/lto/Makefile new file mode 100644 index 0000000..f0f6da7 --- /dev/null +++ b/tools/lto/Makefile @@ -0,0 +1,47 @@ +##===- tools/lto/Makefile ----------------------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LEVEL = ../.. +LIBRARYNAME = LTO + +# Include this here so we can get the configuration of the targets +# that have been configured for construction. We have to do this +# early so we can set up LINK_COMPONENTS before including Makefile.rules +include $(LEVEL)/Makefile.config + +LINK_LIBS_IN_SHARED = 1 +SHARED_LIBRARY = 1 +DONT_BUILD_RELINKED = 1 + +LINK_COMPONENTS := $(TARGETS_TO_BUILD) ipo scalaropts linker bitreader bitwriter + +include $(LEVEL)/Makefile.common + +ifeq ($(OS),Darwin) + # set dylib internal version number to llvmCore submission number + ifdef LLVM_SUBMIT_VERSION + LLVMLibsOptions := $(LLVMLibsOptions) -Wl,-current_version \ + -Wl,$(LLVM_SUBMIT_VERSION).$(LLVM_SUBMIT_SUBVERSION) \ + -Wl,-compatibility_version -Wl,1 + endif + # extra options to override libtool defaults + LLVMLibsOptions := $(LLVMLibsOptions) \ + -no-undefined -avoid-version \ + -Wl,-exported_symbols_list -Wl,$(PROJ_SRC_DIR)/lto.exports \ + -Wl,-dead_strip \ + -Wl,-seg1addr -Wl,0xE0000000 + + # Mac OS X 10.4 and earlier tools do not allow a second -install_name on command line + DARWIN_VERS := $(shell echo $(TARGET_TRIPLE) | sed 's/.*darwin\([0-9]*\).*/\1/') + ifneq ($(DARWIN_VERS),8) + LLVMLibsOptions := $(LLVMLibsOptions) \ + -Wl,-install_name \ + -Wl,"@executable_path/../lib/lib$(LIBRARYNAME)$(SHLIBEXT)" + endif +endif diff --git a/tools/lto/lto.cpp b/tools/lto/lto.cpp new file mode 100644 index 0000000..5c3f90a --- /dev/null +++ b/tools/lto/lto.cpp @@ -0,0 +1,259 @@ +//===-lto.cpp - LLVM Link Time Optimizer ----------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements the Link Time Optimization library. This library is +// intended to be used by linker to optimize code at link time. +// +//===----------------------------------------------------------------------===// + +#include "llvm-c/lto.h" + +#include "LTOModule.h" +#include "LTOCodeGenerator.h" + + +// holds most recent error string +// *** not thread safe *** +static std::string sLastErrorString; + + + +// +// returns a printable string +// +extern const char* lto_get_version() +{ + return LTOCodeGenerator::getVersionString(); +} + +// +// returns the last error string or NULL if last operation was successful +// +const char* lto_get_error_message() +{ + return sLastErrorString.c_str(); +} + + + +// +// validates if a file is a loadable object file +// +bool lto_module_is_object_file(const char* path) +{ + return LTOModule::isBitcodeFile(path); +} + + +// +// validates if a file is a loadable object file compilable for requested target +// +bool lto_module_is_object_file_for_target(const char* path, + const char* target_triplet_prefix) +{ + return LTOModule::isBitcodeFileForTarget(path, target_triplet_prefix); +} + + +// +// validates if a buffer is a loadable object file +// +bool lto_module_is_object_file_in_memory(const void* mem, size_t length) +{ + return LTOModule::isBitcodeFile(mem, length); +} + + +// +// validates if a buffer is a loadable object file compilable for the target +// +bool lto_module_is_object_file_in_memory_for_target(const void* mem, + size_t length, const char* target_triplet_prefix) +{ + return LTOModule::isBitcodeFileForTarget(mem, length, target_triplet_prefix); +} + + + +// +// loads an object file from disk +// returns NULL on error (check lto_get_error_message() for details) +// +lto_module_t lto_module_create(const char* path) +{ + return LTOModule::makeLTOModule(path, sLastErrorString); +} + + +// +// loads an object file from memory +// returns NULL on error (check lto_get_error_message() for details) +// +lto_module_t lto_module_create_from_memory(const void* mem, size_t length) +{ + return LTOModule::makeLTOModule(mem, length, sLastErrorString); +} + + +// +// frees all memory for a module +// upon return the lto_module_t is no longer valid +// +void lto_module_dispose(lto_module_t mod) +{ + delete mod; +} + + +// +// returns triplet string which the object module was compiled under +// +const char* lto_module_get_target_triple(lto_module_t mod) +{ + return mod->getTargetTriple(); +} + + +// +// returns the number of symbols in the object module +// +uint32_t lto_module_get_num_symbols(lto_module_t mod) +{ + return mod->getSymbolCount(); +} + +// +// returns the name of the ith symbol in the object module +// +const char* lto_module_get_symbol_name(lto_module_t mod, uint32_t index) +{ + return mod->getSymbolName(index); +} + + +// +// returns the attributes of the ith symbol in the object module +// +lto_symbol_attributes lto_module_get_symbol_attribute(lto_module_t mod, + uint32_t index) +{ + return mod->getSymbolAttributes(index); +} + + + + + +// +// instantiates a code generator +// returns NULL if there is an error +// +lto_code_gen_t lto_codegen_create() +{ + return new LTOCodeGenerator(); +} + + + +// +// frees all memory for a code generator +// upon return the lto_code_gen_t is no longer valid +// +void lto_codegen_dispose(lto_code_gen_t cg) +{ + delete cg; +} + + + +// +// add an object module to the set of modules for which code will be generated +// returns true on error (check lto_get_error_message() for details) +// +bool lto_codegen_add_module(lto_code_gen_t cg, lto_module_t mod) +{ + return cg->addModule(mod, sLastErrorString); +} + + +// +// sets what if any format of debug info should be generated +// returns true on error (check lto_get_error_message() for details) +// +bool lto_codegen_set_debug_model(lto_code_gen_t cg, lto_debug_model debug) +{ + return cg->setDebugInfo(debug, sLastErrorString); +} + + +// +// sets what code model to generated +// returns true on error (check lto_get_error_message() for details) +// +bool lto_codegen_set_pic_model(lto_code_gen_t cg, lto_codegen_model model) +{ + return cg->setCodePICModel(model, sLastErrorString); +} + +// +// sets the path to gcc +// +void lto_codegen_set_gcc_path(lto_code_gen_t cg, const char* path) +{ + cg->setGccPath(path); +} + +// +// adds to a list of all global symbols that must exist in the final +// generated code. If a function is not listed there, it might be +// inlined into every usage and optimized away. +// +void lto_codegen_add_must_preserve_symbol(lto_code_gen_t cg, const char* symbol) +{ + cg->addMustPreserveSymbol(symbol); +} + + +// +// writes a new file at the specified path that contains the +// merged contents of all modules added so far. +// returns true on error (check lto_get_error_message() for details) +// +bool lto_codegen_write_merged_modules(lto_code_gen_t cg, const char* path) +{ + return cg->writeMergedModules(path, sLastErrorString); +} + + +// +// Generates code for all added modules into one native object file. +// On sucess returns a pointer to a generated mach-o/ELF buffer and +// length set to the buffer size. The buffer is owned by the +// lto_code_gen_t and will be freed when lto_codegen_dispose() +// is called, or lto_codegen_compile() is called again. +// On failure, returns NULL (check lto_get_error_message() for details). +// +extern const void* +lto_codegen_compile(lto_code_gen_t cg, size_t* length) +{ + return cg->compile(length, sLastErrorString); +} + + +// +// Used to pass extra options to the code generator +// +extern void +lto_codegen_debug_options(lto_code_gen_t cg, const char * opt) +{ + cg->setCodeGenDebugOptions(opt); +} + + + diff --git a/tools/lto/lto.exports b/tools/lto/lto.exports new file mode 100644 index 0000000..aff7559 --- /dev/null +++ b/tools/lto/lto.exports @@ -0,0 +1,23 @@ +_lto_get_error_message +_lto_get_version +_lto_module_create +_lto_module_create_from_memory +_lto_module_get_num_symbols +_lto_module_get_symbol_attribute +_lto_module_get_symbol_name +_lto_module_get_target_triple +_lto_module_is_object_file +_lto_module_is_object_file_for_target +_lto_module_is_object_file_in_memory +_lto_module_is_object_file_in_memory_for_target +_lto_module_dispose +_lto_codegen_add_module +_lto_codegen_add_must_preserve_symbol +_lto_codegen_compile +_lto_codegen_create +_lto_codegen_dispose +_lto_codegen_set_debug_model +_lto_codegen_set_pic_model +_lto_codegen_write_merged_modules +_lto_codegen_debug_options + diff --git a/tools/opt/AnalysisWrappers.cpp b/tools/opt/AnalysisWrappers.cpp new file mode 100644 index 0000000..631a0dd --- /dev/null +++ b/tools/opt/AnalysisWrappers.cpp @@ -0,0 +1,88 @@ +//===- AnalysisWrappers.cpp - Wrappers around non-pass analyses -----------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines pass wrappers around LLVM analyses that don't make sense to +// be passes. It provides a nice standard pass interface to these classes so +// that they can be printed out by analyze. +// +// These classes are separated out of analyze.cpp so that it is more clear which +// code is the integral part of the analyze tool, and which part of the code is +// just making it so more passes are available. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Module.h" +#include "llvm/Pass.h" +#include "llvm/Support/CallSite.h" +#include "llvm/Analysis/CallGraph.h" +#include <iostream> +using namespace llvm; + +namespace { + /// ExternalFunctionsPassedConstants - This pass prints out call sites to + /// external functions that are called with constant arguments. This can be + /// useful when looking for standard library functions we should constant fold + /// or handle in alias analyses. + struct ExternalFunctionsPassedConstants : public ModulePass { + static char ID; // Pass ID, replacement for typeid + ExternalFunctionsPassedConstants() : ModulePass(&ID) {} + virtual bool runOnModule(Module &M) { + for (Module::iterator I = M.begin(), E = M.end(); I != E; ++I) + if (I->isDeclaration()) { + bool PrintedFn = false; + for (Value::use_iterator UI = I->use_begin(), E = I->use_end(); + UI != E; ++UI) + if (Instruction *User = dyn_cast<Instruction>(*UI)) { + CallSite CS = CallSite::get(User); + if (CS.getInstruction()) { + for (CallSite::arg_iterator AI = CS.arg_begin(), + E = CS.arg_end(); AI != E; ++AI) + if (isa<Constant>(*AI)) { + if (!PrintedFn) { + std::cerr << "Function '" << I->getName() << "':\n"; + PrintedFn = true; + } + std::cerr << *User; + break; + } + } + } + } + + return false; + } + + virtual void getAnalysisUsage(AnalysisUsage &AU) const { + AU.setPreservesAll(); + } + }; + + char ExternalFunctionsPassedConstants::ID = 0; + RegisterPass<ExternalFunctionsPassedConstants> + P1("print-externalfnconstants", + "Print external fn callsites passed constants"); + + struct CallGraphPrinter : public ModulePass { + static char ID; // Pass ID, replacement for typeid + CallGraphPrinter() : ModulePass(&ID) {} + + virtual void getAnalysisUsage(AnalysisUsage &AU) const { + AU.setPreservesAll(); + AU.addRequiredTransitive<CallGraph>(); + } + virtual bool runOnModule(Module &M) { + getAnalysis<CallGraph>().print(std::cerr, &M); + return false; + } + }; + + char CallGraphPrinter::ID = 0; + RegisterPass<CallGraphPrinter> + P2("print-callgraph", "Print a call graph"); +} diff --git a/tools/opt/CMakeLists.txt b/tools/opt/CMakeLists.txt new file mode 100644 index 0000000..efcca80 --- /dev/null +++ b/tools/opt/CMakeLists.txt @@ -0,0 +1,9 @@ +set(LLVM_REQUIRES_EH 1) +set(LLVM_LINK_COMPONENTS bitreader bitwriter instrumentation scalaropts ipo) + +add_llvm_tool(opt + AnalysisWrappers.cpp + GraphPrinters.cpp + PrintSCC.cpp + opt.cpp + ) diff --git a/tools/opt/GraphPrinters.cpp b/tools/opt/GraphPrinters.cpp new file mode 100644 index 0000000..a52baf7 --- /dev/null +++ b/tools/opt/GraphPrinters.cpp @@ -0,0 +1,115 @@ +//===- GraphPrinters.cpp - DOT printers for various graph types -----------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines several printers for various different types of graphs used +// by the LLVM infrastructure. It uses the generic graph interface to convert +// the graph into a .dot graph. These graphs can then be processed with the +// "dot" tool to convert them to postscript or some other suitable format. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Support/GraphWriter.h" +#include "llvm/Pass.h" +#include "llvm/Value.h" +#include "llvm/Analysis/CallGraph.h" +#include "llvm/Analysis/Dominators.h" +#include <iostream> +#include <fstream> +using namespace llvm; + +template<typename GraphType> +static void WriteGraphToFile(std::ostream &O, const std::string &GraphName, + const GraphType >) { + std::string Filename = GraphName + ".dot"; + O << "Writing '" << Filename << "'..."; + std::ofstream F(Filename.c_str()); + + if (F.good()) + WriteGraph(F, GT); + else + O << " error opening file for writing!"; + O << "\n"; +} + + +//===----------------------------------------------------------------------===// +// Call Graph Printer +//===----------------------------------------------------------------------===// + +namespace llvm { + template<> + struct DOTGraphTraits<CallGraph*> : public DefaultDOTGraphTraits { + static std::string getGraphName(CallGraph *F) { + return "Call Graph"; + } + + static std::string getNodeLabel(CallGraphNode *Node, CallGraph *Graph) { + if (Node->getFunction()) + return ((Value*)Node->getFunction())->getName(); + else + return "Indirect call node"; + } + }; +} + + +namespace { + struct CallGraphPrinter : public ModulePass { + static char ID; // Pass ID, replacement for typeid + CallGraphPrinter() : ModulePass(&ID) {} + + virtual bool runOnModule(Module &M) { + WriteGraphToFile(std::cerr, "callgraph", &getAnalysis<CallGraph>()); + return false; + } + + void print(std::ostream &OS) const {} + void print(std::ostream &OS, const llvm::Module*) const {} + + virtual void getAnalysisUsage(AnalysisUsage &AU) const { + AU.addRequired<CallGraph>(); + AU.setPreservesAll(); + } + }; + + char CallGraphPrinter::ID = 0; + RegisterPass<CallGraphPrinter> P2("dot-callgraph", + "Print Call Graph to 'dot' file"); +} + +//===----------------------------------------------------------------------===// +// DomInfoPrinter Pass +//===----------------------------------------------------------------------===// + +namespace { + class DomInfoPrinter : public FunctionPass { + public: + static char ID; // Pass identification, replacement for typeid + DomInfoPrinter() : FunctionPass(&ID) {} + + virtual void getAnalysisUsage(AnalysisUsage &AU) const { + AU.setPreservesAll(); + AU.addRequired<DominatorTree>(); + AU.addRequired<DominanceFrontier>(); + + } + + virtual bool runOnFunction(Function &F) { + DominatorTree &DT = getAnalysis<DominatorTree>(); + DT.dump(); + DominanceFrontier &DF = getAnalysis<DominanceFrontier>(); + DF.dump(); + return false; + } + }; + + char DomInfoPrinter::ID = 0; + static RegisterPass<DomInfoPrinter> + DIP("print-dom-info", "Dominator Info Printer", true, true); +} diff --git a/tools/opt/Makefile b/tools/opt/Makefile new file mode 100644 index 0000000..0afb002 --- /dev/null +++ b/tools/opt/Makefile @@ -0,0 +1,15 @@ +##===- tools/opt/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 = opt +REQUIRES_EH := 1 + +LINK_COMPONENTS := bitreader bitwriter instrumentation scalaropts ipo + +include $(LEVEL)/Makefile.common diff --git a/tools/opt/PrintSCC.cpp b/tools/opt/PrintSCC.cpp new file mode 100644 index 0000000..be65264 --- /dev/null +++ b/tools/opt/PrintSCC.cpp @@ -0,0 +1,112 @@ +//===- PrintSCC.cpp - Enumerate SCCs in some key graphs -------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file provides passes to print out SCCs in a CFG or a CallGraph. +// Normally, you would not use these passes; instead, you would use the +// scc_iterator directly to enumerate SCCs and process them in some way. These +// passes serve three purposes: +// +// (1) As a reference for how to use the scc_iterator. +// (2) To print out the SCCs for a CFG or a CallGraph: +// analyze -print-cfg-sccs to print the SCCs in each CFG of a module. +// analyze -print-cfg-sccs -stats to print the #SCCs and the maximum SCC size. +// analyze -print-cfg-sccs -debug > /dev/null to watch the algorithm in action. +// +// and similarly: +// analyze -print-callgraph-sccs [-stats] [-debug] to print SCCs in the CallGraph +// +// (3) To test the scc_iterator. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Pass.h" +#include "llvm/Module.h" +#include "llvm/Analysis/CallGraph.h" +#include "llvm/Support/CFG.h" +#include "llvm/ADT/SCCIterator.h" +#include <iostream> +using namespace llvm; + +namespace { + struct CFGSCC : public FunctionPass { + static char ID; // Pass identification, replacement for typeid + CFGSCC() : FunctionPass(&ID) {} + bool runOnFunction(Function& func); + + void print(std::ostream &O, const Module* = 0) const { } + + virtual void getAnalysisUsage(AnalysisUsage &AU) const { + AU.setPreservesAll(); + } + }; + + struct CallGraphSCC : public ModulePass { + static char ID; // Pass identification, replacement for typeid + CallGraphSCC() : ModulePass(&ID) {} + + // run - Print out SCCs in the call graph for the specified module. + bool runOnModule(Module &M); + + void print(std::ostream &O, const Module* = 0) const { } + + // getAnalysisUsage - This pass requires the CallGraph. + virtual void getAnalysisUsage(AnalysisUsage &AU) const { + AU.setPreservesAll(); + AU.addRequired<CallGraph>(); + } + }; + + char CFGSCC::ID = 0; + RegisterPass<CFGSCC> + Y("print-cfg-sccs", "Print SCCs of each function CFG"); + + char CallGraphSCC::ID = 0; + RegisterPass<CallGraphSCC> + Z("print-callgraph-sccs", "Print SCCs of the Call Graph"); +} + +bool CFGSCC::runOnFunction(Function &F) { + unsigned sccNum = 0; + std::cout << "SCCs for Function " << F.getName() << " in PostOrder:"; + for (scc_iterator<Function*> SCCI = scc_begin(&F), + E = scc_end(&F); SCCI != E; ++SCCI) { + std::vector<BasicBlock*> &nextSCC = *SCCI; + std::cout << "\nSCC #" << ++sccNum << " : "; + for (std::vector<BasicBlock*>::const_iterator I = nextSCC.begin(), + E = nextSCC.end(); I != E; ++I) + std::cout << (*I)->getName() << ", "; + if (nextSCC.size() == 1 && SCCI.hasLoop()) + std::cout << " (Has self-loop)."; + } + std::cout << "\n"; + + return true; +} + + +// run - Print out SCCs in the call graph for the specified module. +bool CallGraphSCC::runOnModule(Module &M) { + CallGraphNode* rootNode = getAnalysis<CallGraph>().getRoot(); + unsigned sccNum = 0; + std::cout << "SCCs for the program in PostOrder:"; + for (scc_iterator<CallGraphNode*> SCCI = scc_begin(rootNode), + E = scc_end(rootNode); SCCI != E; ++SCCI) { + const std::vector<CallGraphNode*> &nextSCC = *SCCI; + std::cout << "\nSCC #" << ++sccNum << " : "; + for (std::vector<CallGraphNode*>::const_iterator I = nextSCC.begin(), + E = nextSCC.end(); I != E; ++I) + std::cout << ((*I)->getFunction() ? (*I)->getFunction()->getName() + : std::string("Indirect CallGraph node")) << ", "; + if (nextSCC.size() == 1 && SCCI.hasLoop()) + std::cout << " (Has self-loop)."; + } + std::cout << "\n"; + + return true; +} diff --git a/tools/opt/opt.cpp b/tools/opt/opt.cpp new file mode 100644 index 0000000..680353a --- /dev/null +++ b/tools/opt/opt.cpp @@ -0,0 +1,602 @@ +//===- opt.cpp - The LLVM Modular Optimizer -------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Optimizations may be specified an arbitrary number of times on the command +// line, They are run in the order specified. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Module.h" +#include "llvm/ModuleProvider.h" +#include "llvm/PassManager.h" +#include "llvm/CallGraphSCCPass.h" +#include "llvm/Bitcode/ReaderWriter.h" +#include "llvm/Assembly/PrintModulePass.h" +#include "llvm/Analysis/Verifier.h" +#include "llvm/Analysis/LoopPass.h" +#include "llvm/Analysis/CallGraph.h" +#include "llvm/Target/TargetData.h" +#include "llvm/Target/TargetMachine.h" +#include "llvm/Support/PassNameParser.h" +#include "llvm/System/Signals.h" +#include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/PluginLoader.h" +#include "llvm/Support/Streams.h" +#include "llvm/Support/SystemUtils.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/LinkAllPasses.h" +#include "llvm/LinkAllVMCore.h" +#include <iostream> +#include <fstream> +#include <memory> +#include <algorithm> +using namespace llvm; + +// The OptimizationList is automatically populated with registered Passes by the +// PassNameParser. +// +static cl::list<const PassInfo*, bool, PassNameParser> +PassList(cl::desc("Optimizations available:")); + +// Other command line options... +// +static cl::opt<std::string> +InputFilename(cl::Positional, cl::desc("<input bitcode file>"), + cl::init("-"), cl::value_desc("filename")); + +static cl::opt<std::string> +OutputFilename("o", cl::desc("Override output filename"), + cl::value_desc("filename"), cl::init("-")); + +static cl::opt<bool> +Force("f", cl::desc("Overwrite output files")); + +static cl::opt<bool> +PrintEachXForm("p", cl::desc("Print module after each transformation")); + +static cl::opt<bool> +NoOutput("disable-output", + cl::desc("Do not write result bitcode file"), cl::Hidden); + +static cl::opt<bool> +NoVerify("disable-verify", cl::desc("Do not verify result module"), cl::Hidden); + +static cl::opt<bool> +VerifyEach("verify-each", cl::desc("Verify after each transform")); + +static cl::opt<bool> +StripDebug("strip-debug", + cl::desc("Strip debugger symbol info from translation unit")); + +static cl::opt<bool> +DisableInline("disable-inlining", cl::desc("Do not run the inliner pass")); + +static cl::opt<bool> +DisableOptimizations("disable-opt", + cl::desc("Do not run any optimization passes")); + +static cl::opt<bool> +StandardCompileOpts("std-compile-opts", + cl::desc("Include the standard compile time optimizations")); + +static cl::opt<bool> +OptLevelO1("O1", + cl::desc("Optimization level 1. Similar to llvm-gcc -O1")); + +static cl::opt<bool> +OptLevelO2("O2", + cl::desc("Optimization level 2. Similar to llvm-gcc -O2")); + +static cl::opt<bool> +OptLevelO3("O3", + cl::desc("Optimization level 3. Similar to llvm-gcc -O3")); + +static cl::opt<bool> +UnitAtATime("funit-at-a-time", + cl::desc("Enable IPO. This is same as llvm-gcc's -funit-at-a-time")); + +static cl::opt<bool> +DisableSimplifyLibCalls("disable-simplify-libcalls", + cl::desc("Disable simplify-libcalls")); + +static cl::opt<bool> +Quiet("q", cl::desc("Obsolete option"), cl::Hidden); + +static cl::alias +QuietA("quiet", cl::desc("Alias for -q"), cl::aliasopt(Quiet)); + +static cl::opt<bool> +AnalyzeOnly("analyze", cl::desc("Only perform analysis, no optimization")); + +// ---------- Define Printers for module and function passes ------------ +namespace { + +struct CallGraphSCCPassPrinter : public CallGraphSCCPass { + static char ID; + const PassInfo *PassToPrint; + CallGraphSCCPassPrinter(const PassInfo *PI) : + CallGraphSCCPass(&ID), PassToPrint(PI) {} + + virtual bool runOnSCC(const std::vector<CallGraphNode *>&SCC) { + if (!Quiet) { + cout << "Printing analysis '" << PassToPrint->getPassName() << "':\n"; + + for (unsigned i = 0, e = SCC.size(); i != e; ++i) { + Function *F = SCC[i]->getFunction(); + if (F) + getAnalysisID<Pass>(PassToPrint).print(cout, F->getParent()); + } + } + // Get and print pass... + return false; + } + + virtual const char *getPassName() const { return "'Pass' Printer"; } + + virtual void getAnalysisUsage(AnalysisUsage &AU) const { + AU.addRequiredID(PassToPrint); + AU.setPreservesAll(); + } +}; + +char CallGraphSCCPassPrinter::ID = 0; + +struct ModulePassPrinter : public ModulePass { + static char ID; + const PassInfo *PassToPrint; + ModulePassPrinter(const PassInfo *PI) : ModulePass(&ID), + PassToPrint(PI) {} + + virtual bool runOnModule(Module &M) { + if (!Quiet) { + cout << "Printing analysis '" << PassToPrint->getPassName() << "':\n"; + getAnalysisID<Pass>(PassToPrint).print(cout, &M); + } + + // Get and print pass... + return false; + } + + virtual const char *getPassName() const { return "'Pass' Printer"; } + + virtual void getAnalysisUsage(AnalysisUsage &AU) const { + AU.addRequiredID(PassToPrint); + AU.setPreservesAll(); + } +}; + +char ModulePassPrinter::ID = 0; +struct FunctionPassPrinter : public FunctionPass { + const PassInfo *PassToPrint; + static char ID; + FunctionPassPrinter(const PassInfo *PI) : FunctionPass(&ID), + PassToPrint(PI) {} + + virtual bool runOnFunction(Function &F) { + if (!Quiet) { + cout << "Printing analysis '" << PassToPrint->getPassName() + << "' for function '" << F.getName() << "':\n"; + } + // Get and print pass... + getAnalysisID<Pass>(PassToPrint).print(cout, F.getParent()); + return false; + } + + virtual const char *getPassName() const { return "FunctionPass Printer"; } + + virtual void getAnalysisUsage(AnalysisUsage &AU) const { + AU.addRequiredID(PassToPrint); + AU.setPreservesAll(); + } +}; + +char FunctionPassPrinter::ID = 0; + +struct LoopPassPrinter : public LoopPass { + static char ID; + const PassInfo *PassToPrint; + LoopPassPrinter(const PassInfo *PI) : + LoopPass(&ID), PassToPrint(PI) {} + + virtual bool runOnLoop(Loop *L, LPPassManager &LPM) { + if (!Quiet) { + cout << "Printing analysis '" << PassToPrint->getPassName() << "':\n"; + getAnalysisID<Pass>(PassToPrint).print(cout, + L->getHeader()->getParent()->getParent()); + } + // Get and print pass... + return false; + } + + virtual const char *getPassName() const { return "'Pass' Printer"; } + + virtual void getAnalysisUsage(AnalysisUsage &AU) const { + AU.addRequiredID(PassToPrint); + AU.setPreservesAll(); + } +}; + +char LoopPassPrinter::ID = 0; + +struct BasicBlockPassPrinter : public BasicBlockPass { + const PassInfo *PassToPrint; + static char ID; + BasicBlockPassPrinter(const PassInfo *PI) + : BasicBlockPass(&ID), PassToPrint(PI) {} + + virtual bool runOnBasicBlock(BasicBlock &BB) { + if (!Quiet) { + cout << "Printing Analysis info for BasicBlock '" << BB.getName() + << "': Pass " << PassToPrint->getPassName() << ":\n"; + } + + // Get and print pass... + getAnalysisID<Pass>(PassToPrint).print(cout, BB.getParent()->getParent()); + return false; + } + + virtual const char *getPassName() const { return "BasicBlockPass Printer"; } + + virtual void getAnalysisUsage(AnalysisUsage &AU) const { + AU.addRequiredID(PassToPrint); + AU.setPreservesAll(); + } +}; + +char BasicBlockPassPrinter::ID = 0; +inline void addPass(PassManager &PM, Pass *P) { + // Add the pass to the pass manager... + PM.add(P); + + // If we are verifying all of the intermediate steps, add the verifier... + if (VerifyEach) PM.add(createVerifierPass()); +} + +/// AddOptimizationPasses - This routine adds optimization passes +/// based on selected optimization level, OptLevel. This routine +/// duplicates llvm-gcc behaviour. +/// +/// OptLevel - Optimization Level +void AddOptimizationPasses(PassManager &MPM, FunctionPassManager &FPM, + unsigned OptLevel) { + + if (OptLevel == 0) + return; + + FPM.add(createCFGSimplificationPass()); + if (OptLevel == 1) + FPM.add(createPromoteMemoryToRegisterPass()); + else + FPM.add(createScalarReplAggregatesPass()); + FPM.add(createInstructionCombiningPass()); + + if (UnitAtATime) + MPM.add(createRaiseAllocationsPass()); // call %malloc -> malloc inst + MPM.add(createCFGSimplificationPass()); // Clean up disgusting code + MPM.add(createPromoteMemoryToRegisterPass()); // Kill useless allocas + if (UnitAtATime) { + MPM.add(createGlobalOptimizerPass()); // OptLevel out global vars + MPM.add(createGlobalDCEPass()); // Remove unused fns and globs + MPM.add(createIPConstantPropagationPass()); // IP Constant Propagation + MPM.add(createDeadArgEliminationPass()); // Dead argument elimination + } + MPM.add(createInstructionCombiningPass()); // Clean up after IPCP & DAE + MPM.add(createCFGSimplificationPass()); // Clean up after IPCP & DAE + if (UnitAtATime) { + MPM.add(createPruneEHPass()); // Remove dead EH info + MPM.add(createFunctionAttrsPass()); // Deduce function attrs + } + if (OptLevel > 1) + MPM.add(createFunctionInliningPass()); // Inline small functions + if (OptLevel > 2) + MPM.add(createArgumentPromotionPass()); // Scalarize uninlined fn args + if (!DisableSimplifyLibCalls) + MPM.add(createSimplifyLibCallsPass()); // Library Call Optimizations + MPM.add(createInstructionCombiningPass()); // Cleanup for scalarrepl. + MPM.add(createJumpThreadingPass()); // Thread jumps. + MPM.add(createCFGSimplificationPass()); // Merge & remove BBs + MPM.add(createScalarReplAggregatesPass()); // Break up aggregate allocas + MPM.add(createInstructionCombiningPass()); // Combine silly seq's + MPM.add(createCondPropagationPass()); // Propagate conditionals + MPM.add(createTailCallEliminationPass()); // Eliminate tail calls + MPM.add(createCFGSimplificationPass()); // Merge & remove BBs + MPM.add(createReassociatePass()); // Reassociate expressions + MPM.add(createLoopRotatePass()); // Rotate Loop + MPM.add(createLICMPass()); // Hoist loop invariants + MPM.add(createLoopUnswitchPass()); + MPM.add(createLoopIndexSplitPass()); // Split loop index + MPM.add(createInstructionCombiningPass()); + MPM.add(createIndVarSimplifyPass()); // Canonicalize indvars + MPM.add(createLoopDeletionPass()); // Delete dead loops + if (OptLevel > 1) + MPM.add(createLoopUnrollPass()); // Unroll small loops + MPM.add(createInstructionCombiningPass()); // Clean up after the unroller + MPM.add(createGVNPass()); // Remove redundancies + MPM.add(createMemCpyOptPass()); // Remove memcpy / form memset + MPM.add(createSCCPPass()); // Constant prop with SCCP + + // Run instcombine after redundancy elimination to exploit opportunities + // opened up by them. + MPM.add(createInstructionCombiningPass()); + MPM.add(createCondPropagationPass()); // Propagate conditionals + MPM.add(createDeadStoreEliminationPass()); // Delete dead stores + MPM.add(createAggressiveDCEPass()); // Delete dead instructions + MPM.add(createCFGSimplificationPass()); // Merge & remove BBs + + if (UnitAtATime) { + MPM.add(createStripDeadPrototypesPass()); // Get rid of dead prototypes + MPM.add(createDeadTypeEliminationPass()); // Eliminate dead types + } + + if (OptLevel > 1 && UnitAtATime) + MPM.add(createConstantMergePass()); // Merge dup global constants + + return; +} + +void AddStandardCompilePasses(PassManager &PM) { + PM.add(createVerifierPass()); // Verify that input is correct + + addPass(PM, createLowerSetJmpPass()); // Lower llvm.setjmp/.longjmp + + // If the -strip-debug command line option was specified, do it. + if (StripDebug) + addPass(PM, createStripSymbolsPass(true)); + + if (DisableOptimizations) return; + + addPass(PM, createRaiseAllocationsPass()); // call %malloc -> malloc inst + addPass(PM, createCFGSimplificationPass()); // Clean up disgusting code + addPass(PM, createPromoteMemoryToRegisterPass());// Kill useless allocas + addPass(PM, createGlobalOptimizerPass()); // Optimize out global vars + addPass(PM, createGlobalDCEPass()); // Remove unused fns and globs + addPass(PM, createIPConstantPropagationPass());// IP Constant Propagation + addPass(PM, createDeadArgEliminationPass()); // Dead argument elimination + addPass(PM, createInstructionCombiningPass()); // Clean up after IPCP & DAE + addPass(PM, createCFGSimplificationPass()); // Clean up after IPCP & DAE + + addPass(PM, createPruneEHPass()); // Remove dead EH info + addPass(PM, createFunctionAttrsPass()); // Deduce function attrs + + if (!DisableInline) + addPass(PM, createFunctionInliningPass()); // Inline small functions + addPass(PM, createArgumentPromotionPass()); // Scalarize uninlined fn args + + addPass(PM, createSimplifyLibCallsPass()); // Library Call Optimizations + addPass(PM, createInstructionCombiningPass()); // Cleanup for scalarrepl. + addPass(PM, createJumpThreadingPass()); // Thread jumps. + addPass(PM, createCFGSimplificationPass()); // Merge & remove BBs + addPass(PM, createScalarReplAggregatesPass()); // Break up aggregate allocas + addPass(PM, createInstructionCombiningPass()); // Combine silly seq's + addPass(PM, createCondPropagationPass()); // Propagate conditionals + + addPass(PM, createTailCallEliminationPass()); // Eliminate tail calls + addPass(PM, createCFGSimplificationPass()); // Merge & remove BBs + addPass(PM, createReassociatePass()); // Reassociate expressions + addPass(PM, createLoopRotatePass()); + addPass(PM, createLICMPass()); // Hoist loop invariants + addPass(PM, createLoopUnswitchPass()); // Unswitch loops. + addPass(PM, createLoopIndexSplitPass()); // Index split loops. + // FIXME : Removing instcombine causes nestedloop regression. + addPass(PM, createInstructionCombiningPass()); + addPass(PM, createIndVarSimplifyPass()); // Canonicalize indvars + addPass(PM, createLoopDeletionPass()); // Delete dead loops + addPass(PM, createLoopUnrollPass()); // Unroll small loops + addPass(PM, createInstructionCombiningPass()); // Clean up after the unroller + addPass(PM, createGVNPass()); // Remove redundancies + addPass(PM, createMemCpyOptPass()); // Remove memcpy / form memset + addPass(PM, createSCCPPass()); // Constant prop with SCCP + + // Run instcombine after redundancy elimination to exploit opportunities + // opened up by them. + addPass(PM, createInstructionCombiningPass()); + addPass(PM, createCondPropagationPass()); // Propagate conditionals + + addPass(PM, createDeadStoreEliminationPass()); // Delete dead stores + addPass(PM, createAggressiveDCEPass()); // Delete dead instructions + addPass(PM, createCFGSimplificationPass()); // Merge & remove BBs + addPass(PM, createStripDeadPrototypesPass()); // Get rid of dead prototypes + addPass(PM, createDeadTypeEliminationPass()); // Eliminate dead types + addPass(PM, createConstantMergePass()); // Merge dup global constants +} + +} // anonymous namespace + + +//===----------------------------------------------------------------------===// +// main for opt +// +int main(int argc, char **argv) { + llvm_shutdown_obj X; // Call llvm_shutdown() on exit. + try { + cl::ParseCommandLineOptions(argc, argv, + "llvm .bc -> .bc modular optimizer and analysis printer\n"); + sys::PrintStackTraceOnErrorSignal(); + + // Allocate a full target machine description only if necessary. + // FIXME: The choice of target should be controllable on the command line. + std::auto_ptr<TargetMachine> target; + + std::string ErrorMessage; + + // Load the input module... + std::auto_ptr<Module> M; + if (MemoryBuffer *Buffer + = MemoryBuffer::getFileOrSTDIN(InputFilename, &ErrorMessage)) { + M.reset(ParseBitcodeFile(Buffer, &ErrorMessage)); + delete Buffer; + } + + if (M.get() == 0) { + cerr << argv[0] << ": "; + if (ErrorMessage.size()) + cerr << ErrorMessage << "\n"; + else + cerr << "bitcode didn't read correctly.\n"; + return 1; + } + + // Figure out what stream we are supposed to write to... + // FIXME: cout is not binary! + std::ostream *Out = &std::cout; // Default to printing to stdout... + if (OutputFilename != "-") { + if (!Force && std::ifstream(OutputFilename.c_str())) { + // If force is not specified, make sure not to overwrite a file! + cerr << argv[0] << ": error opening '" << OutputFilename + << "': file exists!\n" + << "Use -f command line argument to force output\n"; + return 1; + } + std::ios::openmode io_mode = std::ios::out | std::ios::trunc | + std::ios::binary; + Out = new std::ofstream(OutputFilename.c_str(), io_mode); + + if (!Out->good()) { + cerr << argv[0] << ": error opening " << OutputFilename << "!\n"; + return 1; + } + + // Make sure that the Output file gets unlinked from the disk if we get a + // SIGINT + sys::RemoveFileOnSignal(sys::Path(OutputFilename)); + } + + // If the output is set to be emitted to standard out, and standard out is a + // console, print out a warning message and refuse to do it. We don't + // impress anyone by spewing tons of binary goo to a terminal. + if (!Force && !NoOutput && CheckBitcodeOutputToConsole(Out,!Quiet)) { + NoOutput = true; + } + + // Create a PassManager to hold and optimize the collection of passes we are + // about to build... + // + PassManager Passes; + + // Add an appropriate TargetData instance for this module... + Passes.add(new TargetData(M.get())); + + FunctionPassManager *FPasses = NULL; + if (OptLevelO1 || OptLevelO2 || OptLevelO3) { + FPasses = new FunctionPassManager(new ExistingModuleProvider(M.get())); + FPasses->add(new TargetData(M.get())); + } + + // If the -strip-debug command line option was specified, add it. If + // -std-compile-opts was also specified, it will handle StripDebug. + if (StripDebug && !StandardCompileOpts) + addPass(Passes, createStripSymbolsPass(true)); + + // Create a new optimization pass for each one specified on the command line + for (unsigned i = 0; i < PassList.size(); ++i) { + // Check to see if -std-compile-opts was specified before this option. If + // so, handle it. + if (StandardCompileOpts && + StandardCompileOpts.getPosition() < PassList.getPosition(i)) { + AddStandardCompilePasses(Passes); + StandardCompileOpts = false; + } + + if (OptLevelO1 && OptLevelO1.getPosition() < PassList.getPosition(i)) { + AddOptimizationPasses(Passes, *FPasses, 1); + OptLevelO1 = false; + } + + if (OptLevelO2 && OptLevelO2.getPosition() < PassList.getPosition(i)) { + AddOptimizationPasses(Passes, *FPasses, 2); + OptLevelO2 = false; + } + + if (OptLevelO3 && OptLevelO3.getPosition() < PassList.getPosition(i)) { + AddOptimizationPasses(Passes, *FPasses, 3); + OptLevelO3 = false; + } + + const PassInfo *PassInf = PassList[i]; + Pass *P = 0; + if (PassInf->getNormalCtor()) + P = PassInf->getNormalCtor()(); + else + cerr << argv[0] << ": cannot create pass: " + << PassInf->getPassName() << "\n"; + if (P) { + bool isBBPass = dynamic_cast<BasicBlockPass*>(P) != 0; + bool isLPass = !isBBPass && dynamic_cast<LoopPass*>(P) != 0; + bool isFPass = !isLPass && dynamic_cast<FunctionPass*>(P) != 0; + bool isCGSCCPass = !isFPass && dynamic_cast<CallGraphSCCPass*>(P) != 0; + + addPass(Passes, P); + + if (AnalyzeOnly) { + if (isBBPass) + Passes.add(new BasicBlockPassPrinter(PassInf)); + else if (isLPass) + Passes.add(new LoopPassPrinter(PassInf)); + else if (isFPass) + Passes.add(new FunctionPassPrinter(PassInf)); + else if (isCGSCCPass) + Passes.add(new CallGraphSCCPassPrinter(PassInf)); + else + Passes.add(new ModulePassPrinter(PassInf)); + } + } + + if (PrintEachXForm) + Passes.add(createPrintModulePass(&errs())); + } + + // If -std-compile-opts was specified at the end of the pass list, add them. + if (StandardCompileOpts) { + AddStandardCompilePasses(Passes); + StandardCompileOpts = false; + } + + if (OptLevelO1) { + AddOptimizationPasses(Passes, *FPasses, 1); + } + + if (OptLevelO2) { + AddOptimizationPasses(Passes, *FPasses, 2); + } + + if (OptLevelO3) { + AddOptimizationPasses(Passes, *FPasses, 3); + } + + if (OptLevelO1 || OptLevelO2 || OptLevelO3) { + for (Module::iterator I = M.get()->begin(), E = M.get()->end(); + I != E; ++I) + FPasses->run(*I); + } + + // Check that the module is well formed on completion of optimization + if (!NoVerify && !VerifyEach) + Passes.add(createVerifierPass()); + + // Write bitcode out to disk or cout as the last step... + if (!NoOutput && !AnalyzeOnly) + Passes.add(CreateBitcodeWriterPass(*Out)); + + // Now that we have all of the passes ready, run them. + Passes.run(*M.get()); + + // Delete the ofstream. + if (Out != &std::cout) + delete Out; + return 0; + + } catch (const std::string& msg) { + cerr << argv[0] << ": " << msg << "\n"; + } catch (...) { + cerr << argv[0] << ": Unexpected unknown exception occurred.\n"; + } + llvm_shutdown(); + return 1; +} |