Add rudamentary LLVM lib stuff, make a fully compiling executable
This commit is contained in:
		
							parent
							
								
									6d3d0fd03e
								
							
						
					
					
						commit
						5f93b7c9c2
					
				
							
								
								
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -2,4 +2,6 @@ | ||||
| src/old_llvm | ||||
| /target | ||||
| /.vscode | ||||
| .env | ||||
| .env | ||||
| hello.* | ||||
| main | ||||
							
								
								
									
										10
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										10
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							| @ -1,6 +1,6 @@ | ||||
| # This file is automatically @generated by Cargo. | ||||
| # It is not intended for manual editing. | ||||
| version = 3 | ||||
| version = 4 | ||||
| 
 | ||||
| [[package]] | ||||
| name = "aho-corasick" | ||||
| @ -103,6 +103,14 @@ dependencies = [ | ||||
|  "thiserror", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "reid-lib" | ||||
| version = "0.1.0" | ||||
| dependencies = [ | ||||
|  "llvm-sys", | ||||
|  "thiserror", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "semver" | ||||
| version = "1.0.18" | ||||
|  | ||||
| @ -1,4 +1,5 @@ | ||||
| [workspace] | ||||
| members = [ | ||||
|     "reid" | ||||
|     "reid", | ||||
|     "reid-llvm-lib" | ||||
| ] | ||||
							
								
								
									
										11
									
								
								libtest.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										11
									
								
								libtest.sh
									
									
									
									
									
										Executable file
									
								
							| @ -0,0 +1,11 @@ | ||||
| #!/bin/sh | ||||
| 
 | ||||
| # Compiles example libtest, which produces hello.o and hello.asm, which is then | ||||
| # compiled with main.cpp and executed for final result | ||||
| # | ||||
| # Do note this file is extremely simply for my own personal convenience | ||||
| 
 | ||||
| export .env | ||||
| cargo run --example libtest && \ | ||||
| clang++ main.cpp hello.o -o main && \ | ||||
| ./main | ||||
							
								
								
									
										7
									
								
								main.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								main.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,7 @@ | ||||
| #include <iostream> | ||||
| 
 | ||||
| extern "C" { | ||||
| int mainfunc(); | ||||
| } | ||||
| 
 | ||||
| int main() { std::cout << "Return value of test: " << mainfunc() << std::endl; } | ||||
							
								
								
									
										12
									
								
								reid-llvm-lib/Cargo.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								reid-llvm-lib/Cargo.toml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,12 @@ | ||||
| [package] | ||||
| name = "reid-lib" | ||||
| version = "0.1.0" | ||||
| edition = "2024" | ||||
| 
 | ||||
| # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | ||||
| 
 | ||||
| [dependencies] | ||||
| ## LLVM Bindings | ||||
| llvm-sys = "160" | ||||
| ## Make it easier to generate errors | ||||
| thiserror = "1.0.44" | ||||
							
								
								
									
										25
									
								
								reid-llvm-lib/examples/libtest.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								reid-llvm-lib/examples/libtest.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,25 @@ | ||||
| use reid_lib::*; | ||||
| 
 | ||||
| pub fn main() { | ||||
|     let context = IRContext::new(); | ||||
|     let module = IRModule::new(&context, &"hello".to_owned()); | ||||
| 
 | ||||
|     let mainfunc = IRFunction::new(&module, &"mainfunc".to_owned()); | ||||
| 
 | ||||
|     let block = IRBlock::new(&context, &"mainblock".to_owned()); | ||||
| 
 | ||||
|     let lhs_cmp = IRValue::from_const(&context, 100); | ||||
|     let rhs_cmp = IRValue::from_const(&context, 200); | ||||
| 
 | ||||
|     let compare = block.less_than(lhs_cmp.into(), rhs_cmp.into()).unwrap(); | ||||
| 
 | ||||
|     let (lhs, rhs) = block.cond_br(&mainfunc, compare); | ||||
| 
 | ||||
|     lhs.ret(&mainfunc, IRValue::from_const(&context, 123).into()); | ||||
|     rhs.ret(&mainfunc, IRValue::from_const(&context, 456).into()); | ||||
| 
 | ||||
|     match module.print_to_string() { | ||||
|         Ok(v) => println!("{}", v), | ||||
|         Err(e) => println!("Err: {:?}", e), | ||||
|     } | ||||
| } | ||||
							
								
								
									
										319
									
								
								reid-llvm-lib/src/lib.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										319
									
								
								reid-llvm-lib/src/lib.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,319 @@ | ||||
| use std::ffi::{CStr, CString, c_char}; | ||||
| use std::marker::PhantomData; | ||||
| use std::mem; | ||||
| use std::ptr::{null, null_mut}; | ||||
| 
 | ||||
| use llvm_sys::analysis::LLVMVerifyModule; | ||||
| use llvm_sys::target::{ | ||||
|     LLVM_InitializeAllAsmParsers, LLVM_InitializeAllAsmPrinters, LLVM_InitializeAllTargetInfos, | ||||
|     LLVM_InitializeAllTargetMCs, LLVM_InitializeAllTargets, LLVM_InitializeNativeAsmParser, | ||||
|     LLVM_InitializeNativeTarget, LLVMInitializeAMDGPUAsmPrinter, LLVMInitializeX86Target, | ||||
|     LLVMInitializeX86TargetInfo, LLVMInitializeX86TargetMC, LLVMSetModuleDataLayout, | ||||
| }; | ||||
| use llvm_sys::target_machine::{ | ||||
|     LLVMCodeGenFileType, LLVMCreateTargetDataLayout, LLVMCreateTargetMachine, | ||||
|     LLVMGetDefaultTargetTriple, LLVMGetFirstTarget, LLVMGetHostCPUFeatures, LLVMGetHostCPUName, | ||||
|     LLVMGetTargetFromTriple, LLVMTargetMachineEmitToFile, LLVMTargetMachineEmitToMemoryBuffer, | ||||
| }; | ||||
| use llvm_sys::transforms::pass_manager_builder::{ | ||||
|     LLVMPassManagerBuilderCreate, LLVMPassManagerBuilderPopulateModulePassManager, | ||||
|     LLVMPassManagerBuilderSetOptLevel, | ||||
| }; | ||||
| use llvm_sys::{ | ||||
|     LLVMBasicBlock, LLVMBuilder, LLVMContext, LLVMModule, LLVMType, LLVMValue, core::*, prelude::*, | ||||
| }; | ||||
| 
 | ||||
| fn into_cstring<T: Into<String>>(value: T) -> CString { | ||||
|     let string = value.into(); | ||||
|     unsafe { CString::from_vec_with_nul_unchecked((string + "\0").into_bytes()) } | ||||
| } | ||||
| 
 | ||||
| fn from_cstring(value: *mut c_char) -> Option<String> { | ||||
|     if value.is_null() { | ||||
|         None | ||||
|     } else { | ||||
|         unsafe { CString::from_raw(value).into_string().ok() } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fn cstring_to_err(value: *mut c_char) -> Result<(), String> { | ||||
|     from_cstring(value) | ||||
|         .filter(|s| !s.is_empty()) | ||||
|         .map_or(Ok(()), |s| Err(s)) | ||||
| } | ||||
| 
 | ||||
| pub trait IRType { | ||||
|     const SIGNED: LLVMBool; | ||||
|     unsafe fn llvm_type(context: &IRContext) -> LLVMTypeRef; | ||||
| } | ||||
| 
 | ||||
| impl IRType for bool { | ||||
|     const SIGNED: LLVMBool = 0; | ||||
|     unsafe fn llvm_type(context: &IRContext) -> LLVMTypeRef { | ||||
|         unsafe { LLVMInt1TypeInContext(context.context) } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl IRType for i32 { | ||||
|     const SIGNED: LLVMBool = 1; | ||||
|     unsafe fn llvm_type(context: &IRContext) -> LLVMTypeRef { | ||||
|         unsafe { LLVMInt32TypeInContext(context.context) } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub struct IRValue<T: IRType>(PhantomData<T>, IROpaqueValue); | ||||
| 
 | ||||
| impl<T: IRType> IRValue<T> { | ||||
|     unsafe fn from_runtime(t: LLVMTypeRef, value: LLVMValueRef) -> IRValue<T> { | ||||
|         IRValue(PhantomData, IROpaqueValue(t, value)) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: IRType + Into<i64>> IRValue<T> { | ||||
|     pub fn from_const(context: &IRContext, value: T) -> Self { | ||||
|         unsafe { | ||||
|             let t = T::llvm_type(context); | ||||
|             let value = LLVMConstInt(t, value.into() as u64, T::SIGNED); | ||||
|             IRValue(PhantomData, IROpaqueValue(t, value)) | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: IRType> From<IRValue<T>> for IROpaqueValue { | ||||
|     fn from(value: IRValue<T>) -> Self { | ||||
|         value.1 | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub struct IROpaqueValue(LLVMTypeRef, LLVMValueRef); | ||||
| 
 | ||||
| pub struct IRContext { | ||||
|     context: *mut LLVMContext, | ||||
|     builder: *mut LLVMBuilder, | ||||
| } | ||||
| 
 | ||||
| impl IRContext { | ||||
|     pub fn new() -> IRContext { | ||||
|         unsafe { | ||||
|             // Set up a context, module and builder in that context.
 | ||||
|             let context = LLVMContextCreate(); | ||||
|             let builder = LLVMCreateBuilderInContext(context); | ||||
| 
 | ||||
|             IRContext { context, builder } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Drop for IRContext { | ||||
|     fn drop(&mut self) { | ||||
|         // Clean up. Values created in the context mostly get cleaned up there.
 | ||||
|         unsafe { | ||||
|             LLVMDisposeBuilder(self.builder); | ||||
|             LLVMContextDispose(self.context); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub struct IRModule<'a> { | ||||
|     context: &'a IRContext, | ||||
|     module: *mut LLVMModule, | ||||
| } | ||||
| 
 | ||||
| impl<'a> IRModule<'a> { | ||||
|     pub fn new(context: &'a IRContext, name: &String) -> IRModule<'a> { | ||||
|         unsafe { | ||||
|             let module = | ||||
|                 LLVMModuleCreateWithNameInContext(into_cstring(name).as_ptr(), context.context); | ||||
| 
 | ||||
|             IRModule { context, module } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn print_to_string(&self) -> Result<&str, String> { | ||||
|         unsafe { | ||||
|             // let pmb = LLVMPassManagerBuilderCreate();
 | ||||
|             // LLVMPassManagerBuilderSetOptLevel(pmb, 0);
 | ||||
|             // let pm = LLVMCreatePassManager();
 | ||||
|             // LLVMPassManagerBuilderPopulateModulePassManager(pmb, pm);
 | ||||
|             // println!("{}", LLVMRunPassManager(pm, self.module));
 | ||||
| 
 | ||||
|             LLVM_InitializeAllTargets(); | ||||
|             LLVM_InitializeAllTargetInfos(); | ||||
|             LLVM_InitializeAllTargetMCs(); | ||||
|             LLVM_InitializeAllAsmParsers(); | ||||
|             LLVM_InitializeAllAsmPrinters(); | ||||
| 
 | ||||
|             let triple = LLVMGetDefaultTargetTriple(); | ||||
| 
 | ||||
|             let mut target: _ = null_mut(); | ||||
|             let mut err: _ = null_mut(); | ||||
|             LLVMGetTargetFromTriple(c"x86_64-unknown-linux-gnu".as_ptr(), &mut target, &mut err); | ||||
|             println!("{:?}, {:?}", from_cstring(triple), target); | ||||
|             cstring_to_err(err).unwrap(); | ||||
| 
 | ||||
|             let target_machine = LLVMCreateTargetMachine( | ||||
|                 target, | ||||
|                 triple, | ||||
|                 c"generic".as_ptr(), | ||||
|                 c"".as_ptr(), | ||||
|                 llvm_sys::target_machine::LLVMCodeGenOptLevel::LLVMCodeGenLevelNone, | ||||
|                 llvm_sys::target_machine::LLVMRelocMode::LLVMRelocDefault, | ||||
|                 llvm_sys::target_machine::LLVMCodeModel::LLVMCodeModelDefault, | ||||
|             ); | ||||
| 
 | ||||
|             let data_layout = LLVMCreateTargetDataLayout(target_machine); | ||||
|             LLVMSetModuleDataLayout(self.module, data_layout); | ||||
| 
 | ||||
|             let mut err = null_mut(); | ||||
|             LLVMVerifyModule( | ||||
|                 self.module, | ||||
|                 llvm_sys::analysis::LLVMVerifierFailureAction::LLVMPrintMessageAction, | ||||
|                 &mut err, | ||||
|             ); | ||||
|             cstring_to_err(err).unwrap(); | ||||
| 
 | ||||
|             let mut err = null_mut(); | ||||
|             LLVMTargetMachineEmitToFile( | ||||
|                 target_machine, | ||||
|                 self.module, | ||||
|                 CString::new("hello.asm").unwrap().into_raw(), | ||||
|                 LLVMCodeGenFileType::LLVMAssemblyFile, | ||||
|                 &mut err, | ||||
|             ); | ||||
|             cstring_to_err(err).unwrap(); | ||||
| 
 | ||||
|             let mut err = null_mut(); | ||||
|             LLVMTargetMachineEmitToFile( | ||||
|                 target_machine, | ||||
|                 self.module, | ||||
|                 CString::new("hello.o").unwrap().into_raw(), | ||||
|                 LLVMCodeGenFileType::LLVMObjectFile, | ||||
|                 &mut err, | ||||
|             ); | ||||
|             cstring_to_err(err).unwrap(); | ||||
| 
 | ||||
|             Ok(CStr::from_ptr(LLVMPrintModuleToString(self.module)) | ||||
|                 .to_str() | ||||
|                 .expect("UTF8-err")) | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<'a> Drop for IRModule<'a> { | ||||
|     fn drop(&mut self) { | ||||
|         // Clean up. Values created in the context mostly get cleaned up there.
 | ||||
|         unsafe { | ||||
|             LLVMDisposeModule(self.module); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub struct IRFunction<'a> { | ||||
|     pub module: &'a IRModule<'a>, | ||||
|     pub functionref: *mut LLVMValue, | ||||
| } | ||||
| 
 | ||||
| impl<'a> IRFunction<'a> { | ||||
|     pub fn new(module: &'a IRModule<'a>, name: &String) -> IRFunction<'a> { | ||||
|         unsafe { | ||||
|             // TODO, fix later!
 | ||||
|             let return_type = LLVMInt32TypeInContext(module.context.context); | ||||
|             let mut argts = []; | ||||
|             let func_type = | ||||
|                 LLVMFunctionType(return_type, argts.as_mut_ptr(), argts.len() as u32, 0); | ||||
| 
 | ||||
|             let functionref = | ||||
|                 LLVMAddFunction(module.module, into_cstring(name).as_ptr(), func_type); | ||||
| 
 | ||||
|             IRFunction { | ||||
|                 module, | ||||
|                 functionref, | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub struct IRBlock<'a> { | ||||
|     context: &'a IRContext, | ||||
|     blockref: *mut LLVMBasicBlock, | ||||
|     inserted: bool, | ||||
| } | ||||
| 
 | ||||
| impl<'a> IRBlock<'a> { | ||||
|     pub fn new(context: &'a IRContext, name: &String) -> IRBlock<'a> { | ||||
|         unsafe { | ||||
|             let blockref = | ||||
|                 LLVMCreateBasicBlockInContext(context.context, into_cstring(name).as_ptr()); | ||||
| 
 | ||||
|             IRBlock { | ||||
|                 context, | ||||
|                 blockref, | ||||
|                 inserted: false, | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn less_than(&self, lhs: IROpaqueValue, rhs: IROpaqueValue) -> Result<IRValue<bool>, ()> { | ||||
|         let IROpaqueValue(t1, lhs) = lhs; | ||||
|         let IROpaqueValue(t2, rhs) = rhs; | ||||
| 
 | ||||
|         if t1 != t2 { | ||||
|             Err(()) | ||||
|         } else { | ||||
|             unsafe { | ||||
|                 let builder = self.context.builder; | ||||
|                 LLVMPositionBuilderAtEnd(builder, self.blockref); | ||||
|                 let value = LLVMBuildICmp( | ||||
|                     builder, | ||||
|                     llvm_sys::LLVMIntPredicate::LLVMIntSLT, | ||||
|                     lhs, | ||||
|                     rhs, | ||||
|                     c"asd".as_ptr(), | ||||
|                 ); | ||||
|                 Ok(IRValue::from_runtime(bool::llvm_type(&self.context), value)) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn cond_br( | ||||
|         self, | ||||
|         function: &IRFunction, | ||||
|         value: IRValue<bool>, | ||||
|     ) -> (IRBlock<'a>, IRBlock<'a>) { | ||||
|         let lhs = IRBlock::new(self.context, &"lhs".to_owned()); | ||||
|         let rhs = IRBlock::new(self.context, &"rhs".to_owned()); | ||||
|         unsafe { | ||||
|             let builder = self.context.builder; | ||||
|             LLVMPositionBuilderAtEnd(builder, self.blockref); | ||||
|             LLVMBuildCondBr(builder, value.1.1, lhs.blockref, rhs.blockref); | ||||
|             self.append(function); | ||||
|             (lhs, rhs) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn ret(self, function: &IRFunction, value: IROpaqueValue) { | ||||
|         unsafe { | ||||
|             let builder = self.context.builder; | ||||
|             LLVMPositionBuilderAtEnd(builder, self.blockref); | ||||
|             LLVMBuildRet(builder, value.1); | ||||
|             self.append(function); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     unsafe fn append(mut self, function: &IRFunction<'a>) { | ||||
|         unsafe { | ||||
|             LLVMAppendExistingBasicBlock(function.functionref, self.blockref); | ||||
|             self.inserted = true; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<'a> Drop for IRBlock<'a> { | ||||
|     fn drop(&mut self) { | ||||
|         unsafe { | ||||
|             if !self.inserted { | ||||
|                 LLVMDeleteBasicBlock(self.blockref); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -4,8 +4,12 @@ use std::mem; | ||||
| use std::ptr::null_mut; | ||||
| 
 | ||||
| use llvm_sys::analysis::LLVMVerifyModule; | ||||
| use llvm_sys::transforms::pass_manager_builder::{ | ||||
|     self, LLVMOpaquePassManagerBuilder, LLVMPassManagerBuilderCreate, | ||||
|     LLVMPassManagerBuilderSetOptLevel, | ||||
| }; | ||||
| use llvm_sys::{ | ||||
|     core::*, prelude::*, LLVMBasicBlock, LLVMBuilder, LLVMContext, LLVMModule, LLVMType, LLVMValue, | ||||
|     LLVMBasicBlock, LLVMBuilder, LLVMContext, LLVMModule, LLVMType, LLVMValue, core::*, prelude::*, | ||||
| }; | ||||
| 
 | ||||
| use crate::ast; | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user