Add intrinsic associated functions

This commit is contained in:
Sofia 2025-07-27 19:55:28 +03:00
parent 0613fc5c53
commit 537167fe4f
7 changed files with 267 additions and 178 deletions

View File

@ -22,6 +22,7 @@ fn main() -> u32 {
let otus = Otus { field: 17 };
print(from_str("otus: ") + Otus::test(&otus) as u64);
print(from_str("i32: ") + i32::test(54) as u64);
print(from_str("sizeof i32: ") + i32::sizeof());
return Otus::test(&otus);
}

View File

@ -36,6 +36,20 @@ pub fn form_intrinsics() -> Vec<FunctionDefinition> {
intrinsics
}
pub fn get_intrinsic_assoc_func(ty: &TypeKind, name: &str) -> Option<FunctionDefinition> {
match name {
"sizeof" => Some(FunctionDefinition {
name: "sizeof".to_owned(),
is_pub: true,
is_imported: false,
return_type: TypeKind::U64,
parameters: Vec::new(),
kind: FunctionDefinitionKind::Intrinsic(Box::new(IntrinsicSizeOf(ty.clone()))),
}),
_ => None,
}
}
fn simple_binop_def<T: Clone + 'static>(op: BinaryOperator, ty: &TypeKind, fun: T) -> BinopDefinition
where
T: FnOnce(&mut Scope, InstructionValue, InstructionValue) -> InstructionValue,
@ -167,28 +181,33 @@ pub fn form_intrinsic_binops() -> Vec<BinopDefinition> {
}
pub trait IntrinsicFunction: std::fmt::Debug {
fn codegen<'ctx, 'a>(&self, scope: &mut Scope<'ctx, 'a>, params: &[&StackValue]) -> Result<StackValue, ErrorKind>;
fn codegen<'ctx, 'a>(&self, scope: &mut Scope<'ctx, 'a>, params: &[StackValue]) -> Result<StackValue, ErrorKind>;
}
macro_rules! intrinsic_debug {
($kind:ty, $name:literal) => {
impl<T> std::fmt::Debug for $kind
where
T: FnOnce(&mut Scope, InstructionValue, InstructionValue) -> InstructionValue,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple($name).finish()
}
}
};
}
#[derive(Clone)]
pub struct IntrinsicSimpleInstr<T>(T)
where
T: FnOnce(&mut Scope, InstructionValue, InstructionValue) -> InstructionValue;
impl<T> std::fmt::Debug for IntrinsicSimpleInstr<T>
where
T: FnOnce(&mut Scope, InstructionValue, InstructionValue) -> InstructionValue,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("IntrinsicSimpleInstr").finish()
}
}
intrinsic_debug!(IntrinsicSimpleInstr<T>, "IntrinsicSimpleInstr");
impl<T: Clone> IntrinsicFunction for IntrinsicSimpleInstr<T>
where
T: FnOnce(&mut Scope, InstructionValue, InstructionValue) -> InstructionValue,
{
fn codegen<'b, 'c>(&self, scope: &mut Scope<'b, 'c>, params: &[&StackValue]) -> Result<StackValue, ErrorKind> {
fn codegen<'b, 'c>(&self, scope: &mut Scope<'b, 'c>, params: &[StackValue]) -> Result<StackValue, ErrorKind> {
let lhs = params.get(0).unwrap();
let rhs = params.get(1).unwrap();
let instr = self.clone().0(scope, lhs.instr(), rhs.instr());
@ -200,21 +219,13 @@ where
pub struct IntrinsicBooleanInstr<T>(T)
where
T: FnOnce(&mut Scope, InstructionValue, InstructionValue) -> InstructionValue;
impl<T> std::fmt::Debug for IntrinsicBooleanInstr<T>
where
T: FnOnce(&mut Scope, InstructionValue, InstructionValue) -> InstructionValue,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("IntrinsicBooleanInstr").finish()
}
}
intrinsic_debug!(IntrinsicBooleanInstr<T>, "IntrinsicBooleanInstr");
impl<T: Clone> IntrinsicFunction for IntrinsicBooleanInstr<T>
where
T: FnOnce(&mut Scope, InstructionValue, InstructionValue) -> InstructionValue,
{
fn codegen<'b, 'c>(&self, scope: &mut Scope<'b, 'c>, params: &[&StackValue]) -> Result<StackValue, ErrorKind> {
fn codegen<'b, 'c>(&self, scope: &mut Scope<'b, 'c>, params: &[StackValue]) -> Result<StackValue, ErrorKind> {
let lhs = params.get(0).unwrap();
let rhs = params.get(1).unwrap();
let instr = self.clone().0(scope, lhs.instr(), rhs.instr());
@ -222,6 +233,24 @@ where
}
}
#[derive(Clone)]
pub struct IntrinsicSizeOf(TypeKind);
impl std::fmt::Debug for IntrinsicSizeOf {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("IntrinsicSizeOf").finish()
}
}
impl IntrinsicFunction for IntrinsicSizeOf {
fn codegen<'ctx, 'a>(&self, scope: &mut Scope<'ctx, 'a>, _: &[StackValue]) -> Result<StackValue, ErrorKind> {
let instr = scope
.block
.build(Instr::Constant(reid_lib::ConstValue::U64(self.0.size_of())))
.unwrap();
Ok(StackValue(StackValueKind::Literal(instr), self.0.clone()))
}
}
// impl IntrinsicFunction for IntrinsicIAdd {
// fn codegen<'ctx, 'a>(
// &self,

View File

@ -211,7 +211,7 @@ impl mir::Module {
};
if let Some(func) = func {
functions.insert(function.name.clone(), func);
functions.insert(function.name.clone(), ScopeFunctionKind::UserGenerated(func));
}
}
@ -251,7 +251,10 @@ impl mir::Module {
};
if let Some(func) = func {
associated_functions.insert(AssociatedFunctionKey(ty.clone(), function.name.clone()), func);
associated_functions.insert(
AssociatedFunctionKey(ty.clone(), function.name.clone()),
ScopeFunctionKind::UserGenerated(func),
);
}
}
@ -332,22 +335,20 @@ impl mir::Module {
)
.unwrap();
StackBinopFunctionKind::UserGenerated(ir_function)
}
FunctionDefinitionKind::Extern(imported) => {
StackBinopFunctionKind::UserGenerated(module.function(
&binop_fn_name,
binop.return_type.get_type(&type_values),
vec![binop.lhs.1.get_type(&type_values), binop.rhs.1.get_type(&type_values)],
FunctionFlags {
is_extern: true,
is_imported: *imported,
..FunctionFlags::default()
},
))
ScopeFunctionKind::UserGenerated(ir_function)
}
FunctionDefinitionKind::Extern(imported) => ScopeFunctionKind::UserGenerated(module.function(
&binop_fn_name,
binop.return_type.get_type(&type_values),
vec![binop.lhs.1.get_type(&type_values), binop.rhs.1.get_type(&type_values)],
FunctionFlags {
is_extern: true,
is_imported: *imported,
..FunctionFlags::default()
},
)),
FunctionDefinitionKind::Intrinsic(intrinsic_function) => {
StackBinopFunctionKind::Intrinsic(intrinsic_function)
ScopeFunctionKind::Intrinsic(intrinsic_function)
}
},
},
@ -355,111 +356,118 @@ impl mir::Module {
}
for mir_function in &self.functions {
let function = functions.get(&mir_function.name).unwrap();
let mut entry = function.block("entry");
if let ScopeFunctionKind::UserGenerated(function) = functions.get(&mir_function.name).unwrap() {
let mut entry = function.block("entry");
let allocator = Allocator::from(
&mir_function.kind,
&mir_function.parameters,
&mut AllocatorScope {
block: &mut entry,
type_values: &type_values,
},
);
let mut scope = Scope {
context,
modules: &modules,
tokens,
module: &module,
module_id: self.module_id,
function,
block: entry,
assoc_functions: &associated_functions,
functions: &functions,
types: &types,
type_values: &type_values,
stack_values: HashMap::new(),
debug: Some(Debug {
info: &debug,
scope: compile_unit,
types: &debug_types,
}),
binops: &binops,
allocator: Rc::new(RefCell::new(allocator)),
};
mir_function
.kind
.codegen(
mir_function.name.clone(),
mir_function.is_pub,
&mut scope,
let allocator = Allocator::from(
&mir_function.kind,
&mir_function.parameters,
&mir_function.return_type,
&function,
match &mir_function.kind {
FunctionDefinitionKind::Local(..) => mir_function.signature().into_debug(tokens, compile_unit),
FunctionDefinitionKind::Extern(_) => None,
FunctionDefinitionKind::Intrinsic(_) => None,
&mut AllocatorScope {
block: &mut entry,
type_values: &type_values,
},
)
.unwrap();
);
let mut scope = Scope {
context,
modules: &modules,
tokens,
module: &module,
module_id: self.module_id,
function,
block: entry,
assoc_functions: &associated_functions,
functions: &functions,
types: &types,
type_values: &type_values,
stack_values: HashMap::new(),
debug: Some(Debug {
info: &debug,
scope: compile_unit,
types: &debug_types,
}),
binops: &binops,
allocator: Rc::new(RefCell::new(allocator)),
};
mir_function
.kind
.codegen(
mir_function.name.clone(),
mir_function.is_pub,
&mut scope,
&mir_function.parameters,
&mir_function.return_type,
&function,
match &mir_function.kind {
FunctionDefinitionKind::Local(..) => {
mir_function.signature().into_debug(tokens, compile_unit)
}
FunctionDefinitionKind::Extern(_) => None,
FunctionDefinitionKind::Intrinsic(_) => None,
},
)
.unwrap();
}
}
for (ty, mir_function) in &self.associated_functions {
let function = associated_functions
if let ScopeFunctionKind::UserGenerated(function) = associated_functions
.get(&AssociatedFunctionKey(ty.clone(), mir_function.name.clone()))
.unwrap();
let mut entry = function.block("entry");
.unwrap()
{
let mut entry = function.block("entry");
let allocator = Allocator::from(
&mir_function.kind,
&mir_function.parameters,
&mut AllocatorScope {
block: &mut entry,
type_values: &type_values,
},
);
let mut scope = Scope {
context,
modules: &modules,
tokens,
module: &module,
module_id: self.module_id,
function,
block: entry,
assoc_functions: &associated_functions,
functions: &functions,
types: &types,
type_values: &type_values,
stack_values: HashMap::new(),
debug: Some(Debug {
info: &debug,
scope: compile_unit,
types: &debug_types,
}),
binops: &binops,
allocator: Rc::new(RefCell::new(allocator)),
};
mir_function
.kind
.codegen(
mir_function.name.clone(),
mir_function.is_pub,
&mut scope,
let allocator = Allocator::from(
&mir_function.kind,
&mir_function.parameters,
&mir_function.return_type,
&function,
match &mir_function.kind {
FunctionDefinitionKind::Local(..) => mir_function.signature().into_debug(tokens, compile_unit),
FunctionDefinitionKind::Extern(_) => None,
FunctionDefinitionKind::Intrinsic(_) => None,
&mut AllocatorScope {
block: &mut entry,
type_values: &type_values,
},
)
.unwrap();
);
let mut scope = Scope {
context,
modules: &modules,
tokens,
module: &module,
module_id: self.module_id,
function,
block: entry,
assoc_functions: &associated_functions,
functions: &functions,
types: &types,
type_values: &type_values,
stack_values: HashMap::new(),
debug: Some(Debug {
info: &debug,
scope: compile_unit,
types: &debug_types,
}),
binops: &binops,
allocator: Rc::new(RefCell::new(allocator)),
};
mir_function
.kind
.codegen(
mir_function.name.clone(),
mir_function.is_pub,
&mut scope,
&mir_function.parameters,
&mir_function.return_type,
&function,
match &mir_function.kind {
FunctionDefinitionKind::Local(..) => {
mir_function.signature().into_debug(tokens, compile_unit)
}
FunctionDefinitionKind::Extern(_) => None,
FunctionDefinitionKind::Intrinsic(_) => None,
},
)
.unwrap();
}
}
Ok(ModuleCodegen { module })
@ -821,7 +829,7 @@ impl mir::Expression {
});
if let Some(operation) = operation {
let a = operation.codegen(&lhs_val, &rhs_val, scope)?;
let a = operation.codegen(lhs_val.clone(), rhs_val.clone(), scope)?;
Some(a)
} else {
let lhs_type = lhs_exp.return_type(&Default::default(), scope.module_id).unwrap().1;
@ -906,19 +914,19 @@ impl mir::Expression {
.map(|v| v.unwrap())
.collect::<Vec<_>>();
let param_instrs = params.iter().map(|e| e.instr()).collect();
let callee = scope.functions.get(&call.name).expect("function not found!");
let val = scope
.block
.build_named(call.name.clone(), Instr::FunctionCall(callee.value(), param_instrs))
.unwrap();
if let Some(debug) = &scope.debug {
let location = call.meta.into_debug(scope.tokens, debug.scope).unwrap();
let location_val = debug.info.location(&debug.scope, location);
val.with_location(&mut scope.block, location_val);
}
let val = callee.codegen(
&call.name,
params.as_slice(),
&call.return_type,
if let Some(debug) = &scope.debug {
call.meta.into_debug(scope.tokens, debug.scope)
} else {
None
},
scope,
)?;
let ptr = if ret_type_kind != TypeKind::Void {
let ptr = scope
@ -927,7 +935,7 @@ impl mir::Expression {
.unwrap();
scope
.block
.build_named(format!("{}.store", call.name), Instr::Store(ptr, val))
.build_named(format!("{}.store", call.name), Instr::Store(ptr, val.instr()))
.unwrap();
Some(ptr)
@ -1332,31 +1340,38 @@ impl mir::Expression {
.map(|v| v.unwrap())
.collect::<Vec<_>>();
let param_instrs = params.iter().map(|e| e.instr()).collect();
let assoc_key = AssociatedFunctionKey(ty.clone(), call.name.clone());
let intrinsic = get_intrinsic_assoc_func(&ty, &call.name);
let intrinsic_owned = intrinsic.map(|func_def| {
let FunctionDefinitionKind::Intrinsic(intrinsic) = func_def.kind else {
panic!();
};
ScopeFunctionKind::IntrinsicOwned(intrinsic)
});
let callee = scope
.assoc_functions
.get(&AssociatedFunctionKey(ty.clone(), call.name.clone()))
.expect("function not found!");
.get(&assoc_key)
.or(intrinsic_owned.as_ref())
.expect(&format!("Function {} does not exist!", call_name));
let val = scope
.block
.build_named(&call_name, Instr::FunctionCall(callee.value(), param_instrs))
let location = if let Some(debug) = &scope.debug {
call.meta.into_debug(scope.tokens, debug.scope)
} else {
None
};
let val = callee
.codegen(&call_name, params.as_slice(), &call.return_type, location, scope)
.unwrap();
if let Some(debug) = &scope.debug {
let location = call.meta.into_debug(scope.tokens, debug.scope).unwrap();
let location_val = debug.info.location(&debug.scope, location);
val.with_location(&mut scope.block, location_val);
}
let ptr = if ret_type_kind != TypeKind::Void {
let ptr = scope
.block
.build_named(&call_name, Instr::Alloca(ret_type.clone()))
.build_named(&call.name, Instr::Alloca(ret_type.clone()))
.unwrap();
scope
.block
.build_named(format!("{}.store", call_name), Instr::Store(ptr, val))
.build_named(format!("{}.store", call_name), Instr::Store(ptr, val.instr()))
.unwrap();
Some(ptr)

View File

@ -2,7 +2,7 @@ use std::{cell::RefCell, collections::HashMap, mem, rc::Rc};
use reid_lib::{
builder::{InstructionValue, TypeValue},
debug_information::{DebugInformation, DebugProgramValue, DebugTypeValue},
debug_information::{DebugInformation, DebugLocation, DebugProgramValue, DebugTypeValue},
Block, Context, Function, Instr, Module,
};
@ -26,8 +26,8 @@ pub struct Scope<'ctx, 'scope> {
pub(super) block: Block<'ctx>,
pub(super) types: &'scope HashMap<TypeValue, TypeDefinition>,
pub(super) type_values: &'scope HashMap<CustomTypeKey, TypeValue>,
pub(super) assoc_functions: &'scope HashMap<AssociatedFunctionKey, Function<'ctx>>,
pub(super) functions: &'scope HashMap<String, Function<'ctx>>,
pub(super) assoc_functions: &'scope HashMap<AssociatedFunctionKey, ScopeFunctionKind<'ctx>>,
pub(super) functions: &'scope HashMap<String, ScopeFunctionKind<'ctx>>,
pub(super) binops: &'scope HashMap<BinopKey, StackBinopDefinition<'ctx>>,
pub(super) stack_values: HashMap<String, StackValue>,
pub(super) debug: Option<Debug<'ctx>>,
@ -131,19 +131,20 @@ impl StackValueKind {
pub struct StackBinopDefinition<'ctx> {
pub(super) parameters: ((String, TypeKind), (String, TypeKind)),
pub(super) return_ty: TypeKind,
pub(super) kind: StackBinopFunctionKind<'ctx>,
pub(super) kind: ScopeFunctionKind<'ctx>,
}
pub enum StackBinopFunctionKind<'ctx> {
pub enum ScopeFunctionKind<'ctx> {
UserGenerated(Function<'ctx>),
Intrinsic(&'ctx Box<dyn IntrinsicFunction>),
IntrinsicOwned(Box<dyn IntrinsicFunction>),
}
impl<'ctx> StackBinopDefinition<'ctx> {
pub fn codegen<'a>(
&self,
lhs: &StackValue,
rhs: &StackValue,
lhs: StackValue,
rhs: StackValue,
scope: &mut Scope<'ctx, 'a>,
) -> Result<StackValue, ErrorKind> {
let (lhs, rhs) = if lhs.1 == self.parameters.0 .1 && rhs.1 == self.parameters.1 .1 {
@ -151,15 +152,43 @@ impl<'ctx> StackBinopDefinition<'ctx> {
} else {
(rhs, lhs)
};
match &self.kind {
StackBinopFunctionKind::UserGenerated(ir) => {
let instr = scope
let name = format!(
"binop.{}.{}.{}.call",
self.parameters.0 .1, self.parameters.1 .1, self.return_ty
);
self.kind.codegen(&name, &[lhs, rhs], &self.return_ty, None, scope)
}
}
impl<'ctx> ScopeFunctionKind<'ctx> {
pub fn codegen<'a>(
&self,
name: &str,
params: &[StackValue],
return_ty: &TypeKind,
location: Option<DebugLocation>,
scope: &mut Scope<'ctx, 'a>,
) -> Result<StackValue, ErrorKind> {
match self {
ScopeFunctionKind::UserGenerated(function) => {
let val = scope
.block
.build(Instr::FunctionCall(ir.value(), vec![lhs.instr(), rhs.instr()]))
.build_named(
name,
Instr::FunctionCall(function.value(), params.iter().map(|p| p.instr()).collect()),
)
.unwrap();
Ok(StackValue(StackValueKind::Immutable(instr), self.return_ty.clone()))
if let Some(debug) = &scope.debug {
if let Some(location) = location {
let location_val = debug.info.location(&debug.scope, location);
val.with_location(&mut scope.block, location_val);
}
}
Ok(StackValue(StackValueKind::Immutable(val), return_ty.clone()))
}
StackBinopFunctionKind::Intrinsic(fun) => fun.codegen(scope, &[&lhs, &rhs]),
ScopeFunctionKind::Intrinsic(fun) => fun.codegen(scope, params),
ScopeFunctionKind::IntrinsicOwned(fun) => fun.codegen(scope, params),
}
}
}

View File

@ -5,7 +5,7 @@ use std::collections::HashMap;
use std::convert::Infallible;
use std::error::Error as STDError;
use crate::codegen::intrinsics::form_intrinsic_binops;
use crate::codegen::intrinsics::{form_intrinsic_binops, get_intrinsic_assoc_func};
use crate::error_raporting::ReidError;
use super::*;
@ -169,6 +169,24 @@ impl<Data: Clone + Default> Scope<Data> {
.find(|(key, def)| key.0 == typekey.0 && def.importer == Some(typekey.1))
.map(|(_, v)| v))
}
pub fn get_associated_function(&mut self, key: &AssociatedFunctionKey) -> Option<ScopeFunction> {
let func = self.associated_functions.get(key);
if let Some(func) = func {
Some(func.clone())
} else if let Some(func) = get_intrinsic_assoc_func(&key.0, &key.1) {
self.associated_functions.set(
key.clone(),
ScopeFunction {
ret: func.return_type,
params: func.parameters.iter().map(|(_, p)| p.clone()).collect(),
},
);
self.associated_functions.get(key).cloned()
} else {
None
}
}
}
#[derive(Clone, Debug)]

View File

@ -732,12 +732,10 @@ impl Expression {
ExprKind::AssociatedFunctionCall(type_kind, function_call) => {
let true_function = state
.scope
.associated_functions
.get(&pass::AssociatedFunctionKey(
.get_associated_function(&pass::AssociatedFunctionKey(
type_kind.clone(),
function_call.name.clone(),
))
.cloned()
.ok_or(ErrorKind::FunctionNotDefined(function_call.name.clone()));
if let Some(f) = state.ok(true_function, self.1) {

View File

@ -598,8 +598,7 @@ impl Expression {
// Get function definition and types
let fn_call = state
.scope
.associated_functions
.get(&AssociatedFunctionKey(type_kind.clone(), function_call.name.clone()))
.get_associated_function(&AssociatedFunctionKey(type_kind.clone(), function_call.name.clone()))
.ok_or(ErrorKind::AssocFunctionNotDefined(
function_call.name.clone(),
type_kind.clone(),