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