diff options
author | Eli Bendersky <eliben@chromium.org> | 2013-05-15 11:13:43 -0700 |
---|---|---|
committer | Eli Bendersky <eliben@chromium.org> | 2013-05-15 11:13:43 -0700 |
commit | 28aeda400274e40253573e51cba1b66ca9099270 (patch) | |
tree | 575ad6c52484c2d181edd590f0bac00b367aa13f /tools | |
parent | c5d198304d851d6930cfe0cf3c719624109c4c95 (diff) |
Separate the PNaCl llc into a tool named pnacl-llc (how original!)
BUG=None
R=jvoung@chromium.org
Review URL: https://codereview.chromium.org/14604011
Diffstat (limited to 'tools')
-rw-r--r-- | tools/CMakeLists.txt | 1 | ||||
-rw-r--r-- | tools/LLVMBuild.txt | 2 | ||||
-rw-r--r-- | tools/Makefile | 2 | ||||
-rw-r--r-- | tools/pnacl-llc/CMakeLists.txt | 8 | ||||
-rw-r--r-- | tools/pnacl-llc/LLVMBuild.txt | 22 | ||||
-rw-r--r-- | tools/pnacl-llc/Makefile | 15 | ||||
-rw-r--r-- | tools/pnacl-llc/SRPCStreamer.cpp | 142 | ||||
-rw-r--r-- | tools/pnacl-llc/SRPCStreamer.h | 117 | ||||
-rw-r--r-- | tools/pnacl-llc/nacl_file.cpp | 422 | ||||
-rw-r--r-- | tools/pnacl-llc/pnacl-llc.cpp | 585 |
10 files changed, 1314 insertions, 2 deletions
diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index e149a14831..0be9602fda 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) diff --git a/tools/LLVMBuild.txt b/tools/LLVMBuild.txt index c301ea9371..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 pnacl-abicheck pnacl-bcanalyzer pnacl-freeze pnacl-thaw +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 1940d7606c..cbf94e743e 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -28,7 +28,7 @@ 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 \ diff --git a/tools/pnacl-llc/CMakeLists.txt b/tools/pnacl-llc/CMakeLists.txt new file mode 100644 index 0000000000..7e5cc10eb1 --- /dev/null +++ b/tools/pnacl-llc/CMakeLists.txt @@ -0,0 +1,8 @@ +set(LLVM_LINK_COMPONENTS ${LLVM_TARGETS_TO_BUILD} bitreader 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..934bed061a --- /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 all-targets NaClAnalysis diff --git a/tools/pnacl-llc/Makefile b/tools/pnacl-llc/Makefile new file mode 100644 index 0000000000..ae8104eac5 --- /dev/null +++ b/tools/pnacl-llc/Makefile @@ -0,0 +1,15 @@ +#===- 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 asmparser naclanalysis + +include $(LEVEL)/Makefile.common + diff --git a/tools/pnacl-llc/SRPCStreamer.cpp b/tools/pnacl-llc/SRPCStreamer.cpp new file mode 100644 index 0000000000..ae70a24822 --- /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__) && defined(NACL_SRPC) +#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..b7ecb407cd --- /dev/null +++ b/tools/pnacl-llc/nacl_file.cpp @@ -0,0 +1,422 @@ +/* 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__) && defined(NACL_SRPC) + +#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 <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; + +// True if the bitcode to be compiled is for a shared library. +// Used to return to the coordinator. +bool g_bitcode_is_shared_library; +// The soname of the current compilation unit, if it is a shared library. +// Empty string otherwise. +std::string* g_bitcode_soname = NULL; +// The newline separated list of libraries that the current bitcode compilation +// unit depends on. +std::string* g_bitcode_lib_dependencies = NULL; +// 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; +} + +void NaClRecordObjectInformation(bool is_shared, const std::string& soname) { + // This function is invoked to begin recording library information. + // To make it reentrant, we clean up what might be left over from last time. + delete g_bitcode_soname; + delete g_bitcode_lib_dependencies; + // Then remember the module global information. + g_bitcode_is_shared_library = is_shared; + g_bitcode_soname = new std::string(soname); + g_bitcode_lib_dependencies = new std::string(); +} + +void NaClRecordSharedLibraryDependency(const std::string& library_name) { + const std::string& kDelimiterString("\n"); + *g_bitcode_lib_dependencies += (library_name + kDelimiterString); +} + +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; + } + out_args[0]->u.ival = g_bitcode_is_shared_library; + // SRPC deletes the strings returned when the closure is invoked. + // Therefore we need to use strdup. + out_args[1]->arrays.str = strdup(g_bitcode_soname->c_str()); + out_args[2]->arrays.str = strdup(g_bitcode_lib_dependencies->c_str()); + 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..437dff7c0b --- /dev/null +++ b/tools/pnacl-llc/pnacl-llc.cpp @@ -0,0 +1,585 @@ +//===-- 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/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/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 <memory> + + +using namespace llvm; + +// @LOCALMOD-BEGIN +// NOTE: this tool can be build as a "sandboxed" translator. +// There are two ways to build the translator +// SRPC-style: no file operations are allowed +// see nacl_file.cc for support code +// non-SRPC-style: some basic file operations are allowed +// This can be useful for debugging but will +// not be deployed. +#if defined(__native_client__) && defined(NACL_SRPC) +int GetObjectFileFD(); +// The following two functions communicate metadata to the SRPC wrapper for LLC. +void NaClRecordObjectInformation(bool is_shared, const std::string& soname); +void NaClRecordSharedLibraryDependency(const std::string& library_name); +DataStreamer* NaClBitcodeStreamer; +#endif +// @LOCALMOD-END + +// @LOCALMOD-BEGIN +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")); +// @LOCALMOD-END + +// 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)); + |