From 5d19d386827f0b87c491938b627ea695e491c7b1 Mon Sep 17 00:00:00 2001 From: sofia Date: Mon, 4 Aug 2025 15:52:53 +0300 Subject: [PATCH] Add intrinsic min/max to integers and floats --- examples/math_intrinsics.reid | 5 + reid-llvm-lib/src/fmt.rs | 2 +- reid-llvm-lib/src/intrinsics.rs | 69 ++++++++++++ reid-llvm-lib/src/lib.rs | 21 ++++ reid-lsp/src/analysis.rs | 5 +- reid/src/codegen/intrinsics.rs | 179 ++++++++++++++++++++------------ reid/src/codegen/mod.rs | 3 + reid/src/codegen/scope.rs | 28 ++++- reid/src/mir/mod.rs | 10 ++ 9 files changed, 250 insertions(+), 72 deletions(-) create mode 100644 examples/math_intrinsics.reid create mode 100644 reid-llvm-lib/src/intrinsics.rs diff --git a/examples/math_intrinsics.reid b/examples/math_intrinsics.reid new file mode 100644 index 0000000..2d8fd2e --- /dev/null +++ b/examples/math_intrinsics.reid @@ -0,0 +1,5 @@ + +fn main() -> i32 { + let b = 5; + return b.min(7); +} \ No newline at end of file diff --git a/reid-llvm-lib/src/fmt.rs b/reid-llvm-lib/src/fmt.rs index 0c9d585..f721297 100644 --- a/reid-llvm-lib/src/fmt.rs +++ b/reid-llvm-lib/src/fmt.rs @@ -6,7 +6,7 @@ use std::{ }; use crate::{ - CmpPredicate, Context, Instr, InstructionData, TerminatorKind, + CmpPredicate, Context, Instr, InstructionData, TerminatorKind, Type, builder::*, debug_information::{ DebugArrayType, DebugBasicType, DebugFieldType, DebugInformation, DebugLocalVariable, DebugLocation, diff --git a/reid-llvm-lib/src/intrinsics.rs b/reid-llvm-lib/src/intrinsics.rs new file mode 100644 index 0000000..520ed5b --- /dev/null +++ b/reid-llvm-lib/src/intrinsics.rs @@ -0,0 +1,69 @@ +use crate::{CompileResult, Type, builder::Builder}; + +#[derive(Clone, Debug)] +pub enum LLVMIntrinsic { + Max(Type), + Min(Type), +} + +impl LLVMIntrinsic { + pub fn check(&self, builder: &Builder) -> CompileResult<()> { + self.signature(builder)?; + Ok(()) + } + + pub fn signature(&self, builder: &Builder) -> CompileResult<(String, Vec, Type)> { + match self { + LLVMIntrinsic::Max(ty) => { + let name = match ty.category() { + crate::TypeCategory::SignedInteger => format!("llvm.smax.{}", ty.llvm_ty_str(builder)), + crate::TypeCategory::UnsignedInteger => format!("llvm.umax.{}", ty.llvm_ty_str(builder)), + crate::TypeCategory::Real => format!("llvm.max.{}", ty.llvm_ty_str(builder)), + _ => return Err(crate::ErrorKind::Null), + }; + Ok((name, vec![ty.clone(), ty.clone()], ty.clone())) + } + LLVMIntrinsic::Min(ty) => { + let name = match ty.category() { + crate::TypeCategory::SignedInteger => format!("llvm.smin.{}", ty.llvm_ty_str(builder)), + crate::TypeCategory::UnsignedInteger => format!("llvm.umin.{}", ty.llvm_ty_str(builder)), + crate::TypeCategory::Real => format!("llvm.min.{}", ty.llvm_ty_str(builder)), + _ => return Err(crate::ErrorKind::Null), + }; + Ok((name, vec![ty.clone(), ty.clone()], ty.clone())) + } + } + } +} + +impl Type { + fn llvm_ty_str(&self, builder: &Builder) -> String { + match self { + Type::I8 => String::from("i8"), + Type::I16 => String::from("u16"), + Type::I32 => String::from("i32"), + Type::I64 => String::from("i64"), + Type::I128 => String::from("i128"), + Type::U8 => String::from("i8"), + Type::U16 => String::from("i16"), + Type::U32 => String::from("i32"), + Type::U64 => String::from("i64"), + Type::U128 => String::from("i128"), + Type::F16 => String::from("half"), + Type::F32B => String::from("bfloat"), + Type::F32 => String::from("float"), + Type::F64 => String::from("double"), + Type::F80 => String::from("x86_fp80"), + Type::F128 => String::from("fp128"), + Type::F128PPC => String::from("ppc_fp128"), + Type::Bool => String::from("i1"), + Type::Void => String::from("void"), + Type::CustomType(type_value) => { + let ty = unsafe { builder.type_data(type_value) }; + ty.name.clone() + } + Type::Array(ty, len) => format!("[{} x {}]", len, ty.llvm_ty_str(builder)), + Type::Ptr(_) => String::from("ptr"), + } + } +} diff --git a/reid-llvm-lib/src/lib.rs b/reid-llvm-lib/src/lib.rs index 64c4696..16ddd28 100644 --- a/reid-llvm-lib/src/lib.rs +++ b/reid-llvm-lib/src/lib.rs @@ -11,12 +11,14 @@ use fmt::PrintableModule; use crate::{ builder::{ConstantValue, GlobalValue}, debug_information::DebugScopeValue, + intrinsics::LLVMIntrinsic, }; pub mod builder; pub mod compile; pub mod debug_information; mod fmt; +pub mod intrinsics; mod pad_adapter; mod util; @@ -95,6 +97,25 @@ impl<'ctx> Module<'ctx> { } } + pub fn intrinsic(&self, intrinsic: LLVMIntrinsic) -> CompileResult { + unsafe { + let (name, params, ret) = intrinsic.signature(&self.builder)?; + Ok(self.builder.add_function( + &self.value, + FunctionData { + name: name.to_owned(), + linkage_name: Some(name.to_owned()), + ret, + params, + flags: FunctionFlags { + is_extern: true, + ..Default::default() + }, + }, + )) + } + } + pub fn custom_type(&self, ty: CustomTypeKind) -> TypeValue { unsafe { let (name, kind) = match &ty { diff --git a/reid-lsp/src/analysis.rs b/reid-lsp/src/analysis.rs index e7f076b..d96f332 100644 --- a/reid-lsp/src/analysis.rs +++ b/reid-lsp/src/analysis.rs @@ -1021,9 +1021,8 @@ pub fn analyze_expr( function_autocomplete.extend( get_intrinsic_assoc_functions(&invoked_ty) .iter() - .filter_map(|(s, f)| f.as_ref().map(|f| (s, f))) - .filter(|(_, fun)| fun.name.starts_with(name)) - .map(|(_, fun)| Autocomplete { + .filter(|fun| fun.name.starts_with(name)) + .map(|fun| Autocomplete { text: fun.name.clone(), kind: AutocompleteKind::Function(fun.parameters.clone(), fun.return_type.clone()), }) diff --git a/reid/src/codegen/intrinsics.rs b/reid/src/codegen/intrinsics.rs index 5f70fa8..759ea7b 100644 --- a/reid/src/codegen/intrinsics.rs +++ b/reid/src/codegen/intrinsics.rs @@ -1,12 +1,10 @@ -use std::{collections::HashMap, hash::Hash}; - use reid_lib::{builder::InstructionValue, CmpPredicate, ConstValueKind, Instr, Type}; use crate::{ - codegen::{ErrorKind, StackValueKind}, + codegen::{scope::IntrinsicKind, ErrorKind, StackValueKind}, mir::{ - BinaryOperator, BinopDefinition, CmpOperator, FunctionDefinition, FunctionDefinitionKind, FunctionParam, - TypeKind, + implement::TypeCategory, BinaryOperator, BinopDefinition, CmpOperator, FunctionDefinition, + FunctionDefinitionKind, FunctionParam, TypeKind, }, }; @@ -60,79 +58,108 @@ pub fn form_intrinsics() -> Vec { intrinsics } -pub fn get_intrinsic_assoc_functions(ty: &TypeKind) -> HashMap> { - let mut map = HashMap::new(); - map.insert("length".to_owned(), get_intrinsic_assoc_func(ty, "length")); - map.insert("sizeof".to_owned(), get_intrinsic_assoc_func(ty, "sizeof")); - map.insert("malloc".to_owned(), get_intrinsic_assoc_func(ty, "malloc")); - map.insert("null".to_owned(), get_intrinsic_assoc_func(ty, "null")); - map -} - -pub fn get_intrinsic_assoc_func(ty: &TypeKind, name: &str) -> Option { +pub fn get_intrinsic_assoc_functions(ty: &TypeKind) -> Vec { + let mut intrinsics = Vec::new(); if let TypeKind::Array(_, len) = ty { - match name { - "length" => { - return Some(FunctionDefinition { - name: "length".to_owned(), - linkage_name: None, - is_pub: true, - is_imported: false, - return_type: TypeKind::U64, - parameters: vec![FunctionParam { - name: String::from("self"), - ty: TypeKind::Borrow(Box::new(ty.clone()), false), - meta: Default::default(), - }], - kind: FunctionDefinitionKind::Intrinsic(Box::new(IntrinsicConst(*len))), - source: None, - signature_meta: Default::default(), - }); - } - _ => {} - } - } - - match name { - "sizeof" => Some(FunctionDefinition { - name: "sizeof".to_owned(), + intrinsics.push(FunctionDefinition { + name: "length".to_owned(), linkage_name: None, is_pub: true, is_imported: false, return_type: TypeKind::U64, - parameters: Vec::new(), - kind: FunctionDefinitionKind::Intrinsic(Box::new(IntrinsicSizeOf(ty.clone()))), - source: None, - signature_meta: Default::default(), - }), - "malloc" => Some(FunctionDefinition { - name: "malloc".to_owned(), - linkage_name: None, - is_pub: true, - is_imported: false, - return_type: TypeKind::UserPtr(Box::new(ty.clone())), parameters: vec![FunctionParam { - name: String::from("size"), - ty: TypeKind::U64, + name: String::from("self"), + ty: TypeKind::Borrow(Box::new(ty.clone()), false), meta: Default::default(), }], - kind: FunctionDefinitionKind::Intrinsic(Box::new(IntrinsicMalloc(ty.clone()))), + kind: FunctionDefinitionKind::Intrinsic(Box::new(IntrinsicConst(*len))), source: None, signature_meta: Default::default(), - }), - "null" => Some(FunctionDefinition { - name: "null".to_owned(), - linkage_name: None, - is_pub: true, - is_imported: false, - return_type: TypeKind::UserPtr(Box::new(ty.clone())), - parameters: Vec::new(), - kind: FunctionDefinitionKind::Intrinsic(Box::new(IntrinsicNullPtr(ty.clone()))), - source: None, - signature_meta: Default::default(), - }), - _ => None, + }); } + match ty.category() { + TypeCategory::Integer | TypeCategory::Real | TypeCategory::Bool => { + intrinsics.push(FunctionDefinition { + name: "max".to_owned(), + linkage_name: None, + is_pub: true, + is_imported: false, + return_type: ty.clone(), + parameters: vec![ + FunctionParam::from("self", ty.clone()), + FunctionParam::from("other", ty.clone()), + ], + kind: FunctionDefinitionKind::Intrinsic(Box::new(IntrinsicLLVM( + IntrinsicKind::Max(ty.clone()), + ty.clone(), + ))), + source: None, + signature_meta: Default::default(), + }); + intrinsics.push(FunctionDefinition { + name: "min".to_owned(), + linkage_name: None, + is_pub: true, + is_imported: false, + return_type: ty.clone(), + parameters: vec![ + FunctionParam::from("self", ty.clone()), + FunctionParam::from("other", ty.clone()), + ], + kind: FunctionDefinitionKind::Intrinsic(Box::new(IntrinsicLLVM( + IntrinsicKind::Min(ty.clone()), + ty.clone(), + ))), + source: None, + signature_meta: Default::default(), + }); + } + _ => {} + } + intrinsics.push(FunctionDefinition { + name: "sizeof".to_owned(), + linkage_name: None, + is_pub: true, + is_imported: false, + return_type: TypeKind::U64, + parameters: Vec::new(), + kind: FunctionDefinitionKind::Intrinsic(Box::new(IntrinsicSizeOf(ty.clone()))), + source: None, + signature_meta: Default::default(), + }); + intrinsics.push(FunctionDefinition { + name: "malloc".to_owned(), + linkage_name: None, + is_pub: true, + is_imported: false, + return_type: TypeKind::UserPtr(Box::new(ty.clone())), + parameters: vec![FunctionParam { + name: String::from("size"), + ty: TypeKind::U64, + meta: Default::default(), + }], + kind: FunctionDefinitionKind::Intrinsic(Box::new(IntrinsicMalloc(ty.clone()))), + source: None, + signature_meta: Default::default(), + }); + + intrinsics.push(FunctionDefinition { + name: "null".to_owned(), + linkage_name: None, + is_pub: true, + is_imported: false, + return_type: TypeKind::UserPtr(Box::new(ty.clone())), + parameters: Vec::new(), + kind: FunctionDefinitionKind::Intrinsic(Box::new(IntrinsicNullPtr(ty.clone()))), + source: None, + signature_meta: Default::default(), + }); + + intrinsics +} + +pub fn get_intrinsic_assoc_func(ty: &TypeKind, name: &str) -> Option { + get_intrinsic_assoc_functions(ty).into_iter().find(|f| f.name == name) } fn simple_binop_def(op: BinaryOperator, ty: &TypeKind, fun: T) -> BinopDefinition @@ -441,6 +468,7 @@ impl IntrinsicFunction for IntrinsicNullPtr { )) } } + #[derive(Clone, Debug)] pub struct IntrinsicConst(u64); impl IntrinsicFunction for IntrinsicConst { @@ -450,6 +478,23 @@ impl IntrinsicFunction for IntrinsicConst { } } +#[derive(Clone, Debug)] +pub struct IntrinsicLLVM(IntrinsicKind, TypeKind); +impl IntrinsicFunction for IntrinsicLLVM { + fn codegen<'ctx, 'a>(&self, scope: &mut Scope<'ctx, 'a>, params: &[StackValue]) -> Result { + let intrinsic = scope.get_intrinsic(self.0.clone()); + let value = scope + .block + .build(Instr::FunctionCall( + intrinsic, + params.iter().map(|p| p.instr()).collect(), + )) + .unwrap(); + + Ok(StackValue(StackValueKind::Literal(value), self.1)) + } +} + // impl IntrinsicFunction for IntrinsicIAdd { // fn codegen<'ctx, 'a>( // &self, diff --git a/reid/src/codegen/mod.rs b/reid/src/codegen/mod.rs index 15f16f6..3db128b 100644 --- a/reid/src/codegen/mod.rs +++ b/reid/src/codegen/mod.rs @@ -393,6 +393,7 @@ impl mir::Module { }), binops: &binops, allocator: Rc::new(RefCell::new(allocator)), + llvm_intrinsics: Rc::new(RefCell::new(HashMap::new())), }; binop @@ -471,6 +472,7 @@ impl mir::Module { globals: &globals, binops: &binops, allocator: Rc::new(RefCell::new(allocator)), + llvm_intrinsics: Rc::new(RefCell::new(HashMap::new())), }; mir_function @@ -533,6 +535,7 @@ impl mir::Module { globals: &globals, binops: &binops, allocator: Rc::new(RefCell::new(allocator)), + llvm_intrinsics: Rc::new(RefCell::new(HashMap::new())), }; mir_function diff --git a/reid/src/codegen/scope.rs b/reid/src/codegen/scope.rs index dc15e79..fbfefd1 100644 --- a/reid/src/codegen/scope.rs +++ b/reid/src/codegen/scope.rs @@ -1,8 +1,9 @@ use std::{cell::RefCell, collections::HashMap, mem, rc::Rc}; use reid_lib::{ - builder::{GlobalValue, InstructionValue, TypeValue}, + builder::{FunctionValue, GlobalValue, InstructionValue, TypeValue}, debug_information::{DebugInformation, DebugLocation, DebugScopeValue, DebugTypeValue}, + intrinsics::LLVMIntrinsic, Block, Context, Function, Instr, Module, }; @@ -16,6 +17,12 @@ use crate::{ use super::{allocator::Allocator, ErrorKind, IntrinsicFunction, ModuleCodegen}; +#[derive(Debug, Clone, Hash, Eq, PartialEq)] +pub enum IntrinsicKind { + Max(TypeKind), + Min(TypeKind), +} + pub struct Scope<'ctx, 'scope> { pub(super) context: &'ctx Context, pub(super) modules: &'scope HashMap>, @@ -34,6 +41,7 @@ pub struct Scope<'ctx, 'scope> { pub(super) globals: &'scope HashMap, pub(super) debug: Option>, pub(super) allocator: Rc>, + pub(super) llvm_intrinsics: Rc>>, } impl<'ctx, 'a> Scope<'ctx, 'a> { @@ -56,6 +64,7 @@ impl<'ctx, 'a> Scope<'ctx, 'a> { allocator: self.allocator.clone(), globals: self.globals, binops: self.binops, + llvm_intrinsics: self.llvm_intrinsics.clone(), } } @@ -74,6 +83,23 @@ impl<'ctx, 'a> Scope<'ctx, 'a> { pub fn allocate(&self, meta: &Metadata, ty: &TypeKind) -> Option { self.allocator.borrow_mut().allocate(meta, ty) } + + pub fn get_intrinsic(&self, kind: IntrinsicKind) -> FunctionValue { + let mut intrinsics = self.llvm_intrinsics.borrow_mut(); + if let Some(fun) = intrinsics.get(&kind) { + *fun + } else { + let intrinsic = self + .module + .intrinsic(match &kind { + IntrinsicKind::Max(ty) => LLVMIntrinsic::Max(ty.get_type(self.type_values)), + IntrinsicKind::Min(ty) => LLVMIntrinsic::Min(ty.get_type(self.type_values)), + }) + .unwrap(); + intrinsics.insert(kind, intrinsic.clone()); + intrinsic + } + } } #[derive(Debug, Clone)] diff --git a/reid/src/mir/mod.rs b/reid/src/mir/mod.rs index 9072a50..49309c3 100644 --- a/reid/src/mir/mod.rs +++ b/reid/src/mir/mod.rs @@ -315,6 +315,16 @@ pub struct FunctionParam { pub meta: Metadata, } +impl FunctionParam { + pub fn from>(name: T, ty: TypeKind) -> FunctionParam { + FunctionParam { + name: name.into(), + ty: ty, + meta: Default::default(), + } + } +} + pub enum SelfKind { Borrow, MutBorrow,