Initial commit
This commit is contained in:
		
						commit
						3e6f6cee40
					
				
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | |||||||
|  | target | ||||||
							
								
								
									
										132
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										132
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @ -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" | ||||||
							
								
								
									
										12
									
								
								Cargo.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								Cargo.toml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,12 @@ | |||||||
|  | [package] | ||||||
|  | name = "reid-lib" | ||||||
|  | version = "1.0.0-beta.4" | ||||||
|  | edition = "2024" | ||||||
|  | 
 | ||||||
|  | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | ||||||
|  | 
 | ||||||
|  | [dependencies] | ||||||
|  | ## LLVM Bindings | ||||||
|  | llvm-sys = {version ="201.0.1" } | ||||||
|  | ## Make it easier to generate errors | ||||||
|  | thiserror = "1.0.44" | ||||||
							
								
								
									
										5
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							| @ -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. | ||||||
							
								
								
									
										56
									
								
								examples/libtest.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								examples/libtest.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,56 @@ | |||||||
|  | use reid_lib::{CmpPredicate, ConstValueKind, Context, FunctionFlags, Instr, TerminatorKind, Type}; | ||||||
|  | 
 | ||||||
|  | fn main() { | ||||||
|  |     use ConstValueKind::*; | ||||||
|  |     use Instr::*; | ||||||
|  | 
 | ||||||
|  |     let context = Context::new("libtest"); | ||||||
|  | 
 | ||||||
|  |     let module = context.module("test", true); | ||||||
|  | 
 | ||||||
|  |     let main = module.function("main", None, Type::I32, Vec::new(), FunctionFlags::default()); | ||||||
|  |     let mut m_entry = main.block("entry"); | ||||||
|  | 
 | ||||||
|  |     let fibonacci = module.function("fibonacci", None, Type::I32, vec![Type::I32], FunctionFlags::default()); | ||||||
|  | 
 | ||||||
|  |     let arg = m_entry.build_named("const", Constant(I32(5))).unwrap(); | ||||||
|  |     let fibonacci_call = m_entry | ||||||
|  |         .build_named("const", FunctionCall(fibonacci.value(), vec![arg])) | ||||||
|  |         .unwrap(); | ||||||
|  |     m_entry.terminate(TerminatorKind::Ret(fibonacci_call)).unwrap(); | ||||||
|  | 
 | ||||||
|  |     let mut f_entry = fibonacci.block("entry"); | ||||||
|  | 
 | ||||||
|  |     let num_3 = f_entry.build_named("const", Constant(I32(3))).unwrap(); | ||||||
|  |     let param_n = f_entry.build_named("param", Param(0)).unwrap(); | ||||||
|  |     let cond = f_entry | ||||||
|  |         .build_named("cmp", ICmp(CmpPredicate::LT, param_n, num_3)) | ||||||
|  |         .unwrap(); | ||||||
|  | 
 | ||||||
|  |     let mut then_b = fibonacci.block("then"); | ||||||
|  |     let mut else_b = fibonacci.block("else"); | ||||||
|  | 
 | ||||||
|  |     f_entry | ||||||
|  |         .terminate(TerminatorKind::CondBr(cond, then_b.value(), else_b.value())) | ||||||
|  |         .unwrap(); | ||||||
|  | 
 | ||||||
|  |     let ret_const = then_b.build_named("const", Constant(I32(1))).unwrap(); | ||||||
|  |     then_b.terminate(TerminatorKind::Ret(ret_const)).unwrap(); | ||||||
|  | 
 | ||||||
|  |     let const_1 = else_b.build_named("const", Constant(I32(1))).unwrap(); | ||||||
|  |     let const_2 = else_b.build_named("const", Constant(I32(2))).unwrap(); | ||||||
|  |     let param_1 = else_b.build_named("sub", Sub(param_n, const_1)).unwrap(); | ||||||
|  |     let param_2 = else_b.build_named("sub", Sub(param_n, const_2)).unwrap(); | ||||||
|  |     let call_1 = else_b | ||||||
|  |         .build_named("fibonacci", FunctionCall(fibonacci.value(), vec![param_1])) | ||||||
|  |         .unwrap(); | ||||||
|  |     let call_2 = else_b | ||||||
|  |         .build_named("fibonacci", FunctionCall(fibonacci.value(), vec![param_2])) | ||||||
|  |         .unwrap(); | ||||||
|  | 
 | ||||||
|  |     let add = else_b.build_named("add", Add(call_1, call_2)).unwrap(); | ||||||
|  | 
 | ||||||
|  |     else_b.terminate(TerminatorKind::Ret(add)).unwrap(); | ||||||
|  | 
 | ||||||
|  |     context.compile(None, Vec::new()); | ||||||
|  | } | ||||||
							
								
								
									
										835
									
								
								src/builder.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										835
									
								
								src/builder.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,835 @@ | |||||||
|  | //! This module contains simply [`Builder`] and it's related utility Values.
 | ||||||
|  | //! Builder is the actual struct being modified when building the LLIR.
 | ||||||
|  | 
 | ||||||
|  | use std::{cell::RefCell, rc::Rc}; | ||||||
|  | 
 | ||||||
|  | use crate::{ | ||||||
|  |     Block, BlockData, CompileResult, ConstValueKind, CustomTypeKind, ErrorKind, FunctionData, Instr, InstructionData, | ||||||
|  |     ModuleData, NamedStruct, TerminatorKind, Type, TypeCategory, TypeData, | ||||||
|  |     debug_information::{ | ||||||
|  |         DebugInformation, DebugLocationValue, DebugMetadataValue, DebugScopeValue, InstructionDebugRecordData, | ||||||
|  |     }, | ||||||
|  |     util::match_types, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | #[derive(Clone, Hash, Copy, PartialEq, Eq, PartialOrd)] | ||||||
|  | pub struct ModuleValue(pub(crate) usize); | ||||||
|  | 
 | ||||||
|  | #[derive(Clone, Hash, Copy, PartialEq, Eq, PartialOrd)] | ||||||
|  | pub struct TypeValue(pub(crate) ModuleValue, pub(crate) usize); | ||||||
|  | 
 | ||||||
|  | #[derive(Clone, Hash, Copy, PartialEq, Eq, PartialOrd)] | ||||||
|  | pub struct FunctionValue(pub(crate) ModuleValue, pub(crate) usize); | ||||||
|  | 
 | ||||||
|  | #[derive(Clone, Hash, Copy, PartialEq, Eq, PartialOrd)] | ||||||
|  | pub struct BlockValue(pub(crate) FunctionValue, pub(crate) usize); | ||||||
|  | 
 | ||||||
|  | #[derive(Clone, Hash, Copy, PartialEq, Eq, PartialOrd)] | ||||||
|  | pub struct InstructionValue(pub(crate) BlockValue, pub(crate) usize); | ||||||
|  | 
 | ||||||
|  | #[derive(Debug, Clone, Hash, Copy, PartialEq, Eq)] | ||||||
|  | pub struct ConstantValue(pub(crate) ModuleValue, pub(crate) usize); | ||||||
|  | 
 | ||||||
|  | #[derive(Debug, Clone, Hash, Copy, PartialEq, Eq)] | ||||||
|  | pub struct GlobalValue(pub(crate) ModuleValue, pub(crate) usize); | ||||||
|  | 
 | ||||||
|  | #[derive(Clone)] | ||||||
|  | pub struct ModuleHolder { | ||||||
|  |     pub(crate) value: ModuleValue, | ||||||
|  |     pub(crate) data: ModuleData, | ||||||
|  |     pub(crate) functions: Vec<FunctionHolder>, | ||||||
|  |     pub(crate) types: Vec<TypeHolder>, | ||||||
|  |     pub(crate) debug_information: Option<DebugInformation>, | ||||||
|  |     pub(crate) constants: Vec<ConstantValueHolder>, | ||||||
|  |     pub(crate) globals: Vec<GlobalValueHolder>, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Clone)] | ||||||
|  | pub struct ConstantValueHolder { | ||||||
|  |     pub(crate) value: ConstantValue, | ||||||
|  |     pub(crate) kind: ConstValueKind, | ||||||
|  | } | ||||||
|  | #[derive(Clone)] | ||||||
|  | pub struct GlobalValueHolder { | ||||||
|  |     pub(crate) value: GlobalValue, | ||||||
|  |     pub(crate) name: String, | ||||||
|  |     pub(crate) initializer: ConstantValue, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Clone)] | ||||||
|  | pub struct TypeHolder { | ||||||
|  |     pub(crate) value: TypeValue, | ||||||
|  |     pub(crate) data: TypeData, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Clone)] | ||||||
|  | pub struct FunctionHolder { | ||||||
|  |     pub(crate) value: FunctionValue, | ||||||
|  |     pub(crate) data: FunctionData, | ||||||
|  |     pub(crate) blocks: Vec<BlockHolder>, | ||||||
|  |     /// Debug scope value of this current function
 | ||||||
|  |     pub(crate) debug_info: Option<DebugScopeValue>, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Clone)] | ||||||
|  | pub struct BlockHolder { | ||||||
|  |     pub(crate) value: BlockValue, | ||||||
|  |     pub(crate) data: BlockData, | ||||||
|  |     pub(crate) instructions: Vec<InstructionHolder>, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Clone)] | ||||||
|  | pub struct InstructionHolder { | ||||||
|  |     pub(crate) value: InstructionValue, | ||||||
|  |     pub(crate) data: InstructionData, | ||||||
|  |     pub(crate) name: String, | ||||||
|  |     pub(crate) record: Option<InstructionDebugRecordData>, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Clone)] | ||||||
|  | pub(crate) struct Builder { | ||||||
|  |     pub(crate) modules: Rc<RefCell<Vec<ModuleHolder>>>, | ||||||
|  |     pub(crate) producer: String, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Builder { | ||||||
|  |     pub fn new(producer: String) -> Builder { | ||||||
|  |         Builder { | ||||||
|  |             modules: Rc::new(RefCell::new(Vec::new())), | ||||||
|  |             producer, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub(crate) fn add_module(&self, data: ModuleData) -> ModuleValue { | ||||||
|  |         let value = ModuleValue(self.modules.borrow().len()); | ||||||
|  |         self.modules.borrow_mut().push(ModuleHolder { | ||||||
|  |             value, | ||||||
|  |             data, | ||||||
|  |             functions: Vec::new(), | ||||||
|  |             types: Vec::new(), | ||||||
|  |             debug_information: None, | ||||||
|  |             constants: Vec::new(), | ||||||
|  |             globals: Vec::new(), | ||||||
|  |         }); | ||||||
|  |         value | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub(crate) fn set_debug_information(&self, mod_val: &ModuleValue, debug_info: DebugInformation) { | ||||||
|  |         unsafe { | ||||||
|  |             let mut modules = self.modules.borrow_mut(); | ||||||
|  |             let module = modules.get_unchecked_mut(mod_val.0); | ||||||
|  |             module.debug_information = Some(debug_info); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub(crate) unsafe fn add_type(&self, mod_val: &ModuleValue, data: TypeData) -> TypeValue { | ||||||
|  |         unsafe { | ||||||
|  |             let mut modules = self.modules.borrow_mut(); | ||||||
|  |             let module = modules.get_unchecked_mut(mod_val.0); | ||||||
|  |             let value = TypeValue(module.value, module.types.len()); | ||||||
|  |             module.types.push(TypeHolder { value, data }); | ||||||
|  |             value | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub(crate) unsafe fn add_function(&self, mod_val: &ModuleValue, data: FunctionData) -> FunctionValue { | ||||||
|  |         unsafe { | ||||||
|  |             let mut modules = self.modules.borrow_mut(); | ||||||
|  |             let module = modules.get_unchecked_mut(mod_val.0); | ||||||
|  |             let value = FunctionValue(module.value, module.functions.len()); | ||||||
|  |             module.functions.push(FunctionHolder { | ||||||
|  |                 value, | ||||||
|  |                 data, | ||||||
|  |                 blocks: Vec::new(), | ||||||
|  |                 debug_info: None, | ||||||
|  |             }); | ||||||
|  |             value | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub(crate) unsafe fn add_block(&self, fun_val: &FunctionValue, data: BlockData) -> BlockValue { | ||||||
|  |         unsafe { | ||||||
|  |             let mut modules = self.modules.borrow_mut(); | ||||||
|  |             let module = modules.get_unchecked_mut(fun_val.0.0); | ||||||
|  |             let function = module.functions.get_unchecked_mut(fun_val.1); | ||||||
|  |             let value = BlockValue(function.value, function.blocks.len()); | ||||||
|  |             function.blocks.push(BlockHolder { | ||||||
|  |                 value, | ||||||
|  |                 data, | ||||||
|  |                 instructions: Vec::new(), | ||||||
|  |             }); | ||||||
|  |             value | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub(crate) unsafe fn add_instruction( | ||||||
|  |         &self, | ||||||
|  |         block_val: &BlockValue, | ||||||
|  |         data: InstructionData, | ||||||
|  |         name: String, | ||||||
|  |     ) -> CompileResult<InstructionValue> { | ||||||
|  |         unsafe { | ||||||
|  |             let mut modules = self.modules.borrow_mut(); | ||||||
|  |             let module = modules.get_unchecked_mut(block_val.0.0.0); | ||||||
|  |             let function = module.functions.get_unchecked_mut(block_val.0.1); | ||||||
|  |             let block = function.blocks.get_unchecked_mut(block_val.1); | ||||||
|  |             let value = InstructionValue(block.value, block.instructions.len()); | ||||||
|  |             block.instructions.push(InstructionHolder { | ||||||
|  |                 value, | ||||||
|  |                 data, | ||||||
|  |                 name, | ||||||
|  |                 record: None, | ||||||
|  |             }); | ||||||
|  | 
 | ||||||
|  |             // Drop modules so that it is no longer mutable borrowed
 | ||||||
|  |             // (check_instruction requires an immutable borrow).
 | ||||||
|  |             drop(modules); | ||||||
|  | 
 | ||||||
|  |             self.check_instruction(&value)?; | ||||||
|  |             Ok(value) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub(crate) unsafe fn build_constant(&self, module: ModuleValue, kind: ConstValueKind) -> ConstantValue { | ||||||
|  |         unsafe { | ||||||
|  |             let mut modules = self.modules.borrow_mut(); | ||||||
|  |             let module = modules.get_unchecked_mut(module.0); | ||||||
|  |             let value = ConstantValue(module.value, module.constants.len()); | ||||||
|  |             module.constants.push(ConstantValueHolder { value, kind }); | ||||||
|  |             value | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub(crate) unsafe fn add_global( | ||||||
|  |         &self, | ||||||
|  |         module: ModuleValue, | ||||||
|  |         name: String, | ||||||
|  |         initializer: ConstantValue, | ||||||
|  |     ) -> GlobalValue { | ||||||
|  |         unsafe { | ||||||
|  |             let mut modules = self.modules.borrow_mut(); | ||||||
|  |             let module = modules.get_unchecked_mut(module.0); | ||||||
|  |             let value = GlobalValue(module.value, module.globals.len()); | ||||||
|  |             module.globals.push(GlobalValueHolder { | ||||||
|  |                 value, | ||||||
|  |                 name, | ||||||
|  |                 initializer, | ||||||
|  |             }); | ||||||
|  |             value | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub(crate) unsafe fn find_function(&self, module: ModuleValue, name: &String) -> Option<FunctionValue> { | ||||||
|  |         unsafe { | ||||||
|  |             let mut modules = self.modules.borrow_mut(); | ||||||
|  |             let module = modules.get_unchecked_mut(module.0); | ||||||
|  |             module.functions.iter().find(|f| f.data.name == *name).map(|f| f.value) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub(crate) unsafe fn add_instruction_location(&self, value: &InstructionValue, location: DebugLocationValue) { | ||||||
|  |         unsafe { | ||||||
|  |             let mut modules = self.modules.borrow_mut(); | ||||||
|  |             let module = modules.get_unchecked_mut(value.0.0.0.0); | ||||||
|  |             let function = module.functions.get_unchecked_mut(value.0.0.1); | ||||||
|  |             let block = function.blocks.get_unchecked_mut(value.0.1); | ||||||
|  |             let instr = block.instructions.get_unchecked_mut(value.1); | ||||||
|  |             instr.data.location = Some(location) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub(crate) unsafe fn add_instruction_metadata(&self, value: &InstructionValue, metadata: DebugMetadataValue) { | ||||||
|  |         unsafe { | ||||||
|  |             let mut modules = self.modules.borrow_mut(); | ||||||
|  |             let module = modules.get_unchecked_mut(value.0.0.0.0); | ||||||
|  |             let function = module.functions.get_unchecked_mut(value.0.0.1); | ||||||
|  |             let block = function.blocks.get_unchecked_mut(value.0.1); | ||||||
|  |             let instr = block.instructions.get_unchecked_mut(value.1); | ||||||
|  |             instr.data.meta = Some(metadata) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub(crate) unsafe fn add_instruction_record(&self, value: &InstructionValue, record: InstructionDebugRecordData) { | ||||||
|  |         unsafe { | ||||||
|  |             let mut modules = self.modules.borrow_mut(); | ||||||
|  |             let module = modules.get_unchecked_mut(value.0.0.0.0); | ||||||
|  |             let function = module.functions.get_unchecked_mut(value.0.0.1); | ||||||
|  |             let block = function.blocks.get_unchecked_mut(value.0.1); | ||||||
|  |             let instr = block.instructions.get_unchecked_mut(value.1); | ||||||
|  |             instr.record = Some(record) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub(crate) unsafe fn set_debug_subprogram(&self, value: &FunctionValue, subprogram: DebugScopeValue) { | ||||||
|  |         unsafe { | ||||||
|  |             let mut modules = self.modules.borrow_mut(); | ||||||
|  |             let module = modules.get_unchecked_mut(value.0.0); | ||||||
|  |             let function = module.functions.get_unchecked_mut(value.1); | ||||||
|  |             function.debug_info = Some(subprogram) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub(crate) unsafe fn terminate(&self, block: &BlockValue, value: TerminatorKind) -> CompileResult<()> { | ||||||
|  |         unsafe { | ||||||
|  |             let mut modules = self.modules.borrow_mut(); | ||||||
|  |             let module = modules.get_unchecked_mut(block.0.0.0); | ||||||
|  |             let function = module.functions.get_unchecked_mut(block.0.1); | ||||||
|  |             let block = function.blocks.get_unchecked_mut(block.1); | ||||||
|  |             if let Some(_) = &block.data.terminator { | ||||||
|  |                 Err(ErrorKind::BlockAlreadyTerminated) | ||||||
|  |             } else { | ||||||
|  |                 block.data.terminator = Some(value); | ||||||
|  |                 Ok(()) | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub(crate) unsafe fn set_terminator_location( | ||||||
|  |         &self, | ||||||
|  |         block: &BlockValue, | ||||||
|  |         location: DebugLocationValue, | ||||||
|  |     ) -> CompileResult<()> { | ||||||
|  |         unsafe { | ||||||
|  |             let mut modules = self.modules.borrow_mut(); | ||||||
|  |             let module = modules.get_unchecked_mut(block.0.0.0); | ||||||
|  |             let function = module.functions.get_unchecked_mut(block.0.1); | ||||||
|  |             let block = function.blocks.get_unchecked_mut(block.1); | ||||||
|  |             if let Some(_) = &block.data.terminator_location { | ||||||
|  |                 Err(ErrorKind::BlockTerminatorLocated) | ||||||
|  |             } else { | ||||||
|  |                 block.data.terminator_location = Some(location); | ||||||
|  |                 Ok(()) | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub(crate) unsafe fn delete_block(&self, block: &BlockValue) -> CompileResult<()> { | ||||||
|  |         unsafe { | ||||||
|  |             let mut modules = self.modules.borrow_mut(); | ||||||
|  |             let module = modules.get_unchecked_mut(block.0.0.0); | ||||||
|  |             let function = module.functions.get_unchecked_mut(block.0.1); | ||||||
|  |             let block = function.blocks.get_unchecked_mut(block.1); | ||||||
|  |             block.data.deleted = true; | ||||||
|  |             Ok(()) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     #[allow(dead_code)] | ||||||
|  |     pub(crate) unsafe fn module_data(&self, value: &ModuleValue) -> ModuleData { | ||||||
|  |         unsafe { self.modules.borrow().get_unchecked(value.0).data.clone() } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub(crate) unsafe fn function_data(&self, value: &FunctionValue) -> FunctionData { | ||||||
|  |         unsafe { | ||||||
|  |             self.modules | ||||||
|  |                 .borrow() | ||||||
|  |                 .get_unchecked(value.0.0) | ||||||
|  |                 .functions | ||||||
|  |                 .get_unchecked(value.1) | ||||||
|  |                 .data | ||||||
|  |                 .clone() | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     #[allow(dead_code)] | ||||||
|  |     pub(crate) unsafe fn block_data(&self, value: &BlockValue) -> BlockData { | ||||||
|  |         unsafe { | ||||||
|  |             self.modules | ||||||
|  |                 .borrow() | ||||||
|  |                 .get_unchecked(value.0.0.0) | ||||||
|  |                 .functions | ||||||
|  |                 .get_unchecked(value.0.1) | ||||||
|  |                 .blocks | ||||||
|  |                 .get_unchecked(value.1) | ||||||
|  |                 .data | ||||||
|  |                 .clone() | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub(crate) unsafe fn instr_data(&self, value: &InstructionValue) -> InstructionData { | ||||||
|  |         unsafe { | ||||||
|  |             self.modules | ||||||
|  |                 .borrow() | ||||||
|  |                 .get_unchecked(value.0.0.0.0) | ||||||
|  |                 .functions | ||||||
|  |                 .get_unchecked(value.0.0.1) | ||||||
|  |                 .blocks | ||||||
|  |                 .get_unchecked(value.0.1) | ||||||
|  |                 .instructions | ||||||
|  |                 .get_unchecked(value.1) | ||||||
|  |                 .data | ||||||
|  |                 .clone() | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub(crate) unsafe fn type_data(&self, value: &TypeValue) -> TypeData { | ||||||
|  |         unsafe { | ||||||
|  |             self.modules | ||||||
|  |                 .borrow() | ||||||
|  |                 .get_unchecked(value.0.0) | ||||||
|  |                 .types | ||||||
|  |                 .get_unchecked(value.1) | ||||||
|  |                 .data | ||||||
|  |                 .clone() | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub(crate) fn find_module<'ctx>(&'ctx self, value: ModuleValue) -> ModuleHolder { | ||||||
|  |         unsafe { self.modules.borrow().get_unchecked(value.0).clone() } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub(crate) fn get_modules(&self) -> Rc<RefCell<Vec<ModuleHolder>>> { | ||||||
|  |         self.modules.clone() | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub(crate) fn get_global_initializer(&self, value: GlobalValue) -> ConstantValue { | ||||||
|  |         unsafe { | ||||||
|  |             let modules = self.modules.borrow(); | ||||||
|  |             let module = modules.get_unchecked(value.0.0); | ||||||
|  |             let global = module.globals.get_unchecked(value.1); | ||||||
|  |             global.initializer | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub(crate) fn get_const_kind(&self, value: ConstantValue) -> ConstValueKind { | ||||||
|  |         unsafe { | ||||||
|  |             let modules = self.modules.borrow(); | ||||||
|  |             let module = modules.get_unchecked(value.0.0); | ||||||
|  |             let constant = module.constants.get_unchecked(value.1); | ||||||
|  |             constant.kind.clone() | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn check_instruction(&self, instruction: &InstructionValue) -> CompileResult<()> { | ||||||
|  |         unsafe { | ||||||
|  |             match self.instr_data(&instruction).kind { | ||||||
|  |                 Instr::Param(_) => Ok(()), | ||||||
|  |                 Instr::Constant(_) => Ok(()), | ||||||
|  |                 Instr::Add(lhs, rhs) => { | ||||||
|  |                     if match_types(&lhs, &rhs, &self)?.category().integer() { | ||||||
|  |                         Ok(()) | ||||||
|  |                     } else { | ||||||
|  |                         Err(ErrorKind::TypeNotInteger(lhs, lhs.get_type(&self)?)) | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 Instr::FAdd(lhs, rhs) => { | ||||||
|  |                     if match_types(&lhs, &rhs, &self)?.category() == TypeCategory::Real { | ||||||
|  |                         Ok(()) | ||||||
|  |                     } else { | ||||||
|  |                         Err(ErrorKind::TypeWrongCategory( | ||||||
|  |                             lhs, | ||||||
|  |                             lhs.get_type(&self)?, | ||||||
|  |                             TypeCategory::Real, | ||||||
|  |                         )) | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 Instr::Sub(lhs, rhs) => { | ||||||
|  |                     if match_types(&lhs, &rhs, &self)?.category().integer() { | ||||||
|  |                         Ok(()) | ||||||
|  |                     } else { | ||||||
|  |                         Err(ErrorKind::TypeNotInteger(lhs, lhs.get_type(&self)?)) | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 Instr::FSub(lhs, rhs) => { | ||||||
|  |                     if match_types(&lhs, &rhs, &self)?.category() == TypeCategory::Real { | ||||||
|  |                         Ok(()) | ||||||
|  |                     } else { | ||||||
|  |                         Err(ErrorKind::TypeWrongCategory( | ||||||
|  |                             lhs, | ||||||
|  |                             lhs.get_type(&self)?, | ||||||
|  |                             TypeCategory::Real, | ||||||
|  |                         )) | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 Instr::Mul(lhs, rhs) => { | ||||||
|  |                     if match_types(&lhs, &rhs, &self)?.category().integer() { | ||||||
|  |                         Ok(()) | ||||||
|  |                     } else { | ||||||
|  |                         Err(ErrorKind::TypeNotInteger(lhs, lhs.get_type(&self)?)) | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 Instr::FMul(lhs, rhs) => { | ||||||
|  |                     if match_types(&lhs, &rhs, &self)?.category() == TypeCategory::Real { | ||||||
|  |                         Ok(()) | ||||||
|  |                     } else { | ||||||
|  |                         Err(ErrorKind::TypeWrongCategory( | ||||||
|  |                             lhs, | ||||||
|  |                             lhs.get_type(&self)?, | ||||||
|  |                             TypeCategory::Real, | ||||||
|  |                         )) | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 Instr::UDiv(lhs, rhs) => { | ||||||
|  |                     if match_types(&lhs, &rhs, &self)?.category() == TypeCategory::UnsignedInteger { | ||||||
|  |                         Ok(()) | ||||||
|  |                     } else { | ||||||
|  |                         Err(ErrorKind::TypeWrongCategory( | ||||||
|  |                             lhs, | ||||||
|  |                             lhs.get_type(&self)?, | ||||||
|  |                             TypeCategory::UnsignedInteger, | ||||||
|  |                         )) | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 Instr::SDiv(lhs, rhs) => { | ||||||
|  |                     if match_types(&lhs, &rhs, &self)?.category() == TypeCategory::SignedInteger { | ||||||
|  |                         Ok(()) | ||||||
|  |                     } else { | ||||||
|  |                         Err(ErrorKind::TypeWrongCategory( | ||||||
|  |                             lhs, | ||||||
|  |                             lhs.get_type(&self)?, | ||||||
|  |                             TypeCategory::SignedInteger, | ||||||
|  |                         )) | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 Instr::FDiv(lhs, rhs) => { | ||||||
|  |                     if match_types(&lhs, &rhs, &self)?.category() == TypeCategory::Real { | ||||||
|  |                         Ok(()) | ||||||
|  |                     } else { | ||||||
|  |                         Err(ErrorKind::TypeWrongCategory( | ||||||
|  |                             lhs, | ||||||
|  |                             lhs.get_type(&self)?, | ||||||
|  |                             TypeCategory::Real, | ||||||
|  |                         )) | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 Instr::URem(lhs, rhs) => { | ||||||
|  |                     if match_types(&lhs, &rhs, &self)?.category() == TypeCategory::UnsignedInteger { | ||||||
|  |                         Ok(()) | ||||||
|  |                     } else { | ||||||
|  |                         Err(ErrorKind::TypeWrongCategory( | ||||||
|  |                             lhs, | ||||||
|  |                             lhs.get_type(&self)?, | ||||||
|  |                             TypeCategory::UnsignedInteger, | ||||||
|  |                         )) | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 Instr::SRem(lhs, rhs) => { | ||||||
|  |                     if match_types(&lhs, &rhs, &self)?.category() == TypeCategory::SignedInteger { | ||||||
|  |                         Ok(()) | ||||||
|  |                     } else { | ||||||
|  |                         Err(ErrorKind::TypeWrongCategory( | ||||||
|  |                             lhs, | ||||||
|  |                             lhs.get_type(&self)?, | ||||||
|  |                             TypeCategory::SignedInteger, | ||||||
|  |                         )) | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 Instr::FRem(lhs, rhs) => { | ||||||
|  |                     if match_types(&lhs, &rhs, &self)?.category() == TypeCategory::Real { | ||||||
|  |                         Ok(()) | ||||||
|  |                     } else { | ||||||
|  |                         Err(ErrorKind::TypeWrongCategory( | ||||||
|  |                             lhs, | ||||||
|  |                             lhs.get_type(&self)?, | ||||||
|  |                             TypeCategory::Real, | ||||||
|  |                         )) | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 Instr::And(lhs, rhs) => match_types(&lhs, &rhs, &self).map(|_| ()), | ||||||
|  |                 Instr::Or(lhs, rhs) => match_types(&lhs, &rhs, &self).map(|_| ()), | ||||||
|  |                 Instr::XOr(lhs, rhs) => match_types(&lhs, &rhs, &self).map(|_| ()), | ||||||
|  |                 Instr::ICmp(_, lhs, rhs) => { | ||||||
|  |                     let t = match_types(&lhs, &rhs, self)?; | ||||||
|  |                     if t.category().comparable() || !t.category().integer() { | ||||||
|  |                         Ok(()) | ||||||
|  |                     } else { | ||||||
|  |                         Err(ErrorKind::TypeNotComparable(lhs, t)) | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 Instr::FCmp(_, lhs, rhs) => { | ||||||
|  |                     let t = match_types(&lhs, &rhs, self)?; | ||||||
|  |                     if t.category().comparable() || t.category() != TypeCategory::Real { | ||||||
|  |                         Ok(()) | ||||||
|  |                     } else { | ||||||
|  |                         Err(ErrorKind::TypeNotComparable(lhs, t)) | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 Instr::FunctionCall(fun, params) => { | ||||||
|  |                     let param_types = self.function_data(&fun).params; | ||||||
|  |                     if param_types.len() != params.len() { | ||||||
|  |                         return Err(ErrorKind::InvalidLenParams(params.len(), param_types.len())); | ||||||
|  |                     } | ||||||
|  |                     for (a, b) in param_types.iter().zip(params) { | ||||||
|  |                         if *a != b.get_type(&self)? { | ||||||
|  |                             return Err(ErrorKind::TypesIncompatible(a.clone(), b.get_type(&self)?)); | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                     Ok(()) | ||||||
|  |                 } | ||||||
|  |                 Instr::Phi(vals) => { | ||||||
|  |                     let mut iter = vals.iter(); | ||||||
|  |                     // TODO error: Phi must contain at least one item
 | ||||||
|  | 
 | ||||||
|  |                     // TODO error: compile can actually crash here if any of the
 | ||||||
|  |                     // incoming values come from blocks that are added later
 | ||||||
|  |                     // than the one where this one exists.
 | ||||||
|  | 
 | ||||||
|  |                     let first = iter.next().ok_or(ErrorKind::EmptyPhiList)?; | ||||||
|  |                     for item in iter { | ||||||
|  |                         match_types(first, item, &self)?; | ||||||
|  |                     } | ||||||
|  |                     Ok(()) | ||||||
|  |                 } | ||||||
|  |                 Instr::Alloca(_) => Ok(()), | ||||||
|  |                 Instr::Load(ptr, load_ty) => { | ||||||
|  |                     let ptr_ty = ptr.get_type(&self)?; | ||||||
|  |                     if let Type::Ptr(ptr_ty_inner) = ptr_ty { | ||||||
|  |                         if *ptr_ty_inner == load_ty { | ||||||
|  |                             Ok(()) | ||||||
|  |                         } else { | ||||||
|  |                             Err(ErrorKind::TypesIncompatible(*ptr_ty_inner, load_ty)) | ||||||
|  |                         } | ||||||
|  |                     } else { | ||||||
|  |                         Err(ErrorKind::NotPointer(ptr, ptr_ty)) | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 Instr::Store(ptr, _) => { | ||||||
|  |                     let ty = ptr.get_type(&self)?; | ||||||
|  |                     if let Type::Ptr(_) = ty { | ||||||
|  |                         Ok(()) | ||||||
|  |                     } else { | ||||||
|  |                         Err(ErrorKind::NotPointer(ptr, ty)) | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 Instr::ArrayAlloca(_, val) => { | ||||||
|  |                     if val.get_type(self)?.category() == TypeCategory::UnsignedInteger { | ||||||
|  |                         Ok(()) | ||||||
|  |                     } else { | ||||||
|  |                         Err(ErrorKind::TypeWrongCategory( | ||||||
|  |                             val, | ||||||
|  |                             val.get_type(self)?, | ||||||
|  |                             TypeCategory::UnsignedInteger, | ||||||
|  |                         )) | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 Instr::GetElemPtr(ptr_val, _) => { | ||||||
|  |                     let ptr_ty = ptr_val.get_type(&self)?; | ||||||
|  |                     match ptr_ty { | ||||||
|  |                         Type::Ptr(_) => Ok(()), | ||||||
|  |                         _ => Err(ErrorKind::NotPointer(ptr_val, ptr_ty)), | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 Instr::GetStructElemPtr(ptr_val, idx) => { | ||||||
|  |                     let ptr_ty = ptr_val.get_type(&self)?; | ||||||
|  |                     if let Type::Ptr(ty) = ptr_ty { | ||||||
|  |                         if let Type::CustomType(val) = *ty { | ||||||
|  |                             match self.type_data(&val).kind { | ||||||
|  |                                 CustomTypeKind::NamedStruct(NamedStruct(_, fields)) => { | ||||||
|  |                                     if fields.len() <= idx as usize { | ||||||
|  |                                         return Err(ErrorKind::NoSuchField(*ty, idx)); | ||||||
|  |                                     } | ||||||
|  |                                 } | ||||||
|  |                             } | ||||||
|  |                             Ok(()) | ||||||
|  |                         } else { | ||||||
|  |                             Err(ErrorKind::NotStruct(ptr_val, *ty)) | ||||||
|  |                         } | ||||||
|  |                     } else { | ||||||
|  |                         Err(ErrorKind::NotPointer(ptr_val, ptr_ty)) | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 Instr::ExtractValue(val, _) => { | ||||||
|  |                     let val_ty = val.get_type(&self)?; | ||||||
|  |                     match val_ty { | ||||||
|  |                         Type::CustomType(custom) => match self.type_data(&custom).kind { | ||||||
|  |                             CustomTypeKind::NamedStruct(_) => Ok(()), | ||||||
|  |                         }, | ||||||
|  |                         Type::Array(_, _) => Ok(()), | ||||||
|  |                         _ => Err(ErrorKind::NotExtractable(val, val_ty)), | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 Instr::Trunc(instr, ty) => instr.cast_to(self, &ty).map(|_| ()), | ||||||
|  |                 Instr::ZExt(instr, ty) => instr.cast_to(self, &ty).map(|_| ()), | ||||||
|  |                 Instr::SExt(instr, ty) => instr.cast_to(self, &ty).map(|_| ()), | ||||||
|  |                 Instr::FPTrunc(instr, ty) => instr.cast_to(self, &ty).map(|_| ()), | ||||||
|  |                 Instr::FPExt(instr, ty) => instr.cast_to(self, &ty).map(|_| ()), | ||||||
|  |                 Instr::FPToUI(instr, ty) => instr.cast_to(self, &ty).map(|_| ()), | ||||||
|  |                 Instr::FPToSI(instr, ty) => instr.cast_to(self, &ty).map(|_| ()), | ||||||
|  |                 Instr::UIToFP(instr, ty) => instr.cast_to(self, &ty).map(|_| ()), | ||||||
|  |                 Instr::SIToFP(instr, ty) => instr.cast_to(self, &ty).map(|_| ()), | ||||||
|  |                 Instr::PtrToInt(instr, ty) => instr.cast_to(self, &ty).map(|_| ()), | ||||||
|  |                 Instr::IntToPtr(instr, ty) => instr.cast_to(self, &ty).map(|_| ()), | ||||||
|  |                 Instr::BitCast(..) => Ok(()), | ||||||
|  |                 Instr::ShiftRightLogical(lhs, rhs) => match_types(&lhs, &rhs, &self).map(|_| ()), | ||||||
|  |                 Instr::ShiftRightArithmetic(lhs, rhs) => match_types(&lhs, &rhs, &self).map(|_| ()), | ||||||
|  |                 Instr::ShiftLeft(lhs, rhs) => match_types(&lhs, &rhs, &self).map(|_| ()), | ||||||
|  |                 Instr::GetGlobal(_) => Ok(()), | ||||||
|  |                 Instr::IsNull(val) => { | ||||||
|  |                     let val_ty = val.get_type(&self)?; | ||||||
|  |                     if let Type::Ptr(_) = val_ty { | ||||||
|  |                         Ok(()) | ||||||
|  |                     } else { | ||||||
|  |                         Err(ErrorKind::NotPointer(val, val_ty)) | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn is_block_used(&self, block_v: BlockValue) -> bool { | ||||||
|  |         unsafe { | ||||||
|  |             let modules = self.modules.borrow(); | ||||||
|  |             let module = modules.get_unchecked(block_v.0.0.0); | ||||||
|  |             let function = module.functions.get_unchecked(block_v.0.1); | ||||||
|  |             let block = function.blocks.get_unchecked(block_v.1); | ||||||
|  | 
 | ||||||
|  |             if block.instructions.len() > 0 || block.data.terminator.is_some() { | ||||||
|  |                 return true; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             for other in &function.blocks { | ||||||
|  |                 if let Some(term) = &other.data.terminator { | ||||||
|  |                     match term { | ||||||
|  |                         TerminatorKind::Ret(_) => {} | ||||||
|  |                         TerminatorKind::RetVoid => {} | ||||||
|  |                         TerminatorKind::Br(other_val) => { | ||||||
|  |                             if other_val == &block_v { | ||||||
|  |                                 return true; | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  |                         TerminatorKind::CondBr(_, then_other_v, else_other_v) => { | ||||||
|  |                             if then_other_v == &block_v || else_other_v == &block_v { | ||||||
|  |                                 return true; | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             false | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl InstructionValue { | ||||||
|  |     pub fn with_location(self, block: &Block, location: DebugLocationValue) -> InstructionValue { | ||||||
|  |         unsafe { | ||||||
|  |             block.builder.add_instruction_location(&self, location); | ||||||
|  |         } | ||||||
|  |         self | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn maybe_location(self, block: &mut Block, location: Option<DebugLocationValue>) -> InstructionValue { | ||||||
|  |         unsafe { | ||||||
|  |             if let Some(location) = location { | ||||||
|  |                 block.builder.add_instruction_location(&self, location); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         self | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn add_record(&self, block: &mut Block, record: InstructionDebugRecordData) { | ||||||
|  |         unsafe { | ||||||
|  |             block.builder.add_instruction_record(self, record); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub(crate) fn get_type(&self, builder: &Builder) -> CompileResult<Type> { | ||||||
|  |         use Instr::*; | ||||||
|  |         unsafe { | ||||||
|  |             match &builder.instr_data(self).kind { | ||||||
|  |                 Param(nth) => builder | ||||||
|  |                     .function_data(&self.0.0) | ||||||
|  |                     .params | ||||||
|  |                     .get(*nth) | ||||||
|  |                     .cloned() | ||||||
|  |                     .ok_or(ErrorKind::NoSuchParam(self.0.0, *nth)), | ||||||
|  |                 Constant(c) => Ok(c.get_type()), | ||||||
|  |                 Add(lhs, rhs) => match_types(lhs, rhs, &builder), | ||||||
|  |                 FAdd(lhs, rhs) => match_types(lhs, rhs, &builder), | ||||||
|  |                 Sub(lhs, rhs) => match_types(lhs, rhs, &builder), | ||||||
|  |                 FSub(lhs, rhs) => match_types(lhs, rhs, &builder), | ||||||
|  |                 Mul(lhs, rhs) => match_types(lhs, rhs, &builder), | ||||||
|  |                 FMul(lhs, rhs) => match_types(lhs, rhs, &builder), | ||||||
|  |                 UDiv(lhs, rhs) => match_types(lhs, rhs, &builder), | ||||||
|  |                 SDiv(lhs, rhs) => match_types(lhs, rhs, &builder), | ||||||
|  |                 FDiv(lhs, rhs) => match_types(lhs, rhs, &builder), | ||||||
|  |                 URem(lhs, rhs) => match_types(lhs, rhs, &builder), | ||||||
|  |                 SRem(lhs, rhs) => match_types(lhs, rhs, &builder), | ||||||
|  |                 FRem(lhs, rhs) => match_types(lhs, rhs, &builder), | ||||||
|  |                 And(lhs, rhs) => match_types(lhs, rhs, &builder), | ||||||
|  |                 Or(lhs, rhs) => match_types(lhs, rhs, &builder), | ||||||
|  |                 XOr(lhs, rhs) => match_types(lhs, rhs, &builder), | ||||||
|  |                 ICmp(_, _, _) => Ok(Type::Bool), | ||||||
|  |                 FCmp(_, _, _) => Ok(Type::Bool), | ||||||
|  |                 FunctionCall(function_value, _) => Ok(builder.function_data(function_value).ret), | ||||||
|  |                 Phi(values) => values | ||||||
|  |                     .first() | ||||||
|  |                     .ok_or(ErrorKind::EmptyPhiList) | ||||||
|  |                     .and_then(|v| v.get_type(&builder)), | ||||||
|  |                 Alloca(ty) => Ok(Type::Ptr(Box::new(ty.clone()))), | ||||||
|  |                 Load(_, ty) => Ok(ty.clone()), | ||||||
|  |                 Store(_, value) => value.get_type(builder), | ||||||
|  |                 ArrayAlloca(ty, _) => Ok(Type::Ptr(Box::new(ty.clone()))), | ||||||
|  |                 GetElemPtr(instr, _) => { | ||||||
|  |                     let instr_ty = instr.get_type(builder)?; | ||||||
|  |                     let Type::Ptr(inner_ty) = &instr_ty else { | ||||||
|  |                         panic!("GetStructElemPtr on non-pointer! ({:?})", &instr_ty) | ||||||
|  |                     }; | ||||||
|  |                     match *inner_ty.clone() { | ||||||
|  |                         Type::Array(elem_ty, _) => Ok(Type::Ptr(Box::new(*elem_ty.clone()))), | ||||||
|  |                         _ => Ok(instr_ty), | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 GetStructElemPtr(instr, idx) => { | ||||||
|  |                     let instr_ty = instr.get_type(builder)?; | ||||||
|  |                     let Type::Ptr(inner_ty) = instr_ty else { | ||||||
|  |                         panic!("GetStructElemPtr on non-pointer! ({:?})", &instr_ty) | ||||||
|  |                     }; | ||||||
|  |                     let Type::CustomType(ty_value) = *inner_ty else { | ||||||
|  |                         panic!("GetStructElemPtr on non-struct! ({:?})", &inner_ty) | ||||||
|  |                     }; | ||||||
|  |                     let field_ty = match builder.type_data(&ty_value).kind { | ||||||
|  |                         CustomTypeKind::NamedStruct(NamedStruct(_, fields)) => { | ||||||
|  |                             fields.get_unchecked(*idx as usize).clone() | ||||||
|  |                         } | ||||||
|  |                     }; | ||||||
|  |                     Ok(Type::Ptr(Box::new(field_ty))) | ||||||
|  |                 } | ||||||
|  |                 ExtractValue(instr, idx) => { | ||||||
|  |                     let instr_ty = instr.get_type(builder)?; | ||||||
|  |                     Ok(match instr_ty { | ||||||
|  |                         Type::CustomType(struct_ty) => { | ||||||
|  |                             let data = builder.type_data(&struct_ty); | ||||||
|  |                             match data.kind { | ||||||
|  |                                 CustomTypeKind::NamedStruct(named_struct) => { | ||||||
|  |                                     named_struct.1.get(*idx as usize).unwrap().clone() | ||||||
|  |                                 } | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  |                         Type::Array(elem_ty, _) => *elem_ty.clone(), | ||||||
|  |                         _ => return Err(ErrorKind::NotExtractable(*instr, instr_ty)), | ||||||
|  |                     }) | ||||||
|  |                 } | ||||||
|  |                 Trunc(instr, ty) => instr.cast_to(builder, ty).map(|_| ty.clone()), | ||||||
|  |                 ZExt(instr, ty) => instr.cast_to(builder, ty).map(|_| ty.clone()), | ||||||
|  |                 SExt(instr, ty) => instr.cast_to(builder, ty).map(|_| ty.clone()), | ||||||
|  |                 FPTrunc(instr, ty) => instr.cast_to(builder, ty).map(|_| ty.clone()), | ||||||
|  |                 FPExt(instr, ty) => instr.cast_to(builder, ty).map(|_| ty.clone()), | ||||||
|  |                 FPToUI(instr, ty) => instr.cast_to(builder, ty).map(|_| ty.clone()), | ||||||
|  |                 FPToSI(instr, ty) => instr.cast_to(builder, ty).map(|_| ty.clone()), | ||||||
|  |                 UIToFP(instr, ty) => instr.cast_to(builder, ty).map(|_| ty.clone()), | ||||||
|  |                 SIToFP(instr, ty) => instr.cast_to(builder, ty).map(|_| ty.clone()), | ||||||
|  |                 PtrToInt(instr, ty) => instr.cast_to(builder, ty).map(|_| ty.clone()), | ||||||
|  |                 IntToPtr(instr, ty) => instr.cast_to(builder, ty).map(|_| ty.clone()), | ||||||
|  |                 BitCast(_, ty) => Ok(ty.clone()), | ||||||
|  |                 ShiftRightLogical(lhs, _) => lhs.get_type(builder), | ||||||
|  |                 ShiftRightArithmetic(lhs, _) => lhs.get_type(builder), | ||||||
|  |                 ShiftLeft(lhs, _) => lhs.get_type(builder), | ||||||
|  |                 GetGlobal(global_value) => { | ||||||
|  |                     let constant = builder.get_global_initializer(*global_value); | ||||||
|  |                     let kind = builder.get_const_kind(constant); | ||||||
|  |                     Ok(kind.get_type()) | ||||||
|  |                 } | ||||||
|  |                 IsNull(_) => Ok(Type::Bool), | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn cast_to(&self, builder: &Builder, ty: &Type) -> CompileResult<Instr> { | ||||||
|  |         let own_type = self.get_type(builder)?; | ||||||
|  |         own_type | ||||||
|  |             .cast_instruction(*self, &ty) | ||||||
|  |             .ok_or(ErrorKind::ImpossibleCast(own_type, ty.clone())) | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										1257
									
								
								src/compile.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1257
									
								
								src/compile.rs
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										399
									
								
								src/debug_information.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										399
									
								
								src/debug_information.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,399 @@ | |||||||
|  | use std::{ | ||||||
|  |     cell::{Ref, RefCell, RefMut}, | ||||||
|  |     rc::Rc, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | use crate::builder::InstructionValue; | ||||||
|  | 
 | ||||||
|  | /// Represents 1. the compilation context, 2. subprogram or 3. a lexical scope
 | ||||||
|  | #[derive(Clone, Hash, PartialEq, Eq)] | ||||||
|  | pub struct DebugScopeValue(pub Vec<usize>); | ||||||
|  | 
 | ||||||
|  | #[derive(Clone, Hash, PartialEq, Eq)] | ||||||
|  | pub struct DebugLocationValue(pub DebugScopeValue, pub usize); | ||||||
|  | 
 | ||||||
|  | #[derive(Clone, Copy, Hash, PartialEq, Eq)] | ||||||
|  | pub struct DebugTypeValue(pub usize); | ||||||
|  | 
 | ||||||
|  | #[derive(Clone, Copy, Hash, PartialEq, Eq)] | ||||||
|  | pub struct DebugMetadataValue(pub usize); | ||||||
|  | 
 | ||||||
|  | #[derive(Debug, Clone)] | ||||||
|  | pub struct DebugFileData { | ||||||
|  |     pub name: String, | ||||||
|  |     pub directory: String, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Debug, Clone)] | ||||||
|  | pub(crate) struct DebugScopeHolder { | ||||||
|  |     pub(crate) value: DebugScopeValue, | ||||||
|  |     pub(crate) data: DebugScopeData, | ||||||
|  |     pub(crate) inner_scopes: Vec<DebugScopeHolder>, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Debug, Clone)] | ||||||
|  | pub struct DebugMetadataHolder { | ||||||
|  |     pub(crate) location: DebugLocation, | ||||||
|  |     pub(crate) value: DebugMetadataValue, | ||||||
|  |     pub(crate) data: DebugMetadata, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Clone)] | ||||||
|  | pub struct DebugTypeHolder { | ||||||
|  |     pub(crate) value: DebugTypeValue, | ||||||
|  |     pub(crate) data: DebugTypeData, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Debug, Clone)] | ||||||
|  | pub(crate) struct DebugLocationHolder { | ||||||
|  |     pub(crate) scope: DebugScopeValue, | ||||||
|  |     pub(crate) value: DebugLocationValue, | ||||||
|  |     pub(crate) location: DebugLocation, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Debug, Clone)] | ||||||
|  | pub struct DebugInformation { | ||||||
|  |     pub file: DebugFileData, | ||||||
|  |     scope: Rc<RefCell<DebugScopeHolder>>, | ||||||
|  |     locations: Rc<RefCell<Vec<DebugLocationHolder>>>, | ||||||
|  |     metadata: Rc<RefCell<Vec<DebugMetadataHolder>>>, | ||||||
|  |     types: Rc<RefCell<Vec<DebugTypeHolder>>>, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl DebugInformation { | ||||||
|  |     pub fn from_file(file: DebugFileData) -> (DebugInformation, DebugScopeValue) { | ||||||
|  |         let scope_value = DebugScopeValue(Vec::new()); | ||||||
|  |         ( | ||||||
|  |             DebugInformation { | ||||||
|  |                 file, | ||||||
|  |                 scope: Rc::new(RefCell::new(DebugScopeHolder { | ||||||
|  |                     value: scope_value.clone(), | ||||||
|  |                     inner_scopes: Vec::new(), | ||||||
|  |                     data: DebugScopeData { | ||||||
|  |                         parent: None, | ||||||
|  |                         kind: DebugScopeKind::CodegenContext, | ||||||
|  |                     }, | ||||||
|  |                 })), | ||||||
|  |                 locations: Rc::new(RefCell::new(Vec::new())), | ||||||
|  |                 metadata: Rc::new(RefCell::new(Vec::new())), | ||||||
|  |                 types: Rc::new(RefCell::new(Vec::new())), | ||||||
|  |             }, | ||||||
|  |             scope_value, | ||||||
|  |         ) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn location(&self, scope_value: &DebugScopeValue, location: DebugLocation) -> DebugLocationValue { | ||||||
|  |         let value = DebugLocationValue(scope_value.clone(), self.locations.borrow().len()); | ||||||
|  |         let location = DebugLocationHolder { | ||||||
|  |             scope: scope_value.clone(), | ||||||
|  |             value: value.clone(), | ||||||
|  |             location, | ||||||
|  |         }; | ||||||
|  |         self.locations.borrow_mut().push(location); | ||||||
|  |         value | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn debug_type(&self, kind: DebugTypeData) -> DebugTypeValue { | ||||||
|  |         let mut types = self.types.borrow_mut(); | ||||||
|  |         let value = DebugTypeValue(types.len()); | ||||||
|  |         types.push(DebugTypeHolder { | ||||||
|  |             value: value.clone(), | ||||||
|  |             data: kind, | ||||||
|  |         }); | ||||||
|  |         value | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn metadata(&self, location: &DebugLocation, kind: DebugMetadata) -> DebugMetadataValue { | ||||||
|  |         let mut metadata = self.metadata.borrow_mut(); | ||||||
|  |         let value = DebugMetadataValue(metadata.len()); | ||||||
|  |         metadata.push(DebugMetadataHolder { | ||||||
|  |             location: location.clone(), | ||||||
|  |             value: value.clone(), | ||||||
|  |             data: kind, | ||||||
|  |         }); | ||||||
|  |         value | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn subprogram(&self, parent: DebugScopeValue, kind: DebugSubprogramData) -> DebugScopeValue { | ||||||
|  |         unsafe { | ||||||
|  |             let mut outer_scope = RefMut::map(self.scope.borrow_mut(), |mut v| { | ||||||
|  |                 for i in &parent.0 { | ||||||
|  |                     v = v.inner_scopes.get_unchecked_mut(*i); | ||||||
|  |                 } | ||||||
|  |                 v | ||||||
|  |             }); | ||||||
|  | 
 | ||||||
|  |             let mut arr = parent.0.clone(); | ||||||
|  |             arr.push(outer_scope.inner_scopes.len()); | ||||||
|  |             let value = DebugScopeValue(arr); | ||||||
|  | 
 | ||||||
|  |             outer_scope.inner_scopes.push(DebugScopeHolder { | ||||||
|  |                 value: value.clone(), | ||||||
|  |                 inner_scopes: Vec::new(), | ||||||
|  |                 data: DebugScopeData { | ||||||
|  |                     parent: Some(parent.clone()), | ||||||
|  |                     kind: DebugScopeKind::Subprogram(kind), | ||||||
|  |                 }, | ||||||
|  |             }); | ||||||
|  |             value | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     pub fn lexical_scope(&self, parent: &DebugScopeValue, data: DebugLexicalScope) -> DebugScopeValue { | ||||||
|  |         unsafe { | ||||||
|  |             let mut outer_scope = RefMut::map(self.scope.borrow_mut(), |mut v| { | ||||||
|  |                 for i in &parent.0 { | ||||||
|  |                     v = v.inner_scopes.get_unchecked_mut(*i); | ||||||
|  |                 } | ||||||
|  |                 v | ||||||
|  |             }); | ||||||
|  | 
 | ||||||
|  |             let mut arr = parent.0.clone(); | ||||||
|  |             arr.push(outer_scope.inner_scopes.len()); | ||||||
|  |             let value = DebugScopeValue(arr); | ||||||
|  | 
 | ||||||
|  |             outer_scope.inner_scopes.push(DebugScopeHolder { | ||||||
|  |                 value: value.clone(), | ||||||
|  |                 inner_scopes: Vec::new(), | ||||||
|  |                 data: DebugScopeData { | ||||||
|  |                     parent: Some(parent.clone()), | ||||||
|  |                     kind: DebugScopeKind::LexicalScope(data), | ||||||
|  |                 }, | ||||||
|  |             }); | ||||||
|  |             value | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn get_metadata(&self, value: DebugMetadataValue) -> DebugMetadata { | ||||||
|  |         unsafe { self.metadata.borrow().get_unchecked(value.0).data.clone() } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn get_metadata_location(&self, value: DebugMetadataValue) -> DebugLocation { | ||||||
|  |         unsafe { self.metadata.borrow().get_unchecked(value.0).location.clone() } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn get_scope_data(&self, value: &DebugScopeValue) -> Option<DebugScopeData> { | ||||||
|  |         let scope = Ref::filter_map(self.scope.borrow(), |v: &DebugScopeHolder| { | ||||||
|  |             let mut opt = Some(v); | ||||||
|  |             for i in &value.0 { | ||||||
|  |                 if let Some(inner) = opt { | ||||||
|  |                     opt = inner.inner_scopes.get(*i); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             opt | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         if let Ok(scope) = scope { | ||||||
|  |             Some(scope.data.clone()) | ||||||
|  |         } else { | ||||||
|  |             None | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn get_type_data(&self, value: DebugTypeValue) -> DebugTypeData { | ||||||
|  |         unsafe { self.types.borrow().get_unchecked(value.0).data.clone() } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn get_location(&self, value: &DebugLocationValue) -> DebugLocation { | ||||||
|  |         unsafe { self.locations.borrow().get_unchecked(value.1).location.clone() } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn get_metadatas(&self) -> Rc<RefCell<Vec<DebugMetadataHolder>>> { | ||||||
|  |         self.metadata.clone() | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub(crate) fn get_scope(&self) -> Rc<RefCell<DebugScopeHolder>> { | ||||||
|  |         self.scope.clone() | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn get_types(&self) -> Rc<RefCell<Vec<DebugTypeHolder>>> { | ||||||
|  |         self.types.clone() | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub(crate) fn get_locations(&self) -> Rc<RefCell<Vec<DebugLocationHolder>>> { | ||||||
|  |         self.locations.clone() | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Clone)] | ||||||
|  | pub struct DebugLocation { | ||||||
|  |     pub scope: DebugScopeValue, | ||||||
|  |     pub pos: DebugPosition, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Clone, Copy)] | ||||||
|  | pub struct DebugPosition { | ||||||
|  |     pub line: u32, | ||||||
|  |     pub column: u32, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Debug, Clone)] | ||||||
|  | pub enum DebugMetadata { | ||||||
|  |     ParamVar(DebugParamVariable), | ||||||
|  |     LocalVar(DebugLocalVariable), | ||||||
|  |     VarAssignment, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Debug, Clone)] | ||||||
|  | pub struct DebugParamVariable { | ||||||
|  |     pub name: String, | ||||||
|  |     /// the index (starting from 1) of this variable in the subprogram
 | ||||||
|  |     /// parameters. arg_idx should not conflict with other parameters of the
 | ||||||
|  |     /// same subprogram.
 | ||||||
|  |     pub arg_idx: u32, | ||||||
|  |     pub ty: DebugTypeValue, | ||||||
|  |     /// If this variable will be referenced from its containing subprogram, and
 | ||||||
|  |     /// will survive some optimizations.
 | ||||||
|  |     pub always_preserve: bool, | ||||||
|  |     pub flags: DwarfFlags, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Debug, Clone)] | ||||||
|  | pub struct DebugLocalVariable { | ||||||
|  |     pub name: String, | ||||||
|  |     pub ty: DebugTypeValue, | ||||||
|  |     pub always_preserve: bool, | ||||||
|  |     pub flags: DwarfFlags, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Default for DebugSubprogramOptionals { | ||||||
|  |     fn default() -> Self { | ||||||
|  |         Self { | ||||||
|  |             scope_line: 0, | ||||||
|  |             is_local: false, | ||||||
|  |             is_definition: true, | ||||||
|  |             is_optimized: false, | ||||||
|  |             flags: DwarfFlags, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Debug, Clone)] | ||||||
|  | pub struct DwarfFlags; | ||||||
|  | 
 | ||||||
|  | #[derive(Clone)] | ||||||
|  | pub enum DebugTypeData { | ||||||
|  |     Basic(DebugBasicType), | ||||||
|  |     Subprogram(DebugSubprogramType), | ||||||
|  |     Pointer(DebugPointerType), | ||||||
|  |     Array(DebugArrayType), | ||||||
|  |     Struct(DebugStructType), | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Clone)] | ||||||
|  | pub struct DebugBasicType { | ||||||
|  |     pub name: String, | ||||||
|  |     /// Size of the type.
 | ||||||
|  |     pub size_bits: u64, | ||||||
|  |     /// DWARF encoding code, e.g., dwarf::DW_ATE_float.
 | ||||||
|  |     pub encoding: DwarfEncoding, | ||||||
|  |     /// Optional DWARF attributes, e.g., DW_AT_endianity.
 | ||||||
|  |     pub flags: DwarfFlags, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Clone)] | ||||||
|  | pub struct DebugArrayType { | ||||||
|  |     pub size_bits: u64, | ||||||
|  |     pub align_bits: u32, | ||||||
|  |     pub element_type: DebugTypeValue, | ||||||
|  |     pub length: u64, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Clone)] | ||||||
|  | pub struct DebugPointerType { | ||||||
|  |     pub name: String, | ||||||
|  |     pub pointee: DebugTypeValue, | ||||||
|  |     pub size_bits: u64, | ||||||
|  | } | ||||||
|  | #[derive(Clone)] | ||||||
|  | pub struct DebugStructType { | ||||||
|  |     pub name: String, | ||||||
|  |     pub scope: DebugScopeValue, | ||||||
|  |     pub pos: Option<DebugPosition>, | ||||||
|  |     pub size_bits: u64, | ||||||
|  |     pub flags: DwarfFlags, | ||||||
|  |     pub fields: Vec<DebugFieldType>, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Clone)] | ||||||
|  | pub struct DebugFieldType { | ||||||
|  |     pub name: String, | ||||||
|  |     pub scope: DebugScopeValue, | ||||||
|  |     pub pos: Option<DebugPosition>, | ||||||
|  |     pub size_bits: u64, | ||||||
|  |     pub offset: u64, | ||||||
|  |     pub flags: DwarfFlags, | ||||||
|  |     pub ty: DebugTypeValue, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Clone)] | ||||||
|  | pub struct DebugSubprogramType { | ||||||
|  |     pub parameters: Vec<DebugTypeValue>, | ||||||
|  |     pub flags: DwarfFlags, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Debug, Clone, Copy)] | ||||||
|  | pub enum DwarfEncoding { | ||||||
|  |     Address = 1, | ||||||
|  |     Boolean = 2, | ||||||
|  |     Float = 4, | ||||||
|  |     Signed = 5, | ||||||
|  |     SignedChar = 6, | ||||||
|  |     Unsigned = 7, | ||||||
|  |     UnsignedChar = 8, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Debug, Clone)] | ||||||
|  | pub struct DebugScopeData { | ||||||
|  |     pub parent: Option<DebugScopeValue>, | ||||||
|  |     pub kind: DebugScopeKind, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Debug, Clone)] | ||||||
|  | pub enum DebugScopeKind { | ||||||
|  |     CodegenContext, | ||||||
|  |     LexicalScope(DebugLexicalScope), | ||||||
|  |     Subprogram(DebugSubprogramData), | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Debug, Clone)] | ||||||
|  | pub struct DebugLexicalScope { | ||||||
|  |     pub location: DebugLocation, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Debug, Clone)] | ||||||
|  | pub struct DebugSubprogramData { | ||||||
|  |     /// Function name.
 | ||||||
|  |     pub name: String, | ||||||
|  |     pub outer_scope: DebugScopeValue, | ||||||
|  |     /// Used for line number.
 | ||||||
|  |     pub location: DebugLocation, | ||||||
|  |     /// Function type.
 | ||||||
|  |     pub ty: DebugTypeValue, | ||||||
|  |     pub opts: DebugSubprogramOptionals, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Debug, Clone)] | ||||||
|  | pub struct DebugSubprogramOptionals { | ||||||
|  |     /// Set to the beginning of the scope this starts
 | ||||||
|  |     pub scope_line: u32, | ||||||
|  |     pub is_local: bool, | ||||||
|  |     pub is_definition: bool, | ||||||
|  |     pub is_optimized: bool, | ||||||
|  |     /// These flags are used to emit dwarf attributes. e.g. is this function
 | ||||||
|  |     /// prototyped or not.
 | ||||||
|  |     pub flags: DwarfFlags, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Clone)] | ||||||
|  | pub struct InstructionDebugRecordData { | ||||||
|  |     pub scope: DebugScopeValue, | ||||||
|  |     pub variable: DebugMetadataValue, | ||||||
|  |     pub location: DebugLocation, | ||||||
|  |     pub kind: DebugRecordKind, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Clone, Copy)] | ||||||
|  | pub enum DebugRecordKind { | ||||||
|  |     Declare(InstructionValue), | ||||||
|  |     Value(InstructionValue), | ||||||
|  | } | ||||||
							
								
								
									
										595
									
								
								src/fmt.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										595
									
								
								src/fmt.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,595 @@ | |||||||
|  | //! Debug implementations for relevant types
 | ||||||
|  | 
 | ||||||
|  | use std::{ | ||||||
|  |     fmt::{Debug, Display, Write}, | ||||||
|  |     marker::PhantomData, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | use crate::{ | ||||||
|  |     CmpPredicate, Context, Instr, InstructionData, TerminatorKind, | ||||||
|  |     builder::*, | ||||||
|  |     debug_information::{ | ||||||
|  |         DebugArrayType, DebugBasicType, DebugFieldType, DebugInformation, DebugLocalVariable, DebugLocation, | ||||||
|  |         DebugLocationValue, DebugMetadata, DebugMetadataValue, DebugParamVariable, DebugPointerType, DebugPosition, | ||||||
|  |         DebugRecordKind, DebugScopeValue, DebugStructType, DebugSubprogramType, DebugTypeData, DebugTypeHolder, | ||||||
|  |         DebugTypeValue, | ||||||
|  |     }, | ||||||
|  |     pad_adapter::PadAdapter, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | impl Display for Context { | ||||||
|  |     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||||||
|  |         Display::fmt(&self.builder, f) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Display for Builder { | ||||||
|  |     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||||||
|  |         writeln!(f, "Producer: {}", self.producer)?; | ||||||
|  |         for module in self.modules.borrow().iter() { | ||||||
|  |             if module.data.is_main { | ||||||
|  |                 write!(f, "main ")?; | ||||||
|  |             } | ||||||
|  |             writeln!(f, "{} ({:?}) {{", module.data.name, module.value)?; | ||||||
|  |             for function in &module.functions { | ||||||
|  |                 let mut state = Default::default(); | ||||||
|  |                 let mut inner = PadAdapter::wrap(f, &mut state); | ||||||
|  |                 function.builder_fmt(&mut inner, self, &module.debug_information)?; | ||||||
|  |             } | ||||||
|  |             writeln!(f, "}}")?; | ||||||
|  |         } | ||||||
|  |         Ok(()) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl FunctionHolder { | ||||||
|  |     fn builder_fmt( | ||||||
|  |         &self, | ||||||
|  |         f: &mut impl std::fmt::Write, | ||||||
|  |         builder: &Builder, | ||||||
|  |         debug: &Option<DebugInformation>, | ||||||
|  |     ) -> std::fmt::Result { | ||||||
|  |         if self.data.flags.is_imported { | ||||||
|  |             write!(f, "imported ")?; | ||||||
|  |         } | ||||||
|  |         if self.data.flags.is_extern { | ||||||
|  |             write!(f, "extern ")?; | ||||||
|  |         } | ||||||
|  |         if self.data.flags.is_pub { | ||||||
|  |             write!(f, "pub ")?; | ||||||
|  |         } | ||||||
|  |         if self.data.flags.is_main { | ||||||
|  |             write!(f, "main ")?; | ||||||
|  |         } | ||||||
|  |         let params = self | ||||||
|  |             .data | ||||||
|  |             .params | ||||||
|  |             .iter() | ||||||
|  |             .map(|p| format!("{:?}", p)) | ||||||
|  |             .collect::<Vec<_>>() | ||||||
|  |             .join(", "); | ||||||
|  |         write!(f, "fn {}({}) -> {:?} ", self.data.name, params, self.data.ret)?; | ||||||
|  | 
 | ||||||
|  |         writeln!(f, "{{")?; | ||||||
|  |         let mut state = Default::default(); | ||||||
|  |         let mut inner = PadAdapter::wrap(f, &mut state); | ||||||
|  |         writeln!(inner, "(Value = {:?}) ", self.value)?; | ||||||
|  |         if let Some(debug) = &self.debug_info { | ||||||
|  |             writeln!(inner, "(Debug = {:?})", debug)?; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         for block in &self.blocks { | ||||||
|  |             let mut state = Default::default(); | ||||||
|  |             let mut inner = PadAdapter::wrap(&mut inner, &mut state); | ||||||
|  |             block.builder_fmt(&mut inner, builder, debug)?; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         writeln!(f, "}}")?; | ||||||
|  |         Ok(()) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl BlockHolder { | ||||||
|  |     fn builder_fmt( | ||||||
|  |         &self, | ||||||
|  |         f: &mut impl std::fmt::Write, | ||||||
|  |         builder: &Builder, | ||||||
|  |         debug: &Option<DebugInformation>, | ||||||
|  |     ) -> std::fmt::Result { | ||||||
|  |         if self.data.deleted { | ||||||
|  |             write!(f, "deleted ")?; | ||||||
|  |         } | ||||||
|  |         writeln!(f, "{} ({:?}):", self.data.name, self.value)?; | ||||||
|  | 
 | ||||||
|  |         let mut state = Default::default(); | ||||||
|  |         let mut inner = PadAdapter::wrap(f, &mut state); | ||||||
|  | 
 | ||||||
|  |         for instr in &self.instructions { | ||||||
|  |             instr.builder_fmt(&mut inner, builder, debug)?; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if let Some(terminator) = &self.data.terminator { | ||||||
|  |             terminator.builder_fmt(&mut inner, builder, debug)?; | ||||||
|  |         } | ||||||
|  |         if let Some(location) = &self.data.terminator_location { | ||||||
|  |             writeln!(inner, "  ^  (At {}) ", debug.as_ref().unwrap().get_location(location))?; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         Ok(()) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl InstructionHolder { | ||||||
|  |     fn builder_fmt( | ||||||
|  |         &self, | ||||||
|  |         f: &mut impl std::fmt::Write, | ||||||
|  |         _builder: &Builder, | ||||||
|  |         debug: &Option<DebugInformation>, | ||||||
|  |     ) -> std::fmt::Result { | ||||||
|  |         if let Some(record) = &self.record { | ||||||
|  |             let kind = match record.kind { | ||||||
|  |                 DebugRecordKind::Declare(instruction_value) => { | ||||||
|  |                     format!("= {:?} (Assign)", instruction_value) | ||||||
|  |                 } | ||||||
|  |                 DebugRecordKind::Value(instruction_value) => { | ||||||
|  |                     format!("= {:?} (Value)", instruction_value) | ||||||
|  |                 } | ||||||
|  |             }; | ||||||
|  | 
 | ||||||
|  |             if let Some(debug) = debug { | ||||||
|  |                 writeln!(f, "  (Debug {} {})", record.variable.hr(debug), kind)?; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         writeln!(f, "{:?} ({}) = {:?} ", self.value, self.name, self.data.kind)?; | ||||||
|  |         if let Some(debug) = debug { | ||||||
|  |             if let Some(location) = &self.data.location { | ||||||
|  |                 writeln!(f, "  ^  (At {}) ", debug.get_location(location))?; | ||||||
|  |             } | ||||||
|  |             if let Some(meta) = self.data.meta { | ||||||
|  |                 writeln!(f, "  ^  (Meta {}) ", meta.hr(debug))?; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         writeln!(f)?; | ||||||
|  | 
 | ||||||
|  |         Ok(()) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl TerminatorKind { | ||||||
|  |     fn builder_fmt( | ||||||
|  |         &self, | ||||||
|  |         f: &mut impl std::fmt::Write, | ||||||
|  |         _builder: &Builder, | ||||||
|  |         _debug: &Option<DebugInformation>, | ||||||
|  |     ) -> std::fmt::Result { | ||||||
|  |         match self { | ||||||
|  |             TerminatorKind::Ret(instr) => writeln!(f, "ret {:?}", instr), | ||||||
|  |             TerminatorKind::RetVoid => writeln!(f, "ret void"), | ||||||
|  |             TerminatorKind::Br(block) => writeln!(f, "br {:?}", block), | ||||||
|  |             TerminatorKind::CondBr(instr, lhs, rhs) => { | ||||||
|  |                 writeln!(f, "condbr {:?}, {:?} or {:?}", instr, lhs, rhs) | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl DebugMetadataValue { | ||||||
|  |     fn hr(&self, debug: &DebugInformation) -> String { | ||||||
|  |         let kind = match debug.get_metadata(*self) { | ||||||
|  |             DebugMetadata::ParamVar(DebugParamVariable { name, arg_idx, ty, .. }) => { | ||||||
|  |                 format!("param {} (idx {}) (type {:?}) ", name, arg_idx, ty) | ||||||
|  |             } | ||||||
|  |             DebugMetadata::LocalVar(DebugLocalVariable { name, ty, .. }) => { | ||||||
|  |                 format!("var {} (type {:?}) ", name, ty) | ||||||
|  |             } | ||||||
|  |             DebugMetadata::VarAssignment => todo!(), | ||||||
|  |         }; | ||||||
|  |         format!("{} at {}", kind, debug.get_metadata_location(*self)) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Display for DebugLocation { | ||||||
|  |     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||||||
|  |         write!(f, "{:?} on scope {:?}", self.pos, self.scope) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Display for DebugPosition { | ||||||
|  |     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||||||
|  |         write!(f, "line {}, col {}", self.line, self.column) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Debug for Builder { | ||||||
|  |     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||||||
|  |         f.debug_list().entries(self.get_modules().borrow().iter()); | ||||||
|  |         Ok(()) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub struct PrintableModule<'ctx> { | ||||||
|  |     pub phantom: PhantomData<&'ctx ()>, | ||||||
|  |     pub module: ModuleHolder, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<'ctx> Debug for PrintableModule<'ctx> { | ||||||
|  |     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||||||
|  |         Debug::fmt(&self.module, f) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Debug for ModuleHolder { | ||||||
|  |     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||||||
|  |         f.debug_tuple(&format!("{}({:#?}) ", self.data.name, self.value)) | ||||||
|  |             .field(&self.functions) | ||||||
|  |             // .field(&self.debug_information)
 | ||||||
|  |             .finish() | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Debug for FunctionHolder { | ||||||
|  |     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||||||
|  |         f.debug_tuple(&format!( | ||||||
|  |             "{}({:?}) -> {:?} ", | ||||||
|  |             self.data.name, self.data.params, self.data.ret | ||||||
|  |         )) | ||||||
|  |         .field(&self.blocks) | ||||||
|  |         .finish() | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Debug for BlockHolder { | ||||||
|  |     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||||||
|  |         let deleted = if self.data.deleted { " (deleted)" } else { "" }; | ||||||
|  |         f.debug_tuple(&format!("{}[{:?}]{} ", &self.data.name, &self.value, deleted)) | ||||||
|  |             .field(&self.instructions) | ||||||
|  |             .field(&self.data.terminator) | ||||||
|  |             .field(&self.data.terminator_location) | ||||||
|  |             .finish() | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Debug for InstructionHolder { | ||||||
|  |     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||||||
|  |         self.value.fmt(f)?; | ||||||
|  |         write!(f, " ({})", self.name)?; | ||||||
|  |         f.write_str(" = ")?; | ||||||
|  |         self.data.fmt(f) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Debug for InstructionData { | ||||||
|  |     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||||||
|  |         self.kind.fmt(f)?; | ||||||
|  |         if let Some(location) = &self.location { | ||||||
|  |             write!(f, " ({:?})", location)?; | ||||||
|  |         } | ||||||
|  |         Ok(()) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Debug for ModuleValue { | ||||||
|  |     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||||||
|  |         write!(f, "M[{:0>2}]", self.0) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Debug for FunctionValue { | ||||||
|  |     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||||||
|  |         write!(f, "F[{:0>2}-{:0>2}]", &self.0.0, self.1) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Debug for BlockValue { | ||||||
|  |     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||||||
|  |         write!(f, "B[{:0>2}-{:0>2}-{:0>2}]", &self.0.0.0, &self.0.1, self.1) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Debug for InstructionValue { | ||||||
|  |     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||||||
|  |         write!(f, "%{}.{}.{}.{}", self.0.0.0.0, self.0.0.1, self.0.1, self.1) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // impl Debug for InstructionValue {
 | ||||||
|  | //     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 | ||||||
|  | //         write!(
 | ||||||
|  | //             f,
 | ||||||
|  | //             "I[{:0>2}-{:0>2}-{:0>2}-{:0>2}]",
 | ||||||
|  | //             &self.0.0.0.0, &self.0.0.1, &self.0.1, self.1
 | ||||||
|  | //         )
 | ||||||
|  | //     }
 | ||||||
|  | // }
 | ||||||
|  | 
 | ||||||
|  | impl Debug for TypeValue { | ||||||
|  |     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||||||
|  |         write!(f, "Ty[{:0>2}-{:0>2}]", &self.0.0, self.1) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Debug for Instr { | ||||||
|  |     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||||||
|  |         match self { | ||||||
|  |             Instr::Param(nth) => fmt_call(f, &"Param", &nth), | ||||||
|  |             Instr::Constant(c) => c.fmt(f), | ||||||
|  |             Instr::Add(lhs, rhs) => fmt_binop(f, lhs, &"+", rhs), | ||||||
|  |             Instr::FAdd(lhs, rhs) => fmt_binop(f, lhs, &"+", rhs), | ||||||
|  |             Instr::Sub(lhs, rhs) => fmt_binop(f, lhs, &"-", rhs), | ||||||
|  |             Instr::FSub(lhs, rhs) => fmt_binop(f, lhs, &"-", rhs), | ||||||
|  |             Instr::Mul(lhs, rhs) => fmt_binop(f, lhs, &"*", rhs), | ||||||
|  |             Instr::FMul(lhs, rhs) => fmt_binop(f, lhs, &"*", rhs), | ||||||
|  |             Instr::UDiv(lhs, rhs) => fmt_binop(f, lhs, &"/", rhs), | ||||||
|  |             Instr::SDiv(lhs, rhs) => fmt_binop(f, lhs, &"/", rhs), | ||||||
|  |             Instr::FDiv(lhs, rhs) => fmt_binop(f, lhs, &"/", rhs), | ||||||
|  |             Instr::URem(lhs, rhs) => fmt_binop(f, lhs, &"%", rhs), | ||||||
|  |             Instr::SRem(lhs, rhs) => fmt_binop(f, lhs, &"%", rhs), | ||||||
|  |             Instr::FRem(lhs, rhs) => fmt_binop(f, lhs, &"%", rhs), | ||||||
|  |             Instr::And(lhs, rhs) => fmt_binop(f, lhs, &"&&", rhs), | ||||||
|  |             Instr::Phi(val) => fmt_call(f, &"Phi", &val), | ||||||
|  |             Instr::ICmp(cmp, lhs, rhs) => fmt_binop(f, lhs, cmp, rhs), | ||||||
|  |             Instr::FCmp(cmp, lhs, rhs) => fmt_binop(f, lhs, cmp, rhs), | ||||||
|  |             Instr::FunctionCall(fun, params) => fmt_call(f, fun, params), | ||||||
|  |             Instr::Alloca(ty) => write!(f, "alloca<{:?}>", ty), | ||||||
|  |             Instr::Load(val, ty) => write!(f, "load<{:?}>({:?})", ty, val), | ||||||
|  |             Instr::Store(ptr, val) => write!(f, "store({:?} = {:?})", ptr, val), | ||||||
|  |             Instr::ArrayAlloca(ty, instruction_value) => { | ||||||
|  |                 write!(f, "array_alloca<{:?}>({:?})", ty, instruction_value) | ||||||
|  |             } | ||||||
|  |             Instr::GetElemPtr(instruction_value, items) => fmt_index( | ||||||
|  |                 f, | ||||||
|  |                 instruction_value, | ||||||
|  |                 &items | ||||||
|  |                     .iter() | ||||||
|  |                     .map(|expr| format!("{:?}", expr)) | ||||||
|  |                     .collect::<Vec<_>>() | ||||||
|  |                     .join(", "), | ||||||
|  |             ), | ||||||
|  |             Instr::GetStructElemPtr(instruction_value, index) => { | ||||||
|  |                 write!(f, "GEP(")?; | ||||||
|  |                 fmt_index(f, instruction_value, &index.to_string())?; | ||||||
|  |                 write!(f, ")") | ||||||
|  |             } | ||||||
|  |             Instr::ExtractValue(instruction_value, index) => fmt_index(f, instruction_value, &index.to_string()), | ||||||
|  |             Instr::Trunc(instr_val, ty) => { | ||||||
|  |                 write!(f, "{:?} to {:?} ({})", instr_val, ty, self.default_name()) | ||||||
|  |             } | ||||||
|  |             Instr::ZExt(instr_val, ty) => { | ||||||
|  |                 write!(f, "{:?} to {:?} ({})", instr_val, ty, self.default_name()) | ||||||
|  |             } | ||||||
|  |             Instr::SExt(instr_val, ty) => { | ||||||
|  |                 write!(f, "{:?} to {:?} ({})", instr_val, ty, self.default_name()) | ||||||
|  |             } | ||||||
|  |             Instr::FPTrunc(instr_val, ty) => { | ||||||
|  |                 write!(f, "{:?} to {:?} ({})", instr_val, ty, self.default_name()) | ||||||
|  |             } | ||||||
|  |             Instr::FPExt(instr_val, ty) => { | ||||||
|  |                 write!(f, "{:?} to {:?} ({})", instr_val, ty, self.default_name()) | ||||||
|  |             } | ||||||
|  |             Instr::FPToUI(instr_val, ty) => { | ||||||
|  |                 write!(f, "{:?} to {:?} ({})", instr_val, ty, self.default_name()) | ||||||
|  |             } | ||||||
|  |             Instr::FPToSI(instr_val, ty) => { | ||||||
|  |                 write!(f, "{:?} to {:?} ({})", instr_val, ty, self.default_name()) | ||||||
|  |             } | ||||||
|  |             Instr::UIToFP(instr_val, ty) => { | ||||||
|  |                 write!(f, "{:?} to {:?} ({})", instr_val, ty, self.default_name()) | ||||||
|  |             } | ||||||
|  |             Instr::SIToFP(instr_val, ty) => { | ||||||
|  |                 write!(f, "{:?} to {:?} ({})", instr_val, ty, self.default_name()) | ||||||
|  |             } | ||||||
|  |             Instr::PtrToInt(instr_val, ty) => { | ||||||
|  |                 write!(f, "{:?} to {:?} ({})", instr_val, ty, self.default_name()) | ||||||
|  |             } | ||||||
|  |             Instr::IntToPtr(instr_val, ty) => { | ||||||
|  |                 write!(f, "{:?} to {:?} ({})", instr_val, ty, self.default_name()) | ||||||
|  |             } | ||||||
|  |             Instr::BitCast(instr_val, ty) => { | ||||||
|  |                 write!(f, "{:?} to {:?} ({})", instr_val, ty, self.default_name()) | ||||||
|  |             } | ||||||
|  |             Instr::Or(lhs, rhs) => fmt_binop(f, lhs, &"||", rhs), | ||||||
|  |             Instr::XOr(lhs, rhs) => fmt_binop(f, lhs, &"^", rhs), | ||||||
|  |             Instr::ShiftRightLogical(lhs, rhs) => fmt_binop(f, lhs, &">>l", rhs), | ||||||
|  |             Instr::ShiftRightArithmetic(lhs, rhs) => fmt_binop(f, lhs, &">>a", rhs), | ||||||
|  |             Instr::ShiftLeft(lhs, rhs) => fmt_binop(f, lhs, &"<<", rhs), | ||||||
|  |             Instr::GetGlobal(global_value) => write!(f, "global {:?}", global_value), | ||||||
|  |             Instr::IsNull(_) => write!(f, "is_null"), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn fmt_binop( | ||||||
|  |     f: &mut std::fmt::Formatter<'_>, | ||||||
|  |     lhs: &impl std::fmt::Debug, | ||||||
|  |     op: &impl std::fmt::Debug, | ||||||
|  |     rhs: &impl std::fmt::Debug, | ||||||
|  | ) -> std::fmt::Result { | ||||||
|  |     lhs.fmt(f)?; | ||||||
|  |     f.write_char(' ')?; | ||||||
|  |     op.fmt(f)?; | ||||||
|  |     f.write_char(' ')?; | ||||||
|  |     rhs.fmt(f) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn fmt_call( | ||||||
|  |     f: &mut std::fmt::Formatter<'_>, | ||||||
|  |     fun: &impl std::fmt::Debug, | ||||||
|  |     params: &impl std::fmt::Debug, | ||||||
|  | ) -> std::fmt::Result { | ||||||
|  |     fun.fmt(f)?; | ||||||
|  |     f.write_char('(')?; | ||||||
|  |     params.fmt(f)?; | ||||||
|  |     f.write_char(')') | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn fmt_index( | ||||||
|  |     f: &mut std::fmt::Formatter<'_>, | ||||||
|  |     fun: &impl std::fmt::Debug, | ||||||
|  |     params: &impl std::fmt::Debug, | ||||||
|  | ) -> std::fmt::Result { | ||||||
|  |     fun.fmt(f)?; | ||||||
|  |     f.write_char('[')?; | ||||||
|  |     params.fmt(f)?; | ||||||
|  |     f.write_char(']') | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Debug for CmpPredicate { | ||||||
|  |     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||||||
|  |         match self { | ||||||
|  |             Self::LT => write!(f, "<"), | ||||||
|  |             Self::GT => write!(f, ">"), | ||||||
|  |             Self::LE => write!(f, "<="), | ||||||
|  |             Self::GE => write!(f, ">="), | ||||||
|  |             Self::EQ => write!(f, "=="), | ||||||
|  |             Self::NE => write!(f, "!="), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Debug for TerminatorKind { | ||||||
|  |     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||||||
|  |         match self { | ||||||
|  |             Self::Ret(val) => { | ||||||
|  |                 write!(f, "Ret ")?; | ||||||
|  |                 val.fmt(f) | ||||||
|  |             } | ||||||
|  |             Self::RetVoid => write!(f, "Void Ret"), | ||||||
|  |             Self::Br(val) => { | ||||||
|  |                 write!(f, "Br ")?; | ||||||
|  |                 val.fmt(f) | ||||||
|  |             } | ||||||
|  |             Self::CondBr(cond, b1, b2) => { | ||||||
|  |                 write!(f, "CondBr ")?; | ||||||
|  |                 cond.fmt(f)?; | ||||||
|  |                 write!(f, " ? ")?; | ||||||
|  |                 b1.fmt(f)?; | ||||||
|  |                 write!(f, " : ")?; | ||||||
|  |                 b2.fmt(f)?; | ||||||
|  |                 Ok(()) | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Debug for DebugTypeHolder { | ||||||
|  |     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||||||
|  |         f.debug_tuple(&format!("DebugTypeHolder {:?}", self.value)) | ||||||
|  |             .field(&self.data) | ||||||
|  |             .finish() | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Debug for DebugTypeData { | ||||||
|  |     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||||||
|  |         match self { | ||||||
|  |             DebugTypeData::Basic(ty) => Debug::fmt(ty, f), | ||||||
|  |             DebugTypeData::Subprogram(ty) => Debug::fmt(ty, f), | ||||||
|  |             DebugTypeData::Pointer(ty) => Debug::fmt(ty, f), | ||||||
|  |             DebugTypeData::Array(ty) => Debug::fmt(ty, f), | ||||||
|  |             DebugTypeData::Struct(ty) => Debug::fmt(ty, f), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Debug for DebugBasicType { | ||||||
|  |     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||||||
|  |         f.debug_tuple("BasicType") | ||||||
|  |             .field(&self.name) | ||||||
|  |             .field(&self.size_bits) | ||||||
|  |             .field(&self.encoding) | ||||||
|  |             .field(&self.flags) | ||||||
|  |             .finish() | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Debug for DebugStructType { | ||||||
|  |     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||||||
|  |         f.debug_struct("Struct") | ||||||
|  |             .field("name", &self.name) | ||||||
|  |             .field("scope", &self.scope) | ||||||
|  |             .field("pos", &self.pos) | ||||||
|  |             .field("size_bit", &self.size_bits) | ||||||
|  |             .field("flags", &self.flags) | ||||||
|  |             .field("elements", &self.fields) | ||||||
|  |             .finish() | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Debug for DebugFieldType { | ||||||
|  |     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||||||
|  |         f.debug_struct(&format!("Field({})", self.name)) | ||||||
|  |             .field("scope", &self.scope) | ||||||
|  |             .field("pos", &self.pos) | ||||||
|  |             .field("size_bits", &self.size_bits) | ||||||
|  |             .field("offset", &self.offset) | ||||||
|  |             .field("flags", &self.flags) | ||||||
|  |             .field("ty", &self.ty) | ||||||
|  |             .finish() | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Debug for DebugSubprogramType { | ||||||
|  |     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||||||
|  |         f.debug_tuple("Subprogram") | ||||||
|  |             .field(&self.parameters) | ||||||
|  |             .field(&self.flags) | ||||||
|  |             .finish() | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Debug for DebugPointerType { | ||||||
|  |     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||||||
|  |         f.debug_tuple(&format!("Pointer<{:?}>({})", self.pointee, self.name)) | ||||||
|  |             .field(&self.size_bits) | ||||||
|  |             .finish() | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Debug for DebugArrayType { | ||||||
|  |     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||||||
|  |         f.debug_struct(&format!("Array<{:?}>[{}]", self.element_type, self.length)) | ||||||
|  |             .field("size_bits", &self.size_bits) | ||||||
|  |             .field("align_bits", &self.align_bits) | ||||||
|  |             .finish() | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Debug for DebugMetadataValue { | ||||||
|  |     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||||||
|  |         write!(f, "Meta[{}]", self.0) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Debug for DebugScopeValue { | ||||||
|  |     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||||||
|  |         write!( | ||||||
|  |             f, | ||||||
|  |             "Scope[{}]", | ||||||
|  |             self.0.iter().map(|v| v.to_string()).collect::<Vec<_>>().join(", ") | ||||||
|  |         ) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Debug for DebugTypeValue { | ||||||
|  |     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||||||
|  |         write!(f, "Type[{}]", self.0) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Debug for DebugLocationValue { | ||||||
|  |     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||||||
|  |         write!(f, "Value[{:?}][{}]", self.0, self.1) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Debug for DebugLocation { | ||||||
|  |     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||||||
|  |         write!(f, "{:?} on scope {:?}", self.pos, self.scope) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Debug for DebugPosition { | ||||||
|  |     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||||||
|  |         write!(f, "ln {}, col {}", self.line, self.column) | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										263
									
								
								src/intrinsics.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										263
									
								
								src/intrinsics.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,263 @@ | |||||||
|  | use crate::{CompileResult, Type, TypeCategory, builder::Builder}; | ||||||
|  | 
 | ||||||
|  | #[derive(Clone, Debug)] | ||||||
|  | pub enum LLVMIntrinsic { | ||||||
|  |     Abs(Type), | ||||||
|  |     Max(Type), | ||||||
|  |     Min(Type), | ||||||
|  |     Memcpy(Type), | ||||||
|  |     Sqrt(Type), | ||||||
|  |     PowI(Type, Type), | ||||||
|  |     Pow(Type), | ||||||
|  |     Sin(Type), | ||||||
|  |     Cos(Type), | ||||||
|  |     Tan(Type), | ||||||
|  |     ASin(Type), | ||||||
|  |     ACos(Type), | ||||||
|  |     ATan(Type), | ||||||
|  |     ATan2(Type), | ||||||
|  |     SinH(Type), | ||||||
|  |     CosH(Type), | ||||||
|  |     TanH(Type), | ||||||
|  |     Log(Type), | ||||||
|  |     Log2(Type), | ||||||
|  |     Log10(Type), | ||||||
|  |     Copysign(Type), | ||||||
|  |     Floor(Type), | ||||||
|  |     Ceil(Type), | ||||||
|  |     Trunc(Type), | ||||||
|  |     RoundEven(Type), | ||||||
|  |     Round(Type), | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl LLVMIntrinsic { | ||||||
|  |     pub(crate) fn signature(&self, builder: &Builder) -> CompileResult<(String, Vec<Type>, Type)> { | ||||||
|  |         match self { | ||||||
|  |             LLVMIntrinsic::Max(ty) => { | ||||||
|  |                 let name = match ty.category() { | ||||||
|  |                     TypeCategory::SignedInteger => format!("llvm.smax.{}", ty.llvm_ty_str(builder)), | ||||||
|  |                     TypeCategory::UnsignedInteger => format!("llvm.umax.{}", ty.llvm_ty_str(builder)), | ||||||
|  |                     TypeCategory::Real => format!("llvm.maximum.{}", ty.llvm_ty_str(builder)), | ||||||
|  |                     _ => return Err(crate::ErrorKind::Null), | ||||||
|  |                 }; | ||||||
|  |                 Ok((name, vec![ty.clone(), ty.clone()], ty.clone())) | ||||||
|  |             } | ||||||
|  |             LLVMIntrinsic::Min(ty) => { | ||||||
|  |                 let name = match ty.category() { | ||||||
|  |                     TypeCategory::SignedInteger => format!("llvm.smin.{}", ty.llvm_ty_str(builder)), | ||||||
|  |                     TypeCategory::UnsignedInteger => format!("llvm.umin.{}", ty.llvm_ty_str(builder)), | ||||||
|  |                     TypeCategory::Real => format!("llvm.minimum.{}", ty.llvm_ty_str(builder)), | ||||||
|  |                     _ => return Err(crate::ErrorKind::Null), | ||||||
|  |                 }; | ||||||
|  |                 Ok((name, vec![ty.clone(), ty.clone()], ty.clone())) | ||||||
|  |             } | ||||||
|  |             LLVMIntrinsic::Abs(ty) => { | ||||||
|  |                 let name = match ty.category() { | ||||||
|  |                     TypeCategory::SignedInteger => format!("llvm.abs.{}", ty.llvm_ty_str(builder)), | ||||||
|  |                     TypeCategory::UnsignedInteger => format!("llvm.abs.{}", ty.llvm_ty_str(builder)), | ||||||
|  |                     TypeCategory::Real => format!("llvm.fabs.{}", ty.llvm_ty_str(builder)), | ||||||
|  |                     _ => return Err(crate::ErrorKind::Null), | ||||||
|  |                 }; | ||||||
|  |                 Ok((name, vec![ty.clone(), Type::Bool], ty.clone())) | ||||||
|  |             } | ||||||
|  |             LLVMIntrinsic::Memcpy(ty) => { | ||||||
|  |                 let name = match ty.category() { | ||||||
|  |                     TypeCategory::Ptr => String::from("llvm.memcpy"), | ||||||
|  |                     _ => return Err(crate::ErrorKind::Null), | ||||||
|  |                 }; | ||||||
|  |                 Ok((name, vec![ty.clone(), ty.clone(), Type::U64, Type::Bool], Type::Void)) | ||||||
|  |             } | ||||||
|  |             LLVMIntrinsic::Sqrt(ty) => { | ||||||
|  |                 let name = match ty.category() { | ||||||
|  |                     TypeCategory::Real => format!("llvm.sqrt.{}", ty.llvm_ty_str(builder)), | ||||||
|  |                     _ => return Err(crate::ErrorKind::Null), | ||||||
|  |                 }; | ||||||
|  |                 Ok((name, vec![ty.clone()], ty.clone())) | ||||||
|  |             } | ||||||
|  |             LLVMIntrinsic::PowI(ty1, ty2) => { | ||||||
|  |                 let name = match (ty1.category(), ty2.category()) { | ||||||
|  |                     (TypeCategory::Real, TypeCategory::SignedInteger) => { | ||||||
|  |                         format!("llvm.powi.{}.{}", ty1.llvm_ty_str(builder), ty2.llvm_ty_str(builder)) | ||||||
|  |                     } | ||||||
|  |                     (TypeCategory::Real, TypeCategory::UnsignedInteger) => { | ||||||
|  |                         format!("llvm.powi.{}.{}", ty1.llvm_ty_str(builder), ty2.llvm_ty_str(builder)) | ||||||
|  |                     } | ||||||
|  |                     _ => return Err(crate::ErrorKind::Null), | ||||||
|  |                 }; | ||||||
|  |                 Ok((name, vec![ty1.clone(), ty2.clone()], ty1.clone())) | ||||||
|  |             } | ||||||
|  |             LLVMIntrinsic::Pow(ty) => { | ||||||
|  |                 let name = match ty.category() { | ||||||
|  |                     TypeCategory::Real => format!("llvm.pow.{}", ty.llvm_ty_str(builder)), | ||||||
|  |                     _ => return Err(crate::ErrorKind::Null), | ||||||
|  |                 }; | ||||||
|  |                 Ok((name, vec![ty.clone(), ty.clone()], ty.clone())) | ||||||
|  |             } | ||||||
|  |             LLVMIntrinsic::Sin(ty) => { | ||||||
|  |                 let name = match ty.category() { | ||||||
|  |                     TypeCategory::Real => format!("llvm.sin.{}", ty.llvm_ty_str(builder)), | ||||||
|  |                     _ => return Err(crate::ErrorKind::Null), | ||||||
|  |                 }; | ||||||
|  |                 Ok((name, vec![ty.clone()], ty.clone())) | ||||||
|  |             } | ||||||
|  |             LLVMIntrinsic::Cos(ty) => { | ||||||
|  |                 let name = match ty.category() { | ||||||
|  |                     TypeCategory::Real => format!("llvm.cos.{}", ty.llvm_ty_str(builder)), | ||||||
|  |                     _ => return Err(crate::ErrorKind::Null), | ||||||
|  |                 }; | ||||||
|  |                 Ok((name, vec![ty.clone()], ty.clone())) | ||||||
|  |             } | ||||||
|  |             LLVMIntrinsic::Tan(ty) => { | ||||||
|  |                 let name = match ty.category() { | ||||||
|  |                     TypeCategory::Real => format!("llvm.tan.{}", ty.llvm_ty_str(builder)), | ||||||
|  |                     _ => return Err(crate::ErrorKind::Null), | ||||||
|  |                 }; | ||||||
|  |                 Ok((name, vec![ty.clone()], ty.clone())) | ||||||
|  |             } | ||||||
|  |             LLVMIntrinsic::ASin(ty) => { | ||||||
|  |                 let name = match ty.category() { | ||||||
|  |                     TypeCategory::Real => format!("llvm.asin.{}", ty.llvm_ty_str(builder)), | ||||||
|  |                     _ => return Err(crate::ErrorKind::Null), | ||||||
|  |                 }; | ||||||
|  |                 Ok((name, vec![ty.clone()], ty.clone())) | ||||||
|  |             } | ||||||
|  |             LLVMIntrinsic::ACos(ty) => { | ||||||
|  |                 let name = match ty.category() { | ||||||
|  |                     TypeCategory::Real => format!("llvm.acos.{}", ty.llvm_ty_str(builder)), | ||||||
|  |                     _ => return Err(crate::ErrorKind::Null), | ||||||
|  |                 }; | ||||||
|  |                 Ok((name, vec![ty.clone()], ty.clone())) | ||||||
|  |             } | ||||||
|  |             LLVMIntrinsic::ATan(ty) => { | ||||||
|  |                 let name = match ty.category() { | ||||||
|  |                     TypeCategory::Real => format!("llvm.atan.{}", ty.llvm_ty_str(builder)), | ||||||
|  |                     _ => return Err(crate::ErrorKind::Null), | ||||||
|  |                 }; | ||||||
|  |                 Ok((name, vec![ty.clone()], ty.clone())) | ||||||
|  |             } | ||||||
|  |             LLVMIntrinsic::ATan2(ty) => { | ||||||
|  |                 let name = match ty.category() { | ||||||
|  |                     TypeCategory::Real => format!("llvm.atan2.{}", ty.llvm_ty_str(builder)), | ||||||
|  |                     _ => return Err(crate::ErrorKind::Null), | ||||||
|  |                 }; | ||||||
|  |                 Ok((name, vec![ty.clone(), ty.clone()], ty.clone())) | ||||||
|  |             } | ||||||
|  |             LLVMIntrinsic::SinH(ty) => { | ||||||
|  |                 let name = match ty.category() { | ||||||
|  |                     TypeCategory::Real => format!("llvm.sinh.{}", ty.llvm_ty_str(builder)), | ||||||
|  |                     _ => return Err(crate::ErrorKind::Null), | ||||||
|  |                 }; | ||||||
|  |                 Ok((name, vec![ty.clone()], ty.clone())) | ||||||
|  |             } | ||||||
|  |             LLVMIntrinsic::CosH(ty) => { | ||||||
|  |                 let name = match ty.category() { | ||||||
|  |                     TypeCategory::Real => format!("llvm.cosh.{}", ty.llvm_ty_str(builder)), | ||||||
|  |                     _ => return Err(crate::ErrorKind::Null), | ||||||
|  |                 }; | ||||||
|  |                 Ok((name, vec![ty.clone()], ty.clone())) | ||||||
|  |             } | ||||||
|  |             LLVMIntrinsic::TanH(ty) => { | ||||||
|  |                 let name = match ty.category() { | ||||||
|  |                     TypeCategory::Real => format!("llvm.tanh.{}", ty.llvm_ty_str(builder)), | ||||||
|  |                     _ => return Err(crate::ErrorKind::Null), | ||||||
|  |                 }; | ||||||
|  |                 Ok((name, vec![ty.clone()], ty.clone())) | ||||||
|  |             } | ||||||
|  |             LLVMIntrinsic::Log(ty) => { | ||||||
|  |                 let name = match ty.category() { | ||||||
|  |                     TypeCategory::Real => format!("llvm.log.{}", ty.llvm_ty_str(builder)), | ||||||
|  |                     _ => return Err(crate::ErrorKind::Null), | ||||||
|  |                 }; | ||||||
|  |                 Ok((name, vec![ty.clone()], ty.clone())) | ||||||
|  |             } | ||||||
|  |             LLVMIntrinsic::Log2(ty) => { | ||||||
|  |                 let name = match ty.category() { | ||||||
|  |                     TypeCategory::Real => format!("llvm.log2.{}", ty.llvm_ty_str(builder)), | ||||||
|  |                     _ => return Err(crate::ErrorKind::Null), | ||||||
|  |                 }; | ||||||
|  |                 Ok((name, vec![ty.clone()], ty.clone())) | ||||||
|  |             } | ||||||
|  |             LLVMIntrinsic::Log10(ty) => { | ||||||
|  |                 let name = match ty.category() { | ||||||
|  |                     TypeCategory::Real => format!("llvm.log10.{}", ty.llvm_ty_str(builder)), | ||||||
|  |                     _ => return Err(crate::ErrorKind::Null), | ||||||
|  |                 }; | ||||||
|  |                 Ok((name, vec![ty.clone()], ty.clone())) | ||||||
|  |             } | ||||||
|  |             LLVMIntrinsic::Copysign(ty) => { | ||||||
|  |                 let name = match ty.category() { | ||||||
|  |                     TypeCategory::Real => format!("llvm.copysign.{}", ty.llvm_ty_str(builder)), | ||||||
|  |                     _ => return Err(crate::ErrorKind::Null), | ||||||
|  |                 }; | ||||||
|  |                 Ok((name, vec![ty.clone()], ty.clone())) | ||||||
|  |             } | ||||||
|  |             LLVMIntrinsic::Floor(ty) => { | ||||||
|  |                 let name = match ty.category() { | ||||||
|  |                     TypeCategory::Real => format!("llvm.floor.{}", ty.llvm_ty_str(builder)), | ||||||
|  |                     _ => return Err(crate::ErrorKind::Null), | ||||||
|  |                 }; | ||||||
|  |                 Ok((name, vec![ty.clone()], ty.clone())) | ||||||
|  |             } | ||||||
|  |             LLVMIntrinsic::Ceil(ty) => { | ||||||
|  |                 let name = match ty.category() { | ||||||
|  |                     TypeCategory::Real => format!("llvm.ceil.{}", ty.llvm_ty_str(builder)), | ||||||
|  |                     _ => return Err(crate::ErrorKind::Null), | ||||||
|  |                 }; | ||||||
|  |                 Ok((name, vec![ty.clone()], ty.clone())) | ||||||
|  |             } | ||||||
|  |             LLVMIntrinsic::Trunc(ty) => { | ||||||
|  |                 let name = match ty.category() { | ||||||
|  |                     TypeCategory::Real => format!("llvm.trunc.{}", ty.llvm_ty_str(builder)), | ||||||
|  |                     _ => return Err(crate::ErrorKind::Null), | ||||||
|  |                 }; | ||||||
|  |                 Ok((name, vec![ty.clone()], ty.clone())) | ||||||
|  |             } | ||||||
|  |             LLVMIntrinsic::RoundEven(ty) => { | ||||||
|  |                 let name = match ty.category() { | ||||||
|  |                     TypeCategory::Real => format!("llvm.roundeven.{}", ty.llvm_ty_str(builder)), | ||||||
|  |                     _ => return Err(crate::ErrorKind::Null), | ||||||
|  |                 }; | ||||||
|  |                 Ok((name, vec![ty.clone()], ty.clone())) | ||||||
|  |             } | ||||||
|  |             LLVMIntrinsic::Round(ty) => { | ||||||
|  |                 let name = match ty.category() { | ||||||
|  |                     TypeCategory::Real => format!("llvm.rint.{}", ty.llvm_ty_str(builder)), | ||||||
|  |                     _ => return Err(crate::ErrorKind::Null), | ||||||
|  |                 }; | ||||||
|  |                 Ok((name, vec![ty.clone()], ty.clone())) | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Type { | ||||||
|  |     fn llvm_ty_str(&self, builder: &Builder) -> String { | ||||||
|  |         match self { | ||||||
|  |             Type::I8 => String::from("i8"), | ||||||
|  |             Type::I16 => String::from("u16"), | ||||||
|  |             Type::I32 => String::from("i32"), | ||||||
|  |             Type::I64 => String::from("i64"), | ||||||
|  |             Type::I128 => String::from("i128"), | ||||||
|  |             Type::U8 => String::from("i8"), | ||||||
|  |             Type::U16 => String::from("i16"), | ||||||
|  |             Type::U32 => String::from("i32"), | ||||||
|  |             Type::U64 => String::from("i64"), | ||||||
|  |             Type::U128 => String::from("i128"), | ||||||
|  |             Type::F16 => String::from("f16"), | ||||||
|  |             Type::F32B => String::from("f32b"), | ||||||
|  |             Type::F32 => String::from("f32"), | ||||||
|  |             Type::F64 => String::from("f64"), | ||||||
|  |             Type::F80 => String::from("x86_fp80"), | ||||||
|  |             Type::F128 => String::from("fp128"), | ||||||
|  |             Type::F128PPC => String::from("ppc_fp128"), | ||||||
|  |             Type::Bool => String::from("i1"), | ||||||
|  |             Type::Void => String::from("void"), | ||||||
|  |             Type::CustomType(type_value) => { | ||||||
|  |                 let ty = unsafe { builder.type_data(type_value) }; | ||||||
|  |                 ty.name.clone() | ||||||
|  |             } | ||||||
|  |             Type::Array(ty, len) => format!("[{} x {}]", len, ty.llvm_ty_str(builder)), | ||||||
|  |             Type::Ptr(_) => String::from("ptr"), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										741
									
								
								src/lib.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										741
									
								
								src/lib.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,741 @@ | |||||||
|  | //! Reid LLVM Lib is an ergonomic Rust'y API which is used to produce a
 | ||||||
|  | //! Low-Level IR (LLIR) using [`Context`] and [`Builder`]. This Builder can then
 | ||||||
|  | //! be used at the end to compile said LLIR into LLVM IR.
 | ||||||
|  | 
 | ||||||
|  | use std::{fmt::Debug, marker::PhantomData}; | ||||||
|  | 
 | ||||||
|  | use builder::{BlockValue, Builder, FunctionValue, InstructionValue, ModuleValue, TypeValue}; | ||||||
|  | use debug_information::{DebugFileData, DebugInformation, DebugLocationValue, DebugMetadataValue}; | ||||||
|  | use fmt::PrintableModule; | ||||||
|  | 
 | ||||||
|  | use crate::{ | ||||||
|  |     builder::{ConstantValue, GlobalValue}, | ||||||
|  |     debug_information::DebugScopeValue, | ||||||
|  |     intrinsics::LLVMIntrinsic, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | pub mod builder; | ||||||
|  | pub mod compile; | ||||||
|  | pub mod debug_information; | ||||||
|  | mod fmt; | ||||||
|  | pub mod intrinsics; | ||||||
|  | mod pad_adapter; | ||||||
|  | mod util; | ||||||
|  | 
 | ||||||
|  | #[derive(thiserror::Error, Debug, Clone, PartialEq, PartialOrd)] | ||||||
|  | pub enum ErrorKind { | ||||||
|  |     #[error("NULL error, should never occur!")] | ||||||
|  |     Null, | ||||||
|  |     #[error("Types {0:?} and {1:?} incompatible")] | ||||||
|  |     TypesIncompatible(Type, Type), | ||||||
|  |     #[error("Phi list of values is empty")] | ||||||
|  |     EmptyPhiList, | ||||||
|  |     #[error("Type {1:?} of value {0:?} is not extractable")] | ||||||
|  |     NotExtractable(InstructionValue, Type), | ||||||
|  |     #[error("Type {0:?} is not castable to {1:?}")] | ||||||
|  |     ImpossibleCast(Type, Type), | ||||||
|  |     #[error("Block is already terminated")] | ||||||
|  |     BlockAlreadyTerminated, | ||||||
|  |     #[error("Block terminator already has a location")] | ||||||
|  |     BlockTerminatorLocated, | ||||||
|  |     #[error("Value {0:?} must be an integer type. Is {1:?}")] | ||||||
|  |     TypeNotInteger(InstructionValue, Type), | ||||||
|  |     #[error("Value {0:?} must be a {2:?} type. Is {1:?}")] | ||||||
|  |     TypeWrongCategory(InstructionValue, Type, TypeCategory), | ||||||
|  |     #[error("Value {0:?} must be comparable, was {1:?}")] | ||||||
|  |     TypeNotComparable(InstructionValue, Type), | ||||||
|  |     #[error("Got {0:?} parameters, expected {1:?}")] | ||||||
|  |     InvalidLenParams(usize, usize), | ||||||
|  |     #[error("Value {0:?} is not a pointer, is {1:?}")] | ||||||
|  |     NotPointer(InstructionValue, Type), | ||||||
|  |     #[error("Value {0:?} is not a struct, is {1:?}")] | ||||||
|  |     NotStruct(InstructionValue, Type), | ||||||
|  |     #[error("Struct {0:?} has no such field as {1:?}")] | ||||||
|  |     NoSuchField(Type, u32), | ||||||
|  |     #[error("Function {0:?} has no such parameter as {1:?}")] | ||||||
|  |     NoSuchParam(FunctionValue, usize), | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub type CompileResult<T> = Result<T, ErrorKind>; | ||||||
|  | 
 | ||||||
|  | #[derive(Debug)] | ||||||
|  | pub struct Context { | ||||||
|  |     builder: Builder, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Context { | ||||||
|  |     pub fn new<T: Into<String>>(producer: T) -> Context { | ||||||
|  |         Context { | ||||||
|  |             builder: Builder::new(producer.into()), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn module<'ctx>(&'ctx self, name: &str, main: bool) -> Module<'ctx> { | ||||||
|  |         let value = self.builder.add_module(ModuleData { | ||||||
|  |             name: name.to_owned(), | ||||||
|  |             is_main: main, | ||||||
|  |         }); | ||||||
|  |         Module { | ||||||
|  |             phantom: PhantomData, | ||||||
|  |             builder: self.builder.clone(), | ||||||
|  |             value, | ||||||
|  |             debug_info: None, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Debug, Clone, Hash)] | ||||||
|  | pub struct ModuleData { | ||||||
|  |     name: String, | ||||||
|  |     is_main: bool, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Clone)] | ||||||
|  | pub struct Module<'ctx> { | ||||||
|  |     phantom: PhantomData<&'ctx ()>, | ||||||
|  |     builder: Builder, | ||||||
|  |     value: ModuleValue, | ||||||
|  |     debug_info: Option<DebugInformation>, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<'ctx> Module<'ctx> { | ||||||
|  |     pub fn function( | ||||||
|  |         &self, | ||||||
|  |         name: &str, | ||||||
|  |         linkage: Option<String>, | ||||||
|  |         ret: Type, | ||||||
|  |         params: Vec<Type>, | ||||||
|  |         flags: FunctionFlags, | ||||||
|  |     ) -> Function<'ctx> { | ||||||
|  |         unsafe { | ||||||
|  |             Function { | ||||||
|  |                 phantom: PhantomData, | ||||||
|  |                 builder: self.builder.clone(), | ||||||
|  |                 value: self.builder.add_function( | ||||||
|  |                     &self.value, | ||||||
|  |                     FunctionData { | ||||||
|  |                         name: name.to_owned(), | ||||||
|  |                         linkage_name: linkage, | ||||||
|  |                         ret, | ||||||
|  |                         params, | ||||||
|  |                         flags, | ||||||
|  |                     }, | ||||||
|  |                 ), | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn intrinsic(&self, intrinsic: LLVMIntrinsic) -> CompileResult<FunctionValue> { | ||||||
|  |         unsafe { | ||||||
|  |             let (name, params, ret) = intrinsic.signature(&self.builder)?; | ||||||
|  |             Ok(self.builder.add_function( | ||||||
|  |                 &self.value, | ||||||
|  |                 FunctionData { | ||||||
|  |                     name: name.to_owned(), | ||||||
|  |                     linkage_name: Some(name.to_owned()), | ||||||
|  |                     ret, | ||||||
|  |                     params, | ||||||
|  |                     flags: FunctionFlags { | ||||||
|  |                         is_extern: true, | ||||||
|  |                         ..Default::default() | ||||||
|  |                     }, | ||||||
|  |                 }, | ||||||
|  |             )) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn custom_type(&self, ty: CustomTypeKind) -> TypeValue { | ||||||
|  |         unsafe { | ||||||
|  |             let (name, kind) = match &ty { | ||||||
|  |                 CustomTypeKind::NamedStruct(NamedStruct(name, _)) => (name.clone(), ty), | ||||||
|  |             }; | ||||||
|  |             self.builder.add_type(&self.value, TypeData { name, kind }) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn value(&self) -> ModuleValue { | ||||||
|  |         self.value | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn as_printable(&self) -> PrintableModule<'ctx> { | ||||||
|  |         PrintableModule { | ||||||
|  |             phantom: PhantomData, | ||||||
|  |             module: self.builder.find_module(self.value), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn create_debug_info(&mut self, file: DebugFileData) -> (DebugInformation, DebugScopeValue) { | ||||||
|  |         let (debug_info, scope_value) = DebugInformation::from_file(file); | ||||||
|  |         self.debug_info = Some(debug_info.clone()); | ||||||
|  |         (debug_info, scope_value) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn get_debug_info(&self) -> &Option<DebugInformation> { | ||||||
|  |         &self.debug_info | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn add_constant(&self, constant: ConstValueKind) -> ConstantValue { | ||||||
|  |         unsafe { self.builder.build_constant(self.value, constant) } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn add_global<T: Into<String>>(&self, name: T, constant: ConstantValue) -> GlobalValue { | ||||||
|  |         unsafe { self.builder.add_global(self.value, name.into(), constant) } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<'ctx> Drop for Module<'ctx> { | ||||||
|  |     fn drop(&mut self) { | ||||||
|  |         if let Some(debug_info) = self.debug_info.take() { | ||||||
|  |             self.builder.set_debug_information(&self.value, debug_info); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Debug, Clone, Hash)] | ||||||
|  | pub struct FunctionData { | ||||||
|  |     name: String, | ||||||
|  |     linkage_name: Option<String>, | ||||||
|  |     ret: Type, | ||||||
|  |     params: Vec<Type>, | ||||||
|  |     flags: FunctionFlags, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Debug, Clone, Copy, Hash)] | ||||||
|  | pub struct FunctionFlags { | ||||||
|  |     /// True in the destination module of the import, false in the source module.
 | ||||||
|  |     pub is_extern: bool, | ||||||
|  |     /// Whether this function is the main function of the module, that should be
 | ||||||
|  |     /// executed (and linked externally also).
 | ||||||
|  |     pub is_main: bool, | ||||||
|  |     /// Whether this function should be available externally always.
 | ||||||
|  |     pub is_pub: bool, | ||||||
|  |     /// If this function is an imported function (either in the source or
 | ||||||
|  |     /// destination module)
 | ||||||
|  |     pub is_imported: bool, | ||||||
|  |     /// Whether this function should add "alwaysinline"-attribute.
 | ||||||
|  |     pub inline: bool, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Default for FunctionFlags { | ||||||
|  |     fn default() -> FunctionFlags { | ||||||
|  |         FunctionFlags { | ||||||
|  |             is_extern: false, | ||||||
|  |             is_main: false, | ||||||
|  |             is_pub: false, | ||||||
|  |             is_imported: false, | ||||||
|  |             inline: false, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub struct Function<'ctx> { | ||||||
|  |     phantom: PhantomData<&'ctx ()>, | ||||||
|  |     builder: Builder, | ||||||
|  |     value: FunctionValue, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<'ctx> Function<'ctx> { | ||||||
|  |     pub fn block(&self, name: &str) -> Block<'ctx> { | ||||||
|  |         unsafe { | ||||||
|  |             Block { | ||||||
|  |                 phantom: PhantomData, | ||||||
|  |                 builder: self.builder.clone(), | ||||||
|  |                 value: self.builder.add_block( | ||||||
|  |                     &self.value, | ||||||
|  |                     BlockData { | ||||||
|  |                         name: name.to_owned(), | ||||||
|  |                         terminator: None, | ||||||
|  |                         terminator_location: None, | ||||||
|  |                         deleted: false, | ||||||
|  |                     }, | ||||||
|  |                 ), | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn set_debug(&self, subprogram: DebugScopeValue) { | ||||||
|  |         unsafe { | ||||||
|  |             self.builder.set_debug_subprogram(&self.value, subprogram); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn value(&self) -> FunctionValue { | ||||||
|  |         self.value | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Debug, Clone, Hash)] | ||||||
|  | pub struct BlockData { | ||||||
|  |     name: String, | ||||||
|  |     terminator: Option<TerminatorKind>, | ||||||
|  |     terminator_location: Option<DebugLocationValue>, | ||||||
|  |     deleted: bool, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Clone)] | ||||||
|  | pub struct Block<'builder> { | ||||||
|  |     phantom: PhantomData<&'builder ()>, | ||||||
|  |     builder: Builder, | ||||||
|  |     value: BlockValue, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Instr { | ||||||
|  |     pub fn default_name(&self) -> &str { | ||||||
|  |         match self { | ||||||
|  |             Instr::Param(_) => "param", | ||||||
|  |             Instr::Constant(_) => "const1", | ||||||
|  |             Instr::Add(..) => "add", | ||||||
|  |             Instr::FAdd(..) => "fadd", | ||||||
|  |             Instr::Sub(..) => "sub", | ||||||
|  |             Instr::FSub(..) => "fsub", | ||||||
|  |             Instr::Mul(..) => "mul", | ||||||
|  |             Instr::FMul(..) => "fmul", | ||||||
|  |             Instr::UDiv(..) => "udiv", | ||||||
|  |             Instr::SDiv(..) => "sdiv", | ||||||
|  |             Instr::FDiv(..) => "fdiv", | ||||||
|  |             Instr::URem(..) => "urem", | ||||||
|  |             Instr::SRem(..) => "srem", | ||||||
|  |             Instr::FRem(..) => "frem", | ||||||
|  |             Instr::And(..) => "and", | ||||||
|  |             Instr::Phi(_) => "phi", | ||||||
|  |             Instr::Alloca(_) => "alloca", | ||||||
|  |             Instr::Load(_, _) => "load", | ||||||
|  |             Instr::Store(..) => "store", | ||||||
|  |             Instr::ArrayAlloca(_, _) => "arrayalloca", | ||||||
|  |             Instr::GetElemPtr(..) => "getelemptr", | ||||||
|  |             Instr::GetStructElemPtr(..) => "getstructelemptr", | ||||||
|  |             Instr::ExtractValue(..) => "extractvalue", | ||||||
|  |             Instr::ICmp(..) => "icmp", | ||||||
|  |             Instr::FunctionCall(..) => "call", | ||||||
|  |             Instr::FCmp(_, _, _) => "fcmp", | ||||||
|  |             Instr::Trunc(_, _) => "trunc", | ||||||
|  |             Instr::ZExt(_, _) => "zext", | ||||||
|  |             Instr::SExt(_, _) => "sext", | ||||||
|  |             Instr::FPTrunc(_, _) => "fptrunc", | ||||||
|  |             Instr::FPExt(_, _) => "pfext", | ||||||
|  |             Instr::FPToUI(_, _) => "fptoui", | ||||||
|  |             Instr::FPToSI(_, _) => "fptosi", | ||||||
|  |             Instr::UIToFP(_, _) => "uitofp", | ||||||
|  |             Instr::SIToFP(_, _) => "sitofp", | ||||||
|  |             Instr::PtrToInt(_, _) => "ptrtoint", | ||||||
|  |             Instr::IntToPtr(_, _) => "inttoptr", | ||||||
|  |             Instr::BitCast(_, _) => "bitcast", | ||||||
|  |             Instr::Or(..) => "or", | ||||||
|  |             Instr::XOr(..) => "xor", | ||||||
|  |             Instr::ShiftRightLogical(..) => "lshr", | ||||||
|  |             Instr::ShiftRightArithmetic(..) => "ashr", | ||||||
|  |             Instr::ShiftLeft(..) => "shl", | ||||||
|  |             Instr::GetGlobal(..) => "global", | ||||||
|  |             Instr::IsNull(..) => "is_null", | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<'builder> Block<'builder> { | ||||||
|  |     pub fn build_named<T: Into<String>>(&mut self, name: T, instruction: Instr) -> CompileResult<InstructionValue> { | ||||||
|  |         unsafe { | ||||||
|  |             self.builder.add_instruction( | ||||||
|  |                 &self.value, | ||||||
|  |                 InstructionData { | ||||||
|  |                     kind: instruction, | ||||||
|  |                     location: None, | ||||||
|  |                     meta: None, | ||||||
|  |                 }, | ||||||
|  |                 name.into(), | ||||||
|  |             ) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn build(&mut self, instruction: Instr) -> CompileResult<InstructionValue> { | ||||||
|  |         unsafe { | ||||||
|  |             let name = instruction.default_name().to_owned(); | ||||||
|  |             self.builder.add_instruction( | ||||||
|  |                 &self.value, | ||||||
|  |                 InstructionData { | ||||||
|  |                     kind: instruction, | ||||||
|  |                     location: None, | ||||||
|  |                     meta: None, | ||||||
|  |                 }, | ||||||
|  |                 name, | ||||||
|  |             ) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn find_function(&mut self, name: &String) -> Option<FunctionValue> { | ||||||
|  |         unsafe { self.builder.find_function(self.value.0.0, name) } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn set_instr_location(&self, instruction: InstructionValue, location: DebugLocationValue) { | ||||||
|  |         unsafe { | ||||||
|  |             self.builder.add_instruction_location(&instruction, location); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn set_instr_metadata(&self, instruction: InstructionValue, location: DebugMetadataValue) { | ||||||
|  |         unsafe { | ||||||
|  |             self.builder.add_instruction_metadata(&instruction, location); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn terminate(&mut self, instruction: TerminatorKind) -> CompileResult<()> { | ||||||
|  |         unsafe { self.builder.terminate(&self.value, instruction) } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn set_terminator_location(&mut self, location: DebugLocationValue) -> CompileResult<()> { | ||||||
|  |         unsafe { self.builder.set_terminator_location(&self.value, location) } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /// Delete block if it is unused. Return true if deleted, false if not.
 | ||||||
|  |     pub fn delete_if_unused(&mut self) -> CompileResult<bool> { | ||||||
|  |         unsafe { | ||||||
|  |             if !self.builder.is_block_used(self.value()) { | ||||||
|  |                 self.builder.delete_block(&self.value)?; | ||||||
|  |                 Ok(true) | ||||||
|  |             } else { | ||||||
|  |                 Ok(false) | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn value(&self) -> BlockValue { | ||||||
|  |         self.value | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Clone)] | ||||||
|  | pub struct InstructionData { | ||||||
|  |     kind: Instr, | ||||||
|  |     location: Option<DebugLocationValue>, | ||||||
|  |     meta: Option<DebugMetadataValue>, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Clone, Copy, Hash)] | ||||||
|  | pub enum CmpPredicate { | ||||||
|  |     LT, | ||||||
|  |     LE, | ||||||
|  |     GT, | ||||||
|  |     GE, | ||||||
|  |     EQ, | ||||||
|  |     NE, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// https://llvm.org/docs/LangRef.html#instruction-reference
 | ||||||
|  | #[derive(Clone)] | ||||||
|  | pub enum Instr { | ||||||
|  |     Param(usize), | ||||||
|  |     Constant(ConstValueKind), | ||||||
|  |     GetGlobal(GlobalValue), | ||||||
|  | 
 | ||||||
|  |     /// Add two integers
 | ||||||
|  |     Add(InstructionValue, InstructionValue), | ||||||
|  |     /// Add two floats
 | ||||||
|  |     FAdd(InstructionValue, InstructionValue), | ||||||
|  |     /// Subtract two integers
 | ||||||
|  |     Sub(InstructionValue, InstructionValue), | ||||||
|  |     /// Subtract two floats
 | ||||||
|  |     FSub(InstructionValue, InstructionValue), | ||||||
|  |     /// Multiply two integers
 | ||||||
|  |     Mul(InstructionValue, InstructionValue), | ||||||
|  |     /// Multiply two floats
 | ||||||
|  |     FMul(InstructionValue, InstructionValue), | ||||||
|  |     /// Divide two unsigned integers
 | ||||||
|  |     UDiv(InstructionValue, InstructionValue), | ||||||
|  |     /// Divide two signed integers
 | ||||||
|  |     SDiv(InstructionValue, InstructionValue), | ||||||
|  |     /// Divide two floats
 | ||||||
|  |     FDiv(InstructionValue, InstructionValue), | ||||||
|  |     /// Get the remainder from two unsigned integers
 | ||||||
|  |     URem(InstructionValue, InstructionValue), | ||||||
|  |     /// Get the remainder from two signed integers
 | ||||||
|  |     SRem(InstructionValue, InstructionValue), | ||||||
|  |     /// Get the remainder from two floats
 | ||||||
|  |     FRem(InstructionValue, InstructionValue), | ||||||
|  | 
 | ||||||
|  |     And(InstructionValue, InstructionValue), | ||||||
|  |     Or(InstructionValue, InstructionValue), | ||||||
|  |     XOr(InstructionValue, InstructionValue), | ||||||
|  |     ShiftRightLogical(InstructionValue, InstructionValue), | ||||||
|  |     ShiftRightArithmetic(InstructionValue, InstructionValue), | ||||||
|  |     ShiftLeft(InstructionValue, InstructionValue), | ||||||
|  | 
 | ||||||
|  |     Phi(Vec<InstructionValue>), | ||||||
|  | 
 | ||||||
|  |     Alloca(Type), | ||||||
|  |     Load(InstructionValue, Type), | ||||||
|  |     Store(InstructionValue, InstructionValue), | ||||||
|  |     ArrayAlloca(Type, InstructionValue), | ||||||
|  |     GetElemPtr(InstructionValue, Vec<InstructionValue>), | ||||||
|  |     GetStructElemPtr(InstructionValue, u32), | ||||||
|  |     ExtractValue(InstructionValue, u32), | ||||||
|  | 
 | ||||||
|  |     /// Integer Comparison
 | ||||||
|  |     ICmp(CmpPredicate, InstructionValue, InstructionValue), | ||||||
|  |     /// FLoat Comparison
 | ||||||
|  |     FCmp(CmpPredicate, InstructionValue, InstructionValue), | ||||||
|  | 
 | ||||||
|  |     /// The `trunc` instruction truncates the high order bits in value and
 | ||||||
|  |     /// converts the remaining bits to ty2. Since the source size must be larger
 | ||||||
|  |     /// than the destination size, `trunc` cannot be a no-op cast. It will
 | ||||||
|  |     /// always truncate bits.
 | ||||||
|  |     Trunc(InstructionValue, Type), | ||||||
|  |     /// The `zext` fills the high order bits of the value with zero bits until
 | ||||||
|  |     /// it reaches the size of the destination type, ty2.
 | ||||||
|  |     ZExt(InstructionValue, Type), | ||||||
|  |     /// The `sext` instruction performs a sign extension by copying the sign bit
 | ||||||
|  |     /// (highest order bit) of the value until it reaches the bit size of the
 | ||||||
|  |     /// type ty2.
 | ||||||
|  |     SExt(InstructionValue, Type), | ||||||
|  |     /// The `fptrunc` instruction casts a value from a larger floating-point
 | ||||||
|  |     /// type to a smaller floating-point type.
 | ||||||
|  |     FPTrunc(InstructionValue, Type), | ||||||
|  |     /// The `fpext` instruction extends the value from a smaller floating-point
 | ||||||
|  |     /// type to a larger floating-point type.
 | ||||||
|  |     FPExt(InstructionValue, Type), | ||||||
|  |     /// The `fptoui` instruction takes a value to cast, which must be a scalar
 | ||||||
|  |     /// or vector floating-point value, and a type to cast it to ty2, which must
 | ||||||
|  |     /// be an integer type.
 | ||||||
|  |     FPToUI(InstructionValue, Type), | ||||||
|  |     /// The `fptosi` instruction takes a value to cast, which must be a scalar
 | ||||||
|  |     /// or vector floating-point value, and a type to cast it to ty2, which must
 | ||||||
|  |     /// be an integer type.
 | ||||||
|  |     FPToSI(InstructionValue, Type), | ||||||
|  |     /// The `uitofp` instruction takes a value to cast, which must be a scalar
 | ||||||
|  |     /// or vector integer value, and a type to cast it to ty2, which must be an
 | ||||||
|  |     /// floating-point type.
 | ||||||
|  |     UIToFP(InstructionValue, Type), | ||||||
|  |     /// The `sitofp` instruction takes a value to cast, which must be a scalar
 | ||||||
|  |     /// or vector integer value, and a type to cast it to ty2, which must be an
 | ||||||
|  |     /// floating-point type
 | ||||||
|  |     SIToFP(InstructionValue, Type), | ||||||
|  |     /// The `ptrtoint` instruction converts value to integer type ty2 by
 | ||||||
|  |     /// interpreting the all pointer representation bits as an integer
 | ||||||
|  |     /// (equivalent to a bitcast) and either truncating or zero extending that
 | ||||||
|  |     /// value to the size of the integer type.
 | ||||||
|  |     PtrToInt(InstructionValue, Type), | ||||||
|  |     /// The `inttoptr` instruction converts value to type ty2 by applying either
 | ||||||
|  |     /// a zero extension or a truncation depending on the size of the integer
 | ||||||
|  |     /// value.
 | ||||||
|  |     IntToPtr(InstructionValue, Type), | ||||||
|  |     /// The `bitcast` instruction converts value to type ty2. It is always a
 | ||||||
|  |     /// no-op cast because no bits change with this conversion.
 | ||||||
|  |     BitCast(InstructionValue, Type), | ||||||
|  | 
 | ||||||
|  |     /// Check if the given instruction value is a null pointer
 | ||||||
|  |     IsNull(InstructionValue), | ||||||
|  | 
 | ||||||
|  |     FunctionCall(FunctionValue, Vec<InstructionValue>), | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Debug, PartialEq, Eq, Clone, Hash, PartialOrd)] | ||||||
|  | pub enum Type { | ||||||
|  |     I8, | ||||||
|  |     I16, | ||||||
|  |     I32, | ||||||
|  |     I64, | ||||||
|  |     I128, | ||||||
|  |     U8, | ||||||
|  |     U16, | ||||||
|  |     U32, | ||||||
|  |     U64, | ||||||
|  |     U128, | ||||||
|  |     F16, | ||||||
|  |     F32B, | ||||||
|  |     F32, | ||||||
|  |     F64, | ||||||
|  |     F80, | ||||||
|  |     F128, | ||||||
|  |     F128PPC, | ||||||
|  |     Bool, | ||||||
|  |     Void, | ||||||
|  |     CustomType(TypeValue), | ||||||
|  |     Array(Box<Type>, u64), | ||||||
|  |     Ptr(Box<Type>), | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Debug, Clone)] | ||||||
|  | pub enum ConstValueKind { | ||||||
|  |     I8(i8), | ||||||
|  |     I16(i16), | ||||||
|  |     I32(i32), | ||||||
|  |     I64(i64), | ||||||
|  |     I128(i128), | ||||||
|  |     U8(u8), | ||||||
|  |     U16(u16), | ||||||
|  |     U32(u32), | ||||||
|  |     U64(u64), | ||||||
|  |     U128(u128), | ||||||
|  |     Bool(bool), | ||||||
|  |     Str(String), | ||||||
|  |     F16(f32), | ||||||
|  |     F32B(f32), | ||||||
|  |     F32(f32), | ||||||
|  |     F64(f64), | ||||||
|  |     F80(f64), | ||||||
|  |     F128(f64), | ||||||
|  |     F128PPC(f64), | ||||||
|  |     Array(Vec<ConstantValue>, Type), | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Clone, Hash)] | ||||||
|  | pub enum TerminatorKind { | ||||||
|  |     Ret(InstructionValue), | ||||||
|  |     RetVoid, | ||||||
|  |     Br(BlockValue), | ||||||
|  |     CondBr(InstructionValue, BlockValue, BlockValue), | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Debug, PartialEq, Eq, Clone, Hash)] | ||||||
|  | pub struct TypeData { | ||||||
|  |     name: String, | ||||||
|  |     kind: CustomTypeKind, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Debug, PartialEq, Eq, Clone, Hash)] | ||||||
|  | pub enum CustomTypeKind { | ||||||
|  |     NamedStruct(NamedStruct), | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Debug, PartialEq, Eq, Clone, Hash)] | ||||||
|  | pub struct NamedStruct(pub String, pub Vec<Type>); | ||||||
|  | 
 | ||||||
|  | impl ConstValueKind { | ||||||
|  |     pub fn get_type(&self) -> Type { | ||||||
|  |         use Type::*; | ||||||
|  |         match self { | ||||||
|  |             ConstValueKind::I8(_) => I8, | ||||||
|  |             ConstValueKind::I16(_) => I16, | ||||||
|  |             ConstValueKind::I32(_) => I32, | ||||||
|  |             ConstValueKind::I64(_) => I64, | ||||||
|  |             ConstValueKind::I128(_) => I128, | ||||||
|  |             ConstValueKind::U8(_) => U8, | ||||||
|  |             ConstValueKind::U16(_) => U16, | ||||||
|  |             ConstValueKind::U32(_) => U32, | ||||||
|  |             ConstValueKind::U64(_) => U64, | ||||||
|  |             ConstValueKind::U128(_) => U128, | ||||||
|  |             ConstValueKind::Str(_) => Type::Ptr(Box::new(U8)), | ||||||
|  |             ConstValueKind::Bool(_) => Bool, | ||||||
|  |             ConstValueKind::F16(_) => F16, | ||||||
|  |             ConstValueKind::F32B(_) => F32B, | ||||||
|  |             ConstValueKind::F32(_) => F32, | ||||||
|  |             ConstValueKind::F64(_) => F64, | ||||||
|  |             ConstValueKind::F80(_) => F80, | ||||||
|  |             ConstValueKind::F128(_) => F128, | ||||||
|  |             ConstValueKind::F128PPC(_) => F128PPC, | ||||||
|  |             ConstValueKind::Array(vals, ty) => Type::Array(Box::new(ty.clone()), vals.len() as u64), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Clone)] | ||||||
|  | pub enum TypeCategory { | ||||||
|  |     SignedInteger, | ||||||
|  |     UnsignedInteger, | ||||||
|  |     Void, | ||||||
|  |     Real, | ||||||
|  |     Ptr, | ||||||
|  |     CustomType, | ||||||
|  |     Array, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl TypeCategory { | ||||||
|  |     pub fn comparable(&self) -> bool { | ||||||
|  |         match self { | ||||||
|  |             TypeCategory::SignedInteger => true, | ||||||
|  |             TypeCategory::UnsignedInteger => true, | ||||||
|  |             TypeCategory::Real => true, | ||||||
|  |             _ => false, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn signed(&self) -> bool { | ||||||
|  |         match self { | ||||||
|  |             TypeCategory::SignedInteger => true, | ||||||
|  |             _ => false, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn integer(&self) -> bool { | ||||||
|  |         match self { | ||||||
|  |             TypeCategory::SignedInteger => true, | ||||||
|  |             TypeCategory::UnsignedInteger => true, | ||||||
|  |             _ => false, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Type { | ||||||
|  |     pub fn category(&self) -> TypeCategory { | ||||||
|  |         match self { | ||||||
|  |             Type::I8 | Type::I16 | Type::I32 | Type::I64 | Type::I128 => TypeCategory::SignedInteger, | ||||||
|  |             Type::U8 | Type::U16 | Type::U32 | Type::U64 | Type::U128 => TypeCategory::UnsignedInteger, | ||||||
|  |             Type::F16 | Type::F32B | Type::F32 | Type::F64 | Type::F80 | Type::F128 | Type::F128PPC => { | ||||||
|  |                 TypeCategory::Real | ||||||
|  |             } | ||||||
|  |             Type::Bool => TypeCategory::UnsignedInteger, | ||||||
|  |             Type::Void => TypeCategory::Void, | ||||||
|  |             Type::CustomType(_) => TypeCategory::CustomType, | ||||||
|  |             Type::Array(_, _) => TypeCategory::Array, | ||||||
|  |             Type::Ptr(_) => TypeCategory::Ptr, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn cast_instruction(&self, value: InstructionValue, other: &Type) -> Option<Instr> { | ||||||
|  |         use Type::*; | ||||||
|  |         match (self, other) { | ||||||
|  |             (I8, I16 | I32 | I64 | I128) => Some(Instr::SExt(value, other.clone())), | ||||||
|  |             (I16, I32 | I64 | I128) => Some(Instr::SExt(value, other.clone())), | ||||||
|  |             (I32, I64 | I128) => Some(Instr::SExt(value, other.clone())), | ||||||
|  |             (I64, I128) => Some(Instr::SExt(value, other.clone())), | ||||||
|  |             (I128 | U128, I64 | U64 | I32 | U32 | I16 | U16 | I8 | U8) => Some(Instr::Trunc(value, other.clone())), | ||||||
|  |             (I64 | U64, I32 | U32 | I16 | U16 | I8 | U8) => Some(Instr::Trunc(value, other.clone())), | ||||||
|  |             (I32 | U32, I16 | U16 | I8 | U8) => Some(Instr::Trunc(value, other.clone())), | ||||||
|  |             (I16 | U16, I8 | U8) => Some(Instr::Trunc(value, other.clone())), | ||||||
|  |             (U8 | I8, U8 | I8 | U16 | I16 | U32 | I32 | U64 | I64 | U128 | I128) => { | ||||||
|  |                 Some(Instr::ZExt(value, other.clone())) | ||||||
|  |             } | ||||||
|  |             (U16 | I16, U16 | I16 | U32 | I32 | U64 | I64 | U128 | I128) => Some(Instr::ZExt(value, other.clone())), | ||||||
|  |             (U32 | I32, U32 | I32 | U64 | I64 | U128 | I128) => Some(Instr::ZExt(value, other.clone())), | ||||||
|  |             (U64 | I64, U64 | I64 | U128 | I128) => Some(Instr::ZExt(value, other.clone())), | ||||||
|  |             (U128 | I128, U128 | I128) => Some(Instr::ZExt(value, other.clone())), | ||||||
|  |             (U8 | U16 | U32 | U64 | U128, F16 | F32 | F32B | F64 | F80 | F128 | F128PPC) => { | ||||||
|  |                 Some(Instr::UIToFP(value, other.clone())) | ||||||
|  |             } | ||||||
|  |             (I8 | I16 | I32 | I64 | I128, F16 | F32 | F32B | F64 | F80 | F128 | F128PPC) => { | ||||||
|  |                 Some(Instr::SIToFP(value, other.clone())) | ||||||
|  |             } | ||||||
|  |             (F16 | F32 | F32B | F64 | F80 | F128 | F128PPC, U8 | U16 | U32 | U64 | U128) => { | ||||||
|  |                 Some(Instr::FPToUI(value, other.clone())) | ||||||
|  |             } | ||||||
|  |             (F16 | F32 | F32B | F64 | F80 | F128 | F128PPC, I8 | I16 | I32 | I64 | I128) => { | ||||||
|  |                 Some(Instr::FPToSI(value, other.clone())) | ||||||
|  |             } | ||||||
|  |             (I128 | U128 | I64 | U64 | I32 | U32 | I16 | U16 | I8 | U8, Ptr(_)) => { | ||||||
|  |                 Some(Instr::IntToPtr(value, other.clone())) | ||||||
|  |             } | ||||||
|  |             (Ptr(_), I128 | U128 | I64 | U64 | I32 | U32 | I16 | U16 | I8 | U8) => { | ||||||
|  |                 Some(Instr::PtrToInt(value, other.clone())) | ||||||
|  |             } | ||||||
|  |             (F16, F32 | F32B | F64 | F80 | F128 | F128PPC) => Some(Instr::FPExt(value, other.clone())), | ||||||
|  |             (F32 | F32B, F64 | F80 | F128 | F128PPC) => Some(Instr::FPExt(value, other.clone())), | ||||||
|  |             (F64, F80 | F128 | F128PPC) => Some(Instr::FPExt(value, other.clone())), | ||||||
|  |             (F80, F128 | F128PPC) => Some(Instr::FPExt(value, other.clone())), | ||||||
|  |             (F128PPC | F128, F80 | F64 | F32B | F32 | F16) => Some(Instr::FPTrunc(value, other.clone())), | ||||||
|  |             (F80, F64 | F32B | F32 | F16) => Some(Instr::FPTrunc(value, other.clone())), | ||||||
|  |             (F64, F32B | F32 | F16) => Some(Instr::FPTrunc(value, other.clone())), | ||||||
|  |             (F32B | F32, F16) => Some(Instr::FPTrunc(value, other.clone())), | ||||||
|  |             _ => None, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl TerminatorKind { | ||||||
|  |     pub(crate) fn get_type(&self, builder: &Builder) -> CompileResult<Type> { | ||||||
|  |         use TerminatorKind::*; | ||||||
|  |         match self { | ||||||
|  |             Ret(instr_val) => instr_val.get_type(builder), | ||||||
|  |             RetVoid => Ok(Type::Void), | ||||||
|  |             Br(_) => Ok(Type::Void), | ||||||
|  |             CondBr(_, _, _) => Ok(Type::Void), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										69
									
								
								src/pad_adapter.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								src/pad_adapter.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,69 @@ | |||||||
|  | //! Copied from
 | ||||||
|  | //! https://github.com/rust-lang/rust/blob/6b3ae3f6e45a33c2d95fa0362c9b2593e567fd34/library/core/src/fmt/builders.rs#L102
 | ||||||
|  | 
 | ||||||
|  | // Copyright (c) The Rust Project Contributors
 | ||||||
|  | //
 | ||||||
|  | // Permission is hereby granted, free of charge, to any person obtaining a copy
 | ||||||
|  | // of this software and associated documentation files (the "Software"), to deal
 | ||||||
|  | // in the Software without restriction, including without limitation the rights
 | ||||||
|  | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | ||||||
|  | // copies of the Software, and to permit persons to whom the Software is
 | ||||||
|  | // furnished to do so, subject to the following conditions:
 | ||||||
|  | //
 | ||||||
|  | // The above copyright notice and this permission notice shall be included in
 | ||||||
|  | // all copies or substantial portions of the Software.
 | ||||||
|  | //
 | ||||||
|  | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | ||||||
|  | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | ||||||
|  | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | ||||||
|  | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | ||||||
|  | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | ||||||
|  | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 | ||||||
|  | // SOFTWARE.
 | ||||||
|  | 
 | ||||||
|  | use std::fmt; | ||||||
|  | 
 | ||||||
|  | pub struct PadAdapter<'buf, 'state> { | ||||||
|  |     buf: &'buf mut (dyn fmt::Write + 'buf), | ||||||
|  |     state: &'state mut PadAdapterState, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub struct PadAdapterState { | ||||||
|  |     on_newline: bool, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Default for PadAdapterState { | ||||||
|  |     fn default() -> Self { | ||||||
|  |         PadAdapterState { on_newline: true } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<'buf, 'state> PadAdapter<'buf, 'state> { | ||||||
|  |     pub fn wrap<'slot, 'fmt: 'buf + 'slot>( | ||||||
|  |         fmt: &'buf mut (dyn fmt::Write + 'buf), | ||||||
|  |         state: &'state mut PadAdapterState, | ||||||
|  |     ) -> Self { | ||||||
|  |         PadAdapter { buf: fmt, state } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl fmt::Write for PadAdapter<'_, '_> { | ||||||
|  |     fn write_str(&mut self, s: &str) -> fmt::Result { | ||||||
|  |         for s in s.split_inclusive('\n') { | ||||||
|  |             if self.state.on_newline { | ||||||
|  |                 self.buf.write_str("    ")?; | ||||||
|  |             } | ||||||
|  |             self.state.on_newline = s.ends_with('\n'); | ||||||
|  |             self.buf.write_str(s)?; | ||||||
|  |         } | ||||||
|  |         Ok(()) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn write_char(&mut self, c: char) -> fmt::Result { | ||||||
|  |         if self.state.on_newline { | ||||||
|  |             self.buf.write_str("    ")?; | ||||||
|  |         } | ||||||
|  |         self.state.on_newline = c == '\n'; | ||||||
|  |         self.buf.write_char(c) | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										115
									
								
								src/util.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										115
									
								
								src/util.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,115 @@ | |||||||
|  | use std::{ | ||||||
|  |     ffi::{CStr, CString, c_char}, | ||||||
|  |     ptr::null_mut, | ||||||
|  |     string::FromUtf8Error, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | use llvm_sys::{ | ||||||
|  |     core::{LLVMCreateMemoryBufferWithMemoryRange, LLVMDisposeMemoryBuffer, LLVMGetBufferSize, LLVMGetBufferStart}, | ||||||
|  |     error::LLVMDisposeErrorMessage, | ||||||
|  |     prelude::LLVMMemoryBufferRef, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | use crate::{ | ||||||
|  |     CompileResult, ErrorKind, Type, | ||||||
|  |     builder::{Builder, InstructionValue}, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | pub fn into_cstring<T: Into<String>>(value: T) -> CString { | ||||||
|  |     let string = value.into(); | ||||||
|  |     unsafe { CString::from_vec_with_nul_unchecked((string + "\0").into_bytes()) } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub fn from_cstring(pointer: *mut c_char) -> Option<String> { | ||||||
|  |     if pointer.is_null() { | ||||||
|  |         None | ||||||
|  |     } else { | ||||||
|  |         unsafe { CStr::from_ptr(pointer).to_str().ok().map(|s| s.to_owned()) } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn cstring_to_err(value: *mut c_char) -> Result<(), String> { | ||||||
|  |     from_cstring(value).filter(|s| !s.is_empty()).map_or(Ok(()), |s| Err(s)) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// Utility struct for LLVM's Error Messages, which need to be disposed
 | ||||||
|  | /// manually.
 | ||||||
|  | pub struct ErrorMessageHolder(*mut c_char); | ||||||
|  | 
 | ||||||
|  | impl ErrorMessageHolder { | ||||||
|  |     pub fn null() -> Self { | ||||||
|  |         ErrorMessageHolder(null_mut()) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn borrow_mut(&mut self) -> *mut *mut c_char { | ||||||
|  |         &mut self.0 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn into_result(&self) -> Result<(), String> { | ||||||
|  |         cstring_to_err(self.0) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Drop for ErrorMessageHolder { | ||||||
|  |     fn drop(&mut self) { | ||||||
|  |         unsafe { | ||||||
|  |             if !self.0.is_null() { | ||||||
|  |                 LLVMDisposeErrorMessage(self.0); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// Utility for creating and handling LLVM MemoryBuffers, needed for printing
 | ||||||
|  | /// out ASM and .o -files without relying on LLVM's own API.
 | ||||||
|  | pub struct MemoryBufferHolder { | ||||||
|  |     pub buffer: LLVMMemoryBufferRef, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl MemoryBufferHolder { | ||||||
|  |     pub fn empty(name: &str) -> MemoryBufferHolder { | ||||||
|  |         let array = [0i8; 0]; | ||||||
|  |         unsafe { | ||||||
|  |             let buffer = | ||||||
|  |                 LLVMCreateMemoryBufferWithMemoryRange(array.as_ptr(), array.len(), into_cstring(name).as_ptr(), 0); | ||||||
|  |             MemoryBufferHolder { buffer } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn as_buffer(&self) -> Vec<u8> { | ||||||
|  |         unsafe { | ||||||
|  |             let start = LLVMGetBufferStart(self.buffer); | ||||||
|  |             let size = LLVMGetBufferSize(self.buffer); | ||||||
|  | 
 | ||||||
|  |             let mut buff = Vec::with_capacity(size); | ||||||
|  |             for i in 0..size { | ||||||
|  |                 buff.push(*start.add(i) as u8); | ||||||
|  |             } | ||||||
|  |             buff | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn as_string(&self) -> Result<String, FromUtf8Error> { | ||||||
|  |         String::from_utf8(self.as_buffer()) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Drop for MemoryBufferHolder { | ||||||
|  |     fn drop(&mut self) { | ||||||
|  |         unsafe { | ||||||
|  |             LLVMDisposeMemoryBuffer(self.buffer); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// Make sure types for given instructions match. Return Ok(type) if they do,
 | ||||||
|  | /// and error otherwise.
 | ||||||
|  | pub fn match_types(lhs: &InstructionValue, rhs: &InstructionValue, builder: &Builder) -> CompileResult<Type> { | ||||||
|  |     let lhs_t = lhs.get_type(&builder)?; | ||||||
|  |     let rhs_t = rhs.get_type(&builder)?; | ||||||
|  |     if lhs_t == rhs_t { | ||||||
|  |         Ok(lhs_t) | ||||||
|  |     } else { | ||||||
|  |         Err(ErrorKind::TypesIncompatible(lhs_t, rhs_t)) | ||||||
|  |     } | ||||||
|  | } | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user