about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2015-06-09 04:28:57 +0000
committerbors <bors@rust-lang.org>2015-06-09 04:28:57 +0000
commit71a8d313c8351771dc5507dde14f654ca4f0707d (patch)
treeb428370aa123041e946c1275471ad6be32bff79b /src
parenta35ea4d35837c5cffbf4fe81c00e94092c35d5fe (diff)
parent021e48326db304559a74f2f338d511f9674d0da7 (diff)
downloadrust-71a8d313c8351771dc5507dde14f654ca4f0707d.tar.gz
rust-71a8d313c8351771dc5507dde14f654ca4f0707d.zip
Auto merge of #25627 - murarth:execution-engine-fix, r=nrc
* 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.
Diffstat (limited to 'src')
-rw-r--r--src/librustc_llvm/lib.rs8
-rw-r--r--src/rustllvm/ExecutionEngineWrapper.cpp50
-rw-r--r--src/rustllvm/rustllvm.h8
-rw-r--r--src/test/run-make/execution-engine/Makefile8
-rw-r--r--src/test/run-make/execution-engine/test.rs249
5 files changed, 293 insertions, 30 deletions
diff --git a/src/librustc_llvm/lib.rs b/src/librustc_llvm/lib.rs
index 379a7da459f..4d7f00e9523 100644
--- a/src/librustc_llvm/lib.rs
+++ b/src/librustc_llvm/lib.rs
@@ -478,9 +478,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)]
@@ -1090,10 +1087,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 7e0630fd242..e37ede82bb5 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<void*>(__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<uint64_t>(morestack);
+            return reinterpret_cast<uint64_t>(__morestack);
+        if (Name == "__morestack_addr" || Name == "___morestack_addr")
+            return reinterpret_cast<uint64_t>(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<Module>(unwrap(mod)))
-            .setMCJITMemoryManager(std::unique_ptr<RustJITMemoryManager>(unwrap(mref)))
+            .setMCJITMemoryManager(std::unique_ptr<RustJITMemoryManager>(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 8a4330acf43..bb82c0c8186 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 <sys/stat.h>
-#include <fcntl.h>
-#include <unistd.h>
-#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 00000000000..387905f45d8
--- /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 00000000000..ba6d0d246e4
--- /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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<llvm::ModuleRef>,
+    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<PathBuf>)> {
+    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
+    }
+}