#![cfg_attr(not(test), allow(dead_code))]
#![allow(clippy::cognitive_complexity)]
#![allow(clippy::too_many_arguments)]
#![allow(clippy::missing_safety_doc)]
#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate log;
use chrono;
use env_logger;
use fnv;
use libc;
use time;
use self::time::PreciseTime;
use std::default::Default;
use std::error::Error;
use std::ffi::{CStr, CString};
use std::fmt;
use std::cell::RefCell;
use std::rc::Rc;
use uuid::Uuid;
#[macro_export]
macro_rules! weld_err {
( $($arg:tt)* ) => ({
::std::result::Result::Err($crate::WeldError::new_unknown(format!($($arg)*)))
})
}
pub const BUILD: &str = env!("BUILD_ID");
pub const VERSION: Option<&'static str> = option_env!("CARGO_PKG_VERSION");
#[macro_use]
mod error;
mod codegen;
mod conf;
mod optimizer;
mod sir;
mod syntax;
mod util;
pub mod ast;
pub mod data;
pub mod runtime;
pub use crate::conf::constants::*;
#[cfg(test)]
mod tests;
use crate::conf::ParsedConf;
use crate::runtime::WeldRuntimeContext;
use crate::util::dump::{write_code, DumpCodeFormat};
use crate::util::stats::CompilationStats;
pub use crate::runtime::WeldRuntimeErrno;
pub type Data = *const libc::c_void;
pub type DataMut = *mut libc::c_void;
pub type RunId = i64;
#[derive(Debug, Clone)]
pub struct WeldError {
message: CString,
code: WeldRuntimeErrno,
}
pub type WeldResult<T> = Result<T, WeldError>;
#[derive(Clone, Debug, PartialEq)]
pub struct WeldContext {
context: Rc<RefCell<WeldRuntimeContext>>,
}
impl WeldContext {
pub fn new(conf: &WeldConf) -> WeldResult<WeldContext> {
let conf = &mut ParsedConf::parse(conf)?;
let threads = conf.threads;
let mem_limit = conf.memory_limit;
let run = WeldRuntimeContext::new(threads as i32, mem_limit);
Ok(WeldContext {
context: Rc::new(RefCell::new(run)),
})
}
pub fn memory_usage(&self) -> i64 {
self.context.borrow().memory_usage()
}
pub fn memory_limit(&self) -> i64 {
self.context.borrow().memory_limit()
}
}
impl WeldError {
pub fn new<T: Into<Vec<u8>>>(message: T, code: WeldRuntimeErrno) -> WeldError {
WeldError {
message: CString::new(message).unwrap(),
code,
}
}
pub fn new_unknown<T: Into<Vec<u8>>>(message: T) -> WeldError {
WeldError {
message: CString::new(message).unwrap(),
code: WeldRuntimeErrno::Unknown,
}
}
pub fn new_success() -> WeldError {
WeldError::new(CString::new("Success").unwrap(), WeldRuntimeErrno::Success)
}
pub fn code(&self) -> WeldRuntimeErrno {
self.code
}
pub fn message(&self) -> &CStr {
self.message.as_ref()
}
}
impl Default for WeldError {
fn default() -> WeldError {
WeldError {
message: CString::new("").unwrap(),
code: WeldRuntimeErrno::Success,
}
}
}
impl From<error::WeldCompileError> for WeldError {
fn from(err: error::WeldCompileError) -> WeldError {
WeldError::new(
CString::new(err.description()).unwrap(),
WeldRuntimeErrno::CompileError,
)
}
}
#[derive(Debug, Clone)]
pub struct WeldValue {
data: Data,
run: Option<RunId>,
context: Option<WeldContext>,
}
impl WeldValue {
pub fn new_from_data(data: Data) -> WeldValue {
WeldValue {
data,
run: None,
context: None,
}
}
pub fn data(&self) -> Data {
self.data
}
pub fn context(&self) -> Option<WeldContext> {
self.context.clone()
}
pub fn run_id(&self) -> Option<RunId> {
Some(0)
}
}
#[derive(Debug, Clone, Default)]
pub struct WeldConf {
dict: fnv::FnvHashMap<String, CString>,
}
impl WeldConf {
pub fn new() -> WeldConf {
WeldConf {
dict: fnv::FnvHashMap::default(),
}
}
pub fn set<K: Into<String>, V: Into<Vec<u8>>>(&mut self, key: K, value: V) {
self.dict.insert(key.into(), CString::new(value).unwrap());
}
pub fn get(&self, key: &str) -> Option<&CString> {
self.dict.get(key)
}
}
#[derive(Debug)]
pub struct WeldModule {
llvm_module: codegen::CompiledModule,
param_types: Vec<ast::Type>,
return_type: ast::Type,
module_id: Uuid,
}
impl WeldModule {
pub fn compile<S: AsRef<str>>(code: S, conf: &WeldConf) -> WeldResult<WeldModule> {
use self::ast::*;
let e2e_start = PreciseTime::now();
let mut stats = CompilationStats::new();
let conf = &mut ParsedConf::parse(conf)?;
let code = code.as_ref();
let uuid = Uuid::new_v4();
debug!("{:?}", conf);
let start = PreciseTime::now();
let program = syntax::parser::parse_program(code)?;
let end = PreciseTime::now();
stats
.weld_times
.push(("Parsing".to_string(), start.to(end)));
let mut expr = syntax::macro_processor::process_program(&program)?;
debug!("After macro substitution:\n{}\n", expr.pretty_print());
let unoptimized_code = expr.pretty_print();
info!(
"Compiling module with UUID={}, code\n{}",
uuid.to_hyphenated(),
unoptimized_code
);
nonfatal!(write_code(
&unoptimized_code,
DumpCodeFormat::Weld,
&conf.dump_code
));
let start = PreciseTime::now();
expr.uniquify()?;
let end = PreciseTime::now();
let mut uniquify_dur = start.to(end);
let start = PreciseTime::now();
expr.infer_types()?;
let end = PreciseTime::now();
stats
.weld_times
.push(("Type Inference".to_string(), start.to(end)));
debug!("After type inference:\n{}\n", expr.pretty_print());
optimizer::apply_passes(
&mut expr,
&conf.optimization_passes,
&mut stats,
conf.enable_experimental_passes,
)?;
let start = PreciseTime::now();
expr.uniquify()?;
let end = PreciseTime::now();
uniquify_dur = uniquify_dur + start.to(end);
stats
.weld_times
.push(("Uniquify outside Passes".to_string(), uniquify_dur));
debug!("Optimized Weld program:\n{}\n", expr.pretty_print());
let start = PreciseTime::now();
let mut sir_prog = sir::ast_to_sir(&expr)?;
let end = PreciseTime::now();
stats
.weld_times
.push(("AST to SIR".to_string(), start.to(end)));
debug!("SIR program:\n{}\n", &sir_prog);
let start = PreciseTime::now();
if conf.enable_sir_opt {
use crate::sir::optimizations;
info!("Applying SIR optimizations");
optimizations::fold_constants::fold_constants(&mut sir_prog)?;
optimizations::simplify_assignments::simplify_assignments(&mut sir_prog)?;
}
let end = PreciseTime::now();
debug!("Optimized SIR program:\n{}\n", &sir_prog);
stats
.weld_times
.push(("SIR Optimization".to_string(), start.to(end)));
nonfatal!(write_code(
expr.pretty_print(),
DumpCodeFormat::WeldOpt,
&conf.dump_code
));
nonfatal!(write_code(
sir_prog.to_string(),
DumpCodeFormat::SIR,
&conf.dump_code
));
let compiled_module = codegen::compile_program(&sir_prog, conf, &mut stats)?;
debug!("\n{}\n", stats.pretty_print());
let (param_types, return_type) =
if let Type::Function(ref param_tys, ref return_ty) = expr.ty {
(param_tys.clone(), *return_ty.clone())
} else {
unreachable!()
};
let end = PreciseTime::now();
let duration = e2e_start.to(end);
let us = duration.num_microseconds().unwrap_or(std::i64::MAX);
let e2e_ms: f64 = us as f64 / 1000.0;
info!(
"Compiled module with UUID={} in {} ms",
uuid.to_hyphenated(),
e2e_ms
);
Ok(WeldModule {
llvm_module: compiled_module,
param_types,
return_type,
module_id: uuid,
})
}
pub unsafe fn run(&self, context: &mut WeldContext, arg: &WeldValue) -> WeldResult<WeldValue> {
let start = PreciseTime::now();
let nworkers = context.context.borrow().threads();
let mem_limit = context.context.borrow().memory_limit();
let mut context_borrowed = context.context.borrow_mut();
let (raw, result) = {
let input = Box::new(codegen::WeldInputArgs {
input: arg.data as i64,
nworkers,
mem_limit,
run: context.context.as_ptr() as i64,
});
let ptr = Box::into_raw(input) as i64;
let raw = self.llvm_module.run(ptr) as *const codegen::WeldOutputArgs;
let result = (*raw).clone();
let _ = Box::from_raw(ptr as *mut codegen::WeldInputArgs);
(raw, result)
};
let value = WeldValue {
data: result.output as Data,
run: None,
context: Some(context.clone()),
};
let end = PreciseTime::now();
let duration = start.to(end);
let us = duration.num_microseconds().unwrap_or(std::i64::MAX);
let ms: f64 = us as f64 / 1000.0;
debug!(
"Ran module UUID={} in {} ms",
self.module_id.to_hyphenated(),
ms
);
if result.errno != WeldRuntimeErrno::Success {
let message =
CString::new(format!("Weld program failed with error {:?}", result.errno)).unwrap();
Err(WeldError::new(message, result.errno))
} else {
context_borrowed.free(raw as *mut u8);
Ok(value)
}
}
pub fn param_types(&self) -> Vec<ast::Type> {
self.param_types.clone()
}
pub fn return_type(&self) -> ast::Type {
self.return_type.clone()
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd)]
#[repr(u64)]
pub enum WeldLogLevel {
Off = 0,
Error,
Warn,
Info,
Debug,
Trace,
}
impl From<u64> for WeldLogLevel {
fn from(value: u64) -> WeldLogLevel {
use WeldLogLevel::*;
match value {
0 => Off,
1 => Error,
2 => Warn,
3 => Info,
4 => Debug,
_ => Trace,
}
}
}
impl fmt::Display for WeldLogLevel {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?}", self)
}
}
impl From<WeldLogLevel> for log::LogLevelFilter {
fn from(level: WeldLogLevel) -> log::LogLevelFilter {
match level {
WeldLogLevel::Error => log::LogLevelFilter::Error,
WeldLogLevel::Warn => log::LogLevelFilter::Warn,
WeldLogLevel::Info => log::LogLevelFilter::Info,
WeldLogLevel::Debug => log::LogLevelFilter::Debug,
WeldLogLevel::Trace => log::LogLevelFilter::Trace,
_ => log::LogLevelFilter::Off,
}
}
}
impl From<log::LogLevelFilter> for WeldLogLevel {
fn from(level: log::LogLevelFilter) -> WeldLogLevel {
match level {
log::LogLevelFilter::Error => WeldLogLevel::Error,
log::LogLevelFilter::Warn => WeldLogLevel::Warn,
log::LogLevelFilter::Info => WeldLogLevel::Info,
log::LogLevelFilter::Debug => WeldLogLevel::Debug,
log::LogLevelFilter::Trace => WeldLogLevel::Trace,
_ => WeldLogLevel::Off,
}
}
}
pub fn load_linked_library<S: AsRef<str>>(filename: S) -> WeldResult<()> {
codegen::load_library(filename.as_ref()).map_err(WeldError::from)
}
pub fn set_log_level(level: WeldLogLevel) {
use crate::util::colors::Color::*;
use crate::util::colors::*;
let filter: log::LogLevelFilter = level.into();
let format = |rec: &log::LogRecord<'_>| {
let prefix = match rec.level() {
log::LogLevel::Error => format_color(Red, "error"),
log::LogLevel::Warn => format_color(Yellow, "warn"),
log::LogLevel::Info => format_color(Yellow, "info"),
log::LogLevel::Debug => format_color(Green, "debug"),
log::LogLevel::Trace => format_color(Green, "trace"),
};
let date = chrono::Local::now().format("%T%.3f");
format!("[{}] {}: {}", prefix, date, rec.args())
};
let mut builder = env_logger::LogBuilder::new();
builder.format(format);
builder.filter(None, filter);
builder.init().unwrap_or(());
info!(
"Weld Version {} (Build {})",
VERSION.unwrap_or("unknown"),
BUILD
);
}