]> git.proxmox.com Git - rustc.git/blob - src/test/run-make/execution-engine/test.rs
8af3844e62eefdee3de563b9c759018b42101a84
[rustc.git] / src / test / run-make / execution-engine / test.rs
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::ast_map;
25 use rustc::llvm;
26 use rustc::metadata::cstore::RequireDynamic;
27 use rustc::middle::ty;
28 use rustc::session::config::{self, basic_options, build_configuration, Input, Options};
29 use rustc::session::build_session;
30 use rustc_driver::driver;
31 use rustc_resolve::MakeGlobMap;
32
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
41 let program2 = r#"
42 #[no_mangle]
43 pub fn test_add(a: i32, b: i32) -> i32 { a + b }
44 "#;
45
46 let mut path = match std::env::args().nth(2) {
47 Some(path) => PathBuf::from(&path),
48 None => panic!("missing rustc path")
49 };
50
51 // Remove two segments from rustc path to get sysroot.
52 path.pop();
53 path.pop();
54
55 let mut ee = ExecutionEngine::new(program, path);
56
57 let test_static = match ee.get_global("TEST_STATIC") {
58 Some(g) => g as *const i32,
59 None => panic!("failed to get global")
60 };
61
62 assert_eq!(unsafe { *test_static }, 42);
63
64 ee.add_module(program2);
65
66 let test_add: fn(i32, i32) -> i32;
67
68 test_add = match ee.get_function("test_add") {
69 Some(f) => unsafe { transmute(f) },
70 None => panic!("failed to get function")
71 };
72
73 assert_eq!(test_add(1, 2), 3);
74 }
75
76 struct ExecutionEngine {
77 ee: llvm::ExecutionEngineRef,
78 modules: Vec<llvm::ModuleRef>,
79 sysroot: PathBuf,
80 }
81
82 impl ExecutionEngine {
83 pub fn new(program: &str, sysroot: PathBuf) -> ExecutionEngine {
84 let (llmod, deps) = compile_program(program, sysroot.clone())
85 .expect("failed to compile program");
86
87 let ee = unsafe { llvm::LLVMBuildExecutionEngine(llmod) };
88
89 if ee.is_null() {
90 panic!("Failed to create ExecutionEngine: {}", llvm_error());
91 }
92
93 let ee = ExecutionEngine{
94 ee: ee,
95 modules: vec![llmod],
96 sysroot: sysroot,
97 };
98
99 ee.load_deps(&deps);
100 ee
101 }
102
103 pub fn add_module(&mut self, program: &str) {
104 let (llmod, deps) = compile_program(program, self.sysroot.clone())
105 .expect("failed to compile program in add_module");
106
107 unsafe { llvm::LLVMExecutionEngineAddModule(self.ee, llmod); }
108
109 self.modules.push(llmod);
110 self.load_deps(&deps);
111 }
112
113 /// Returns a raw pointer to the named function.
114 pub fn get_function(&mut self, name: &str) -> Option<*const ()> {
115 let s = CString::new(name.as_bytes()).unwrap();
116
117 for &m in &self.modules {
118 let fv = unsafe { llvm::LLVMGetNamedFunction(m, s.as_ptr()) };
119
120 if !fv.is_null() {
121 let fp = unsafe { llvm::LLVMGetPointerToGlobal(self.ee, fv) };
122
123 assert!(!fp.is_null());
124 return Some(fp);
125 }
126 }
127 None
128 }
129
130 /// Returns a raw pointer to the named global item.
131 pub fn get_global(&mut self, name: &str) -> Option<*const ()> {
132 let s = CString::new(name.as_bytes()).unwrap();
133
134 for &m in &self.modules {
135 let gv = unsafe { llvm::LLVMGetNamedGlobal(m, s.as_ptr()) };
136
137 if !gv.is_null() {
138 let gp = unsafe { llvm::LLVMGetPointerToGlobal(self.ee, gv) };
139
140 assert!(!gp.is_null());
141 return Some(gp);
142 }
143 }
144 None
145 }
146
147 /// Loads all dependencies of compiled code.
148 /// Expects a series of paths to dynamic library files.
149 fn load_deps(&self, deps: &[PathBuf]) {
150 for path in deps {
151 let s = match path.as_os_str().to_str() {
152 Some(s) => s,
153 None => panic!(
154 "Could not convert crate path to UTF-8 string: {:?}", path)
155 };
156 let cs = CString::new(s).unwrap();
157
158 let res = unsafe { llvm::LLVMRustLoadDynamicLibrary(cs.as_ptr()) };
159
160 if res == 0 {
161 panic!("Failed to load crate {:?}: {}",
162 path.display(), llvm_error());
163 }
164 }
165 }
166 }
167
168 impl Drop for ExecutionEngine {
169 fn drop(&mut self) {
170 unsafe { llvm::LLVMDisposeExecutionEngine(self.ee) };
171 }
172 }
173
174 /// Returns last error from LLVM wrapper code.
175 fn llvm_error() -> String {
176 String::from_utf8_lossy(
177 unsafe { CStr::from_ptr(llvm::LLVMRustGetLastError()).to_bytes() })
178 .into_owned()
179 }
180
181 fn build_exec_options(sysroot: PathBuf) -> Options {
182 let mut opts = basic_options();
183
184 // librustc derives sysroot from the executable name.
185 // Since we are not rustc, we must specify it.
186 opts.maybe_sysroot = Some(sysroot);
187
188 // Prefer faster build time
189 opts.optimize = config::No;
190
191 // Don't require a `main` function
192 opts.crate_types = vec![config::CrateTypeDylib];
193
194 opts
195 }
196
197 /// Compiles input up to phase 4, translation to LLVM.
198 ///
199 /// Returns the LLVM `ModuleRef` and a series of paths to dynamic libraries
200 /// for crates used in the given input.
201 fn compile_program(input: &str, sysroot: PathBuf)
202 -> Option<(llvm::ModuleRef, Vec<PathBuf>)> {
203 let input = Input::Str(input.to_string());
204 let thread = Builder::new().name("compile_program".to_string());
205
206 let handle = thread.spawn(move || {
207 let opts = build_exec_options(sysroot);
208 let sess = build_session(opts, None, Registry::new(&rustc::DIAGNOSTICS));
209 rustc_lint::register_builtins(&mut sess.lint_store.borrow_mut(), Some(&sess));
210
211 let cfg = build_configuration(&sess);
212
213 let id = "input".to_string();
214
215 let krate = driver::phase_1_parse_input(&sess, cfg, &input);
216
217 let krate = driver::phase_2_configure_and_expand(&sess, krate, &id, None)
218 .expect("phase_2 returned `None`");
219
220 let mut forest = ast_map::Forest::new(krate);
221 let arenas = ty::CtxtArenas::new();
222 let ast_map = driver::assign_node_ids_and_map(&sess, &mut forest);
223
224 driver::phase_3_run_analysis_passes(
225 sess, ast_map, &arenas, id, MakeGlobMap::No, |tcx, analysis| {
226
227 let trans = driver::phase_4_translate_to_llvm(tcx, analysis);
228
229 let crates = tcx.sess.cstore.get_used_crates(RequireDynamic);
230
231 // Collect crates used in the session.
232 // Reverse order finds dependencies first.
233 let deps = crates.into_iter().rev()
234 .filter_map(|(_, p)| p).collect();
235
236 assert_eq!(trans.modules.len(), 1);
237 let llmod = trans.modules[0].llmod;
238
239 // Workaround because raw pointers do not impl Send
240 let modp = llmod as usize;
241
242 (modp, deps)
243 }).1
244 }).unwrap();
245
246 match handle.join() {
247 Ok((llmod, deps)) => Some((llmod as llvm::ModuleRef, deps)),
248 Err(_) => None
249 }
250 }