use libc::{c_char, c_uint};
use ffi::prelude::{LLVMValueRef, LLVMModuleRef};
use ffi::analysis::LLVMVerifierFailureAction;
use ffi::{analysis, core, linker, LLVMModule};
use ffi::transforms::pass_manager_builder as builder;
use ffi::bit_writer as writer;
use ffi::bit_reader as reader;
use cbox::{CBox, CSemiBox};
use std::ffi::CString;
use std::iter::{Iterator, IntoIterator};
use std::io::{Error, ErrorKind};
use std::io::Result as IoResult;
use std::{env, fmt, mem};
use std::marker::PhantomData;
use std::path::Path;
use std::process::Command;
use buffer::MemoryBuffer;
use context::{Context, GetContext};
use value::{Function, Value};
use ty::Type;
use util;
pub struct Module;
native_ref!(&Module = LLVMModuleRef);
impl Module {
pub fn new<'a>(name: &str, context: &'a Context) -> CSemiBox<'a, Module> {
let c_name = CString::new(name).unwrap();
unsafe { CSemiBox::new(core::LLVMModuleCreateWithNameInContext(c_name.as_ptr(), context.into())) }
}
pub fn add_global<'a>(&'a self, name: &str, ty: &'a Type) -> &'a Value {
util::with_cstr(name, |ptr| unsafe {
core::LLVMAddGlobal(self.into(), ty.into(), ptr).into()
})
}
pub fn add_global_constant<'a>(&'a self, name: &str, val: &'a Value) -> &'a Value {
util::with_cstr(name, |ptr| unsafe {
let global = core::LLVMAddGlobal(self.into(), val.get_type().into(), ptr);
core::LLVMSetInitializer (global.into(), val.into());
global.into()
})
}
pub fn get_global<'a>(&'a self, name: &str) -> Option<&'a Value> {
util::with_cstr(name, |ptr| unsafe {
let ptr = core::LLVMGetNamedGlobal(self.into(), ptr);
util::ptr_to_null(ptr)
})
}
pub fn parse_bitcode<'a>(context: &'a Context, path: &str) -> Result<CSemiBox<'a, Module>, CBox<str>> {
unsafe {
let mut out = mem::uninitialized();
let mut err = mem::uninitialized();
let buf = try!(MemoryBuffer::new_from_file(path));
if reader::LLVMParseBitcodeInContext(context.into(), buf.as_ptr(), &mut out, &mut err) == 1 {
Err(CBox::new(err))
} else {
Ok(CSemiBox::new(out))
}
}
}
pub fn write_bitcode(&self, path: &str) -> IoResult<()> {
util::with_cstr(path, |cpath| unsafe {
if writer::LLVMWriteBitcodeToFile(self.into(), cpath) != 0 {
Err(Error::new(ErrorKind::Other, &format!("could not write to {}", path) as &str))
} else {
Ok(())
}
})
}
pub fn add_function<'a>(&'a self, name: &str, sig: &'a Type) -> &'a mut Function {
let c_name = CString::new(name).unwrap();
unsafe { core::LLVMAddFunction(self.into(), c_name.as_ptr(), sig.into()) }.into()
}
pub fn get_function<'a>(&'a self, name: &str) -> Option<&'a Function> {
let c_name = CString::new(name).unwrap();
unsafe {
let ty = core::LLVMGetNamedFunction(self.into(), c_name.as_ptr());
util::ptr_to_null(ty)
}
}
pub fn get_type<'a>(&'a self, name: &str) -> Option<&'a Type> {
let c_name = CString::new(name).unwrap();
unsafe {
let ty = core::LLVMGetTypeByName(self.into(), c_name.as_ptr());
util::ptr_to_null(ty)
}
}
pub fn clone<'a>(&'a self) -> CSemiBox<'a, Module> {
CSemiBox::new(unsafe { core::LLVMCloneModule(self.into()) })
}
pub fn optimize(&self, opt_level: usize, size_level: usize) {
unsafe {
let builder = builder::LLVMPassManagerBuilderCreate();
builder::LLVMPassManagerBuilderSetOptLevel(builder, opt_level as c_uint);
builder::LLVMPassManagerBuilderSetSizeLevel(builder, size_level as c_uint);
let pass_manager = core::LLVMCreatePassManager();
builder::LLVMPassManagerBuilderPopulateModulePassManager(builder, pass_manager);
builder::LLVMPassManagerBuilderDispose(builder);
core::LLVMRunPassManager(pass_manager, self.into());
}
}
pub fn get_target(&self) -> &str {
unsafe {
let target = core::LLVMGetTarget(self.into());
util::to_str(target as *mut c_char)
}
}
pub fn set_target(&self, target: &str) {
let c_target = CString::new(target).unwrap();
unsafe { core::LLVMSetTarget(self.into(), c_target.as_ptr()) }
}
pub fn verify(&self) -> Result<(), CBox<str>> {
unsafe {
let mut error = mem::uninitialized();
let action = LLVMVerifierFailureAction::LLVMReturnStatusAction;
if analysis::LLVMVerifyModule(self.into(), action, &mut error) == 1 {
Err(CBox::new(error))
} else {
Ok(())
}
}
}
pub fn compile(&self, path: &Path, opt_level: usize) -> IoResult<()> {
let dir = env::temp_dir();
let path = path.to_str().unwrap();
let mod_path = dir.join("module.bc");
let mod_path = mod_path.to_str().unwrap();
try!(self.write_bitcode(mod_path));
Command::new("llc")
.arg(&format!("-O={}", opt_level))
.arg("-filetype=obj")
.arg("-o").arg(path)
.arg(mod_path)
.spawn()
.map(|_| ())
}
pub fn link(&self, src: &Module) -> Result<(), CBox<str>> {
unsafe {
let dest = self.into();
let src = src.into();
let mut message = mem::uninitialized();
if linker::LLVMLinkModules(dest, src, linker::LLVMLinkerMode::LLVMLinkerPreserveSource, &mut message) == 1 {
Err(CBox::new(message))
} else {
Ok(())
}
}
}
pub fn link_destroy(&self, src: CSemiBox<Module>) -> Result<(), CBox<str>> {
unsafe {
let dest = self.into();
let src = src.as_ptr();
let mut message = mem::uninitialized();
if linker::LLVMLinkModules(dest, src, linker::LLVMLinkerMode::LLVMLinkerDestroySource, &mut message) == 1 {
Err(CBox::new(message))
} else {
Ok(())
}
}
}
}
impl<'a> IntoIterator for &'a Module {
type Item = &'a Function;
type IntoIter = Functions<'a>;
fn into_iter(self) -> Functions<'a> {
Functions {
value: unsafe { core::LLVMGetFirstFunction(self.into()) },
marker: PhantomData
}
}
}
get_context!(Module, LLVMGetModuleContext);
to_str!(Module, LLVMPrintModuleToString);
dispose!(Module, LLVMModule, core::LLVMDisposeModule);
#[derive(Copy, Clone)]
pub struct Functions<'a> {
value: LLVMValueRef,
marker: PhantomData<&'a ()>
}
impl<'a> Iterator for Functions<'a> {
type Item = &'a Function;
fn next(&mut self) -> Option<&'a Function> {
if self.value.is_null() {
None
} else {
let c_next = unsafe { core::LLVMGetNextFunction(self.value) };
self.value = c_next;
Some(self.value.into())
}
}
}