From 3e6f6cee40fee37771859231a5774b34e29fff8c Mon Sep 17 00:00:00 2001 From: Sofia Date: Sun, 7 Sep 2025 15:32:57 +0300 Subject: [PATCH] Initial commit --- .gitignore | 1 + Cargo.lock | 132 ++++ Cargo.toml | 12 + README.md | 5 + examples/libtest.rs | 56 ++ src/builder.rs | 835 +++++++++++++++++++++++++ src/compile.rs | 1257 ++++++++++++++++++++++++++++++++++++++ src/debug_information.rs | 399 ++++++++++++ src/fmt.rs | 595 ++++++++++++++++++ src/intrinsics.rs | 263 ++++++++ src/lib.rs | 741 ++++++++++++++++++++++ src/pad_adapter.rs | 69 +++ src/util.rs | 115 ++++ 13 files changed, 4480 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 README.md create mode 100644 examples/libtest.rs create mode 100644 src/builder.rs create mode 100644 src/compile.rs create mode 100644 src/debug_information.rs create mode 100644 src/fmt.rs create mode 100644 src/intrinsics.rs create mode 100644 src/lib.rs create mode 100644 src/pad_adapter.rs create mode 100644 src/util.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1de5659 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +target \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..5f05668 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,132 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "anyhow" +version = "1.0.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100" + +[[package]] +name = "cc" +version = "1.2.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5252b3d2648e5eedbc1a6f501e3c795e07025c1e93bbf8bbdd6eef7f447a6d54" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "find-msvc-tools" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fd99930f64d146689264c637b5af2f0233a933bef0d8570e2526bf9e083192d" + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.175" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" + +[[package]] +name = "llvm-sys" +version = "201.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bb947e8b79254ca10d496d0798a9ba1287dcf68e50a92b016fec1cc45bef447" +dependencies = [ + "anyhow", + "cc", + "lazy_static", + "libc", + "regex-lite", + "semver", +] + +[[package]] +name = "proc-macro2" +version = "1.0.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "regex-lite" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "943f41321c63ef1c92fd763bfe054d2668f7f225a5c29f0105903dc2fc04ba30" + +[[package]] +name = "reid-lib" +version = "1.0.0-beta.4" +dependencies = [ + "llvm-sys", + "thiserror", +] + +[[package]] +name = "semver" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "syn" +version = "2.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..76e8e34 --- /dev/null +++ b/Cargo.toml @@ -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" \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..23ca439 --- /dev/null +++ b/README.md @@ -0,0 +1,5 @@ +## Reid LLVM Library + +Rusty bindings for LLVM, used primarily for +[Reid](https://git.teascade.net/teascade/reid-llvm) (hence the naming), but can +be used for other LLVM-based projects as well. Uses [llvm-sys](https://gitlab.com/taricorp/llvm-sys.rs) directly. \ No newline at end of file diff --git a/examples/libtest.rs b/examples/libtest.rs new file mode 100644 index 0000000..4733ad1 --- /dev/null +++ b/examples/libtest.rs @@ -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()); +} diff --git a/src/builder.rs b/src/builder.rs new file mode 100644 index 0000000..fd89f89 --- /dev/null +++ b/src/builder.rs @@ -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, + pub(crate) types: Vec, + pub(crate) debug_information: Option, + pub(crate) constants: Vec, + pub(crate) globals: Vec, +} + +#[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, + /// Debug scope value of this current function + pub(crate) debug_info: Option, +} + +#[derive(Clone)] +pub struct BlockHolder { + pub(crate) value: BlockValue, + pub(crate) data: BlockData, + pub(crate) instructions: Vec, +} + +#[derive(Clone)] +pub struct InstructionHolder { + pub(crate) value: InstructionValue, + pub(crate) data: InstructionData, + pub(crate) name: String, + pub(crate) record: Option, +} + +#[derive(Clone)] +pub(crate) struct Builder { + pub(crate) modules: Rc>>, + 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 { + 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 { + 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>> { + 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) -> 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 { + 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 { + let own_type = self.get_type(builder)?; + own_type + .cast_instruction(*self, &ty) + .ok_or(ErrorKind::ImpossibleCast(own_type, ty.clone())) + } +} diff --git a/src/compile.rs b/src/compile.rs new file mode 100644 index 0000000..ff60150 --- /dev/null +++ b/src/compile.rs @@ -0,0 +1,1257 @@ +//! This module contains all of the relevant code that is needed to compile the +//! LLIR ([`Context`]) into LLVM IR. This module is the only one that interfaces +//! with the LLVM API. + +use std::{ + cell::Ref, + collections::HashMap, + ptr::{null, null_mut}, +}; + +use llvm_sys::{ + LLVMIntPredicate, LLVMLinkage, LLVMRealPredicate, LLVMValueKind, + analysis::LLVMVerifyModule, + core::*, + debuginfo::*, + linker::LLVMLinkModules2, + prelude::*, + target::{ + LLVM_InitializeAllAsmParsers, LLVM_InitializeAllAsmPrinters, LLVM_InitializeAllTargetInfos, + LLVM_InitializeAllTargetMCs, LLVM_InitializeAllTargets, LLVMSetModuleDataLayout, + }, + target_machine::{ + LLVMCodeGenFileType, LLVMCreateTargetDataLayout, LLVMCreateTargetMachine, LLVMGetDefaultTargetTriple, + LLVMGetTargetFromTriple, LLVMTargetMachineEmitToMemoryBuffer, + }, +}; + +use crate::{ + CustomTypeKind, + builder::{ConstantValue, GlobalValue, TypeHolder, TypeValue}, + debug_information::*, + util::{ErrorMessageHolder, MemoryBufferHolder, from_cstring, into_cstring}, +}; + +use super::{ + CmpPredicate, ConstValueKind, Context, TerminatorKind, Type, + builder::{ + BlockHolder, BlockValue, Builder, FunctionHolder, FunctionValue, InstructionHolder, InstructionValue, + ModuleHolder, + }, +}; + +pub struct LLVMContext { + context_ref: LLVMContextRef, + builder_ref: LLVMBuilderRef, +} + +impl Drop for LLVMContext { + fn drop(&mut self) { + unsafe { + LLVMDisposeBuilder(self.builder_ref); + LLVMContextDispose(self.context_ref); + } + } +} + +pub struct CompiledModule { + module_ref: LLVMModuleRef, + _context: LLVMContext, + cpu: String, + features: String, +} + +pub struct CompileOutput { + pub triple: String, + pub assembly: String, + pub obj_buffer: Vec, + pub llvm_ir: String, +} + +impl CompiledModule { + pub fn output(&self) -> CompileOutput { + unsafe { + LLVM_InitializeAllTargets(); + LLVM_InitializeAllTargetInfos(); + LLVM_InitializeAllTargetMCs(); + LLVM_InitializeAllAsmParsers(); + LLVM_InitializeAllAsmPrinters(); + + let triple = LLVMGetDefaultTargetTriple(); + + let mut target: _ = null_mut(); + let mut err = ErrorMessageHolder::null(); + LLVMGetTargetFromTriple(triple, &mut target, err.borrow_mut()); + err.into_result().unwrap(); + + let target_machine = LLVMCreateTargetMachine( + target, + triple, + into_cstring(self.cpu.clone()).as_ptr(), + into_cstring(self.features.clone()).as_ptr(), + llvm_sys::target_machine::LLVMCodeGenOptLevel::LLVMCodeGenLevelNone, + llvm_sys::target_machine::LLVMRelocMode::LLVMRelocDefault, + llvm_sys::target_machine::LLVMCodeModel::LLVMCodeModelDefault, + ); + + let data_layout = LLVMCreateTargetDataLayout(target_machine); + LLVMSetTarget(self.module_ref, triple); + LLVMSetModuleDataLayout(self.module_ref, data_layout); + + let mut asm_buffer = MemoryBufferHolder::empty("asm"); + let mut err = ErrorMessageHolder::null(); + LLVMTargetMachineEmitToMemoryBuffer( + target_machine, + self.module_ref, + LLVMCodeGenFileType::LLVMAssemblyFile, + err.borrow_mut(), + &mut asm_buffer.buffer, + ); + err.into_result().unwrap(); + + let mut obj_buffer = MemoryBufferHolder::empty("obj"); + let mut err = ErrorMessageHolder::null(); + LLVMTargetMachineEmitToMemoryBuffer( + target_machine, + self.module_ref, + LLVMCodeGenFileType::LLVMObjectFile, + err.borrow_mut(), + &mut obj_buffer.buffer, + ); + err.into_result().unwrap(); + + let llvm_ir = + from_cstring(LLVMPrintModuleToString(self.module_ref)).expect("Unable to print LLVM IR to string"); + + let mut err = ErrorMessageHolder::null(); + LLVMVerifyModule( + self.module_ref, + llvm_sys::analysis::LLVMVerifierFailureAction::LLVMPrintMessageAction, + err.borrow_mut(), + ); + + CompileOutput { + triple: from_cstring(triple).expect("Unable to convert triple from cstring"), + assembly: asm_buffer + .as_string() + .expect("Error while converting assembly-buffer to string"), + obj_buffer: obj_buffer.as_buffer(), + llvm_ir, + } + } + } +} + +impl Context { + pub fn compile(&self, cpu: Option, features: Vec) -> CompiledModule { + unsafe { + let context_ref = LLVMContextCreate(); + + let context = LLVMContext { + context_ref, + builder_ref: LLVMCreateBuilderInContext(context_ref), + }; + + let module_holders = self.builder.get_modules(); + + let main_module = module_holders + .borrow() + .iter() + .find(|m| m.data.name == "main") + .unwrap_or(module_holders.borrow().first().unwrap()) + .clone(); + + let main_module_ref = main_module.compile(&context, &self.builder); + + for holder in module_holders.borrow().iter() { + if holder.value == main_module.value { + continue; + } + let module_ref = holder.compile(&context, &self.builder); + LLVMLinkModules2(main_module_ref, module_ref); + } + + CompiledModule { + module_ref: main_module_ref, + _context: context, + cpu: cpu.unwrap_or("generic".to_owned()), + features: features.join(","), + } + } + } +} + +pub struct LLVMModule<'a> { + builder: &'a Builder, + context_ref: LLVMContextRef, + builder_ref: LLVMBuilderRef, + #[allow(dead_code)] + module_ref: LLVMModuleRef, + functions: HashMap, + blocks: HashMap, + values: HashMap, + types: HashMap, + constants: HashMap, + globals: HashMap, + debug: Option>, +} + +impl<'a> Drop for LLVMModule<'a> { + fn drop(&mut self) { + if let Some(LLVMDebugInformation { builder, .. }) = self.debug { + unsafe { + LLVMDisposeDIBuilder(builder); + } + } + } +} + +pub struct LLVMDebugInformation<'a> { + info: &'a DebugInformation, + builder: LLVMDIBuilderRef, + file_ref: LLVMMetadataRef, + scopes: &'a mut HashMap, + types: &'a mut HashMap, + metadata: &'a mut HashMap, + locations: &'a mut HashMap, +} + +#[derive(Clone, Copy)] +pub struct LLVMFunction { + type_ref: LLVMTypeRef, + value_ref: LLVMValueRef, + metadata: Option, +} + +pub struct LLVMValue { + ty: Type, + value_ref: LLVMValueRef, +} + +impl ModuleHolder { + fn compile(&self, context: &LLVMContext, builder: &Builder) -> LLVMModuleRef { + unsafe { + let module_ref = + LLVMModuleCreateWithNameInContext(into_cstring(&self.data.name).as_ptr(), context.context_ref); + + // Compile the contents + + let mut types = HashMap::new(); + let mut constants = HashMap::new(); + let mut globals = HashMap::new(); + let mut metadata = HashMap::new(); + let mut scopes = HashMap::new(); + let mut locations = HashMap::new(); + + let mut debug = if let Some(debug) = &self.debug_information { + let di_builder = LLVMCreateDIBuilder(module_ref); + + let file_ref = LLVMDIBuilderCreateFile( + di_builder, + into_cstring(debug.file.name.clone()).as_ptr(), + debug.file.name.len(), + into_cstring(debug.file.directory.clone()).as_ptr(), + debug.file.directory.len(), + ); + + let flags = String::new(); + + let compile_unit = LLVMDIBuilderCreateCompileUnit( + di_builder, + LLVMDWARFSourceLanguage::LLVMDWARFSourceLanguageC, + file_ref, + into_cstring(builder.producer.clone()).as_ptr(), + builder.producer.len(), + // is optimized + 0, + into_cstring(flags.clone()).as_ptr(), + flags.len(), + // Runtime version + 0, + // Split filename + into_cstring(debug.file.name.clone()).as_ptr(), + debug.file.name.len(), + LLVMDWARFEmissionKind::LLVMDWARFEmissionKindFull, + // The DWOId if this is a split skeleton compile unit. + 0, + // Whether to emit inline debug info. + true as i32, + // Whether to emit extra debug info for + // profile collection. + false as i32, + // The clang system root (value of -isysroot). + c"".as_ptr(), + 0, + // The SDK name. On Darwin, this is the last component of + // the sysroot. + c"".as_ptr(), + 0, + ); + + // let scope = debug.get_scopes(); + // scopes.insert(scope.borrow().value.clone(), compile_unit); + // for scope in &scope.borrow().inner_scopes { + // dbg!("hello"); + // scope.compile_scope(compile_unit, file_ref, &mut scopes, di_builder); + // } + // dbg!("after!"); + + scopes.insert(DebugScopeValue(Vec::new()), compile_unit); + + let debug = LLVMDebugInformation { + builder: di_builder, + info: debug, + file_ref, + types: &mut types, + metadata: &mut metadata, + scopes: &mut scopes, + locations: &mut locations, + }; + + for ty in debug.info.get_types().borrow().iter() { + let meta_ref = ty.compile_debug(&debug); + debug.types.insert(ty.value.clone(), meta_ref); + } + Some(debug) + } else { + None + }; + + let mut types = HashMap::new(); + for ty in &self.types { + types.insert(ty.value, ty.compile_type(context, &types)); + } + + for constant in &self.constants { + constants.insert( + constant.value, + LLVMValue { + ty: constant.kind.get_type(), + value_ref: constant + .kind + .as_llvm(context.context_ref, context.builder_ref, &constants, &types), + }, + ); + } + + for global in &self.globals { + let initializer = constants.get(&global.initializer).expect("No initializer?"); + let global_value = LLVMAddGlobal( + module_ref, + initializer.ty.as_llvm(context.context_ref, &types), + into_cstring(global.name.clone()).as_ptr(), + ); + LLVMSetInitializer(global_value, initializer.value_ref); + globals.insert(global.value, global_value); + } + + let mut functions = HashMap::new(); + for function in &self.functions { + let func = function.compile_signature(context, module_ref, &types, &mut debug); + functions.insert(function.value, func); + + if let (Some(debug), Some(debug_info)) = (&mut debug, &function.debug_info) { + let scope_refcell = debug.info.get_scope(); + let mut scope = scope_refcell.borrow(); + for i in &debug_info.0 { + scope = Ref::map(scope, |v| v.inner_scopes.get_unchecked(*i)); + } + for scope in &scope.inner_scopes { + scope.compile_scope(func.metadata.unwrap(), debug.file_ref, debug.scopes, debug.builder); + } + } + } + + if let Some(debug) = &mut debug { + for location in debug.info.get_locations().borrow().iter() { + let location_ref = location.compile(context, &debug); + debug.locations.insert(location.value.clone(), location_ref); + } + + for meta in debug.info.get_metadatas().borrow().iter() { + let meta_ref = meta.compile(&debug); + debug.metadata.insert(meta.value.clone(), meta_ref); + } + } + + let mut module = LLVMModule { + builder, + context_ref: context.context_ref, + builder_ref: context.builder_ref, + module_ref, + functions, + types, + blocks: HashMap::new(), + values: HashMap::new(), + constants, + globals, + debug, + }; + + for function in &self.functions { + function.compile(&mut module, self.data.is_main); + } + + if let Some(debug) = &module.debug { + LLVMDIBuilderFinalize(debug.builder); + } + + module_ref + } + } +} + +impl DebugLocationHolder { + unsafe fn compile(&self, context: &LLVMContext, debug: &LLVMDebugInformation) -> LLVMMetadataRef { + unsafe { + LLVMDIBuilderCreateDebugLocation( + context.context_ref, + self.location.pos.line, + self.location.pos.column, + *debug.scopes.get(&self.scope).unwrap(), + null_mut(), + ) + } + } +} + +impl DebugScopeHolder { + unsafe fn compile_scope( + &self, + parent: LLVMMetadataRef, + file: LLVMMetadataRef, + map: &mut HashMap, + di_builder: LLVMDIBuilderRef, + ) { + unsafe { + let scope = match &self.data.kind { + DebugScopeKind::CodegenContext => panic!(), + DebugScopeKind::LexicalScope(data) => LLVMDIBuilderCreateLexicalBlock( + di_builder, + parent, + file, + data.location.pos.line, + data.location.pos.column, + ), + DebugScopeKind::Subprogram(_) => panic!(), + }; + + for inner in &self.inner_scopes { + inner.compile_scope(scope, file, map, di_builder); + } + + map.insert(self.value.clone(), scope); + } + } +} + +impl DebugMetadataHolder { + unsafe fn compile(&self, debug: &LLVMDebugInformation) -> LLVMMetadataRef { + unsafe { + match &self.data { + DebugMetadata::ParamVar(param) => LLVMDIBuilderCreateParameterVariable( + debug.builder, + *debug.scopes.get(&self.location.scope).unwrap(), + into_cstring(param.name.clone()).as_ptr(), + param.name.len(), + param.arg_idx + 1, + debug.file_ref, + self.location.pos.line, + *debug.types.get(¶m.ty).unwrap(), + param.always_preserve as i32, + param.flags.as_llvm(), + ), + DebugMetadata::LocalVar(var) => LLVMDIBuilderCreateAutoVariable( + debug.builder, + *debug.scopes.get(&self.location.scope).unwrap(), + into_cstring(var.name.clone()).as_ptr(), + var.name.len(), + debug.file_ref, + self.location.pos.line, + *debug.types.get(&var.ty).unwrap(), + var.always_preserve as i32, + var.flags.as_llvm(), + 0, + ), + DebugMetadata::VarAssignment => todo!(), + } + } + } +} + +impl DebugTypeHolder { + unsafe fn compile_debug(&self, debug: &LLVMDebugInformation) -> LLVMMetadataRef { + unsafe { + match &self.data { + DebugTypeData::Basic(ty) => LLVMDIBuilderCreateBasicType( + debug.builder, + into_cstring(ty.name.clone()).as_ptr(), + ty.name.len(), + ty.size_bits as u64, + ty.encoding as u32, + ty.flags.as_llvm(), + ), + DebugTypeData::Subprogram(subprogram) => { + let mut params = subprogram + .parameters + .iter() + .map(|p| *debug.types.get(p).unwrap()) + .collect::>(); + LLVMDIBuilderCreateSubroutineType( + debug.builder, + debug.file_ref, + params.as_mut_ptr(), + params.len() as u32, + subprogram.flags.as_llvm(), + ) + } + DebugTypeData::Pointer(ptr) => LLVMDIBuilderCreatePointerType( + debug.builder, + *debug.types.get(&ptr.pointee).unwrap(), + ptr.size_bits, + 0, + 0, + into_cstring(ptr.name.clone()).as_ptr(), + ptr.name.len(), + ), + DebugTypeData::Array(array) => { + let subrange = LLVMDIBuilderGetOrCreateSubrange(debug.builder, 0, array.length as i64); + let mut elements = vec![subrange]; + LLVMDIBuilderCreateArrayType( + debug.builder, + array.size_bits, + 0, + *debug.types.get(&array.element_type).unwrap(), + elements.as_mut_ptr(), + elements.len() as u32, + ) + } + DebugTypeData::Struct(st) => { + let mut elements = st + .fields + .iter() + .map(|field| { + LLVMDIBuilderCreateMemberType( + debug.builder, + *debug.scopes.get(&st.scope).unwrap(), + into_cstring(field.name.clone()).as_ptr(), + field.name.len(), + debug.file_ref, + field.pos.map(|p| p.line).unwrap_or(1), + field.size_bits, + 0, + field.offset, + field.flags.as_llvm(), + *debug.types.get(&field.ty).unwrap(), + ) + }) + .collect::>(); + LLVMDIBuilderCreateStructType( + debug.builder, + *debug.scopes.get(&st.scope).unwrap(), + into_cstring(st.name.clone()).as_ptr(), + st.name.len(), + debug.file_ref, + st.pos.map(|p| p.line).unwrap_or(1), + st.size_bits, + 0, + st.flags.as_llvm(), + null_mut(), // derived from + elements.as_mut_ptr(), + elements.len() as u32, + 0, // Runtime lang + null_mut(), // VTable + null(), // Unique ID + 0, // Unique ID len + ) + } + } + } + } +} + +impl DwarfFlags { + pub fn as_llvm(&self) -> i32 { + 0 + } +} + +impl TypeHolder { + unsafe fn compile_type(&self, context: &LLVMContext, types: &HashMap) -> LLVMTypeRef { + unsafe { + match &self.data.kind { + CustomTypeKind::NamedStruct(named_struct) => { + let mut elem_types = Vec::new(); + for ty in &named_struct.1 { + elem_types.push(ty.as_llvm(context.context_ref, types)); + } + let struct_ty = + LLVMStructCreateNamed(context.context_ref, into_cstring(named_struct.0.clone()).as_ptr()); + LLVMStructSetBody(struct_ty, elem_types.as_mut_ptr(), elem_types.len() as u32, 0); + struct_ty + } + } + } + } +} + +impl FunctionHolder { + unsafe fn compile_signature( + &self, + context: &LLVMContext, + module_ref: LLVMModuleRef, + types: &HashMap, + debug: &mut Option, + ) -> LLVMFunction { + unsafe { + let ret_type = self.data.ret.as_llvm(context.context_ref, types); + let mut param_types: Vec = self + .data + .params + .iter() + .map(|t| t.as_llvm(context.context_ref, types)) + .collect(); + let param_ptr = param_types.as_mut_ptr(); + let param_len = param_types.len(); + + let name = if self.data.flags.is_main { + c"main" + } else { + &into_cstring(&self.data.linkage_name.clone().unwrap_or(self.data.name.clone())) + }; + + let fn_type = LLVMFunctionType(ret_type, param_ptr, param_len as u32, 0); + + let function_ref = LLVMAddFunction(module_ref, name.as_ptr(), fn_type); + + if self.data.flags.inline { + let attribute = LLVMCreateEnumAttribute(context.context_ref, LLVMEnumAttribute::AlwaysInline as u32, 0); + LLVMAddAttributeAtIndex(function_ref, 0, attribute); + } + + let metadata = if let Some(debug) = debug { + if let Some(scope_value) = &self.debug_info { + let scope_data = debug.info.get_scope_data(scope_value).unwrap(); + + let mangled_length_ptr = &mut 0; + let mangled_name = LLVMGetValueName2(function_ref, mangled_length_ptr); + let mangled_length = *mangled_length_ptr; + + let subprogram = match scope_data.kind { + DebugScopeKind::CodegenContext => panic!(), + DebugScopeKind::LexicalScope(_) => panic!(), + DebugScopeKind::Subprogram(subprogram) => LLVMDIBuilderCreateFunction( + debug.builder, + *debug.scopes.get(&scope_data.parent.unwrap()).unwrap(), + into_cstring(subprogram.name.clone()).as_ptr(), + subprogram.name.clone().len(), + mangled_name, + mangled_length, + debug.file_ref, + subprogram.location.pos.line, + *debug.types.get(&subprogram.ty).unwrap(), + subprogram.opts.is_local as i32, + subprogram.opts.is_definition as i32, + subprogram.opts.scope_line, + subprogram.opts.flags.as_llvm(), + subprogram.opts.is_optimized as i32, + ), + }; + + LLVMSetSubprogram(function_ref, subprogram); + debug.scopes.insert(scope_value.clone(), subprogram); + Some(subprogram) + } else { + None + } + } else { + None + }; + + LLVMFunction { + type_ref: fn_type, + value_ref: function_ref, + metadata, + } + } + } + + unsafe fn compile(&self, module: &mut LLVMModule, in_main_module: bool) { + unsafe { + let own_function = *module.functions.get(&self.value).unwrap(); + + if self.data.flags.is_extern { + LLVMSetLinkage(own_function.value_ref, LLVMLinkage::LLVMExternalLinkage); + // TODO Use "available internally" if the other kind of extern + return; + } + + if self.data.flags.is_imported { + if self.data.flags.is_extern { + LLVMSetLinkage(own_function.value_ref, LLVMLinkage::LLVMAvailableExternallyLinkage); + } else { + LLVMSetLinkage(own_function.value_ref, LLVMLinkage::LLVMExternalLinkage); + } + } else { + LLVMSetLinkage(own_function.value_ref, LLVMLinkage::LLVMPrivateLinkage); + } + + if in_main_module && self.data.flags.is_main || self.data.flags.is_pub { + LLVMSetLinkage(own_function.value_ref, LLVMLinkage::LLVMExternalLinkage); + } + + for block in &self.blocks { + if block.data.deleted { + continue; + } + + let block_ref = + LLVMCreateBasicBlockInContext(module.context_ref, into_cstring(&block.data.name).as_ptr()); + LLVMAppendExistingBasicBlock(own_function.value_ref, block_ref); + module.blocks.insert(block.value, block_ref); + } + + for block in &self.blocks { + block.compile(module, &own_function); + } + } + } +} + +impl BlockHolder { + unsafe fn compile(&self, module: &mut LLVMModule, function: &LLVMFunction) { + unsafe { + if self.data.deleted { + return; + } + + let block_ref = *module.blocks.get(&self.value).unwrap(); + LLVMPositionBuilderAtEnd(module.builder_ref, block_ref); + + for instruction in &self.instructions { + let key = instruction.value; + let ret = instruction.compile(module, function, block_ref); + module.values.insert(key, ret); + } + + let term_instr = self + .data + .terminator + .clone() + .expect(&format!("Block {} does not have a terminator!", self.data.name)) + .compile(module, function, block_ref); + + if let Some(location) = &self.data.terminator_location { + LLVMInstructionSetDebugLoc( + term_instr.value_ref, + *module.debug.as_ref().unwrap().locations.get(&location).unwrap(), + ); + } + } + } +} + +impl InstructionHolder { + unsafe fn compile(&self, module: &LLVMModule, function: &LLVMFunction, _block: LLVMBasicBlockRef) -> LLVMValue { + let _ty = self.value.get_type(module.builder).unwrap(); + let name = into_cstring(self.name.clone()); + let val = unsafe { + use super::Instr::*; + match &self.data.kind { + Param(nth) => LLVMGetParam(function.value_ref, *nth as u32), + Constant(val) => val.as_llvm(module.context_ref, module.builder_ref, &module.constants, &module.types), + Add(lhs, rhs) => { + let lhs_val = module.values.get(&lhs).unwrap().value_ref; + let rhs_val = module.values.get(&rhs).unwrap().value_ref; + LLVMBuildAdd(module.builder_ref, lhs_val, rhs_val, name.as_ptr()) + } + FAdd(lhs, rhs) => { + let lhs_val = module.values.get(&lhs).unwrap().value_ref; + let rhs_val = module.values.get(&rhs).unwrap().value_ref; + LLVMBuildFAdd(module.builder_ref, lhs_val, rhs_val, name.as_ptr()) + } + Sub(lhs, rhs) => { + let lhs_val = module.values.get(&lhs).unwrap().value_ref; + let rhs_val = module.values.get(&rhs).unwrap().value_ref; + LLVMBuildSub(module.builder_ref, lhs_val, rhs_val, name.as_ptr()) + } + FSub(lhs, rhs) => { + let lhs_val = module.values.get(&lhs).unwrap().value_ref; + let rhs_val = module.values.get(&rhs).unwrap().value_ref; + LLVMBuildFSub(module.builder_ref, lhs_val, rhs_val, name.as_ptr()) + } + Mul(lhs, rhs) => { + let lhs_val = module.values.get(&lhs).unwrap().value_ref; + let rhs_val = module.values.get(&rhs).unwrap().value_ref; + LLVMBuildMul(module.builder_ref, lhs_val, rhs_val, name.as_ptr()) + } + FMul(lhs, rhs) => { + let lhs_val = module.values.get(&lhs).unwrap().value_ref; + let rhs_val = module.values.get(&rhs).unwrap().value_ref; + LLVMBuildFMul(module.builder_ref, lhs_val, rhs_val, name.as_ptr()) + } + UDiv(lhs, rhs) => { + let lhs_val = module.values.get(&lhs).unwrap().value_ref; + let rhs_val = module.values.get(&rhs).unwrap().value_ref; + LLVMBuildUDiv(module.builder_ref, lhs_val, rhs_val, name.as_ptr()) + } + SDiv(lhs, rhs) => { + let lhs_val = module.values.get(&lhs).unwrap().value_ref; + let rhs_val = module.values.get(&rhs).unwrap().value_ref; + LLVMBuildSDiv(module.builder_ref, lhs_val, rhs_val, name.as_ptr()) + } + FDiv(lhs, rhs) => { + let lhs_val = module.values.get(&lhs).unwrap().value_ref; + let rhs_val = module.values.get(&rhs).unwrap().value_ref; + LLVMBuildFDiv(module.builder_ref, lhs_val, rhs_val, name.as_ptr()) + } + URem(lhs, rhs) => { + let lhs_val = module.values.get(&lhs).unwrap().value_ref; + let rhs_val = module.values.get(&rhs).unwrap().value_ref; + LLVMBuildURem(module.builder_ref, lhs_val, rhs_val, name.as_ptr()) + } + SRem(lhs, rhs) => { + let lhs_val = module.values.get(&lhs).unwrap().value_ref; + let rhs_val = module.values.get(&rhs).unwrap().value_ref; + LLVMBuildSRem(module.builder_ref, lhs_val, rhs_val, name.as_ptr()) + } + FRem(lhs, rhs) => { + let lhs_val = module.values.get(&lhs).unwrap().value_ref; + let rhs_val = module.values.get(&rhs).unwrap().value_ref; + LLVMBuildFRem(module.builder_ref, lhs_val, rhs_val, name.as_ptr()) + } + And(lhs, rhs) => { + let lhs_val = module.values.get(&lhs).unwrap().value_ref; + let rhs_val = module.values.get(&rhs).unwrap().value_ref; + LLVMBuildAnd(module.builder_ref, lhs_val, rhs_val, name.as_ptr()) + } + ICmp(pred, lhs, rhs) => { + let lhs = module.values.get(&lhs).unwrap(); + let rhs_val = module.values.get(&rhs).unwrap().value_ref; + LLVMBuildICmp( + module.builder_ref, + // Signedness from LHS + pred.as_llvm_int(lhs.ty.category().signed()), + lhs.value_ref, + rhs_val, + name.as_ptr(), + ) + } + FCmp(pred, lhs, rhs) => { + let lhs = module.values.get(&lhs).unwrap(); + let rhs_val = module.values.get(&rhs).unwrap().value_ref; + LLVMBuildFCmp( + module.builder_ref, + pred.as_llvm_unsorted_float(), + lhs.value_ref, + rhs_val, + name.as_ptr(), + ) + } + FunctionCall(function_value, instruction_values) => { + let fun = module.functions.get(&function_value).unwrap(); + let mut param_list: Vec = instruction_values + .iter() + .map(|i| module.values.get(i).unwrap().value_ref) + .collect(); + + let is_void = module.builder.function_data(&*function_value).ret == Type::Void; + if is_void { + LLVMContextSetDiscardValueNames(module.context_ref, 1); + } + let value = LLVMBuildCall2( + module.builder_ref, + fun.type_ref, + fun.value_ref, + param_list.as_mut_ptr(), + param_list.len() as u32, + name.as_ptr(), + ); + if is_void { + LLVMContextSetDiscardValueNames(module.context_ref, 0); + } + value + } + Phi(values) => { + let mut inc_values = Vec::new(); + let mut inc_blocks = Vec::new(); + for item in values { + inc_values.push(module.values.get(&item).unwrap().value_ref); + inc_blocks.push(*module.blocks.get(&item.0).unwrap()); + } + let phi = LLVMBuildPhi( + module.builder_ref, + _ty.as_llvm(module.context_ref, &module.types), + name.as_ptr(), + ); + LLVMAddIncoming( + phi, + inc_values.as_mut_ptr(), + inc_blocks.as_mut_ptr(), + values.len() as u32, + ); + phi + } + Alloca(ty) => LLVMBuildAlloca( + module.builder_ref, + ty.as_llvm(module.context_ref, &module.types), + name.as_ptr(), + ), + Load(ptr, ty) => LLVMBuildLoad2( + module.builder_ref, + ty.as_llvm(module.context_ref, &module.types), + module.values.get(&ptr).unwrap().value_ref, + name.as_ptr(), + ), + Store(ptr, val) => { + let store = LLVMBuildStore( + module.builder_ref, + module.values.get(&val).unwrap().value_ref, + module.values.get(&ptr).unwrap().value_ref, + ); + store + } + ArrayAlloca(ty, len) => LLVMBuildArrayAlloca( + module.builder_ref, + ty.as_llvm(module.context_ref, &module.types), + module.values.get(&len).unwrap().value_ref, + name.as_ptr(), + ), + GetElemPtr(arr, indices) => { + let t = arr.get_type(module.builder).unwrap(); + let Type::Ptr(elem_t) = t else { panic!() }; + + let mut llvm_indices: Vec<_> = indices + .iter() + .map(|idx_elem| module.values.get(idx_elem).unwrap().value_ref) + .collect(); + + LLVMBuildInBoundsGEP2( + module.builder_ref, + elem_t.as_llvm(module.context_ref, &module.types), + module.values.get(arr).unwrap().value_ref, + llvm_indices.as_mut_ptr(), + llvm_indices.len() as u32, + name.as_ptr(), + ) + } + GetStructElemPtr(struct_val, idx) => { + let t = struct_val.get_type(module.builder).unwrap(); + let Type::Ptr(struct_t) = t else { panic!() }; + + LLVMBuildStructGEP2( + module.builder_ref, + struct_t.as_llvm(module.context_ref, &module.types), + module.values.get(struct_val).unwrap().value_ref, + *idx, + name.as_ptr(), + ) + } + ExtractValue(agg_val, idx) => LLVMBuildExtractValue( + module.builder_ref, + module.values.get(agg_val).unwrap().value_ref, + *idx, + name.as_ptr(), + ), + Trunc(instr_val, ty) => LLVMBuildTrunc( + module.builder_ref, + module.values.get(instr_val).unwrap().value_ref, + ty.as_llvm(module.context_ref, &module.types), + name.as_ptr(), + ), + ZExt(instr_val, ty) => LLVMBuildZExt( + module.builder_ref, + module.values.get(instr_val).unwrap().value_ref, + ty.as_llvm(module.context_ref, &module.types), + name.as_ptr(), + ), + SExt(instr_val, ty) => LLVMBuildSExt( + module.builder_ref, + module.values.get(instr_val).unwrap().value_ref, + ty.as_llvm(module.context_ref, &module.types), + name.as_ptr(), + ), + FPTrunc(instr_val, ty) => LLVMBuildFPTrunc( + module.builder_ref, + module.values.get(instr_val).unwrap().value_ref, + ty.as_llvm(module.context_ref, &module.types), + name.as_ptr(), + ), + FPExt(instr_val, ty) => LLVMBuildFPExt( + module.builder_ref, + module.values.get(instr_val).unwrap().value_ref, + ty.as_llvm(module.context_ref, &module.types), + name.as_ptr(), + ), + FPToUI(instr_val, ty) => LLVMBuildFPToUI( + module.builder_ref, + module.values.get(instr_val).unwrap().value_ref, + ty.as_llvm(module.context_ref, &module.types), + name.as_ptr(), + ), + FPToSI(instr_val, ty) => LLVMBuildFPToSI( + module.builder_ref, + module.values.get(instr_val).unwrap().value_ref, + ty.as_llvm(module.context_ref, &module.types), + name.as_ptr(), + ), + UIToFP(instr_val, ty) => LLVMBuildUIToFP( + module.builder_ref, + module.values.get(instr_val).unwrap().value_ref, + ty.as_llvm(module.context_ref, &module.types), + name.as_ptr(), + ), + SIToFP(instr_val, ty) => LLVMBuildSIToFP( + module.builder_ref, + module.values.get(instr_val).unwrap().value_ref, + ty.as_llvm(module.context_ref, &module.types), + name.as_ptr(), + ), + PtrToInt(instr_val, ty) => LLVMBuildPtrToInt( + module.builder_ref, + module.values.get(instr_val).unwrap().value_ref, + ty.as_llvm(module.context_ref, &module.types), + name.as_ptr(), + ), + IntToPtr(instr_val, ty) => LLVMBuildIntToPtr( + module.builder_ref, + module.values.get(instr_val).unwrap().value_ref, + ty.as_llvm(module.context_ref, &module.types), + name.as_ptr(), + ), + BitCast(instr_val, ty) => LLVMBuildBitCast( + module.builder_ref, + module.values.get(instr_val).unwrap().value_ref, + ty.as_llvm(module.context_ref, &module.types), + name.as_ptr(), + ), + Or(lhs, rhs) => { + let lhs_val = module.values.get(&lhs).unwrap().value_ref; + let rhs_val = module.values.get(&rhs).unwrap().value_ref; + LLVMBuildOr(module.builder_ref, lhs_val, rhs_val, name.as_ptr()) + } + XOr(lhs, rhs) => { + let lhs_val = module.values.get(&lhs).unwrap().value_ref; + let rhs_val = module.values.get(&rhs).unwrap().value_ref; + LLVMBuildXor(module.builder_ref, lhs_val, rhs_val, name.as_ptr()) + } + ShiftRightLogical(lhs, rhs) => { + let lhs_val = module.values.get(&lhs).unwrap().value_ref; + let rhs_val = module.values.get(&rhs).unwrap().value_ref; + LLVMBuildLShr(module.builder_ref, lhs_val, rhs_val, name.as_ptr()) + } + ShiftRightArithmetic(lhs, rhs) => { + let lhs_val = module.values.get(&lhs).unwrap().value_ref; + let rhs_val = module.values.get(&rhs).unwrap().value_ref; + LLVMBuildAShr(module.builder_ref, lhs_val, rhs_val, name.as_ptr()) + } + ShiftLeft(lhs, rhs) => { + let lhs_val = module.values.get(&lhs).unwrap().value_ref; + let rhs_val = module.values.get(&rhs).unwrap().value_ref; + LLVMBuildShl(module.builder_ref, lhs_val, rhs_val, name.as_ptr()) + } + GetGlobal(global_value) => module.globals.get(global_value).unwrap().clone(), + IsNull(instruction_value) => { + let val = module.values.get(&*instruction_value).unwrap().value_ref; + LLVMBuildIsNull(module.builder_ref, val, name.as_ptr()) + } + } + }; + if let Some(record) = &self.record { + let debug = module.debug.as_ref().unwrap(); + + unsafe { + let mut addr = Vec::::new(); + let expr = LLVMDIBuilderCreateExpression(debug.builder, addr.as_mut_ptr(), addr.len()); + + let location = LLVMDIBuilderCreateDebugLocation( + module.context_ref, + record.location.pos.line, + record.location.pos.column, + *debug.scopes.get(&record.scope).unwrap(), + null_mut(), + ); + + match record.kind { + DebugRecordKind::Declare(instruction_value) => LLVMDIBuilderInsertDeclareRecordBefore( + debug.builder, + module.values.get(&instruction_value).unwrap().value_ref, + *debug.metadata.get(&record.variable).unwrap(), + expr, + location, + val, + ), + DebugRecordKind::Value(instruction_value) => LLVMDIBuilderInsertDbgValueRecordBefore( + debug.builder, + module.values.get(&instruction_value).unwrap().value_ref, + *debug.metadata.get(&record.variable).unwrap(), + expr, + location, + val, + ), + }; + } + } + if let Some(location) = &self.data.location { + unsafe { + match LLVMGetValueKind(val) { + LLVMValueKind::LLVMInstructionValueKind + | LLVMValueKind::LLVMMemoryDefValueKind + | LLVMValueKind::LLVMMemoryUseValueKind + | LLVMValueKind::LLVMMemoryPhiValueKind => { + LLVMInstructionSetDebugLoc( + val, + *module.debug.as_ref().unwrap().locations.get(&location).unwrap(), + ); + } + _ => {} + } + } + } + LLVMValue { + ty: _ty, + value_ref: val, + } + } +} + +impl TerminatorKind { + fn compile(&self, module: &LLVMModule, _function: &LLVMFunction, _block: LLVMBasicBlockRef) -> LLVMValue { + let _ty = self.get_type(module.builder).unwrap(); + let val = unsafe { + match self { + TerminatorKind::Ret(val) => { + let value = module.values.get(val).unwrap(); + LLVMBuildRet(module.builder_ref, value.value_ref) + } + TerminatorKind::RetVoid => LLVMBuildRetVoid(module.builder_ref), + TerminatorKind::Br(block_value) => { + let dest = *module.blocks.get(block_value).unwrap(); + LLVMBuildBr(module.builder_ref, dest) + } + TerminatorKind::CondBr(cond, then_b, else_b) => { + let cond_val = module.values.get(cond).unwrap().value_ref; + let then_bb = *module.blocks.get(then_b).unwrap(); + let else_bb = *module.blocks.get(else_b).unwrap(); + LLVMBuildCondBr(module.builder_ref, cond_val, then_bb, else_bb) + } + } + }; + LLVMValue { + ty: _ty, + value_ref: val, + } + } +} + +impl CmpPredicate { + fn as_llvm_int(&self, signed: bool) -> LLVMIntPredicate { + use CmpPredicate::*; + use LLVMIntPredicate::*; + match (self, signed) { + (LT, true) => LLVMIntSLT, + (LE, true) => LLVMIntSLE, + (GT, true) => LLVMIntSGT, + (GE, true) => LLVMIntSGE, + (LT, false) => LLVMIntULT, + (LE, false) => LLVMIntULE, + (GT, false) => LLVMIntUGT, + (GE, false) => LLVMIntUGE, + (EQ, _) => LLVMIntEQ, + (NE, _) => LLVMIntNE, + } + } + + fn as_llvm_unsorted_float(&self) -> LLVMRealPredicate { + use CmpPredicate::*; + use LLVMRealPredicate::*; + match self { + LT => LLVMRealULT, + LE => LLVMRealULE, + GT => LLVMRealUGT, + GE => LLVMRealUGE, + EQ => LLVMRealUEQ, + NE => LLVMRealUNE, + } + } +} + +impl ConstValueKind { + fn as_llvm( + &self, + context: LLVMContextRef, + builder: LLVMBuilderRef, + constants: &HashMap, + types: &HashMap, + ) -> LLVMValueRef { + unsafe { + let t = self.get_type().as_llvm(context, &types); + match self { + ConstValueKind::Bool(val) => LLVMConstInt(t, *val as u64, 1), + ConstValueKind::I8(val) => LLVMConstInt(t, *val as u64, 1), + ConstValueKind::I16(val) => LLVMConstInt(t, *val as u64, 1), + ConstValueKind::I32(val) => LLVMConstInt(t, *val as u64, 1), + ConstValueKind::I64(val) => LLVMConstInt(t, *val as u64, 1), + ConstValueKind::I128(val) => LLVMConstInt(t, *val as u64, 1), + ConstValueKind::U8(val) => LLVMConstInt(t, *val as u64, 1), + ConstValueKind::U16(val) => LLVMConstInt(t, *val as u64, 1), + ConstValueKind::U32(val) => LLVMConstInt(t, *val as u64, 1), + ConstValueKind::U64(val) => LLVMConstInt(t, *val as u64, 1), + ConstValueKind::U128(val) => LLVMConstInt(t, *val as u64, 1), + ConstValueKind::Str(val) => { + LLVMBuildGlobalString(builder, into_cstring(val).as_ptr(), c"string".as_ptr()) + } + ConstValueKind::F16(val) => LLVMConstReal(t, *val as f64), + ConstValueKind::F32B(val) => LLVMConstReal(t, *val as f64), + ConstValueKind::F32(val) => LLVMConstReal(t, *val as f64), + ConstValueKind::F64(val) => LLVMConstReal(t, *val as f64), + ConstValueKind::F80(val) => LLVMConstReal(t, *val as f64), + ConstValueKind::F128(val) => LLVMConstReal(t, *val as f64), + ConstValueKind::F128PPC(val) => LLVMConstReal(t, *val as f64), + ConstValueKind::Array(constant_values, elem_ty) => { + let mut values = constant_values + .iter() + .map(|v| constants.get(v).unwrap().value_ref) + .collect::>(); + + LLVMConstArray2( + elem_ty.as_llvm(context, &types), + values.as_mut_ptr(), + values.len() as u64, + ) + } + } + } + } +} + +impl Type { + fn as_llvm(&self, context: LLVMContextRef, typemap: &HashMap) -> LLVMTypeRef { + use Type::*; + unsafe { + match self { + I8 | U8 => LLVMInt8TypeInContext(context), + I16 | U16 => LLVMInt16TypeInContext(context), + I32 | U32 => LLVMInt32TypeInContext(context), + I64 | U64 => LLVMInt64TypeInContext(context), + I128 | U128 => LLVMInt128TypeInContext(context), + Bool => LLVMInt1TypeInContext(context), + Void => LLVMVoidTypeInContext(context), + Ptr(ty) => LLVMPointerType(ty.as_llvm(context, typemap), 0), + CustomType(struct_ty) => *typemap.get(struct_ty).unwrap(), + Array(r#type, len) => LLVMArrayType2(r#type.as_llvm(context, typemap), *len), + F16 => LLVMHalfTypeInContext(context), + F32 => LLVMFloatTypeInContext(context), + F32B => LLVMBFloatTypeInContext(context), + F64 => LLVMDoubleTypeInContext(context), + F80 => LLVMX86FP80TypeInContext(context), + F128 => LLVMFP128TypeInContext(context), + F128PPC => LLVMPPCFP128TypeInContext(context), + } + } + } +} + +pub enum LLVMEnumAttribute { + AlwaysInline = 3, +} diff --git a/src/debug_information.rs b/src/debug_information.rs new file mode 100644 index 0000000..bb7b994 --- /dev/null +++ b/src/debug_information.rs @@ -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); + +#[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, +} + +#[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>, + locations: Rc>>, + metadata: Rc>>, + types: Rc>>, +} + +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 { + 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>> { + self.metadata.clone() + } + + pub(crate) fn get_scope(&self) -> Rc> { + self.scope.clone() + } + + pub fn get_types(&self) -> Rc>> { + self.types.clone() + } + + pub(crate) fn get_locations(&self) -> Rc>> { + 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, + pub size_bits: u64, + pub flags: DwarfFlags, + pub fields: Vec, +} + +#[derive(Clone)] +pub struct DebugFieldType { + pub name: String, + pub scope: DebugScopeValue, + pub pos: Option, + pub size_bits: u64, + pub offset: u64, + pub flags: DwarfFlags, + pub ty: DebugTypeValue, +} + +#[derive(Clone)] +pub struct DebugSubprogramType { + pub parameters: Vec, + 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, + 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), +} diff --git a/src/fmt.rs b/src/fmt.rs new file mode 100644 index 0000000..c9f77a9 --- /dev/null +++ b/src/fmt.rs @@ -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, + ) -> 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::>() + .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, + ) -> 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, + ) -> 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, + ) -> 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::>() + .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::>().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) + } +} diff --git a/src/intrinsics.rs b/src/intrinsics.rs new file mode 100644 index 0000000..b76b443 --- /dev/null +++ b/src/intrinsics.rs @@ -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)> { + 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"), + } + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..6a4e735 --- /dev/null +++ b/src/lib.rs @@ -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 = Result; + +#[derive(Debug)] +pub struct Context { + builder: Builder, +} + +impl Context { + pub fn new>(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, +} + +impl<'ctx> Module<'ctx> { + pub fn function( + &self, + name: &str, + linkage: Option, + ret: Type, + params: Vec, + 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 { + 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 { + &self.debug_info + } + + pub fn add_constant(&self, constant: ConstValueKind) -> ConstantValue { + unsafe { self.builder.build_constant(self.value, constant) } + } + + pub fn add_global>(&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, + ret: Type, + params: Vec, + 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, + terminator_location: Option, + 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>(&mut self, name: T, instruction: Instr) -> CompileResult { + unsafe { + self.builder.add_instruction( + &self.value, + InstructionData { + kind: instruction, + location: None, + meta: None, + }, + name.into(), + ) + } + } + + pub fn build(&mut self, instruction: Instr) -> CompileResult { + 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 { + 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 { + 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, + meta: Option, +} + +#[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), + + Alloca(Type), + Load(InstructionValue, Type), + Store(InstructionValue, InstructionValue), + ArrayAlloca(Type, InstructionValue), + GetElemPtr(InstructionValue, Vec), + 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), +} + +#[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, u64), + Ptr(Box), +} + +#[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, 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); + +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 { + 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 { + use TerminatorKind::*; + match self { + Ret(instr_val) => instr_val.get_type(builder), + RetVoid => Ok(Type::Void), + Br(_) => Ok(Type::Void), + CondBr(_, _, _) => Ok(Type::Void), + } + } +} diff --git a/src/pad_adapter.rs b/src/pad_adapter.rs new file mode 100644 index 0000000..3014fa3 --- /dev/null +++ b/src/pad_adapter.rs @@ -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) + } +} diff --git a/src/util.rs b/src/util.rs new file mode 100644 index 0000000..4df2b83 --- /dev/null +++ b/src/util.rs @@ -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>(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 { + 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 { + 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::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 { + 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)) + } +}