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.
This commit is contained in:
commit
71a8d313c8
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
8
src/test/run-make/execution-engine/Makefile
Normal file
8
src/test/run-make/execution-engine/Makefile
Normal file
@ -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))
|
249
src/test/run-make/execution-engine/test.rs
Normal file
249
src/test/run-make/execution-engine/test.rs
Normal file
@ -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
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user