Browse Source

more work on vire

master
Alison Watson 3 months ago
parent
commit
cdbb853c2b
5 changed files with 446 additions and 161 deletions
  1. +53
    -92
      source/framework/vire/compiler.rs
  2. +100
    -23
      source/framework/vire/compiler/function.rs
  3. +3
    -2
      source/framework/vire/compiler/nat.rs
  4. +164
    -16
      source/framework/vire/compiler/types.rs
  5. +126
    -28
      source/macros/lib.rs

+ 53
- 92
source/framework/vire/compiler.rs View File

@@ -1,19 +1,15 @@
pub(self) mod function;
pub(self) mod nat;
pub(self) mod types;
pub mod function;
pub mod nat;
pub mod types;

pub(self) use self::{function::*, types::*};
pub(self) use super::parser::Datum;
pub(self) use crate::ffi;
pub(self) use llvm_sys::{
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 Err {
#[error("Couldn't initialize JIT: {0}")]
@@ -22,6 +18,8 @@ pub enum Err {
VerifyFunc(String),
#[error("Invalid syntax")]
Syntax,
#[error("Not in compilation context")]
Context,
}

pub struct Context {
@@ -29,89 +27,22 @@ pub struct Context {
modul: LLVMModuleRef,
build: LLVMBuilderRef,
engin: LLVMExecutionEngineRef,
main: function::Function,
types: Vec<ComplType>,
funcs: Vec<Function>,
}

impl Context {
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 => 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(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!(""),
))
}
_ => 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, Err> {
const DEF_FUNCTIONS: &[unsafe fn(LLVMContextRef) -> DeclResult] = &[
const MODULE: ffi::Nts = c_str!("main");
const NAT_FUNCTIONS: &[fn(&mut Context)] = &[
nat::c_dbgi,
nat::c_addi,
nat::c_subi,
nat::c_muli,
nat::c_divi,
nat::c_remi,
nat::c_dbgi,
];

const MODULE: ffi::Nts = c_str!("main");

unsafe {
init_llvm();
}
@@ -120,23 +51,53 @@ impl Context {
let modul = unsafe { LLVMModuleCreateWithNameInContext(MODULE, contx) };
let build = unsafe { LLVMCreateBuilderInContext(contx) };
let engin = unsafe { jit_engine(modul)? };
let types = Vec::new();
let funcs = Vec::new();

for decl in DEF_FUNCTIONS.iter() {
unsafe {
let (name, ftyp, fdef) = decl(contx);
let func = LLVMAddFunction(modul, name, ftyp);
LLVMAddGlobalMapping(engin, func, fdef);
}
let mut c = Self { contx, modul, build, engin, types, funcs };

for ty in types::Basic::iter() {
c.types.push(types::ComplType::Basic(types::BasicType::new(ty)));
}

let main = function::Function::new(
contx,
modul,
for decl in NAT_FUNCTIONS.iter() {
decl(&mut c);
}

Ok(c)
}

pub fn basic_type(&self, ty: Basic) -> &ComplType {
&self.types[usize::from(u8::from(ty))]
}

pub fn compile_all(&self, datum: Vec<Datum>) -> Result<(), Err> {
let mut main = IncompleteFunction::new(
self,
c_str!("-main"),
func_type![(void) || -> void],
blonkus_ma::compl_type![self, || -> Void],
);

Ok(Self { contx, modul, build, engin, main })
main.begin_context(|f| {
for data in datum {
f.compile(&data)?;
}
Ok(())
})?;

let _main = main.complete()?;

Ok(())
}

pub fn execute(&self) -> Result<(), Err> {
unsafe {
let func: unsafe extern "C" fn() = std::mem::transmute(
LLVMGetFunctionAddress(self.engin, c_str!("-main")),
);
func();
}
Ok(())
}
}



+ 100
- 23
source/framework/vire/compiler/function.rs View File

@@ -1,5 +1,5 @@
use super::*;
use std::io::Read;
use std::{ffi::CString, io::Read};

#[derive(Error, Debug)]
enum ErrCapture {
@@ -9,34 +9,103 @@ enum ErrCapture {
Utf8(#[from] std::string::FromUtf8Error),
}

pub(super) struct Function {
pub struct IncompleteFunction<'a> {
value: LLVMValueRef,
block: LLVMBasicBlockRef,
contx: &'a Context,
in_context: bool,
}

impl Function {
pub(super) fn new(
contx: LLVMContextRef,
modul: LLVMModuleRef,
name: ffi::Nts,
ftyp: LLVMTypeRef,
) -> Self {
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(modul, name, ftyp) };
let block = unsafe { LLVMAppendBasicBlockInContext(contx, value, ENT) };
Self { value, block }
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(super) unsafe fn make_active(&self, build: LLVMBuilderRef) {
LLVMPositionBuilderAtEnd(build, self.block);
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(super) unsafe fn complete(
&self,
build: LLVMBuilderRef,
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
LLVMBuildRetVoid(build);
unsafe {
LLVMBuildRetVoid(self.contx.build);
}

// redirect stderr to a buffer so we can actually see what
// failed in LLVM...
@@ -61,10 +130,12 @@ impl Function {
};

// verify
let verified = LLVMVerifyFunction(
self.value,
LLVMVerifierFailureAction::LLVMPrintMessageAction,
) != 1;
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
@@ -74,7 +145,7 @@ impl Function {
// failed to verify, get the output (or error) from the thread
if let Ok(res) = thd.join() {
if verified {
Ok(())
Ok(Function::new(self.value))
} else {
match res {
Ok(error_message) => Err(Err::VerifyFunc(error_message)),
@@ -87,4 +158,10 @@ impl Function {
}
}

impl Function {
pub fn new(value: LLVMValueRef) -> Self {
Self { value }
}
}

// EOF

+ 3
- 2
source/framework/vire/compiler/nat.rs View File

@@ -1,16 +1,17 @@
use super::*;

use blonkus_ma::nat_call;

macro_rules! nat_binop {
( $id:literal , $name:ident , $op:tt ) => {
#[nat_call($id, (i64) |i64, i64| -> i64)]
#[nat_call($id, |Q64_0, Q64_0| -> Q64_0)]
fn $name(x: i64, y: i64) -> i64 {
x $op y
}
};
}

#[nat_call("dbgi", (void, i64) |i64| -> void)]
#[nat_call("dbgi", |Q64_0| -> Void)]
fn dbgi(n: i64) {
eprintln!("{}", n);
}


+ 164
- 16
source/framework/vire/compiler/types.rs View File

@@ -1,24 +1,172 @@
#![allow(dead_code)]

use super::*;

macro_rules! ctx_type {
( typ , $id:ident , $fn:ident $( , $arg:expr )? ) => {
pub(super) unsafe fn $id(context: LLVMContextRef) -> LLVMTypeRef {
$fn(context $(, $arg)?)
#[repr(u8)]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum Basic {
Void,
Bool,
Q8_0,
Q16_0,
Q32_0,
Q64_0,
Q8_0U,
Q16_0U,
Q32_0U,
Q64_0U,
Q24_8,
Q24_8U,
Q16,
Q16U,
}

impl From<Basic> for u8 {
fn from(ty: Basic) -> Self {
ty as Self
}
}

impl Basic {
pub fn iter() -> impl DoubleEndedIterator<Item = Self> {
(0..14).map(|it| unsafe { std::mem::transmute(it as u8) })
}
}

#[derive(Clone, Copy)]
pub struct BasicType {
btyp: Basic,
handle: LLVMTypeRef,
}

#[derive(Clone)]
pub struct ArrayType {
size: usize,
memb: Box<ComplType>,
handle: LLVMTypeRef,
}

#[derive(Clone)]
pub struct StrucType {
memb: Vec<ComplType>,
handle: LLVMTypeRef,
}

#[derive(Clone)]
pub struct FunctType {
args: Vec<ComplType>,
retn: Box<ComplType>,
handle: LLVMTypeRef,
}

#[derive(Clone)]
pub enum ComplType {
Basic(BasicType),
Array(ArrayType),
Struc(StrucType),
Funct(FunctType),
}

pub trait TypeHandle {
fn handle(&self) -> LLVMTypeRef;
}

impl std::fmt::Debug for dyn TypeHandle {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
unsafe {
let ptr = LLVMPrintTypeToString(self.handle());
let res = std::ffi::CStr::from_ptr(ptr).fmt(f);
LLVMDisposeMessage(ptr);
res
}
};
}
}

impl BasicType {
pub fn new(btyp: Basic) -> Self {
let handle = unsafe {
match btyp {
Basic::Void => LLVMVoidType(),
Basic::Bool => LLVMInt1Type(),
Basic::Q8_0 | Basic::Q8_0U => LLVMInt8Type(),
Basic::Q16_0 | Basic::Q16_0U | Basic::Q16 | Basic::Q16U => {
LLVMInt16Type()
}
Basic::Q32_0 | Basic::Q32_0U | Basic::Q24_8 | Basic::Q24_8U => {
LLVMInt32Type()
}
Basic::Q64_0 | Basic::Q64_0U => LLVMInt64Type(),
}
};
Self { btyp, handle }
}

pub fn btyp(&self) -> Basic {
self.btyp
}
}

impl ArrayType {
pub fn new(size: usize, memb: &ComplType) -> Self {
let memb = Box::new(memb.clone());
let handle = unsafe { LLVMArrayType(memb.handle(), size as _) };
Self { size, memb, handle }
}

pub fn size(&self) -> usize {
self.size
}

( int , $id:ident , $bits:expr ) => {
ctx_type!(typ, $id, LLVMIntTypeInContext, $bits);
};
pub fn memb(&self) -> &ComplType {
&self.memb
}
}

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);
impl FunctType {
pub fn new(args: &[&ComplType], retn: &ComplType) -> Self {
let mut arg_handles =
args.iter().map(|arg| arg.handle()).collect::<Vec<_>>();
let args = args.iter().map(|&arg| arg.clone()).collect();
let retn = Box::new(retn.clone());
let handle = unsafe {
LLVMFunctionType(
retn.handle(),
arg_handles.as_mut_ptr(),
arg_handles.len() as _,
0,
)
};
Self { args, retn, handle }
}
}

impl TypeHandle for BasicType {
fn handle(&self) -> LLVMTypeRef {
self.handle
}
}
impl TypeHandle for ArrayType {
fn handle(&self) -> LLVMTypeRef {
self.handle
}
}
impl TypeHandle for StrucType {
fn handle(&self) -> LLVMTypeRef {
self.handle
}
}
impl TypeHandle for FunctType {
fn handle(&self) -> LLVMTypeRef {
self.handle
}
}
impl TypeHandle for ComplType {
fn handle(&self) -> LLVMTypeRef {
match self {
Self::Basic(t) => t.handle(),
Self::Array(t) => t.handle(),
Self::Struc(t) => t.handle(),
Self::Funct(t) => t.handle(),
}
}
}

// EOF

+ 126
- 28
source/macros/lib.rs View File

@@ -9,18 +9,49 @@ use syn::{
Token,
};

struct FuncType {
typs: Punctuated<Ident, Token![,]>,
args: Punctuated<Ident, Token![,]>,
retn: Ident,
#[derive(Clone)]
struct BasicType {
ty: Ident,
}

impl Parse for FuncType {
#[derive(Clone)]
struct ArrayType {
size: syn::LitInt,
memb: Box<ComplType>,
}

#[derive(Clone)]
struct FunctType {
args: Punctuated<ComplType, Token![,]>,
retn: Box<ComplType>,
}

#[derive(Clone)]
enum ComplType {
BasicType(BasicType),
ArrayType(ArrayType),
FunctType(FunctType),
}

impl Parse for BasicType {
fn parse(input: ParseStream) -> syn::Result<Self> {
let typs;
syn::parenthesized!(typs in input);
let typs = Punctuated::parse_terminated(&typs)?;
Ok(Self { ty: input.parse()? })
}
}

impl Parse for ArrayType {
fn parse(input: ParseStream) -> syn::Result<Self> {
let inner;
syn::bracketed!(inner in input);
let memb = inner.parse()?;
input.parse::<Token![;]>()?;
let size = inner.parse()?;
Ok(Self { size, memb })
}
}

impl Parse for FunctType {
fn parse(input: ParseStream) -> syn::Result<Self> {
input.parse::<Token![|]>()?;

let mut args = Punctuated::new();
@@ -39,29 +70,89 @@ impl Parse for FuncType {
input.parse::<Token![->]>()?;

let retn = input.parse()?;
Ok(Self { typs, args, retn })
Ok(Self { args, retn })
}
}

impl Parse for ComplType {
fn parse(input: ParseStream) -> syn::Result<Self> {
if input.peek(Token![|]) {
let funct = input.parse()?;
Ok(Self::FunctType(funct))
} else if input.peek(syn::token::Bracket) {
let array = input.parse()?;
Ok(Self::ArrayType(array))
} else {
let basic = input.parse()?;
Ok(Self::BasicType(basic))
}
}
}

impl ToTokens for BasicType {
fn to_tokens(&self, tokens: &mut pm2::TokenStream) {
let ty = self.ty.clone();
tokens.extend(quote::quote! {
c.basic_type(Basic::#ty)
});
}
}

impl ToTokens for FuncType {
impl ToTokens for ArrayType {
fn to_tokens(&self, tokens: &mut pm2::TokenStream) {
let size = self.size.clone();
let memb = self.memb.clone();
tokens.extend(quote::quote! {
&ArrayType::new(#size, #memb)
});
}
}

impl ToTokens for FunctType {
fn to_tokens(&self, tokens: &mut pm2::TokenStream) {
let typ = self.typs.iter().collect::<Vec<_>>();
let ret = self.retn.clone();
let arg = self.args.iter().collect::<Vec<_>>();
let out = quote::quote! {
unsafe {
#(let #typ = types::#typ(contx);)*
let mut par = [#(#arg),*];
LLVMFunctionType(#ret, par.as_mut_ptr(), par.len() as _, 0)
}
};
tokens.extend(out);

tokens.extend(quote::quote! {
&FunctType::new(&[#(#arg),*], #ret)
});
}
}

impl ToTokens for ComplType {
fn to_tokens(&self, tokens: &mut pm2::TokenStream) {
match self {
Self::BasicType(basic) => basic.to_tokens(tokens),
Self::ArrayType(array) => array.to_tokens(tokens),
Self::FunctType(funct) => funct.to_tokens(tokens),
}
}
}

#[proc_macro]
pub fn func_type(input: pm::TokenStream) -> pm::TokenStream {
let expr = syn::parse_macro_input!(input as FuncType);
pub fn compl_type(input: pm::TokenStream) -> pm::TokenStream {
struct InputComplType {
contx: syn::ExprPath,
compl: ComplType,
}

impl Parse for InputComplType {
fn parse(input: ParseStream) -> syn::Result<Self> {
let contx = input.parse()?;
input.parse::<Token![,]>()?;
let compl = input.parse()?;
Ok(Self { contx, compl })
}
}

let InputComplType { contx, compl } =
syn::parse_macro_input!(input as InputComplType);
let expr = quote::quote! {
{
let c = #contx;
#compl
}
};

let mut output = pm2::TokenStream::new();
expr.to_tokens(&mut output);
@@ -112,15 +203,15 @@ pub fn nat_call(
) -> pm::TokenStream {
struct Args {
name: syn::LitStr,
func_type: FuncType,
funct_type: FunctType,
}

impl Parse for Args {
fn parse(input: ParseStream) -> syn::Result<Self> {
let name = input.parse()?;
input.parse::<Token![,]>()?;
let func_type = input.parse()?;
Ok(Self { name, func_type })
let funct_type = input.parse()?;
Ok(Self { name, funct_type })
}
}

@@ -128,11 +219,18 @@ pub fn nat_call(
i_name: Ident,
c_name: Ident,
name: syn::LitStr,
func_type: FuncType,
funct_type: FunctType,
) -> pm2::TokenStream {
quote::quote! {
pub(super) unsafe fn #c_name(contx: LLVMContextRef) -> DeclResult {
(c_str!(#name), #func_type, #i_name as _)
pub fn #c_name(c: &mut Context) {
let name = c_str!(#name);
let ftyp = #funct_type;
let fdef = #i_name as _;
unsafe {
let func = LLVMAddFunction(c.modul, name, ftyp.handle());
LLVMAddGlobalMapping(c.engin, func, fdef);
c.funcs.push(Function::new(func));
}
}
}
}
@@ -146,7 +244,7 @@ pub fn nat_call(

let mut output = pm2::TokenStream::new();
item.to_tokens(&mut output);
output.extend(to_decl_item(i_name, c_name, args.name, args.func_type));
output.extend(to_decl_item(i_name, c_name, args.name, args.funct_type));
output.into()
}



Loading…
Cancel
Save