diff options
Diffstat (limited to 'tools')
59 files changed, 3719 insertions, 29 deletions
diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 6b7c884516..e32aef3169 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -14,6 +14,7 @@ add_subdirectory(llvm-dis) add_subdirectory(llvm-mc) add_subdirectory(llc) +add_subdirectory(pnacl-llc) add_subdirectory(llvm-ranlib) add_subdirectory(llvm-ar) add_subdirectory(llvm-nm) @@ -42,6 +43,11 @@ add_subdirectory(llvm-stress) add_subdirectory(llvm-mcmarkup) add_subdirectory(llvm-symbolizer) +add_subdirectory(pnacl-abicheck) +add_subdirectory(pnacl-bcanalyzer) +add_subdirectory(pnacl-freeze) +add_subdirectory(pnacl-thaw) +add_subdirectory(bc-wrap) add_subdirectory(obj2yaml) add_subdirectory(yaml2obj) diff --git a/tools/LLVMBuild.txt b/tools/LLVMBuild.txt index 25aa177b35..d7160f774d 100644 --- a/tools/LLVMBuild.txt +++ b/tools/LLVMBuild.txt @@ -16,7 +16,7 @@ ;===------------------------------------------------------------------------===; [common] -subdirectories = bugpoint llc lli llvm-ar llvm-as llvm-bcanalyzer llvm-cov llvm-diff llvm-dis llvm-dwarfdump llvm-extract llvm-jitlistener llvm-link llvm-mc llvm-nm llvm-objdump llvm-prof llvm-ranlib llvm-rtdyld llvm-size macho-dump opt llvm-mcmarkup +subdirectories = bugpoint llc pnacl-llc lli llvm-ar llvm-as llvm-bcanalyzer llvm-cov llvm-diff llvm-dis llvm-dwarfdump llvm-extract llvm-jitlistener llvm-link llvm-mc llvm-nm llvm-objdump llvm-prof llvm-ranlib llvm-rtdyld llvm-size macho-dump opt llvm-mcmarkup pnacl-abicheck pnacl-bcanalyzer pnacl-freeze pnacl-thaw [component_0] type = Group diff --git a/tools/Makefile b/tools/Makefile index eaf9ed3577..b94f08f81c 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -28,14 +28,15 @@ OPTIONAL_DIRS := lldb # in parallel builds. Please retain this ordering. DIRS := llvm-config PARALLEL_DIRS := opt llvm-as llvm-dis \ - llc llvm-ranlib llvm-ar llvm-nm \ + llc pnacl-llc llvm-ranlib llvm-ar llvm-nm \ llvm-prof llvm-link \ lli llvm-extract llvm-mc \ bugpoint llvm-bcanalyzer \ llvm-diff macho-dump llvm-objdump llvm-readobj \ llvm-rtdyld llvm-dwarfdump llvm-cov \ - llvm-size llvm-stress llvm-mcmarkup \ - llvm-symbolizer obj2yaml yaml2obj + llvm-size llvm-stress llvm-mcmarkup bc-wrap pso-stub \ + llvm-symbolizer pnacl-abicheck pnacl-bcanalyzer pnacl-freeze \ + pnacl-thaw obj2yaml yaml2obj # If Intel JIT Events support is configured, build an extra tool to test it. ifeq ($(USE_INTEL_JITEVENTS), 1) diff --git a/tools/bc-wrap/CMakeLists.txt b/tools/bc-wrap/CMakeLists.txt new file mode 100644 index 0000000000..7d8ce4fc11 --- /dev/null +++ b/tools/bc-wrap/CMakeLists.txt @@ -0,0 +1,5 @@ +set(LLVM_LINK_COMPONENTS wrap support ) + +add_llvm_tool(bc-wrap + bc_wrap.cpp + )
\ No newline at end of file diff --git a/tools/bc-wrap/LLVMBuild.txt b/tools/bc-wrap/LLVMBuild.txt new file mode 100644 index 0000000000..a91f77625e --- /dev/null +++ b/tools/bc-wrap/LLVMBuild.txt @@ -0,0 +1,22 @@ +;===- ./tools/bc-wrap/LLVMBuild.txt ----------------------------*- Conf -*--===; +; +; The LLVM Compiler Infrastructure +; +; This file is distributed under the University of Illinois Open Source +; License. See LICENSE.TXT for details. +; +;===------------------------------------------------------------------------===; +; +; This is an LLVMBuild description file for the components in this subdirectory. +; +; For more information on the LLVMBuild system, please see: +; +; http://llvm.org/docs/LLVMBuild.html +; +;===------------------------------------------------------------------------===; + +[component_0] +type = Tool +name = bc-wrap +parent = Tools +required_libraries = Wrap Support diff --git a/tools/bc-wrap/Makefile b/tools/bc-wrap/Makefile new file mode 100644 index 0000000000..dccff2ecde --- /dev/null +++ b/tools/bc-wrap/Makefile @@ -0,0 +1,20 @@ +#===- tools/bc-wrap/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 = bc-wrap + +# 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) Wrap + +include $(LLVM_SRC_ROOT)/Makefile.rules diff --git a/tools/bc-wrap/bc_wrap.cpp b/tools/bc-wrap/bc_wrap.cpp new file mode 100644 index 0000000000..5311f714ee --- /dev/null +++ b/tools/bc-wrap/bc_wrap.cpp @@ -0,0 +1,123 @@ +/* Copyright 2012 The Native Client Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can + * be found in the LICENSE file. + */ +/* + * Utility to wrap a .bc file, using LLVM standard+ custom headers. + */ + +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Wrap/bitcode_wrapperer.h" +#include "llvm/Wrap/file_wrapper_input.h" +#include "llvm/Wrap/file_wrapper_output.h" + +#include <ctype.h> +#include <string.h> + +using namespace llvm; + +static cl::opt<std::string> +InputFilename(cl::Positional, cl::desc("<input file>"), cl::Required); + +static cl::opt<std::string> +OutputFilename("o", cl::desc("<output file>")); + +static cl::opt<bool> UnwrapFlag("u", + cl::desc("unwrap rather than wrap the file"), + cl::init(false)); + +static cl::opt<bool> VerboseFlag("v", + cl::desc("print verbose header information"), + cl::init(false)); + +static cl::opt<bool> DryRunFlag("n", + cl::desc("Dry run (implies -v)"), + cl::init(false)); + +// Accept the hash on the command line to avoid having to include sha1 +// library with the LLVM code +static cl::opt<std::string> BitcodeHash("hash", + cl::desc("Hash of bitcode (ignored if -u is given)")); + +const int kMaxBinaryHashLen = 32; + +// Convert ASCII hex hash to binary hash. return buffer and length. +// The caller must free the returned buffer. +static uint8_t* ParseBitcodeHash(int* len) { + if (BitcodeHash.size() > kMaxBinaryHashLen * 2 || + BitcodeHash.size() % 2) return NULL; + *len = BitcodeHash.size() / 2; + uint8_t* buf = new uint8_t[*len]; + const char* arg = BitcodeHash.data(); + for (size_t i = 0; i < BitcodeHash.size() / 2; i++) { + unsigned int r; // glibc has %hhx but it's nonstandard + if (!isxdigit(*(arg + 2 * i + 1)) || // sscanf ignores trailing junk + !sscanf(arg + 2 * i, "%2x", &r) || + r > std::numeric_limits<uint8_t>::max()) { + delete [] buf; + return NULL; + } + buf[i] = static_cast<uint8_t>(r); + } + return buf; +} + +int main(const int argc, const char* argv[]) { + bool success = true; + cl::ParseCommandLineOptions(argc, argv, "bitcode wrapper/unwrapper\n"); + if (OutputFilename == "") { + // Default to input file = output file. The cl lib doesn't seem to + // directly support initializing one opt from another. + OutputFilename = InputFilename; + } + if (DryRunFlag) VerboseFlag = true; + sys::fs::file_status outfile_status; + std::string outfile_temp; + outfile_temp = std::string(OutputFilename) + ".temp"; + if (UnwrapFlag) { + FileWrapperInput inbc(InputFilename); + FileWrapperOutput outbc(outfile_temp); + BitcodeWrapperer wrapperer(&inbc, &outbc); + if (wrapperer.IsInputBitcodeWrapper()) { + if (VerboseFlag) { + fprintf(stderr, "Headers read from infile:\n"); + wrapperer.PrintWrapperHeader(); + } + if (DryRunFlag) + return 0; + success = wrapperer.GenerateRawBitcodeFile(); + } + } else { + FileWrapperInput inbc(InputFilename); + FileWrapperOutput outbc(outfile_temp); + BitcodeWrapperer wrapperer(&inbc, &outbc); + if (BitcodeHash.size()) { + // SHA-2 hash is 256 bit + int hash_len; + uint8_t* buf = ParseBitcodeHash(&hash_len); + if (!buf) { + fprintf(stderr, "Bitcode hash must be a hex string <= 64 chars.\n"); + exit(1); + } + BCHeaderField hash(BCHeaderField::kBitcodeHash, hash_len, buf); + wrapperer.AddHeaderField(&hash); + } + if (VerboseFlag) { + fprintf(stderr, "Headers generated:\n"); + wrapperer.PrintWrapperHeader(); + } + if (DryRunFlag) + return 0; + success = wrapperer.GenerateWrappedBitcodeFile(); + } + error_code ec; + if ((ec = sys::fs::rename(outfile_temp, OutputFilename))) { + fprintf(stderr, "Could not rename temporary: %s\n", ec.message().c_str()); + success = false; + } + if (success) return 0; + fprintf(stderr, "error: Unable to generate a proper %s bitcode file!\n", + (UnwrapFlag ? "unwrapped" : "wrapped")); + return 1; +} diff --git a/tools/gold/Makefile b/tools/gold/Makefile index 496e31cc39..31812e1f8c 100644 --- a/tools/gold/Makefile +++ b/tools/gold/Makefile @@ -14,6 +14,10 @@ LINK_LIBS_IN_SHARED := 1 SHARED_LIBRARY := 1 LOADABLE_MODULE := 1 +# @LOCALMOD: this forces to appear -lLTO *after* the object file +# on the linkline. This is necessary for linking on ubuntu precise. +# Otherwise LLVMgold.so will not have a dt_needed entry for LTO +EXTRA_LIBS := -lLTO EXPORTED_SYMBOL_FILE = $(PROJ_SRC_DIR)/gold.exports # Include this here so we can get the configuration of the targets diff --git a/tools/gold/gold-plugin.cpp b/tools/gold/gold-plugin.cpp index 40f5fd6086..1e96762d10 100644 --- a/tools/gold/gold-plugin.cpp +++ b/tools/gold/gold-plugin.cpp @@ -52,6 +52,25 @@ namespace { ld_plugin_set_extra_library_path set_extra_library_path = NULL; ld_plugin_get_view get_view = NULL; ld_plugin_message message = discard_message; + // @LOCALMOD-BEGIN + // REL, DYN, or EXEC + ld_plugin_output_file_type linker_output; + + // Callback for getting link soname from gold + ld_plugin_get_output_soname get_output_soname = NULL; + + // Callback for getting needed libraries from gold + ld_plugin_get_needed get_needed = NULL; + + // Callback for getting number of needed library from gold + ld_plugin_get_num_needed get_num_needed = NULL; + + // Callback for getting the number of --wrap'd symbols. + ld_plugin_get_num_wrapped get_num_wrapped = NULL; + + // Callback for getting the name of a wrapped symbol. + ld_plugin_get_wrapped get_wrapped = NULL; + // @LOCALMOD-END int api_version = 0; int gold_version = 0; @@ -59,11 +78,17 @@ namespace { struct claimed_file { void *handle; std::vector<ld_plugin_symbol> syms; + bool is_linked_in; // @LOCALMOD }; lto_codegen_model output_type = LTO_CODEGEN_PIC_MODEL_STATIC; std::string output_name = ""; std::list<claimed_file> Modules; + + // @LOCALMOD-BEGIN + std::vector<std::string> DepLibs; + // @LOCALMOD-END + std::vector<sys::Path> Cleanup; lto_code_gen_t code_gen = NULL; } @@ -71,6 +96,7 @@ namespace { namespace options { enum generate_bc { BC_NO, BC_ALSO, BC_ONLY }; static bool generate_api_file = false; + static bool gather_then_link = true; // @LOCALMOD static generate_bc generate_bc_file = BC_NO; static std::string bc_path; static std::string obj_path; @@ -100,6 +126,10 @@ namespace options { triple = opt.substr(strlen("mtriple=")); } else if (opt.startswith("obj-path=")) { obj_path = opt.substr(strlen("obj-path=")); + // @LOCALMOD-BEGIN + } else if (opt == "no-gather-then-link") { + gather_then_link = false; + // @LOCALMOD-END } else if (opt == "emit-llvm") { generate_bc_file = BC_ONLY; } else if (opt == "also-emit-llvm") { @@ -120,6 +150,18 @@ namespace options { } } +// @LOCALMOD-BEGIN +static const char *get_basename(const char *path) { + if (path == NULL) + return NULL; + const char *slash = strrchr(path, '/'); + if (slash) + return slash + 1; + + return path; +} +// @LOCALMOD-END + static ld_plugin_status claim_file_hook(const ld_plugin_input_file *file, int *claimed); static ld_plugin_status all_symbols_read_hook(void); @@ -147,6 +189,10 @@ ld_plugin_status onload(ld_plugin_tv *tv) { output_name = tv->tv_u.tv_string; break; case LDPT_LINKER_OUTPUT: + // @LOCALMOD-BEGIN + linker_output = + static_cast<ld_plugin_output_file_type>(tv->tv_u.tv_val); + // @LOCALMOD-END switch (tv->tv_u.tv_val) { case LDPO_REL: // .o case LDPO_DYN: // .so @@ -210,7 +256,23 @@ ld_plugin_status onload(ld_plugin_tv *tv) { break; case LDPT_GET_VIEW: get_view = tv->tv_u.tv_get_view; + // @LOCALMOD-BEGIN + case LDPT_GET_OUTPUT_SONAME: + get_output_soname = tv->tv_u.tv_get_output_soname; break; + case LDPT_GET_NEEDED: + get_needed = tv->tv_u.tv_get_needed; + break; + case LDPT_GET_NUM_NEEDED: + get_num_needed = tv->tv_u.tv_get_num_needed; + break; + case LDPT_GET_WRAPPED: + get_wrapped = tv->tv_u.tv_get_wrapped; + break; + case LDPT_GET_NUM_WRAPPED: + get_num_wrapped = tv->tv_u.tv_get_num_wrapped; + break; + // @LOCALMOD-END case LDPT_MESSAGE: message = tv->tv_u.tv_message; break; @@ -228,6 +290,24 @@ ld_plugin_status onload(ld_plugin_tv *tv) { return LDPS_ERR; } + // @LOCALMOD-BEGIN + // Parse extra command-line options + // Although lto_codegen provides a way to parse command-line arguments, + // we need the arguments to be parsed and applied before LTOModules are + // even created. In particular, this is needed because the + // "-add-nacl-read-tp-dependency" flag affects how modules are created. + if (!options::extra.empty()) { + for (std::vector<std::string>::iterator it = options::extra.begin(); + it != options::extra.end(); ++it) { + lto_add_command_line_option((*it).c_str()); + } + lto_parse_command_line_options(); + // We clear the options so that they don't get parsed again in + // lto_codegen_debug_options. + options::extra.clear(); + } + // @LOCALMOD-END + return LDPS_OK; } @@ -294,7 +374,21 @@ static ld_plugin_status claim_file_hook(const ld_plugin_input_file *file, ld_plugin_symbol &sym = cf.syms.back(); sym.name = const_cast<char *>(lto_module_get_symbol_name(M, i)); sym.name = strdup(sym.name); + // @LOCALMOD-BEGIN + // Localmods have disabled the use of the 'version' field for passing + // version information to Gold. Instead, the version is now transmitted as + // part of the 'name' field, which has the form "sym@VER" or "sym@@VER". + // This is nicer because it communicates one extra bit of information (@@ + // marks the default version), and allows us to access the real symbol + // name in all_symbols_read. + + // These fields are set by Gold to communicate the updated version info + // to the plugin. They are used in all_symbols_read_hook(). + // Initialize them for predictability. sym.version = NULL; + sym.is_default = false; + sym.dynfile = NULL; + // @LOCALMOD-END int scope = attrs & LTO_SYMBOL_SCOPE_MASK; switch (scope) { @@ -343,18 +437,45 @@ static ld_plugin_status claim_file_hook(const ld_plugin_input_file *file, } cf.syms.reserve(cf.syms.size()); + // @LOCALMOD-BEGIN + bool is_shared = + (lto_module_get_output_format(M) == LTO_OUTPUT_FORMAT_SHARED); + const char* soname = lto_module_get_soname(M); + if (soname[0] == '\0') + soname = NULL; + // @LOCALMOD-END if (!cf.syms.empty()) { - if ((*add_symbols)(cf.handle, cf.syms.size(), &cf.syms[0]) != LDPS_OK) { + if ((*add_symbols)(cf.handle, cf.syms.size(), &cf.syms[0], + is_shared, soname) != LDPS_OK) { // @LOCALMOD (*message)(LDPL_ERROR, "Unable to add symbols!"); return LDPS_ERR; } } - if (code_gen) - lto_codegen_add_module(code_gen, M); + // @LOCALMOD-BEGIN + // Do not merge the module if it's a PSO. + // If the PSO's soname is set, add it to DepLibs. + cf.is_linked_in = false; + if (code_gen) { + if (is_shared) { + if (soname && strlen(soname) > 0) { + DepLibs.push_back(soname); + } + } else { + if (options::gather_then_link) { + lto_codegen_gather_module_for_link(code_gen, M); + } else { + lto_codegen_add_module(code_gen, M); + } + cf.is_linked_in = true; + } + } - lto_module_dispose(M); + // With gather_then_link, the modules are disposed when linking. + if (!options::gather_then_link) + lto_module_dispose(M); + // @LOCALMOD-END return LDPS_OK; } @@ -367,6 +488,12 @@ static ld_plugin_status all_symbols_read_hook(void) { std::ofstream api_file; assert(code_gen); + // @LOCALMOD-BEGIN + if (options::gather_then_link) { + lto_codegen_link_gathered_modules_and_dispose(code_gen); + } + // @LOCALMOD-END + if (options::generate_api_file) { api_file.open("apifile.txt", std::ofstream::out | std::ofstream::trunc); if (!api_file.is_open()) { @@ -381,12 +508,45 @@ static ld_plugin_status all_symbols_read_hook(void) { continue; (*get_symbols)(I->handle, I->syms.size(), &I->syms[0]); for (unsigned i = 0, e = I->syms.size(); i != e; i++) { + // @LOCALMOD-BEGIN + // Don't process the symbols inside a dynamic object. + if (!I->is_linked_in) + continue; + // @LOCALMOD-END + if (I->syms[i].resolution == LDPR_PREVAILING_DEF) { + // @LOCALMOD-BEGIN + // Set the symbol version in the module. + if (linker_output != LDPO_REL && I->syms[i].version) { + // NOTE: This may change the name of the symbol, so it must happen + // before the call to lto_codegen_add_must_preserve_symbols() below. + I->syms[i].name = const_cast<char *>( + lto_codegen_set_symbol_def_version(code_gen, I->syms[i].name, + I->syms[i].version, + I->syms[i].is_default)); + } lto_codegen_add_must_preserve_symbol(code_gen, I->syms[i].name); + // @LOCALMOD-END if (options::generate_api_file) api_file << I->syms[i].name << "\n"; } + // @LOCALMOD-BEGIN + else if (linker_output != LDPO_REL && + (I->syms[i].resolution == LDPR_RESOLVED_DYN || + I->syms[i].resolution == LDPR_UNDEF)) { + // This symbol is provided by an external object. + // Set the version and source dynamic file for it. + const char *ver = I->syms[i].version; + const char *dynfile = I->syms[i].dynfile; + dynfile = get_basename(dynfile); + // NOTE: This may change the name of the symbol. + I->syms[i].name = const_cast<char *>( + lto_codegen_set_symbol_needed(code_gen, I->syms[i].name, + ver ? ver : "", + dynfile ? dynfile : "")); + } + // @LOCALMOD-END } } @@ -398,6 +558,11 @@ static ld_plugin_status all_symbols_read_hook(void) { if (!options::mcpu.empty()) lto_codegen_set_cpu(code_gen, options::mcpu.c_str()); + // @LOCALMOD-BEGIN (COMMENT) + // "extra" will always be empty below, because we process the extra + // options earlier, at the end of onload(). + // @LOCALMOD-END + // Pass through extra options to the code generator. if (!options::extra.empty()) { for (std::vector<std::string>::iterator it = options::extra.begin(); @@ -406,6 +571,57 @@ static ld_plugin_status all_symbols_read_hook(void) { } } + // @LOCALMOD-BEGIN + // Store the linker output format into the bitcode. + lto_output_format format; + switch (linker_output) { + case LDPO_REL: + format = LTO_OUTPUT_FORMAT_OBJECT; + break; + case LDPO_DYN: + format = LTO_OUTPUT_FORMAT_SHARED; + break; + case LDPO_EXEC: + format = LTO_OUTPUT_FORMAT_EXEC; + break; + default: + (*message)(LDPL_FATAL, "Unknown linker output format (gold-plugin)"); + abort(); + break; + } + lto_codegen_set_merged_module_output_format(code_gen, format); + // @LOCALMOD-END + + // @LOCALMOD-BEGIN + // For -shared linking, store the soname into the bitcode. + if (linker_output == LDPO_DYN) { + const char *soname = (*get_output_soname)(); + lto_codegen_set_merged_module_soname(code_gen, soname); + } + // @LOCALMOD-END + + // @LOCALMOD-BEGIN + // Add the needed libraries to the bitcode. + unsigned int num_needed = (*get_num_needed)(); + for (unsigned i=0; i < num_needed; ++i) { + const char *soname = (*get_needed)(i); + soname = get_basename(soname); + lto_codegen_add_merged_module_library_dep(code_gen, soname); + } + for (std::vector<std::string>::iterator I = DepLibs.begin(), + E = DepLibs.end(); I != E; ++I) { + lto_codegen_add_merged_module_library_dep(code_gen, I->c_str()); + } + // @LOCALMOD-END + + // @LOCALMOD-BEGIN + // Perform symbol wrapping. + unsigned int num_wrapped = (*get_num_wrapped)(); + for (unsigned i=0; i < num_wrapped; ++i) { + const char *sym = (*get_wrapped)(i); + lto_codegen_wrap_symbol_in_merged_module(code_gen, sym); + } + // @LOCALMOD-END if (options::generate_bc_file != options::BC_NO) { std::string path; if (options::generate_bc_file == options::BC_ONLY) diff --git a/tools/llvm-as/llvm-as.cpp b/tools/llvm-as/llvm-as.cpp index d6f191961d..bb9afce271 100644 --- a/tools/llvm-as/llvm-as.cpp +++ b/tools/llvm-as/llvm-as.cpp @@ -77,8 +77,11 @@ static void WriteOutputFile(const Module *M) { exit(1); } - if (Force || !CheckBitcodeOutputToConsole(Out->os(), true)) + // @LOCALMOD-BEGIN + if (Force || !CheckBitcodeOutputToConsole(Out->os(), true)) { WriteBitcodeToFile(M, Out->os()); + } + // @LOCALMOD-END // Declare success. Out->keep(); diff --git a/tools/llvm-dis/CMakeLists.txt b/tools/llvm-dis/CMakeLists.txt index 9f12ecb666..d9883a2147 100644 --- a/tools/llvm-dis/CMakeLists.txt +++ b/tools/llvm-dis/CMakeLists.txt @@ -1,4 +1,4 @@ -set(LLVM_LINK_COMPONENTS bitreader analysis) +set(LLVM_LINK_COMPONENTS bitreader naclbitreader analysis) add_llvm_tool(llvm-dis llvm-dis.cpp diff --git a/tools/llvm-dis/LLVMBuild.txt b/tools/llvm-dis/LLVMBuild.txt index 4525010c1f..cf1cbf7a40 100644 --- a/tools/llvm-dis/LLVMBuild.txt +++ b/tools/llvm-dis/LLVMBuild.txt @@ -19,4 +19,4 @@ type = Tool name = llvm-dis parent = Tools -required_libraries = Analysis BitReader +required_libraries = Analysis BitReader NaClBitReader diff --git a/tools/llvm-dis/Makefile b/tools/llvm-dis/Makefile index aeeeed0d68..0719006a15 100644 --- a/tools/llvm-dis/Makefile +++ b/tools/llvm-dis/Makefile @@ -9,7 +9,7 @@ LEVEL := ../.. TOOLNAME := llvm-dis -LINK_COMPONENTS := bitreader analysis +LINK_COMPONENTS := bitreader naclbitreader analysis # This tool has no plugins, optimize startup time. TOOL_NO_EXPORTS := 1 diff --git a/tools/llvm-dis/llvm-dis.cpp b/tools/llvm-dis/llvm-dis.cpp index 067955e5cc..db9ca40f45 100644 --- a/tools/llvm-dis/llvm-dis.cpp +++ b/tools/llvm-dis/llvm-dis.cpp @@ -19,10 +19,12 @@ #include "llvm/IR/LLVMContext.h" #include "llvm/Assembly/AssemblyAnnotationWriter.h" #include "llvm/Bitcode/ReaderWriter.h" +#include "llvm/Bitcode/NaCl/NaClReaderWriter.h" // @LOCALMOD #include "llvm/DebugInfo.h" #include "llvm/IR/IntrinsicInst.h" #include "llvm/IR/Module.h" #include "llvm/IR/Type.h" +#include "llvm/IRReader/IRReader.h" // @LOCALMOD #include "llvm/Support/CommandLine.h" #include "llvm/Support/DataStream.h" #include "llvm/Support/FormattedStream.h" @@ -51,6 +53,23 @@ static cl::opt<bool> ShowAnnotations("show-annotations", cl::desc("Add informational comments to the .ll file")); +// @LOCALMOD-BEGIN +// Print bitcode metadata only, in text format. +// (includes output format, soname, and dependencies). +static cl::opt<bool> +DumpMetadata("dump-metadata", cl::desc("Dump bitcode metadata")); + +static cl::opt<NaClFileFormat> +InputFileFormat( + "bitcode-format", + cl::desc("Define format of input bitcode file:"), + cl::values( + clEnumValN(LLVMFormat, "llvm", "LLVM bitcode file (default)"), + clEnumValN(PNaClFormat, "pnacl", "PNaCl bitcode file"), + clEnumValEnd), + cl::init(LLVMFormat)); +// @LOCALMOD-END + namespace { static void printDebugLoc(const DebugLoc &DL, formatted_raw_ostream &OS) { @@ -133,8 +152,22 @@ int main(int argc, char **argv) { DisplayFilename = "<stdin>"; else DisplayFilename = InputFilename; - M.reset(getStreamedBitcodeModule(DisplayFilename, streamer, Context, - &ErrorMessage)); + + // @LOCALMOD-BEGIN + switch (InputFileFormat) { + case LLVMFormat: + M.reset(getStreamedBitcodeModule(DisplayFilename, streamer, Context, + &ErrorMessage)); + break; + case PNaClFormat: + M.reset(getNaClStreamedBitcodeModule(DisplayFilename, streamer, Context, + &ErrorMessage)); + break; + default: + ErrorMessage = "Don't understand specified bitcode format"; + break; + } + // @LOCALMOD-END if(M.get() != 0 && M->MaterializeAllPermanently(&ErrorMessage)) { M.reset(); } @@ -154,7 +187,7 @@ int main(int argc, char **argv) { OutputFilename = "-"; if (OutputFilename.empty()) { // Unspecified output, infer it. - if (InputFilename == "-") { + if (InputFilename == "-" || DumpMetadata) { // @LOCALMOD OutputFilename = "-"; } else { const std::string &IFN = InputFilename; @@ -176,6 +209,14 @@ int main(int argc, char **argv) { return 1; } + // @LOCALMOD-BEGIN + if (DumpMetadata) { + M->dumpMeta(Out->os()); + Out->keep(); + return 0; + } + // @LOCALMOD-END + OwningPtr<AssemblyAnnotationWriter> Annotator; if (ShowAnnotations) Annotator.reset(new CommentWriter()); diff --git a/tools/llvm-extract/llvm-extract.cpp b/tools/llvm-extract/llvm-extract.cpp index 2f45b4eae5..8108996996 100644 --- a/tools/llvm-extract/llvm-extract.cpp +++ b/tools/llvm-extract/llvm-extract.cpp @@ -22,6 +22,8 @@ #include "llvm/IRReader/IRReader.h" #include "llvm/PassManager.h" #include "llvm/Support/CommandLine.h" +#include "llvm/Support/Debug.h" // @LOCALMOD +#include "llvm/Support/IRReader.h" #include "llvm/Support/ManagedStatic.h" #include "llvm/Support/PrettyStackTrace.h" #include "llvm/Support/Regex.h" @@ -48,6 +50,18 @@ Force("f", cl::desc("Enable binary output on terminals")); static cl::opt<bool> DeleteFn("delete", cl::desc("Delete specified Globals from Module")); +// @LOCALMOD-BEGIN +static cl::opt<unsigned> +Divisor("divisor", + cl::init(0), + cl::desc("select GV by position (pos % divisor = remainder ")); + +static cl::opt<unsigned> +Remainder("remainder", + cl::init(0), + cl::desc("select GV by position (pos % divisor = remainder ")); +// @LOCALMOD-END + // ExtractFuncs - The functions to extract from the module. static cl::list<std::string> ExtractFuncs("func", cl::desc("Specify function to extract"), @@ -179,6 +193,24 @@ int main(int argc, char **argv) { } } + // @LOCALMOD-BEGIN + // Extract globals via modulo operation. + size_t count_globals = 0; + if (Divisor != 0) { + size_t pos = 0; + for (Module::global_iterator GV = M->global_begin(), E = M->global_end(); + GV != E; + GV++, pos++) { + if (pos % Divisor == Remainder) { + GVs.insert(&*GV); + } + } + dbgs() << "total globals: " << pos << "\n"; + count_globals = GVs.size(); + dbgs() << "selected globals: " << count_globals << "\n"; + } + // @LOCALMOD-END + // Figure out which functions we should extract. for (size_t i = 0, e = ExtractFuncs.size(); i != e; ++i) { GlobalValue *GV = M->getFunction(ExtractFuncs[i]); @@ -213,6 +245,22 @@ int main(int argc, char **argv) { } } + // @LOCALMOD-BEGIN + // Extract functions via modulo operation. + if (Divisor != 0) { + size_t pos = 0; + for (Module::iterator F = M->begin(), E = M->end(); + F != E; + F++, pos++) { + if (pos % Divisor == Remainder) { + GVs.insert(&*F); + } + } + dbgs() << "total functions: " << pos << "\n"; + dbgs() << "selected functions: " << GVs.size() - count_globals << "\n"; + } + // @LOCALMOD-END + // Materialize requisite global values. if (!DeleteFn) for (size_t i = 0, e = GVs.size(); i != e; ++i) { diff --git a/tools/llvm-link/CMakeLists.txt b/tools/llvm-link/CMakeLists.txt index 4df53564e1..a414e5ac7f 100644 --- a/tools/llvm-link/CMakeLists.txt +++ b/tools/llvm-link/CMakeLists.txt @@ -1,4 +1,4 @@ -set(LLVM_LINK_COMPONENTS linker bitreader bitwriter asmparser irreader) +set(LLVM_LINK_COMPONENTS linker bitreader bitwriter naclbitwriter asmparser irreader) add_llvm_tool(llvm-link llvm-link.cpp diff --git a/tools/llvm-mc/llvm-mc.cpp b/tools/llvm-mc/llvm-mc.cpp index 4b01c33504..db2628fbf8 100644 --- a/tools/llvm-mc/llvm-mc.cpp +++ b/tools/llvm-mc/llvm-mc.cpp @@ -19,6 +19,7 @@ #include "llvm/MC/MCContext.h" #include "llvm/MC/MCInstPrinter.h" #include "llvm/MC/MCInstrInfo.h" +#include "llvm/MC/MCNaCl.h" #include "llvm/MC/MCObjectFileInfo.h" #include "llvm/MC/MCParser/AsmLexer.h" #include "llvm/MC/MCRegisterInfo.h" @@ -450,6 +451,11 @@ int main(int argc, char **argv) { Str.reset(TheTarget->createMCObjectStreamer(TripleName, Ctx, *MAB, FOS, CE, RelaxAll, NoExecStack)); + // @LOCALMOD-BEGIN + Triple T(TripleName); + if (T.isOSNaCl()) + initializeNaClMCStreamer(*Str.get(), Ctx, T); + // @LOCALMOD-END } int Res = 1; diff --git a/tools/llvm-nm/CMakeLists.txt b/tools/llvm-nm/CMakeLists.txt index b6cd80b477..de06ca28d9 100644 --- a/tools/llvm-nm/CMakeLists.txt +++ b/tools/llvm-nm/CMakeLists.txt @@ -1,4 +1,4 @@ -set(LLVM_LINK_COMPONENTS archive bitreader object) +set(LLVM_LINK_COMPONENTS archive bitreader naclbitreader object) add_llvm_tool(llvm-nm llvm-nm.cpp diff --git a/tools/llvm-nm/LLVMBuild.txt b/tools/llvm-nm/LLVMBuild.txt index 38ecbfd2e6..baba9ca50b 100644 --- a/tools/llvm-nm/LLVMBuild.txt +++ b/tools/llvm-nm/LLVMBuild.txt @@ -19,4 +19,4 @@ type = Tool name = llvm-nm parent = Tools -required_libraries = Archive BitReader Object +required_libraries = Archive BitReader NaClBitReader Object diff --git a/tools/llvm-nm/Makefile b/tools/llvm-nm/Makefile index d9cee98995..fe208a8f24 100644 --- a/tools/llvm-nm/Makefile +++ b/tools/llvm-nm/Makefile @@ -9,7 +9,7 @@ LEVEL := ../.. TOOLNAME := llvm-nm -LINK_COMPONENTS := archive bitreader object +LINK_COMPONENTS := archive bitreader naclbitreader object # This tool has no plugins, optimize startup time. TOOL_NO_EXPORTS := 1 diff --git a/tools/llvm-nm/llvm-nm.cpp b/tools/llvm-nm/llvm-nm.cpp index a24aae6061..a4f9ab0730 100644 --- a/tools/llvm-nm/llvm-nm.cpp +++ b/tools/llvm-nm/llvm-nm.cpp @@ -19,6 +19,8 @@ #include "llvm/IR/LLVMContext.h" #include "llvm/Bitcode/Archive.h" #include "llvm/Bitcode/ReaderWriter.h" +#include "llvm/Bitcode/NaCl/NaClReaderWriter.h" // @LOCALMOD +#include "llvm/IRReader/IRReader.h" // @LOCALMOD #include "llvm/IR/Module.h" #include "llvm/Object/Archive.h" #include "llvm/Object/ObjectFile.h" @@ -122,6 +124,18 @@ namespace { bool MultipleFiles = false; std::string ToolName; + + // @LOCALMOD-BEGIN + cl::opt<NaClFileFormat> + InputFileFormat( + "bitcode-format", + cl::desc("Define format of input file:"), + cl::values( + clEnumValN(LLVMFormat, "llvm", "LLVM file (default)"), + clEnumValN(PNaClFormat, "pnacl", "PNaCl bitcode file"), + clEnumValEnd), + cl::init(LLVMFormat)); + // @LOCALMOD-END } @@ -345,7 +359,20 @@ static void DumpSymbolNamesFromFile(std::string &Filename) { LLVMContext &Context = getGlobalContext(); std::string ErrorMessage; - if (magic == sys::fs::file_magic::bitcode) { + // @LOCALMOD-BEGIN + // Support parsing PNaCl bitcode files + if (InputFileFormat == PNaClFormat) { + Module *Result = NaClParseBitcodeFile(Buffer.get(), Context, &ErrorMessage); + if (Result) { + DumpSymbolNamesFromModule(Result); + delete Result; + } else { + error(ErrorMessage, Filename); + return; + } + } + // @LOCALMOD-END + else if (magic == sys::fs::file_magic::bitcode) { Module *Result = 0; Result = ParseBitcodeFile(Buffer.get(), Context, &ErrorMessage); if (Result) { diff --git a/tools/lto/LTOCodeGenerator.cpp b/tools/lto/LTOCodeGenerator.cpp index 57e7a2d07f..75c718c019 100644 --- a/tools/lto/LTOCodeGenerator.cpp +++ b/tools/lto/LTOCodeGenerator.cpp @@ -98,6 +98,68 @@ bool LTOCodeGenerator::addModule(LTOModule* mod, std::string& errMsg) { return ret; } +// @LOCALMOD-BEGIN +/// Add a module that will be merged with the final output module. +/// The merging does not happen until linkGatheredModulesAndDispose(). +void LTOCodeGenerator::gatherModuleForLinking(LTOModule* mod) { + _gatheredModules.push_back(mod); +} + +/// Merge all modules gathered from gatherModuleForLinking(), and +/// destroy the source modules in the process. +bool LTOCodeGenerator::linkGatheredModulesAndDispose(std::string& errMsg) { + + // We gather the asm undefs earlier than addModule() does, + // since we delete the modules during linking, and would not be + // able to do this after linking. The undefs vector contain lists + // of global variable names which are considered "used", which will be + // appended into the "llvm.compiler.used" list. The names must be the + // same before linking as they are after linking, since we have switched + // the order. + for (unsigned i = 0, ei = _gatheredModules.size(); i != ei; ++i) { + const std::vector<const char*> &undefs = + _gatheredModules[i]->getAsmUndefinedRefs(); + for (int j = 0, ej = undefs.size(); j != ej; ++j) { + _asmUndefinedRefs[undefs[j]] = 1; + } + } + + // Tree-reduce the mods, re-using the incoming mods as scratch + // intermediate results. Module i is linked with (i + stride), with i as + // the dest. We begin with a stride of 1, and double each time. E.g., + // after the first round, only the even-indexed modules are still available, + // and after the second, only those with index that are a multiple of 4 + // are available. Eventually the Module with the content of all other modules + // will be Module 0. + // NOTE: we may be able to be smarter about linking if we did not do them + // pairwise using Linker::LinkModules. We also disregard module sizes + // and try our best to keep the modules in order (linking adjacent modules). + for (unsigned stride = 1, len = _gatheredModules.size(); + stride < len; + stride *= 2) { + for (unsigned i = 0; i + stride < len; i = i + (stride * 2)) { + if (Linker::LinkModules(_gatheredModules[i]->getLLVVMModule(), + _gatheredModules[i+stride]->getLLVVMModule(), + Linker::DestroySource, &errMsg)) { + errs() << "LinkModules " << i << " w/ " << i + stride << " failed...\n"; + // We leak the memory in this case... + return true; + } + delete _gatheredModules[i+stride]; + } + } + + // Finally, link Node 0 with the Dest and delete Node 0. + if (_linker.LinkInModule(_gatheredModules[0]->getLLVVMModule(), &errMsg)) { + errs() << "LinkModules Dst w/ _gatheredModules[0] failed...\n"; + return true; + } + delete _gatheredModules[0]; + + return false; +} +// @LOCALMOD-END + bool LTOCodeGenerator::setDebugInfo(lto_debug_model debug, std::string& errMsg) { switch (debug) { @@ -124,6 +186,83 @@ bool LTOCodeGenerator::setCodePICModel(lto_codegen_model model, llvm_unreachable("Unknown PIC model!"); } +// @LOCALMOD-BEGIN +void LTOCodeGenerator::setMergedModuleOutputFormat(lto_output_format format) +{ + Module::OutputFormat outputFormat; + switch (format) { + case LTO_OUTPUT_FORMAT_OBJECT: + outputFormat = Module::ObjectOutputFormat; + break; + case LTO_OUTPUT_FORMAT_SHARED: + outputFormat = Module::SharedOutputFormat; + break; + case LTO_OUTPUT_FORMAT_EXEC: + outputFormat = Module::ExecutableOutputFormat; + break; + default: + llvm_unreachable("Unexpected output format"); + } + Module *mergedModule = _linker.getModule(); + mergedModule->setOutputFormat(outputFormat); +} + +void LTOCodeGenerator::setMergedModuleSOName(const char *soname) +{ + Module *mergedModule = _linker.getModule(); + mergedModule->setSOName(soname); +} + +void LTOCodeGenerator::addLibraryDep(const char *lib) +{ + Module *mergedModule = _linker.getModule(); + mergedModule->addLibrary(lib); +} + +void LTOCodeGenerator::wrapSymbol(const char *sym) +{ + Module *mergedModule = _linker.getModule(); + mergedModule->wrapSymbol(sym); +} + +const char* LTOCodeGenerator::setSymbolDefVersion(const char *sym, + const char *ver, + bool is_default) +{ + Module *mergedModule = _linker.getModule(); + GlobalValue *GV = mergedModule->getNamedValue(sym); + if (!GV) { + llvm_unreachable("Invalid global in setSymbolDefVersion"); + } + GV->setVersionDef(ver, is_default); + return strdup(GV->getName().str().c_str()); +} + +const char* LTOCodeGenerator::setSymbolNeeded(const char *sym, + const char *ver, + const char *dynfile) +{ + Module *mergedModule = _linker.getModule(); + GlobalValue *GV = mergedModule->getNamedValue(sym); + if (!GV) { + // Symbol lookup may have failed because this symbol was already + // renamed for versioning. Make sure this is the case. + if (strchr(sym, '@') != NULL || ver == NULL || ver[0] == '\0') { + llvm_unreachable("Unexpected condition in setSymbolNeeded"); + } + std::string NewName = std::string(sym) + "@" + ver; + GV = mergedModule->getNamedValue(NewName); + } + if (!GV) { + // Ignore failures due to unused declarations. + // This caused a falure to build libppruntime.so for glibc. + // TODO(sehr): better document under which circumstances this is needed. + return sym; + } + GV->setNeeded(ver, dynfile); + return strdup(GV->getName().str().c_str()); +} +// @LOCALMOD-END bool LTOCodeGenerator::writeMergedModules(const char *path, std::string &errMsg) { if (determineTarget(errMsg)) @@ -142,7 +281,6 @@ bool LTOCodeGenerator::writeMergedModules(const char *path, return true; } - // write bitcode to it WriteBitcodeToFile(_linker.getModule(), Out.os()); Out.os().close(); diff --git a/tools/lto/LTOCodeGenerator.h b/tools/lto/LTOCodeGenerator.h index a4ade9fd26..4cc3928340 100644 --- a/tools/lto/LTOCodeGenerator.h +++ b/tools/lto/LTOCodeGenerator.h @@ -41,6 +41,12 @@ struct LTOCodeGenerator { ~LTOCodeGenerator(); bool addModule(struct LTOModule*, std::string &errMsg); + // @LOCALMOD-BEGIN + // Alternative methods of adding modules, which delay merging modules until + // all modules are available. + void gatherModuleForLinking(struct LTOModule*); + bool linkGatheredModulesAndDispose(std::string &errMsg); + // @LOCALMOD-END bool setDebugInfo(lto_debug_model, std::string &errMsg); bool setCodePICModel(lto_codegen_model, std::string &errMsg); @@ -51,6 +57,16 @@ struct LTOCodeGenerator { } bool writeMergedModules(const char *path, std::string &errMsg); + // @LOCALMOD-BEGIN + void setMergedModuleOutputFormat(lto_output_format format); + void setMergedModuleSOName(const char *soname); + void addLibraryDep(const char *lib); + void wrapSymbol(const char *sym); + const char* setSymbolDefVersion(const char *sym, const char *ver, + bool is_default); + const char* setSymbolNeeded(const char *sym, const char *ver, + const char *dynfile); + // @LOCALMOD-END bool compile_to_file(const char **name, std::string &errMsg); const void *compile(size_t *length, std::string &errMsg); void setCodeGenDebugOptions(const char *opts); @@ -78,6 +94,9 @@ private: std::vector<char*> _codegenOptions; std::string _mCpu; std::string _nativeObjectPath; + + // @LOCALMOD + std::vector<LTOModule*> _gatheredModules; }; #endif // LTO_CODE_GENERATOR_H diff --git a/tools/lto/LTOModule.cpp b/tools/lto/LTOModule.cpp index d805f49f9a..2f98517c1c 100644 --- a/tools/lto/LTOModule.cpp +++ b/tools/lto/LTOModule.cpp @@ -28,6 +28,7 @@ #include "llvm/MC/MCTargetAsmParser.h" #include "llvm/MC/SubtargetFeature.h" #include "llvm/Support/CommandLine.h" +#include "llvm/Support/ErrorHandling.h" // @LOCALMOD #include "llvm/Support/Host.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Path.h" @@ -271,7 +272,7 @@ LTOModule *LTOModule::makeLTOModule(MemoryBuffer *buffer, } // parse bitcode buffer - OwningPtr<Module> m(getLazyBitcodeModule(buffer, getGlobalContext(), + OwningPtr<Module> m(ParseBitcodeFile(buffer, getGlobalContext(), // @LOCALMOD &errMsg)); if (!m) { delete buffer; @@ -304,6 +305,7 @@ LTOModule *LTOModule::makeLTOModule(MemoryBuffer *buffer, getTargetOptions(Options); TargetMachine *target = march->createTargetMachine(TripleStr, CPU, FeatureStr, Options); + LTOModule *Ret = new LTOModule(m.take(), target); if (Ret->parseSymbols(errMsg)) { delete Ret; @@ -319,6 +321,36 @@ MemoryBuffer *LTOModule::makeBuffer(const void *mem, size_t length) { return MemoryBuffer::getMemBuffer(StringRef(startPtr, length), "", false); } +// @LOCALMOD-BEGIN +lto_output_format LTOModule::getOutputFormat() { + Module::OutputFormat format = _module->getOutputFormat(); + switch (format) { + case Module::ObjectOutputFormat: return LTO_OUTPUT_FORMAT_OBJECT; + case Module::SharedOutputFormat: return LTO_OUTPUT_FORMAT_SHARED; + case Module::ExecutableOutputFormat: return LTO_OUTPUT_FORMAT_EXEC; + } + llvm_unreachable("Unknown output format in LTOModule"); +} + +const char *LTOModule::getSOName() { + return _module->getSOName().c_str(); +} + +const char* LTOModule::getLibraryDep(uint32_t index) { + /* make it compile until we bring back deplibs + const Module::LibraryListType &Libs = _module->getLibraries(); + if (index < Libs.size()) + return Libs[index].c_str(); + */ + return NULL; +} + +uint32_t LTOModule::getNumLibraryDeps() { + //return _module->getLibraries().size(); + return 0; +} +// @LOCALMOD-END + /// objcClassNameFromExpression - Get string that the data pointer points to. bool LTOModule::objcClassNameFromExpression(const Constant *c, std::string &name) { @@ -614,6 +646,14 @@ LTOModule::addPotentialUndefinedSymbol(const GlobalValue *decl, bool isFunc) { if (decl->getName().startswith("llvm.")) return; + // @LOCALMOD-BEGIN + // Bitcode modules may have declarations for functions or globals + // which are unused. Ignore them here so that gold does not mistake + // them for undefined symbols. + if (decl->use_empty()) + return; + // @LOCALMOD-END + // ignore all aliases if (isa<GlobalAlias>(decl)) return; @@ -800,6 +840,7 @@ namespace { unsigned MaxBytesToEmit) {} virtual bool EmitValueToOffset(const MCExpr *Offset, unsigned char Value ) { return false; } + virtual void EmitFileDirective(StringRef Filename) {} virtual void EmitDwarfAdvanceLineAddr(int64_t LineDelta, const MCSymbol *LastLabel, diff --git a/tools/lto/LTOModule.h b/tools/lto/LTOModule.h index 83f3a7def1..6f97699e90 100644 --- a/tools/lto/LTOModule.h +++ b/tools/lto/LTOModule.h @@ -99,6 +99,14 @@ public: _module->setTargetTriple(triple); } + // @LOCALMOD-BEGIN + lto_output_format getOutputFormat(); + const char* getSOName(); + const char* getLibraryDep(uint32_t index); + uint32_t getNumLibraryDeps(); + // @LOCALMOD-END + + /// getSymbolCount - Get the number of symbols uint32_t getSymbolCount() { return _symbols.size(); diff --git a/tools/lto/Makefile b/tools/lto/Makefile index ab2e16e5fa..c13a0ba7f6 100644 --- a/tools/lto/Makefile +++ b/tools/lto/Makefile @@ -57,3 +57,11 @@ ifeq ($(HOST_OS),Darwin) -Wl,-object_path_lto -Wl,$(TempFile) endif endif + +#@ LOCALMOD-BEGIN +# This is to fix an upstream bug. It is in the process of being upstreamed. +# This line can be removed after it has been fixed upstream and we've merged. +ifneq ($(HOST_OS),Darwin) + LLVMLibsOptions := $(LLVMLibsOptions) -Wl,-soname=$(SharedPrefix)LTO$(SHLIBEXT) +endif +#@ LOCALMOD-END diff --git a/tools/lto/lto.cpp b/tools/lto/lto.cpp index 11ad532be8..1915acbdae 100644 --- a/tools/lto/lto.cpp +++ b/tools/lto/lto.cpp @@ -13,6 +13,8 @@ //===----------------------------------------------------------------------===// #include "llvm-c/lto.h" +#include "llvm/Support/CommandLine.h" // @LOCALMOD + #include "LTOCodeGenerator.h" #include "LTOModule.h" #include "llvm-c/Core.h" @@ -22,6 +24,25 @@ // *** Not thread safe *** static std::string sLastErrorString; +// @LOCALMOD-BEGIN +static std::vector<const char*> lto_options; +extern void lto_add_command_line_option(const char* opt) +{ + // ParseCommandLineOptions() expects argv[0] to be program name. + if (lto_options.empty()) + lto_options.push_back("libLTO"); + + lto_options.push_back(strdup(opt)); +} + +extern void lto_parse_command_line_options() +{ + if ( !lto_options.empty() ) + llvm::cl::ParseCommandLineOptions(lto_options.size(), + const_cast<char **>(<o_options[0])); +} +// @LOCALMOD-END + /// lto_get_version - Returns a printable string. extern const char* lto_get_version() { return LTOCodeGenerator::getVersionString(); @@ -106,6 +127,45 @@ void lto_module_set_target_triple(lto_module_t mod, const char *triple) { return mod->setTargetTriple(triple); } +// @LOCALMOD-BEGIN + +// +// Get the module format for this module +// +lto_output_format lto_module_get_output_format(lto_module_t mod) +{ + return mod->getOutputFormat(); +} + +// +// Get the module soname +// +const char* lto_module_get_soname(lto_module_t mod) +{ + return mod->getSOName(); +} + +// +// Get the i'th library dependency. +// Returns NULL if i >= lto_module_get_num_library_deps() +// +const char * +lto_module_get_library_dep(lto_module_t mod, unsigned int i) +{ + return mod->getLibraryDep(i); +} + +// +// Return the number of library dependencies of this module. +// +unsigned int +lto_module_get_num_library_deps(lto_module_t mod) +{ + return mod->getNumLibraryDeps(); +} + +// @LOCALMOD-END + /// lto_module_get_num_symbols - Returns the number of symbols in the object /// module. unsigned int lto_module_get_num_symbols(lto_module_t mod) { @@ -144,6 +204,16 @@ bool lto_codegen_add_module(lto_code_gen_t cg, lto_module_t mod) { return cg->addModule(mod, sLastErrorString); } +// @LOCALMOD-BEGIN +void lto_codegen_gather_module_for_link(lto_code_gen_t cg, lto_module_t mod) { + cg->gatherModuleForLinking(mod); +} + +bool lto_codegen_link_gathered_modules_and_dispose(lto_code_gen_t cg) { + return cg->linkGatheredModulesAndDispose(sLastErrorString); +} +// @LOCALMOD-END + /// lto_codegen_set_debug_model - Sets what if any format of debug info should /// be generated. Returns true on error (check lto_get_error_message() for /// details). @@ -182,6 +252,77 @@ void lto_codegen_add_must_preserve_symbol(lto_code_gen_t cg, cg->addMustPreserveSymbol(symbol); } +// @LOCALMOD-BEGIN + +// +// Set the module format for the merged module +// +void lto_codegen_set_merged_module_output_format(lto_code_gen_t cg, + lto_output_format format) +{ + cg->setMergedModuleOutputFormat(format); +} + +// +// Set the module soname (for shared library bitcode) +// +void lto_codegen_set_merged_module_soname(lto_code_gen_t cg, + const char* soname) +{ + cg->setMergedModuleSOName(soname); +} + +// +// Add a library dependency to the linked bitcode module. +// +void lto_codegen_add_merged_module_library_dep(lto_code_gen_t cg, + const char* soname) +{ + cg->addLibraryDep(soname); +} + +// +// Apply symbol wrapping in the linked bitcode module. +// +void lto_codegen_wrap_symbol_in_merged_module(lto_code_gen_t cg, + const char* sym) { + cg->wrapSymbol(sym); +} + +// +// Set the symbol version of defined symbol 'sym'. +// 'sym' is the name of the GlobalValue, exactly as it is +// in the LLVM module. It may already have a version suffix. +// In that case, this function verifies that the old version +// and new version match. +// Returns a reference to the new name. +// +const char * +lto_codegen_set_symbol_def_version(lto_code_gen_t cg, + const char *sym, + const char *version, + bool is_default) { + return cg->setSymbolDefVersion(sym, version, is_default); +} + +// +// Set the symbol version of needed symbol 'sym' from file 'dynfile'. +// 'sym' is the name of the GlobalValue, exactly as it is +// in the LLVM module. It may already have a version suffix. +// In that case, this function verifies that the old version +// and new version match. +// In any case, it adds a NeededRecord entry. +// Returns a reference to the new name. +// +const char* +lto_codegen_set_symbol_needed(lto_code_gen_t cg, + const char *sym, + const char *version, + const char *dynfile) { + return cg->setSymbolNeeded(sym, version, dynfile); +} +// @LOCALMOD-END + /// lto_codegen_write_merged_modules - 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). diff --git a/tools/lto/lto.exports b/tools/lto/lto.exports index 46d0d74c82..10d2fe03f6 100644 --- a/tools/lto/lto.exports +++ b/tools/lto/lto.exports @@ -1,3 +1,5 @@ +lto_add_command_line_option +lto_parse_command_line_options lto_get_error_message lto_get_version lto_initialize_disassembler @@ -10,16 +12,25 @@ lto_module_get_symbol_attribute lto_module_get_symbol_name lto_module_get_target_triple lto_module_set_target_triple +lto_module_get_output_format +lto_module_get_soname +lto_module_get_library_dep +lto_module_get_num_library_deps 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_gather_module_for_link +lto_codegen_link_gathered_modules_and_dispose lto_codegen_add_must_preserve_symbol lto_codegen_compile lto_codegen_create lto_codegen_dispose +lto_codegen_set_assembler_args +lto_codegen_set_assembler_path +lto_codegen_set_cpu lto_codegen_set_debug_model lto_codegen_set_pic_model lto_codegen_write_merged_modules @@ -27,6 +38,12 @@ lto_codegen_debug_options lto_codegen_set_assembler_args lto_codegen_set_assembler_path lto_codegen_set_cpu +lto_codegen_set_merged_module_output_format +lto_codegen_set_merged_module_soname +lto_codegen_add_merged_module_library_dep +lto_codegen_set_symbol_def_version +lto_codegen_set_symbol_needed +lto_codegen_wrap_symbol_in_merged_module lto_codegen_compile_to_file LLVMCreateDisasm LLVMCreateDisasmCPU diff --git a/tools/opt/CMakeLists.txt b/tools/opt/CMakeLists.txt index 91959119e4..b308fa7264 100644 --- a/tools/opt/CMakeLists.txt +++ b/tools/opt/CMakeLists.txt @@ -1,4 +1,4 @@ -set(LLVM_LINK_COMPONENTS ${LLVM_TARGETS_TO_BUILD} bitreader asmparser bitwriter irreader instrumentation scalaropts objcarcopts ipo vectorize) +set(LLVM_LINK_COMPONENTS ${LLVM_TARGETS_TO_BUILD} bitreader asmparser bitwriter irreader naclbitwriter naclbitreader instrumentation naclanalysis nacltransforms scalaropts objcarcopts ipo vectorize) add_llvm_tool(opt AnalysisWrappers.cpp diff --git a/tools/opt/LLVMBuild.txt b/tools/opt/LLVMBuild.txt index 77b94469ed..6cf3a79219 100644 --- a/tools/opt/LLVMBuild.txt +++ b/tools/opt/LLVMBuild.txt @@ -19,4 +19,4 @@ type = Tool name = opt parent = Tools -required_libraries = AsmParser BitReader BitWriter IRReader IPO Instrumentation Scalar ObjCARC all-targets +required_libraries = AsmParser BitReader BitWriter IRReader NaClBitWriter IPO Instrumentation Scalar ObjCARC all-targets NaClTransforms NaClAnalysis diff --git a/tools/opt/Makefile b/tools/opt/Makefile index a451005574..5413125972 100644 --- a/tools/opt/Makefile +++ b/tools/opt/Makefile @@ -9,6 +9,6 @@ LEVEL := ../.. TOOLNAME := opt -LINK_COMPONENTS := bitreader bitwriter asmparser irreader instrumentation scalaropts objcarcopts ipo vectorize all-targets +LINK_COMPONENTS := bitreader bitwriter naclbitwriter asmparser irreader instrumentation scalaropts objcarcopts ipo vectorize nacltransforms naclanalysis all-targets include $(LEVEL)/Makefile.common diff --git a/tools/opt/opt.cpp b/tools/opt/opt.cpp index e385d7f577..1e8fb65e51 100644 --- a/tools/opt/opt.cpp +++ b/tools/opt/opt.cpp @@ -22,22 +22,23 @@ #include "llvm/Analysis/Verifier.h" #include "llvm/Assembly/PrintModulePass.h" #include "llvm/Bitcode/ReaderWriter.h" +#include "llvm/Bitcode/NaCl/NaClReaderWriter.h" // @LOCALMOD #include "llvm/CodeGen/CommandFlags.h" #include "llvm/DebugInfo.h" #include "llvm/IR/DataLayout.h" #include "llvm/IR/Module.h" -#include "llvm/IRReader/IRReader.h" +#include "llvm/IRReader/IRReader.h" // @LOCALMOD #include "llvm/LinkAllIR.h" #include "llvm/LinkAllPasses.h" #include "llvm/MC/SubtargetFeature.h" #include "llvm/PassManager.h" #include "llvm/Support/Debug.h" +#include "llvm/Support/IRReader.h" #include "llvm/Support/ManagedStatic.h" #include "llvm/Support/PassNameParser.h" #include "llvm/Support/PluginLoader.h" #include "llvm/Support/PrettyStackTrace.h" #include "llvm/Support/Signals.h" -#include "llvm/Support/SourceMgr.h" #include "llvm/Support/SystemUtils.h" #include "llvm/Support/TargetRegistry.h" #include "llvm/Support/TargetSelect.h" @@ -45,6 +46,7 @@ #include "llvm/Target/TargetLibraryInfo.h" #include "llvm/Target/TargetMachine.h" #include "llvm/Transforms/IPO/PassManagerBuilder.h" +#include "llvm/Transforms/NaCl.h" // @LOCALMOD #include <algorithm> #include <memory> using namespace llvm; @@ -127,6 +129,18 @@ static cl::opt<bool> OptLevelO3("O3", cl::desc("Optimization level 3. Similar to clang -O3")); +// @LOCALMOD-BEGIN +static cl::opt<bool> +PNaClABISimplifyPreOpt( + "pnacl-abi-simplify-preopt", + cl::desc("PNaCl ABI simplifications for before optimizations")); + +static cl::opt<bool> +PNaClABISimplifyPostOpt( + "pnacl-abi-simplify-postopt", + cl::desc("PNaCl ABI simplifications for after optimizations")); +// @LOCALMOD-END + static cl::opt<std::string> TargetTriple("mtriple", cl::desc("Override target triple for module")); @@ -157,6 +171,18 @@ DefaultDataLayout("default-data-layout", cl::desc("data layout string to use if not specified by module"), cl::value_desc("layout-string"), cl::init("")); +// @LOCALMOD-BEGIN +static cl::opt<NaClFileFormat> +OutputFileFormat( + "bitcode-format", + cl::desc("Define format of generated bitcode file:"), + cl::values( + clEnumValN(LLVMFormat, "llvm", "LLVM bitcode file (default)"), + clEnumValN(PNaClFormat, "pnacl", "PNaCl bitcode file"), + clEnumValEnd), + cl::init(LLVMFormat)); +// @LOCALMOD-END + // ---------- Define Printers for module and function passes ------------ namespace { @@ -577,6 +603,34 @@ int main(int argc, char **argv) { initializeInstCombine(Registry); initializeInstrumentation(Registry); initializeTarget(Registry); + // @LOCALMOD-BEGIN + initializeAddPNaClExternalDeclsPass(Registry); + initializeCanonicalizeMemIntrinsicsPass(Registry); + initializeExpandArithWithOverflowPass(Registry); + initializeExpandByValPass(Registry); + initializeExpandConstantExprPass(Registry); + initializeExpandCtorsPass(Registry); + initializeExpandGetElementPtrPass(Registry); + initializeExpandSmallArgumentsPass(Registry); + initializeExpandStructRegsPass(Registry); + initializeExpandTlsPass(Registry); + initializeExpandTlsConstantExprPass(Registry); + initializeExpandVarArgsPass(Registry); + initializeFlattenGlobalsPass(Registry); + initializeGlobalCleanupPass(Registry); + initializeInsertDivideCheckPass(Registry); + initializePNaClABIVerifyFunctionsPass(Registry); + initializePNaClABIVerifyModulePass(Registry); + initializePromoteI1OpsPass(Registry); + initializePromoteIntegersPass(Registry); + initializeReplacePtrsWithIntsPass(Registry); + initializeResolveAliasesPass(Registry); + initializeResolvePNaClIntrinsicsPass(Registry); + initializeRewriteLLVMIntrinsicsPass(Registry); + initializeRewritePNaClLibraryCallsPass(Registry); + initializeStripAttributesPass(Registry); + initializeStripMetadataPass(Registry); + // @LOCALMOD-END cl::ParseCommandLineOptions(argc, argv, "llvm .bc -> .bc modular optimizer and analysis printer\n"); @@ -733,6 +787,20 @@ int main(int argc, char **argv) { OptLevelO3 = false; } + // @LOCALMOD-BEGIN + if (PNaClABISimplifyPreOpt && + PNaClABISimplifyPreOpt.getPosition() < PassList.getPosition(i)) { + PNaClABISimplifyAddPreOptPasses(Passes); + PNaClABISimplifyPreOpt = false; + } + + if (PNaClABISimplifyPostOpt && + PNaClABISimplifyPostOpt.getPosition() < PassList.getPosition(i)) { + PNaClABISimplifyAddPostOptPasses(Passes); + PNaClABISimplifyPostOpt = false; + } + // @LOCALMOD-END + const PassInfo *PassInf = PassList[i]; Pass *P = 0; if (PassInf->getNormalCtor()) @@ -805,6 +873,14 @@ int main(int argc, char **argv) { FPasses->doFinalization(); } + // @LOCALMOD-BEGIN + if (PNaClABISimplifyPreOpt) + PNaClABISimplifyAddPreOptPasses(Passes); + + if (PNaClABISimplifyPostOpt) + PNaClABISimplifyAddPostOptPasses(Passes); + // @LOCALMOD-END + // Check that the module is well formed on completion of optimization if (!NoVerify && !VerifyEach) Passes.add(createVerifierPass()); @@ -813,8 +889,7 @@ int main(int argc, char **argv) { if (!NoOutput && !AnalyzeOnly) { if (OutputAssembly) Passes.add(createPrintModulePass(&Out->os())); - else - Passes.add(createBitcodeWriterPass(Out->os())); + // @LOCALMOD } // Before executing passes, print the final values of the LLVM options. @@ -823,6 +898,23 @@ int main(int argc, char **argv) { // Now that we have all of the passes ready, run them. Passes.run(*M.get()); +// @LOCALMOD-BEGIN + // Write bitcode to the output. + if (!NoOutput && !AnalyzeOnly && !OutputAssembly) { + switch (OutputFileFormat) { + case LLVMFormat: + WriteBitcodeToFile(M.get(), Out->os()); + break; + case PNaClFormat: + NaClWriteBitcodeToFile(M.get(), Out->os()); + break; + default: + errs() << "Don't understand bitcode format for generated bitcode.\n"; + return 1; + } + } +// @LOCALMOD-END + // Declare success. if (!NoOutput || PrintBreakpoints) Out->keep(); diff --git a/tools/pnacl-abicheck/CMakeLists.txt b/tools/pnacl-abicheck/CMakeLists.txt new file mode 100644 index 0000000000..fda6d26ac8 --- /dev/null +++ b/tools/pnacl-abicheck/CMakeLists.txt @@ -0,0 +1,5 @@ +set(LLVM_LINK_COMPONENTS bitreader naclbitreader irreader asmparser naclanalysis) + +add_llvm_tool(pnacl-abicheck + pnacl-abicheck.cpp + ) diff --git a/tools/pnacl-abicheck/LLVMBuild.txt b/tools/pnacl-abicheck/LLVMBuild.txt new file mode 100644 index 0000000000..9e45f87f0a --- /dev/null +++ b/tools/pnacl-abicheck/LLVMBuild.txt @@ -0,0 +1,22 @@ +;===- ./tools/pnacl-abicheck/LLVMBuild.txt ---------------------*- Conf -*--===; +; +; The LLVM Compiler Infrastructure +; +; This file is distributed under the University of Illinois Open Source +; License. See LICENSE.TXT for details. +; +;===------------------------------------------------------------------------===; +; +; This is an LLVMBuild description file for the components in this subdirectory. +; +; For more information on the LLVMBuild system, please see: +; +; http://llvm.org/docs/LLVMBuild.html +; +;===------------------------------------------------------------------------===; + +[component_0] +type = Tool +name = pnacl-abicheck +parent = Tools +required_libraries = AsmParser BitReader NaClBitReader IRReader NaClAnalysis diff --git a/tools/pnacl-abicheck/Makefile b/tools/pnacl-abicheck/Makefile new file mode 100644 index 0000000000..97e2d22399 --- /dev/null +++ b/tools/pnacl-abicheck/Makefile @@ -0,0 +1,16 @@ +#===- tools/pnacl-abicheck/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 := pnacl-abicheck +LINK_COMPONENTS := bitreader asmparser naclbitreader irreader naclanalysis + +include $(LEVEL)/Makefile.common + + diff --git a/tools/pnacl-abicheck/pnacl-abicheck.cpp b/tools/pnacl-abicheck/pnacl-abicheck.cpp new file mode 100644 index 0000000000..8b96f17954 --- /dev/null +++ b/tools/pnacl-abicheck/pnacl-abicheck.cpp @@ -0,0 +1,87 @@ +//===-- pnacl-abicheck.cpp - Check PNaCl bitcode ABI ----------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This tool checks files for compliance with the PNaCl bitcode ABI +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/OwningPtr.h" +#include "llvm/Analysis/NaCl.h" +#include "llvm/IRReader/IRReader.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Module.h" +#include "llvm/Pass.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/FormattedStream.h" +#include "llvm/Support/IRReader.h" +#include <string> + +using namespace llvm; + +static cl::opt<std::string> +InputFilename(cl::Positional, cl::desc("<input bitcode>"), cl::init("-")); + +static cl::opt<bool> +Quiet("q", cl::desc("Do not print error messages")); + +static cl::opt<NaClFileFormat> +InputFileFormat( + "bitcode-format", + cl::desc("Define format of input file:"), + cl::values( + clEnumValN(LLVMFormat, "llvm", "LLVM file (default)"), + clEnumValN(PNaClFormat, "pnacl", "PNaCl bitcode file"), + clEnumValEnd), + cl::init(LLVMFormat)); + +// Print any errors collected by the error reporter. Return true if +// there were any. +static bool CheckABIVerifyErrors(PNaClABIErrorReporter &Reporter, + const Twine &Name) { + bool HasErrors = Reporter.getErrorCount() > 0; + if (HasErrors) { + if (!Quiet) { + outs() << "ERROR: " << Name << " is not valid PNaCl bitcode:\n"; + Reporter.printErrors(outs()); + } + } + Reporter.reset(); + return HasErrors; +} + +int main(int argc, char **argv) { + LLVMContext &Context = getGlobalContext(); + SMDiagnostic Err; + cl::ParseCommandLineOptions(argc, argv, "PNaCl Bitcode ABI checker\n"); + + OwningPtr<Module> Mod( + NaClParseIRFile(InputFilename, InputFileFormat, Err, Context)); + if (Mod.get() == 0) { + Err.print(argv[0], errs()); + return 1; + } + PNaClABIErrorReporter ABIErrorReporter; + ABIErrorReporter.setNonFatal(); + bool ErrorsFound = false; + // Manually run the passes so we can tell the user which function had the + // error. No need for a pass manager since it's just one pass. + OwningPtr<ModulePass> ModuleChecker( + createPNaClABIVerifyModulePass(&ABIErrorReporter)); + ModuleChecker->runOnModule(*Mod); + ErrorsFound |= CheckABIVerifyErrors(ABIErrorReporter, "Module"); + OwningPtr<FunctionPass> FunctionChecker( + createPNaClABIVerifyFunctionsPass(&ABIErrorReporter)); + for (Module::iterator MI = Mod->begin(), ME = Mod->end(); MI != ME; ++MI) { + FunctionChecker->runOnFunction(*MI); + ErrorsFound |= CheckABIVerifyErrors(ABIErrorReporter, + "Function " + MI->getName()); + } + + return ErrorsFound ? 1 : 0; +} diff --git a/tools/pnacl-bcanalyzer/CMakeLists.txt b/tools/pnacl-bcanalyzer/CMakeLists.txt new file mode 100644 index 0000000000..0cf17b8886 --- /dev/null +++ b/tools/pnacl-bcanalyzer/CMakeLists.txt @@ -0,0 +1,5 @@ +set(LLVM_LINK_COMPONENTS naclbitreader) + +add_llvm_tool(pnacl-bcanalyzer + pnacl-bcanalyzer.cpp + ) diff --git a/tools/pnacl-bcanalyzer/LLVMBuild.txt b/tools/pnacl-bcanalyzer/LLVMBuild.txt new file mode 100644 index 0000000000..2944fca4b0 --- /dev/null +++ b/tools/pnacl-bcanalyzer/LLVMBuild.txt @@ -0,0 +1,22 @@ +;===- ./tools/pnacl-bcanalyzer/LLVMBuild.txt -------------------*- Conf -*--===; +; +; The LLVM Compiler Infrastructure +; +; This file is distributed under the University of Illinois Open Source +; License. See LICENSE.TXT for details. +; +;===------------------------------------------------------------------------===; +; +; This is an LLVMBuild description file for the components in this subdirectory. +; +; For more information on the LLVMBuild system, please see: +; +; http://llvm.org/docs/LLVMBuild.html +; +;===------------------------------------------------------------------------===; + +[component_0] +type = Tool +name = pnacl-bcanalyzer +parent = Tools +required_libraries = NaClBitReader diff --git a/tools/pnacl-bcanalyzer/Makefile b/tools/pnacl-bcanalyzer/Makefile new file mode 100644 index 0000000000..d3ec1a81a9 --- /dev/null +++ b/tools/pnacl-bcanalyzer/Makefile @@ -0,0 +1,17 @@ +##===- tools/pnacl-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 := pnacl-bcanalyzer +LINK_COMPONENTS := naclbitreader + +# This tool has no plugins, optimize startup time. +TOOL_NO_EXPORTS := 1 + +include $(LEVEL)/Makefile.common diff --git a/tools/pnacl-bcanalyzer/pnacl-bcanalyzer.cpp b/tools/pnacl-bcanalyzer/pnacl-bcanalyzer.cpp new file mode 100644 index 0000000000..225827e47b --- /dev/null +++ b/tools/pnacl-bcanalyzer/pnacl-bcanalyzer.cpp @@ -0,0 +1,641 @@ +//===-- pnacl-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: +// pnacl-bcanalyzer [options] - Read frozen PNaCl bitcode from stdin +// pnacl-bcanalyzer [options] x.bc - Read frozen PNaCl 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). +// +//===----------------------------------------------------------------------===// + +#define DEBUG_TYPE "pnacl-bcanalyzer" + +#include "llvm/ADT/OwningPtr.h" +#include "llvm/Analysis/Verifier.h" +#include "llvm/Bitcode/NaCl/NaClBitcodeHeader.h" +#include "llvm/Bitcode/NaCl/NaClBitstreamReader.h" +#include "llvm/Bitcode/NaCl/NaClLLVMBitCodes.h" +#include "llvm/Bitcode/NaCl/NaClReaderWriter.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/PrettyStackTrace.h" +#include "llvm/Support/Signals.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Support/system_error.h" +#include <algorithm> +#include <map> +using namespace llvm; + +static cl::opt<std::string> + InputFilename(cl::Positional, cl::desc("<input bitcode>"), cl::init("-")); + +static cl::opt<bool> Dump("dump", cl::desc("Dump low level bitcode trace")); + +static cl::opt<unsigned> OpsPerLine( + "operands-per-line", + cl::desc("Number of operands to print per dump line. 0 implies " + "all operands will be printed on the same line (default)"), + cl::init(0)); + +//===----------------------------------------------------------------------===// +// 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 numeric info in dump even if" + " symbolic info is available")); + + +/// GetBlockName - Return a symbolic block name if known, otherwise return +/// null. +static const char *GetBlockName(unsigned BlockID, + const NaClBitstreamReader &StreamFile) { + // Standard blocks for all bitcode files. + if (BlockID < naclbitc::FIRST_APPLICATION_BLOCKID) { + if (BlockID == naclbitc::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 NaClBitstreamReader::BlockInfo *Info = + StreamFile.getBlockInfo(BlockID)) { + if (!Info->Name.empty()) + return Info->Name.c_str(); + } + + switch (BlockID) { + default: return 0; + case naclbitc::MODULE_BLOCK_ID: return "MODULE_BLOCK"; + case naclbitc::PARAMATTR_BLOCK_ID: return "PARAMATTR_BLOCK"; + case naclbitc::PARAMATTR_GROUP_BLOCK_ID: return "PARAMATTR_GROUP_BLOCK_ID"; + case naclbitc::TYPE_BLOCK_ID_NEW: return "TYPE_BLOCK_ID"; + case naclbitc::CONSTANTS_BLOCK_ID: return "CONSTANTS_BLOCK"; + case naclbitc::FUNCTION_BLOCK_ID: return "FUNCTION_BLOCK"; + case naclbitc::VALUE_SYMTAB_BLOCK_ID: return "VALUE_SYMTAB"; + case naclbitc::METADATA_BLOCK_ID: return "METADATA_BLOCK"; + case naclbitc::METADATA_ATTACHMENT_ID: return "METADATA_ATTACHMENT_BLOCK"; + case naclbitc::USELIST_BLOCK_ID: return "USELIST_BLOCK_ID"; + case naclbitc::GLOBALVAR_BLOCK_ID: return "GLOBALVAR_BLOCK"; + } +} + +/// GetCodeName - Return a symbolic code name if known, otherwise return +/// null. +static const char *GetCodeName(unsigned CodeID, unsigned BlockID, + const NaClBitstreamReader &StreamFile) { + // Standard blocks for all bitcode files. + if (BlockID < naclbitc::FIRST_APPLICATION_BLOCKID) { + if (BlockID == naclbitc::BLOCKINFO_BLOCK_ID) { + switch (CodeID) { + default: return 0; + case naclbitc::BLOCKINFO_CODE_SETBID: return "SETBID"; + case naclbitc::BLOCKINFO_CODE_BLOCKNAME: return "BLOCKNAME"; + case naclbitc::BLOCKINFO_CODE_SETRECORDNAME: return "SETRECORDNAME"; + } + } + return 0; + } + + // Check to see if we have a blockinfo record for this record, with a name. + if (const NaClBitstreamReader::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(); + } + + switch (BlockID) { + default: return 0; + case naclbitc::MODULE_BLOCK_ID: + switch (CodeID) { + default: return 0; + case naclbitc::MODULE_CODE_VERSION: return "VERSION"; + case naclbitc::MODULE_CODE_TRIPLE: return "TRIPLE"; + case naclbitc::MODULE_CODE_DATALAYOUT: return "DATALAYOUT"; + case naclbitc::MODULE_CODE_ASM: return "ASM"; + case naclbitc::MODULE_CODE_SECTIONNAME: return "SECTIONNAME"; + case naclbitc::MODULE_CODE_DEPLIB: return "DEPLIB"; // FIXME: Remove in 4.0 + case naclbitc::MODULE_CODE_GLOBALVAR: return "GLOBALVAR"; + case naclbitc::MODULE_CODE_FUNCTION: return "FUNCTION"; + case naclbitc::MODULE_CODE_ALIAS: return "ALIAS"; + case naclbitc::MODULE_CODE_PURGEVALS: return "PURGEVALS"; + case naclbitc::MODULE_CODE_GCNAME: return "GCNAME"; + } + case naclbitc::PARAMATTR_BLOCK_ID: + switch (CodeID) { + default: return 0; + case naclbitc::PARAMATTR_CODE_ENTRY_OLD: return "ENTRY"; + case naclbitc::PARAMATTR_CODE_ENTRY: return "ENTRY"; + case naclbitc::PARAMATTR_GRP_CODE_ENTRY: return "ENTRY"; + } + case naclbitc::TYPE_BLOCK_ID_NEW: + switch (CodeID) { + default: return 0; + case naclbitc::TYPE_CODE_NUMENTRY: return "NUMENTRY"; + case naclbitc::TYPE_CODE_VOID: return "VOID"; + case naclbitc::TYPE_CODE_FLOAT: return "FLOAT"; + case naclbitc::TYPE_CODE_DOUBLE: return "DOUBLE"; + case naclbitc::TYPE_CODE_LABEL: return "LABEL"; + case naclbitc::TYPE_CODE_OPAQUE: return "OPAQUE"; + case naclbitc::TYPE_CODE_INTEGER: return "INTEGER"; + case naclbitc::TYPE_CODE_POINTER: return "POINTER"; + case naclbitc::TYPE_CODE_ARRAY: return "ARRAY"; + case naclbitc::TYPE_CODE_VECTOR: return "VECTOR"; + case naclbitc::TYPE_CODE_X86_FP80: return "X86_FP80"; + case naclbitc::TYPE_CODE_FP128: return "FP128"; + case naclbitc::TYPE_CODE_PPC_FP128: return "PPC_FP128"; + case naclbitc::TYPE_CODE_METADATA: return "METADATA"; + case naclbitc::TYPE_CODE_STRUCT_ANON: return "STRUCT_ANON"; + case naclbitc::TYPE_CODE_STRUCT_NAME: return "STRUCT_NAME"; + case naclbitc::TYPE_CODE_STRUCT_NAMED: return "STRUCT_NAMED"; + case naclbitc::TYPE_CODE_FUNCTION: return "FUNCTION"; + } + + case naclbitc::CONSTANTS_BLOCK_ID: + switch (CodeID) { + default: return 0; + case naclbitc::CST_CODE_SETTYPE: return "SETTYPE"; + case naclbitc::CST_CODE_NULL: return "NULL"; + case naclbitc::CST_CODE_UNDEF: return "UNDEF"; + case naclbitc::CST_CODE_INTEGER: return "INTEGER"; + case naclbitc::CST_CODE_WIDE_INTEGER: return "WIDE_INTEGER"; + case naclbitc::CST_CODE_FLOAT: return "FLOAT"; + case naclbitc::CST_CODE_AGGREGATE: return "AGGREGATE"; + case naclbitc::CST_CODE_STRING: return "STRING"; + case naclbitc::CST_CODE_CSTRING: return "CSTRING"; + case naclbitc::CST_CODE_CE_BINOP: return "CE_BINOP"; + case naclbitc::CST_CODE_CE_CAST: return "CE_CAST"; + case naclbitc::CST_CODE_CE_GEP: return "CE_GEP"; + case naclbitc::CST_CODE_CE_INBOUNDS_GEP: return "CE_INBOUNDS_GEP"; + case naclbitc::CST_CODE_CE_SELECT: return "CE_SELECT"; + case naclbitc::CST_CODE_CE_EXTRACTELT: return "CE_EXTRACTELT"; + case naclbitc::CST_CODE_CE_INSERTELT: return "CE_INSERTELT"; + case naclbitc::CST_CODE_CE_SHUFFLEVEC: return "CE_SHUFFLEVEC"; + case naclbitc::CST_CODE_CE_CMP: return "CE_CMP"; + case naclbitc::CST_CODE_INLINEASM: return "INLINEASM"; + case naclbitc::CST_CODE_CE_SHUFVEC_EX: return "CE_SHUFVEC_EX"; + case naclbitc::CST_CODE_BLOCKADDRESS: return "CST_CODE_BLOCKADDRESS"; + case naclbitc::CST_CODE_DATA: return "DATA"; + } + case naclbitc::FUNCTION_BLOCK_ID: + switch (CodeID) { + default: return 0; + case naclbitc::FUNC_CODE_DECLAREBLOCKS: return "DECLAREBLOCKS"; + + case naclbitc::FUNC_CODE_INST_BINOP: return "INST_BINOP"; + case naclbitc::FUNC_CODE_INST_CAST: return "INST_CAST"; + case naclbitc::FUNC_CODE_INST_GEP: return "INST_GEP"; + case naclbitc::FUNC_CODE_INST_INBOUNDS_GEP: return "INST_INBOUNDS_GEP"; + case naclbitc::FUNC_CODE_INST_SELECT: return "INST_SELECT"; + case naclbitc::FUNC_CODE_INST_EXTRACTELT: return "INST_EXTRACTELT"; + case naclbitc::FUNC_CODE_INST_INSERTELT: return "INST_INSERTELT"; + case naclbitc::FUNC_CODE_INST_SHUFFLEVEC: return "INST_SHUFFLEVEC"; + case naclbitc::FUNC_CODE_INST_CMP: return "INST_CMP"; + + case naclbitc::FUNC_CODE_INST_RET: return "INST_RET"; + case naclbitc::FUNC_CODE_INST_BR: return "INST_BR"; + case naclbitc::FUNC_CODE_INST_SWITCH: return "INST_SWITCH"; + case naclbitc::FUNC_CODE_INST_INVOKE: return "INST_INVOKE"; + case naclbitc::FUNC_CODE_INST_UNREACHABLE: return "INST_UNREACHABLE"; + + case naclbitc::FUNC_CODE_INST_PHI: return "INST_PHI"; + case naclbitc::FUNC_CODE_INST_ALLOCA: return "INST_ALLOCA"; + case naclbitc::FUNC_CODE_INST_LOAD: return "INST_LOAD"; + case naclbitc::FUNC_CODE_INST_VAARG: return "INST_VAARG"; + case naclbitc::FUNC_CODE_INST_STORE: return "INST_STORE"; + case naclbitc::FUNC_CODE_INST_EXTRACTVAL: return "INST_EXTRACTVAL"; + case naclbitc::FUNC_CODE_INST_INSERTVAL: return "INST_INSERTVAL"; + case naclbitc::FUNC_CODE_INST_CMP2: return "INST_CMP2"; + case naclbitc::FUNC_CODE_INST_VSELECT: return "INST_VSELECT"; + case naclbitc::FUNC_CODE_DEBUG_LOC_AGAIN: return "DEBUG_LOC_AGAIN"; + case naclbitc::FUNC_CODE_INST_CALL: return "INST_CALL"; + case naclbitc::FUNC_CODE_DEBUG_LOC: return "DEBUG_LOC"; + case naclbitc::FUNC_CODE_INST_FORWARDTYPEREF: return "FORWARDTYPEREF"; + } + case naclbitc::VALUE_SYMTAB_BLOCK_ID: + switch (CodeID) { + default: return 0; + case naclbitc::VST_CODE_ENTRY: return "ENTRY"; + case naclbitc::VST_CODE_BBENTRY: return "BBENTRY"; + } + case naclbitc::METADATA_ATTACHMENT_ID: + switch(CodeID) { + default:return 0; + case naclbitc::METADATA_ATTACHMENT: return "METADATA_ATTACHMENT"; + } + case naclbitc::METADATA_BLOCK_ID: + switch(CodeID) { + default:return 0; + case naclbitc::METADATA_STRING: return "METADATA_STRING"; + case naclbitc::METADATA_NAME: return "METADATA_NAME"; + case naclbitc::METADATA_KIND: return "METADATA_KIND"; + case naclbitc::METADATA_NODE: return "METADATA_NODE"; + case naclbitc::METADATA_FN_NODE: return "METADATA_FN_NODE"; + case naclbitc::METADATA_NAMED_NODE: return "METADATA_NAMED_NODE"; + } + case naclbitc::USELIST_BLOCK_ID: + switch(CodeID) { + default:return 0; + case naclbitc::USELIST_CODE_ENTRY: return "USELIST_CODE_ENTRY"; + } + case naclbitc::GLOBALVAR_BLOCK_ID: + switch (CodeID) { + default: return 0; + case naclbitc::GLOBALVAR_VAR: return "VAR"; + case naclbitc::GLOBALVAR_COMPOUND: return "COMPOUND"; + case naclbitc::GLOBALVAR_ZEROFILL: return "ZEROFILL"; + case naclbitc::GLOBALVAR_DATA: return "DATA"; + case naclbitc::GLOBALVAR_RELOC: return "RELOC"; + case naclbitc::GLOBALVAR_COUNT: return "COUNT"; + } + } +} + +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) { + errs() << Err << "\n"; + return true; +} + +/// ParseBlock - Read a block, updating statistics, etc. +static bool ParseBlock(NaClBitstreamCursor &Stream, unsigned BlockID, + unsigned IndentLevel) { + std::string Indent(IndentLevel*2, ' '); + DEBUG(dbgs() << Indent << "-> ParseBlock(" << BlockID << ")\n"); + uint64_t BlockBitStart = Stream.GetCurrentBitNo(); + + // Get the statistics for this BlockID. + PerBlockIDStats &BlockStats = BlockIDStats[BlockID]; + + BlockStats.NumInstances++; + + // BLOCKINFO is a special part of the stream. + if (BlockID == naclbitc::BLOCKINFO_BLOCK_ID) { + if (Dump) outs() << Indent << "<BLOCKINFO_BLOCK/>\n"; + if (Stream.ReadBlockInfoBlock()) + return Error("Malformed BlockInfoBlock"); + uint64_t BlockBitEnd = Stream.GetCurrentBitNo(); + BlockStats.NumBits += BlockBitEnd-BlockBitStart; + DEBUG(dbgs() << Indent << "<- ParseBlock\n"); + return false; + } + + unsigned NumWords = 0; + if (Stream.EnterSubBlock(BlockID, &NumWords)) + return Error("Malformed block record"); + + const char *BlockName = 0; + if (Dump) { + outs() << Indent << "<"; + if ((BlockName = GetBlockName(BlockID, *Stream.getBitStreamReader()))) + outs() << BlockName; + else + outs() << "UnknownBlock" << BlockID; + + if (NonSymbolic && BlockName) + outs() << " BlockID=" << BlockID; + + outs() << " 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(); + + NaClBitstreamEntry Entry = + Stream.advance(NaClBitstreamCursor::AF_DontAutoprocessAbbrevs); + + switch (Entry.Kind) { + case NaClBitstreamEntry::Error: + return Error("malformed bitcode file"); + case NaClBitstreamEntry::EndBlock: { + uint64_t BlockBitEnd = Stream.GetCurrentBitNo(); + BlockStats.NumBits += BlockBitEnd-BlockBitStart; + if (Dump) { + outs() << Indent << "</"; + if (BlockName) + outs() << BlockName << ">\n"; + else + outs() << "UnknownBlock" << BlockID << ">\n"; + } + DEBUG(dbgs() << Indent << "<- ParseBlock\n"); + return false; + } + + case NaClBitstreamEntry::SubBlock: { + uint64_t SubBlockBitStart = Stream.GetCurrentBitNo(); + if (ParseBlock(Stream, Entry.ID, 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; + continue; + } + case NaClBitstreamEntry::Record: + // The interesting case. + break; + } + + if (Entry.ID == naclbitc::DEFINE_ABBREV) { + Stream.ReadAbbrevRecord(); + ++BlockStats.NumAbbrevs; + continue; + } + + Record.clear(); + + ++BlockStats.NumRecords; + + StringRef Blob; + unsigned Code = Stream.readRecord(Entry.ID, Record, &Blob); + + // 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 (Entry.ID != naclbitc::UNABBREV_RECORD) { + BlockStats.CodeFreq[Code].NumAbbrev++; + ++BlockStats.NumAbbreviatedRecords; + } + + if (Dump) { + outs() << Indent << " <"; + const char *CodeName = + GetCodeName(Code, BlockID, *Stream.getBitStreamReader()); + if (CodeName) + outs() << CodeName; + else + outs() << "UnknownCode" << Code; + if (NonSymbolic && CodeName) + outs() << " codeid=" << Code; + if (Entry.ID != naclbitc::UNABBREV_RECORD) + outs() << " abbrevid=" << Entry.ID; + + for (unsigned i = 0, e = Record.size(); i != e; ++i) { + if (OpsPerLine && (i % OpsPerLine) == 0 && i > 0) { + outs() << "\n" << Indent << " "; + if (CodeName) { + for (unsigned j = 0; j < strlen(CodeName); ++j) + outs() << " "; + } else { + outs() << " "; + } + } + outs() << " op" << i << "=" << (int64_t)Record[i]; + } + + outs() << "/>"; + + if (Blob.data()) { + outs() << " blob data = "; + bool BlobIsPrintable = true; + for (unsigned i = 0, e = Blob.size(); i != e; ++i) + if (!isprint(static_cast<unsigned char>(Blob[i]))) { + BlobIsPrintable = false; + break; + } + + if (BlobIsPrintable) + outs() << "'" << Blob << "'"; + else + outs() << "unprintable, " << Blob.size() << " bytes."; + } + + outs() << "\n"; + } + } +} + +static void PrintSize(double Bits) { + outs() << format("%.2f/%.2fB/%luW", Bits, Bits/8,(unsigned long)(Bits/32)); +} +static void PrintSize(uint64_t Bits) { + outs() << format("%lub/%.2fB/%luW", (unsigned long)Bits, + (double)Bits/8, (unsigned long)(Bits/32)); +} + + +/// AnalyzeBitcode - Analyze the bitcode file specified by InputFilename. +static int AnalyzeBitcode() { + DEBUG(dbgs() << "-> AnalyzeBitcode\n"); + // Read the input file. + OwningPtr<MemoryBuffer> MemBuf; + + if (error_code ec = + MemoryBuffer::getFileOrSTDIN(InputFilename.c_str(), MemBuf)) + return Error("Error reading '" + InputFilename + "': " + ec.message()); + + if (MemBuf->getBufferSize() & 3) + return Error("Bitcode stream should be a multiple of 4 bytes in length"); + + const unsigned char *BufPtr = (const unsigned char *)MemBuf->getBufferStart(); + const unsigned char *EndBufPtr = BufPtr+MemBuf->getBufferSize(); + + NaClBitcodeHeader Header; + if (Header.Read(BufPtr, EndBufPtr)) + return Error("Invalid PNaCl bitcode header"); + + if (!Header.IsSupported()) + errs() << "Warning: " << Header.Unsupported() << "\n"; + + if (!Header.IsReadable()) + Error("Bitcode file is not readable"); + + NaClBitstreamReader StreamFile(BufPtr, EndBufPtr); + NaClBitstreamCursor Stream(StreamFile); + StreamFile.CollectBlockInfoNames(); + + unsigned NumTopBlocks = 0; + + // Print out header information. + for (size_t i = 0, limit = Header.NumberFields(); i < limit; ++i) { + outs() << Header.GetField(i)->Contents() << "\n"; + } + if (Header.NumberFields()) outs() << "\n"; + + // Parse the top-level structure. We only allow blocks at the top-level. + while (!Stream.AtEndOfStream()) { + unsigned Code = Stream.ReadCode(); + if (Code != naclbitc::ENTER_SUBBLOCK) + return Error("Invalid record at top-level"); + + unsigned BlockID = Stream.ReadSubBlockID(); + + if (ParseBlock(Stream, BlockID, 0)) + return true; + ++NumTopBlocks; + } + + if (Dump) outs() << "\n\n"; + + uint64_t BufferSizeBits = (EndBufPtr-BufPtr)*CHAR_BIT; + // Print a summary of the read file. + outs() << "Summary of " << InputFilename << ":\n"; + outs() << " Total size: "; + PrintSize(BufferSizeBits); + outs() << "\n"; + outs() << " # Toplevel Blocks: " << NumTopBlocks << "\n"; + outs() << "\n"; + + // Emit per-block stats. + outs() << "Per-block Summary:\n"; + for (std::map<unsigned, PerBlockIDStats>::iterator I = BlockIDStats.begin(), + E = BlockIDStats.end(); I != E; ++I) { + outs() << " Block ID #" << I->first; + if (const char *BlockName = GetBlockName(I->first, StreamFile)) + outs() << " (" << BlockName << ")"; + outs() << ":\n"; + + const PerBlockIDStats &Stats = I->second; + outs() << " Num Instances: " << Stats.NumInstances << "\n"; + outs() << " Total Size: "; + PrintSize(Stats.NumBits); + outs() << "\n"; + double pct = (Stats.NumBits * 100.0) / BufferSizeBits; + outs() << " Percent of file: " << format("%2.4f%%", pct) << "\n"; + if (Stats.NumInstances > 1) { + outs() << " Average Size: "; + PrintSize(Stats.NumBits/(double)Stats.NumInstances); + outs() << "\n"; + outs() << " Tot/Avg SubBlocks: " << Stats.NumSubBlocks << "/" + << Stats.NumSubBlocks/(double)Stats.NumInstances << "\n"; + outs() << " Tot/Avg Abbrevs: " << Stats.NumAbbrevs << "/" + << Stats.NumAbbrevs/(double)Stats.NumInstances << "\n"; + outs() << " Tot/Avg Records: " << Stats.NumRecords << "/" + << Stats.NumRecords/(double)Stats.NumInstances << "\n"; + } else { + outs() << " Num SubBlocks: " << Stats.NumSubBlocks << "\n"; + outs() << " Num Abbrevs: " << Stats.NumAbbrevs << "\n"; + outs() << " Num Records: " << Stats.NumRecords << "\n"; + } + if (Stats.NumRecords) { + double pct = (Stats.NumAbbreviatedRecords * 100.0) / Stats.NumRecords; + outs() << " Percent Abbrevs: " << format("%2.4f%%", pct) << "\n"; + } + outs() << "\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()); + + outs() << "\tRecord Histogram:\n"; + outs() << "\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]; + + outs() << format("\t\t%7d %9lu", + RecStats.NumInstances, + (unsigned long)RecStats.TotalBits); + + if (RecStats.NumAbbrev) + outs() << + format("%7.2f ", + (double)RecStats.NumAbbrev/RecStats.NumInstances*100); + else + outs() << " "; + + if (const char *CodeName = + GetCodeName(FreqPairs[i].second, I->first, StreamFile)) + outs() << CodeName << "\n"; + else + outs() << "UnknownCode" << FreqPairs[i].second << "\n"; + } + outs() << "\n"; + + } + } + DEBUG(dbgs() << "<- AnalyzeBitcode\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, "pnacl-bcanalyzer file analyzer\n"); + + return AnalyzeBitcode(); +} diff --git a/tools/pnacl-freeze/CMakeLists.txt b/tools/pnacl-freeze/CMakeLists.txt new file mode 100644 index 0000000000..fca58c7d5a --- /dev/null +++ b/tools/pnacl-freeze/CMakeLists.txt @@ -0,0 +1,5 @@ +set(LLVM_LINK_COMPONENTS bitreader naclbitwriter naclbitreader) + +add_llvm_tool(pnacl-freeze + pnacl-freeze.cpp + ) diff --git a/tools/pnacl-freeze/LLVMBuild.txt b/tools/pnacl-freeze/LLVMBuild.txt new file mode 100644 index 0000000000..8e3499b991 --- /dev/null +++ b/tools/pnacl-freeze/LLVMBuild.txt @@ -0,0 +1,16 @@ +;===- ./tools/pnacl-freeze/LLVMBuild.txt -----------------------*- Conf -*--===; +;===------------------------------------------------------------------------===; +; +; This is an LLVMBuild description file for the components in this subdirectory. +; +; For more information on the LLVMBuild system, please see: +; +; http://llvm.org/docs/LLVMBuild.html +; +;===------------------------------------------------------------------------===; + +[component_0] +type = Tool +name = pnacl-freeze +parent = Tools +required_libraries = NaClBitWriter BitReader diff --git a/tools/pnacl-freeze/Makefile b/tools/pnacl-freeze/Makefile new file mode 100644 index 0000000000..5872f1cd15 --- /dev/null +++ b/tools/pnacl-freeze/Makefile @@ -0,0 +1,17 @@ +##===- tools/pnacl-freeze/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 := pnacl-freeze +LINK_COMPONENTS := naclbitwriter bitreader + +# This tool has no plugins, optimize startup time. +TOOL_NO_EXPORTS := 1 + +include $(LEVEL)/Makefile.common diff --git a/tools/pnacl-freeze/pnacl-freeze.cpp b/tools/pnacl-freeze/pnacl-freeze.cpp new file mode 100644 index 0000000000..297edb85a7 --- /dev/null +++ b/tools/pnacl-freeze/pnacl-freeze.cpp @@ -0,0 +1,95 @@ +/* Copyright 2013 The Native Client Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can + * be found in the LICENSE file. + */ + +//===-- pnacl-freeze.cpp - The low-level NaCl bitcode freezer --------===// +// +//===----------------------------------------------------------------------===// +// +// Generates NaCl pexe wire format. +// +//===----------------------------------------------------------------------===// + +#include "llvm/IR/LLVMContext.h" +// Note: We need the following to provide the API for calling the NaCl +// Bitcode Writer to generate the frozen file. +#include "llvm/Bitcode/NaCl/NaClReaderWriter.h" +// Note: We need the following to provide the API for calling the (LLVM) +// Bitcode Reader to read in the corresonding pexe file to freeze. +#include "llvm/Bitcode/ReaderWriter.h" +#include "llvm/IR/Module.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/DataStream.h" +#include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/PrettyStackTrace.h" +#include "llvm/Support/Signals.h" +#include "llvm/Support/ToolOutputFile.h" + +using namespace llvm; + + +static cl::opt<std::string> +OutputFilename("o", cl::desc("Specify output filename"), + cl::value_desc("filename"), cl::init("-")); + +static cl::opt<std::string> +InputFilename(cl::Positional, cl::desc("<pexe file>"), cl::init("-")); + +static void WriteOutputFile(const Module *M) { + + std::string ErrorInfo; + OwningPtr<tool_output_file> Out + (new tool_output_file(OutputFilename.c_str(), ErrorInfo, + raw_fd_ostream::F_Binary)); + if (!ErrorInfo.empty()) { + errs() << ErrorInfo << '\n'; + exit(1); + } + + NaClWriteBitcodeToFile(M, Out->os()); + + // Declare success. + Out->keep(); +} + +int main(int argc, char **argv) { + // Print a stack trace if we signal out. + sys::PrintStackTraceOnErrorSignal(); + PrettyStackTraceProgram X(argc, argv); + + LLVMContext &Context = getGlobalContext(); + llvm_shutdown_obj Y; // Call llvm_shutdown() on exit. + + cl::ParseCommandLineOptions(argc, argv, "Generates NaCl pexe wire format\n"); + + std::string ErrorMessage; + std::auto_ptr<Module> M; + + // Use the bitcode streaming interface + DataStreamer *streamer = getDataFileStreamer(InputFilename, &ErrorMessage); + if (streamer) { + std::string DisplayFilename; + if (InputFilename == "-") + DisplayFilename = "<stdin>"; + else + DisplayFilename = InputFilename; + M.reset(getStreamedBitcodeModule(DisplayFilename, streamer, Context, + &ErrorMessage)); + if(M.get() != 0 && M->MaterializeAllPermanently(&ErrorMessage)) { + M.reset(); + } + } + + if (M.get() == 0) { + errs() << argv[0] << ": "; + if (ErrorMessage.size()) + errs() << ErrorMessage << "\n"; + else + errs() << "bitcode didn't read correctly.\n"; + return 1; + } + + WriteOutputFile(M.get()); + return 0; +} diff --git a/tools/pnacl-llc/CMakeLists.txt b/tools/pnacl-llc/CMakeLists.txt new file mode 100644 index 0000000000..9e53a28aff --- /dev/null +++ b/tools/pnacl-llc/CMakeLists.txt @@ -0,0 +1,9 @@ +set(LLVM_LINK_COMPONENTS ${LLVM_TARGETS_TO_BUILD} bitreader naclbitreader + irreader asmparser naclanalysis) + +add_llvm_tool(pnacl-llc +# This file provides wrappers to lseek(2), read(2), etc. + nacl_file.cpp + SRPCStreamer.cpp + pnacl-llc.cpp + ) diff --git a/tools/pnacl-llc/LLVMBuild.txt b/tools/pnacl-llc/LLVMBuild.txt new file mode 100644 index 0000000000..8d441f5a70 --- /dev/null +++ b/tools/pnacl-llc/LLVMBuild.txt @@ -0,0 +1,22 @@ +;===- ./tools/pnacl-llc/LLVMBuild.txt --------------------------*- Conf -*--===; +; +; The LLVM Compiler Infrastructure +; +; This file is distributed under the University of Illinois Open Source +; License. See LICENSE.TXT for details. +; +;===------------------------------------------------------------------------===; +; +; This is an LLVMBuild description file for the components in this subdirectory. +; +; For more information on the LLVMBuild system, please see: +; +; http://llvm.org/docs/LLVMBuild.html +; +;===------------------------------------------------------------------------===; + +[component_0] +type = Tool +name = pnacl-llc +parent = Tools +required_libraries = AsmParser BitReader NaClBitReader IRReader all-targets NaClAnalysis diff --git a/tools/pnacl-llc/Makefile b/tools/pnacl-llc/Makefile new file mode 100644 index 0000000000..bf4a0e8be8 --- /dev/null +++ b/tools/pnacl-llc/Makefile @@ -0,0 +1,16 @@ +#===- tools/pnacl-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 := pnacl-llc +LINK_COMPONENTS := all-targets bitreader naclbitreader irreader \ + asmparser naclanalysis nacltransforms + +include $(LEVEL)/Makefile.common + diff --git a/tools/pnacl-llc/SRPCStreamer.cpp b/tools/pnacl-llc/SRPCStreamer.cpp new file mode 100644 index 0000000000..c41650c89a --- /dev/null +++ b/tools/pnacl-llc/SRPCStreamer.cpp @@ -0,0 +1,142 @@ +//===-- SRPCStreamer.cpp - Stream bitcode over SRPC ----------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// +// +//===----------------------------------------------------------------------===// + +#if defined(__native_client__) +#define DEBUG_TYPE "bitcode-stream" +#include "SRPCStreamer.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/raw_ostream.h" +#include <errno.h> + +using llvm::dbgs; + +const size_t QueueStreamer::queuesize_limit_; + +size_t QueueStreamer::GetBytes(unsigned char *buf, size_t len) { + size_t total_copied = 0; + pthread_mutex_lock(&Mutex); + while (!Done && queueSize() < len - total_copied) { + size_t size = queueSize(); + DEBUG(dbgs() << "QueueStreamer::GetBytes len " << len << " size " << + size << " << waiting\n"); + queueGet(buf + total_copied, size); + total_copied += size; + pthread_cond_signal(&Cond); + pthread_cond_wait(&Cond, &Mutex); + } + // If this is the last partial chunk, adjust len such that the amount we + // fetch will be just the remaining bytes. + if (Done && queueSize() < len - total_copied) { + len = queueSize() + total_copied; + } + queueGet(buf + total_copied, len - total_copied); + pthread_cond_signal(&Cond); + pthread_mutex_unlock(&Mutex); + return len; +} + +size_t QueueStreamer::PutBytes(unsigned char *buf, size_t len) { + size_t total_copied = 0; + pthread_mutex_lock(&Mutex); + while (capacityRemaining() < len - total_copied) { + if (Bytes.size() * 2 > queuesize_limit_) { + size_t space = capacityRemaining(); + queuePut(buf + total_copied, space); + total_copied += space; + pthread_cond_signal(&Cond); + pthread_cond_wait(&Cond, &Mutex); + } else { + queueResize(); + } + } + queuePut(buf + total_copied, len - total_copied); + pthread_cond_signal(&Cond); + pthread_mutex_unlock(&Mutex); + return len; +} + +void QueueStreamer::SetDone() { + // Still need the lock to avoid signaling between the check and + // the wait in GetBytes. + pthread_mutex_lock(&Mutex); + Done = true; + pthread_cond_signal(&Cond); + pthread_mutex_unlock(&Mutex); +} + +// Double the size of the queue. Called with Mutex to protect Cons/Prod/Bytes. +void QueueStreamer::queueResize() { + int leftover = Bytes.size() - Cons; + DEBUG(dbgs() << "resizing to " << Bytes.size() * 2 << " " << leftover << " " + << Prod << " " << Cons << "\n"); + Bytes.resize(Bytes.size() * 2); + if (Cons > Prod) { + // There are unread bytes left between Cons and the previous end of the + // buffer. Move them to the new end of the buffer. + memmove(&Bytes[Bytes.size() - leftover], &Bytes[Cons], leftover); + Cons = Bytes.size() - leftover; + } +} + +// Called with Mutex held to protect Cons, Prod, and Bytes +void QueueStreamer::queuePut(unsigned char *buf, size_t len) { + size_t EndSpace = std::min(len, Bytes.size() - Prod); + DEBUG(dbgs() << "put, len " << len << " Endspace " << EndSpace << " p " << + Prod << " c " << Cons << "\n"); + // Copy up to the end of the buffer + memcpy(&Bytes[Prod], buf, EndSpace); + // Wrap around if necessary + memcpy(&Bytes[0], buf + EndSpace, len - EndSpace); + Prod = (Prod + len) % Bytes.size(); +} + +// Called with Mutex held to protect Cons, Prod, and Bytes +void QueueStreamer::queueGet(unsigned char *buf, size_t len) { + assert(len <= queueSize()); + size_t EndSpace = std::min(len, Bytes.size() - Cons); + DEBUG(dbgs() << "get, len " << len << " Endspace " << EndSpace << " p " << + Prod << " c " << Cons << "\n"); + // Copy up to the end of the buffer + memcpy(buf, &Bytes[Cons], EndSpace); + // Wrap around if necessary + memcpy(buf + EndSpace, &Bytes[0], len - EndSpace); + Cons = (Cons + len) % Bytes.size(); +} + +llvm::DataStreamer *SRPCStreamer::init(void *(*Callback)(void *), void *arg, + std::string *ErrMsg) { + int err = pthread_create(&CompileThread, NULL, Callback, arg); + if (err) { + if (ErrMsg) *ErrMsg = std::string(strerror(errno)); + return NULL; + } + return &Q; +} + +size_t SRPCStreamer::gotChunk(unsigned char *bytes, size_t len) { + if (Error) return 0; + return Q.PutBytes(bytes, len); +} + +int SRPCStreamer::streamEnd(std::string *ErrMsg) { + Q.SetDone(); + int err = pthread_join(CompileThread, NULL); + if (err) { + if (ErrMsg) *ErrMsg = std::string(strerror(errno)); + return err; + } + if (Error && ErrMsg) *ErrMsg = std::string("compile failed."); + return Error; +} + +#endif diff --git a/tools/pnacl-llc/SRPCStreamer.h b/tools/pnacl-llc/SRPCStreamer.h new file mode 100644 index 0000000000..4c1c6737e6 --- /dev/null +++ b/tools/pnacl-llc/SRPCStreamer.h @@ -0,0 +1,117 @@ +//===-- SRPCStreamer.cpp - Stream bitcode over SRPC ----------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// +// +//===----------------------------------------------------------------------===// + +#ifndef SRPCSTREAMER_H +#define SRPCSTREAMER_H + +#include <pthread.h> +#include <cassert> +#include <cstdio> +#include <cstring> +#include <vector> +#include "llvm/Support/DataStream.h" + +// Implements LLVM's interface for fetching data from a stream source. +// Bitcode bytes from the RPC thread are placed here with PutBytes and buffered +// until the bitcode reader calls GetBytes to remove them. +// The blocking behavior of GetBytes and PutBytes means that if the +// compilation happens faster than the bytes come in from the browser, the +// whole pipeline can block waiting for the RPC thread to put more bytes. + +class QueueStreamer : public llvm::DataStreamer { + public: + QueueStreamer() : Done(false), Prod(0), Cons(0) { + pthread_mutex_init(&Mutex, NULL); + pthread_cond_init(&Cond, NULL); + Bytes.resize(64 * 1024); + } + + // Called by the compilation thread. Copy len bytes from the queue into + // buf. If there are less than len bytes available, copy as many as + // there are, signal the RPC thread, and block to wait for the rest. + // If all bytes have been received from the browser and there are + // fewer than len bytes available, copy all remaining bytes. + // Return the number of bytes copied. + virtual size_t GetBytes(unsigned char *buf, size_t len); + + // Called by the RPC thread. Copy len bytes from buf into the queue. + // If there is not enough space in the queue, copy as many bytes as + // will fit, signal the compilation thread, and block until there is + // enough space for the rest. + // Return the number of bytes copied. + size_t PutBytes(unsigned char *buf, size_t len); + + // Called by the RPC thread. Signal that all bytes have been received, + // so the last call to GetBytes will return the remaining bytes rather + // than waiting for the entire requested amound. + void SetDone(); + + private: + bool Done; + pthread_mutex_t Mutex; + pthread_cond_t Cond; + // Maximum size of the queue. The limitation on the queue size means that + // if the compilation happens slower than bytes arrive from the network, + // the queue will fill up, the RPC thread will be blocked most of the time, + // the RPC thread on the browser side will be waiting for the SRPC to return, + // and the buffer on the browser side will grow unboundedly until the + // whole bitcode file arrives (which is better than having the queue on + // the untrusted side consume all that memory). + // The partial-copying behavior of GetBytes and PutBytes prevents deadlock + // even if the requested number of bytes is greater than the size limit + // (although it will of course be less efficient). + // The initial size of the queue is expected to be smaller than this, but + // if not, it will simply never be resized. + const static size_t queuesize_limit_ = 256 * 1024; + + // Variables and functions to manage the circular queue + std::vector<unsigned char> Bytes; + size_t Prod; // Queue producer index + size_t Cons; // Queue consumer index + size_t queueSize() { + return Prod >= Cons ? Prod - Cons : Bytes.size() - (Cons - Prod); + } + size_t capacityRemaining() { + return (Prod >= Cons ? Bytes.size() - (Prod - Cons) : (Cons - Prod)) - 1; + } + void queueResize(); + void queuePut(unsigned char *buf, size_t len); + void queueGet(unsigned char *buf, size_t len); +}; + +// Class to manage the compliation thread and serve as the interface from +// the SRPC thread +class SRPCStreamer { +public: + SRPCStreamer() : Error(false) {} + // Initialize streamer, create a new thread running Callback, and + // return a pointer to the DataStreamer the threads will use to + // synchronize. On error, return NULL and fill in the ErrorMsg string + llvm::DataStreamer *init(void *(*Callback)(void *), + void *arg, std::string *ErrMsg); + // Called by the RPC thread. Copy len bytes from buf. Return bytes copied. + size_t gotChunk(unsigned char *bytes, size_t len); + // Called by the RPC thread. Wait for the compilation thread to finish. + int streamEnd(std::string *ErrMsg); + // Called by the compilation thread. Signal that there was a compilation + // error so the RPC thread can abort the stream. + void setError() { Error = true; } +private: + bool Error; + QueueStreamer Q; + pthread_t CompileThread; +}; + + + +#endif // SRPCSTREAMER_H diff --git a/tools/pnacl-llc/nacl_file.cpp b/tools/pnacl-llc/nacl_file.cpp new file mode 100644 index 0000000000..8ae0399476 --- /dev/null +++ b/tools/pnacl-llc/nacl_file.cpp @@ -0,0 +1,399 @@ +/* Copyright 2012 The Native Client Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can + * be found in the LICENSE file. + * + * This file provides wrappers to open() to use pre-opened file descriptors + * for the input bitcode and the output file. + * + * It also has the SRPC interfaces, but that should probably be refactored + * into a separate file. + */ + +#if defined(__native_client__) + +#include <argz.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +// Headers which are not properly part of the SDK are included by their +// path in the nacl tree +#include "native_client/src/shared/srpc/nacl_srpc.h" +#ifdef __pnacl__ +#include "native_client/src/untrusted/nacl/pnacl.h" +#endif +#include "SRPCStreamer.h" + +#include <string> +#include <map> +#include <vector> + +#include "llvm/ADT/OwningPtr.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/system_error.h" + + +using llvm::MemoryBuffer; +using llvm::StringRef; +using std::string; +using std::map; + +#define printerr(...) fprintf(stderr, __VA_ARGS__) +// printdbg is currently disabled to reduce spew. +#define printdbg(...) + +#define ARRAY_SIZE(array) (sizeof array / sizeof array[0]) + +namespace { + +typedef std::vector<std::string> string_vector; + +// The filename used internally for looking up the bitcode file. +char kBitcodeFilename[] = "pnacl.pexe"; +// The filename used internally for looking up the object code file. +char kObjectFilename[] = "pnacl.o"; +// Object which manages streaming bitcode over SRPC and threading. +SRPCStreamer *srpc_streamer; +// FD of the object file. +int object_file_fd; + +} // namespace + +//TODO(dschuff): a little more elegant interface into llc than this? +extern llvm::DataStreamer* NaClBitcodeStreamer; + +extern int llc_main(int argc, char **argv); + +int GetObjectFileFD() { + return object_file_fd; +} + +namespace { + +int DoTranslate(string_vector* cmd_line_vec, int object_fd) { + if (cmd_line_vec == NULL) { + return 1; + } + object_file_fd = object_fd; + // Make an argv array from the input vector. + size_t argc = cmd_line_vec->size(); + char** argv = new char*[argc]; + for (size_t i = 0; i < argc; ++i) { + // llc_main will not mutate the command line, so this is safe. + argv[i] = const_cast<char*>((*cmd_line_vec)[i].c_str()); + } + argv[argc] = NULL; + // Call main. + return llc_main(static_cast<int>(argc), argv); +} + +string_vector* CommandLineFromArgz(char* str, size_t str_len) { + char* entry = str; + string_vector* vec = new string_vector; + while (entry != NULL) { + vec->push_back(entry); + entry = argz_next(str, str_len, entry); + } + return vec; +} + +void AddFixedArguments(string_vector* vec) { + // Add fixed arguments to the command line. These specify the bitcode + // and object code filenames, removing them from the contract with the + // coordinator. + vec->push_back(kBitcodeFilename); + vec->push_back("-o"); + vec->push_back(kObjectFilename); +} + +bool AddDefaultCPU(string_vector* vec) { +#if defined (__pnacl__) + switch (__builtin_nacl_target_arch()) { + case PnaclTargetArchitectureX86_32: { + vec->push_back("-mcpu=pentium4"); + break; + } + case PnaclTargetArchitectureX86_64: { + vec->push_back("-mcpu=core2"); + break; + } + case PnaclTargetArchitectureARM_32: { + vec->push_back("-mcpu=cortex-a9"); + break; + } + default: + printerr("no target architecture match.\n"); + return false; + } +// Some cases for building this with nacl-gcc. +#elif defined (__i386__) + vec->push_back("-mcpu=pentium4"); +#elif defined (__x86_64__) + vec->push_back("-mcpu=core2"); +#elif defined (__arm__) + vec->push_back("-mcpu=cortex-a9"); +#error "Unknown architecture" +#endif + return true; +} + +bool HasCPUOverride(string_vector* vec) { + std::string mcpu = std::string("-mcpu="); + size_t len = mcpu.length(); + for (size_t i = 0; i < vec->size(); ++i) { + std::string prefix = (*vec)[i].substr(0, len); + if (prefix.compare(mcpu) == 0) + return true; + } + return false; +} + +string_vector* GetDefaultCommandLine() { + string_vector* command_line = new string_vector; + size_t i; + // First, those common to all architectures. + static const char* common_args[] = { "pnacl_translator", + "-filetype=obj" }; + for (i = 0; i < ARRAY_SIZE(common_args); ++i) { + command_line->push_back(common_args[i]); + } + // Then those particular to a platform. + static const char* llc_args_x8632[] = { "-mtriple=i686-none-nacl-gnu", + NULL }; + static const char* llc_args_x8664[] = { "-mtriple=x86_64-none-nacl-gnu", + NULL }; + static const char* llc_args_arm[] = { "-mtriple=armv7a-none-nacl-gnueabi", + "-arm-reserve-r9", + "-sfi-disable-cp", + "-sfi-store", + "-sfi-load", + "-sfi-stack", + "-sfi-branch", + "-sfi-data", + "-mattr=+neon", + "-no-inline-jumptables", + "-float-abi=hard", + NULL }; + + const char **llc_args = NULL; +#if defined (__pnacl__) + switch (__builtin_nacl_target_arch()) { + case PnaclTargetArchitectureX86_32: { + llc_args = llc_args_x8632; + break; + } + case PnaclTargetArchitectureX86_64: { + llc_args = llc_args_x8664; + break; + } + case PnaclTargetArchitectureARM_32: { + llc_args = llc_args_arm; + break; + } + default: + printerr("no target architecture match.\n"); + delete command_line; + return NULL; + } +// Some cases for building this with nacl-gcc. +#elif defined (__i386__) + llc_args = llc_args_x8632; +#elif defined (__x86_64__) + llc_args = llc_args_x8664; +#elif defined (__arm__) + llc_args = llc_args_arm; +#else +#error "Unknown architecture" +#endif + for (i = 0; llc_args[i] != NULL; i++) command_line->push_back(llc_args[i]); + return command_line; +} + +// Data passed from main thread to compile thread. +// Takes ownership of the commandline vector. +class StreamingThreadData { + public: + StreamingThreadData(int object_fd, string_vector* cmd_line_vec) : + object_fd_(object_fd), cmd_line_vec_(cmd_line_vec) {} + int ObjectFD() const { return object_fd_; } + string_vector* CmdLineVec() const { return cmd_line_vec_.get(); } + const int object_fd_; + const llvm::OwningPtr<string_vector> cmd_line_vec_; +}; + +void *run_streamed(void *arg) { + StreamingThreadData* data = reinterpret_cast<StreamingThreadData*>(arg); + data->CmdLineVec()->push_back("-streaming-bitcode"); + if (DoTranslate(data->CmdLineVec(), data->ObjectFD()) != 0) { + printerr("DoTranslate failed.\n"); + srpc_streamer->setError(); + return NULL; + } + delete data; + return NULL; +} + +// Actually do the work for stream initialization. +void do_stream_init(NaClSrpcRpc *rpc, + NaClSrpcArg **in_args, + NaClSrpcArg **out_args, + NaClSrpcClosure *done, + string_vector* command_line_vec) { + NaClSrpcClosureRunner runner(done); + rpc->result = NACL_SRPC_RESULT_APP_ERROR; + srpc_streamer = new SRPCStreamer(); + std::string StrError; + StreamingThreadData* thread_data = new StreamingThreadData( + in_args[0]->u.hval, command_line_vec); + NaClBitcodeStreamer = srpc_streamer->init(run_streamed, + reinterpret_cast<void *>(thread_data), + &StrError); + if (NaClBitcodeStreamer) { + rpc->result = NACL_SRPC_RESULT_OK; + out_args[0]->arrays.str = strdup("no error"); + } else { + out_args[0]->arrays.str = strdup(StrError.c_str()); + } +} + +// Invoked by the StreamInit RPC to initialize bitcode streaming over SRPC. +// Under the hood it forks a new thread at starts the llc_main, which sets +// up the compilation and blocks when it tries to start reading the bitcode. +// Input arg is a file descriptor to write the output object file to. +// Returns a string, containing an error message if the call fails. +void stream_init(NaClSrpcRpc *rpc, + NaClSrpcArg **in_args, + NaClSrpcArg **out_args, + NaClSrpcClosure *done) { + // cmd_line_vec allocated by GetDefaultCommandLine() is freed by the + // translation thread in run_streamed() + string_vector* cmd_line_vec = GetDefaultCommandLine(); + if (!cmd_line_vec || !AddDefaultCPU(cmd_line_vec)) { + NaClSrpcClosureRunner runner(done); + rpc->result = NACL_SRPC_RESULT_APP_ERROR; + out_args[0]->arrays.str = strdup("Failed to get default commandline."); + return; + } + AddFixedArguments(cmd_line_vec); + do_stream_init(rpc, in_args, out_args, done, cmd_line_vec); +} + +// Invoked by StreamInitWithCommandLine RPC. Same as stream_init, but +// provides a command line to use instead of the default. +void stream_init_with_command_line(NaClSrpcRpc *rpc, + NaClSrpcArg **in_args, + NaClSrpcArg **out_args, + NaClSrpcClosure *done) { + char* command_line = in_args[1]->arrays.carr; + size_t command_line_len = in_args[1]->u.count; + string_vector* cmd_line_vec = + CommandLineFromArgz(command_line, command_line_len); + AddFixedArguments(cmd_line_vec); + // cmd_line_vec is freed by the translation thread in run_streamed + do_stream_init(rpc, in_args, out_args, done, cmd_line_vec); +} + +// Invoked by StreamInitWithOverrides RPC. Same as stream_init, but +// provides commandline flag overrides (appended to the default). +void stream_init_with_overrides(NaClSrpcRpc *rpc, + NaClSrpcArg **in_args, + NaClSrpcArg **out_args, + NaClSrpcClosure *done) { + string_vector* cmd_line_vec = GetDefaultCommandLine(); + if (!cmd_line_vec) { + NaClSrpcClosureRunner runner(done); + rpc->result = NACL_SRPC_RESULT_APP_ERROR; + out_args[0]->arrays.str = strdup("Failed to get default commandline."); + return; + } + AddFixedArguments(cmd_line_vec); + + char* command_line = in_args[1]->arrays.carr; + size_t command_line_len = in_args[1]->u.count; + llvm::OwningPtr<string_vector> extra_vec( + CommandLineFromArgz(command_line, command_line_len)); + cmd_line_vec->insert(cmd_line_vec->end(), + extra_vec->begin(), extra_vec->end()); + // Make sure some -mcpu override exists for now to prevent + // auto-cpu feature detection from triggering instructions that + // we do not validate yet. + if (!HasCPUOverride(extra_vec.get())) { + AddDefaultCPU(cmd_line_vec); + } + extra_vec.reset(NULL); + // cmd_line_vec is freed by the translation thread in run_streamed. + do_stream_init(rpc, in_args, out_args, done, cmd_line_vec); +} + +// Invoked by the StreamChunk RPC. Receives a chunk of the bitcode and +// buffers it for later retrieval by the compilation thread. +void stream_chunk(NaClSrpcRpc *rpc, + NaClSrpcArg **in_args, + NaClSrpcArg **out_args, + NaClSrpcClosure *done) { + NaClSrpcClosureRunner runner(done); + rpc->result = NACL_SRPC_RESULT_APP_ERROR; + size_t len = in_args[0]->u.count; + unsigned char *bytes = reinterpret_cast<unsigned char*>( + in_args[0]->arrays.carr); + if (srpc_streamer->gotChunk(bytes, len) != len) { + return; + } + rpc->result = NACL_SRPC_RESULT_OK; +} + +// Invoked by the StreamEnd RPC. Waits until the compilation finishes, +// then returns. Returns an int indicating whether the bitcode is a +// shared library, a string with the soname, a string with dependencies, +// and a string which contains an error message if applicable. +void stream_end(NaClSrpcRpc *rpc, + NaClSrpcArg **in_args, + NaClSrpcArg **out_args, + NaClSrpcClosure *done) { + NaClSrpcClosureRunner runner(done); + rpc->result = NACL_SRPC_RESULT_APP_ERROR; + std::string StrError; + if (srpc_streamer->streamEnd(&StrError)) { + out_args[3]->arrays.str = strdup(StrError.c_str()); + return; + } + // TODO(eliben): We don't really use shared libraries now. At some + // point this should be cleaned up from SRPC as well. + out_args[0]->u.ival = false; + // SRPC deletes the strings returned when the closure is invoked. + // Therefore we need to use strdup. + out_args[1]->arrays.str = strdup(""); + out_args[2]->arrays.str = strdup(""); + rpc->result = NACL_SRPC_RESULT_OK; +} + +const struct NaClSrpcHandlerDesc srpc_methods[] = { + // Protocol for streaming: + // (StreamInit(obj_fd) -> error_str | + // StreamInitWIthCommandLine(obj_fd, escaped_cmdline) -> error_str) + // StreamChunk(data) + + // StreamEnd() -> (is_shared_lib,soname,dependencies,error_str) + { "StreamInit:h:s", stream_init }, + { "StreamInitWithCommandLine:hC:s:", stream_init_with_command_line }, + { "StreamInitWithOverrides:hC:s:", stream_init_with_overrides }, + { "StreamChunk:C:", stream_chunk }, + { "StreamEnd::isss", stream_end }, + { NULL, NULL }, +}; + +} // namespace + +int +main() { + if (!NaClSrpcModuleInit()) { + return 1; + } + + if (!NaClSrpcAcceptClientConnection(srpc_methods)) { + return 1; + } + NaClSrpcModuleFini(); + return 0; +} + +#endif diff --git a/tools/pnacl-llc/pnacl-llc.cpp b/tools/pnacl-llc/pnacl-llc.cpp new file mode 100644 index 0000000000..6292001ab1 --- /dev/null +++ b/tools/pnacl-llc/pnacl-llc.cpp @@ -0,0 +1,563 @@ +//===-- pnacl-llc.cpp - PNaCl-specific llc: pexe ---> nexe ---------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// pnacl-llc: the core of the PNaCl translator, compiling a pexe into a nexe. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/Triple.h" +#include "llvm/Analysis/NaCl.h" +#include "llvm/Assembly/PrintModulePass.h" +#include "llvm/Support/DataStream.h" +#include "llvm/Bitcode/NaCl/NaClReaderWriter.h" +#include "llvm/CodeGen/CommandFlags.h" +#include "llvm/CodeGen/LinkAllAsmWriterComponents.h" +#include "llvm/CodeGen/LinkAllCodegenComponents.h" +#include "llvm/IR/DataLayout.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Module.h" +#include "llvm/IRReader/IRReader.h" +#include "llvm/MC/SubtargetFeature.h" +#include "llvm/Pass.h" +#include "llvm/PassManager.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/FormattedStream.h" +#include "llvm/Support/Host.h" +#include "llvm/Support/IRReader.h" +#include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/PrettyStackTrace.h" +#include "llvm/Support/Signals.h" +#include "llvm/Support/TargetRegistry.h" +#include "llvm/Support/TargetSelect.h" +#include "llvm/Support/Timer.h" +#include "llvm/Support/ToolOutputFile.h" +#include "llvm/Target/TargetLibraryInfo.h" +#include "llvm/Target/TargetMachine.h" +#include "llvm/Transforms/NaCl.h" +#include <memory> + + +using namespace llvm; + +// NOTE: When __native_client__ is defined it means pnacl-llc is built as a +// sandboxed translator (from pnacl-llc.pexe to pnacl-llc.nexe). In this mode +// it uses SRPC operations instead of direct OS intefaces. +#if defined(__native_client__) +int GetObjectFileFD(); +DataStreamer* NaClBitcodeStreamer; +#endif + +const char *TimeIRParsingGroupName = "LLVM IR Parsing"; +const char *TimeIRParsingName = "Parse IR"; + +bool TimeIRParsingIsEnabled = false; +static cl::opt<bool,true> +EnableTimeIRParsing("time-ir-parsing", cl::location(TimeIRParsingIsEnabled), + cl::desc("Measure the time IR parsing takes")); + +cl::opt<NaClFileFormat> +InputFileFormat( + "bitcode-format", + cl::desc("Define format of input file:"), + cl::values( + clEnumValN(LLVMFormat, "llvm", "LLVM file (default)"), + clEnumValN(PNaClFormat, "pnacl", "PNaCl bitcode file"), + clEnumValEnd), + cl::init(LLVMFormat)); + +// 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<unsigned> +TimeCompilations("time-compilations", cl::Hidden, cl::init(1u), + cl::value_desc("N"), + cl::desc("Repeat compilation N times for timing")); + +// Using bitcode streaming has a couple of ramifications. Primarily it means +// that the module in the file will be compiled one function at a time rather +// than the whole module. This allows earlier functions to be compiled before +// later functions are read from the bitcode but of course means no whole-module +// optimizations. For now, streaming is only supported for files and stdin. +static cl::opt<bool> +LazyBitcode("streaming-bitcode", + cl::desc("Use lazy bitcode streaming for file inputs"), + cl::init(false)); + +// The option below overlaps very much with bitcode streaming. +// We keep it separate because it is still experimental and we want +// to use it without changing the outside behavior which is especially +// relevant for the sandboxed case. +static cl::opt<bool> +ReduceMemoryFootprint("reduce-memory-footprint", + cl::desc("Aggressively reduce memory used by llc"), + cl::init(false)); + +static cl::opt<bool> +PNaClABIVerify("pnaclabi-verify", + cl::desc("Verify PNaCl bitcode ABI before translating"), + cl::init(false)); +static cl::opt<bool> +PNaClABIVerifyFatalErrors("pnaclabi-verify-fatal-errors", + cl::desc("PNaCl ABI verification errors are fatal"), + cl::init(false)); + +// 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> +UserDefinedTriple("mtriple", cl::desc("Set target triple")); + +cl::opt<bool> NoVerify("disable-verify", cl::Hidden, + cl::desc("Do not verify input module")); + +cl::opt<bool> +DisableSimplifyLibCalls("disable-simplify-libcalls", + cl::desc("Disable simplify-libcalls"), + cl::init(false)); + +static int compileModule(char**, LLVMContext&); + +// 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') || + (IFN[Len-2] == 'l' && IFN[Len-1] == 'l'))) { + outputFilename = std::string(IFN.begin(), IFN.end()-3); // s/.bc/.s/ + } else { + outputFilename = IFN; + } + return outputFilename; +} + +static tool_output_file *GetOutputStream(const char *TargetName, + Triple::OSType OS, + const char *ProgName) { + // If we don't yet have an output filename, make one. + if (OutputFilename.empty()) { + if (InputFilename == "-") + OutputFilename = "-"; + else { + OutputFilename = GetFileNameRoot(InputFilename); + + switch (FileType) { + case TargetMachine::CGFT_AssemblyFile: + if (TargetName[0] == 'c') { + if (TargetName[1] == 0) + OutputFilename += ".cbe.c"; + else if (TargetName[1] == 'p' && TargetName[2] == 'p') + OutputFilename += ".cpp"; + else + OutputFilename += ".s"; + } else + OutputFilename += ".s"; + break; + case TargetMachine::CGFT_ObjectFile: + if (OS == Triple::Win32) + OutputFilename += ".obj"; + else + OutputFilename += ".o"; + break; + case TargetMachine::CGFT_Null: + OutputFilename += ".null"; + break; + } + } + } + + // Decide if we need "binary" output. + bool Binary = false; + switch (FileType) { + case TargetMachine::CGFT_AssemblyFile: + break; + case TargetMachine::CGFT_ObjectFile: + case TargetMachine::CGFT_Null: + Binary = true; + break; + } + + // Open the file. + std::string error; + unsigned OpenFlags = 0; + if (Binary) OpenFlags |= raw_fd_ostream::F_Binary; + OwningPtr<tool_output_file> FDOut( + new tool_output_file(OutputFilename.c_str(), error, OpenFlags)); + if (!error.empty()) { + errs() << error << '\n'; + return 0; + } + + return FDOut.take(); +} + +// main - Entry point for the llc compiler. +// +int llc_main(int argc, char **argv) { + sys::PrintStackTraceOnErrorSignal(); + PrettyStackTraceProgram X(argc, argv); + + // Enable debug stream buffering. + EnableDebugBuffering = true; + + LLVMContext &Context = getGlobalContext(); + llvm_shutdown_obj Y; // Call llvm_shutdown() on exit. + + // Initialize targets first, so that --version shows registered targets. + InitializeAllTargets(); + InitializeAllTargetMCs(); + InitializeAllAsmPrinters(); +#if !defined(__native_client__) + // Prune asm parsing from sandboxed translator. + // Do not prune "AsmPrinters" because that includes + // the direct object emission. + InitializeAllAsmParsers(); +#endif + + // Initialize codegen and IR passes used by llc so that the -print-after, + // -print-before, and -stop-after options work. + PassRegistry *Registry = PassRegistry::getPassRegistry(); + initializeCore(*Registry); + initializeCodeGen(*Registry); + initializeLoopStrengthReducePass(*Registry); + initializeLowerIntrinsicsPass(*Registry); + initializeUnreachableBlockElimPass(*Registry); + + // Register the target printer for --version. + cl::AddExtraVersionPrinter(TargetRegistry::printRegisteredTargetsForVersion); + + // Enable the PNaCl ABI verifier by default in sandboxed mode. +#if defined(__native_client__) + PNaClABIVerify = true; + PNaClABIVerifyFatalErrors = true; +#endif + + cl::ParseCommandLineOptions(argc, argv, "pnacl-llc\n"); + + // Compile the module TimeCompilations times to give better compile time + // metrics. + for (unsigned I = TimeCompilations; I; --I) + if (int RetVal = compileModule(argv, Context)) + return RetVal; + return 0; +} + +static void CheckABIVerifyErrors(PNaClABIErrorReporter &Reporter, + const Twine &Name) { + if (PNaClABIVerify && Reporter.getErrorCount() > 0) { + errs() << (PNaClABIVerifyFatalErrors ? "ERROR: " : "WARNING: "); + errs() << Name << " is not valid PNaCl bitcode:\n"; + Reporter.printErrors(errs()); + if (PNaClABIVerifyFatalErrors) + exit(1); + } + Reporter.reset(); +} + +static int compileModule(char **argv, LLVMContext &Context) { + // Load the module to be compiled... + SMDiagnostic Err; + std::auto_ptr<Module> M; + Module *mod = 0; + Triple TheTriple; + + PNaClABIErrorReporter ABIErrorReporter; + +#if defined(__native_client__) + if (LazyBitcode) { + std::string StrError; + M.reset(getNaClStreamedBitcodeModule( + std::string("<SRPC stream>"), + NaClBitcodeStreamer, Context, &StrError)); + if (!StrError.empty()) + Err = SMDiagnostic(InputFilename, SourceMgr::DK_Error, StrError); + } else { + llvm_unreachable("native client SRPC only supports streaming"); + } +#else + { + // TODO: after the next merge this can be removed. + // https://code.google.com/p/nativeclient/issues/detail?id=3349 + NamedRegionTimer T(TimeIRParsingName, TimeIRParsingGroupName, + TimeIRParsingIsEnabled); + M.reset(NaClParseIRFile(InputFilename, InputFileFormat, Err, Context)); + } +#endif + + mod = M.get(); + if (mod == 0) { + Err.print(argv[0], errs()); + return 1; + } + + if (PNaClABIVerify) { + // Verify the module (but not the functions yet) + ModulePass *VerifyPass = createPNaClABIVerifyModulePass(&ABIErrorReporter, + LazyBitcode); + VerifyPass->runOnModule(*mod); + CheckABIVerifyErrors(ABIErrorReporter, "Module"); + } + + // Add declarations for external functions required by PNaCl. The + // ResolvePNaClIntrinsics function pass running during streaming + // depends on these declarations being in the module. + OwningPtr<ModulePass> AddPNaClExternalDeclsPass( + createAddPNaClExternalDeclsPass()); + AddPNaClExternalDeclsPass->runOnModule(*mod); + + if (UserDefinedTriple.empty()) { + report_fatal_error("-mtriple must be set to a target triple for pnacl-llc"); + } else { + mod->setTargetTriple(Triple::normalize(UserDefinedTriple)); + TheTriple = Triple(mod->getTargetTriple()); + } + + // Get the target specific parser. + std::string Error; + const Target *TheTarget = TargetRegistry::lookupTarget(MArch, TheTriple, + Error); + if (!TheTarget) { + errs() << argv[0] << ": " << Error; + return 1; + } + + // Package up features to be passed to target/subtarget + std::string FeaturesStr; + if (MAttrs.size()) { + SubtargetFeatures Features; + for (unsigned i = 0; i != MAttrs.size(); ++i) + Features.AddFeature(MAttrs[i]); + FeaturesStr = Features.getString(); + } + + CodeGenOpt::Level OLvl = CodeGenOpt::Default; + switch (OptLevel) { + default: + errs() << argv[0] << ": invalid optimization level.\n"; + return 1; + case ' ': break; + case '0': OLvl = CodeGenOpt::None; break; + case '1': OLvl = CodeGenOpt::Less; break; + case '2': OLvl = CodeGenOpt::Default; break; + case '3': OLvl = CodeGenOpt::Aggressive; break; + } + + TargetOptions Options; + Options.LessPreciseFPMADOption = EnableFPMAD; + Options.NoFramePointerElim = DisableFPElim; + Options.NoFramePointerElimNonLeaf = DisableFPElimNonLeaf; + Options.AllowFPOpFusion = FuseFPOps; + Options.UnsafeFPMath = EnableUnsafeFPMath; + Options.NoInfsFPMath = EnableNoInfsFPMath; + Options.NoNaNsFPMath = EnableNoNaNsFPMath; + Options.HonorSignDependentRoundingFPMathOption = + EnableHonorSignDependentRoundingFPMath; + Options.UseSoftFloat = GenerateSoftFloatCalls; + if (FloatABIForCalls != FloatABI::Default) + Options.FloatABIType = FloatABIForCalls; + Options.NoZerosInBSS = DontPlaceZerosInBSS; + Options.GuaranteedTailCallOpt = EnableGuaranteedTailCallOpt; + Options.DisableTailCalls = DisableTailCalls; + Options.StackAlignmentOverride = OverrideStackAlignment; + Options.RealignStack = EnableRealignStack; + Options.TrapFuncName = TrapFuncName; + Options.PositionIndependentExecutable = EnablePIE; + Options.EnableSegmentedStacks = SegmentedStacks; + Options.UseInitArray = UseInitArray; + Options.SSPBufferSize = SSPBufferSize; + + std::auto_ptr<TargetMachine> + target(TheTarget->createTargetMachine(TheTriple.getTriple(), + MCPU, FeaturesStr, Options, + RelocModel, CMModel, OLvl)); + assert(target.get() && "Could not allocate target machine!"); + assert(mod && "Should have exited after outputting help!"); + TargetMachine &Target = *target.get(); + + if (DisableDotLoc) + Target.setMCUseLoc(false); + + if (DisableCFI) + Target.setMCUseCFI(false); + + if (EnableDwarfDirectory) + Target.setMCUseDwarfDirectory(true); + + if (GenerateSoftFloatCalls) + FloatABIForCalls = FloatABI::Soft; + + // Disable .loc support for older OS X versions. + if (TheTriple.isMacOSX() && + TheTriple.isMacOSXVersionLT(10, 6)) + Target.setMCUseLoc(false); + +#if !defined(__native_client__) + // Figure out where we are going to send the output. + OwningPtr<tool_output_file> Out + (GetOutputStream(TheTarget->getName(), TheTriple.getOS(), argv[0])); + if (!Out) return 1; +#endif + + // Build up all of the passes that we want to do to the module. + OwningPtr<PassManagerBase> PM; + if (LazyBitcode || ReduceMemoryFootprint) + PM.reset(new FunctionPassManager(mod)); + else + PM.reset(new PassManager()); + + // Add the ABI verifier pass before the analysis and code emission passes. + FunctionPass *FunctionVerifyPass = NULL; + if (PNaClABIVerify) { + FunctionVerifyPass = createPNaClABIVerifyFunctionsPass(&ABIErrorReporter); + PM->add(FunctionVerifyPass); + } + + // Add the intrinsic resolution pass. It assumes ABI-conformant code. + PM->add(createResolvePNaClIntrinsicsPass()); + + // Add an appropriate TargetLibraryInfo pass for the module's triple. + TargetLibraryInfo *TLI = new TargetLibraryInfo(TheTriple); + if (DisableSimplifyLibCalls) + TLI->disableAllFunctions(); + PM->add(TLI); + + // Add intenal analysis passes from the target machine. + Target.addAnalysisPasses(*PM.get()); + + // Add the target data from the target machine, if it exists, or the module. + if (const DataLayout *TD = Target.getDataLayout()) + PM->add(new DataLayout(*TD)); + else + PM->add(new DataLayout(mod)); + + // Override default to generate verbose assembly. + Target.setAsmVerbosityDefault(true); + + if (RelaxAll) { + if (FileType != TargetMachine::CGFT_ObjectFile) + errs() << argv[0] + << ": warning: ignoring -mc-relax-all because filetype != obj"; + else + Target.setMCRelaxAll(true); + } + +#if defined __native_client__ + { + raw_fd_ostream ROS(GetObjectFileFD(), true); + ROS.SetBufferSize(1 << 20); + formatted_raw_ostream FOS(ROS); + + // Ask the target to add backend passes as necessary. + if (Target.addPassesToEmitFile(*PM, FOS, FileType, NoVerify)) { + errs() << argv[0] << ": target does not support generation of this" + << " file type!\n"; + return 1; + } + + if (LazyBitcode || ReduceMemoryFootprint) { + FunctionPassManager* P = static_cast<FunctionPassManager*>(PM.get()); + P->doInitialization(); + for (Module::iterator I = mod->begin(), E = mod->end(); I != E; ++I) { + P->run(*I); + CheckABIVerifyErrors(ABIErrorReporter, "Function " + I->getName()); + if (ReduceMemoryFootprint) { + I->Dematerialize(); + } + } + P->doFinalization(); + } else { + static_cast<PassManager*>(PM.get())->run(*mod); + } + FOS.flush(); + ROS.flush(); + } +#else + + { + formatted_raw_ostream FOS(Out->os()); + + AnalysisID StartAfterID = 0; + AnalysisID StopAfterID = 0; + const PassRegistry *PR = PassRegistry::getPassRegistry(); + if (!StartAfter.empty()) { + const PassInfo *PI = PR->getPassInfo(StartAfter); + if (!PI) { + errs() << argv[0] << ": start-after pass is not registered.\n"; + return 1; + } + StartAfterID = PI->getTypeInfo(); + } + if (!StopAfter.empty()) { + const PassInfo *PI = PR->getPassInfo(StopAfter); + if (!PI) { + errs() << argv[0] << ": stop-after pass is not registered.\n"; + return 1; + } + StopAfterID = PI->getTypeInfo(); + } + + // Ask the target to add backend passes as necessary. + if (Target.addPassesToEmitFile(*PM, FOS, FileType, NoVerify, + StartAfterID, StopAfterID)) { + errs() << argv[0] << ": target does not support generation of this" + << " file type!\n"; + return 1; + } + + // Before executing passes, print the final values of the LLVM options. + cl::PrintOptionValues(); + + if (LazyBitcode || ReduceMemoryFootprint) { + FunctionPassManager *P = static_cast<FunctionPassManager*>(PM.get()); + P->doInitialization(); + for (Module::iterator I = mod->begin(), E = mod->end(); I != E; ++I) { + P->run(*I); + CheckABIVerifyErrors(ABIErrorReporter, "Function " + I->getName()); + if (ReduceMemoryFootprint) { + I->Dematerialize(); + } + } + P->doFinalization(); + } else { + static_cast<PassManager*>(PM.get())->run(*mod); + } + } + + // Declare success. + Out->keep(); +#endif + + return 0; +} + +#if !defined(__native_client__) +int +main (int argc, char **argv) { + return llc_main(argc, argv); +} +#else +// main() is in nacl_file.cpp. +#endif diff --git a/tools/pnacl-thaw/CMakeLists.txt b/tools/pnacl-thaw/CMakeLists.txt new file mode 100644 index 0000000000..91b818efe6 --- /dev/null +++ b/tools/pnacl-thaw/CMakeLists.txt @@ -0,0 +1,5 @@ +set(LLVM_LINK_COMPONENTS bitwriter naclbitreader) + +add_llvm_tool(pnacl-thaw + pnacl-thaw.cpp + ) diff --git a/tools/pnacl-thaw/LLVMBuild.txt b/tools/pnacl-thaw/LLVMBuild.txt new file mode 100644 index 0000000000..864da2cbd5 --- /dev/null +++ b/tools/pnacl-thaw/LLVMBuild.txt @@ -0,0 +1,16 @@ +;===- ./tools/pnacl-thaw/LLVMBuild.txt -----------------------*- Conf -*--===; +;===------------------------------------------------------------------------===; +; +; This is an LLVMBuild description file for the components in this subdirectory. +; +; For more information on the LLVMBuild system, please see: +; +; http://llvm.org/docs/LLVMBuild.html +; +;===------------------------------------------------------------------------===; + +[component_0] +type = Tool +name = pnacl-thaw +parent = Tools +required_libraries = BitWriter NaClBitReader diff --git a/tools/pnacl-thaw/Makefile b/tools/pnacl-thaw/Makefile new file mode 100644 index 0000000000..8e7699e185 --- /dev/null +++ b/tools/pnacl-thaw/Makefile @@ -0,0 +1,17 @@ +##===- tools/pnacl-thaw/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 := pnacl-thaw +LINK_COMPONENTS := bitwriter naclbitreader + +# This tool has no plugins, optimize startup time. +TOOL_NO_EXPORTS := 1 + +include $(LEVEL)/Makefile.common diff --git a/tools/pnacl-thaw/pnacl-thaw.cpp b/tools/pnacl-thaw/pnacl-thaw.cpp new file mode 100644 index 0000000000..7f27bd58fc --- /dev/null +++ b/tools/pnacl-thaw/pnacl-thaw.cpp @@ -0,0 +1,96 @@ +/* Copyright 2013 The Native Client Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can + * be found in the LICENSE file. + */ + +//===-- pnacl-thaw.cpp - The low-level NaCl bitcode thawer ----------------===// +// +//===----------------------------------------------------------------------===// +// +// Converts NaCl wire format back to LLVM bitcode. +// +//===----------------------------------------------------------------------===// + +#include "llvm/IR/LLVMContext.h" +// Note: We need the following to provide the API for calling the NaCl +// Bitcode Reader to read the frozen file. +#include "llvm/Bitcode/NaCl/NaClReaderWriter.h" +// Note: We need the following to provide the API for calling the (LLVM) +// Bitcode Writer to generate the corresponding LLVM bitcode file. +#include "llvm/Bitcode/ReaderWriter.h" +#include "llvm/IR/Module.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/DataStream.h" +#include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/PrettyStackTrace.h" +#include "llvm/Support/Signals.h" +#include "llvm/Support/ToolOutputFile.h" + +using namespace llvm; + +static cl::opt<std::string> +OutputFilename("o", cl::desc("Specify thawed pexe filename"), + cl::value_desc("filename"), cl::init("-")); + +static cl::opt<std::string> +InputFilename(cl::Positional, cl::desc("<frozen file>"), cl::init("-")); + +static void WriteOutputFile(const Module *M) { + + std::string ErrorInfo; + OwningPtr<tool_output_file> Out + (new tool_output_file(OutputFilename.c_str(), ErrorInfo, + raw_fd_ostream::F_Binary)); + if (!ErrorInfo.empty()) { + errs() << ErrorInfo << '\n'; + exit(1); + } + + WriteBitcodeToFile(M, Out->os()); + + // Declare success. + Out->keep(); +} + +int main(int argc, char **argv) { + // Print a stack trace if we signal out. + sys::PrintStackTraceOnErrorSignal(); + PrettyStackTraceProgram X(argc, argv); + + LLVMContext &Context = getGlobalContext(); + llvm_shutdown_obj Y; // Call llvm_shutdown() on exit. + + cl::ParseCommandLineOptions( + argc, argv, "Converts NaCl pexe wire format into LLVM bitcode format\n"); + + std::string ErrorMessage; + std::auto_ptr<Module> M; + + // Use the bitcode streaming interface + DataStreamer *streamer = getDataFileStreamer(InputFilename, &ErrorMessage); + if (streamer) { + std::string DisplayFilename; + if (InputFilename == "-") + DisplayFilename = "<stdin>"; + else + DisplayFilename = InputFilename; + M.reset(getNaClStreamedBitcodeModule( + DisplayFilename, streamer, Context, + &ErrorMessage, /*AcceptSupportedOnly=*/false)); + if(M.get() != 0 && M->MaterializeAllPermanently(&ErrorMessage)) { + M.reset(); + } + } + + if (M.get() == 0) { + errs() << argv[0] << ": "; + if (ErrorMessage.size()) + errs() << ErrorMessage << "\n"; + else + errs() << "bitcode didn't read correctly.\n"; + return 1; + } + + WriteOutputFile(M.get()); + return 0; +} diff --git a/tools/pso-stub/CMakeLists.txt b/tools/pso-stub/CMakeLists.txt new file mode 100644 index 0000000000..4b2f779cb0 --- /dev/null +++ b/tools/pso-stub/CMakeLists.txt @@ -0,0 +1,5 @@ +set(LLVM_LINK_COMPONENTS bitreader bitwriter object support analysis) + +add_llvm_tool(pso-stub + pso-stub.cpp + ) diff --git a/tools/pso-stub/LLVMBuild.txt b/tools/pso-stub/LLVMBuild.txt new file mode 100644 index 0000000000..e643053dbf --- /dev/null +++ b/tools/pso-stub/LLVMBuild.txt @@ -0,0 +1,22 @@ +;===- ./tools/pso-stub/LLVMBuild.txt ---------------------------*- Conf -*--===; +; +; The LLVM Compiler Infrastructure +; +; This file is distributed under the University of Illinois Open Source +; License. See LICENSE.TXT for details. +; +;===------------------------------------------------------------------------===; +; +; This is an LLVMBuild description file for the components in this subdirectory. +; +; For more information on the LLVMBuild system, please see: +; +; http://llvm.org/docs/LLVMBuild.html +; +;===------------------------------------------------------------------------===; + +[component_0] +type = Tool +name = pso-stub +parent = Tools +required_libraries = BitReader BitWriter Object Support Analysis diff --git a/tools/pso-stub/Makefile b/tools/pso-stub/Makefile new file mode 100644 index 0000000000..c2860e65f6 --- /dev/null +++ b/tools/pso-stub/Makefile @@ -0,0 +1,18 @@ +##===- tools/pso-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 := pso-stub +LINK_COMPONENTS := bitreader bitwriter object support analysis + +# This tool has no plugins, optimize startup time. +TOOL_NO_EXPORTS := 1 + +include $(LEVEL)/Makefile.common + diff --git a/tools/pso-stub/pso-stub.cpp b/tools/pso-stub/pso-stub.cpp new file mode 100644 index 0000000000..26ce0c5056 --- /dev/null +++ b/tools/pso-stub/pso-stub.cpp @@ -0,0 +1,309 @@ +/*===- pso-stub.c - Create bitcode shared object stubs -------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Create a bitcode stub for a native shared object. +// Usage: pso-stub <input.so> -o <output.pso> +// +// The stub bitcode file contains the same dynamic symbols as the input shared +// object, with identical attributes (e.g. weak, undefined, TLS). +// +// Undefined functions become declarations in the bitcode. +// Undefined variables become external variable declarations in the bitcode. +// Defined functions become trivial stub functions in the bitcode (which do +// nothing but "ret void"). +// Defined object/tls symbols became dummy variable definitions (int foo = 0). +// +// The generated bitcode is suitable for linking against (as a shared object), +// but nothing else. +// +// TODO(pdox): Implement GNU symbol versioning. +// TODO(pdox): Mark IFUNC symbols as functions, and store +// this attribute as metadata. +//===----------------------------------------------------------------------===*/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/GlobalValue.h" +#include "llvm/IR/Type.h" +#include "llvm/IR/DerivedTypes.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/Constant.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/Instructions.h" +#include "llvm/Object/ObjectFile.h" +#include "llvm/Object/ELF.h" +#include "llvm/Analysis/Verifier.h" +#include "llvm/Bitcode/ReaderWriter.h" +#include "llvm/Support/ToolOutputFile.h" +#include "llvm/Support/ELF.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/PrettyStackTrace.h" +#include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/Signals.h" +#include "llvm/Support/FormattedStream.h" +#include "llvm/ADT/APInt.h" + +using namespace llvm; +using namespace llvm::object; + +namespace { + +cl::opt<std::string> +InputFilename(cl::Positional, cl::desc("<input native shared object>"), + cl::init("")); + +cl::opt<std::string> +OutputFilename("o", cl::desc("Output filename"), cl::value_desc("filename")); + +// Variables / declarations to place in llvm.used array. +std::vector<GlobalValue*> LLVMUsed; + +void AddUsedGlobal(GlobalValue *GV) { + // Clang normally asserts that these are not decls. We do need + // decls to survive though, and those are really the ones we + // worry about, so only add those. + // We run verifyModule() below, so that we know this is somewhat valid. + if (GV->isDeclaration()) { + LLVMUsed.push_back(GV); + } +} + +// Emit llvm.used array. +// This is almost exactly like clang/lib/CodeGen/CodeGenModule.cpp::EmitLLVMUsed +void EmitLLVMUsed(Module *M) { + // Don't create llvm.used if there is no need. + if (LLVMUsed.empty()) + return; + + Type *Int8PtrTy = Type::getInt8PtrTy(M->getContext()); + // Convert LLVMUsed to what ConstantArray needs. + SmallVector<llvm::Constant*, 8> UsedArray; + UsedArray.resize(LLVMUsed.size()); + for (unsigned i = 0, e = LLVMUsed.size(); i != e; ++i) { + UsedArray[i] = + llvm::ConstantExpr::getBitCast(cast<llvm::Constant>(&*LLVMUsed[i]), + Int8PtrTy); + } + + if (UsedArray.empty()) + return; + llvm::ArrayType *ATy = llvm::ArrayType::get(Int8PtrTy, UsedArray.size()); + + llvm::GlobalVariable *GV = + new llvm::GlobalVariable(*M, ATy, false, + llvm::GlobalValue::AppendingLinkage, + llvm::ConstantArray::get(ATy, UsedArray), + "llvm.used"); + + GV->setSection("llvm.metadata"); +} + +// Add a stub function definition or declaration +void +AddFunction(Module *M, + GlobalValue::LinkageTypes Linkage, + const StringRef &Name, + bool isDefine) { + // Create an empty function with no arguments. + // void Name(void); + Type *RetTy = Type::getVoidTy(M->getContext()); + FunctionType *FT = FunctionType::get(RetTy, /*isVarArg=*/ false); + Function *F = Function::Create(FT, Linkage, Name, M); + if (isDefine) { + // Add a single basic block with "ret void" + BasicBlock *BB = BasicBlock::Create(F->getContext(), "", F); + BB->getInstList().push_back(ReturnInst::Create(F->getContext())); + } + AddUsedGlobal(F); +} + +// Add a stub global variable declaration or definition. +void +AddGlobalVariable(Module *M, + GlobalValue::LinkageTypes Linkage, + const StringRef &Name, + bool isTLS, + bool isDefine) { + // Use 'int' as the dummy type. + Type *Ty = Type::getInt32Ty(M->getContext()); + + Constant *InitVal = NULL; + if (isDefine) { + // Define to dummy value, 0. + InitVal = Constant::getIntegerValue(Ty, APInt(32, 0)); + } + GlobalVariable *GV = + new GlobalVariable(*M, Ty, /*isConstant=*/ false, + Linkage, /*Initializer=*/ InitVal, + Twine(Name), /*InsertBefore=*/ NULL, + isTLS ? GlobalVariable::GeneralDynamicTLSModel : + GlobalVariable::NotThreadLocal, + /*AddressSpace=*/ 0); + AddUsedGlobal(GV); +} + +// Iterate through the ObjectFile's needed libraries, and +// add them to the module. +void TransferLibrariesNeeded(Module *M, const ObjectFile *obj) { + library_iterator it = obj->begin_libraries_needed(); + library_iterator ie = obj->end_libraries_needed(); + error_code ec; + for (; it != ie; it.increment(ec)) { + StringRef path; + it->getPath(path); + outs() << "Adding library " << path << "\n"; + M->addLibrary(path); + } +} + +// Set the Module's SONAME from the ObjectFile +void TransferLibraryName(Module *M, const ObjectFile *obj) { + StringRef soname = obj->getLoadName(); + outs() << "Setting soname to: " << soname << "\n"; + M->setSOName(soname); +} + +// Create stubs in the module for the dynamic symbols +void TransferDynamicSymbols(Module *M, const ObjectFile *obj) { + // Iterate through the dynamic symbols in the ObjectFile. + symbol_iterator it = obj->begin_dynamic_symbols(); + symbol_iterator ie = obj->end_dynamic_symbols(); + error_code ec; + for (; it != ie; it.increment(ec)) { + const SymbolRef &sym = *it; + StringRef Name; + SymbolRef::Type Type; + uint32_t Flags; + + sym.getName(Name); + sym.getType(Type); + sym.getFlags(Flags); + + // Ignore debug info and section labels + if (Flags & SymbolRef::SF_FormatSpecific) + continue; + + // Ignore local symbols + if (!(Flags & SymbolRef::SF_Global)) + continue; + outs() << "Transferring symbol " << Name << "\n"; + + bool isFunc = (Type == SymbolRef::ST_Function); + bool isUndef = (Flags & SymbolRef::SF_Undefined); + bool isTLS = (Flags & SymbolRef::SF_ThreadLocal); + bool isCommon = (Flags & SymbolRef::SF_Common); + bool isWeak = (Flags & SymbolRef::SF_Weak); + + if (Type == SymbolRef::ST_Unknown) { + // Weak symbols can be "v" according to NM, which are definitely + // data, but they may also be "w", which are of unknown type. + // Thus there is already a mechanism to say "weak object", but not + // for weak function. Assume unknown weak symbols are functions. + if (isWeak) { + outs() << "Warning: Symbol '" << Name << + "' has unknown type (weak). Assuming function.\n"; + Type = SymbolRef::ST_Function; + isFunc = true; + } else { + // If it is undef, we likely don't care, since it won't be used + // to bind to unresolved symbols in the real pexe and real pso. + // Other cases seen where it is not undef: _end, __bss_start, + // which are markers provided by the linker scripts. + outs() << "Warning: Symbol '" << Name << + "' has unknown type (isUndef=" << isUndef << "). Assuming data.\n"; + Type = SymbolRef::ST_Data; + isFunc = false; + } + } + + // Determine Linkage type. + GlobalValue::LinkageTypes Linkage; + if (isWeak) + Linkage = isUndef ? GlobalValue::ExternalWeakLinkage : + GlobalValue::WeakAnyLinkage; + else if (isCommon) + Linkage = GlobalValue::CommonLinkage; + else + Linkage = GlobalValue::ExternalLinkage; + + if (isFunc) + AddFunction(M, Linkage, Name, !isUndef); + else + AddGlobalVariable(M, Linkage, Name, isTLS, !isUndef); + } +} + +} // namespace + + +int main(int argc, const char** argv) { + sys::PrintStackTraceOnErrorSignal(); + PrettyStackTraceProgram X(argc, argv); + LLVMContext &Context = getGlobalContext(); + llvm_shutdown_obj Y; // Call llvm_shutdown() on exit. + + cl::ParseCommandLineOptions(argc, argv, + "Portable Shared Object Stub Maker\n"); + + if (InputFilename.empty()) { + errs() << "Please specify an input filename\n"; + return 1; + } + if (OutputFilename.empty()) { + errs() << "Please specify an output filename with -o\n"; + return 1; + } + + // Open the object file + OwningPtr<MemoryBuffer> File; + if (MemoryBuffer::getFile(InputFilename, File)) { + errs() << InputFilename << ": Open failed\n"; + return 1; + } + + ObjectFile *obj = ObjectFile::createObjectFile(File.take()); + if (!obj) { + errs() << InputFilename << ": Object type not recognized\n"; + } + + // Create the new module + OwningPtr<Module> M(new Module(InputFilename, Context)); + + // Transfer the relevant ELF information + M->setOutputFormat(Module::SharedOutputFormat); + TransferLibrariesNeeded(M.get(), obj); + TransferLibraryName(M.get(), obj); + TransferDynamicSymbols(M.get(), obj); + EmitLLVMUsed(M.get()); + + // Verify the module + std::string Err; + if (verifyModule(*M.get(), ReturnStatusAction, &Err)) { + errs() << "Module created is invalid:\n"; + errs() << Err; + return 1; + } + + // Write the module to a file + std::string ErrorInfo; + OwningPtr<tool_output_file> Out( + new tool_output_file(OutputFilename.c_str(), ErrorInfo, + raw_fd_ostream::F_Binary)); + if (!ErrorInfo.empty()) { + errs() << ErrorInfo << '\n'; + return 1; + } + WriteBitcodeToFile(M.get(), Out->os()); + Out->keep(); + return 0; +} |