Skip to content

Commit d81e5bf

Browse files
committed
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 a test for basic `ExecutionEngine` functionality.
1 parent aca207a commit d81e5bf

File tree

4 files changed

+255
-22
lines changed

4 files changed

+255
-22
lines changed

src/librustc_llvm/lib.rs

+1-7
Original file line numberDiff line numberDiff line change
@@ -468,9 +468,6 @@ pub type BuilderRef = *mut Builder_opaque;
468468
pub enum ExecutionEngine_opaque {}
469469
pub type ExecutionEngineRef = *mut ExecutionEngine_opaque;
470470
#[allow(missing_copy_implementations)]
471-
pub enum RustJITMemoryManager_opaque {}
472-
pub type RustJITMemoryManagerRef = *mut RustJITMemoryManager_opaque;
473-
#[allow(missing_copy_implementations)]
474471
pub enum MemoryBuffer_opaque {}
475472
pub type MemoryBufferRef = *mut MemoryBuffer_opaque;
476473
#[allow(missing_copy_implementations)]
@@ -1080,10 +1077,7 @@ extern {
10801077
pub fn LLVMDisposeBuilder(Builder: BuilderRef);
10811078

10821079
/* Execution engine */
1083-
pub fn LLVMRustCreateJITMemoryManager(morestack: *const ())
1084-
-> RustJITMemoryManagerRef;
1085-
pub fn LLVMBuildExecutionEngine(Mod: ModuleRef,
1086-
MM: RustJITMemoryManagerRef) -> ExecutionEngineRef;
1080+
pub fn LLVMBuildExecutionEngine(Mod: ModuleRef) -> ExecutionEngineRef;
10871081
pub fn LLVMDisposeExecutionEngine(EE: ExecutionEngineRef);
10881082
pub fn LLVMExecutionEngineFinalizeObject(EE: ExecutionEngineRef);
10891083
pub fn LLVMRustLoadDynamicLibrary(path: *const c_char) -> Bool;

src/rustllvm/ExecutionEngineWrapper.cpp

+15-15
Original file line numberDiff line numberDiff line change
@@ -16,34 +16,33 @@ using namespace llvm;
1616
using namespace llvm::sys;
1717
using namespace llvm::object;
1818

19+
extern "C" void __morestack(void);
20+
21+
static void* morestack_addr() {
22+
return reinterpret_cast<void*>(__morestack);
23+
}
24+
1925
class RustJITMemoryManager : public SectionMemoryManager
2026
{
2127
typedef SectionMemoryManager Base;
2228

23-
const void *morestack;
24-
2529
public:
2630

27-
RustJITMemoryManager(const void *morestack_ptr)
28-
: morestack(morestack_ptr)
29-
{}
31+
RustJITMemoryManager() {}
3032

3133
uint64_t getSymbolAddress(const std::string &Name) override
3234
{
3335
if (Name == "__morestack" || Name == "___morestack")
34-
return reinterpret_cast<uint64_t>(morestack);
36+
return reinterpret_cast<uint64_t>(__morestack);
37+
if (Name == "__morestack_addr" || Name == "___morestack_addr")
38+
return reinterpret_cast<uint64_t>(morestack_addr);
3539

3640
return Base::getSymbolAddress(Name);
3741
}
3842
};
3943

4044
DEFINE_SIMPLE_CONVERSION_FUNCTIONS(RustJITMemoryManager, LLVMRustJITMemoryManagerRef)
4145

42-
extern "C" LLVMRustJITMemoryManagerRef LLVMRustCreateJITMemoryManager(void *morestack)
43-
{
44-
return wrap(new RustJITMemoryManager(morestack));
45-
}
46-
4746
extern "C" LLVMBool LLVMRustLoadDynamicLibrary(const char *path)
4847
{
4948
std::string err;
@@ -74,8 +73,7 @@ extern "C" LLVMBool LLVMExecutionEngineRemoveModule(
7473
return ee->removeModule(m);
7574
}
7675

77-
extern "C" LLVMExecutionEngineRef LLVMBuildExecutionEngine(
78-
LLVMModuleRef mod, LLVMRustJITMemoryManagerRef mref)
76+
extern "C" LLVMExecutionEngineRef LLVMBuildExecutionEngine(LLVMModuleRef mod)
7977
{
8078
// These are necessary for code generation to work properly.
8179
InitializeNativeTarget();
@@ -88,13 +86,15 @@ extern "C" LLVMExecutionEngineRef LLVMBuildExecutionEngine(
8886
options.JITEmitDebugInfo = true;
8987
options.NoFramePointerElim = true;
9088

89+
RustJITMemoryManager *mm = new RustJITMemoryManager;
90+
9191
ExecutionEngine *ee =
9292
#if LLVM_VERSION_MINOR >= 6
9393
EngineBuilder(std::unique_ptr<Module>(unwrap(mod)))
94-
.setMCJITMemoryManager(std::unique_ptr<RustJITMemoryManager>(unwrap(mref)))
94+
.setMCJITMemoryManager(std::unique_ptr<RustJITMemoryManager>(mm))
9595
#else
9696
EngineBuilder(unwrap(mod))
97-
.setMCJITMemoryManager(unwrap(mref))
97+
.setMCJITMemoryManager(mm)
9898
#endif
9999
.setEngineKind(EngineKind::JIT)
100100
.setErrorStr(&error_str)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
-include ../tools.mk
2+
3+
# This is a basic test of LLVM ExecutionEngine functionality using compiled
4+
# Rust code built using the `rustc` crate.
5+
6+
all:
7+
$(RUSTC) test.rs
8+
$(call RUN,test $(RUSTC))
+231
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,231 @@
1+
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#![feature(rustc_private)]
12+
13+
extern crate rustc;
14+
extern crate rustc_driver;
15+
extern crate rustc_lint;
16+
extern crate rustc_resolve;
17+
extern crate syntax;
18+
19+
use std::ffi::{CStr, CString};
20+
use std::mem::transmute;
21+
use std::path::PathBuf;
22+
use std::thread::Builder;
23+
24+
use rustc::llvm;
25+
use rustc::metadata::cstore::RequireDynamic;
26+
use rustc::middle::ty;
27+
use rustc::session::config::{self, basic_options, build_configuration, Input, Options};
28+
use rustc::session::build_session;
29+
use rustc_driver::driver;
30+
use rustc_resolve::MakeGlobMap;
31+
32+
use syntax::ast_map;
33+
use syntax::diagnostics::registry::Registry;
34+
35+
fn main() {
36+
let program = r#"
37+
#[no_mangle]
38+
pub static TEST_STATIC: i32 = 42;
39+
40+
#[no_mangle]
41+
pub fn test_add(a: i32, b: i32) -> i32 { a + b }
42+
"#;
43+
44+
let mut path = match std::env::args().nth(2) {
45+
Some(path) => PathBuf::from(&path),
46+
None => panic!("missing rustc path")
47+
};
48+
49+
// Remove two segments from rustc path to get sysroot.
50+
path.pop();
51+
path.pop();
52+
53+
let mut ee = ExecutionEngine::new(program, path);
54+
55+
let test_static = match ee.get_global("TEST_STATIC") {
56+
Some(g) => g as *const i32,
57+
None => panic!("failed to get global")
58+
};
59+
60+
assert_eq!(unsafe { *test_static }, 42);
61+
62+
let test_add: fn(i32, i32) -> i32;
63+
64+
test_add = match ee.get_function("test_add") {
65+
Some(f) => unsafe { transmute(f) },
66+
None => panic!("failed to get function")
67+
};
68+
69+
assert_eq!(test_add(1, 2), 3);
70+
}
71+
72+
struct ExecutionEngine {
73+
ee: llvm::ExecutionEngineRef,
74+
module: llvm::ModuleRef,
75+
}
76+
77+
impl ExecutionEngine {
78+
pub fn new(program: &str, sysroot: PathBuf) -> ExecutionEngine {
79+
let (llmod, deps) = compile_program(program, sysroot)
80+
.expect("failed to compile program");
81+
82+
let ee = unsafe { llvm::LLVMBuildExecutionEngine(llmod) };
83+
84+
if ee.is_null() {
85+
panic!("Failed to create ExecutionEngine: {}", llvm_error());
86+
}
87+
88+
let ee = ExecutionEngine{
89+
ee: ee,
90+
module: llmod,
91+
};
92+
93+
ee.load_deps(&deps);
94+
ee
95+
}
96+
97+
/// Returns a raw pointer to the named function.
98+
pub fn get_function(&mut self, name: &str) -> Option<*const ()> {
99+
let s = CString::new(name.as_bytes()).unwrap();
100+
101+
let fv = unsafe { llvm::LLVMGetNamedFunction(self.module, s.as_ptr()) };
102+
103+
if fv.is_null() {
104+
None
105+
} else {
106+
let fp = unsafe { llvm::LLVMGetPointerToGlobal(self.ee, fv) };
107+
108+
assert!(!fp.is_null());
109+
Some(fp)
110+
}
111+
}
112+
113+
/// Returns a raw pointer to the named global item.
114+
pub fn get_global(&mut self, name: &str) -> Option<*const ()> {
115+
let s = CString::new(name.as_bytes()).unwrap();
116+
117+
let gv = unsafe { llvm::LLVMGetNamedGlobal(self.module, s.as_ptr()) };
118+
119+
if gv.is_null() {
120+
None
121+
} else {
122+
let gp = unsafe { llvm::LLVMGetPointerToGlobal(self.ee, gv) };
123+
124+
assert!(!gp.is_null());
125+
Some(gp)
126+
}
127+
}
128+
129+
/// Loads all dependencies of compiled code.
130+
/// Expects a series of paths to dynamic library files.
131+
fn load_deps(&self, deps: &[PathBuf]) {
132+
for path in deps {
133+
let s = match path.as_os_str().to_str() {
134+
Some(s) => s,
135+
None => panic!(
136+
"Could not convert crate path to UTF-8 string: {:?}", path)
137+
};
138+
let cs = CString::new(s).unwrap();
139+
140+
let res = unsafe { llvm::LLVMRustLoadDynamicLibrary(cs.as_ptr()) };
141+
142+
if res == 0 {
143+
panic!("Failed to load crate {:?}: {}",
144+
path.display(), llvm_error());
145+
}
146+
}
147+
}
148+
}
149+
150+
impl Drop for ExecutionEngine {
151+
fn drop(&mut self) {
152+
unsafe { llvm::LLVMDisposeExecutionEngine(self.ee) };
153+
}
154+
}
155+
156+
/// Returns last error from LLVM wrapper code.
157+
fn llvm_error() -> String {
158+
String::from_utf8_lossy(
159+
unsafe { CStr::from_ptr(llvm::LLVMRustGetLastError()).to_bytes() })
160+
.into_owned()
161+
}
162+
163+
fn build_exec_options(sysroot: PathBuf) -> Options {
164+
let mut opts = basic_options();
165+
166+
// librustc derives sysroot from the executable name.
167+
// Since we are not rustc, we must specify it.
168+
opts.maybe_sysroot = Some(sysroot);
169+
170+
// Prefer faster build time
171+
opts.optimize = config::No;
172+
173+
// Don't require a `main` function
174+
opts.crate_types = vec![config::CrateTypeDylib];
175+
176+
opts
177+
}
178+
179+
/// Compiles input up to phase 4, translation to LLVM.
180+
///
181+
/// Returns the LLVM `ModuleRef` and a series of paths to dynamic libraries
182+
/// for crates used in the given input.
183+
fn compile_program(input: &str, sysroot: PathBuf)
184+
-> Option<(llvm::ModuleRef, Vec<PathBuf>)> {
185+
let input = Input::Str(input.to_string());
186+
let thread = Builder::new().name("compile_program".to_string());
187+
188+
let handle = thread.spawn(move || {
189+
let opts = build_exec_options(sysroot);
190+
let sess = build_session(opts, None, Registry::new(&rustc::DIAGNOSTICS));
191+
rustc_lint::register_builtins(&mut sess.lint_store.borrow_mut(), Some(&sess));
192+
193+
let cfg = build_configuration(&sess);
194+
195+
let id = "input".to_string();
196+
197+
let krate = driver::phase_1_parse_input(&sess, cfg, &input);
198+
199+
let krate = driver::phase_2_configure_and_expand(&sess, krate, &id, None)
200+
.expect("phase_2 returned `None`");
201+
202+
let mut forest = ast_map::Forest::new(krate);
203+
let arenas = ty::CtxtArenas::new();
204+
let ast_map = driver::assign_node_ids_and_map(&sess, &mut forest);
205+
206+
let analysis = driver::phase_3_run_analysis_passes(
207+
sess, ast_map, &arenas, id, MakeGlobMap::No);
208+
209+
let (tcx, trans) = driver::phase_4_translate_to_llvm(analysis);
210+
211+
let crates = tcx.sess.cstore.get_used_crates(RequireDynamic);
212+
213+
// Collect crates used in the session.
214+
// Reverse order finds dependencies first.
215+
let deps = crates.into_iter().rev()
216+
.filter_map(|(_, p)| p).collect();
217+
218+
assert_eq!(trans.modules.len(), 1);
219+
let llmod = trans.modules[0].llmod;
220+
221+
// Workaround because raw pointers do not impl Send
222+
let modp = llmod as usize;
223+
224+
(modp, deps)
225+
}).unwrap();
226+
227+
match handle.join() {
228+
Ok((llmod, deps)) => Some((llmod as llvm::ModuleRef, deps)),
229+
Err(_) => None
230+
}
231+
}

0 commit comments

Comments
 (0)