Skip to content

Commit 01c6c69

Browse files
committed
Initial commit
0 parents  commit 01c6c69

File tree

7 files changed

+425
-0
lines changed

7 files changed

+425
-0
lines changed

.gitignore

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
/target
2+
**/*.rs.bk
3+
Cargo.lock
4+
5+
src/llvm_gen.rs

.travis.yml

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
language: rust
2+
cache: cargo
3+
4+
matrix:
5+
include:
6+
- os: linux
7+
rust: nightly
8+
9+
- os: linux
10+
rust: stable
11+
12+
- os: osx
13+
rust: nightly
14+
15+
- os: osx
16+
rust: stable
17+
18+
script:
19+
- cargo check
20+
- cargo test
21+
22+
branches:
23+
only:
24+
- master
25+
26+
notifications:
27+
email:
28+
on_success: never

Cargo.toml

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
[package]
2+
name = "rustc-llvm-proxy"
3+
version = "0.1.0"
4+
authors = ["Denys Zariaiev <denys.zariaiev@gmail.com>"]
5+
6+
[dependencies]
7+
libloading = "0.5"
8+
lazy_static = "1.0"
9+
failure = "0.1"
10+
libc = "0.2"
11+
12+
[dependencies.llvm-sys]
13+
git = "https://github.com/denzp/llvm-sys.rs.git"
14+
features = ["no-llvm-linking"]
15+
16+
[build-dependencies]
17+
cargo_metadata = "0.6"
18+
failure = "0.1"
19+
quote = "0.6"
20+
21+
[build-dependencies.syn]
22+
version = "0.14"
23+
features = ["full", "extra-traits"] # TODO: remove extra-traits

build.rs

+165
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
extern crate cargo_metadata;
2+
extern crate quote;
3+
extern crate syn;
4+
5+
#[macro_use]
6+
extern crate failure;
7+
8+
fn main() {
9+
println!("cargo:rerun-if-changed=build.rs");
10+
println!("cargo:rerun-if-changed=src/llvm_gen.rs");
11+
12+
llvm::Generator::default()
13+
.parse_llvm_sys_crate()
14+
.expect("Unable to parse 'llvm-sys' crate")
15+
.write_declarations("src/llvm_gen.rs")
16+
.expect("Unable to write generated LLVM declarations");
17+
}
18+
19+
#[derive(Debug)]
20+
struct Declaration {
21+
name: String,
22+
args: String,
23+
ret_ty: String,
24+
}
25+
26+
mod llvm {
27+
use std::fs::File;
28+
use std::io::{Read, Write};
29+
use std::path::{Path, PathBuf};
30+
31+
use cargo_metadata::metadata_deps;
32+
use failure::Error;
33+
use quote::ToTokens;
34+
use syn::{parse_file, Abi, ForeignItem, Item, ItemForeignMod, ReturnType};
35+
36+
use super::*;
37+
38+
const LLVM_SOURCES: &[&str] = &[
39+
"core.rs",
40+
"linker.rs",
41+
"bit_reader.rs",
42+
"bit_writer.rs",
43+
"target.rs",
44+
"target_machine.rs",
45+
"transforms/ipo.rs",
46+
"transforms/pass_manager_builder.rs",
47+
"transforms/scalar.rs",
48+
"transforms/vectorize.rs",
49+
];
50+
51+
#[derive(Default)]
52+
pub struct Generator {
53+
declarations: Vec<Declaration>,
54+
}
55+
56+
impl Generator {
57+
pub fn parse_llvm_sys_crate(mut self) -> Result<Self, Error> {
58+
let llvm_src_path = self.get_llvm_sys_crate_path()?;
59+
60+
for file in LLVM_SOURCES {
61+
let path = llvm_src_path.join(file);
62+
let mut declarations = self.extract_file_declarations(&path)?;
63+
64+
self.declarations.append(&mut declarations);
65+
}
66+
67+
Ok(self)
68+
}
69+
70+
pub fn write_declarations(self, path: &str) -> Result<(), Error> {
71+
let mut file = File::create(path)?;
72+
73+
for decl in self.declarations {
74+
writeln!(
75+
file,
76+
"create_proxy!({}; {}; {});",
77+
decl.name, decl.ret_ty, decl.args
78+
);
79+
}
80+
81+
Ok(())
82+
}
83+
84+
fn get_llvm_sys_crate_path(&self) -> Result<PathBuf, Error> {
85+
let metadata = metadata_deps(None, true)
86+
.map_err(|_| format_err!("Unable to get crate metadata"))?;
87+
88+
let llvm_dependency = metadata
89+
.packages
90+
.into_iter()
91+
.find(|item| item.name == "llvm-sys")
92+
.ok_or(format_err!(
93+
"Unable to find 'llvm-sys' in the crate metadata"
94+
))?;
95+
96+
let llvm_lib_rs_path = PathBuf::from(
97+
llvm_dependency
98+
.targets
99+
.into_iter()
100+
.find(|item| item.name == "llvm-sys")
101+
.ok_or(format_err!(
102+
"Unable to find lib target for 'llvm-sys' crate"
103+
))?.src_path,
104+
);
105+
106+
Ok(llvm_lib_rs_path.parent().unwrap().into())
107+
}
108+
109+
fn extract_file_declarations(&self, path: &Path) -> Result<Vec<Declaration>, Error> {
110+
let mut file = File::open(path)
111+
.map_err(|_| format_err!("Unable to open file: {}", path.to_str().unwrap()))?;
112+
113+
let mut content = String::new();
114+
file.read_to_string(&mut content)?;
115+
116+
let ast = parse_file(&content)?;
117+
118+
Ok(ast.items.iter().fold(vec![], |mut list, item| match item {
119+
Item::ForeignMod(ref item) if item.abi.is_c() => {
120+
list.append(&mut self.extract_foreign_mod_declarations(item));
121+
list
122+
}
123+
124+
_ => list,
125+
}))
126+
}
127+
128+
fn extract_foreign_mod_declarations(&self, item: &ItemForeignMod) -> Vec<Declaration> {
129+
item.items.iter().fold(vec![], |mut list, item| match item {
130+
ForeignItem::Fn(ref item) => {
131+
let ret_ty = match item.decl.output {
132+
ReturnType::Default => "()".into(),
133+
ReturnType::Type(_, ref ty) => ty.into_token_stream().to_string(),
134+
};
135+
136+
list.push(Declaration {
137+
name: item.ident.to_string(),
138+
args: item.decl.inputs.clone().into_token_stream().to_string(),
139+
ret_ty,
140+
});
141+
142+
list
143+
}
144+
145+
_ => list,
146+
})
147+
}
148+
}
149+
150+
trait AbiExt {
151+
fn is_c(&self) -> bool;
152+
}
153+
154+
impl AbiExt for Abi {
155+
fn is_c(&self) -> bool {
156+
let abi_name = self
157+
.name
158+
.as_ref()
159+
.map(|item| item.value())
160+
.unwrap_or(String::new());
161+
162+
abi_name == "C"
163+
}
164+
}
165+
}

src/lib.rs

+65
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
#![deny(warnings)]
2+
#![allow(non_snake_case)]
3+
4+
#[macro_use]
5+
extern crate lazy_static;
6+
#[macro_use]
7+
extern crate failure;
8+
9+
extern crate libc;
10+
extern crate libloading as lib;
11+
extern crate llvm_sys;
12+
13+
use lib::Library;
14+
15+
mod path;
16+
use path::find_lib_path;
17+
18+
lazy_static! {
19+
static ref SHARED_LIB: Library = {
20+
let lib_path = match find_lib_path() {
21+
Ok(path) => path,
22+
23+
Err(error) => {
24+
eprintln!("{}", error);
25+
panic!();
26+
}
27+
};
28+
29+
match Library::new(lib_path) {
30+
Ok(path) => path,
31+
32+
Err(error) => {
33+
eprintln!("Unable to open LLVM shared lib: {}", error);
34+
panic!();
35+
}
36+
}
37+
};
38+
}
39+
40+
pub mod proxy {
41+
use super::SHARED_LIB;
42+
43+
use llvm_sys::prelude::*;
44+
use llvm_sys::target::*;
45+
use llvm_sys::target_machine::*;
46+
use llvm_sys::transforms::pass_manager_builder::*;
47+
use llvm_sys::*;
48+
49+
macro_rules! create_proxy {
50+
($name:ident ; $ret_ty:ty ; $($arg:ident : $arg_ty:ty),*) => {
51+
#[no_mangle]
52+
pub unsafe extern "C" fn $name($($arg: $arg_ty),*) -> $ret_ty {
53+
let entrypoint = {
54+
*SHARED_LIB
55+
.get::<unsafe extern "C" fn($($arg: $arg_ty),*) -> $ret_ty>(stringify!($name).as_bytes())
56+
.unwrap()
57+
};
58+
59+
entrypoint($($arg),*)
60+
}
61+
};
62+
}
63+
64+
include!("llvm_gen.rs");
65+
}

src/path.rs

+106
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
use std::env;
2+
use std::path::PathBuf;
3+
use std::process::Command;
4+
5+
use failure::Error;
6+
7+
pub fn find_lib_path() -> Result<PathBuf, Error> {
8+
let paths = collect_possible_paths()?;
9+
10+
if paths.len() == 0 {
11+
bail!("Unable to find possible LLVM shared lib locations.");
12+
}
13+
14+
for path in &paths {
15+
if path.join("librustc_codegen_llvm-llvm.so").exists() {
16+
return Ok(path.join("librustc_codegen_llvm-llvm.so"));
17+
}
18+
19+
if path.join("librustc_codegen_llvm-llvm.dll").exists() {
20+
return Ok(path.join("librustc_codegen_llvm-llvm.dll"));
21+
}
22+
}
23+
24+
bail!(
25+
"Unable to find LLVM shared lib in possible locations:\n- {}",
26+
paths
27+
.into_iter()
28+
.map(|item| item.to_str().unwrap().to_owned())
29+
.collect::<Vec<_>>()
30+
.join("\n- ")
31+
);
32+
}
33+
34+
fn collect_possible_paths() -> Result<Vec<PathBuf>, Error> {
35+
let mut paths = vec![];
36+
37+
if let Ok(rustup_home) = env::var("RUSTUP_HOME") {
38+
let rustup_home = PathBuf::from(rustup_home);
39+
let rustup_toolchain = env::var("RUSTUP_TOOLCHAIN")?;
40+
let rustup_arch = extract_arch(&rustup_toolchain);
41+
42+
paths.push(
43+
rustup_home
44+
.join("toolchains")
45+
.join(&rustup_toolchain)
46+
.join("lib/rustlib")
47+
.join(rustup_arch)
48+
.join("codegen-backends"),
49+
);
50+
}
51+
52+
if let Ok(lib_paths) = env::var("LD_LIBRARY_PATH") {
53+
for item in lib_paths.split(":") {
54+
let mut possible_path = PathBuf::from(item);
55+
possible_path.pop();
56+
57+
let possible_toolchain = possible_path.file_name().unwrap();
58+
let possible_arch = extract_arch(possible_toolchain.to_str().unwrap());
59+
60+
paths.push(
61+
possible_path
62+
.join("lib/rustlib")
63+
.join(possible_arch)
64+
.join("codegen-backends"),
65+
);
66+
}
67+
}
68+
69+
if let Ok(cargo) = env::var("CARGO") {
70+
let mut cargo_path = PathBuf::from(cargo);
71+
cargo_path.pop();
72+
cargo_path.pop();
73+
74+
let toolchain = cargo_path.file_name().unwrap();
75+
let arch = extract_arch(toolchain.to_str().unwrap());
76+
77+
paths.push(
78+
cargo_path
79+
.join("lib/rustlib")
80+
.join(arch)
81+
.join("codegen-backends"),
82+
);
83+
}
84+
85+
if let Ok(output) = Command::new("rustup").args(&["which", "rustc"]).output() {
86+
let mut rustc_path = PathBuf::from(String::from_utf8_lossy(&output.stdout).trim());
87+
rustc_path.pop();
88+
rustc_path.pop();
89+
90+
let toolchain = rustc_path.file_name().unwrap();
91+
let arch = extract_arch(toolchain.to_str().unwrap());
92+
93+
paths.push(
94+
rustc_path
95+
.join("lib/rustlib")
96+
.join(arch)
97+
.join("codegen-backends"),
98+
);
99+
}
100+
101+
Ok(paths)
102+
}
103+
104+
fn extract_arch(toolchain: &str) -> String {
105+
toolchain.split('-').skip(1).collect::<Vec<_>>().join("-")
106+
}

0 commit comments

Comments
 (0)