forked from M-Labs/nac3
161 lines
5.0 KiB
Rust
161 lines
5.0 KiB
Rust
use regex::Regex;
|
|
use std::{
|
|
env,
|
|
fs::File,
|
|
io::Write,
|
|
path::{Path, PathBuf},
|
|
process::{Command, Stdio},
|
|
};
|
|
|
|
const CMD_IRRT_CLANG: &str = "clang-irrt";
|
|
const CMD_IRRT_CLANG_TEST: &str = "clang-irrt-test";
|
|
const CMD_IRRT_LLVM_AS: &str = "llvm-as-irrt";
|
|
|
|
fn get_out_dir() -> PathBuf {
|
|
PathBuf::from(env::var("OUT_DIR").unwrap())
|
|
}
|
|
|
|
fn get_irrt_dir() -> &'static Path {
|
|
Path::new("irrt")
|
|
}
|
|
|
|
/// Compile `irrt.cpp` for use in `src/codegen`
|
|
fn compile_irrt_cpp() {
|
|
let out_dir = get_out_dir();
|
|
let irrt_dir = get_irrt_dir();
|
|
|
|
/*
|
|
* HACK: Sadly, clang doesn't let us emit generic LLVM bitcode.
|
|
* Compiling for WASM32 and filtering the output with regex is the closest we can get.
|
|
*/
|
|
let irrt_cpp_path = irrt_dir.join("irrt.cpp");
|
|
|
|
let mut flags = vec![];
|
|
flags.push("--target=wasm32");
|
|
flags.extend(&["-x", "c++"]);
|
|
flags.extend(&["-fno-discard-value-names", "-fno-exceptions", "-fno-rtti"]);
|
|
flags.push("-emit-llvm");
|
|
flags.push("-S");
|
|
flags.extend(&["-Wall", "-Wextra"]);
|
|
flags.extend(&["-o", "-"]);
|
|
flags.extend(&["-I", irrt_dir.to_str().unwrap()]);
|
|
flags.push(irrt_cpp_path.to_str().unwrap());
|
|
|
|
match env::var("PROFILE").as_deref() {
|
|
Ok("debug") => {
|
|
flags.push("-O0");
|
|
flags.push("-DIRRT_DEBUG");
|
|
}
|
|
Ok("release") => {
|
|
flags.push("-O3");
|
|
}
|
|
flavor => panic!("Unknown or missing build flavor {flavor:?}"),
|
|
};
|
|
|
|
// Tell Cargo to rerun if any file under `irrt_dir` (recursive) changes
|
|
println!("cargo:rerun-if-changed={}", irrt_dir.to_str().unwrap());
|
|
|
|
// Compile IRRT and capture the LLVM IR output
|
|
let output = Command::new(CMD_IRRT_CLANG)
|
|
.args(flags)
|
|
.output()
|
|
.map(|o| {
|
|
assert!(o.status.success(), "{}", std::str::from_utf8(&o.stderr).unwrap());
|
|
o
|
|
})
|
|
.unwrap();
|
|
|
|
// https://github.com/rust-lang/regex/issues/244
|
|
let output = std::str::from_utf8(&output.stdout).unwrap().replace("\r\n", "\n");
|
|
let mut filtered_output = String::with_capacity(output.len());
|
|
|
|
// Filter out irrelevant IR
|
|
//
|
|
// Regex:
|
|
// - `(?ms:^define.*?\}$)` captures LLVM `define` blocks
|
|
// - `(?m:^declare.*?$)` captures LLVM `declare` lines
|
|
// - `(?m:^%.+?=\s*type\s*\{.+?\}$)` captures LLVM `type` declarations
|
|
// - `(?m:^@.+?=.+$)` captures global constants
|
|
let regex_filter = Regex::new(
|
|
r"(?ms:^define.*?\}$)|(?m:^declare.*?$)|(?m:^%.+?=\s*type\s*\{.+?\}$)|(?m:^@.+?=.+$)",
|
|
)
|
|
.unwrap();
|
|
for f in regex_filter.captures_iter(&output) {
|
|
assert_eq!(f.len(), 1);
|
|
filtered_output.push_str(&f[0]);
|
|
filtered_output.push('\n');
|
|
}
|
|
|
|
let filtered_output = Regex::new("(#\\d+)|(, *![0-9A-Za-z.]+)|(![0-9A-Za-z.]+)|(!\".*?\")")
|
|
.unwrap()
|
|
.replace_all(&filtered_output, "");
|
|
|
|
// For debugging
|
|
// Doing `DEBUG_DUMP_IRRT=1 cargo build -p nac3core` dumps the LLVM IR generated
|
|
const DEBUG_DUMP_IRRT: &str = "DEBUG_DUMP_IRRT";
|
|
println!("cargo:rerun-if-env-changed={DEBUG_DUMP_IRRT}");
|
|
if env::var(DEBUG_DUMP_IRRT).is_ok() {
|
|
let mut file = File::create(out_dir.join("irrt.ll")).unwrap();
|
|
file.write_all(output.as_bytes()).unwrap();
|
|
|
|
let mut file = File::create(out_dir.join("irrt-filtered.ll")).unwrap();
|
|
file.write_all(filtered_output.as_bytes()).unwrap();
|
|
}
|
|
|
|
// Assemble the emitted and filtered IR to .bc
|
|
// That .bc will be integrated into nac3core's codegen
|
|
let mut llvm_as = Command::new(CMD_IRRT_LLVM_AS)
|
|
.stdin(Stdio::piped())
|
|
.arg("-o")
|
|
.arg(out_dir.join("irrt.bc"))
|
|
.spawn()
|
|
.unwrap();
|
|
llvm_as.stdin.as_mut().unwrap().write_all(filtered_output.as_bytes()).unwrap();
|
|
assert!(llvm_as.wait().unwrap().success());
|
|
}
|
|
|
|
/// Compile `irrt_test.cpp` for testing
|
|
fn compile_irrt_test_cpp() {
|
|
let out_dir = get_out_dir();
|
|
let irrt_dir = get_irrt_dir();
|
|
|
|
let exe_path = out_dir.join("irrt_test.out"); // Output path of the compiled test executable
|
|
let irrt_test_cpp_path = irrt_dir.join("irrt_test.cpp");
|
|
let flags: &[&str] = &[
|
|
irrt_test_cpp_path.to_str().unwrap(),
|
|
"-x",
|
|
"c++",
|
|
"-I",
|
|
irrt_dir.to_str().unwrap(),
|
|
"-g",
|
|
"-fno-discard-value-names",
|
|
"-O0",
|
|
"-Wall",
|
|
"-Wextra",
|
|
"-Werror=return-type",
|
|
"-lm", // for `tgamma()`, `lgamma()`
|
|
"-o",
|
|
exe_path.to_str().unwrap(),
|
|
];
|
|
|
|
Command::new(CMD_IRRT_CLANG_TEST)
|
|
.args(flags)
|
|
.output()
|
|
.map(|o| {
|
|
assert!(o.status.success(), "{}", std::str::from_utf8(&o.stderr).unwrap());
|
|
o
|
|
})
|
|
.unwrap();
|
|
println!("cargo:rerun-if-changed={}", irrt_dir.to_str().unwrap());
|
|
}
|
|
|
|
fn main() {
|
|
compile_irrt_cpp();
|
|
|
|
// https://github.com/rust-lang/cargo/issues/2549
|
|
// `cargo test -F test` to also build `irrt_test.cpp
|
|
if cfg!(feature = "test") {
|
|
compile_irrt_test_cpp();
|
|
}
|
|
}
|