From 021e48326db304559a74f2f338d511f9674d0da7 Mon Sep 17 00:00:00 2001 From: Murarth Date: Tue, 19 May 2015 16:00:59 -0700 Subject: [PATCH] Changes to LLVM `ExecutionEngine` wrapper * Removes `RustJITMemoryManager` from public API. This was really sort of an implementation detail to begin with. * `__morestack` is linked to C++ wrapper code and this pointer is used when resolving the symbol for `ExecutionEngine` code. * `__morestack_addr` is also resolved for `ExecutionEngine` code. This function is sometimes referenced in LLVM-generated code, but was not able to be resolved on Mac OS systems. * Added Windows support to `ExecutionEngine` API. * Added a test for basic `ExecutionEngine` functionality. --- src/librustc_llvm/lib.rs | 8 +- src/rustllvm/ExecutionEngineWrapper.cpp | 50 ++-- src/rustllvm/rustllvm.h | 8 - src/test/run-make/execution-engine/Makefile | 8 + src/test/run-make/execution-engine/test.rs | 249 ++++++++++++++++++++ 5 files changed, 293 insertions(+), 30 deletions(-) create mode 100644 src/test/run-make/execution-engine/Makefile create mode 100644 src/test/run-make/execution-engine/test.rs diff --git a/src/librustc_llvm/lib.rs b/src/librustc_llvm/lib.rs index 4ba4399ae2d03..a9b3c4a093a96 100644 --- a/src/librustc_llvm/lib.rs +++ b/src/librustc_llvm/lib.rs @@ -468,9 +468,6 @@ pub type BuilderRef = *mut Builder_opaque; pub enum ExecutionEngine_opaque {} pub type ExecutionEngineRef = *mut ExecutionEngine_opaque; #[allow(missing_copy_implementations)] -pub enum RustJITMemoryManager_opaque {} -pub type RustJITMemoryManagerRef = *mut RustJITMemoryManager_opaque; -#[allow(missing_copy_implementations)] pub enum MemoryBuffer_opaque {} pub type MemoryBufferRef = *mut MemoryBuffer_opaque; #[allow(missing_copy_implementations)] @@ -1080,10 +1077,7 @@ extern { pub fn LLVMDisposeBuilder(Builder: BuilderRef); /* Execution engine */ - pub fn LLVMRustCreateJITMemoryManager(morestack: *const ()) - -> RustJITMemoryManagerRef; - pub fn LLVMBuildExecutionEngine(Mod: ModuleRef, - MM: RustJITMemoryManagerRef) -> ExecutionEngineRef; + pub fn LLVMBuildExecutionEngine(Mod: ModuleRef) -> ExecutionEngineRef; pub fn LLVMDisposeExecutionEngine(EE: ExecutionEngineRef); pub fn LLVMExecutionEngineFinalizeObject(EE: ExecutionEngineRef); pub fn LLVMRustLoadDynamicLibrary(path: *const c_char) -> Bool; diff --git a/src/rustllvm/ExecutionEngineWrapper.cpp b/src/rustllvm/ExecutionEngineWrapper.cpp index 7e0630fd242d3..e37ede82bb560 100644 --- a/src/rustllvm/ExecutionEngineWrapper.cpp +++ b/src/rustllvm/ExecutionEngineWrapper.cpp @@ -16,22 +16,31 @@ using namespace llvm; using namespace llvm::sys; using namespace llvm::object; +// libmorestack is not used on Windows +#ifndef _WIN32 +extern "C" void __morestack(void); + +static void* morestack_addr() { + return reinterpret_cast(__morestack); +} +#endif + class RustJITMemoryManager : public SectionMemoryManager { typedef SectionMemoryManager Base; - const void *morestack; - public: - RustJITMemoryManager(const void *morestack_ptr) - : morestack(morestack_ptr) - {} + RustJITMemoryManager() {} uint64_t getSymbolAddress(const std::string &Name) override { +#ifndef _WIN32 if (Name == "__morestack" || Name == "___morestack") - return reinterpret_cast(morestack); + return reinterpret_cast(__morestack); + if (Name == "__morestack_addr" || Name == "___morestack_addr") + return reinterpret_cast(morestack_addr); +#endif return Base::getSymbolAddress(Name); } @@ -39,11 +48,6 @@ class RustJITMemoryManager : public SectionMemoryManager DEFINE_SIMPLE_CONVERSION_FUNCTIONS(RustJITMemoryManager, LLVMRustJITMemoryManagerRef) -extern "C" LLVMRustJITMemoryManagerRef LLVMRustCreateJITMemoryManager(void *morestack) -{ - return wrap(new RustJITMemoryManager(morestack)); -} - extern "C" LLVMBool LLVMRustLoadDynamicLibrary(const char *path) { std::string err; @@ -60,6 +64,13 @@ extern "C" LLVMBool LLVMRustLoadDynamicLibrary(const char *path) extern "C" void LLVMExecutionEngineAddModule( LLVMExecutionEngineRef eeref, LLVMModuleRef mref) { +#ifdef _WIN32 + // On Windows, MCJIT must generate ELF objects + std::string target = getProcessTriple(); + target += "-elf"; + target = Triple::normalize(target); + unwrap(mref)->setTargetTriple(target); +#endif LLVMAddModule(eeref, mref); } @@ -74,27 +85,36 @@ extern "C" LLVMBool LLVMExecutionEngineRemoveModule( return ee->removeModule(m); } -extern "C" LLVMExecutionEngineRef LLVMBuildExecutionEngine( - LLVMModuleRef mod, LLVMRustJITMemoryManagerRef mref) +extern "C" LLVMExecutionEngineRef LLVMBuildExecutionEngine(LLVMModuleRef mod) { // These are necessary for code generation to work properly. InitializeNativeTarget(); InitializeNativeTargetAsmPrinter(); InitializeNativeTargetAsmParser(); +#ifdef _WIN32 + // On Windows, MCJIT must generate ELF objects + std::string target = getProcessTriple(); + target += "-elf"; + target = Triple::normalize(target); + unwrap(mod)->setTargetTriple(target); +#endif + std::string error_str; TargetOptions options; options.JITEmitDebugInfo = true; options.NoFramePointerElim = true; + RustJITMemoryManager *mm = new RustJITMemoryManager; + ExecutionEngine *ee = #if LLVM_VERSION_MINOR >= 6 EngineBuilder(std::unique_ptr(unwrap(mod))) - .setMCJITMemoryManager(std::unique_ptr(unwrap(mref))) + .setMCJITMemoryManager(std::unique_ptr(mm)) #else EngineBuilder(unwrap(mod)) - .setMCJITMemoryManager(unwrap(mref)) + .setMCJITMemoryManager(mm) #endif .setEngineKind(EngineKind::JIT) .setErrorStr(&error_str) diff --git a/src/rustllvm/rustllvm.h b/src/rustllvm/rustllvm.h index 8a4330acf4341..bb82c0c818677 100644 --- a/src/rustllvm/rustllvm.h +++ b/src/rustllvm/rustllvm.h @@ -51,14 +51,6 @@ #include "llvm/IR/DIBuilder.h" #include "llvm/Linker/Linker.h" -// Used by RustMCJITMemoryManager::getPointerToNamedFunction() -// to get around glibc issues. See the function for more information. -#ifdef __linux__ -#include -#include -#include -#endif - void LLVMRustSetLastError(const char*); typedef struct OpaqueRustString *RustStringRef; diff --git a/src/test/run-make/execution-engine/Makefile b/src/test/run-make/execution-engine/Makefile new file mode 100644 index 0000000000000..387905f45d84a --- /dev/null +++ b/src/test/run-make/execution-engine/Makefile @@ -0,0 +1,8 @@ +-include ../tools.mk + +# This is a basic test of LLVM ExecutionEngine functionality using compiled +# Rust code built using the `rustc` crate. + +all: + $(RUSTC) test.rs + $(call RUN,test $(RUSTC)) diff --git a/src/test/run-make/execution-engine/test.rs b/src/test/run-make/execution-engine/test.rs new file mode 100644 index 0000000000000..ba6d0d246e4e4 --- /dev/null +++ b/src/test/run-make/execution-engine/test.rs @@ -0,0 +1,249 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(rustc_private)] + +extern crate rustc; +extern crate rustc_driver; +extern crate rustc_lint; +extern crate rustc_resolve; +extern crate syntax; + +use std::ffi::{CStr, CString}; +use std::mem::transmute; +use std::path::PathBuf; +use std::thread::Builder; + +use rustc::llvm; +use rustc::metadata::cstore::RequireDynamic; +use rustc::middle::ty; +use rustc::session::config::{self, basic_options, build_configuration, Input, Options}; +use rustc::session::build_session; +use rustc_driver::driver; +use rustc_resolve::MakeGlobMap; + +use syntax::ast_map; +use syntax::diagnostics::registry::Registry; + +fn main() { + let program = r#" + #[no_mangle] + pub static TEST_STATIC: i32 = 42; + "#; + + let program2 = r#" + #[no_mangle] + pub fn test_add(a: i32, b: i32) -> i32 { a + b } + "#; + + let mut path = match std::env::args().nth(2) { + Some(path) => PathBuf::from(&path), + None => panic!("missing rustc path") + }; + + // Remove two segments from rustc path to get sysroot. + path.pop(); + path.pop(); + + let mut ee = ExecutionEngine::new(program, path); + + let test_static = match ee.get_global("TEST_STATIC") { + Some(g) => g as *const i32, + None => panic!("failed to get global") + }; + + assert_eq!(unsafe { *test_static }, 42); + + ee.add_module(program2); + + let test_add: fn(i32, i32) -> i32; + + test_add = match ee.get_function("test_add") { + Some(f) => unsafe { transmute(f) }, + None => panic!("failed to get function") + }; + + assert_eq!(test_add(1, 2), 3); +} + +struct ExecutionEngine { + ee: llvm::ExecutionEngineRef, + modules: Vec, + sysroot: PathBuf, +} + +impl ExecutionEngine { + pub fn new(program: &str, sysroot: PathBuf) -> ExecutionEngine { + let (llmod, deps) = compile_program(program, sysroot.clone()) + .expect("failed to compile program"); + + let ee = unsafe { llvm::LLVMBuildExecutionEngine(llmod) }; + + if ee.is_null() { + panic!("Failed to create ExecutionEngine: {}", llvm_error()); + } + + let ee = ExecutionEngine{ + ee: ee, + modules: vec![llmod], + sysroot: sysroot, + }; + + ee.load_deps(&deps); + ee + } + + pub fn add_module(&mut self, program: &str) { + let (llmod, deps) = compile_program(program, self.sysroot.clone()) + .expect("failed to compile program in add_module"); + + unsafe { llvm::LLVMExecutionEngineAddModule(self.ee, llmod); } + + self.modules.push(llmod); + self.load_deps(&deps); + } + + /// Returns a raw pointer to the named function. + pub fn get_function(&mut self, name: &str) -> Option<*const ()> { + let s = CString::new(name.as_bytes()).unwrap(); + + for &m in &self.modules { + let fv = unsafe { llvm::LLVMGetNamedFunction(m, s.as_ptr()) }; + + if !fv.is_null() { + let fp = unsafe { llvm::LLVMGetPointerToGlobal(self.ee, fv) }; + + assert!(!fp.is_null()); + return Some(fp); + } + } + None + } + + /// Returns a raw pointer to the named global item. + pub fn get_global(&mut self, name: &str) -> Option<*const ()> { + let s = CString::new(name.as_bytes()).unwrap(); + + for &m in &self.modules { + let gv = unsafe { llvm::LLVMGetNamedGlobal(m, s.as_ptr()) }; + + if !gv.is_null() { + let gp = unsafe { llvm::LLVMGetPointerToGlobal(self.ee, gv) }; + + assert!(!gp.is_null()); + return Some(gp); + } + } + None + } + + /// Loads all dependencies of compiled code. + /// Expects a series of paths to dynamic library files. + fn load_deps(&self, deps: &[PathBuf]) { + for path in deps { + let s = match path.as_os_str().to_str() { + Some(s) => s, + None => panic!( + "Could not convert crate path to UTF-8 string: {:?}", path) + }; + let cs = CString::new(s).unwrap(); + + let res = unsafe { llvm::LLVMRustLoadDynamicLibrary(cs.as_ptr()) }; + + if res == 0 { + panic!("Failed to load crate {:?}: {}", + path.display(), llvm_error()); + } + } + } +} + +impl Drop for ExecutionEngine { + fn drop(&mut self) { + unsafe { llvm::LLVMDisposeExecutionEngine(self.ee) }; + } +} + +/// Returns last error from LLVM wrapper code. +fn llvm_error() -> String { + String::from_utf8_lossy( + unsafe { CStr::from_ptr(llvm::LLVMRustGetLastError()).to_bytes() }) + .into_owned() +} + +fn build_exec_options(sysroot: PathBuf) -> Options { + let mut opts = basic_options(); + + // librustc derives sysroot from the executable name. + // Since we are not rustc, we must specify it. + opts.maybe_sysroot = Some(sysroot); + + // Prefer faster build time + opts.optimize = config::No; + + // Don't require a `main` function + opts.crate_types = vec![config::CrateTypeDylib]; + + opts +} + +/// Compiles input up to phase 4, translation to LLVM. +/// +/// Returns the LLVM `ModuleRef` and a series of paths to dynamic libraries +/// for crates used in the given input. +fn compile_program(input: &str, sysroot: PathBuf) + -> Option<(llvm::ModuleRef, Vec)> { + let input = Input::Str(input.to_string()); + let thread = Builder::new().name("compile_program".to_string()); + + let handle = thread.spawn(move || { + let opts = build_exec_options(sysroot); + let sess = build_session(opts, None, Registry::new(&rustc::DIAGNOSTICS)); + rustc_lint::register_builtins(&mut sess.lint_store.borrow_mut(), Some(&sess)); + + let cfg = build_configuration(&sess); + + let id = "input".to_string(); + + let krate = driver::phase_1_parse_input(&sess, cfg, &input); + + let krate = driver::phase_2_configure_and_expand(&sess, krate, &id, None) + .expect("phase_2 returned `None`"); + + let mut forest = ast_map::Forest::new(krate); + let arenas = ty::CtxtArenas::new(); + let ast_map = driver::assign_node_ids_and_map(&sess, &mut forest); + + let analysis = driver::phase_3_run_analysis_passes( + sess, ast_map, &arenas, id, MakeGlobMap::No); + + let (tcx, trans) = driver::phase_4_translate_to_llvm(analysis); + + let crates = tcx.sess.cstore.get_used_crates(RequireDynamic); + + // Collect crates used in the session. + // Reverse order finds dependencies first. + let deps = crates.into_iter().rev() + .filter_map(|(_, p)| p).collect(); + + assert_eq!(trans.modules.len(), 1); + let llmod = trans.modules[0].llmod; + + // Workaround because raw pointers do not impl Send + let modp = llmod as usize; + + (modp, deps) + }).unwrap(); + + match handle.join() { + Ok((llmod, deps)) => Some((llmod as llvm::ModuleRef, deps)), + Err(_) => None + } +}