Compare commits

..

2 Commits

11 changed files with 160 additions and 84 deletions

6
.gitignore vendored
View File

@ -4,4 +4,8 @@ src/old_llvm
/.vscode /.vscode
.env .env
hello.* hello.*
main main
reid_src/*.o
reid_src/*.ll
reid_src/*.asm
reid_src/*.out

17
Makefile Normal file
View File

@ -0,0 +1,17 @@
SRC=foo.reid
BIN=$(SRC:.reid=.out)
REID=cargo run --example cli
LD=ld
LDFLAGS=
all: $(BIN)
clean:
rm -f $(BIN) $(SRC:.reid=.o) $(SRC:.reid=.asm) $(SRC:.reid=.ll)
$(BIN): $(SRC:.reid=.o)
$(LD) -dynamic-linker /lib64/ld-linux-x86-64.so.2 /usr/lib/crt1.o -lc $(LDFLAGS) $< -o$@
.SUFFIXES: .o .reid
.reid.o:
$(REID) $<

View File

@ -5,13 +5,17 @@
# #
# Do note this file is extremely simply for my own personal convenience # Do note this file is extremely simply for my own personal convenience
export .env # export .env
cargo run --release --example cli $1 && \ # cargo run --release --example cli $1 && \
# clang hello.o -o main && \ # # clang hello.o -o main && \
ld -dynamic-linker /lib64/ld-linux-x86-64.so.2 \ # ld -dynamic-linker /lib64/ld-linux-x86-64.so.2 \
-o main /usr/lib/crt1.o hello.o -lc && \ # -o main /usr/lib/crt1.o hello.o -lc && \
./main ; echo "Return value: ""$?" # ./main ; echo "Return value: ""$?"
$BINARY="$($1 | cut -d'.' -f1)"".out"
make clean SRC=$1 && make SRC=$1 && echo "" && \
$BINARY ; echo "Return value: ""$?"
## Command from: clang -v hello.o -o test ## Command from: clang -v hello.o -o test
## Original command: ## Original command:

View File

@ -2,7 +2,7 @@
//! LLIR ([`Context`]) into LLVM IR. This module is the only one that interfaces //! LLIR ([`Context`]) into LLVM IR. This module is the only one that interfaces
//! with the LLVM API. //! with the LLVM API.
use std::{collections::HashMap, ffi::CString, ptr::null_mut}; use std::{collections::HashMap, ptr::null_mut};
use llvm_sys::{ use llvm_sys::{
LLVMIntPredicate, LLVMLinkage, LLVMIntPredicate, LLVMLinkage,
@ -16,11 +16,11 @@ use llvm_sys::{
}, },
target_machine::{ target_machine::{
LLVMCodeGenFileType, LLVMCreateTargetDataLayout, LLVMCreateTargetMachine, 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::{ use super::{
CmpPredicate, ConstValue, Context, TerminatorKind, Type, CmpPredicate, ConstValue, Context, TerminatorKind, Type,
@ -49,8 +49,15 @@ pub struct CompiledModule {
_context: LLVMContext, _context: LLVMContext,
} }
pub struct CompileOutput {
pub triple: String,
pub assembly: String,
pub obj_buffer: Vec<u8>,
pub llvm_ir: String,
}
impl CompiledModule { impl CompiledModule {
pub fn output(&self) { pub fn output(&self) -> CompileOutput {
unsafe { unsafe {
LLVM_InitializeAllTargets(); LLVM_InitializeAllTargets();
LLVM_InitializeAllTargetInfos(); LLVM_InitializeAllTargetInfos();
@ -63,7 +70,6 @@ impl CompiledModule {
let mut target: _ = null_mut(); let mut target: _ = null_mut();
let mut err = ErrorMessageHolder::null(); let mut err = ErrorMessageHolder::null();
LLVMGetTargetFromTriple(triple, &mut target, err.borrow_mut()); LLVMGetTargetFromTriple(triple, &mut target, err.borrow_mut());
println!("{:?}, {:?}", from_cstring(triple), target);
err.into_result().unwrap(); err.into_result().unwrap();
let target_machine = LLVMCreateTargetMachine( let target_machine = LLVMCreateTargetMachine(
@ -88,28 +94,37 @@ impl CompiledModule {
); );
err.into_result().unwrap(); err.into_result().unwrap();
let mut asm_buffer = MemoryBufferHolder::empty("asm");
let mut err = ErrorMessageHolder::null(); let mut err = ErrorMessageHolder::null();
LLVMTargetMachineEmitToFile( LLVMTargetMachineEmitToMemoryBuffer(
target_machine, target_machine,
self.module_ref, self.module_ref,
CString::new("hello.asm").unwrap().into_raw(),
LLVMCodeGenFileType::LLVMAssemblyFile, LLVMCodeGenFileType::LLVMAssemblyFile,
err.borrow_mut(), err.borrow_mut(),
&mut asm_buffer.buffer,
); );
err.into_result().unwrap(); err.into_result().unwrap();
let mut obj_buffer = MemoryBufferHolder::empty("obj");
let mut err = ErrorMessageHolder::null(); let mut err = ErrorMessageHolder::null();
LLVMTargetMachineEmitToFile( LLVMTargetMachineEmitToMemoryBuffer(
target_machine, target_machine,
self.module_ref, self.module_ref,
CString::new("hello.o").unwrap().into_raw(),
LLVMCodeGenFileType::LLVMObjectFile, LLVMCodeGenFileType::LLVMObjectFile,
err.borrow_mut(), err.borrow_mut(),
&mut obj_buffer.buffer,
); );
err.into_result().unwrap(); err.into_result().unwrap();
let module_str = from_cstring(LLVMPrintModuleToString(self.module_ref)); CompileOutput {
println!("{}", module_str.unwrap()); 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"),
}
} }
} }
} }

View File

@ -1,9 +1,17 @@
use std::{ use std::{
ffi::{CStr, CString, c_char}, ffi::{CStr, CString, c_char},
ptr::null_mut, ptr::null_mut,
string::FromUtf8Error,
}; };
use llvm_sys::error::LLVMDisposeErrorMessage; use llvm_sys::{
core::{
LLVMCreateMemoryBufferWithMemoryRange, LLVMDisposeMemoryBuffer, LLVMGetBufferSize,
LLVMGetBufferStart,
},
error::LLVMDisposeErrorMessage,
prelude::LLVMMemoryBufferRef,
};
use crate::{ use crate::{
Type, 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<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, /// Make sure types for given instructions match. Return Ok(type) if they do,
/// and error otherwise. /// and error otherwise.
pub fn match_types( pub fn match_types(

View File

@ -1,18 +1,37 @@
use std::{env, fs, path::PathBuf}; use std::{env, fs, path::PathBuf};
use reid::compile; use reid::compile;
use reid_lib::compile::CompileOutput;
fn main() -> Result<(), std::io::Error> { fn main() -> Result<(), std::io::Error> {
let args: Vec<String> = env::args().collect(); let args: Vec<String> = env::args().collect();
if let Some(filename) = args.get(1) { 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 text = fs::read_to_string(&path)?;
let output = match compile(&text, PathBuf::from(path)) { match compile(&text, PathBuf::from(&path)) {
Ok(t) => t, 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), Err(e) => panic!("{}", e),
}; };
println!("{}", output);
} else { } else {
println!("Please input compiled file path!") println!("Please input compiled file path!")
} }

View File

@ -46,7 +46,7 @@ use std::path::PathBuf;
use mir::{ use mir::{
linker::LinkerPass, typecheck::TypeCheck, typeinference::TypeInference, typerefs::TypeRefs, 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}; 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` /// 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 /// and `hello.asm` from it, which can be linked using `ld` to produce an
/// executable file. /// executable file.
pub fn compile(source: &str, path: PathBuf) -> Result<String, ReidError> { pub fn compile(source: &str, path: PathBuf) -> Result<CompileOutput, ReidError> {
let path = path.canonicalize().unwrap(); let path = path.canonicalize().unwrap();
let mut mir_context = mir::Context::from( let mut mir_context = mir::Context::from(
@ -167,7 +167,5 @@ pub fn compile(source: &str, path: PathBuf) -> Result<String, ReidError> {
dbg!(&codegen_modules); dbg!(&codegen_modules);
let compiled = codegen_modules.compile(); let compiled = codegen_modules.compile();
compiled.output(); Ok(compiled.output())
Ok(String::new())
} }

View File

@ -163,7 +163,9 @@ impl IndexedVariableReference {
} }
super::IndexedVariableReferenceKind::Index(inner, _) => { super::IndexedVariableReferenceKind::Index(inner, _) => {
if let Some((mutable, inner_ref)) = inner.find_hint(hints)? { if let Some((mutable, inner_ref)) = inner.find_hint(hints)? {
let inner_ty = inner_ref.as_type(); // Check that the resolved type is at least an array, no
// need for further resolution.
let inner_ty = inner_ref.resolve_weak().unwrap();
match inner_ty { match inner_ty {
Array(type_kind, _) => Ok(hints Array(type_kind, _) => Ok(hints
.from_type(&type_kind) .from_type(&type_kind)
@ -284,7 +286,9 @@ impl Expression {
ExprKind::Index(expression, index_ty, _) => { ExprKind::Index(expression, index_ty, _) => {
let expr_ty = expression.infer_types(state, type_refs)?; let expr_ty = expression.infer_types(state, type_refs)?;
let kind = unsafe { expr_ty.resolve_type() }; // Check that the resolved type is at least an array, no
// need for further resolution.
let kind = expr_ty.resolve_weak().unwrap();
match kind { match kind {
Array(type_kind, _) => { Array(type_kind, _) => {
let elem_ty = type_refs.from_type(&type_kind).unwrap(); let elem_ty = type_refs.from_type(&type_kind).unwrap();

View File

@ -15,23 +15,22 @@ use super::{
pub struct TypeRef<'scope>(TypeIdRef, &'scope ScopeTypeRefs<'scope>); pub struct TypeRef<'scope>(TypeIdRef, &'scope ScopeTypeRefs<'scope>);
impl<'scope> TypeRef<'scope> { impl<'scope> TypeRef<'scope> {
pub unsafe fn resolve_type(&self) -> TypeKind { /// Resolve current type in a weak manner, not resolving any Arrays or
unsafe { /// further inner types
let resolved = self pub fn resolve_weak(&self) -> Option<TypeKind> {
.1 Some(self.1.types.retrieve_type(*self.0.borrow())?)
.types }
.hints
.borrow()
.get_unchecked(*self.0.borrow())
.clone();
match resolved { /// Resolve type deeply, trying to resolve any inner types as well.
TypeKind::Array(elem_ty, len) => { pub fn resolve_deep(&self) -> Option<TypeKind> {
let resolved_elem_ty = self.1.from_type(&elem_ty).unwrap().resolve_type(); let resolved = self.resolve_weak()?;
TypeKind::Array(Box::new(resolved_elem_ty), len)
} match resolved {
_ => resolved, TypeKind::Array(elem_ty, len) => {
let resolved_elem_ty = self.1.from_type(&elem_ty).unwrap().resolve_weak()?;
Some(TypeKind::Array(Box::new(resolved_elem_ty), len))
} }
_ => Some(resolved),
} }
} }
@ -48,7 +47,7 @@ impl<'scope> std::fmt::Debug for TypeRef<'scope> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("Hint") f.debug_tuple("Hint")
.field(&self.0) .field(&self.0)
.field(unsafe { &self.resolve_type() }) .field(&self.resolve_deep().unwrap())
.finish() .finish()
} }
} }

BIN
reid_src/array Executable file

Binary file not shown.

38
test.ll
View File

@ -1,38 +0,0 @@
; ModuleID = 'test'
source_filename = "test"
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"
define ptr @array() {
array:
%array_alloca = alloca i16, i16 4, align 2
%array_gep = getelementptr i16, ptr %array_alloca, i32 0
store i16 10, ptr %array_gep, align 2
%array_gep1 = getelementptr i16, ptr %array_alloca, i32 1
store i16 15, ptr %array_gep1, align 2
%array_gep2 = getelementptr i16, ptr %array_alloca, i32 2
store i16 7, ptr %array_gep2, align 2
%array_gep3 = getelementptr i16, ptr %array_alloca, i32 3
store i16 9, ptr %array_gep3, align 2
%array_alloca4 = alloca ptr, i16 1, align 8
%array_gep5 = getelementptr ptr, ptr %array_alloca4, i32 0
store ptr %array_alloca, ptr %array_gep5, align 8
ret ptr %array_alloca4
}
define i16 @main() {
main:
%call = call ptr @array()
%array_gep = getelementptr ptr, ptr %call, i32 0
%load5 = load ptr, ptr %array_gep2, align 8
%array_gep1 = getelementptr i16, ptr %load5, i32 3
store i16 5, ptr %array_gep1, align 2
%array_gep2 = getelementptr ptr, ptr %call, i32 0
%load = load ptr, ptr %array_gep2, align 8
%array_gep3 = getelementptr i16, ptr %load, i32 3
%load4 = load i16, ptr %array_gep3, align 2
ret i16 %load4
}