You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

167 lines
3.8 KiB

use super::*;
use std::{ffi::CString, io::Read};
#[derive(Error, Debug)]
enum ErrCapture {
#[error(transparent)]
Io(#[from] std::io::Error),
#[error(transparent)]
Utf8(#[from] std::string::FromUtf8Error),
}
pub struct IncompleteFunction<'a> {
value: LLVMValueRef,
block: LLVMBasicBlockRef,
contx: &'a Context,
in_context: bool,
}
pub struct Function {
value: LLVMValueRef,
}
impl<'a> IncompleteFunction<'a> {
pub fn new(c: &'a Context, name: ffi::Nts, ftyp: &FunctType) -> Self {
const ENT: ffi::Nts = c_str!("entry");
let value = unsafe { LLVMAddFunction(c.modul, name, ftyp.handle()) };
let block =
unsafe { LLVMAppendBasicBlockInContext(c.contx, value, ENT) };
Self { value, block, contx: c, in_context: false }
}
pub fn compile(&mut self, datum: &Datum) -> Result<LLVMValueRef, Err> {
if !self.in_context {
return Err(Err::Context);
}
unsafe {
match datum {
Datum::Null => {
Ok(LLVMGetUndef(self.contx.basic_type(Basic::Void).handle()))
}
Datum::Bool(value) => Ok(LLVMConstInt(
self.contx.basic_type(Basic::Bool).handle(),
*value as _,
0,
)),
Datum::Char(character) => Ok(LLVMConstInt(
self.contx.basic_type(Basic::Q32_0U).handle(),
*character as _,
0,
)),
Datum::Strn(_) => {
// TODO
Err(Err::Syntax)
}
Datum::Symb(name) => {
// TODO: must support more than global function symbols
let name = CString::new(name.as_bytes()).unwrap().into_raw();
let func = LLVMGetNamedFunction(self.contx.modul, name);
CString::from_raw(name);
Ok(func)
}
Datum::Numb(number) => Ok(LLVMConstInt(
self.contx.basic_type(Basic::Q64_0).handle(),
*number as _,
0,
)),
Datum::Cons { cdr, car } => {
// function call
let func = self.compile(car)?;
let mut args = Vec::new();
let mut cdr = cdr.as_ref();
while let Datum::Cons { cdr: cdr_next, car: car_next } = cdr {
args.push(self.compile(car_next)?);
cdr = cdr_next.as_ref();
}
Ok(LLVMBuildCall(
self.contx.build,
func,
args.as_mut_ptr(),
args.len() as u32,
c_str!(""),
))
}
}
}
}
pub fn begin_context(
&mut self,
block: impl FnOnce(&mut Self) -> Result<(), Err>,
) -> Result<(), Err> {
unsafe {
LLVMPositionBuilderAtEnd(self.contx.build, self.block);
}
self.in_context = true;
let res = block(self);
self.in_context = false;
res
}
pub fn complete(self) -> Result<Function, Err> {
// insert ret void
unsafe {
LLVMBuildRetVoid(self.contx.build);
}
// redirect stderr to a buffer so we can actually see what
// failed in LLVM...
let (snd, rcv) = std::sync::mpsc::channel();
let thd = {
let inp = shh::stderr();
std::thread::spawn(move || -> Result<String, ErrCapture> {
let mut inp = inp?;
let mut buf = Vec::new();
loop {
inp.read_to_end(&mut buf)?;
if rcv.try_recv().is_ok() {
break;
}
}
Ok(String::from_utf8(buf)?)
})
};
// verify
let verified = unsafe {
LLVMVerifyFunction(
self.value,
LLVMVerifierFailureAction::LLVMPrintMessageAction,
) != 1
};
// it's okay if the sender errors because the receiver will only
// be destroyed when the thread panics
let _ = snd.send(());
// if the thread panicked, then handle that. otherwise, if it
// failed to verify, get the output (or error) from the thread
if let Ok(res) = thd.join() {
if verified {
Ok(Function::new(self.value))
} else {
match res {
Ok(error_message) => Err(Err::VerifyFunc(error_message)),
Err(err) => Err(Err::VerifyFunc(err.to_string())),
}
}
} else {
Err(Err::VerifyFunc("Capture thread panicked".to_owned()))
}
}
}
impl Function {
pub fn new(value: LLVMValueRef) -> Self {
Self { value }
}
}
// EOF