Compare commits

..

9 Commits

Author SHA1 Message Date
384703fbd2 Fix bug in generic function name replacement 2025-08-25 21:15:48 +03:00
107303aa98 Update generic types for let-statements 2025-08-25 21:12:34 +03:00
08626c8559 Check type argument count 2025-08-25 21:00:37 +03:00
ffb3515aaa Implement generic type swapping 2025-08-25 20:49:38 +03:00
1e4667449a Find all function calls 2025-08-25 20:15:40 +03:00
6fbb26ba88 Start adding generics-pass 2025-08-25 20:04:30 +03:00
91fd0d4d5a Add generic typedef 2025-08-25 19:18:25 +03:00
13e462def7 Add generics parsing 2025-08-25 19:08:10 +03:00
ff83d7b4f0 Add generics to MIR data structures 2025-08-25 18:46:29 +03:00
32 changed files with 4807 additions and 28 deletions

3
.gitmodules vendored
View File

@ -1,3 +0,0 @@
[submodule "reid-llvm-lib"]
path = reid-llvm-lib
url = gitea@git.teascade.net:teascade/reid-llvm-lib.git

6
Cargo.lock generated
View File

@ -784,7 +784,7 @@ checksum = "53a49587ad06b26609c52e423de037e7f57f20d53535d66e08c695f347df952a"
[[package]]
name = "reid"
version = "1.0.0"
version = "1.0.0-beta.4"
dependencies = [
"argh",
"colored",
@ -796,7 +796,7 @@ dependencies = [
[[package]]
name = "reid-language-server"
version = "1.0.0"
version = "1.0.0-beta.1"
dependencies = [
"dashmap 6.1.0",
"reid",
@ -809,7 +809,7 @@ dependencies = [
[[package]]
name = "reid-lib"
version = "1.0.0"
version = "1.0.0-beta.4"
dependencies = [
"llvm-sys",
"thiserror",

10
examples/generics.reid Normal file
View File

@ -0,0 +1,10 @@
// Arithmetic, function calls and imports!
fn test<T>(value: T) -> T {
let b: T = value;
return b;
}
fn main() -> u32 {
return test<u64>(15) as u32 + test<u32>(5);
}

View File

@ -16,7 +16,7 @@ BINARY="$(echo $1 | cut -d'.' -f1)"".out"
echo $1
cargo run -p reid -- $@ && \
cargo run -p reid -- run $@ && \
./$BINARY ; echo "Return value: ""$?"
## Command from: clang -v hello.o -o test

@ -1 +0,0 @@
Subproject commit 100cd96a6dc3bb882ce60e78c4eab47e77fdd8c4

12
reid-llvm-lib/Cargo.toml Normal file
View File

@ -0,0 +1,12 @@
[package]
name = "reid-lib"
version = "1.0.0-beta.4"
edition = "2024"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
## LLVM Bindings
llvm-sys = {version ="201.0.1" }
## Make it easier to generate errors
thiserror = "1.0.44"

View File

@ -0,0 +1,56 @@
use reid_lib::{CmpPredicate, ConstValueKind, Context, FunctionFlags, Instr, TerminatorKind, Type};
fn main() {
use ConstValueKind::*;
use Instr::*;
let context = Context::new("libtest");
let module = context.module("test", true);
let main = module.function("main", None, Type::I32, Vec::new(), FunctionFlags::default());
let mut m_entry = main.block("entry");
let fibonacci = module.function("fibonacci", None, Type::I32, vec![Type::I32], FunctionFlags::default());
let arg = m_entry.build_named("const", Constant(I32(5))).unwrap();
let fibonacci_call = m_entry
.build_named("const", FunctionCall(fibonacci.value(), vec![arg]))
.unwrap();
m_entry.terminate(TerminatorKind::Ret(fibonacci_call)).unwrap();
let mut f_entry = fibonacci.block("entry");
let num_3 = f_entry.build_named("const", Constant(I32(3))).unwrap();
let param_n = f_entry.build_named("param", Param(0)).unwrap();
let cond = f_entry
.build_named("cmp", ICmp(CmpPredicate::LT, param_n, num_3))
.unwrap();
let mut then_b = fibonacci.block("then");
let mut else_b = fibonacci.block("else");
f_entry
.terminate(TerminatorKind::CondBr(cond, then_b.value(), else_b.value()))
.unwrap();
let ret_const = then_b.build_named("const", Constant(I32(1))).unwrap();
then_b.terminate(TerminatorKind::Ret(ret_const)).unwrap();
let const_1 = else_b.build_named("const", Constant(I32(1))).unwrap();
let const_2 = else_b.build_named("const", Constant(I32(2))).unwrap();
let param_1 = else_b.build_named("sub", Sub(param_n, const_1)).unwrap();
let param_2 = else_b.build_named("sub", Sub(param_n, const_2)).unwrap();
let call_1 = else_b
.build_named("fibonacci", FunctionCall(fibonacci.value(), vec![param_1]))
.unwrap();
let call_2 = else_b
.build_named("fibonacci", FunctionCall(fibonacci.value(), vec![param_2]))
.unwrap();
let add = else_b.build_named("add", Add(call_1, call_2)).unwrap();
else_b.terminate(TerminatorKind::Ret(add)).unwrap();
context.compile(None, Vec::new());
}

View File

@ -0,0 +1,835 @@
//! This module contains simply [`Builder`] and it's related utility Values.
//! Builder is the actual struct being modified when building the LLIR.
use std::{cell::RefCell, rc::Rc};
use crate::{
Block, BlockData, CompileResult, ConstValueKind, CustomTypeKind, ErrorKind, FunctionData, Instr, InstructionData,
ModuleData, NamedStruct, TerminatorKind, Type, TypeCategory, TypeData,
debug_information::{
DebugInformation, DebugLocationValue, DebugMetadataValue, DebugScopeValue, InstructionDebugRecordData,
},
util::match_types,
};
#[derive(Clone, Hash, Copy, PartialEq, Eq, PartialOrd)]
pub struct ModuleValue(pub(crate) usize);
#[derive(Clone, Hash, Copy, PartialEq, Eq, PartialOrd)]
pub struct TypeValue(pub(crate) ModuleValue, pub(crate) usize);
#[derive(Clone, Hash, Copy, PartialEq, Eq, PartialOrd)]
pub struct FunctionValue(pub(crate) ModuleValue, pub(crate) usize);
#[derive(Clone, Hash, Copy, PartialEq, Eq, PartialOrd)]
pub struct BlockValue(pub(crate) FunctionValue, pub(crate) usize);
#[derive(Clone, Hash, Copy, PartialEq, Eq, PartialOrd)]
pub struct InstructionValue(pub(crate) BlockValue, pub(crate) usize);
#[derive(Debug, Clone, Hash, Copy, PartialEq, Eq)]
pub struct ConstantValue(pub(crate) ModuleValue, pub(crate) usize);
#[derive(Debug, Clone, Hash, Copy, PartialEq, Eq)]
pub struct GlobalValue(pub(crate) ModuleValue, pub(crate) usize);
#[derive(Clone)]
pub struct ModuleHolder {
pub(crate) value: ModuleValue,
pub(crate) data: ModuleData,
pub(crate) functions: Vec<FunctionHolder>,
pub(crate) types: Vec<TypeHolder>,
pub(crate) debug_information: Option<DebugInformation>,
pub(crate) constants: Vec<ConstantValueHolder>,
pub(crate) globals: Vec<GlobalValueHolder>,
}
#[derive(Clone)]
pub struct ConstantValueHolder {
pub(crate) value: ConstantValue,
pub(crate) kind: ConstValueKind,
}
#[derive(Clone)]
pub struct GlobalValueHolder {
pub(crate) value: GlobalValue,
pub(crate) name: String,
pub(crate) initializer: ConstantValue,
}
#[derive(Clone)]
pub struct TypeHolder {
pub(crate) value: TypeValue,
pub(crate) data: TypeData,
}
#[derive(Clone)]
pub struct FunctionHolder {
pub(crate) value: FunctionValue,
pub(crate) data: FunctionData,
pub(crate) blocks: Vec<BlockHolder>,
/// Debug scope value of this current function
pub(crate) debug_info: Option<DebugScopeValue>,
}
#[derive(Clone)]
pub struct BlockHolder {
pub(crate) value: BlockValue,
pub(crate) data: BlockData,
pub(crate) instructions: Vec<InstructionHolder>,
}
#[derive(Clone)]
pub struct InstructionHolder {
pub(crate) value: InstructionValue,
pub(crate) data: InstructionData,
pub(crate) name: String,
pub(crate) record: Option<InstructionDebugRecordData>,
}
#[derive(Clone)]
pub(crate) struct Builder {
pub(crate) modules: Rc<RefCell<Vec<ModuleHolder>>>,
pub(crate) producer: String,
}
impl Builder {
pub fn new(producer: String) -> Builder {
Builder {
modules: Rc::new(RefCell::new(Vec::new())),
producer,
}
}
pub(crate) fn add_module(&self, data: ModuleData) -> ModuleValue {
let value = ModuleValue(self.modules.borrow().len());
self.modules.borrow_mut().push(ModuleHolder {
value,
data,
functions: Vec::new(),
types: Vec::new(),
debug_information: None,
constants: Vec::new(),
globals: Vec::new(),
});
value
}
pub(crate) fn set_debug_information(&self, mod_val: &ModuleValue, debug_info: DebugInformation) {
unsafe {
let mut modules = self.modules.borrow_mut();
let module = modules.get_unchecked_mut(mod_val.0);
module.debug_information = Some(debug_info);
}
}
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, data: FunctionData) -> FunctionValue {
unsafe {
let mut modules = self.modules.borrow_mut();
let module = modules.get_unchecked_mut(mod_val.0);
let value = FunctionValue(module.value, module.functions.len());
module.functions.push(FunctionHolder {
value,
data,
blocks: Vec::new(),
debug_info: None,
});
value
}
}
pub(crate) unsafe fn add_block(&self, fun_val: &FunctionValue, data: BlockData) -> BlockValue {
unsafe {
let mut modules = self.modules.borrow_mut();
let module = modules.get_unchecked_mut(fun_val.0.0);
let function = module.functions.get_unchecked_mut(fun_val.1);
let value = BlockValue(function.value, function.blocks.len());
function.blocks.push(BlockHolder {
value,
data,
instructions: Vec::new(),
});
value
}
}
pub(crate) unsafe fn add_instruction(
&self,
block_val: &BlockValue,
data: InstructionData,
name: String,
) -> CompileResult<InstructionValue> {
unsafe {
let mut modules = self.modules.borrow_mut();
let module = modules.get_unchecked_mut(block_val.0.0.0);
let function = module.functions.get_unchecked_mut(block_val.0.1);
let block = function.blocks.get_unchecked_mut(block_val.1);
let value = InstructionValue(block.value, block.instructions.len());
block.instructions.push(InstructionHolder {
value,
data,
name,
record: None,
});
// Drop modules so that it is no longer mutable borrowed
// (check_instruction requires an immutable borrow).
drop(modules);
self.check_instruction(&value)?;
Ok(value)
}
}
pub(crate) unsafe fn build_constant(&self, module: ModuleValue, kind: ConstValueKind) -> ConstantValue {
unsafe {
let mut modules = self.modules.borrow_mut();
let module = modules.get_unchecked_mut(module.0);
let value = ConstantValue(module.value, module.constants.len());
module.constants.push(ConstantValueHolder { value, kind });
value
}
}
pub(crate) unsafe fn add_global(
&self,
module: ModuleValue,
name: String,
initializer: ConstantValue,
) -> GlobalValue {
unsafe {
let mut modules = self.modules.borrow_mut();
let module = modules.get_unchecked_mut(module.0);
let value = GlobalValue(module.value, module.globals.len());
module.globals.push(GlobalValueHolder {
value,
name,
initializer,
});
value
}
}
pub(crate) unsafe fn find_function(&self, module: ModuleValue, name: &String) -> Option<FunctionValue> {
unsafe {
let mut modules = self.modules.borrow_mut();
let module = modules.get_unchecked_mut(module.0);
module.functions.iter().find(|f| f.data.name == *name).map(|f| f.value)
}
}
pub(crate) unsafe fn add_instruction_location(&self, value: &InstructionValue, location: DebugLocationValue) {
unsafe {
let mut modules = self.modules.borrow_mut();
let module = modules.get_unchecked_mut(value.0.0.0.0);
let function = module.functions.get_unchecked_mut(value.0.0.1);
let block = function.blocks.get_unchecked_mut(value.0.1);
let instr = block.instructions.get_unchecked_mut(value.1);
instr.data.location = Some(location)
}
}
pub(crate) unsafe fn add_instruction_metadata(&self, value: &InstructionValue, metadata: DebugMetadataValue) {
unsafe {
let mut modules = self.modules.borrow_mut();
let module = modules.get_unchecked_mut(value.0.0.0.0);
let function = module.functions.get_unchecked_mut(value.0.0.1);
let block = function.blocks.get_unchecked_mut(value.0.1);
let instr = block.instructions.get_unchecked_mut(value.1);
instr.data.meta = Some(metadata)
}
}
pub(crate) unsafe fn add_instruction_record(&self, value: &InstructionValue, record: InstructionDebugRecordData) {
unsafe {
let mut modules = self.modules.borrow_mut();
let module = modules.get_unchecked_mut(value.0.0.0.0);
let function = module.functions.get_unchecked_mut(value.0.0.1);
let block = function.blocks.get_unchecked_mut(value.0.1);
let instr = block.instructions.get_unchecked_mut(value.1);
instr.record = Some(record)
}
}
pub(crate) unsafe fn set_debug_subprogram(&self, value: &FunctionValue, subprogram: DebugScopeValue) {
unsafe {
let mut modules = self.modules.borrow_mut();
let module = modules.get_unchecked_mut(value.0.0);
let function = module.functions.get_unchecked_mut(value.1);
function.debug_info = Some(subprogram)
}
}
pub(crate) unsafe fn terminate(&self, block: &BlockValue, value: TerminatorKind) -> CompileResult<()> {
unsafe {
let mut modules = self.modules.borrow_mut();
let module = modules.get_unchecked_mut(block.0.0.0);
let function = module.functions.get_unchecked_mut(block.0.1);
let block = function.blocks.get_unchecked_mut(block.1);
if let Some(_) = &block.data.terminator {
Err(ErrorKind::BlockAlreadyTerminated)
} else {
block.data.terminator = Some(value);
Ok(())
}
}
}
pub(crate) unsafe fn set_terminator_location(
&self,
block: &BlockValue,
location: DebugLocationValue,
) -> CompileResult<()> {
unsafe {
let mut modules = self.modules.borrow_mut();
let module = modules.get_unchecked_mut(block.0.0.0);
let function = module.functions.get_unchecked_mut(block.0.1);
let block = function.blocks.get_unchecked_mut(block.1);
if let Some(_) = &block.data.terminator_location {
Err(ErrorKind::BlockTerminatorLocated)
} else {
block.data.terminator_location = Some(location);
Ok(())
}
}
}
pub(crate) unsafe fn delete_block(&self, block: &BlockValue) -> CompileResult<()> {
unsafe {
let mut modules = self.modules.borrow_mut();
let module = modules.get_unchecked_mut(block.0.0.0);
let function = module.functions.get_unchecked_mut(block.0.1);
let block = function.blocks.get_unchecked_mut(block.1);
block.data.deleted = true;
Ok(())
}
}
#[allow(dead_code)]
pub(crate) unsafe fn module_data(&self, value: &ModuleValue) -> ModuleData {
unsafe { self.modules.borrow().get_unchecked(value.0).data.clone() }
}
pub(crate) unsafe fn function_data(&self, value: &FunctionValue) -> FunctionData {
unsafe {
self.modules
.borrow()
.get_unchecked(value.0.0)
.functions
.get_unchecked(value.1)
.data
.clone()
}
}
#[allow(dead_code)]
pub(crate) unsafe fn block_data(&self, value: &BlockValue) -> BlockData {
unsafe {
self.modules
.borrow()
.get_unchecked(value.0.0.0)
.functions
.get_unchecked(value.0.1)
.blocks
.get_unchecked(value.1)
.data
.clone()
}
}
pub(crate) unsafe fn instr_data(&self, value: &InstructionValue) -> InstructionData {
unsafe {
self.modules
.borrow()
.get_unchecked(value.0.0.0.0)
.functions
.get_unchecked(value.0.0.1)
.blocks
.get_unchecked(value.0.1)
.instructions
.get_unchecked(value.1)
.data
.clone()
}
}
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() }
}
pub(crate) fn get_modules(&self) -> Rc<RefCell<Vec<ModuleHolder>>> {
self.modules.clone()
}
pub(crate) fn get_global_initializer(&self, value: GlobalValue) -> ConstantValue {
unsafe {
let modules = self.modules.borrow();
let module = modules.get_unchecked(value.0.0);
let global = module.globals.get_unchecked(value.1);
global.initializer
}
}
pub(crate) fn get_const_kind(&self, value: ConstantValue) -> ConstValueKind {
unsafe {
let modules = self.modules.borrow();
let module = modules.get_unchecked(value.0.0);
let constant = module.constants.get_unchecked(value.1);
constant.kind.clone()
}
}
pub fn check_instruction(&self, instruction: &InstructionValue) -> CompileResult<()> {
unsafe {
match self.instr_data(&instruction).kind {
Instr::Param(_) => Ok(()),
Instr::Constant(_) => Ok(()),
Instr::Add(lhs, rhs) => {
if match_types(&lhs, &rhs, &self)?.category().integer() {
Ok(())
} else {
Err(ErrorKind::TypeNotInteger(lhs, lhs.get_type(&self)?))
}
}
Instr::FAdd(lhs, rhs) => {
if match_types(&lhs, &rhs, &self)?.category() == TypeCategory::Real {
Ok(())
} else {
Err(ErrorKind::TypeWrongCategory(
lhs,
lhs.get_type(&self)?,
TypeCategory::Real,
))
}
}
Instr::Sub(lhs, rhs) => {
if match_types(&lhs, &rhs, &self)?.category().integer() {
Ok(())
} else {
Err(ErrorKind::TypeNotInteger(lhs, lhs.get_type(&self)?))
}
}
Instr::FSub(lhs, rhs) => {
if match_types(&lhs, &rhs, &self)?.category() == TypeCategory::Real {
Ok(())
} else {
Err(ErrorKind::TypeWrongCategory(
lhs,
lhs.get_type(&self)?,
TypeCategory::Real,
))
}
}
Instr::Mul(lhs, rhs) => {
if match_types(&lhs, &rhs, &self)?.category().integer() {
Ok(())
} else {
Err(ErrorKind::TypeNotInteger(lhs, lhs.get_type(&self)?))
}
}
Instr::FMul(lhs, rhs) => {
if match_types(&lhs, &rhs, &self)?.category() == TypeCategory::Real {
Ok(())
} else {
Err(ErrorKind::TypeWrongCategory(
lhs,
lhs.get_type(&self)?,
TypeCategory::Real,
))
}
}
Instr::UDiv(lhs, rhs) => {
if match_types(&lhs, &rhs, &self)?.category() == TypeCategory::UnsignedInteger {
Ok(())
} else {
Err(ErrorKind::TypeWrongCategory(
lhs,
lhs.get_type(&self)?,
TypeCategory::UnsignedInteger,
))
}
}
Instr::SDiv(lhs, rhs) => {
if match_types(&lhs, &rhs, &self)?.category() == TypeCategory::SignedInteger {
Ok(())
} else {
Err(ErrorKind::TypeWrongCategory(
lhs,
lhs.get_type(&self)?,
TypeCategory::SignedInteger,
))
}
}
Instr::FDiv(lhs, rhs) => {
if match_types(&lhs, &rhs, &self)?.category() == TypeCategory::Real {
Ok(())
} else {
Err(ErrorKind::TypeWrongCategory(
lhs,
lhs.get_type(&self)?,
TypeCategory::Real,
))
}
}
Instr::URem(lhs, rhs) => {
if match_types(&lhs, &rhs, &self)?.category() == TypeCategory::UnsignedInteger {
Ok(())
} else {
Err(ErrorKind::TypeWrongCategory(
lhs,
lhs.get_type(&self)?,
TypeCategory::UnsignedInteger,
))
}
}
Instr::SRem(lhs, rhs) => {
if match_types(&lhs, &rhs, &self)?.category() == TypeCategory::SignedInteger {
Ok(())
} else {
Err(ErrorKind::TypeWrongCategory(
lhs,
lhs.get_type(&self)?,
TypeCategory::SignedInteger,
))
}
}
Instr::FRem(lhs, rhs) => {
if match_types(&lhs, &rhs, &self)?.category() == TypeCategory::Real {
Ok(())
} else {
Err(ErrorKind::TypeWrongCategory(
lhs,
lhs.get_type(&self)?,
TypeCategory::Real,
))
}
}
Instr::And(lhs, rhs) => match_types(&lhs, &rhs, &self).map(|_| ()),
Instr::Or(lhs, rhs) => match_types(&lhs, &rhs, &self).map(|_| ()),
Instr::XOr(lhs, rhs) => match_types(&lhs, &rhs, &self).map(|_| ()),
Instr::ICmp(_, lhs, rhs) => {
let t = match_types(&lhs, &rhs, self)?;
if t.category().comparable() || !t.category().integer() {
Ok(())
} else {
Err(ErrorKind::TypeNotComparable(lhs, t))
}
}
Instr::FCmp(_, lhs, rhs) => {
let t = match_types(&lhs, &rhs, self)?;
if t.category().comparable() || t.category() != TypeCategory::Real {
Ok(())
} else {
Err(ErrorKind::TypeNotComparable(lhs, t))
}
}
Instr::FunctionCall(fun, params) => {
let param_types = self.function_data(&fun).params;
if param_types.len() != params.len() {
return Err(ErrorKind::InvalidLenParams(params.len(), param_types.len()));
}
for (a, b) in param_types.iter().zip(params) {
if *a != b.get_type(&self)? {
return Err(ErrorKind::TypesIncompatible(a.clone(), b.get_type(&self)?));
}
}
Ok(())
}
Instr::Phi(vals) => {
let mut iter = vals.iter();
// TODO error: Phi must contain at least one item
// TODO error: compile can actually crash here if any of the
// incoming values come from blocks that are added later
// than the one where this one exists.
let first = iter.next().ok_or(ErrorKind::EmptyPhiList)?;
for item in iter {
match_types(first, item, &self)?;
}
Ok(())
}
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(ErrorKind::TypesIncompatible(*ptr_ty_inner, load_ty))
}
} else {
Err(ErrorKind::NotPointer(ptr, ptr_ty))
}
}
Instr::Store(ptr, _) => {
let ty = ptr.get_type(&self)?;
if let Type::Ptr(_) = ty {
Ok(())
} else {
Err(ErrorKind::NotPointer(ptr, ty))
}
}
Instr::ArrayAlloca(_, val) => {
if val.get_type(self)?.category() == TypeCategory::UnsignedInteger {
Ok(())
} else {
Err(ErrorKind::TypeWrongCategory(
val,
val.get_type(self)?,
TypeCategory::UnsignedInteger,
))
}
}
Instr::GetElemPtr(ptr_val, _) => {
let ptr_ty = ptr_val.get_type(&self)?;
match ptr_ty {
Type::Ptr(_) => Ok(()),
_ => Err(ErrorKind::NotPointer(ptr_val, ptr_ty)),
}
}
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(ErrorKind::NoSuchField(*ty, idx));
}
}
}
Ok(())
} else {
Err(ErrorKind::NotStruct(ptr_val, *ty))
}
} else {
Err(ErrorKind::NotPointer(ptr_val, ptr_ty))
}
}
Instr::ExtractValue(val, _) => {
let val_ty = val.get_type(&self)?;
match val_ty {
Type::CustomType(custom) => match self.type_data(&custom).kind {
CustomTypeKind::NamedStruct(_) => Ok(()),
},
Type::Array(_, _) => Ok(()),
_ => Err(ErrorKind::NotExtractable(val, val_ty)),
}
}
Instr::Trunc(instr, ty) => instr.cast_to(self, &ty).map(|_| ()),
Instr::ZExt(instr, ty) => instr.cast_to(self, &ty).map(|_| ()),
Instr::SExt(instr, ty) => instr.cast_to(self, &ty).map(|_| ()),
Instr::FPTrunc(instr, ty) => instr.cast_to(self, &ty).map(|_| ()),
Instr::FPExt(instr, ty) => instr.cast_to(self, &ty).map(|_| ()),
Instr::FPToUI(instr, ty) => instr.cast_to(self, &ty).map(|_| ()),
Instr::FPToSI(instr, ty) => instr.cast_to(self, &ty).map(|_| ()),
Instr::UIToFP(instr, ty) => instr.cast_to(self, &ty).map(|_| ()),
Instr::SIToFP(instr, ty) => instr.cast_to(self, &ty).map(|_| ()),
Instr::PtrToInt(instr, ty) => instr.cast_to(self, &ty).map(|_| ()),
Instr::IntToPtr(instr, ty) => instr.cast_to(self, &ty).map(|_| ()),
Instr::BitCast(..) => Ok(()),
Instr::ShiftRightLogical(lhs, rhs) => match_types(&lhs, &rhs, &self).map(|_| ()),
Instr::ShiftRightArithmetic(lhs, rhs) => match_types(&lhs, &rhs, &self).map(|_| ()),
Instr::ShiftLeft(lhs, rhs) => match_types(&lhs, &rhs, &self).map(|_| ()),
Instr::GetGlobal(_) => Ok(()),
Instr::IsNull(val) => {
let val_ty = val.get_type(&self)?;
if let Type::Ptr(_) = val_ty {
Ok(())
} else {
Err(ErrorKind::NotPointer(val, val_ty))
}
}
}
}
}
pub fn is_block_used(&self, block_v: BlockValue) -> bool {
unsafe {
let modules = self.modules.borrow();
let module = modules.get_unchecked(block_v.0.0.0);
let function = module.functions.get_unchecked(block_v.0.1);
let block = function.blocks.get_unchecked(block_v.1);
if block.instructions.len() > 0 || block.data.terminator.is_some() {
return true;
}
for other in &function.blocks {
if let Some(term) = &other.data.terminator {
match term {
TerminatorKind::Ret(_) => {}
TerminatorKind::RetVoid => {}
TerminatorKind::Br(other_val) => {
if other_val == &block_v {
return true;
}
}
TerminatorKind::CondBr(_, then_other_v, else_other_v) => {
if then_other_v == &block_v || else_other_v == &block_v {
return true;
}
}
}
}
}
false
}
}
}
impl InstructionValue {
pub fn with_location(self, block: &Block, location: DebugLocationValue) -> InstructionValue {
unsafe {
block.builder.add_instruction_location(&self, location);
}
self
}
pub fn maybe_location(self, block: &mut Block, location: Option<DebugLocationValue>) -> InstructionValue {
unsafe {
if let Some(location) = location {
block.builder.add_instruction_location(&self, location);
}
}
self
}
pub fn add_record(&self, block: &mut Block, record: InstructionDebugRecordData) {
unsafe {
block.builder.add_instruction_record(self, record);
}
}
pub(crate) fn get_type(&self, builder: &Builder) -> CompileResult<Type> {
use Instr::*;
unsafe {
match &builder.instr_data(self).kind {
Param(nth) => builder
.function_data(&self.0.0)
.params
.get(*nth)
.cloned()
.ok_or(ErrorKind::NoSuchParam(self.0.0, *nth)),
Constant(c) => Ok(c.get_type()),
Add(lhs, rhs) => match_types(lhs, rhs, &builder),
FAdd(lhs, rhs) => match_types(lhs, rhs, &builder),
Sub(lhs, rhs) => match_types(lhs, rhs, &builder),
FSub(lhs, rhs) => match_types(lhs, rhs, &builder),
Mul(lhs, rhs) => match_types(lhs, rhs, &builder),
FMul(lhs, rhs) => match_types(lhs, rhs, &builder),
UDiv(lhs, rhs) => match_types(lhs, rhs, &builder),
SDiv(lhs, rhs) => match_types(lhs, rhs, &builder),
FDiv(lhs, rhs) => match_types(lhs, rhs, &builder),
URem(lhs, rhs) => match_types(lhs, rhs, &builder),
SRem(lhs, rhs) => match_types(lhs, rhs, &builder),
FRem(lhs, rhs) => match_types(lhs, rhs, &builder),
And(lhs, rhs) => match_types(lhs, rhs, &builder),
Or(lhs, rhs) => match_types(lhs, rhs, &builder),
XOr(lhs, rhs) => match_types(lhs, rhs, &builder),
ICmp(_, _, _) => Ok(Type::Bool),
FCmp(_, _, _) => Ok(Type::Bool),
FunctionCall(function_value, _) => Ok(builder.function_data(function_value).ret),
Phi(values) => values
.first()
.ok_or(ErrorKind::EmptyPhiList)
.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(instr, _) => {
let instr_ty = instr.get_type(builder)?;
let Type::Ptr(inner_ty) = &instr_ty else {
panic!("GetStructElemPtr on non-pointer! ({:?})", &instr_ty)
};
match *inner_ty.clone() {
Type::Array(elem_ty, _) => Ok(Type::Ptr(Box::new(*elem_ty.clone()))),
_ => Ok(instr_ty),
}
}
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)))
}
ExtractValue(instr, idx) => {
let instr_ty = instr.get_type(builder)?;
Ok(match instr_ty {
Type::CustomType(struct_ty) => {
let data = builder.type_data(&struct_ty);
match data.kind {
CustomTypeKind::NamedStruct(named_struct) => {
named_struct.1.get(*idx as usize).unwrap().clone()
}
}
}
Type::Array(elem_ty, _) => *elem_ty.clone(),
_ => return Err(ErrorKind::NotExtractable(*instr, instr_ty)),
})
}
Trunc(instr, ty) => instr.cast_to(builder, ty).map(|_| ty.clone()),
ZExt(instr, ty) => instr.cast_to(builder, ty).map(|_| ty.clone()),
SExt(instr, ty) => instr.cast_to(builder, ty).map(|_| ty.clone()),
FPTrunc(instr, ty) => instr.cast_to(builder, ty).map(|_| ty.clone()),
FPExt(instr, ty) => instr.cast_to(builder, ty).map(|_| ty.clone()),
FPToUI(instr, ty) => instr.cast_to(builder, ty).map(|_| ty.clone()),
FPToSI(instr, ty) => instr.cast_to(builder, ty).map(|_| ty.clone()),
UIToFP(instr, ty) => instr.cast_to(builder, ty).map(|_| ty.clone()),
SIToFP(instr, ty) => instr.cast_to(builder, ty).map(|_| ty.clone()),
PtrToInt(instr, ty) => instr.cast_to(builder, ty).map(|_| ty.clone()),
IntToPtr(instr, ty) => instr.cast_to(builder, ty).map(|_| ty.clone()),
BitCast(_, ty) => Ok(ty.clone()),
ShiftRightLogical(lhs, _) => lhs.get_type(builder),
ShiftRightArithmetic(lhs, _) => lhs.get_type(builder),
ShiftLeft(lhs, _) => lhs.get_type(builder),
GetGlobal(global_value) => {
let constant = builder.get_global_initializer(*global_value);
let kind = builder.get_const_kind(constant);
Ok(kind.get_type())
}
IsNull(_) => Ok(Type::Bool),
}
}
}
fn cast_to(&self, builder: &Builder, ty: &Type) -> CompileResult<Instr> {
let own_type = self.get_type(builder)?;
own_type
.cast_instruction(*self, &ty)
.ok_or(ErrorKind::ImpossibleCast(own_type, ty.clone()))
}
}

1257
reid-llvm-lib/src/compile.rs Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,399 @@
use std::{
cell::{Ref, RefCell, RefMut},
rc::Rc,
};
use crate::builder::InstructionValue;
/// Represents 1. the compilation context, 2. subprogram or 3. a lexical scope
#[derive(Clone, Hash, PartialEq, Eq)]
pub struct DebugScopeValue(pub Vec<usize>);
#[derive(Clone, Hash, PartialEq, Eq)]
pub struct DebugLocationValue(pub DebugScopeValue, pub usize);
#[derive(Clone, Copy, Hash, PartialEq, Eq)]
pub struct DebugTypeValue(pub usize);
#[derive(Clone, Copy, Hash, PartialEq, Eq)]
pub struct DebugMetadataValue(pub usize);
#[derive(Debug, Clone)]
pub struct DebugFileData {
pub name: String,
pub directory: String,
}
#[derive(Debug, Clone)]
pub(crate) struct DebugScopeHolder {
pub(crate) value: DebugScopeValue,
pub(crate) data: DebugScopeData,
pub(crate) inner_scopes: Vec<DebugScopeHolder>,
}
#[derive(Debug, Clone)]
pub struct DebugMetadataHolder {
pub(crate) location: DebugLocation,
pub(crate) value: DebugMetadataValue,
pub(crate) data: DebugMetadata,
}
#[derive(Clone)]
pub struct DebugTypeHolder {
pub(crate) value: DebugTypeValue,
pub(crate) data: DebugTypeData,
}
#[derive(Debug, Clone)]
pub(crate) struct DebugLocationHolder {
pub(crate) scope: DebugScopeValue,
pub(crate) value: DebugLocationValue,
pub(crate) location: DebugLocation,
}
#[derive(Debug, Clone)]
pub struct DebugInformation {
pub file: DebugFileData,
scope: Rc<RefCell<DebugScopeHolder>>,
locations: Rc<RefCell<Vec<DebugLocationHolder>>>,
metadata: Rc<RefCell<Vec<DebugMetadataHolder>>>,
types: Rc<RefCell<Vec<DebugTypeHolder>>>,
}
impl DebugInformation {
pub fn from_file(file: DebugFileData) -> (DebugInformation, DebugScopeValue) {
let scope_value = DebugScopeValue(Vec::new());
(
DebugInformation {
file,
scope: Rc::new(RefCell::new(DebugScopeHolder {
value: scope_value.clone(),
inner_scopes: Vec::new(),
data: DebugScopeData {
parent: None,
kind: DebugScopeKind::CodegenContext,
},
})),
locations: Rc::new(RefCell::new(Vec::new())),
metadata: Rc::new(RefCell::new(Vec::new())),
types: Rc::new(RefCell::new(Vec::new())),
},
scope_value,
)
}
pub fn location(&self, scope_value: &DebugScopeValue, location: DebugLocation) -> DebugLocationValue {
let value = DebugLocationValue(scope_value.clone(), self.locations.borrow().len());
let location = DebugLocationHolder {
scope: scope_value.clone(),
value: value.clone(),
location,
};
self.locations.borrow_mut().push(location);
value
}
pub fn debug_type(&self, kind: DebugTypeData) -> DebugTypeValue {
let mut types = self.types.borrow_mut();
let value = DebugTypeValue(types.len());
types.push(DebugTypeHolder {
value: value.clone(),
data: kind,
});
value
}
pub fn metadata(&self, location: &DebugLocation, kind: DebugMetadata) -> DebugMetadataValue {
let mut metadata = self.metadata.borrow_mut();
let value = DebugMetadataValue(metadata.len());
metadata.push(DebugMetadataHolder {
location: location.clone(),
value: value.clone(),
data: kind,
});
value
}
pub fn subprogram(&self, parent: DebugScopeValue, kind: DebugSubprogramData) -> DebugScopeValue {
unsafe {
let mut outer_scope = RefMut::map(self.scope.borrow_mut(), |mut v| {
for i in &parent.0 {
v = v.inner_scopes.get_unchecked_mut(*i);
}
v
});
let mut arr = parent.0.clone();
arr.push(outer_scope.inner_scopes.len());
let value = DebugScopeValue(arr);
outer_scope.inner_scopes.push(DebugScopeHolder {
value: value.clone(),
inner_scopes: Vec::new(),
data: DebugScopeData {
parent: Some(parent.clone()),
kind: DebugScopeKind::Subprogram(kind),
},
});
value
}
}
pub fn lexical_scope(&self, parent: &DebugScopeValue, data: DebugLexicalScope) -> DebugScopeValue {
unsafe {
let mut outer_scope = RefMut::map(self.scope.borrow_mut(), |mut v| {
for i in &parent.0 {
v = v.inner_scopes.get_unchecked_mut(*i);
}
v
});
let mut arr = parent.0.clone();
arr.push(outer_scope.inner_scopes.len());
let value = DebugScopeValue(arr);
outer_scope.inner_scopes.push(DebugScopeHolder {
value: value.clone(),
inner_scopes: Vec::new(),
data: DebugScopeData {
parent: Some(parent.clone()),
kind: DebugScopeKind::LexicalScope(data),
},
});
value
}
}
pub fn get_metadata(&self, value: DebugMetadataValue) -> DebugMetadata {
unsafe { self.metadata.borrow().get_unchecked(value.0).data.clone() }
}
pub fn get_metadata_location(&self, value: DebugMetadataValue) -> DebugLocation {
unsafe { self.metadata.borrow().get_unchecked(value.0).location.clone() }
}
pub fn get_scope_data(&self, value: &DebugScopeValue) -> Option<DebugScopeData> {
let scope = Ref::filter_map(self.scope.borrow(), |v: &DebugScopeHolder| {
let mut opt = Some(v);
for i in &value.0 {
if let Some(inner) = opt {
opt = inner.inner_scopes.get(*i);
}
}
opt
});
if let Ok(scope) = scope {
Some(scope.data.clone())
} else {
None
}
}
pub fn get_type_data(&self, value: DebugTypeValue) -> DebugTypeData {
unsafe { self.types.borrow().get_unchecked(value.0).data.clone() }
}
pub fn get_location(&self, value: &DebugLocationValue) -> DebugLocation {
unsafe { self.locations.borrow().get_unchecked(value.1).location.clone() }
}
pub fn get_metadatas(&self) -> Rc<RefCell<Vec<DebugMetadataHolder>>> {
self.metadata.clone()
}
pub(crate) fn get_scope(&self) -> Rc<RefCell<DebugScopeHolder>> {
self.scope.clone()
}
pub fn get_types(&self) -> Rc<RefCell<Vec<DebugTypeHolder>>> {
self.types.clone()
}
pub(crate) fn get_locations(&self) -> Rc<RefCell<Vec<DebugLocationHolder>>> {
self.locations.clone()
}
}
#[derive(Clone)]
pub struct DebugLocation {
pub scope: DebugScopeValue,
pub pos: DebugPosition,
}
#[derive(Clone, Copy)]
pub struct DebugPosition {
pub line: u32,
pub column: u32,
}
#[derive(Debug, Clone)]
pub enum DebugMetadata {
ParamVar(DebugParamVariable),
LocalVar(DebugLocalVariable),
VarAssignment,
}
#[derive(Debug, Clone)]
pub struct DebugParamVariable {
pub name: String,
/// the index (starting from 1) of this variable in the subprogram
/// parameters. arg_idx should not conflict with other parameters of the
/// same subprogram.
pub arg_idx: u32,
pub ty: DebugTypeValue,
/// If this variable will be referenced from its containing subprogram, and
/// will survive some optimizations.
pub always_preserve: bool,
pub flags: DwarfFlags,
}
#[derive(Debug, Clone)]
pub struct DebugLocalVariable {
pub name: String,
pub ty: DebugTypeValue,
pub always_preserve: bool,
pub flags: DwarfFlags,
}
impl Default for DebugSubprogramOptionals {
fn default() -> Self {
Self {
scope_line: 0,
is_local: false,
is_definition: true,
is_optimized: false,
flags: DwarfFlags,
}
}
}
#[derive(Debug, Clone)]
pub struct DwarfFlags;
#[derive(Clone)]
pub enum DebugTypeData {
Basic(DebugBasicType),
Subprogram(DebugSubprogramType),
Pointer(DebugPointerType),
Array(DebugArrayType),
Struct(DebugStructType),
}
#[derive(Clone)]
pub struct DebugBasicType {
pub name: String,
/// Size of the type.
pub size_bits: u64,
/// DWARF encoding code, e.g., dwarf::DW_ATE_float.
pub encoding: DwarfEncoding,
/// Optional DWARF attributes, e.g., DW_AT_endianity.
pub flags: DwarfFlags,
}
#[derive(Clone)]
pub struct DebugArrayType {
pub size_bits: u64,
pub align_bits: u32,
pub element_type: DebugTypeValue,
pub length: u64,
}
#[derive(Clone)]
pub struct DebugPointerType {
pub name: String,
pub pointee: DebugTypeValue,
pub size_bits: u64,
}
#[derive(Clone)]
pub struct DebugStructType {
pub name: String,
pub scope: DebugScopeValue,
pub pos: Option<DebugPosition>,
pub size_bits: u64,
pub flags: DwarfFlags,
pub fields: Vec<DebugFieldType>,
}
#[derive(Clone)]
pub struct DebugFieldType {
pub name: String,
pub scope: DebugScopeValue,
pub pos: Option<DebugPosition>,
pub size_bits: u64,
pub offset: u64,
pub flags: DwarfFlags,
pub ty: DebugTypeValue,
}
#[derive(Clone)]
pub struct DebugSubprogramType {
pub parameters: Vec<DebugTypeValue>,
pub flags: DwarfFlags,
}
#[derive(Debug, Clone, Copy)]
pub enum DwarfEncoding {
Address = 1,
Boolean = 2,
Float = 4,
Signed = 5,
SignedChar = 6,
Unsigned = 7,
UnsignedChar = 8,
}
#[derive(Debug, Clone)]
pub struct DebugScopeData {
pub parent: Option<DebugScopeValue>,
pub kind: DebugScopeKind,
}
#[derive(Debug, Clone)]
pub enum DebugScopeKind {
CodegenContext,
LexicalScope(DebugLexicalScope),
Subprogram(DebugSubprogramData),
}
#[derive(Debug, Clone)]
pub struct DebugLexicalScope {
pub location: DebugLocation,
}
#[derive(Debug, Clone)]
pub struct DebugSubprogramData {
/// Function name.
pub name: String,
pub outer_scope: DebugScopeValue,
/// Used for line number.
pub location: DebugLocation,
/// Function type.
pub ty: DebugTypeValue,
pub opts: DebugSubprogramOptionals,
}
#[derive(Debug, Clone)]
pub struct DebugSubprogramOptionals {
/// Set to the beginning of the scope this starts
pub scope_line: u32,
pub is_local: bool,
pub is_definition: bool,
pub is_optimized: bool,
/// These flags are used to emit dwarf attributes. e.g. is this function
/// prototyped or not.
pub flags: DwarfFlags,
}
#[derive(Clone)]
pub struct InstructionDebugRecordData {
pub scope: DebugScopeValue,
pub variable: DebugMetadataValue,
pub location: DebugLocation,
pub kind: DebugRecordKind,
}
#[derive(Clone, Copy)]
pub enum DebugRecordKind {
Declare(InstructionValue),
Value(InstructionValue),
}

595
reid-llvm-lib/src/fmt.rs Normal file
View File

@ -0,0 +1,595 @@
//! Debug implementations for relevant types
use std::{
fmt::{Debug, Display, Write},
marker::PhantomData,
};
use crate::{
CmpPredicate, Context, Instr, InstructionData, TerminatorKind,
builder::*,
debug_information::{
DebugArrayType, DebugBasicType, DebugFieldType, DebugInformation, DebugLocalVariable, DebugLocation,
DebugLocationValue, DebugMetadata, DebugMetadataValue, DebugParamVariable, DebugPointerType, DebugPosition,
DebugRecordKind, DebugScopeValue, DebugStructType, DebugSubprogramType, DebugTypeData, DebugTypeHolder,
DebugTypeValue,
},
pad_adapter::PadAdapter,
};
impl Display for Context {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Display::fmt(&self.builder, f)
}
}
impl Display for Builder {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
writeln!(f, "Producer: {}", self.producer)?;
for module in self.modules.borrow().iter() {
if module.data.is_main {
write!(f, "main ")?;
}
writeln!(f, "{} ({:?}) {{", module.data.name, module.value)?;
for function in &module.functions {
let mut state = Default::default();
let mut inner = PadAdapter::wrap(f, &mut state);
function.builder_fmt(&mut inner, self, &module.debug_information)?;
}
writeln!(f, "}}")?;
}
Ok(())
}
}
impl FunctionHolder {
fn builder_fmt(
&self,
f: &mut impl std::fmt::Write,
builder: &Builder,
debug: &Option<DebugInformation>,
) -> std::fmt::Result {
if self.data.flags.is_imported {
write!(f, "imported ")?;
}
if self.data.flags.is_extern {
write!(f, "extern ")?;
}
if self.data.flags.is_pub {
write!(f, "pub ")?;
}
if self.data.flags.is_main {
write!(f, "main ")?;
}
let params = self
.data
.params
.iter()
.map(|p| format!("{:?}", p))
.collect::<Vec<_>>()
.join(", ");
write!(f, "fn {}({}) -> {:?} ", self.data.name, params, self.data.ret)?;
writeln!(f, "{{")?;
let mut state = Default::default();
let mut inner = PadAdapter::wrap(f, &mut state);
writeln!(inner, "(Value = {:?}) ", self.value)?;
if let Some(debug) = &self.debug_info {
writeln!(inner, "(Debug = {:?})", debug)?;
}
for block in &self.blocks {
let mut state = Default::default();
let mut inner = PadAdapter::wrap(&mut inner, &mut state);
block.builder_fmt(&mut inner, builder, debug)?;
}
writeln!(f, "}}")?;
Ok(())
}
}
impl BlockHolder {
fn builder_fmt(
&self,
f: &mut impl std::fmt::Write,
builder: &Builder,
debug: &Option<DebugInformation>,
) -> std::fmt::Result {
if self.data.deleted {
write!(f, "deleted ")?;
}
writeln!(f, "{} ({:?}):", self.data.name, self.value)?;
let mut state = Default::default();
let mut inner = PadAdapter::wrap(f, &mut state);
for instr in &self.instructions {
instr.builder_fmt(&mut inner, builder, debug)?;
}
if let Some(terminator) = &self.data.terminator {
terminator.builder_fmt(&mut inner, builder, debug)?;
}
if let Some(location) = &self.data.terminator_location {
writeln!(inner, " ^ (At {}) ", debug.as_ref().unwrap().get_location(location))?;
}
Ok(())
}
}
impl InstructionHolder {
fn builder_fmt(
&self,
f: &mut impl std::fmt::Write,
_builder: &Builder,
debug: &Option<DebugInformation>,
) -> std::fmt::Result {
if let Some(record) = &self.record {
let kind = match record.kind {
DebugRecordKind::Declare(instruction_value) => {
format!("= {:?} (Assign)", instruction_value)
}
DebugRecordKind::Value(instruction_value) => {
format!("= {:?} (Value)", instruction_value)
}
};
if let Some(debug) = debug {
writeln!(f, " (Debug {} {})", record.variable.hr(debug), kind)?;
}
}
writeln!(f, "{:?} ({}) = {:?} ", self.value, self.name, self.data.kind)?;
if let Some(debug) = debug {
if let Some(location) = &self.data.location {
writeln!(f, " ^ (At {}) ", debug.get_location(location))?;
}
if let Some(meta) = self.data.meta {
writeln!(f, " ^ (Meta {}) ", meta.hr(debug))?;
}
}
writeln!(f)?;
Ok(())
}
}
impl TerminatorKind {
fn builder_fmt(
&self,
f: &mut impl std::fmt::Write,
_builder: &Builder,
_debug: &Option<DebugInformation>,
) -> std::fmt::Result {
match self {
TerminatorKind::Ret(instr) => writeln!(f, "ret {:?}", instr),
TerminatorKind::RetVoid => writeln!(f, "ret void"),
TerminatorKind::Br(block) => writeln!(f, "br {:?}", block),
TerminatorKind::CondBr(instr, lhs, rhs) => {
writeln!(f, "condbr {:?}, {:?} or {:?}", instr, lhs, rhs)
}
}
}
}
impl DebugMetadataValue {
fn hr(&self, debug: &DebugInformation) -> String {
let kind = match debug.get_metadata(*self) {
DebugMetadata::ParamVar(DebugParamVariable { name, arg_idx, ty, .. }) => {
format!("param {} (idx {}) (type {:?}) ", name, arg_idx, ty)
}
DebugMetadata::LocalVar(DebugLocalVariable { name, ty, .. }) => {
format!("var {} (type {:?}) ", name, ty)
}
DebugMetadata::VarAssignment => todo!(),
};
format!("{} at {}", kind, debug.get_metadata_location(*self))
}
}
impl Display for DebugLocation {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?} on scope {:?}", self.pos, self.scope)
}
}
impl Display for DebugPosition {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "line {}, col {}", self.line, self.column)
}
}
impl Debug for Builder {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_list().entries(self.get_modules().borrow().iter());
Ok(())
}
}
pub struct PrintableModule<'ctx> {
pub phantom: PhantomData<&'ctx ()>,
pub module: ModuleHolder,
}
impl<'ctx> Debug for PrintableModule<'ctx> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Debug::fmt(&self.module, f)
}
}
impl Debug for ModuleHolder {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple(&format!("{}({:#?}) ", self.data.name, self.value))
.field(&self.functions)
// .field(&self.debug_information)
.finish()
}
}
impl Debug for FunctionHolder {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple(&format!(
"{}({:?}) -> {:?} ",
self.data.name, self.data.params, self.data.ret
))
.field(&self.blocks)
.finish()
}
}
impl Debug for BlockHolder {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let deleted = if self.data.deleted { " (deleted)" } else { "" };
f.debug_tuple(&format!("{}[{:?}]{} ", &self.data.name, &self.value, deleted))
.field(&self.instructions)
.field(&self.data.terminator)
.field(&self.data.terminator_location)
.finish()
}
}
impl Debug for InstructionHolder {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.value.fmt(f)?;
write!(f, " ({})", self.name)?;
f.write_str(" = ")?;
self.data.fmt(f)
}
}
impl Debug for InstructionData {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.kind.fmt(f)?;
if let Some(location) = &self.location {
write!(f, " ({:?})", location)?;
}
Ok(())
}
}
impl Debug for ModuleValue {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "M[{:0>2}]", self.0)
}
}
impl Debug for FunctionValue {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "F[{:0>2}-{:0>2}]", &self.0.0, self.1)
}
}
impl Debug for BlockValue {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "B[{:0>2}-{:0>2}-{:0>2}]", &self.0.0.0, &self.0.1, self.1)
}
}
impl Debug for InstructionValue {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "%{}.{}.{}.{}", self.0.0.0.0, self.0.0.1, self.0.1, self.1)
}
}
// impl Debug for InstructionValue {
// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
// write!(
// f,
// "I[{:0>2}-{:0>2}-{:0>2}-{:0>2}]",
// &self.0.0.0.0, &self.0.0.1, &self.0.1, self.1
// )
// }
// }
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 {
Instr::Param(nth) => fmt_call(f, &"Param", &nth),
Instr::Constant(c) => c.fmt(f),
Instr::Add(lhs, rhs) => fmt_binop(f, lhs, &"+", rhs),
Instr::FAdd(lhs, rhs) => fmt_binop(f, lhs, &"+", rhs),
Instr::Sub(lhs, rhs) => fmt_binop(f, lhs, &"-", rhs),
Instr::FSub(lhs, rhs) => fmt_binop(f, lhs, &"-", rhs),
Instr::Mul(lhs, rhs) => fmt_binop(f, lhs, &"*", rhs),
Instr::FMul(lhs, rhs) => fmt_binop(f, lhs, &"*", rhs),
Instr::UDiv(lhs, rhs) => fmt_binop(f, lhs, &"/", rhs),
Instr::SDiv(lhs, rhs) => fmt_binop(f, lhs, &"/", rhs),
Instr::FDiv(lhs, rhs) => fmt_binop(f, lhs, &"/", rhs),
Instr::URem(lhs, rhs) => fmt_binop(f, lhs, &"%", rhs),
Instr::SRem(lhs, rhs) => fmt_binop(f, lhs, &"%", rhs),
Instr::FRem(lhs, rhs) => fmt_binop(f, lhs, &"%", rhs),
Instr::And(lhs, rhs) => fmt_binop(f, lhs, &"&&", rhs),
Instr::Phi(val) => fmt_call(f, &"Phi", &val),
Instr::ICmp(cmp, lhs, rhs) => fmt_binop(f, lhs, cmp, rhs),
Instr::FCmp(cmp, lhs, rhs) => fmt_binop(f, lhs, cmp, rhs),
Instr::FunctionCall(fun, params) => fmt_call(f, fun, params),
Instr::Alloca(ty) => write!(f, "alloca<{:?}>", ty),
Instr::Load(val, ty) => write!(f, "load<{:?}>({:?})", ty, val),
Instr::Store(ptr, val) => write!(f, "store({:?} = {:?})", ptr, val),
Instr::ArrayAlloca(ty, instruction_value) => {
write!(f, "array_alloca<{:?}>({:?})", ty, instruction_value)
}
Instr::GetElemPtr(instruction_value, items) => fmt_index(
f,
instruction_value,
&items
.iter()
.map(|expr| format!("{:?}", expr))
.collect::<Vec<_>>()
.join(", "),
),
Instr::GetStructElemPtr(instruction_value, index) => {
write!(f, "GEP(")?;
fmt_index(f, instruction_value, &index.to_string())?;
write!(f, ")")
}
Instr::ExtractValue(instruction_value, index) => fmt_index(f, instruction_value, &index.to_string()),
Instr::Trunc(instr_val, ty) => {
write!(f, "{:?} to {:?} ({})", instr_val, ty, self.default_name())
}
Instr::ZExt(instr_val, ty) => {
write!(f, "{:?} to {:?} ({})", instr_val, ty, self.default_name())
}
Instr::SExt(instr_val, ty) => {
write!(f, "{:?} to {:?} ({})", instr_val, ty, self.default_name())
}
Instr::FPTrunc(instr_val, ty) => {
write!(f, "{:?} to {:?} ({})", instr_val, ty, self.default_name())
}
Instr::FPExt(instr_val, ty) => {
write!(f, "{:?} to {:?} ({})", instr_val, ty, self.default_name())
}
Instr::FPToUI(instr_val, ty) => {
write!(f, "{:?} to {:?} ({})", instr_val, ty, self.default_name())
}
Instr::FPToSI(instr_val, ty) => {
write!(f, "{:?} to {:?} ({})", instr_val, ty, self.default_name())
}
Instr::UIToFP(instr_val, ty) => {
write!(f, "{:?} to {:?} ({})", instr_val, ty, self.default_name())
}
Instr::SIToFP(instr_val, ty) => {
write!(f, "{:?} to {:?} ({})", instr_val, ty, self.default_name())
}
Instr::PtrToInt(instr_val, ty) => {
write!(f, "{:?} to {:?} ({})", instr_val, ty, self.default_name())
}
Instr::IntToPtr(instr_val, ty) => {
write!(f, "{:?} to {:?} ({})", instr_val, ty, self.default_name())
}
Instr::BitCast(instr_val, ty) => {
write!(f, "{:?} to {:?} ({})", instr_val, ty, self.default_name())
}
Instr::Or(lhs, rhs) => fmt_binop(f, lhs, &"||", rhs),
Instr::XOr(lhs, rhs) => fmt_binop(f, lhs, &"^", rhs),
Instr::ShiftRightLogical(lhs, rhs) => fmt_binop(f, lhs, &">>l", rhs),
Instr::ShiftRightArithmetic(lhs, rhs) => fmt_binop(f, lhs, &">>a", rhs),
Instr::ShiftLeft(lhs, rhs) => fmt_binop(f, lhs, &"<<", rhs),
Instr::GetGlobal(global_value) => write!(f, "global {:?}", global_value),
Instr::IsNull(_) => write!(f, "is_null"),
}
}
}
fn fmt_binop(
f: &mut std::fmt::Formatter<'_>,
lhs: &impl std::fmt::Debug,
op: &impl std::fmt::Debug,
rhs: &impl std::fmt::Debug,
) -> std::fmt::Result {
lhs.fmt(f)?;
f.write_char(' ')?;
op.fmt(f)?;
f.write_char(' ')?;
rhs.fmt(f)
}
fn fmt_call(
f: &mut std::fmt::Formatter<'_>,
fun: &impl std::fmt::Debug,
params: &impl std::fmt::Debug,
) -> std::fmt::Result {
fun.fmt(f)?;
f.write_char('(')?;
params.fmt(f)?;
f.write_char(')')
}
fn fmt_index(
f: &mut std::fmt::Formatter<'_>,
fun: &impl std::fmt::Debug,
params: &impl std::fmt::Debug,
) -> std::fmt::Result {
fun.fmt(f)?;
f.write_char('[')?;
params.fmt(f)?;
f.write_char(']')
}
impl Debug for CmpPredicate {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::LT => write!(f, "<"),
Self::GT => write!(f, ">"),
Self::LE => write!(f, "<="),
Self::GE => write!(f, ">="),
Self::EQ => write!(f, "=="),
Self::NE => write!(f, "!="),
}
}
}
impl Debug for TerminatorKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Ret(val) => {
write!(f, "Ret ")?;
val.fmt(f)
}
Self::RetVoid => write!(f, "Void Ret"),
Self::Br(val) => {
write!(f, "Br ")?;
val.fmt(f)
}
Self::CondBr(cond, b1, b2) => {
write!(f, "CondBr ")?;
cond.fmt(f)?;
write!(f, " ? ")?;
b1.fmt(f)?;
write!(f, " : ")?;
b2.fmt(f)?;
Ok(())
}
}
}
}
impl Debug for DebugTypeHolder {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple(&format!("DebugTypeHolder {:?}", self.value))
.field(&self.data)
.finish()
}
}
impl Debug for DebugTypeData {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
DebugTypeData::Basic(ty) => Debug::fmt(ty, f),
DebugTypeData::Subprogram(ty) => Debug::fmt(ty, f),
DebugTypeData::Pointer(ty) => Debug::fmt(ty, f),
DebugTypeData::Array(ty) => Debug::fmt(ty, f),
DebugTypeData::Struct(ty) => Debug::fmt(ty, f),
}
}
}
impl Debug for DebugBasicType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("BasicType")
.field(&self.name)
.field(&self.size_bits)
.field(&self.encoding)
.field(&self.flags)
.finish()
}
}
impl Debug for DebugStructType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Struct")
.field("name", &self.name)
.field("scope", &self.scope)
.field("pos", &self.pos)
.field("size_bit", &self.size_bits)
.field("flags", &self.flags)
.field("elements", &self.fields)
.finish()
}
}
impl Debug for DebugFieldType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct(&format!("Field({})", self.name))
.field("scope", &self.scope)
.field("pos", &self.pos)
.field("size_bits", &self.size_bits)
.field("offset", &self.offset)
.field("flags", &self.flags)
.field("ty", &self.ty)
.finish()
}
}
impl Debug for DebugSubprogramType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("Subprogram")
.field(&self.parameters)
.field(&self.flags)
.finish()
}
}
impl Debug for DebugPointerType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple(&format!("Pointer<{:?}>({})", self.pointee, self.name))
.field(&self.size_bits)
.finish()
}
}
impl Debug for DebugArrayType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct(&format!("Array<{:?}>[{}]", self.element_type, self.length))
.field("size_bits", &self.size_bits)
.field("align_bits", &self.align_bits)
.finish()
}
}
impl Debug for DebugMetadataValue {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Meta[{}]", self.0)
}
}
impl Debug for DebugScopeValue {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"Scope[{}]",
self.0.iter().map(|v| v.to_string()).collect::<Vec<_>>().join(", ")
)
}
}
impl Debug for DebugTypeValue {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Type[{}]", self.0)
}
}
impl Debug for DebugLocationValue {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Value[{:?}][{}]", self.0, self.1)
}
}
impl Debug for DebugLocation {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?} on scope {:?}", self.pos, self.scope)
}
}
impl Debug for DebugPosition {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "ln {}, col {}", self.line, self.column)
}
}

View File

@ -0,0 +1,263 @@
use crate::{CompileResult, Type, TypeCategory, builder::Builder};
#[derive(Clone, Debug)]
pub enum LLVMIntrinsic {
Abs(Type),
Max(Type),
Min(Type),
Memcpy(Type),
Sqrt(Type),
PowI(Type, Type),
Pow(Type),
Sin(Type),
Cos(Type),
Tan(Type),
ASin(Type),
ACos(Type),
ATan(Type),
ATan2(Type),
SinH(Type),
CosH(Type),
TanH(Type),
Log(Type),
Log2(Type),
Log10(Type),
Copysign(Type),
Floor(Type),
Ceil(Type),
Trunc(Type),
RoundEven(Type),
Round(Type),
}
impl LLVMIntrinsic {
pub(crate) fn signature(&self, builder: &Builder) -> CompileResult<(String, Vec<Type>, Type)> {
match self {
LLVMIntrinsic::Max(ty) => {
let name = match ty.category() {
TypeCategory::SignedInteger => format!("llvm.smax.{}", ty.llvm_ty_str(builder)),
TypeCategory::UnsignedInteger => format!("llvm.umax.{}", ty.llvm_ty_str(builder)),
TypeCategory::Real => format!("llvm.maximum.{}", 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() {
TypeCategory::SignedInteger => format!("llvm.smin.{}", ty.llvm_ty_str(builder)),
TypeCategory::UnsignedInteger => format!("llvm.umin.{}", ty.llvm_ty_str(builder)),
TypeCategory::Real => format!("llvm.minimum.{}", ty.llvm_ty_str(builder)),
_ => return Err(crate::ErrorKind::Null),
};
Ok((name, vec![ty.clone(), ty.clone()], ty.clone()))
}
LLVMIntrinsic::Abs(ty) => {
let name = match ty.category() {
TypeCategory::SignedInteger => format!("llvm.abs.{}", ty.llvm_ty_str(builder)),
TypeCategory::UnsignedInteger => format!("llvm.abs.{}", ty.llvm_ty_str(builder)),
TypeCategory::Real => format!("llvm.fabs.{}", ty.llvm_ty_str(builder)),
_ => return Err(crate::ErrorKind::Null),
};
Ok((name, vec![ty.clone(), Type::Bool], ty.clone()))
}
LLVMIntrinsic::Memcpy(ty) => {
let name = match ty.category() {
TypeCategory::Ptr => String::from("llvm.memcpy"),
_ => return Err(crate::ErrorKind::Null),
};
Ok((name, vec![ty.clone(), ty.clone(), Type::U64, Type::Bool], Type::Void))
}
LLVMIntrinsic::Sqrt(ty) => {
let name = match ty.category() {
TypeCategory::Real => format!("llvm.sqrt.{}", ty.llvm_ty_str(builder)),
_ => return Err(crate::ErrorKind::Null),
};
Ok((name, vec![ty.clone()], ty.clone()))
}
LLVMIntrinsic::PowI(ty1, ty2) => {
let name = match (ty1.category(), ty2.category()) {
(TypeCategory::Real, TypeCategory::SignedInteger) => {
format!("llvm.powi.{}.{}", ty1.llvm_ty_str(builder), ty2.llvm_ty_str(builder))
}
(TypeCategory::Real, TypeCategory::UnsignedInteger) => {
format!("llvm.powi.{}.{}", ty1.llvm_ty_str(builder), ty2.llvm_ty_str(builder))
}
_ => return Err(crate::ErrorKind::Null),
};
Ok((name, vec![ty1.clone(), ty2.clone()], ty1.clone()))
}
LLVMIntrinsic::Pow(ty) => {
let name = match ty.category() {
TypeCategory::Real => format!("llvm.pow.{}", ty.llvm_ty_str(builder)),
_ => return Err(crate::ErrorKind::Null),
};
Ok((name, vec![ty.clone(), ty.clone()], ty.clone()))
}
LLVMIntrinsic::Sin(ty) => {
let name = match ty.category() {
TypeCategory::Real => format!("llvm.sin.{}", ty.llvm_ty_str(builder)),
_ => return Err(crate::ErrorKind::Null),
};
Ok((name, vec![ty.clone()], ty.clone()))
}
LLVMIntrinsic::Cos(ty) => {
let name = match ty.category() {
TypeCategory::Real => format!("llvm.cos.{}", ty.llvm_ty_str(builder)),
_ => return Err(crate::ErrorKind::Null),
};
Ok((name, vec![ty.clone()], ty.clone()))
}
LLVMIntrinsic::Tan(ty) => {
let name = match ty.category() {
TypeCategory::Real => format!("llvm.tan.{}", ty.llvm_ty_str(builder)),
_ => return Err(crate::ErrorKind::Null),
};
Ok((name, vec![ty.clone()], ty.clone()))
}
LLVMIntrinsic::ASin(ty) => {
let name = match ty.category() {
TypeCategory::Real => format!("llvm.asin.{}", ty.llvm_ty_str(builder)),
_ => return Err(crate::ErrorKind::Null),
};
Ok((name, vec![ty.clone()], ty.clone()))
}
LLVMIntrinsic::ACos(ty) => {
let name = match ty.category() {
TypeCategory::Real => format!("llvm.acos.{}", ty.llvm_ty_str(builder)),
_ => return Err(crate::ErrorKind::Null),
};
Ok((name, vec![ty.clone()], ty.clone()))
}
LLVMIntrinsic::ATan(ty) => {
let name = match ty.category() {
TypeCategory::Real => format!("llvm.atan.{}", ty.llvm_ty_str(builder)),
_ => return Err(crate::ErrorKind::Null),
};
Ok((name, vec![ty.clone()], ty.clone()))
}
LLVMIntrinsic::ATan2(ty) => {
let name = match ty.category() {
TypeCategory::Real => format!("llvm.atan2.{}", ty.llvm_ty_str(builder)),
_ => return Err(crate::ErrorKind::Null),
};
Ok((name, vec![ty.clone(), ty.clone()], ty.clone()))
}
LLVMIntrinsic::SinH(ty) => {
let name = match ty.category() {
TypeCategory::Real => format!("llvm.sinh.{}", ty.llvm_ty_str(builder)),
_ => return Err(crate::ErrorKind::Null),
};
Ok((name, vec![ty.clone()], ty.clone()))
}
LLVMIntrinsic::CosH(ty) => {
let name = match ty.category() {
TypeCategory::Real => format!("llvm.cosh.{}", ty.llvm_ty_str(builder)),
_ => return Err(crate::ErrorKind::Null),
};
Ok((name, vec![ty.clone()], ty.clone()))
}
LLVMIntrinsic::TanH(ty) => {
let name = match ty.category() {
TypeCategory::Real => format!("llvm.tanh.{}", ty.llvm_ty_str(builder)),
_ => return Err(crate::ErrorKind::Null),
};
Ok((name, vec![ty.clone()], ty.clone()))
}
LLVMIntrinsic::Log(ty) => {
let name = match ty.category() {
TypeCategory::Real => format!("llvm.log.{}", ty.llvm_ty_str(builder)),
_ => return Err(crate::ErrorKind::Null),
};
Ok((name, vec![ty.clone()], ty.clone()))
}
LLVMIntrinsic::Log2(ty) => {
let name = match ty.category() {
TypeCategory::Real => format!("llvm.log2.{}", ty.llvm_ty_str(builder)),
_ => return Err(crate::ErrorKind::Null),
};
Ok((name, vec![ty.clone()], ty.clone()))
}
LLVMIntrinsic::Log10(ty) => {
let name = match ty.category() {
TypeCategory::Real => format!("llvm.log10.{}", ty.llvm_ty_str(builder)),
_ => return Err(crate::ErrorKind::Null),
};
Ok((name, vec![ty.clone()], ty.clone()))
}
LLVMIntrinsic::Copysign(ty) => {
let name = match ty.category() {
TypeCategory::Real => format!("llvm.copysign.{}", ty.llvm_ty_str(builder)),
_ => return Err(crate::ErrorKind::Null),
};
Ok((name, vec![ty.clone()], ty.clone()))
}
LLVMIntrinsic::Floor(ty) => {
let name = match ty.category() {
TypeCategory::Real => format!("llvm.floor.{}", ty.llvm_ty_str(builder)),
_ => return Err(crate::ErrorKind::Null),
};
Ok((name, vec![ty.clone()], ty.clone()))
}
LLVMIntrinsic::Ceil(ty) => {
let name = match ty.category() {
TypeCategory::Real => format!("llvm.ceil.{}", ty.llvm_ty_str(builder)),
_ => return Err(crate::ErrorKind::Null),
};
Ok((name, vec![ty.clone()], ty.clone()))
}
LLVMIntrinsic::Trunc(ty) => {
let name = match ty.category() {
TypeCategory::Real => format!("llvm.trunc.{}", ty.llvm_ty_str(builder)),
_ => return Err(crate::ErrorKind::Null),
};
Ok((name, vec![ty.clone()], ty.clone()))
}
LLVMIntrinsic::RoundEven(ty) => {
let name = match ty.category() {
TypeCategory::Real => format!("llvm.roundeven.{}", ty.llvm_ty_str(builder)),
_ => return Err(crate::ErrorKind::Null),
};
Ok((name, vec![ty.clone()], ty.clone()))
}
LLVMIntrinsic::Round(ty) => {
let name = match ty.category() {
TypeCategory::Real => format!("llvm.rint.{}", ty.llvm_ty_str(builder)),
_ => return Err(crate::ErrorKind::Null),
};
Ok((name, vec![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("f16"),
Type::F32B => String::from("f32b"),
Type::F32 => String::from("f32"),
Type::F64 => String::from("f64"),
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"),
}
}
}

741
reid-llvm-lib/src/lib.rs Normal file
View File

@ -0,0 +1,741 @@
//! Reid LLVM Lib is an ergonomic Rust'y API which is used to produce a
//! Low-Level IR (LLIR) using [`Context`] and [`Builder`]. This Builder can then
//! be used at the end to compile said LLIR into LLVM IR.
use std::{fmt::Debug, marker::PhantomData};
use builder::{BlockValue, Builder, FunctionValue, InstructionValue, ModuleValue, TypeValue};
use debug_information::{DebugFileData, DebugInformation, DebugLocationValue, DebugMetadataValue};
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;
#[derive(thiserror::Error, Debug, Clone, PartialEq, PartialOrd)]
pub enum ErrorKind {
#[error("NULL error, should never occur!")]
Null,
#[error("Types {0:?} and {1:?} incompatible")]
TypesIncompatible(Type, Type),
#[error("Phi list of values is empty")]
EmptyPhiList,
#[error("Type {1:?} of value {0:?} is not extractable")]
NotExtractable(InstructionValue, Type),
#[error("Type {0:?} is not castable to {1:?}")]
ImpossibleCast(Type, Type),
#[error("Block is already terminated")]
BlockAlreadyTerminated,
#[error("Block terminator already has a location")]
BlockTerminatorLocated,
#[error("Value {0:?} must be an integer type. Is {1:?}")]
TypeNotInteger(InstructionValue, Type),
#[error("Value {0:?} must be a {2:?} type. Is {1:?}")]
TypeWrongCategory(InstructionValue, Type, TypeCategory),
#[error("Value {0:?} must be comparable, was {1:?}")]
TypeNotComparable(InstructionValue, Type),
#[error("Got {0:?} parameters, expected {1:?}")]
InvalidLenParams(usize, usize),
#[error("Value {0:?} is not a pointer, is {1:?}")]
NotPointer(InstructionValue, Type),
#[error("Value {0:?} is not a struct, is {1:?}")]
NotStruct(InstructionValue, Type),
#[error("Struct {0:?} has no such field as {1:?}")]
NoSuchField(Type, u32),
#[error("Function {0:?} has no such parameter as {1:?}")]
NoSuchParam(FunctionValue, usize),
}
pub type CompileResult<T> = Result<T, ErrorKind>;
#[derive(Debug)]
pub struct Context {
builder: Builder,
}
impl Context {
pub fn new<T: Into<String>>(producer: T) -> Context {
Context {
builder: Builder::new(producer.into()),
}
}
pub fn module<'ctx>(&'ctx self, name: &str, main: bool) -> Module<'ctx> {
let value = self.builder.add_module(ModuleData {
name: name.to_owned(),
is_main: main,
});
Module {
phantom: PhantomData,
builder: self.builder.clone(),
value,
debug_info: None,
}
}
}
#[derive(Debug, Clone, Hash)]
pub struct ModuleData {
name: String,
is_main: bool,
}
#[derive(Clone)]
pub struct Module<'ctx> {
phantom: PhantomData<&'ctx ()>,
builder: Builder,
value: ModuleValue,
debug_info: Option<DebugInformation>,
}
impl<'ctx> Module<'ctx> {
pub fn function(
&self,
name: &str,
linkage: Option<String>,
ret: Type,
params: Vec<Type>,
flags: FunctionFlags,
) -> Function<'ctx> {
unsafe {
Function {
phantom: PhantomData,
builder: self.builder.clone(),
value: self.builder.add_function(
&self.value,
FunctionData {
name: name.to_owned(),
linkage_name: linkage,
ret,
params,
flags,
},
),
}
}
}
pub fn intrinsic(&self, intrinsic: LLVMIntrinsic) -> CompileResult<FunctionValue> {
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 {
CustomTypeKind::NamedStruct(NamedStruct(name, _)) => (name.clone(), ty),
};
self.builder.add_type(&self.value, TypeData { name, kind })
}
}
pub fn value(&self) -> ModuleValue {
self.value
}
pub fn as_printable(&self) -> PrintableModule<'ctx> {
PrintableModule {
phantom: PhantomData,
module: self.builder.find_module(self.value),
}
}
pub fn create_debug_info(&mut self, file: DebugFileData) -> (DebugInformation, DebugScopeValue) {
let (debug_info, scope_value) = DebugInformation::from_file(file);
self.debug_info = Some(debug_info.clone());
(debug_info, scope_value)
}
pub fn get_debug_info(&self) -> &Option<DebugInformation> {
&self.debug_info
}
pub fn add_constant(&self, constant: ConstValueKind) -> ConstantValue {
unsafe { self.builder.build_constant(self.value, constant) }
}
pub fn add_global<T: Into<String>>(&self, name: T, constant: ConstantValue) -> GlobalValue {
unsafe { self.builder.add_global(self.value, name.into(), constant) }
}
}
impl<'ctx> Drop for Module<'ctx> {
fn drop(&mut self) {
if let Some(debug_info) = self.debug_info.take() {
self.builder.set_debug_information(&self.value, debug_info);
}
}
}
#[derive(Debug, Clone, Hash)]
pub struct FunctionData {
name: String,
linkage_name: Option<String>,
ret: Type,
params: Vec<Type>,
flags: FunctionFlags,
}
#[derive(Debug, Clone, Copy, Hash)]
pub struct FunctionFlags {
/// True in the destination module of the import, false in the source module.
pub is_extern: bool,
/// Whether this function is the main function of the module, that should be
/// executed (and linked externally also).
pub is_main: bool,
/// Whether this function should be available externally always.
pub is_pub: bool,
/// If this function is an imported function (either in the source or
/// destination module)
pub is_imported: bool,
/// Whether this function should add "alwaysinline"-attribute.
pub inline: bool,
}
impl Default for FunctionFlags {
fn default() -> FunctionFlags {
FunctionFlags {
is_extern: false,
is_main: false,
is_pub: false,
is_imported: false,
inline: false,
}
}
}
pub struct Function<'ctx> {
phantom: PhantomData<&'ctx ()>,
builder: Builder,
value: FunctionValue,
}
impl<'ctx> Function<'ctx> {
pub fn block(&self, name: &str) -> Block<'ctx> {
unsafe {
Block {
phantom: PhantomData,
builder: self.builder.clone(),
value: self.builder.add_block(
&self.value,
BlockData {
name: name.to_owned(),
terminator: None,
terminator_location: None,
deleted: false,
},
),
}
}
}
pub fn set_debug(&self, subprogram: DebugScopeValue) {
unsafe {
self.builder.set_debug_subprogram(&self.value, subprogram);
}
}
pub fn value(&self) -> FunctionValue {
self.value
}
}
#[derive(Debug, Clone, Hash)]
pub struct BlockData {
name: String,
terminator: Option<TerminatorKind>,
terminator_location: Option<DebugLocationValue>,
deleted: bool,
}
#[derive(Clone)]
pub struct Block<'builder> {
phantom: PhantomData<&'builder ()>,
builder: Builder,
value: BlockValue,
}
impl Instr {
pub fn default_name(&self) -> &str {
match self {
Instr::Param(_) => "param",
Instr::Constant(_) => "const1",
Instr::Add(..) => "add",
Instr::FAdd(..) => "fadd",
Instr::Sub(..) => "sub",
Instr::FSub(..) => "fsub",
Instr::Mul(..) => "mul",
Instr::FMul(..) => "fmul",
Instr::UDiv(..) => "udiv",
Instr::SDiv(..) => "sdiv",
Instr::FDiv(..) => "fdiv",
Instr::URem(..) => "urem",
Instr::SRem(..) => "srem",
Instr::FRem(..) => "frem",
Instr::And(..) => "and",
Instr::Phi(_) => "phi",
Instr::Alloca(_) => "alloca",
Instr::Load(_, _) => "load",
Instr::Store(..) => "store",
Instr::ArrayAlloca(_, _) => "arrayalloca",
Instr::GetElemPtr(..) => "getelemptr",
Instr::GetStructElemPtr(..) => "getstructelemptr",
Instr::ExtractValue(..) => "extractvalue",
Instr::ICmp(..) => "icmp",
Instr::FunctionCall(..) => "call",
Instr::FCmp(_, _, _) => "fcmp",
Instr::Trunc(_, _) => "trunc",
Instr::ZExt(_, _) => "zext",
Instr::SExt(_, _) => "sext",
Instr::FPTrunc(_, _) => "fptrunc",
Instr::FPExt(_, _) => "pfext",
Instr::FPToUI(_, _) => "fptoui",
Instr::FPToSI(_, _) => "fptosi",
Instr::UIToFP(_, _) => "uitofp",
Instr::SIToFP(_, _) => "sitofp",
Instr::PtrToInt(_, _) => "ptrtoint",
Instr::IntToPtr(_, _) => "inttoptr",
Instr::BitCast(_, _) => "bitcast",
Instr::Or(..) => "or",
Instr::XOr(..) => "xor",
Instr::ShiftRightLogical(..) => "lshr",
Instr::ShiftRightArithmetic(..) => "ashr",
Instr::ShiftLeft(..) => "shl",
Instr::GetGlobal(..) => "global",
Instr::IsNull(..) => "is_null",
}
}
}
impl<'builder> Block<'builder> {
pub fn build_named<T: Into<String>>(&mut self, name: T, instruction: Instr) -> CompileResult<InstructionValue> {
unsafe {
self.builder.add_instruction(
&self.value,
InstructionData {
kind: instruction,
location: None,
meta: None,
},
name.into(),
)
}
}
pub fn build(&mut self, instruction: Instr) -> CompileResult<InstructionValue> {
unsafe {
let name = instruction.default_name().to_owned();
self.builder.add_instruction(
&self.value,
InstructionData {
kind: instruction,
location: None,
meta: None,
},
name,
)
}
}
pub fn find_function(&mut self, name: &String) -> Option<FunctionValue> {
unsafe { self.builder.find_function(self.value.0.0, name) }
}
pub fn set_instr_location(&self, instruction: InstructionValue, location: DebugLocationValue) {
unsafe {
self.builder.add_instruction_location(&instruction, location);
}
}
pub fn set_instr_metadata(&self, instruction: InstructionValue, location: DebugMetadataValue) {
unsafe {
self.builder.add_instruction_metadata(&instruction, location);
}
}
pub fn terminate(&mut self, instruction: TerminatorKind) -> CompileResult<()> {
unsafe { self.builder.terminate(&self.value, instruction) }
}
pub fn set_terminator_location(&mut self, location: DebugLocationValue) -> CompileResult<()> {
unsafe { self.builder.set_terminator_location(&self.value, location) }
}
/// Delete block if it is unused. Return true if deleted, false if not.
pub fn delete_if_unused(&mut self) -> CompileResult<bool> {
unsafe {
if !self.builder.is_block_used(self.value()) {
self.builder.delete_block(&self.value)?;
Ok(true)
} else {
Ok(false)
}
}
}
pub fn value(&self) -> BlockValue {
self.value
}
}
#[derive(Clone)]
pub struct InstructionData {
kind: Instr,
location: Option<DebugLocationValue>,
meta: Option<DebugMetadataValue>,
}
#[derive(Clone, Copy, Hash)]
pub enum CmpPredicate {
LT,
LE,
GT,
GE,
EQ,
NE,
}
/// https://llvm.org/docs/LangRef.html#instruction-reference
#[derive(Clone)]
pub enum Instr {
Param(usize),
Constant(ConstValueKind),
GetGlobal(GlobalValue),
/// Add two integers
Add(InstructionValue, InstructionValue),
/// Add two floats
FAdd(InstructionValue, InstructionValue),
/// Subtract two integers
Sub(InstructionValue, InstructionValue),
/// Subtract two floats
FSub(InstructionValue, InstructionValue),
/// Multiply two integers
Mul(InstructionValue, InstructionValue),
/// Multiply two floats
FMul(InstructionValue, InstructionValue),
/// Divide two unsigned integers
UDiv(InstructionValue, InstructionValue),
/// Divide two signed integers
SDiv(InstructionValue, InstructionValue),
/// Divide two floats
FDiv(InstructionValue, InstructionValue),
/// Get the remainder from two unsigned integers
URem(InstructionValue, InstructionValue),
/// Get the remainder from two signed integers
SRem(InstructionValue, InstructionValue),
/// Get the remainder from two floats
FRem(InstructionValue, InstructionValue),
And(InstructionValue, InstructionValue),
Or(InstructionValue, InstructionValue),
XOr(InstructionValue, InstructionValue),
ShiftRightLogical(InstructionValue, InstructionValue),
ShiftRightArithmetic(InstructionValue, InstructionValue),
ShiftLeft(InstructionValue, InstructionValue),
Phi(Vec<InstructionValue>),
Alloca(Type),
Load(InstructionValue, Type),
Store(InstructionValue, InstructionValue),
ArrayAlloca(Type, InstructionValue),
GetElemPtr(InstructionValue, Vec<InstructionValue>),
GetStructElemPtr(InstructionValue, u32),
ExtractValue(InstructionValue, u32),
/// Integer Comparison
ICmp(CmpPredicate, InstructionValue, InstructionValue),
/// FLoat Comparison
FCmp(CmpPredicate, InstructionValue, InstructionValue),
/// The `trunc` instruction truncates the high order bits in value and
/// converts the remaining bits to ty2. Since the source size must be larger
/// than the destination size, `trunc` cannot be a no-op cast. It will
/// always truncate bits.
Trunc(InstructionValue, Type),
/// The `zext` fills the high order bits of the value with zero bits until
/// it reaches the size of the destination type, ty2.
ZExt(InstructionValue, Type),
/// The `sext` instruction performs a sign extension by copying the sign bit
/// (highest order bit) of the value until it reaches the bit size of the
/// type ty2.
SExt(InstructionValue, Type),
/// The `fptrunc` instruction casts a value from a larger floating-point
/// type to a smaller floating-point type.
FPTrunc(InstructionValue, Type),
/// The `fpext` instruction extends the value from a smaller floating-point
/// type to a larger floating-point type.
FPExt(InstructionValue, Type),
/// The `fptoui` instruction takes a value to cast, which must be a scalar
/// or vector floating-point value, and a type to cast it to ty2, which must
/// be an integer type.
FPToUI(InstructionValue, Type),
/// The `fptosi` instruction takes a value to cast, which must be a scalar
/// or vector floating-point value, and a type to cast it to ty2, which must
/// be an integer type.
FPToSI(InstructionValue, Type),
/// The `uitofp` instruction takes a value to cast, which must be a scalar
/// or vector integer value, and a type to cast it to ty2, which must be an
/// floating-point type.
UIToFP(InstructionValue, Type),
/// The `sitofp` instruction takes a value to cast, which must be a scalar
/// or vector integer value, and a type to cast it to ty2, which must be an
/// floating-point type
SIToFP(InstructionValue, Type),
/// The `ptrtoint` instruction converts value to integer type ty2 by
/// interpreting the all pointer representation bits as an integer
/// (equivalent to a bitcast) and either truncating or zero extending that
/// value to the size of the integer type.
PtrToInt(InstructionValue, Type),
/// The `inttoptr` instruction converts value to type ty2 by applying either
/// a zero extension or a truncation depending on the size of the integer
/// value.
IntToPtr(InstructionValue, Type),
/// The `bitcast` instruction converts value to type ty2. It is always a
/// no-op cast because no bits change with this conversion.
BitCast(InstructionValue, Type),
/// Check if the given instruction value is a null pointer
IsNull(InstructionValue),
FunctionCall(FunctionValue, Vec<InstructionValue>),
}
#[derive(Debug, PartialEq, Eq, Clone, Hash, PartialOrd)]
pub enum Type {
I8,
I16,
I32,
I64,
I128,
U8,
U16,
U32,
U64,
U128,
F16,
F32B,
F32,
F64,
F80,
F128,
F128PPC,
Bool,
Void,
CustomType(TypeValue),
Array(Box<Type>, u64),
Ptr(Box<Type>),
}
#[derive(Debug, Clone)]
pub enum ConstValueKind {
I8(i8),
I16(i16),
I32(i32),
I64(i64),
I128(i128),
U8(u8),
U16(u16),
U32(u32),
U64(u64),
U128(u128),
Bool(bool),
Str(String),
F16(f32),
F32B(f32),
F32(f32),
F64(f64),
F80(f64),
F128(f64),
F128PPC(f64),
Array(Vec<ConstantValue>, Type),
}
#[derive(Clone, Hash)]
pub enum TerminatorKind {
Ret(InstructionValue),
RetVoid,
Br(BlockValue),
CondBr(InstructionValue, BlockValue, BlockValue),
}
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub struct TypeData {
name: String,
kind: CustomTypeKind,
}
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub enum CustomTypeKind {
NamedStruct(NamedStruct),
}
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub struct NamedStruct(pub String, pub Vec<Type>);
impl ConstValueKind {
pub fn get_type(&self) -> Type {
use Type::*;
match self {
ConstValueKind::I8(_) => I8,
ConstValueKind::I16(_) => I16,
ConstValueKind::I32(_) => I32,
ConstValueKind::I64(_) => I64,
ConstValueKind::I128(_) => I128,
ConstValueKind::U8(_) => U8,
ConstValueKind::U16(_) => U16,
ConstValueKind::U32(_) => U32,
ConstValueKind::U64(_) => U64,
ConstValueKind::U128(_) => U128,
ConstValueKind::Str(_) => Type::Ptr(Box::new(U8)),
ConstValueKind::Bool(_) => Bool,
ConstValueKind::F16(_) => F16,
ConstValueKind::F32B(_) => F32B,
ConstValueKind::F32(_) => F32,
ConstValueKind::F64(_) => F64,
ConstValueKind::F80(_) => F80,
ConstValueKind::F128(_) => F128,
ConstValueKind::F128PPC(_) => F128PPC,
ConstValueKind::Array(vals, ty) => Type::Array(Box::new(ty.clone()), vals.len() as u64),
}
}
}
#[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Clone)]
pub enum TypeCategory {
SignedInteger,
UnsignedInteger,
Void,
Real,
Ptr,
CustomType,
Array,
}
impl TypeCategory {
pub fn comparable(&self) -> bool {
match self {
TypeCategory::SignedInteger => true,
TypeCategory::UnsignedInteger => true,
TypeCategory::Real => true,
_ => false,
}
}
pub fn signed(&self) -> bool {
match self {
TypeCategory::SignedInteger => true,
_ => false,
}
}
pub fn integer(&self) -> bool {
match self {
TypeCategory::SignedInteger => true,
TypeCategory::UnsignedInteger => true,
_ => false,
}
}
}
impl Type {
pub fn category(&self) -> TypeCategory {
match self {
Type::I8 | Type::I16 | Type::I32 | Type::I64 | Type::I128 => TypeCategory::SignedInteger,
Type::U8 | Type::U16 | Type::U32 | Type::U64 | Type::U128 => TypeCategory::UnsignedInteger,
Type::F16 | Type::F32B | Type::F32 | Type::F64 | Type::F80 | Type::F128 | Type::F128PPC => {
TypeCategory::Real
}
Type::Bool => TypeCategory::UnsignedInteger,
Type::Void => TypeCategory::Void,
Type::CustomType(_) => TypeCategory::CustomType,
Type::Array(_, _) => TypeCategory::Array,
Type::Ptr(_) => TypeCategory::Ptr,
}
}
pub fn cast_instruction(&self, value: InstructionValue, other: &Type) -> Option<Instr> {
use Type::*;
match (self, other) {
(I8, I16 | I32 | I64 | I128) => Some(Instr::SExt(value, other.clone())),
(I16, I32 | I64 | I128) => Some(Instr::SExt(value, other.clone())),
(I32, I64 | I128) => Some(Instr::SExt(value, other.clone())),
(I64, I128) => Some(Instr::SExt(value, other.clone())),
(I128 | U128, I64 | U64 | I32 | U32 | I16 | U16 | I8 | U8) => Some(Instr::Trunc(value, other.clone())),
(I64 | U64, I32 | U32 | I16 | U16 | I8 | U8) => Some(Instr::Trunc(value, other.clone())),
(I32 | U32, I16 | U16 | I8 | U8) => Some(Instr::Trunc(value, other.clone())),
(I16 | U16, I8 | U8) => Some(Instr::Trunc(value, other.clone())),
(U8 | I8, U8 | I8 | U16 | I16 | U32 | I32 | U64 | I64 | U128 | I128) => {
Some(Instr::ZExt(value, other.clone()))
}
(U16 | I16, U16 | I16 | U32 | I32 | U64 | I64 | U128 | I128) => Some(Instr::ZExt(value, other.clone())),
(U32 | I32, U32 | I32 | U64 | I64 | U128 | I128) => Some(Instr::ZExt(value, other.clone())),
(U64 | I64, U64 | I64 | U128 | I128) => Some(Instr::ZExt(value, other.clone())),
(U128 | I128, U128 | I128) => Some(Instr::ZExt(value, other.clone())),
(U8 | U16 | U32 | U64 | U128, F16 | F32 | F32B | F64 | F80 | F128 | F128PPC) => {
Some(Instr::UIToFP(value, other.clone()))
}
(I8 | I16 | I32 | I64 | I128, F16 | F32 | F32B | F64 | F80 | F128 | F128PPC) => {
Some(Instr::SIToFP(value, other.clone()))
}
(F16 | F32 | F32B | F64 | F80 | F128 | F128PPC, U8 | U16 | U32 | U64 | U128) => {
Some(Instr::FPToUI(value, other.clone()))
}
(F16 | F32 | F32B | F64 | F80 | F128 | F128PPC, I8 | I16 | I32 | I64 | I128) => {
Some(Instr::FPToSI(value, other.clone()))
}
(I128 | U128 | I64 | U64 | I32 | U32 | I16 | U16 | I8 | U8, Ptr(_)) => {
Some(Instr::IntToPtr(value, other.clone()))
}
(Ptr(_), I128 | U128 | I64 | U64 | I32 | U32 | I16 | U16 | I8 | U8) => {
Some(Instr::PtrToInt(value, other.clone()))
}
(F16, F32 | F32B | F64 | F80 | F128 | F128PPC) => Some(Instr::FPExt(value, other.clone())),
(F32 | F32B, F64 | F80 | F128 | F128PPC) => Some(Instr::FPExt(value, other.clone())),
(F64, F80 | F128 | F128PPC) => Some(Instr::FPExt(value, other.clone())),
(F80, F128 | F128PPC) => Some(Instr::FPExt(value, other.clone())),
(F128PPC | F128, F80 | F64 | F32B | F32 | F16) => Some(Instr::FPTrunc(value, other.clone())),
(F80, F64 | F32B | F32 | F16) => Some(Instr::FPTrunc(value, other.clone())),
(F64, F32B | F32 | F16) => Some(Instr::FPTrunc(value, other.clone())),
(F32B | F32, F16) => Some(Instr::FPTrunc(value, other.clone())),
_ => None,
}
}
}
impl TerminatorKind {
pub(crate) fn get_type(&self, builder: &Builder) -> CompileResult<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

@ -0,0 +1,69 @@
//! Copied from
//! https://github.com/rust-lang/rust/blob/6b3ae3f6e45a33c2d95fa0362c9b2593e567fd34/library/core/src/fmt/builders.rs#L102
// Copyright (c) The Rust Project Contributors
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
use std::fmt;
pub struct PadAdapter<'buf, 'state> {
buf: &'buf mut (dyn fmt::Write + 'buf),
state: &'state mut PadAdapterState,
}
pub struct PadAdapterState {
on_newline: bool,
}
impl Default for PadAdapterState {
fn default() -> Self {
PadAdapterState { on_newline: true }
}
}
impl<'buf, 'state> PadAdapter<'buf, 'state> {
pub fn wrap<'slot, 'fmt: 'buf + 'slot>(
fmt: &'buf mut (dyn fmt::Write + 'buf),
state: &'state mut PadAdapterState,
) -> Self {
PadAdapter { buf: fmt, state }
}
}
impl fmt::Write for PadAdapter<'_, '_> {
fn write_str(&mut self, s: &str) -> fmt::Result {
for s in s.split_inclusive('\n') {
if self.state.on_newline {
self.buf.write_str(" ")?;
}
self.state.on_newline = s.ends_with('\n');
self.buf.write_str(s)?;
}
Ok(())
}
fn write_char(&mut self, c: char) -> fmt::Result {
if self.state.on_newline {
self.buf.write_str(" ")?;
}
self.state.on_newline = c == '\n';
self.buf.write_char(c)
}
}

115
reid-llvm-lib/src/util.rs Normal file
View File

@ -0,0 +1,115 @@
use std::{
ffi::{CStr, CString, c_char},
ptr::null_mut,
string::FromUtf8Error,
};
use llvm_sys::{
core::{LLVMCreateMemoryBufferWithMemoryRange, LLVMDisposeMemoryBuffer, LLVMGetBufferSize, LLVMGetBufferStart},
error::LLVMDisposeErrorMessage,
prelude::LLVMMemoryBufferRef,
};
use crate::{
CompileResult, ErrorKind, Type,
builder::{Builder, InstructionValue},
};
pub fn into_cstring<T: Into<String>>(value: T) -> CString {
let string = value.into();
unsafe { CString::from_vec_with_nul_unchecked((string + "\0").into_bytes()) }
}
pub fn from_cstring(pointer: *mut c_char) -> Option<String> {
if pointer.is_null() {
None
} else {
unsafe { CStr::from_ptr(pointer).to_str().ok().map(|s| s.to_owned()) }
}
}
fn cstring_to_err(value: *mut c_char) -> Result<(), String> {
from_cstring(value).filter(|s| !s.is_empty()).map_or(Ok(()), |s| Err(s))
}
/// Utility struct for LLVM's Error Messages, which need to be disposed
/// manually.
pub struct ErrorMessageHolder(*mut c_char);
impl ErrorMessageHolder {
pub fn null() -> Self {
ErrorMessageHolder(null_mut())
}
pub fn borrow_mut(&mut self) -> *mut *mut c_char {
&mut self.0
}
pub fn into_result(&self) -> Result<(), String> {
cstring_to_err(self.0)
}
}
impl Drop for ErrorMessageHolder {
fn drop(&mut self) {
unsafe {
if !self.0.is_null() {
LLVMDisposeErrorMessage(self.0);
}
}
}
}
/// Utility for creating and handling LLVM MemoryBuffers, needed for printing
/// out ASM and .o -files without relying on LLVM's own API.
pub struct MemoryBufferHolder {
pub buffer: LLVMMemoryBufferRef,
}
impl MemoryBufferHolder {
pub fn empty(name: &str) -> MemoryBufferHolder {
let array = [0i8; 0];
unsafe {
let buffer =
LLVMCreateMemoryBufferWithMemoryRange(array.as_ptr(), array.len(), into_cstring(name).as_ptr(), 0);
MemoryBufferHolder { buffer }
}
}
pub fn as_buffer(&self) -> Vec<u8> {
unsafe {
let start = LLVMGetBufferStart(self.buffer);
let size = LLVMGetBufferSize(self.buffer);
let mut buff = Vec::with_capacity(size);
for i in 0..size {
buff.push(*start.add(i) as u8);
}
buff
}
}
pub fn as_string(&self) -> Result<String, FromUtf8Error> {
String::from_utf8(self.as_buffer())
}
}
impl Drop for MemoryBufferHolder {
fn drop(&mut self) {
unsafe {
LLVMDisposeMemoryBuffer(self.buffer);
}
}
}
/// Make sure types for given instructions match. Return Ok(type) if they do,
/// and error otherwise.
pub fn match_types(lhs: &InstructionValue, rhs: &InstructionValue, builder: &Builder) -> CompileResult<Type> {
let lhs_t = lhs.get_type(&builder)?;
let rhs_t = rhs.get_type(&builder)?;
if lhs_t == rhs_t {
Ok(lhs_t)
} else {
Err(ErrorKind::TypesIncompatible(lhs_t, rhs_t))
}
}

View File

@ -1,13 +1,13 @@
[package]
name = "reid-language-server"
version = "1.0.0"
version = "1.0.0-beta.1"
edition = "2024"
[dependencies]
socket = "0.0.7"
tokio = { version = "1.47.0", features = ["full"] }
tower-lsp = "0.20.0"
reid = { path = "../reid", version = "1.0.0", registry="gitea-teascade", features=[] }
reid = { path = "../reid", version = "1.0.0-beta.4", registry="gitea-teascade", features=[] }
dashmap = "6.1.0"
serde = "*"
serde_json = "*"

View File

@ -2,7 +2,7 @@
"name": "reid-language-server",
"displayName": "Reid Language Server",
"description": "Language Server Extension for Reid",
"version": "1.0.0",
"version": "1.0.0-beta.1",
"repository": {
"url": "https://git.teascade.net/teascade"
},

View File

@ -1,6 +1,6 @@
[package]
name = "reid"
version = "1.0.0"
version = "1.0.0-beta.4"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@ -15,7 +15,7 @@ cli = ["argh", "stderrlog"]
[dependencies]
## Make it easier to generate errors
thiserror = "1.0.44"
reid-lib = { path = "../reid-llvm-lib", version = "1.0.0", registry="gitea-teascade" }
reid-lib = { path = "../reid-llvm-lib", version = "1.0.0-beta.4", registry="gitea-teascade" }
argh = { version = "0.1.13", optional = true }
stderrlog = { version = "0.6.0", optional = true }

View File

@ -162,6 +162,7 @@ impl BinaryOperator {
pub struct FunctionCallExpression {
pub name: String,
pub params: Vec<Expression>,
pub generics: Vec<Type>,
pub range: TokenRange,
pub is_macro: bool,
}
@ -192,6 +193,7 @@ pub struct FunctionDefinition(pub FunctionSignature, pub bool, pub Block, pub To
#[derive(Debug, Clone)]
pub struct FunctionSignature {
pub name: String,
pub generics: Vec<String>,
pub documentation: Option<String>,
pub self_kind: SelfKind,
pub params: Vec<(String, Type, TokenRange)>,

View File

@ -182,6 +182,7 @@ impl Parse for AssociatedFunctionCall {
ty,
FunctionCallExpression {
name: String::new(),
generics: Vec::new(),
params: Vec::new(),
range: stream.get_range_prev_curr().unwrap(),
is_macro: false,
@ -200,6 +201,7 @@ impl Parse for AssociatedFunctionCall {
ty,
FunctionCallExpression {
name: fn_name,
generics: Vec::new(),
params: Vec::new(),
range: stream.get_range_prev_curr().unwrap(),
is_macro: false,
@ -211,6 +213,7 @@ impl Parse for AssociatedFunctionCall {
ty,
FunctionCallExpression {
name: String::new(),
generics: Vec::new(),
params: Vec::new(),
range: stream.get_range_prev_curr().unwrap(),
is_macro: false,
@ -591,7 +594,8 @@ impl Parse for FunctionCallExpression {
let args = stream.parse::<FunctionArgs>()?;
Ok(FunctionCallExpression {
name,
params: args.0,
generics: args.0,
params: args.1,
range: stream.get_range().unwrap(),
is_macro,
})
@ -602,10 +606,23 @@ impl Parse for FunctionCallExpression {
}
#[derive(Debug)]
pub struct FunctionArgs(Vec<Expression>);
pub struct FunctionArgs(Vec<Type>, Vec<Expression>);
impl Parse for FunctionArgs {
fn parse(mut stream: TokenStream) -> Result<Self, Error> {
let mut generics: Vec<Type> = Vec::new();
if let Some(Token::LessThan) = stream.peek() {
stream.next();
if let Ok(ty) = stream.parse() {
generics.push(ty);
while let Some(Token::Comma) = stream.peek() {
stream.next();
generics.push(stream.parse()?);
}
}
stream.expect(Token::GreaterThan)?;
}
stream.expect(Token::ParenOpen)?;
let mut params = Vec::new();
@ -618,7 +635,7 @@ impl Parse for FunctionArgs {
}
stream.expect(Token::ParenClose)?;
Ok(FunctionArgs(params))
Ok(FunctionArgs(generics, params))
}
}
@ -780,6 +797,18 @@ impl Parse for SelfParam {
impl Parse for FunctionSignature {
fn parse(mut stream: TokenStream) -> Result<Self, Error> {
if let Some(Token::Identifier(name)) = stream.next() {
let mut generics = Vec::new();
if let Some(Token::LessThan) = stream.peek() {
stream.next();
while let Some(Token::Identifier(ident)) = stream.peek() {
stream.next();
generics.push(ident);
}
stream.expect(Token::GreaterThan)?;
}
stream.expect(Token::ParenOpen)?;
let mut params = Vec::new();
@ -814,6 +843,7 @@ impl Parse for FunctionSignature {
Ok(FunctionSignature {
name,
generics,
documentation: None,
params,
self_kind,
@ -961,7 +991,8 @@ impl Parse for DotIndexKind {
if let Ok(args) = stream.parse::<FunctionArgs>() {
Ok(Self::FunctionCall(FunctionCallExpression {
name,
params: args.0,
generics: args.0,
params: args.1,
range: stream.get_range_prev().unwrap(),
is_macro: false,
}))

View File

@ -1,7 +1,7 @@
use std::path::PathBuf;
use std::{collections::HashMap, path::PathBuf};
use crate::{
ast::{self, ReturnType},
ast::{self, ReturnType, TypeKind},
mir::{
self, CustomTypeKey, FunctionParam, ModuleMap, NamedVariableRef, ReturnKind, SourceModuleId, StmtKind,
StructField, StructType, WhileStatement,
@ -44,6 +44,11 @@ impl ast::Module {
let def = mir::FunctionDefinition {
name: signature.name.clone(),
documentation: signature.documentation.clone(),
generics: signature
.generics
.iter()
.map(|g| (g.clone(), mir::TypeKind::Vague(mir::VagueType::Unknown)))
.collect(),
linkage_name: None,
is_pub: false,
is_imported: false,
@ -175,9 +180,15 @@ impl ast::FunctionDefinition {
ty: p.1 .0.into_mir(module_id),
meta: p.2.as_meta(module_id),
}));
mir::FunctionDefinition {
name: signature.name.clone(),
documentation: signature.documentation.clone(),
generics: signature
.generics
.iter()
.map(|g| (g.clone(), mir::TypeKind::Vague(mir::VagueType::Unknown)))
.collect(),
linkage_name: None,
is_pub: *is_pub,
is_imported: false,
@ -379,6 +390,7 @@ impl ast::Expression {
),
ast::ExpressionKind::FunctionCall(fn_call_expr) => mir::ExprKind::FunctionCall(mir::FunctionCall {
name: fn_call_expr.name.clone(),
generics: fn_call_expr.generics.iter().map(|g| g.0.into_mir(module_id)).collect(),
return_type: mir::TypeKind::Vague(mir::VagueType::Unknown),
parameters: fn_call_expr.params.iter().map(|e| e.process(module_id)).collect(),
meta: fn_call_expr.range.as_meta(module_id),
@ -468,6 +480,7 @@ impl ast::Expression {
ty.0.into_mir(module_id),
mir::FunctionCall {
name: fn_call_expr.name.clone(),
generics: fn_call_expr.generics.iter().map(|g| g.0.into_mir(module_id)).collect(),
return_type: mir::TypeKind::Vague(mir::VagueType::Unknown),
parameters: fn_call_expr.params.iter().map(|e| e.process(module_id)).collect(),
meta: fn_call_expr.range.as_meta(module_id),
@ -491,6 +504,7 @@ impl ast::Expression {
mir::TypeKind::Vague(mir::VagueType::Unknown),
mir::FunctionCall {
name: fn_call_expr.name.clone(),
generics: fn_call_expr.generics.iter().map(|g| g.0.into_mir(module_id)).collect(),
return_type: mir::TypeKind::Vague(mir::VagueType::Unknown),
parameters: params,
meta: fn_call_expr.range.as_meta(module_id),

View File

@ -77,6 +77,7 @@ pub fn form_intrinsics() -> Vec<FunctionDefinition> {
intrinsics.push(FunctionDefinition {
name: MALLOC_IDENT.to_owned(),
generics: Vec::new(),
documentation: doc!("Allocates `size` bytes and returns a `u8`-pointer."),
linkage_name: Some("malloc".to_owned()),
is_pub: false,
@ -104,6 +105,7 @@ pub fn simple_intrinsic<T: Into<String> + Clone>(
) -> FunctionDefinition {
FunctionDefinition {
name: name.into(),
generics: Vec::new(),
documentation: Some(doc.into()),
linkage_name: None,
is_pub: true,
@ -124,6 +126,7 @@ pub fn get_intrinsic_assoc_functions(ty: &TypeKind) -> Vec<FunctionDefinition> {
if let TypeKind::Array(_, len) = ty {
intrinsics.push(FunctionDefinition {
name: "length".to_owned(),
generics: Vec::new(),
documentation: doc!("Returns the length of this given array"),
linkage_name: None,
is_pub: true,
@ -282,6 +285,7 @@ pub fn get_intrinsic_assoc_functions(ty: &TypeKind) -> Vec<FunctionDefinition> {
));
intrinsics.push(FunctionDefinition {
name: "powi".to_owned(),
generics: Vec::new(),
documentation: doc!("Returns `value` raised to the exponent of `exponent`."),
linkage_name: None,
is_pub: true,
@ -326,6 +330,7 @@ pub fn get_intrinsic_assoc_functions(ty: &TypeKind) -> Vec<FunctionDefinition> {
if ty.signed() {
intrinsics.push(FunctionDefinition {
name: "abs".to_owned(),
generics: Vec::new(),
documentation: doc!("Returns the absolute value of `value`."),
linkage_name: None,
is_pub: true,
@ -357,6 +362,7 @@ pub fn get_intrinsic_assoc_functions(ty: &TypeKind) -> Vec<FunctionDefinition> {
}
intrinsics.push(FunctionDefinition {
name: "sizeof".to_owned(),
generics: Vec::new(),
documentation: doc!("Simply returns the size of type `T` in bytes."),
linkage_name: None,
is_pub: true,
@ -369,6 +375,7 @@ pub fn get_intrinsic_assoc_functions(ty: &TypeKind) -> Vec<FunctionDefinition> {
});
intrinsics.push(FunctionDefinition {
name: "malloc".to_owned(),
generics: Vec::new(),
documentation: doc!("Allocates `T::sizeof() * size` bytes and returns a pointer to `T`."),
linkage_name: None,
is_pub: true,
@ -386,6 +393,7 @@ pub fn get_intrinsic_assoc_functions(ty: &TypeKind) -> Vec<FunctionDefinition> {
intrinsics.push(FunctionDefinition {
name: "memcpy".to_owned(),
generics: Vec::new(),
documentation: doc!(
"Copies `T::sizeof() * size` bytes from pointer `source` to pointer
`destination`."
@ -418,6 +426,7 @@ pub fn get_intrinsic_assoc_functions(ty: &TypeKind) -> Vec<FunctionDefinition> {
intrinsics.push(FunctionDefinition {
name: "null".to_owned(),
generics: Vec::new(),
documentation: doc!("Returns a null-pointer of type `T`."),
linkage_name: None,
is_pub: true,
@ -431,6 +440,7 @@ pub fn get_intrinsic_assoc_functions(ty: &TypeKind) -> Vec<FunctionDefinition> {
intrinsics.push(FunctionDefinition {
name: "is_null".to_owned(),
generics: Vec::new(),
documentation: doc!("Returns a boolean representing if `val` is a nullptr or not."),
linkage_name: None,
is_pub: true,

View File

@ -1218,7 +1218,9 @@ impl mir::Expression {
let TypeKind::CustomType(key) = *inner.clone() else {
panic!("tried accessing non-custom-type");
};
let TypeDefinitionKind::Struct(struct_ty) = scope.get_typedef(&key).unwrap().kind.clone();
let TypeDefinitionKind::Struct(struct_ty) = scope.get_typedef(&key).unwrap().kind.clone() else {
panic!();
};
let idx = struct_ty.find_index(field).unwrap();
let gep_n = format!("{}.{}.gep", key.0, field);
@ -1259,7 +1261,10 @@ impl mir::Expression {
let TypeDefinition {
kind: TypeDefinitionKind::Struct(struct_ty),
..
} = scope.types.get(scope.type_values.get(&key).unwrap()).unwrap();
} = scope.types.get(scope.type_values.get(&key).unwrap()).unwrap()
else {
panic!()
};
let indices = struct_ty.0.iter().enumerate();

View File

@ -8,7 +8,7 @@ use crate::{
ast::token_stream::{self, TokenRange},
codegen,
lexer::{self, Cursor, FullToken, Position},
mir::{self, macros, pass, typecheck, Metadata, SourceModuleId},
mir::{self, generics, macros, pass, typecheck, Metadata, SourceModuleId},
};
use crate::mir::typecheck::ErrorKind as TypecheckError;
@ -36,6 +36,8 @@ pub enum ErrorKind {
LinkerError(#[from] mir::pass::Error<mir::linker::ErrorKind>),
#[error("{}{}", label("(Macro) "), .0)]
MacroError(#[from] mir::pass::Error<macros::ErrorKind>),
#[error("{}{}", label("(Generics) "), .0)]
GenericsError(#[from] mir::pass::Error<generics::ErrorKind>),
#[error("{}{}", label("(Codegen) "), .0)]
CodegenError(#[from] codegen::ErrorKind),
}
@ -62,6 +64,7 @@ impl ErrorKind {
codegen::ErrorKind::Null => Default::default(),
},
ErrorKind::MacroError(error) => error.metadata,
ErrorKind::GenericsError(error) => error.metadata,
}
}
@ -74,6 +77,7 @@ impl ErrorKind {
ErrorKind::LinkerError(_) => "linker",
ErrorKind::MacroError(_) => "macro-pass",
ErrorKind::CodegenError(_) => "codegen",
ErrorKind::GenericsError(_) => "generics",
}
}
}

View File

@ -70,6 +70,7 @@ use reid_lib::{compile::CompileOutput, Context};
use crate::{
ast::TopLevelStatement,
mir::{
generics::GenericsPass,
macros::{form_macros, MacroModule, MacroPass},
SourceModuleId,
},
@ -177,6 +178,24 @@ pub fn perform_all_passes<'map>(
is_lib: true,
})?;
#[cfg(debug_assertions)]
log::trace!("{:-^100}", "LINKER OUTPUT");
#[cfg(debug_assertions)]
log::trace!("{:#}", &context);
#[cfg(debug_assertions)]
log::trace!("{:#?}", &state);
if !state.errors.is_empty() {
return Err(ReidError::from_kind(
state.errors.iter().map(|e| e.clone().into()).collect(),
module_map.clone(),
));
}
let state = context.pass(&mut GenericsPass {
function_map: HashMap::new(),
})?;
for module in &mut context.modules {
for intrinsic in form_intrinsics() {
module.1.functions.insert(0, intrinsic);
@ -184,7 +203,7 @@ pub fn perform_all_passes<'map>(
}
#[cfg(debug_assertions)]
log::trace!("{:-^100}", "LINKER OUTPUT");
log::trace!("{:-^100}", "GENERICS OUTPUT");
#[cfg(debug_assertions)]
log::trace!("{:#}", &context);
#[cfg(debug_assertions)]

View File

@ -157,9 +157,14 @@ impl Display for FunctionDefinition {
}
write!(
f,
"{}fn {}({}) -> {:#} ",
"{}fn {}<{}>({}) -> {:#} ",
if self.is_pub { "pub " } else { "" },
self.name,
self.generics
.iter()
.map(|(n, t)| format!("{n} = {:?}", t))
.collect::<Vec<_>>()
.join(", "),
self.parameters
.iter()
.map(|FunctionParam { name, ty, .. }| format!("{}: {:#}", name, ty))
@ -489,12 +494,14 @@ impl Display for VagueType {
VagueType::Integer => write!(f, "Number"),
VagueType::TypeRef(id) => write!(f, "TypeRef({0})", id),
VagueType::Decimal => write!(f, "Decimal"),
VagueType::Named(name) => write!(f, "Named<{name}>"),
}
} else {
match self {
VagueType::Unknown => write!(f, "{{unknown}}"),
VagueType::Integer => write!(f, "Number"),
VagueType::TypeRef(_) => write!(f, "{{unknown}}"),
VagueType::Named(name) => write!(f, "{name}"),
VagueType::Decimal => write!(f, "Decimal"),
}
}

296
reid/src/mir/generics.rs Normal file
View File

@ -0,0 +1,296 @@
use std::{collections::HashMap, path::PathBuf};
use log::Metadata;
use crate::mir::{
self, generics, CustomTypeKey, FunctionCall, FunctionDefinition, FunctionParam, GlobalKind, GlobalValue,
IfExpression, Literal, Module, SourceModuleId, TypeKind, WhileStatement,
};
use super::pass::{Pass, PassResult, PassState};
#[derive(thiserror::Error, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum ErrorKind {
#[error("Should never be encountered!")]
Null,
#[error("Expected {0} type-arguments, got {1}!")]
InvalidNumberTypeArguments(u32, u32),
}
type Calls = Vec<(Vec<TypeKind>, mir::Metadata)>;
pub struct GenericsPass {
pub function_map: HashMap<SourceModuleId, Functions>,
}
#[derive(Debug)]
pub struct Functions {
calls: HashMap<String, Calls>,
assoc_calls: HashMap<(TypeKind, String), Calls>,
}
#[derive(Default, Clone)]
pub struct GenericsPassData {
generic_types: HashMap<String, TypeKind>,
}
type GenericsPassState<'map, 'st, 'sc> = PassState<'st, 'sc, GenericsPassData, ErrorKind>;
impl Pass for GenericsPass {
type Data = GenericsPassData;
type TError = ErrorKind;
fn context(&mut self, context: &mut mir::Context, mut _state: PassState<Self::Data, Self::TError>) -> PassResult {
let mut function_map = HashMap::new();
for module in &context.modules {
function_map.insert(
module.0.clone(),
Functions {
calls: HashMap::new(),
assoc_calls: HashMap::new(),
},
);
}
for module in &mut context.modules {
let mut calls = HashMap::new();
let mut assoc_calls = HashMap::new();
for function in &mut module.1.associated_functions {
match &mut function.1.kind {
mir::FunctionDefinitionKind::Local(block, _) => block.find_calls(&mut calls, &mut assoc_calls),
mir::FunctionDefinitionKind::Extern(_) => {}
mir::FunctionDefinitionKind::Intrinsic(_) => {}
}
}
for function in &mut module.1.functions {
match &mut function.kind {
mir::FunctionDefinitionKind::Local(block, _) => block.find_calls(&mut calls, &mut assoc_calls),
mir::FunctionDefinitionKind::Extern(_) => {}
mir::FunctionDefinitionKind::Intrinsic(_) => {}
}
}
for function in &module.1.associated_functions {
if let Some(source) = function.1.source {
let key = (function.0.clone(), function.1.name.clone());
function_map
.get_mut(&source)
.unwrap()
.assoc_calls
.insert(key.clone(), assoc_calls.get(&key).cloned().unwrap_or_default());
}
}
for function in &module.1.functions {
if let Some(source) = function.source {
function_map.get_mut(&source).unwrap().calls.insert(
function.name.clone(),
calls.get(&function.name).cloned().unwrap_or_default(),
);
}
}
}
self.function_map = function_map;
Ok(())
}
fn module(&mut self, module: &mut mir::Module, mut state: PassState<Self::Data, Self::TError>) -> PassResult {
for function in module.functions.drain(..).collect::<Vec<_>>() {
if let Some(source) = function.source {
let functions = self.function_map.get(&source).unwrap();
let calls = functions.calls.get(&function.name).unwrap();
for call in calls {
if call.0.len() != function.generics.len() {
state.note_errors(
&vec![ErrorKind::InvalidNumberTypeArguments(
function.generics.len() as u32,
call.0.len() as u32,
)],
call.1,
);
}
}
if function.generics.len() > 0 {
for call in calls {
if let Some(clone) = function.try_clone() {
let generics = function
.generics
.iter()
.zip(call.0.clone())
.map(|((n, _), t)| (n.clone(), t.clone()))
.collect();
module.functions.push(FunctionDefinition {
name: name_fmt(function.name.clone(), call.0.clone()),
return_type: function.return_type.replace_generic(&generics),
parameters: function
.parameters
.iter()
.map(|p| FunctionParam {
ty: p.ty.replace_generic(&generics),
..p.clone()
})
.collect(),
generics,
..clone
});
}
}
} else {
module.functions.push(function);
}
} else {
module.functions.push(function);
}
}
Ok(())
}
fn function(
&mut self,
func: &mut FunctionDefinition,
mut state: PassState<Self::Data, Self::TError>,
) -> PassResult {
for (name, ty) in &func.generics {
state.scope.data.generic_types.insert(name.clone(), ty.clone());
}
Ok(())
}
fn stmt(&mut self, stmt: &mut mir::Statement, mut state: PassState<Self::Data, Self::TError>) -> PassResult {
match &mut stmt.0 {
mir::StmtKind::Let(var_ref, _, _) => match var_ref.0.clone() {
TypeKind::CustomType(custom_type_key) => {
if let Some(ty) = state.scope.data.generic_types.get(&custom_type_key.0) {
var_ref.0 = ty.clone();
}
}
_ => {}
},
_ => {}
}
Ok(())
}
}
impl mir::Block {
fn find_calls(&mut self, calls: &mut HashMap<String, Calls>, assoc_calls: &mut HashMap<(TypeKind, String), Calls>) {
for statement in &mut self.statements {
statement.find_calls(calls, assoc_calls);
}
if let Some((_, Some(e))) = &mut self.return_expression {
e.find_calls(calls, assoc_calls);
}
}
}
impl mir::Statement {
fn find_calls(&mut self, calls: &mut HashMap<String, Calls>, assoc_calls: &mut HashMap<(TypeKind, String), Calls>) {
match &mut self.0 {
mir::StmtKind::Let(_, _, expression) => expression.find_calls(calls, assoc_calls),
mir::StmtKind::Set(expression, expression1) => {
expression.find_calls(calls, assoc_calls);
expression1.find_calls(calls, assoc_calls);
}
mir::StmtKind::Import(_) => {}
mir::StmtKind::Expression(expression) => expression.find_calls(calls, assoc_calls),
mir::StmtKind::While(WhileStatement { condition, block, .. }) => {
condition.find_calls(calls, assoc_calls);
block.find_calls(calls, assoc_calls);
}
}
}
}
impl mir::Expression {
fn find_calls(&mut self, calls: &mut HashMap<String, Calls>, assoc_calls: &mut HashMap<(TypeKind, String), Calls>) {
match &mut self.0 {
mir::ExprKind::Variable(_) => {}
mir::ExprKind::Indexed(expression, _, expression1) => {
expression.find_calls(calls, assoc_calls);
expression1.find_calls(calls, assoc_calls);
}
mir::ExprKind::Accessed(expression, _, _, _) => {
expression.find_calls(calls, assoc_calls);
}
mir::ExprKind::Array(expressions) => {
for expression in expressions {
expression.find_calls(calls, assoc_calls);
}
}
mir::ExprKind::Struct(_, items) => {
for item in items {
item.1.find_calls(calls, assoc_calls);
}
}
mir::ExprKind::Literal(_) => {}
mir::ExprKind::BinOp(_, lhs, rhs, _) => {
lhs.find_calls(calls, assoc_calls);
rhs.find_calls(calls, assoc_calls);
}
mir::ExprKind::FunctionCall(function_call) => {
if let Some(calls) = calls.get_mut(&function_call.name) {
calls.push((function_call.generics.clone(), self.1));
} else {
calls.insert(
function_call.name.clone(),
vec![(function_call.generics.clone(), self.1)],
);
}
if function_call.generics.len() > 0 {
function_call.name = name_fmt(function_call.name.clone(), function_call.generics.clone())
}
}
mir::ExprKind::AssociatedFunctionCall(ty, function_call) => {
if let Some(calls) = assoc_calls.get_mut(&(ty.clone(), function_call.name.clone())) {
calls.push((function_call.generics.clone(), self.1));
} else {
assoc_calls.insert(
(ty.clone(), function_call.name.clone()),
vec![(function_call.generics.clone(), self.1)],
);
}
if function_call.generics.len() > 0 {
function_call.name = name_fmt(function_call.name.clone(), function_call.generics.clone())
}
}
mir::ExprKind::If(IfExpression(cond, then_e, else_e)) => {
cond.find_calls(calls, assoc_calls);
then_e.find_calls(calls, assoc_calls);
if let Some(else_e) = else_e.as_mut() {
else_e.find_calls(calls, assoc_calls);
}
}
mir::ExprKind::Block(block) => block.find_calls(calls, assoc_calls),
mir::ExprKind::Borrow(expression, _) => expression.find_calls(calls, assoc_calls),
mir::ExprKind::Deref(expression) => expression.find_calls(calls, assoc_calls),
mir::ExprKind::CastTo(expression, _) => expression.find_calls(calls, assoc_calls),
mir::ExprKind::GlobalRef(_, _) => {}
}
}
}
fn name_fmt(name: String, generics: Vec<TypeKind>) -> String {
format!(
"{}.{}",
name,
generics.iter().map(|t| t.to_string()).collect::<Vec<_>>().join(".")
)
}
impl TypeKind {
fn replace_generic(&self, generics: &Vec<(String, TypeKind)>) -> TypeKind {
match self {
TypeKind::CustomType(CustomTypeKey(name, _)) => {
if let Some((_, inner)) = generics.iter().find(|(n, _)| n == name) {
inner.clone()
} else {
self.clone()
}
}
_ => self.clone(),
}
}
}

View File

@ -171,6 +171,7 @@ impl TypeKind {
VagueType::Integer => TypeCategory::Integer,
VagueType::Decimal => TypeCategory::Real,
VagueType::TypeRef(_) => TypeCategory::TypeRef,
VagueType::Named(_) => TypeCategory::Other,
},
}
}

View File

@ -311,6 +311,7 @@ impl<'map> Pass for LinkerPass<'map> {
importer_module.functions.push(FunctionDefinition {
name: function.name.clone(),
generics: function.generics.clone(),
documentation: function.documentation.clone(),
linkage_name: None,
is_pub: false,
@ -460,6 +461,7 @@ impl<'map> Pass for LinkerPass<'map> {
FunctionDefinition {
name: func_name.clone(),
documentation: func.documentation.clone(),
generics: func.generics.clone(),
linkage_name: Some(format!("{}::{}", ty, func_name)),
is_pub: false,
is_imported: false,

View File

@ -11,6 +11,7 @@ use crate::{
};
mod fmt;
pub mod generics;
pub mod implement;
pub mod linker;
pub mod macros;
@ -135,13 +136,14 @@ pub enum TypeKind {
Vague(VagueType),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum VagueType {
Unknown,
/// Some integer value (e.g. 5)
Integer,
/// Some decimal fractional value (e.g. 1.5)
Decimal,
Named(String),
TypeRef(usize),
}
@ -164,7 +166,7 @@ impl StructType {
impl TypeKind {
pub fn known(&self) -> Result<TypeKind, VagueType> {
if let TypeKind::Vague(vague) = self {
Err(*vague)
Err(vague.clone())
} else {
Ok(self.clone())
}
@ -309,6 +311,7 @@ pub struct IfExpression(pub Box<Expression>, pub Box<Expression>, pub Box<Option
#[derive(Debug, Clone)]
pub struct FunctionCall {
pub name: String,
pub generics: Vec<TypeKind>,
pub return_type: TypeKind,
pub parameters: Vec<Expression>,
pub is_macro: bool,
@ -320,6 +323,7 @@ pub struct FunctionDefinition {
pub name: String,
pub documentation: Option<String>,
pub linkage_name: Option<String>,
pub generics: Vec<(String, TypeKind)>,
/// Whether this function is visible to outside modules
pub is_pub: bool,
/// Whether this module is from an external module, and has been imported
@ -331,6 +335,40 @@ pub struct FunctionDefinition {
pub signature_meta: Metadata,
}
impl FunctionDefinition {
pub fn try_clone(&self) -> Option<FunctionDefinition> {
match &self.kind {
FunctionDefinitionKind::Local(block, metadata) => Some(FunctionDefinition {
name: self.name.clone(),
documentation: self.documentation.clone(),
linkage_name: self.linkage_name.clone(),
generics: self.generics.clone(),
is_pub: self.is_pub.clone(),
is_imported: self.is_imported.clone(),
return_type: self.return_type.clone(),
parameters: self.parameters.clone(),
kind: FunctionDefinitionKind::Local(block.clone(), metadata.clone()),
source: self.source.clone(),
signature_meta: self.signature_meta.clone(),
}),
FunctionDefinitionKind::Extern(e) => Some(FunctionDefinition {
name: self.name.clone(),
documentation: self.documentation.clone(),
linkage_name: self.linkage_name.clone(),
generics: self.generics.clone(),
is_pub: self.is_pub.clone(),
is_imported: self.is_imported.clone(),
return_type: self.return_type.clone(),
parameters: self.parameters.clone(),
kind: FunctionDefinitionKind::Extern(*e),
source: self.source.clone(),
signature_meta: self.signature_meta.clone(),
}),
FunctionDefinitionKind::Intrinsic(intrinsic_function) => None,
}
}
}
#[derive(Debug, Clone, PartialEq, PartialOrd)]
pub struct FunctionParam {
pub name: String,

View File

@ -290,10 +290,11 @@ impl TypeKind {
pub(super) fn or_default(&self) -> Result<TypeKind, ErrorKind> {
Ok(match self {
TypeKind::Vague(vague_type) => match &vague_type {
Vague::Unknown => Err(ErrorKind::TypeIsVague(*vague_type))?,
Vague::Unknown => Err(ErrorKind::TypeIsVague(vague_type.clone()))?,
Vague::Integer => TypeKind::I32,
Vague::TypeRef(_) => panic!("Hinted default!"),
VagueType::Decimal => TypeKind::F32,
VagueType::Named(_) => panic!("Defaulted unknown named!"),
},
TypeKind::Array(type_kind, len) => TypeKind::Array(Box::new(type_kind.or_default()?), *len),
TypeKind::Borrow(type_kind, mutable) => TypeKind::Borrow(Box::new(type_kind.or_default()?), *mutable),
@ -339,7 +340,7 @@ impl TypeKind {
TypeKind::Borrow(type_kind, _) => type_kind.is_known(state),
TypeKind::UserPtr(type_kind) => type_kind.is_known(state),
TypeKind::CodegenPtr(type_kind) => type_kind.is_known(state),
TypeKind::Vague(vague_type) => Err(ErrorKind::TypeIsVague(*vague_type)),
TypeKind::Vague(vague_type) => Err(ErrorKind::TypeIsVague(vague_type.clone())),
_ => Ok(()),
}
}

View File

@ -368,6 +368,7 @@ impl<'outer> ScopeTypeRefs<'outer> {
self.narrow_to_type(&typeref, &self.try_default_deep(&typeref.resolve_deep()?)?)?
.resolve_deep()?
}
VagueType::Named(_) => ty.clone(),
},
_ => ty.clone(),
})