Compare commits

...

7 Commits

Author SHA1 Message Date
89002f34e4 Add function double-definition checking 2025-07-24 12:13:34 +03:00
954f3438d3 Codegen intrinsics 2025-07-24 11:56:44 +03:00
b793ef7526 Add intrinsic code generation part 1 2025-07-24 11:34:44 +03:00
5ff5651f5f Work on intrinsics, clean up code a bit 2025-07-24 11:29:58 +03:00
4f1dc5e59d Merge branch 'main' into intrinsicts 2025-07-24 11:18:33 +03:00
9ba091973a Update readme 2025-07-24 11:18:23 +03:00
4bef1c2379 Start implementing intrinsics 2025-07-24 01:39:49 +03:00
16 changed files with 185 additions and 76 deletions

View File

@ -47,7 +47,7 @@ Currently missing big features (TODOs) are:
- Debug Information (PARTIALLY DONE)
- Ability to specify types in literals and variable definitions
- Intrinsic functions
- Not-Unary
- Not-Unary and NEQ-binary
Big features that I want later but are not necessary:
- Associated functions

View File

@ -7,7 +7,7 @@ import std::new_string;
import std::add_num_to_str;
import std::concat_strings;
fn main() -> i32 {
fn main() -> u8 {
let mut test = from_str("hello");
concat_strings(&mut test, from_str(" world"));
@ -21,5 +21,5 @@ fn main() -> i32 {
free_string(&test);
return 0;
return addition(5, 3);
}

View File

@ -155,7 +155,7 @@ impl Default for FunctionFlags {
FunctionFlags {
is_extern: false,
is_main: false,
is_pub: true,
is_pub: false,
is_imported: false,
}
}

View File

@ -18,11 +18,16 @@ pub struct Allocator {
pub struct AllocatorScope<'ctx, 'a> {
pub(super) block: &'a mut Block<'ctx>,
pub(super) module_id: SourceModuleId,
pub(super) types: &'a HashMap<TypeValue, TypeDefinition>,
pub(super) type_values: &'a HashMap<CustomTypeKey, TypeValue>,
}
impl Allocator {
pub fn empty() -> Allocator {
Allocator {
allocations: Vec::new(),
}
}
pub fn from(func: &FunctionDefinition, scope: &mut AllocatorScope) -> Allocator {
func.allocate(scope)
}
@ -44,22 +49,21 @@ impl mir::FunctionDefinition {
fn allocate<'ctx, 'a>(&self, scope: &mut AllocatorScope<'ctx, 'a>) -> Allocator {
let mut allocated = Vec::new();
match &self.kind {
crate::mir::FunctionDefinitionKind::Local(block, _) => {
mir::FunctionDefinitionKind::Local(block, _) => {
for param in &self.parameters {
let allocation = scope
.block
.build_named(
param.0.clone(),
reid_lib::Instr::Alloca(
param.1.get_type(scope.type_values, scope.types),
),
reid_lib::Instr::Alloca(param.1.get_type(scope.type_values)),
)
.unwrap();
allocated.push(Allocation(param.0.clone(), param.1.clone(), allocation));
}
allocated.extend(block.allocate(scope));
}
crate::mir::FunctionDefinitionKind::Extern(_) => {}
mir::FunctionDefinitionKind::Extern(_) => {}
mir::FunctionDefinitionKind::Intrinsic(_) => {}
}
Allocator {
@ -95,11 +99,7 @@ impl mir::Statement {
.block
.build_named(
named_variable_ref.1.clone(),
reid_lib::Instr::Alloca(
named_variable_ref
.0
.get_type(scope.type_values, scope.types),
),
reid_lib::Instr::Alloca(named_variable_ref.0.get_type(scope.type_values)),
)
.unwrap();
allocated.push(Allocation(

View File

@ -50,7 +50,6 @@ impl ast::Module {
block.into_mir(module_id),
(*range).as_meta(module_id),
),
source: module_id,
};
functions.push(def);
}
@ -71,7 +70,6 @@ impl ast::Module {
.map(|p| (p.0, p.1 .0.into_mir(module_id)))
.collect(),
kind: mir::FunctionDefinitionKind::Extern(false),
source: module_id,
};
functions.push(def);
}

View File

@ -275,7 +275,7 @@ impl mir::Module {
// TODO: Reorder custom-type definitions such that
// inner types get evaluated first. Otherwise this
// will cause a panic!
.map(|StructField(_, t, _)| t.get_type(&type_values, &types))
.map(|StructField(_, t, _)| t.get_type(&type_values))
.collect(),
)))
}
@ -291,14 +291,14 @@ impl mir::Module {
let param_types: Vec<Type> = function
.parameters
.iter()
.map(|(_, p)| p.get_type(&type_values, &types))
.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(&type_values, &types),
function.return_type.get_type(&type_values),
param_types,
FunctionFlags {
is_pub: function.is_pub || is_main,
@ -309,7 +309,7 @@ impl mir::Module {
),
mir::FunctionDefinitionKind::Extern(imported) => module.function(
&function.name,
function.return_type.get_type(&type_values, &types),
function.return_type.get_type(&type_values),
param_types,
FunctionFlags {
is_extern: true,
@ -317,6 +317,14 @@ impl mir::Module {
..FunctionFlags::default()
},
),
mir::FunctionDefinitionKind::Intrinsic(_) => module.function(
&function.name,
function.return_type.get_type(&type_values),
param_types,
FunctionFlags {
..FunctionFlags::default()
},
),
};
functions.insert(function.name.clone(), StackFunction { ir: func });
@ -333,7 +341,6 @@ impl mir::Module {
&mut AllocatorScope {
block: &mut entry,
module_id: self.module_id,
types: &types,
type_values: &type_values,
},
);
@ -470,6 +477,26 @@ impl mir::Module {
}
}
mir::FunctionDefinitionKind::Extern(_) => {}
mir::FunctionDefinitionKind::Intrinsic(kind) => {
let entry = function.ir.block("entry");
let mut scope = Scope {
context,
modules: &modules,
tokens,
module: &module,
module_id: self.module_id,
function,
block: entry,
functions: &functions,
types: &types,
type_values: &type_values,
stack_values: Default::default(),
debug: None,
allocator: Rc::new(RefCell::new(Allocator::empty())),
};
kind.codegen(&mut scope)?
}
}
}
@ -694,7 +721,7 @@ impl mir::Expression {
format!("{}", varref.1),
Instr::Load(
v.0.instr(),
inner.get_type(scope.type_values, scope.types),
inner.get_type(scope.type_values),
),
)
.unwrap(),
@ -800,7 +827,7 @@ impl mir::Expression {
.known()
.expect("function return type unknown");
let ret_type = ret_type_kind.get_type(scope.type_values, scope.types);
let ret_type = ret_type_kind.get_type(scope.type_values);
let params = try_all(
call.parameters
@ -903,10 +930,7 @@ impl mir::Expression {
.block
.build_named(
"load",
Instr::Load(
kind.instr(),
inner.get_type(scope.type_values, scope.types),
),
Instr::Load(kind.instr(), inner.get_type(scope.type_values)),
)
.unwrap();
(
@ -962,10 +986,7 @@ impl mir::Expression {
.block
.build_named(
"array.load",
Instr::Load(
ptr,
contained_ty.get_type(scope.type_values, scope.types),
),
Instr::Load(ptr, contained_ty.get_type(scope.type_values)),
)
.unwrap()
.maybe_location(&mut scope.block, location),
@ -1003,7 +1024,7 @@ impl mir::Expression {
.unwrap_or(TypeKind::Void);
let array_ty = Type::Array(
Box::new(elem_ty_kind.get_type(scope.type_values, scope.types)),
Box::new(elem_ty_kind.get_type(scope.type_values)),
instr_list.len() as u64,
);
let array_name = format!("{}.{}", elem_ty_kind, instr_list.len());
@ -1086,10 +1107,7 @@ impl mir::Expression {
.block
.build_named(
load_n,
Instr::Load(
value,
type_kind.get_type(scope.type_values, scope.types),
),
Instr::Load(value, type_kind.get_type(scope.type_values)),
)
.unwrap(),
),
@ -1180,10 +1198,7 @@ impl mir::Expression {
.block
.build_named(
format!("{}.deref", varref.1),
Instr::Load(
v.0.instr(),
ptr_inner.get_type(scope.type_values, scope.types),
),
Instr::Load(v.0.instr(), ptr_inner.get_type(scope.type_values)),
)
.unwrap();
@ -1198,7 +1213,7 @@ impl mir::Expression {
format!("{}.deref.inner", varref.1),
Instr::Load(
var_ptr_instr,
inner.get_type(scope.type_values, scope.types),
inner.get_type(scope.type_values),
),
)
.unwrap(),
@ -1236,7 +1251,7 @@ impl mir::Expression {
.build(Instr::BitCast(
val.instr(),
Type::Ptr(Box::new(
type_kind.get_type(scope.type_values, scope.types),
type_kind.get_type(scope.type_values),
)),
))
.unwrap(),
@ -1254,7 +1269,7 @@ impl mir::Expression {
.block
.build(Instr::BitCast(
val.instr(),
type_kind.get_type(scope.type_values, scope.types),
type_kind.get_type(scope.type_values),
))
.unwrap(),
),
@ -1263,10 +1278,10 @@ impl mir::Expression {
_ => {
let cast_instr = val
.1
.get_type(scope.type_values, scope.types)
.get_type(scope.type_values)
.cast_instruction(
val.instr(),
&type_kind.get_type(scope.type_values, scope.types),
&type_kind.get_type(scope.type_values),
)
.unwrap();
@ -1438,11 +1453,7 @@ impl mir::Literal {
}
impl TypeKind {
pub(super) fn get_type(
&self,
type_vals: &HashMap<CustomTypeKey, TypeValue>,
typedefs: &HashMap<TypeValue, TypeDefinition>,
) -> Type {
pub(super) fn get_type(&self, type_vals: &HashMap<CustomTypeKey, TypeValue>) -> Type {
match &self {
TypeKind::I8 => Type::I8,
TypeKind::I16 => Type::I16,
@ -1463,24 +1474,16 @@ impl TypeKind {
TypeKind::F80 => Type::F80,
TypeKind::F128PPC => Type::F128PPC,
TypeKind::Char => Type::U8,
TypeKind::Array(elem_t, len) => {
Type::Array(Box::new(elem_t.get_type(type_vals, typedefs)), *len)
}
TypeKind::Array(elem_t, len) => Type::Array(Box::new(elem_t.get_type(type_vals)), *len),
TypeKind::Void => Type::Void,
TypeKind::Vague(_) => panic!("Tried to compile a vague type!"),
TypeKind::CustomType(n) => {
let type_val = type_vals.get(n).unwrap().clone();
Type::CustomType(type_val)
}
TypeKind::UserPtr(type_kind) => {
Type::Ptr(Box::new(type_kind.get_type(type_vals, typedefs)))
}
TypeKind::CodegenPtr(type_kind) => {
Type::Ptr(Box::new(type_kind.get_type(type_vals, typedefs)))
}
TypeKind::Borrow(type_kind, _) => {
Type::Ptr(Box::new(type_kind.get_type(type_vals, typedefs)))
}
TypeKind::UserPtr(type_kind) => Type::Ptr(Box::new(type_kind.get_type(type_vals))),
TypeKind::CodegenPtr(type_kind) => Type::Ptr(Box::new(type_kind.get_type(type_vals))),
TypeKind::Borrow(type_kind, _) => Type::Ptr(Box::new(type_kind.get_type(type_vals))),
}
}
}

57
reid/src/intrinsics.rs Normal file
View File

@ -0,0 +1,57 @@
use reid_lib::Instr;
use crate::{
codegen::{ErrorKind, Scope},
mir::{FunctionDefinition, FunctionDefinitionKind, TypeKind},
};
#[derive(Debug, Clone, Copy)]
pub enum InstrinsicKind {
IAdd,
}
fn intrinsic(
name: &str,
ret_ty: TypeKind,
params: Vec<(&str, TypeKind)>,
kind: InstrinsicKind,
) -> FunctionDefinition {
FunctionDefinition {
name: name.into(),
is_pub: false,
is_imported: false,
return_type: ret_ty,
parameters: params.into_iter().map(|(n, ty)| (n.into(), ty)).collect(),
kind: FunctionDefinitionKind::Intrinsic(kind),
}
}
pub fn form_intrinsics() -> Vec<FunctionDefinition> {
let mut intrinsics = Vec::new();
intrinsics.push(intrinsic(
"addition",
TypeKind::U8,
vec![("lhs".into(), TypeKind::U8), ("rhs".into(), TypeKind::U8)],
InstrinsicKind::IAdd,
));
intrinsics
}
impl InstrinsicKind {
pub fn codegen<'ctx, 'a>(&self, scope: &mut Scope<'ctx, 'a>) -> Result<(), ErrorKind> {
match self {
InstrinsicKind::IAdd => {
let lhs = scope.block.build(Instr::Param(0)).unwrap();
let rhs = scope.block.build(Instr::Param(1)).unwrap();
let add = scope.block.build(Instr::Add(lhs, rhs)).unwrap();
scope
.block
.terminate(reid_lib::TerminatorKind::Ret(add))
.unwrap()
}
}
Ok(())
}
}

View File

@ -44,6 +44,7 @@
use std::path::PathBuf;
use error_raporting::{ErrorKind as ErrorRapKind, ErrorModules, ReidError};
use intrinsics::form_intrinsics;
use lexer::FullToken;
use mir::{
linker::LinkerPass, typecheck::TypeCheck, typeinference::TypeInference, typerefs::TypeRefs,
@ -56,6 +57,7 @@ mod allocator;
mod ast;
mod codegen;
pub mod error_raporting;
pub mod intrinsics;
pub mod ld;
mod lexer;
pub mod mir;
@ -126,6 +128,12 @@ pub fn perform_all_passes<'map>(
#[cfg(debug_assertions)]
dbg!(&context);
for module in &mut context.modules {
for intrinsic in form_intrinsics() {
module.1.functions.insert(0, intrinsic);
}
}
#[cfg(debug_assertions)]
println!("{}", &context);

View File

@ -108,12 +108,13 @@ impl Display for FunctionDefinition {
impl Display for FunctionDefinitionKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Local(block, _) => {
FunctionDefinitionKind::Local(block, _) => {
write!(f, "{}", block)?;
Ok(())
}
Self::Extern(true) => write!(f, "<Imported Extern>"),
Self::Extern(false) => write!(f, "<Linked Extern>"),
FunctionDefinitionKind::Extern(true) => write!(f, "<Imported Extern>"),
FunctionDefinitionKind::Extern(false) => write!(f, "<Linked Extern>"),
FunctionDefinitionKind::Intrinsic(_) => write!(f, "<Intrinsic>"),
}
}
}

View File

@ -675,6 +675,8 @@ pub enum EqualsIssue {
AlreadyExtern(String, Metadata),
#[error("Function {0} is already imported from another module")]
ConflictWithImport(String),
#[error("Function is defined as an intrinsic")]
ExistsAsIntrinsic,
}
impl FunctionDefinition {
@ -701,6 +703,7 @@ impl FunctionDefinition {
}
}
}
FunctionDefinitionKind::Intrinsic(_) => Err(EqualsIssue::ExistsAsIntrinsic),
}
}
}

View File

@ -331,7 +331,6 @@ impl<'map> Pass for LinkerPass<'map> {
return_type,
parameters: param_tys,
kind: super::FunctionDefinitionKind::Extern(true),
source: imported_mod_id,
});
}
}

View File

@ -5,6 +5,7 @@
use std::{collections::HashMap, path::PathBuf};
use crate::{
intrinsics::InstrinsicKind,
lexer::{FullToken, Position},
token_stream::TokenRange,
};
@ -293,7 +294,6 @@ pub struct FunctionDefinition {
pub return_type: TypeKind,
pub parameters: Vec<(String, TypeKind)>,
pub kind: FunctionDefinitionKind,
pub source: SourceModuleId,
}
#[derive(Debug)]
@ -302,6 +302,8 @@ pub enum FunctionDefinitionKind {
Local(Block, Metadata),
/// True = imported from other module, False = Is user defined extern
Extern(bool),
/// Intrinsic definition, defined within the compiler
Intrinsic(InstrinsicKind),
}
impl FunctionDefinition {
@ -309,6 +311,7 @@ impl FunctionDefinition {
match &self.kind {
FunctionDefinitionKind::Local(block, _) => block.meta.clone(),
FunctionDefinitionKind::Extern(_) => Metadata::default(),
FunctionDefinitionKind::Intrinsic(_) => Metadata::default(),
}
}
@ -316,6 +319,7 @@ impl FunctionDefinition {
match &self.kind {
FunctionDefinitionKind::Local(_, metadata) => metadata.clone(),
FunctionDefinitionKind::Extern(_) => Metadata::default(),
FunctionDefinitionKind::Intrinsic(_) => Metadata::default(),
}
}
}

View File

@ -352,6 +352,7 @@ impl FunctionDefinition {
block.pass(pass, state, scope, mod_id)?;
}
FunctionDefinitionKind::Extern(_) => {}
FunctionDefinitionKind::Intrinsic(..) => {}
};
Ok(())
}

View File

@ -26,8 +26,8 @@ pub enum ErrorKind {
FunctionNotDefined(String),
#[error("Expected a return type of {0}, got {1} instead")]
ReturnTypeMismatch(TypeKind, TypeKind),
#[error("Function already defined: {0}")]
FunctionAlreadyDefined(String),
#[error("Function {0} already defined {1}")]
FunctionAlreadyDefined(String, ErrorTypedefKind),
#[error("Variable already defined: {0}")]
VariableAlreadyDefined(String),
#[error("Variable {0} is not declared as mutable")]
@ -80,6 +80,16 @@ pub struct TypeCheck<'t> {
type TypecheckPassState<'st, 'sc> = PassState<'st, 'sc, (), ErrorKind>;
#[derive(thiserror::Error, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum ErrorTypedefKind {
#[error("locally")]
Local,
#[error("as an extern")]
Extern,
#[error("as an intrinsic")]
Intrinsic,
}
impl<'t> Pass for TypeCheck<'t> {
type Data = ();
type TError = ErrorKind;
@ -116,10 +126,9 @@ impl<'t> Pass for TypeCheck<'t> {
}
}
let seen = HashSet::new();
for typedef in defmap.values() {
let mut curr = seen.clone();
curr.insert(typedef.name.clone());
let mut seen_types = HashSet::new();
seen_types.insert(typedef.name.clone());
check_typedefs_for_recursion(&defmap, typedef, HashSet::new(), &mut state);
}
@ -196,6 +205,9 @@ impl FunctionDefinition {
FunctionDefinitionKind::Extern(_) => {
Ok((ReturnKind::Soft, TypeKind::Vague(Vague::Unknown)))
}
FunctionDefinitionKind::Intrinsic(..) => {
Ok((ReturnKind::Soft, TypeKind::Vague(Vague::Unknown)))
}
};
match inferred {

View File

@ -4,13 +4,13 @@
//! must then be passed through TypeCheck with the same [`TypeRefs`] in order to
//! place the correct types from the IDs and check that there are no issues.
use std::{convert::Infallible, iter};
use std::{collections::HashMap, convert::Infallible, iter};
use crate::{mir::TypeKind, util::try_all};
use super::{
pass::{Pass, PassResult, PassState},
typecheck::ErrorKind,
typecheck::{ErrorKind, ErrorTypedefKind},
typerefs::{ScopeTypeRefs, TypeRef, TypeRefs},
Block, CustomTypeKey, ExprKind, Expression, FunctionDefinition, FunctionDefinitionKind,
IfExpression, Module, ReturnKind, StmtKind,
@ -33,6 +33,28 @@ impl<'t> Pass for TypeInference<'t> {
type TError = ErrorKind;
fn module(&mut self, module: &mut Module, mut state: TypeInferencePassState) -> PassResult {
let mut seen_functions = HashMap::new();
for function in &mut module.functions {
if let Some(kind) = seen_functions.get(&function.name) {
state.note_errors(
&vec![ErrorKind::FunctionAlreadyDefined(
function.name.clone(),
*kind,
)],
function.signature(),
);
} else {
seen_functions.insert(
function.name.clone(),
match function.kind {
FunctionDefinitionKind::Local(..) => ErrorTypedefKind::Local,
FunctionDefinitionKind::Extern(..) => ErrorTypedefKind::Extern,
FunctionDefinitionKind::Intrinsic(..) => ErrorTypedefKind::Intrinsic,
},
);
}
}
for function in &mut module.functions {
let res = function.infer_types(&self.refs, &mut state.inner());
state.ok(res, function.block_meta());
@ -69,6 +91,7 @@ impl FunctionDefinition {
}
}
FunctionDefinitionKind::Extern(_) => {}
FunctionDefinitionKind::Intrinsic(_) => {}
};
Ok(())

View File

@ -98,7 +98,7 @@ fn float_compiles_well() {
}
#[test]
fn hello_world_compiles_well() {
test(include_str!("../../examples/hello_world.reid"), "test", 0);
test(include_str!("../../examples/hello_world.reid"), "test", 8);
}
#[test]
fn mutable_compiles_well() {