Compare commits

...

5 Commits

Author SHA1 Message Date
233ddb60f7 Fix struct gep return type, refactor a bit 2025-07-16 17:53:41 +03:00
2a879b5ef4 Implement codegen for structs 2025-07-16 17:08:15 +03:00
d1a37058df Fix warnings 2025-07-16 16:16:58 +03:00
29996c4a30 Add builder methods to create custom types 2025-07-16 16:16:18 +03:00
31185d921e Properly implement structs in lib 2025-07-16 16:11:45 +03:00
10 changed files with 439 additions and 199 deletions

View File

@ -4,8 +4,8 @@
use std::{cell::RefCell, rc::Rc};
use crate::{
BlockData, ConstValue, FunctionData, Instr, InstructionData, ModuleData, TerminatorKind, Type,
TypeData, util::match_types,
BlockData, CustomTypeKind, FunctionData, Instr, InstructionData, ModuleData, NamedStruct,
TerminatorKind, Type, TypeData, util::match_types,
};
#[derive(Clone, Hash, Copy, PartialEq, Eq)]
@ -80,6 +80,16 @@ impl Builder {
value
}
pub(crate) unsafe fn add_type(&self, mod_val: &ModuleValue, data: TypeData) -> TypeValue {
unsafe {
let mut modules = self.modules.borrow_mut();
let module = modules.get_unchecked_mut(mod_val.0);
let value = TypeValue(module.value, module.types.len());
module.types.push(TypeHolder { value, data });
value
}
}
pub(crate) unsafe fn add_function(
&self,
mod_val: &ModuleValue,
@ -213,6 +223,18 @@ impl Builder {
}
}
pub(crate) unsafe fn type_data(&self, value: &TypeValue) -> TypeData {
unsafe {
self.modules
.borrow()
.get_unchecked(value.0.0)
.types
.get_unchecked(value.1)
.data
.clone()
}
}
pub(crate) fn find_module<'ctx>(&'ctx self, value: ModuleValue) -> ModuleHolder {
unsafe { self.modules.borrow().get_unchecked(value.0).clone() }
}
@ -222,16 +244,15 @@ impl Builder {
}
pub fn check_instruction(&self, instruction: &InstructionValue) -> Result<(), ()> {
use super::Instr::*;
unsafe {
match self.instr_data(&instruction).kind {
Param(_) => Ok(()),
Constant(_) => Ok(()),
Add(lhs, rhs) => match_types(&lhs, &rhs, &self).map(|_| ()),
Sub(lhs, rhs) => match_types(&lhs, &rhs, &self).map(|_| ()),
Mult(lhs, rhs) => match_types(&lhs, &rhs, &self).map(|_| ()),
And(lhs, rhs) => match_types(&lhs, &rhs, &self).map(|_| ()),
ICmp(_, lhs, rhs) => {
Instr::Param(_) => Ok(()),
Instr::Constant(_) => Ok(()),
Instr::Add(lhs, rhs) => match_types(&lhs, &rhs, &self).map(|_| ()),
Instr::Sub(lhs, rhs) => match_types(&lhs, &rhs, &self).map(|_| ()),
Instr::Mult(lhs, rhs) => match_types(&lhs, &rhs, &self).map(|_| ()),
Instr::And(lhs, rhs) => match_types(&lhs, &rhs, &self).map(|_| ()),
Instr::ICmp(_, lhs, rhs) => {
let t = match_types(&lhs, &rhs, self)?;
if t.comparable() {
Ok(())
@ -239,7 +260,7 @@ impl Builder {
Err(()) // TODO error: Types not comparable
}
}
FunctionCall(fun, params) => {
Instr::FunctionCall(fun, params) => {
let param_types = self.function_data(&fun).params;
if param_types.len() != params.len() {
return Err(()); // TODO error: invalid amount of params
@ -251,7 +272,7 @@ impl Builder {
}
Ok(())
}
Phi(vals) => {
Instr::Phi(vals) => {
let mut iter = vals.iter();
// TODO error: Phi must contain at least one item
@ -265,40 +286,53 @@ impl Builder {
}
Ok(())
}
Alloca(_, _) => Ok(()),
Load(ptr, load_ty) => {
if let Ok(ptr_ty) = ptr.get_type(&self) {
if let Type::Ptr(ptr_ty_inner) = ptr_ty {
if *ptr_ty_inner == load_ty {
Ok(())
} else {
Err(())
}
} else {
Err(())
}
} else {
Err(())
}
}
Store(ptr, _) => {
if let Ok(ty) = ptr.get_type(&self) {
if let Type::Ptr(_) = ty {
Instr::Alloca(_, _) => Ok(()),
Instr::Load(ptr, load_ty) => {
let ptr_ty = ptr.get_type(&self)?;
if let Type::Ptr(ptr_ty_inner) = ptr_ty {
if *ptr_ty_inner == load_ty {
Ok(())
} else {
Err(())
Err(()) // TODO error: inner type mismatch
}
} else {
Err(())
Err(()) // TODO error: not a pointer
}
}
ArrayAlloca(_, _) => Ok(()),
GetElemPtr(arr, _) => {
let arr_ty = arr.get_type(&self)?;
if let Type::Ptr(_) = arr_ty {
Instr::Store(ptr, _) => {
let ty = ptr.get_type(&self)?;
if let Type::Ptr(_) = ty {
Ok(())
} else {
Err(())
Err(()) // TODO error: not a pointer
}
}
Instr::ArrayAlloca(_, _) => Ok(()),
Instr::GetElemPtr(ptr_val, _) => {
let ptr_ty = ptr_val.get_type(&self)?;
if let Type::Ptr(_) = ptr_ty {
Ok(())
} else {
Err(()) // TODO error: not a pointer
}
}
Instr::GetStructElemPtr(ptr_val, idx) => {
let ptr_ty = ptr_val.get_type(&self)?;
if let Type::Ptr(ty) = ptr_ty {
if let Type::CustomType(val) = *ty {
match self.type_data(&val).kind {
CustomTypeKind::NamedStruct(NamedStruct(_, fields)) => {
if fields.len() <= idx as usize {
return Err(()); // TODO error: no such field
}
}
}
Ok(())
} else {
Err(()) // TODO error: not a struct
}
} else {
Err(()) // TODO error: not a pointer
}
}
}
@ -339,102 +373,3 @@ impl Builder {
}
}
}
impl InstructionValue {
pub(crate) fn get_type(&self, builder: &Builder) -> Result<Type, ()> {
use Instr::*;
unsafe {
match &builder.instr_data(self).kind {
Param(nth) => builder
.function_data(&self.0.0)
.params
.get(*nth)
.cloned()
.ok_or(()),
Constant(c) => Ok(c.get_type()),
Add(lhs, rhs) => match_types(lhs, rhs, &builder),
Sub(lhs, rhs) => match_types(lhs, rhs, &builder),
Mult(lhs, rhs) => match_types(lhs, rhs, &builder),
And(lhs, rhs) => match_types(lhs, rhs, &builder),
ICmp(_, _, _) => Ok(Type::Bool),
FunctionCall(function_value, _) => Ok(builder.function_data(function_value).ret),
Phi(values) => values.first().ok_or(()).and_then(|v| v.get_type(&builder)),
Alloca(_, ty) => Ok(Type::Ptr(Box::new(ty.clone()))),
Load(_, ty) => Ok(ty.clone()),
Store(_, value) => value.get_type(builder),
ArrayAlloca(ty, _) => Ok(Type::Ptr(Box::new(ty.clone()))),
GetElemPtr(ptr, _) => ptr.get_type(builder),
}
}
}
}
impl ConstValue {
pub fn get_type(&self) -> Type {
use Type::*;
match self {
ConstValue::I8(_) => I8,
ConstValue::I16(_) => I16,
ConstValue::I32(_) => I32,
ConstValue::I64(_) => I64,
ConstValue::I128(_) => I128,
ConstValue::U8(_) => U8,
ConstValue::U16(_) => U16,
ConstValue::U32(_) => U32,
ConstValue::U64(_) => U64,
ConstValue::U128(_) => U128,
ConstValue::String(_) => Ptr(Box::new(I8)),
ConstValue::Bool(_) => Bool,
}
}
}
impl Type {
pub fn comparable(&self) -> bool {
match self {
Type::I8 => true,
Type::I16 => true,
Type::I32 => true,
Type::I64 => true,
Type::I128 => true,
Type::U8 => true,
Type::U16 => true,
Type::U32 => true,
Type::U64 => true,
Type::U128 => true,
Type::Bool => true,
Type::Void => false,
Type::Ptr(_) => false,
}
}
pub fn signed(&self) -> bool {
match self {
Type::I8 => true,
Type::I16 => true,
Type::I32 => true,
Type::I64 => true,
Type::I128 => true,
Type::U8 => false,
Type::U16 => false,
Type::U32 => false,
Type::U64 => false,
Type::U128 => false,
Type::Bool => false,
Type::Void => false,
Type::Ptr(_) => false,
}
}
}
impl TerminatorKind {
pub(crate) fn get_type(&self, builder: &Builder) -> Result<Type, ()> {
use TerminatorKind::*;
match self {
Ret(instr_val) => instr_val.get_type(builder),
RetVoid => Ok(Type::Void),
Br(_) => Ok(Type::Void),
CondBr(_, _, _) => Ok(Type::Void),
}
}
}

View File

@ -20,7 +20,11 @@ use llvm_sys::{
},
};
use crate::util::{ErrorMessageHolder, MemoryBufferHolder, from_cstring, into_cstring};
use crate::{
CustomTypeKind, NamedStruct,
builder::{TypeHolder, TypeValue},
util::{ErrorMessageHolder, MemoryBufferHolder, from_cstring, into_cstring},
};
use super::{
CmpPredicate, ConstValue, Context, TerminatorKind, Type,
@ -175,6 +179,7 @@ pub struct LLVMModule<'a> {
functions: HashMap<FunctionValue, LLVMFunction>,
blocks: HashMap<BlockValue, LLVMBasicBlockRef>,
values: HashMap<InstructionValue, LLVMValue>,
types: HashMap<TypeValue, LLVMTypeRef>,
}
#[derive(Clone, Copy)]
@ -196,18 +201,18 @@ impl ModuleHolder {
context.context_ref,
);
for _ty in &self.types {
todo!("Do something with types!");
}
// Compile the contents
let mut functions = HashMap::new();
let mut types = HashMap::new();
for ty in &self.types {
types.insert(ty.value, ty.compile_type(context, &types));
}
let mut functions = HashMap::new();
for function in &self.functions {
functions.insert(
function.value,
function.compile_signature(context, module_ref),
function.compile_signature(context, module_ref, &types),
);
}
@ -217,6 +222,7 @@ impl ModuleHolder {
builder_ref: context.builder_ref,
module_ref,
functions,
types,
blocks: HashMap::new(),
values: HashMap::new(),
};
@ -230,19 +236,46 @@ impl ModuleHolder {
}
}
impl TypeHolder {
unsafe fn compile_type(
&self,
context: &LLVMContext,
types: &HashMap<TypeValue, LLVMTypeRef>,
) -> LLVMTypeRef {
unsafe {
match &self.data.kind {
CustomTypeKind::NamedStruct(named_struct) => {
let mut elem_types = Vec::new();
for ty in &named_struct.1 {
elem_types.push(ty.as_llvm(context.context_ref, types));
}
let struct_ty = LLVMStructTypeInContext(
context.context_ref,
elem_types.as_mut_ptr(),
elem_types.len() as u32,
0,
);
struct_ty
}
}
}
}
}
impl FunctionHolder {
unsafe fn compile_signature(
&self,
context: &LLVMContext,
module_ref: LLVMModuleRef,
types: &HashMap<TypeValue, LLVMTypeRef>,
) -> LLVMFunction {
unsafe {
let ret_type = self.data.ret.as_llvm(context.context_ref);
let ret_type = self.data.ret.as_llvm(context.context_ref, types);
let mut param_types: Vec<LLVMTypeRef> = self
.data
.params
.iter()
.map(|t| t.as_llvm(context.context_ref))
.map(|t| t.as_llvm(context.context_ref, types))
.collect();
let param_ptr = param_types.as_mut_ptr();
let param_len = param_types.len();
@ -346,7 +379,7 @@ impl InstructionHolder {
use super::Instr::*;
match &self.data.kind {
Param(nth) => LLVMGetParam(function.value_ref, *nth as u32),
Constant(val) => val.as_llvm(module.context_ref, module.builder_ref),
Constant(val) => val.as_llvm(module),
Add(lhs, rhs) => {
let lhs_val = module.values.get(&lhs).unwrap().value_ref;
let rhs_val = module.values.get(&rhs).unwrap().value_ref;
@ -412,7 +445,7 @@ impl InstructionHolder {
}
let phi = LLVMBuildPhi(
module.builder_ref,
_ty.as_llvm(module.context_ref),
_ty.as_llvm(module.context_ref, &module.types),
c"phi".as_ptr(),
);
LLVMAddIncoming(
@ -425,12 +458,12 @@ impl InstructionHolder {
}
Alloca(name, ty) => LLVMBuildAlloca(
module.builder_ref,
ty.as_llvm(module.context_ref),
ty.as_llvm(module.context_ref, &module.types),
into_cstring(name).as_ptr(),
),
Load(ptr, ty) => LLVMBuildLoad2(
module.builder_ref,
ty.as_llvm(module.context_ref),
ty.as_llvm(module.context_ref, &module.types),
module.values.get(&ptr).unwrap().value_ref,
c"load".as_ptr(),
),
@ -440,11 +473,10 @@ impl InstructionHolder {
module.values.get(&ptr).unwrap().value_ref,
),
ArrayAlloca(ty, len) => {
let array_len = ConstValue::U16(*len as u16)
.as_llvm(module.context_ref, module.builder_ref);
let array_len = ConstValue::U16(*len as u16).as_llvm(module);
LLVMBuildArrayAlloca(
module.builder_ref,
ty.as_llvm(module.context_ref),
ty.as_llvm(module.context_ref, &module.types),
array_len,
c"array_alloca".as_ptr(),
)
@ -453,20 +485,46 @@ impl InstructionHolder {
let t = arr.get_type(module.builder).unwrap();
let Type::Ptr(elem_t) = t else { panic!() };
let mut indices: Vec<_> = indices
let mut llvm_indices: Vec<_> = indices
.iter()
.map(|idx| {
ConstValue::U32(*idx).as_llvm(module.context_ref, module.builder_ref)
})
.map(|idx| ConstValue::U32(*idx).as_llvm(module))
.collect();
LLVMBuildGEP2(
module.builder_ref,
elem_t.as_llvm(module.context_ref),
elem_t.as_llvm(module.context_ref, &module.types),
module.values.get(arr).unwrap().value_ref,
indices.as_mut_ptr(),
indices.len() as u32,
c"array_gep".as_ptr(),
llvm_indices.as_mut_ptr(),
llvm_indices.len() as u32,
into_cstring(format!(
"array_gep_{:?}",
indices
.iter()
.map(|v| v.to_string())
.collect::<Vec<_>>()
.join("_")
))
.as_ptr(),
)
}
GetStructElemPtr(struct_val, idx) => {
let t = struct_val.get_type(module.builder).unwrap();
dbg!(&t);
let Type::Ptr(struct_t) = t else { panic!() };
let type_fmt = if let Type::CustomType(type_val) = *struct_t {
format!("M{}T{}", type_val.0.0, type_val.1)
} else {
format!("{:?}", struct_t)
};
dbg!(idx);
LLVMBuildStructGEP2(
module.builder_ref,
struct_t.as_llvm(module.context_ref, &module.types),
module.values.get(struct_val).unwrap().value_ref,
*idx,
into_cstring(format!("struct.{}.{}.gep", type_fmt, idx)).as_ptr(),
)
}
}
@ -532,9 +590,9 @@ impl CmpPredicate {
}
impl ConstValue {
fn as_llvm(&self, context: LLVMContextRef, builder: LLVMBuilderRef) -> LLVMValueRef {
fn as_llvm(&self, module: &LLVMModule) -> LLVMValueRef {
unsafe {
let t = self.get_type().as_llvm(context);
let t = self.get_type().as_llvm(module.context_ref, &module.types);
match self {
ConstValue::Bool(val) => LLVMConstInt(t, *val as u64, 1),
ConstValue::I8(val) => LLVMConstInt(t, *val as u64, 1),
@ -547,8 +605,8 @@ impl ConstValue {
ConstValue::U32(val) => LLVMConstInt(t, *val as u64, 1),
ConstValue::U64(val) => LLVMConstInt(t, *val as u64, 1),
ConstValue::U128(val) => LLVMConstInt(t, *val as u64, 1),
ConstValue::String(val) => LLVMBuildGlobalStringPtr(
builder,
ConstValue::StringPtr(val) => LLVMBuildGlobalStringPtr(
module.builder_ref,
into_cstring(val).as_ptr(),
c"string".as_ptr(),
),
@ -558,7 +616,11 @@ impl ConstValue {
}
impl Type {
fn as_llvm(&self, context: LLVMContextRef) -> LLVMTypeRef {
fn as_llvm(
&self,
context: LLVMContextRef,
typemap: &HashMap<TypeValue, LLVMTypeRef>,
) -> LLVMTypeRef {
use Type::*;
unsafe {
match self {
@ -569,7 +631,8 @@ impl Type {
I128 | U128 => LLVMInt128TypeInContext(context),
Bool => LLVMInt1TypeInContext(context),
Void => LLVMVoidTypeInContext(context),
Ptr(ty) => LLVMPointerType(ty.as_llvm(context), 0),
Ptr(ty) => LLVMPointerType(ty.as_llvm(context, typemap), 0),
CustomType(struct_ty) => *typemap.get(struct_ty).unwrap(),
}
}
}

View File

@ -99,6 +99,12 @@ impl Debug for InstructionValue {
}
}
impl Debug for TypeValue {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Ty[{:0>2}-{:0>2}]", &self.0.0, self.1)
}
}
impl Debug for Instr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
@ -126,6 +132,9 @@ impl Debug for Instr {
.collect::<Vec<_>>()
.join(", "),
),
Instr::GetStructElemPtr(instruction_value, index) => {
fmt_index(f, instruction_value, &index.to_string())
}
}
}
}

View File

@ -4,8 +4,9 @@
use std::{fmt::Debug, marker::PhantomData};
use builder::{BlockValue, Builder, FunctionValue, InstructionValue, ModuleValue};
use builder::{BlockValue, Builder, FunctionValue, InstructionValue, ModuleValue, TypeValue};
use debug::PrintableModule;
use util::match_types;
pub mod builder;
pub mod compile;
@ -76,6 +77,15 @@ impl<'ctx> Module<'ctx> {
}
}
pub fn custom_type(&mut self, ty: CustomTypeKind) -> TypeValue {
unsafe {
let (name, kind) = match &ty {
CustomTypeKind::NamedStruct(NamedStruct(name, _)) => (name.clone(), ty),
};
self.builder.add_type(&self.value, TypeData { name, kind })
}
}
pub fn value(&self) -> ModuleValue {
self.value
}
@ -205,6 +215,7 @@ pub enum CmpPredicate {
pub enum Instr {
Param(usize),
Constant(ConstValue),
Add(InstructionValue, InstructionValue),
Sub(InstructionValue, InstructionValue),
Mult(InstructionValue, InstructionValue),
@ -216,6 +227,7 @@ pub enum Instr {
Store(InstructionValue, InstructionValue),
ArrayAlloca(Type, u32),
GetElemPtr(InstructionValue, Vec<u32>),
GetStructElemPtr(InstructionValue, u32),
/// Integer Comparison
ICmp(CmpPredicate, InstructionValue, InstructionValue),
@ -237,6 +249,7 @@ pub enum Type {
U128,
Bool,
Void,
CustomType(TypeValue),
Ptr(Box<Type>),
}
@ -253,7 +266,7 @@ pub enum ConstValue {
U64(u64),
U128(u128),
Bool(bool),
String(String),
StringPtr(String),
}
#[derive(Clone, Hash)]
@ -272,5 +285,124 @@ pub struct TypeData {
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub enum CustomTypeKind {
Struct(Vec<Type>),
NamedStruct(NamedStruct),
}
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub struct NamedStruct(pub String, pub Vec<Type>);
impl InstructionValue {
pub(crate) fn get_type(&self, builder: &Builder) -> Result<Type, ()> {
use Instr::*;
unsafe {
match &builder.instr_data(self).kind {
Param(nth) => builder
.function_data(&self.0.0)
.params
.get(*nth)
.cloned()
.ok_or(()),
Constant(c) => Ok(c.get_type()),
Add(lhs, rhs) => match_types(lhs, rhs, &builder),
Sub(lhs, rhs) => match_types(lhs, rhs, &builder),
Mult(lhs, rhs) => match_types(lhs, rhs, &builder),
And(lhs, rhs) => match_types(lhs, rhs, &builder),
ICmp(_, _, _) => Ok(Type::Bool),
FunctionCall(function_value, _) => Ok(builder.function_data(function_value).ret),
Phi(values) => values.first().ok_or(()).and_then(|v| v.get_type(&builder)),
Alloca(_, ty) => Ok(Type::Ptr(Box::new(ty.clone()))),
Load(_, ty) => Ok(ty.clone()),
Store(_, value) => value.get_type(builder),
ArrayAlloca(ty, _) => Ok(Type::Ptr(Box::new(ty.clone()))),
GetElemPtr(ptr, _) => ptr.get_type(builder),
GetStructElemPtr(instr, idx) => {
let instr_ty = instr.get_type(builder)?;
let Type::Ptr(inner_ty) = instr_ty else {
panic!("GetStructElemPtr on non-pointer! ({:?})", &instr_ty)
};
let Type::CustomType(ty_value) = *inner_ty else {
panic!("GetStructElemPtr on non-struct! ({:?})", &inner_ty)
};
let field_ty = match builder.type_data(&ty_value).kind {
CustomTypeKind::NamedStruct(NamedStruct(_, fields)) => {
fields.get_unchecked(*idx as usize).clone()
}
};
Ok(Type::Ptr(Box::new(field_ty)))
}
}
}
}
}
impl ConstValue {
pub fn get_type(&self) -> Type {
use Type::*;
match self {
ConstValue::I8(_) => I8,
ConstValue::I16(_) => I16,
ConstValue::I32(_) => I32,
ConstValue::I64(_) => I64,
ConstValue::I128(_) => I128,
ConstValue::U8(_) => U8,
ConstValue::U16(_) => U16,
ConstValue::U32(_) => U32,
ConstValue::U64(_) => U64,
ConstValue::U128(_) => U128,
ConstValue::StringPtr(_) => Ptr(Box::new(I8)),
ConstValue::Bool(_) => Bool,
}
}
}
impl Type {
pub fn comparable(&self) -> bool {
match self {
Type::I8 => true,
Type::I16 => true,
Type::I32 => true,
Type::I64 => true,
Type::I128 => true,
Type::U8 => true,
Type::U16 => true,
Type::U32 => true,
Type::U64 => true,
Type::U128 => true,
Type::Bool => true,
Type::Void => false,
Type::Ptr(_) => false,
Type::CustomType(_) => false,
}
}
pub fn signed(&self) -> bool {
match self {
Type::I8 => true,
Type::I16 => true,
Type::I32 => true,
Type::I64 => true,
Type::I128 => true,
Type::U8 => false,
Type::U16 => false,
Type::U32 => false,
Type::U64 => false,
Type::U128 => false,
Type::Bool => false,
Type::Void => false,
Type::Ptr(_) => false,
Type::CustomType(_) => false,
}
}
}
impl TerminatorKind {
pub(crate) fn get_type(&self, builder: &Builder) -> Result<Type, ()> {
use TerminatorKind::*;
match self {
Ret(instr_val) => instr_val.get_type(builder),
RetVoid => Ok(Type::Void),
Br(_) => Ok(Type::Void),
CondBr(_, _, _) => Ok(Type::Void),
}
}
}

View File

@ -1,11 +1,16 @@
use std::{collections::HashMap, mem};
use reid_lib::{
builder::InstructionValue, compile::CompiledModule, Block, CmpPredicate, ConstValue, Context,
Function, FunctionFlags, Instr, Module, TerminatorKind as Term, Type,
builder::{InstructionValue, TypeValue},
compile::CompiledModule,
Block, CmpPredicate, ConstValue, Context, CustomTypeKind, Function, FunctionFlags, Instr,
Module, NamedStruct, TerminatorKind as Term, Type,
};
use crate::mir::{self, types::ReturnType, IndexedVariableReference, NamedVariableRef, TypeKind};
use crate::mir::{
self, types::ReturnType, IndexedVariableReference, NamedVariableRef, StructType,
TypeDefinitionKind, TypeKind,
};
/// Context that contains all of the given modules as complete codegenerated
/// LLIR that can then be finally compiled into LLVM IR.
@ -47,20 +52,42 @@ impl mir::Module {
fn codegen<'ctx>(&self, context: &'ctx Context) -> ModuleCodegen<'ctx> {
let mut module = context.module(&self.name, self.is_main);
let mut types = HashMap::new();
let mut type_values = HashMap::new();
for typedef in &self.typedefs {
let type_value = match &typedef.kind {
TypeDefinitionKind::Struct(StructType(fields)) => {
module.custom_type(CustomTypeKind::NamedStruct(NamedStruct(
typedef.name.clone(),
fields
.iter()
// TODO: Reorder custom-type definitions such that
// inner types get evaluated first. Otherwise this
// will cause a panic!
.map(|(_, t)| t.get_type(&type_values))
.collect(),
)))
}
};
types.insert(type_value, typedef.kind.clone());
type_values.insert(typedef.name.clone(), type_value);
}
let mut functions = HashMap::new();
for function in &self.functions {
let param_types: Vec<Type> = function
.parameters
.iter()
.map(|(_, p)| p.get_type())
.map(|(_, p)| p.get_type(&type_values))
.collect();
let is_main = self.is_main && function.name == "main";
let func = match &function.kind {
mir::FunctionDefinitionKind::Local(_, _) => module.function(
&function.name,
function.return_type.get_type(),
function.return_type.get_type(&type_values),
param_types,
FunctionFlags {
is_pub: function.is_pub || is_main,
@ -71,7 +98,7 @@ impl mir::Module {
),
mir::FunctionDefinitionKind::Extern(imported) => module.function(
&function.name,
function.return_type.get_type(),
function.return_type.get_type(&type_values),
param_types,
FunctionFlags {
is_extern: true,
@ -93,7 +120,7 @@ impl mir::Module {
p_name.clone(),
StackValue(
StackValueKind::Immutable(entry.build(Instr::Param(i)).unwrap()),
p_ty.get_type(),
p_ty.get_type(&type_values),
),
);
}
@ -104,6 +131,8 @@ impl mir::Module {
function,
block: entry,
functions: &functions,
types: &types,
type_values: &type_values,
stack_values,
};
match &mir_function.kind {
@ -131,6 +160,8 @@ pub struct Scope<'ctx, 'a> {
module: &'ctx Module<'ctx>,
function: &'ctx Function<'ctx>,
block: Block<'ctx>,
types: &'a HashMap<TypeValue, TypeDefinitionKind>,
type_values: &'a HashMap<String, TypeValue>,
functions: &'a HashMap<String, Function<'ctx>>,
stack_values: HashMap<String, StackValue>,
}
@ -152,6 +183,8 @@ impl<'ctx, 'a> Scope<'ctx, 'a> {
context: self.context,
module: self.module,
functions: self.functions,
types: self.types,
type_values: self.type_values,
stack_values: self.stack_values.clone(),
}
}
@ -163,6 +196,10 @@ impl<'ctx, 'a> Scope<'ctx, 'a> {
mem::swap(&mut self.block, &mut old_block);
old_block
}
fn get_typedef(&self, name: &String) -> Option<&TypeDefinitionKind> {
self.type_values.get(name).and_then(|v| self.types.get(v))
}
}
impl IndexedVariableReference {
@ -184,8 +221,24 @@ impl IndexedVariableReference {
_ => panic!("Tried to codegen indexing a non-indexable value!"),
}
}
mir::IndexedVariableReferenceKind::StructIndex(indexed_variable_reference, _) => {
todo!("codegen for indexed var refrence")
mir::IndexedVariableReferenceKind::StructIndex(inner, field) => {
let (inner_val, mut indices) = inner.get_stack_value(scope)?;
let idx = if let Type::CustomType(ty_val) = inner_val.1 {
match scope.types.get(&ty_val).unwrap() {
TypeDefinitionKind::Struct(struct_type) => struct_type.find_index(field),
}
} else {
None
}?;
match &inner_val.1 {
Type::Ptr(_) => {
indices.push(idx as u32);
Some((inner_val, indices))
}
_ => panic!("Tried to codegen indexing a non-indexable value!"),
}
}
}
}
@ -206,14 +259,17 @@ impl mir::Statement {
_ => StackValueKind::Mutable({
let alloca = scope
.block
.build(Instr::Alloca(name.clone(), ty.get_type()))
.build(Instr::Alloca(
name.clone(),
ty.get_type(scope.type_values),
))
.unwrap();
scope.block.build(Instr::Store(alloca, value)).unwrap();
alloca
}),
},
},
ty.get_type(),
ty.get_type(scope.type_values),
),
);
None
@ -398,7 +454,7 @@ impl mir::Expression {
Some(
scope
.block
.build(Instr::Load(ptr, val_t.get_type()))
.build(Instr::Load(ptr, val_t.get_type(scope.type_values)))
.unwrap(),
)
}
@ -416,7 +472,7 @@ impl mir::Expression {
let array = scope
.block
.build(Instr::ArrayAlloca(
instr_t.get_type(),
instr_t.get_type(scope.type_values),
instr_list.len() as u32,
))
.unwrap();
@ -431,10 +487,49 @@ impl mir::Expression {
Some(array)
}
mir::ExprKind::StructIndex(expression, type_kind, _) => {
todo!("codegen for struct index")
mir::ExprKind::StructIndex(expression, type_kind, field) => {
let struct_val = expression.codegen(scope)?;
let struct_ty = expression.return_type().ok()?.1.known().ok()?;
let TypeKind::CustomType(name) = struct_ty else {
return None;
};
let TypeDefinitionKind::Struct(struct_ty) = scope.get_typedef(&name)?;
let idx = struct_ty.find_index(field)?;
let ptr = scope
.block
.build(Instr::GetStructElemPtr(struct_val, idx as u32))
.unwrap();
Some(
scope
.block
.build(Instr::Load(ptr, type_kind.get_type(scope.type_values)))
.unwrap(),
)
}
mir::ExprKind::Struct(name, items) => {
let struct_ptr = scope
.block
.build(Instr::Alloca(
name.clone(),
Type::CustomType(*scope.type_values.get(name)?),
))
.unwrap();
for (i, (_, exp)) in items.iter().enumerate() {
let elem_ptr = scope
.block
.build(Instr::GetStructElemPtr(struct_ptr, i as u32))
.unwrap();
if let Some(val) = exp.codegen(scope) {
scope.block.build(Instr::Store(elem_ptr, val)).unwrap();
}
}
Some(struct_ptr)
}
mir::ExprKind::Struct(_, items) => todo!("codegen for struct expression"),
}
}
}
@ -494,14 +589,14 @@ impl mir::Literal {
mir::Literal::U64(val) => ConstValue::U64(val),
mir::Literal::U128(val) => ConstValue::U128(val),
mir::Literal::Bool(val) => ConstValue::Bool(val),
mir::Literal::String(val) => ConstValue::String(val.clone()),
mir::Literal::String(val) => ConstValue::StringPtr(val.clone()),
mir::Literal::Vague(_) => panic!("Got vague literal!"),
})
}
}
impl TypeKind {
fn get_type(&self) -> Type {
fn get_type(&self, type_vals: &HashMap<String, TypeValue>) -> Type {
match &self {
TypeKind::I8 => Type::I8,
TypeKind::I16 => Type::I16,
@ -515,10 +610,10 @@ impl TypeKind {
TypeKind::U128 => Type::U128,
TypeKind::Bool => Type::Bool,
TypeKind::StringPtr => Type::Ptr(Box::new(Type::I8)),
TypeKind::Array(elem_t, _) => Type::Ptr(Box::new(elem_t.get_type())),
TypeKind::Array(elem_t, _) => Type::Ptr(Box::new(elem_t.get_type(type_vals))),
TypeKind::Void => Type::Void,
TypeKind::Vague(_) => panic!("Tried to compile a vague type!"),
TypeKind::CustomType(_) => todo!("codegen for custom type"),
TypeKind::CustomType(n) => Type::CustomType(type_vals.get(n).unwrap().clone()),
}
}
}

View File

@ -84,6 +84,16 @@ pub enum VagueType {
#[derive(Clone, Debug)]
pub struct StructType(pub Vec<(String, TypeKind)>);
impl StructType {
pub fn find_index(&self, name: &String) -> Option<u32> {
self.0
.iter()
.enumerate()
.find(|(_, (n, _))| n == name)
.map(|(i, _)| i as u32)
}
}
pub type TypedefMap = HashMap<String, TypeDefinitionKind>;
impl TypeKind {

View File

@ -300,7 +300,7 @@ impl Statement {
StmtKind::Set(_, expression) => {
expression.pass(pass, state, scope);
}
StmtKind::Import(_) => todo!(),
StmtKind::Import(_) => {} // Never exists at this stage
StmtKind::Expression(expression) => {
expression.pass(pass, state, scope);
}
@ -322,7 +322,7 @@ impl Statement {
.ok();
}
StmtKind::Set(_, _) => {}
StmtKind::Import(_) => todo!(),
StmtKind::Import(_) => {} // Never exists at this stage
StmtKind::Expression(_) => {}
};
}

View File

@ -9,13 +9,13 @@ use std::{convert::Infallible, iter};
use crate::{mir::TypeKind, util::try_all};
use super::{
pass::{self, Pass, PassState},
pass::{Pass, PassState},
typecheck::ErrorKind,
typerefs::{ScopeTypeRefs, TypeRef, TypeRefs},
types::{pick_return, ReturnType},
Block, ExprKind, Expression, FunctionDefinition, FunctionDefinitionKind, IfExpression,
IndexedVariableReference, IndexedVariableReferenceKind, Module, NamedVariableRef, ReturnKind,
StmtKind, StructType, TypeDefinitionKind,
StmtKind, TypeDefinitionKind,
TypeKind::*,
VagueType::*,
};

View File

@ -8,7 +8,7 @@ use crate::mir::VagueType;
use super::{
typecheck::{Collapsable, ErrorKind},
BinaryOperator, TypeDefinition, TypeKind,
BinaryOperator, TypeKind,
};
#[derive(Clone)]

View File

@ -1,7 +1,3 @@
use std::collections::HashMap;
use crate::util::try_all;
use super::*;
#[derive(Debug, Clone)]