diff options
| author | Murarth <murarth@gmail.com> | 2015-05-19 16:00:59 -0700 |
|---|---|---|
| committer | Murarth <murarth@gmail.com> | 2015-06-08 16:54:50 -0700 |
| commit | 021e48326db304559a74f2f338d511f9674d0da7 (patch) | |
| tree | 95a987c78c2e1a24bea1f19ce62bcc88d15285d7 /src/test/run-make/execution-engine | |
| parent | aca207a65c82d7581076772846ef424f1a64449b (diff) | |
| download | rust-021e48326db304559a74f2f338d511f9674d0da7.tar.gz rust-021e48326db304559a74f2f338d511f9674d0da7.zip | |
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.
Diffstat (limited to 'src/test/run-make/execution-engine')
| -rw-r--r-- | src/test/run-make/execution-engine/Makefile | 8 | ||||
| -rw-r--r-- | src/test/run-make/execution-engine/test.rs | 249 |
2 files changed, 257 insertions, 0 deletions
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 + } +} |
