Skip to content

Commit

Permalink
Initializes codegen for functions
Browse files Browse the repository at this point in the history
  • Loading branch information
ultimaweapon committed Sep 16, 2023
1 parent 43695e3 commit 09ee5dd
Show file tree
Hide file tree
Showing 12 changed files with 399 additions and 77 deletions.
56 changes: 55 additions & 1 deletion stage0/src/ast/func.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use super::{Attribute, Statement, Type};
use crate::lexer::{FnKeyword, Identifier};
use crate::codegen::{Codegen, LlvmFunc, LlvmType, LlvmVoid};
use crate::lexer::{FnKeyword, Identifier, SyntaxError};
use std::ffi::CString;

/// A function.
pub struct Function {
Expand Down Expand Up @@ -29,6 +31,58 @@ impl Function {
body,
}
}

pub fn build<'a, 'b: 'a>(
&self,
cx: &'a Codegen<'b>,
container: &str,
) -> Result<LlvmFunc<'a, 'b>, SyntaxError> {
// Check if function already exists.
let name = CString::new(cx.encode_name(container, self.name.value())).unwrap();

if LlvmFunc::get(cx, &name).is_some() {
return Err(SyntaxError::new(
self.name.span().clone(),
"multiple definition of the same name",
));
}

// Get params.
let mut params = Vec::<LlvmType<'a, 'b>>::new();

for p in &self.params {
let ty = match p.ty.build(cx) {
Some(v) => v,
None => {
return Err(SyntaxError::new(
p.ty.name().span(),
"function parameter cannot be a never type",
));
}
};

params.push(ty);
}

// Get return type.
let mut never = false;
let ret = match &self.ret {
Some(v) => match v.build(cx) {
Some(v) => v,
None => {
never = true;
LlvmType::Void(LlvmVoid::new(cx))
}
},
None => LlvmType::Void(LlvmVoid::new(cx)),
};

// Create a function.
let func = LlvmFunc::new(cx, name, &params, ret);

// TODO: Build function body.
Ok(func)
}
}

/// A function parameter.
Expand Down
15 changes: 12 additions & 3 deletions stage0/src/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pub use self::expr::*;
pub use self::func::*;
pub use self::imp::*;
pub use self::node::*;
pub use self::path::*;
pub use self::stmt::*;
pub use self::struc::*;
pub use self::ty::*;
Expand All @@ -21,6 +22,7 @@ mod expr;
mod func;
mod imp;
mod node;
mod path;
mod stmt;
mod struc;
mod ty;
Expand Down Expand Up @@ -55,6 +57,10 @@ impl SourceFile {
Ok(file)
}

pub fn path(&self) -> &std::path::Path {
&self.path
}

pub fn ty(&self) -> Option<&TypeDefinition> {
self.ty.as_ref()
}
Expand Down Expand Up @@ -843,7 +849,10 @@ impl SourceFile {

loop {
match lex.next()? {
Some(Token::FullStop(_)) => fqtn.push(ident),
Some(Token::FullStop(v)) => {
fqtn.push(Token::FullStop(v));
fqtn.push(Token::Identifier(ident));
}
Some(_) => {
lex.undo();
break;
Expand All @@ -865,9 +874,9 @@ impl SourceFile {
};
}

fqtn.push(ident);
fqtn.push(Token::Identifier(ident));

TypeName::Ident(fqtn)
TypeName::Ident(Path::new(fqtn))
}
t => return Err(SyntaxError::new(t.span().clone(), "invalid type")),
};
Expand Down
34 changes: 34 additions & 0 deletions stage0/src/ast/path.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
use crate::lexer::{Span, Token};

/// A path of identifier (e.g. `foo.bar.Foo`).
pub struct Path {
components: Vec<Token>,
}

impl Path {
pub fn new(components: Vec<Token>) -> Self {
assert!(!components.is_empty());
assert!(components.last().unwrap().is_identifier());

for i in 0..components.len() {
if i % 2 == 0 {
assert!(components[i].is_identifier());
} else {
assert!(components[i].is_full_stop());
}
}

Self { components }
}

pub fn span(&self) -> Span {
let mut iter = self.components.iter();
let mut span = iter.next().unwrap().span().clone();

for s in iter {
span = &span + s.span();
}

span
}
}
23 changes: 20 additions & 3 deletions stage0/src/ast/ty.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use super::Path;
use crate::codegen::{Codegen, LlvmType, LlvmVoid};
use crate::lexer::{Asterisk, CloseParenthesis, ExclamationMark, Identifier, OpenParenthesis};
use crate::lexer::{Asterisk, CloseParenthesis, ExclamationMark, OpenParenthesis, Span};

/// A type of something (e.g. variable).
pub struct Type {
Expand All @@ -12,19 +13,35 @@ impl Type {
Self { prefixes, name }
}

pub fn build<'a>(&self, cx: &'a Codegen) -> Option<LlvmType<'a>> {
pub fn name(&self) -> &TypeName {
&self.name
}

pub fn build<'a, 'b: 'a>(&self, cx: &'a Codegen<'b>) -> Option<LlvmType<'a, 'b>> {
let mut ty = match &self.name {
TypeName::Unit(_, _) => Some(LlvmType::Void(LlvmVoid::new(cx))),
TypeName::Never(_) => None,
TypeName::Ident(_) => todo!(),
};

// TODO: Resolve pointers.
ty
}
}

/// Name of a [`Type`].
pub enum TypeName {
Unit(OpenParenthesis, CloseParenthesis),
Never(ExclamationMark),
Ident(Vec<Identifier>),
Ident(Path),
}

impl TypeName {
pub fn span(&self) -> Span {
match self {
TypeName::Unit(o, c) => o.span() + c.span(),
TypeName::Never(v) => v.span().clone(),
TypeName::Ident(v) => v.span(),
}
}
}
50 changes: 50 additions & 0 deletions stage0/src/codegen/func.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
use super::{Codegen, LlvmType};
use llvm_sys::core::{LLVMAddFunction, LLVMFunctionType, LLVMGetNamedFunction};
use llvm_sys::prelude::{LLVMTypeRef, LLVMValueRef};
use std::ffi::CStr;
use std::marker::PhantomData;

/// A function.
pub struct LlvmFunc<'a, 'b: 'a> {
value: LLVMValueRef,
phantom: PhantomData<&'a Codegen<'b>>,
}

impl<'a, 'b: 'a> LlvmFunc<'a, 'b> {
pub fn get<N: AsRef<CStr>>(cx: &'a Codegen<'b>, name: N) -> Option<Self> {
let name = name.as_ref();
let value = unsafe { LLVMGetNamedFunction(cx.module, name.as_ptr()) };

if value.is_null() {
None
} else {
Some(Self {
value,
phantom: PhantomData,
})
}
}

pub fn new<N: AsRef<CStr>>(
cx: &'a Codegen<'b>,
name: N,
params: &[LlvmType<'a, 'b>],
ret: LlvmType<'a, 'b>,
) -> Self {
let name = name.as_ref();
let mut params: Vec<LLVMTypeRef> = params.iter().map(|p| p.as_raw()).collect();
let ty = unsafe {
LLVMFunctionType(
ret.as_raw(),
params.as_mut_ptr(),
params.len().try_into().unwrap(),
0,
)
};

Self {
value: unsafe { LLVMAddFunction(cx.module, name.as_ptr(), ty) },
phantom: PhantomData,
}
}
}
42 changes: 37 additions & 5 deletions stage0/src/codegen/mod.rs
Original file line number Diff line number Diff line change
@@ -1,31 +1,63 @@
pub use self::func::*;
pub use self::ty::*;

use crate::pkg::PackageVersion;
use llvm_sys::core::{
LLVMContextCreate, LLVMContextDispose, LLVMDisposeModule, LLVMModuleCreateWithNameInContext,
};
use llvm_sys::prelude::{LLVMContextRef, LLVMModuleRef};
use std::ffi::CStr;

mod func;
mod ty;

/// A context for code generation.
///
/// Each [`Codegen`] can output only one binary.
pub struct Codegen {
pub struct Codegen<'a> {
module: LLVMModuleRef,
llvm: LLVMContextRef,
pkg: &'a str,
version: &'a PackageVersion,
}

impl Codegen {
pub fn new<M: AsRef<CStr>>(module: M) -> Self {
impl<'a> Codegen<'a> {
pub fn new<M: AsRef<CStr>>(pkg: &'a str, version: &'a PackageVersion, module: M) -> Self {
let llvm = unsafe { LLVMContextCreate() };
let module = unsafe { LLVMModuleCreateWithNameInContext(module.as_ref().as_ptr(), llvm) };

Self { module, llvm }
Self {
module,
llvm,
pkg,
version,
}
}

pub fn encode_name(&self, container: &str, name: &str) -> String {
// TODO: Create a mangleg name according to Itanium C++ ABI.
// https://itanium-cxx-abi.github.io/cxx-abi/abi.html might be useful.
if self.version.major() == 0 {
format!(
"{}::0.{}::{}.{}",
self.pkg,
self.version.minor(),
container,
name
)
} else {
format!(
"{}::{}::{}.{}",
self.pkg,
self.version.major(),
container,
name
)
}
}
}

impl Drop for Codegen {
impl<'a> Drop for Codegen<'a> {
fn drop(&mut self) {
unsafe { LLVMDisposeModule(self.module) };
unsafe { LLVMContextDispose(self.llvm) };
Expand Down
44 changes: 27 additions & 17 deletions stage0/src/codegen/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,30 @@ use llvm_sys::prelude::LLVMTypeRef;
use std::marker::PhantomData;

/// Encapsulate an LLVM type.
pub enum LlvmType<'a> {
Void(LlvmVoid<'a>),
U8(LlvmU8<'a>),
Ptr(LlvmPtr<'a>),
pub enum LlvmType<'a, 'b: 'a> {
Void(LlvmVoid<'a, 'b>),
U8(LlvmU8<'a, 'b>),
Ptr(LlvmPtr<'a, 'b>),
}

impl<'a, 'b: 'a> LlvmType<'a, 'b> {
pub fn as_raw(&self) -> LLVMTypeRef {
match self {
Self::Void(v) => v.ty,
Self::U8(v) => v.ty,
Self::Ptr(v) => v.ty,
}
}
}

/// An unit type.
pub struct LlvmVoid<'a> {
pub struct LlvmVoid<'a, 'b: 'a> {
ty: LLVMTypeRef,
phantom: PhantomData<&'a Codegen>,
phantom: PhantomData<&'a Codegen<'b>>,
}

impl<'a> LlvmVoid<'a> {
pub fn new(cx: &'a Codegen) -> Self {
impl<'a, 'b: 'a> LlvmVoid<'a, 'b> {
pub fn new(cx: &'a Codegen<'b>) -> Self {
Self {
ty: unsafe { LLVMVoidTypeInContext(cx.llvm) },
phantom: PhantomData,
Expand All @@ -26,13 +36,13 @@ impl<'a> LlvmVoid<'a> {
}

/// A `u8` type.
pub struct LlvmU8<'a> {
pub struct LlvmU8<'a, 'b: 'a> {
ty: LLVMTypeRef,
phantom: PhantomData<&'a Codegen>,
phantom: PhantomData<&'a Codegen<'b>>,
}

impl<'a> LlvmU8<'a> {
pub fn new(cx: &'a Codegen) -> Self {
impl<'a, 'b: 'a> LlvmU8<'a, 'b> {
pub fn new(cx: &'a Codegen<'b>) -> Self {
Self {
ty: unsafe { LLVMInt8TypeInContext(cx.llvm) },
phantom: PhantomData,
Expand All @@ -41,14 +51,14 @@ impl<'a> LlvmU8<'a> {
}

/// A pointer to something.
pub struct LlvmPtr<'a> {
pub struct LlvmPtr<'a, 'b: 'a> {
ty: LLVMTypeRef,
pointee: Box<LlvmType<'a>>,
phantom: PhantomData<&'a Codegen>,
pointee: Box<LlvmType<'a, 'b>>,
phantom: PhantomData<&'a Codegen<'b>>,
}

impl<'a> LlvmPtr<'a> {
pub fn new(cx: &'a Codegen, pointee: LlvmType<'a>) -> Self {
impl<'a, 'b: 'a> LlvmPtr<'a, 'b> {
pub fn new(cx: &'a Codegen<'b>, pointee: LlvmType<'a, 'b>) -> Self {
Self {
ty: unsafe { LLVMPointerTypeInContext(cx.llvm, 0) },
pointee: Box::new(pointee),
Expand Down
Loading

0 comments on commit 09ee5dd

Please sign in to comment.