Browse Source

more work on compiler, use shh for better llvm error handling

master
Alison Watson 2 years ago
parent
commit
2aef36b082
  1. 11
      Cargo.lock
  2. 1
      Cargo.toml
  3. 110
      source/framework/vire/compiler.rs
  4. 90
      source/framework/vire/compiler/function.rs
  5. 10
      source/framework/vire/compiler/nat.rs
  6. 1
      source/framework/vire/compiler/types.rs
  7. 10
      source/framework/vire/parser.rs
  8. 16
      source/main_test/entry.rs

11
Cargo.lock generated

@ -29,6 +29,7 @@ dependencies = [
"memchr",
"sdl2-sys",
"serde",
"shh",
"smol_str",
"termcolor",
"thiserror",
@ -186,6 +187,16 @@ dependencies = [
"syn",
]
[[package]]
name = "shh"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5205eb079ac8be8ec77b7470aff4a050610d42c32819deee362ca414c926d3ab"
dependencies = [
"libc",
"winapi",
]
[[package]]
name = "smol_str"
version = "0.1.16"

1
Cargo.toml

@ -25,6 +25,7 @@ llvm-sys = "100"
memchr = "2.3"
sdl2-sys = "0.34"
serde = { version = "1.0", features = ["derive"] }
shh = "1.0"
smol_str = "0.1"
termcolor = { version = "1.1", optional = true }
thiserror = "1.0"

110
source/framework/vire/compiler.rs

@ -1,19 +1,25 @@
pub(self) mod function;
pub(self) mod nat;
pub(self) mod types;
pub(self) use crate::ffi;
pub(self) use llvm_sys::{
core::*, execution_engine::*, prelude::*, target::*, target_machine::*,
analysis::*, core::*, execution_engine::*, prelude::*, target::*,
target_machine::*,
};
use super::parser::Datum;
use blonkus_ma::func_type;
use std::ffi::CString;
pub(self) type DeclResult = (ffi::Nts, LLVMTypeRef, *mut std::ffi::c_void);
#[derive(Error, Debug)]
pub enum Error {
pub enum Err {
#[error("Couldn't initialize JIT: {0}")]
JitInit(String),
#[error("Verifying function failed: {0}")]
VerifyFunc(String),
#[error("Invalid syntax")]
Syntax,
}
@ -23,31 +29,78 @@ pub struct Context {
modul: LLVMModuleRef,
build: LLVMBuilderRef,
engin: LLVMExecutionEngineRef,
main: function::Function,
}
impl Context {
/*
pub fn compile(&self, datum: Datum) -> Result<(), Error> {
pub fn execute(&self) -> Result<(), Err> {
unsafe {
let func: unsafe extern "C" fn() = std::mem::transmute(
LLVMGetFunctionAddress(self.engin, c_str!("-main")),
);
func();
}
Ok(())
}
unsafe fn compile(&self, datum: &Datum) -> Result<LLVMValueRef, Err> {
match datum {
//Datum::Null,
//Datum::Bool(bool),
//Datum::Char(char),
//Datum::Numb(i64),
Datum::Null => Ok(LLVMGetUndef(types::void(self.contx))),
Datum::Bool(value) => {
Ok(LLVMConstInt(types::i1(self.contx), *value as _, 0))
}
Datum::Char(character) => {
Ok(LLVMConstInt(types::i32(self.contx), *character as _, 0))
}
//Datum::Strn(String),
//Datum::Symb(String),
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.modul, name);
CString::from_raw(name);
Ok(func)
}
Datum::Numb(number) => {
Ok(LLVMConstInt(types::i64(self.contx), *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.build,
func,
args.as_mut_ptr(),
args.len() as u32,
c_str!(""),
))
}
_ => {
// compiles to no code
Ok(())
_ => Err(Err::Syntax),
}
}
pub fn compile_all(&self, datum: Vec<Datum>) -> Result<(), Err> {
unsafe {
self.main.make_active(self.build);
for data in datum {
self.compile(&data)?;
}
self.main.complete(self.build)?;
}
Ok(())
}
*/
pub fn new() -> Result<Self, Error> {
pub fn new() -> Result<Self, Err> {
const DEF_FUNCTIONS: &[unsafe fn(LLVMContextRef) -> DeclResult] = &[
nat::c_addi,
nat::c_subi,
@ -57,22 +110,33 @@ impl Context {
nat::c_dbgi,
];
const MODULE: ffi::Nts = c_str!("main");
unsafe {
init_llvm();
}
let contx = LLVMContextCreate();
let modul = LLVMModuleCreateWithNameInContext(c_str!("test"), contx);
let build = LLVMCreateBuilderInContext(contx);
let engin = jit_engine(modul)?;
let contx = unsafe { LLVMContextCreate() };
let modul = unsafe { LLVMModuleCreateWithNameInContext(MODULE, contx) };
let build = unsafe { LLVMCreateBuilderInContext(contx) };
let engin = unsafe { jit_engine(modul)? };
for decl in DEF_FUNCTIONS.iter() {
for decl in DEF_FUNCTIONS.iter() {
unsafe {
let (name, ftyp, fdef) = decl(contx);
let func = LLVMAddFunction(modul, name, ftyp);
LLVMAddGlobalMapping(engin, func, fdef);
}
Ok(Self { contx, modul, build, engin })
}
let main = function::Function::new(
contx,
modul,
c_str!("-main"),
func_type![(void) || -> void],
);
Ok(Self { contx, modul, build, engin, main })
}
}
@ -88,7 +152,7 @@ impl Drop for Context {
unsafe fn jit_engine(
modul: LLVMModuleRef,
) -> Result<LLVMExecutionEngineRef, Error> {
) -> Result<LLVMExecutionEngineRef, Err> {
let mut opt = LLVMMCJITCompilerOptions {
OptLevel: LLVMCodeGenOptLevel::LLVMCodeGenLevelAggressive as _,
CodeModel: LLVMCodeModel::LLVMCodeModelJITDefault,
@ -110,7 +174,7 @@ unsafe fn jit_engine(
if res != 0 {
let msg =
ffi::cstr_from_ptr(msg).unwrap_or("Couldn't parse error").to_owned();
Err(Error::JitInit(msg))
Err(Err::JitInit(msg))
} else {
Ok(jit)
}

90
source/framework/vire/compiler/function.rs

@ -0,0 +1,90 @@
use super::*;
use std::io::Read;
#[derive(Error, Debug)]
enum ErrCapture {
#[error(transparent)]
Io(#[from] std::io::Error),
#[error(transparent)]
Utf8(#[from] std::string::FromUtf8Error),
}
pub(super) struct Function {
value: LLVMValueRef,
block: LLVMBasicBlockRef,
}
impl Function {
pub(super) fn new(
contx: LLVMContextRef,
modul: LLVMModuleRef,
name: ffi::Nts,
ftyp: LLVMTypeRef,
) -> Self {
const ENT: ffi::Nts = c_str!("entry");
let value = unsafe { LLVMAddFunction(modul, name, ftyp) };
let block = unsafe { LLVMAppendBasicBlockInContext(contx, value, ENT) };
Self { value, block }
}
pub(super) unsafe fn make_active(&self, build: LLVMBuilderRef) {
LLVMPositionBuilderAtEnd(build, self.block);
}
pub(super) unsafe fn complete(
&self,
build: LLVMBuilderRef,
) -> Result<(), Err> {
// insert ret void
LLVMBuildRetVoid(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 = 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(())
} 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()))
}
}
}
// EOF

10
source/framework/vire/compiler/nat.rs

@ -15,10 +15,10 @@ fn dbgi(n: i64) {
eprintln!("{}", n);
}
nat_binop!("+i", addi, x + y);
nat_binop!("-i", subi, x - y);
nat_binop!("*i", muli, x * y);
nat_binop!("/i", divi, x / y);
nat_binop!("%i", remi, x % y);
nat_binop!("i+", addi, x + y);
nat_binop!("i-", subi, x - y);
nat_binop!("i*", muli, x * y);
nat_binop!("i/", divi, x / y);
nat_binop!("i%", remi, x % y);
// EOF

1
source/framework/vire/compiler/types.rs

@ -18,6 +18,7 @@ ctx_type!(int, i64, 64);
ctx_type!(int, i32, 32);
ctx_type!(int, i16, 16);
ctx_type!(int, i8, 8);
ctx_type!(int, i1, 1);
ctx_type!(typ, void, LLVMVoidTypeInContext);
// EOF

10
source/framework/vire/parser.rs

@ -18,6 +18,16 @@ pub enum Datum {
}
impl Datum {
pub fn parse_all(mut tki: TokenIter) -> Result<Vec<Self>, Err> {
let mut datum = Vec::new();
while tki.peek().is_some() {
datum.push(Self::parse(&mut tki)?);
}
Ok(datum)
}
pub fn parse(tki: &mut TokenIter) -> Result<Self, Err> {
match tki.err_next()? {
// atoms

16
source/main_test/entry.rs

@ -352,14 +352,18 @@ pub fn run(
let mut vfs = vfs::Vfs::default();
vfs.add_arc(vfs::Arc::read_path("testres".as_ref())?);
//vire::compiler::Context::new()?;
let ctx = vire::compiler::Context::new()?;
dbg!(vire::parser::Datum::parse(dbg!(
&mut vire::parser::tki::TokenIter::from_data(
let datum = vire::parser::Datum::parse_all(
vire::parser::tki::TokenIter::from_data(
"main.vire.gz",
vfs.get("main.vire.gz")?.text()?
)?
))?);
vfs.get("main.vire.gz")?.text()?,
)?,
)?;
ctx.compile_all(datum)?;
ctx.execute()?;
/*
let concur_frames = conf.render.concurrent_frames.into();

Loading…
Cancel
Save