diff --git a/stage0/src/ast/attr.rs b/stage0/src/ast/attr.rs index eeacb7c..6351003 100644 --- a/stage0/src/ast/attr.rs +++ b/stage0/src/ast/attr.rs @@ -48,6 +48,10 @@ impl Attributes { self.config.as_ref() } + pub fn ext(&self) -> Option<&(AttributeName, Extern)> { + self.ext.as_ref() + } + pub fn repr(&self) -> Option<&(AttributeName, Representation)> { self.repr.as_ref() } diff --git a/stage0/src/ast/func.rs b/stage0/src/ast/func.rs index 883714a..59c37e8 100644 --- a/stage0/src/ast/func.rs +++ b/stage0/src/ast/func.rs @@ -1,12 +1,12 @@ -use super::{Attributes, Statement, Type}; -use crate::codegen::{Codegen, LlvmFunc, LlvmType, LlvmVoid}; -use crate::lexer::{FnKeyword, Identifier, SyntaxError}; +use super::{Attributes, Extern, Statement, Type}; +use crate::codegen::{BasicBlock, Builder, Codegen, LlvmFunc, LlvmType, LlvmVoid}; +use crate::lexer::{Identifier, SyntaxError}; +use std::borrow::Cow; use std::ffi::CString; /// A function. pub struct Function { attrs: Attributes, - def: FnKeyword, name: Identifier, params: Vec, ret: Option, @@ -16,7 +16,6 @@ pub struct Function { impl Function { pub fn new( attrs: Attributes, - def: FnKeyword, name: Identifier, params: Vec, ret: Option, @@ -24,7 +23,6 @@ impl Function { ) -> Self { Self { attrs, - def, name, params, ret, @@ -44,8 +42,14 @@ impl Function { } } + // Build function name. + let name = match self.attrs.ext() { + Some((_, Extern::C)) => Cow::Borrowed(self.name.value()), + None => Cow::Owned(cx.encode_name(container, self.name.value())), + }; + // Check if function already exists. - let name = CString::new(cx.encode_name(container, self.name.value())).unwrap(); + let name = CString::new(name.as_ref()).unwrap(); if LlvmFunc::get(cx, &name).is_some() { return Err(SyntaxError::new( @@ -85,11 +89,35 @@ impl Function { }; // Create a function. - let func = LlvmFunc::new(cx, name, ¶ms, ret); + let mut func = LlvmFunc::new(cx, name, ¶ms, ret); + + match &self.body { + Some(v) => Self::build_body(cx, &mut func, v), + None => { + if self.attrs.ext().is_none() { + return Err(SyntaxError::new( + self.name.span().clone(), + "a body is required for non-extern or non-abstract", + )); + } + } + } - // TODO: Build function body. Ok(Some(func)) } + + fn build_body<'a, 'b: 'a>( + cx: &'a Codegen<'b>, + func: &mut LlvmFunc<'a, 'b>, + stmts: &[Statement], + ) { + let mut bb = BasicBlock::new(cx); + let mut b = Builder::new(cx, &mut bb); + + b.ret_void(); + + func.append(bb); + } } /// A function parameter. diff --git a/stage0/src/ast/mod.rs b/stage0/src/ast/mod.rs index 9be8749..30a93dc 100644 --- a/stage0/src/ast/mod.rs +++ b/stage0/src/ast/mod.rs @@ -10,8 +10,7 @@ pub use self::ty::*; pub use self::using::*; use crate::lexer::{ - ClassKeyword, FnKeyword, Identifier, ImplKeyword, Lexer, StructKeyword, SyntaxError, Token, - UseKeyword, + ClassKeyword, Identifier, ImplKeyword, Lexer, StructKeyword, SyntaxError, Token, UseKeyword, }; use std::path::PathBuf; use thiserror::Error; @@ -342,8 +341,8 @@ impl SourceFile { match tok { Token::AttributeName(name) => attrs = Some(Attributes::parse(lex, name)?), - Token::FnKeyword(def) => { - functions.push(Self::parse_fn(lex, attrs.take().unwrap_or_default(), def)?); + Token::FnKeyword(_) => { + functions.push(Self::parse_fn(lex, attrs.take().unwrap_or_default())?); } Token::CloseCurly(_) => break, t => return Err(SyntaxError::new(t.span().clone(), "syntax error")), @@ -353,11 +352,7 @@ impl SourceFile { Ok(TypeImpl::new(def, ty, functions)) } - fn parse_fn( - lex: &mut Lexer, - attrs: Attributes, - def: FnKeyword, - ) -> Result { + fn parse_fn(lex: &mut Lexer, attrs: Attributes) -> Result { let name = lex.next_ident()?; // Parse parameters. @@ -416,7 +411,7 @@ impl SourceFile { }; let ret = match next { - Token::Semicolon(_) => return Ok(Function::new(attrs, def, name, params, None, None)), + Token::Semicolon(_) => return Ok(Function::new(attrs, name, params, None, None)), Token::OpenCurly(_) => None, Token::Colon(_) => { let ret = Self::parse_type(lex)?; @@ -432,7 +427,7 @@ impl SourceFile { match next { Token::Semicolon(_) => { - return Ok(Function::new(attrs, def, name, params, Some(ret), None)); + return Ok(Function::new(attrs, name, params, Some(ret), None)); } Token::OpenCurly(_) => {} t => { @@ -456,7 +451,7 @@ impl SourceFile { // Parse body. let body = Statement::parse_block(lex)?; - Ok(Function::new(attrs, def, name, params, ret, Some(body))) + Ok(Function::new(attrs, name, params, ret, Some(body))) } fn parse_type(lex: &mut Lexer) -> Result { diff --git a/stage0/src/codegen/block.rs b/stage0/src/codegen/block.rs new file mode 100644 index 0000000..c53fe8b --- /dev/null +++ b/stage0/src/codegen/block.rs @@ -0,0 +1,29 @@ +use super::Codegen; +use llvm_sys::core::{LLVMCreateBasicBlockInContext, LLVMDeleteBasicBlock}; +use llvm_sys::prelude::LLVMBasicBlockRef; +use std::marker::PhantomData; + +/// Encapsulate an LLVM basic block. +pub struct BasicBlock<'a, 'b: 'a> { + value: LLVMBasicBlockRef, + phantom: PhantomData<&'a Codegen<'b>>, +} + +impl<'a, 'b: 'a> BasicBlock<'a, 'b> { + pub fn new(cx: &'a Codegen<'b>) -> Self { + Self { + value: unsafe { LLVMCreateBasicBlockInContext(cx.llvm, b"\0".as_ptr() as _) }, + phantom: PhantomData, + } + } + + pub fn as_raw(&self) -> LLVMBasicBlockRef { + self.value + } +} + +impl<'a, 'b: 'a> Drop for BasicBlock<'a, 'b> { + fn drop(&mut self) { + unsafe { LLVMDeleteBasicBlock(self.value) }; + } +} diff --git a/stage0/src/codegen/builder.rs b/stage0/src/codegen/builder.rs new file mode 100644 index 0000000..27bac27 --- /dev/null +++ b/stage0/src/codegen/builder.rs @@ -0,0 +1,35 @@ +use super::{BasicBlock, Codegen}; +use llvm_sys::core::{ + LLVMBuildRetVoid, LLVMCreateBuilderInContext, LLVMDisposeBuilder, LLVMPositionBuilderAtEnd, +}; +use llvm_sys::prelude::LLVMBuilderRef; +use std::marker::PhantomData; + +/// Encapsulate an LLVM IR builder. +pub struct Builder<'a, 'b: 'a> { + raw: LLVMBuilderRef, + phantom: PhantomData<&'a Codegen<'b>>, +} + +impl<'a, 'b: 'a> Builder<'a, 'b> { + pub fn new(cx: &'a Codegen<'b>, block: &mut BasicBlock<'a, 'b>) -> Self { + let raw = unsafe { LLVMCreateBuilderInContext(cx.llvm) }; + + unsafe { LLVMPositionBuilderAtEnd(raw, block.as_raw()) }; + + Self { + raw, + phantom: PhantomData, + } + } + + pub fn ret_void(&mut self) { + unsafe { LLVMBuildRetVoid(self.raw) }; + } +} + +impl<'a, 'b: 'a> Drop for Builder<'a, 'b> { + fn drop(&mut self) { + unsafe { LLVMDisposeBuilder(self.raw) }; + } +} diff --git a/stage0/src/codegen/func.rs b/stage0/src/codegen/func.rs index f574135..d02bdcd 100644 --- a/stage0/src/codegen/func.rs +++ b/stage0/src/codegen/func.rs @@ -1,8 +1,11 @@ -use super::{Codegen, LlvmType}; -use llvm_sys::core::{LLVMAddFunction, LLVMFunctionType, LLVMGetNamedFunction}; +use super::{BasicBlock, Codegen, LlvmType}; +use llvm_sys::core::{ + LLVMAddFunction, LLVMAppendExistingBasicBlock, LLVMFunctionType, LLVMGetNamedFunction, +}; use llvm_sys::prelude::{LLVMTypeRef, LLVMValueRef}; use std::ffi::CStr; use std::marker::PhantomData; +use std::mem::forget; /// A function. pub struct LlvmFunc<'a, 'b: 'a> { @@ -47,4 +50,9 @@ impl<'a, 'b: 'a> LlvmFunc<'a, 'b> { phantom: PhantomData, } } + + pub fn append(&mut self, block: BasicBlock<'a, 'b>) { + unsafe { LLVMAppendExistingBasicBlock(self.value, block.as_raw()) }; + forget(block); + } } diff --git a/stage0/src/codegen/mod.rs b/stage0/src/codegen/mod.rs index 981ba7a..0f1713a 100644 --- a/stage0/src/codegen/mod.rs +++ b/stage0/src/codegen/mod.rs @@ -1,3 +1,5 @@ +pub use self::block::*; +pub use self::builder::*; pub use self::func::*; pub use self::resolver::*; pub use self::target::*; @@ -22,6 +24,8 @@ use llvm_sys::target_machine::{ use std::ffi::CStr; use std::ptr::{null, null_mut}; +mod block; +mod builder; mod func; mod resolver; mod target;