diff --git a/.gitignore b/.gitignore index a73c16d..b8c37e2 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,7 @@ src/old_llvm /.vscode .env hello.* -main \ No newline at end of file +main +reid_src/*.o +reid_src/*.ll +reid_src/*.asm diff --git a/reid-llvm-lib/src/compile.rs b/reid-llvm-lib/src/compile.rs index 0c433c8..48860b8 100644 --- a/reid-llvm-lib/src/compile.rs +++ b/reid-llvm-lib/src/compile.rs @@ -2,7 +2,7 @@ //! LLIR ([`Context`]) into LLVM IR. This module is the only one that interfaces //! with the LLVM API. -use std::{collections::HashMap, ffi::CString, ptr::null_mut}; +use std::{collections::HashMap, ptr::null_mut}; use llvm_sys::{ LLVMIntPredicate, LLVMLinkage, @@ -16,11 +16,11 @@ use llvm_sys::{ }, target_machine::{ LLVMCodeGenFileType, LLVMCreateTargetDataLayout, LLVMCreateTargetMachine, - LLVMGetDefaultTargetTriple, LLVMGetTargetFromTriple, LLVMTargetMachineEmitToFile, + LLVMGetDefaultTargetTriple, LLVMGetTargetFromTriple, LLVMTargetMachineEmitToMemoryBuffer, }, }; -use crate::util::{ErrorMessageHolder, from_cstring, into_cstring}; +use crate::util::{ErrorMessageHolder, MemoryBufferHolder, from_cstring, into_cstring}; use super::{ CmpPredicate, ConstValue, Context, TerminatorKind, Type, @@ -49,8 +49,15 @@ pub struct CompiledModule { _context: LLVMContext, } +pub struct CompileOutput { + pub triple: String, + pub assembly: String, + pub obj_buffer: Vec, + pub llvm_ir: String, +} + impl CompiledModule { - pub fn output(&self) { + pub fn output(&self) -> CompileOutput { unsafe { LLVM_InitializeAllTargets(); LLVM_InitializeAllTargetInfos(); @@ -63,7 +70,6 @@ impl CompiledModule { let mut target: _ = null_mut(); let mut err = ErrorMessageHolder::null(); LLVMGetTargetFromTriple(triple, &mut target, err.borrow_mut()); - println!("{:?}, {:?}", from_cstring(triple), target); err.into_result().unwrap(); let target_machine = LLVMCreateTargetMachine( @@ -88,28 +94,37 @@ impl CompiledModule { ); err.into_result().unwrap(); + let mut asm_buffer = MemoryBufferHolder::empty("asm"); let mut err = ErrorMessageHolder::null(); - LLVMTargetMachineEmitToFile( + LLVMTargetMachineEmitToMemoryBuffer( target_machine, self.module_ref, - CString::new("hello.asm").unwrap().into_raw(), LLVMCodeGenFileType::LLVMAssemblyFile, err.borrow_mut(), + &mut asm_buffer.buffer, ); err.into_result().unwrap(); + let mut obj_buffer = MemoryBufferHolder::empty("obj"); let mut err = ErrorMessageHolder::null(); - LLVMTargetMachineEmitToFile( + LLVMTargetMachineEmitToMemoryBuffer( target_machine, self.module_ref, - CString::new("hello.o").unwrap().into_raw(), LLVMCodeGenFileType::LLVMObjectFile, err.borrow_mut(), + &mut obj_buffer.buffer, ); err.into_result().unwrap(); - let module_str = from_cstring(LLVMPrintModuleToString(self.module_ref)); - println!("{}", module_str.unwrap()); + CompileOutput { + triple: from_cstring(triple).expect("Unable to convert triple from cstring"), + assembly: asm_buffer + .as_string() + .expect("Error while converting assembly-buffer to string"), + obj_buffer: obj_buffer.as_buffer(), + llvm_ir: from_cstring(LLVMPrintModuleToString(self.module_ref)) + .expect("Unable to print LLVM IR to string"), + } } } } diff --git a/reid-llvm-lib/src/util.rs b/reid-llvm-lib/src/util.rs index 7319d1b..bb405b8 100644 --- a/reid-llvm-lib/src/util.rs +++ b/reid-llvm-lib/src/util.rs @@ -1,9 +1,17 @@ use std::{ ffi::{CStr, CString, c_char}, ptr::null_mut, + string::FromUtf8Error, }; -use llvm_sys::error::LLVMDisposeErrorMessage; +use llvm_sys::{ + core::{ + LLVMCreateMemoryBufferWithMemoryRange, LLVMDisposeMemoryBuffer, LLVMGetBufferSize, + LLVMGetBufferStart, + }, + error::LLVMDisposeErrorMessage, + prelude::LLVMMemoryBufferRef, +}; use crate::{ Type, @@ -57,6 +65,52 @@ impl Drop for ErrorMessageHolder { } } +/// Utility for creating and handling LLVM MemoryBuffers, needed for printing +/// out ASM and .o -files without relying on LLVM's own API. +pub struct MemoryBufferHolder { + pub buffer: LLVMMemoryBufferRef, +} + +impl MemoryBufferHolder { + pub fn empty(name: &str) -> MemoryBufferHolder { + let array = [0i8; 0]; + unsafe { + let buffer = LLVMCreateMemoryBufferWithMemoryRange( + array.as_ptr(), + array.len(), + into_cstring(name).as_ptr(), + 0, + ); + MemoryBufferHolder { buffer } + } + } + + pub fn as_buffer(&self) -> Vec { + unsafe { + let start = LLVMGetBufferStart(self.buffer); + let size = LLVMGetBufferSize(self.buffer); + + let mut buff = Vec::with_capacity(size); + for i in 0..size { + buff.push(*start.add(i) as u8); + } + buff + } + } + + pub fn as_string(&self) -> Result { + String::from_utf8(self.as_buffer()) + } +} + +impl Drop for MemoryBufferHolder { + fn drop(&mut self) { + unsafe { + LLVMDisposeMemoryBuffer(self.buffer); + } + } +} + /// Make sure types for given instructions match. Return Ok(type) if they do, /// and error otherwise. pub fn match_types( diff --git a/reid/examples/cli.rs b/reid/examples/cli.rs index 9a8c8fa..daf8c6a 100644 --- a/reid/examples/cli.rs +++ b/reid/examples/cli.rs @@ -1,18 +1,37 @@ use std::{env, fs, path::PathBuf}; use reid::compile; +use reid_lib::compile::CompileOutput; fn main() -> Result<(), std::io::Error> { let args: Vec = env::args().collect(); if let Some(filename) = args.get(1) { - let path = PathBuf::from(filename); + let path = PathBuf::from(filename).canonicalize().unwrap(); + let parent = path.with_extension(""); + let llvm_ir_path = parent.with_extension("ll"); + let object_path = parent.with_extension("o"); + let asm_path = parent.with_extension("asm"); let text = fs::read_to_string(&path)?; - let output = match compile(&text, PathBuf::from(path)) { - Ok(t) => t, + match compile(&text, PathBuf::from(&path)) { + Ok(CompileOutput { + triple, + assembly, + obj_buffer, + llvm_ir, + }) => { + println!("Compiled with triple: {}", &triple); + fs::write(&llvm_ir_path, &llvm_ir).expect("Could not write LLVM IR -file!"); + println!("Output LLVM IR to {:?}", llvm_ir_path); + fs::write(&asm_path, &assembly).expect("Could not write Assembly-file!"); + println!("Output Assembly to {:?}", asm_path); + fs::write(&object_path, &obj_buffer).expect("Could not write Object-file!"); + println!("Output Object-file to {:?}", object_path); + + println!("{}", llvm_ir); + } Err(e) => panic!("{}", e), }; - println!("{}", output); } else { println!("Please input compiled file path!") } diff --git a/reid/src/lib.rs b/reid/src/lib.rs index 2ae89d7..372bf93 100644 --- a/reid/src/lib.rs +++ b/reid/src/lib.rs @@ -46,7 +46,7 @@ use std::path::PathBuf; use mir::{ linker::LinkerPass, typecheck::TypeCheck, typeinference::TypeInference, typerefs::TypeRefs, }; -use reid_lib::Context; +use reid_lib::{compile::CompileOutput, Context}; use crate::{ast::TopLevelStatement, lexer::Token, token_stream::TokenStream}; @@ -145,7 +145,7 @@ pub fn perform_all_passes(context: &mut mir::Context) -> Result<(), ReidError> { /// Takes in a bit of source code, parses and compiles it and produces `hello.o` /// and `hello.asm` from it, which can be linked using `ld` to produce an /// executable file. -pub fn compile(source: &str, path: PathBuf) -> Result { +pub fn compile(source: &str, path: PathBuf) -> Result { let path = path.canonicalize().unwrap(); let mut mir_context = mir::Context::from( @@ -167,7 +167,5 @@ pub fn compile(source: &str, path: PathBuf) -> Result { dbg!(&codegen_modules); let compiled = codegen_modules.compile(); - compiled.output(); - - Ok(String::new()) + Ok(compiled.output()) }