Skip to content

Commit

Permalink
Test the compiler on core and alloc
Browse files Browse the repository at this point in the history
This commit uses the previously-compiled LLVM IR representations of the
rust `core`, `alloc` and `compiler_builtins` libraries to test the
functionality of the hieratika compiler. At the same time it fixes any
bugs that are found through this process.

It also enables the compiler to consistency check the target definition
in the file being compiled against the expected target definition,
ensuring that it does not do unnecessary work on unsupported inputs.

It also implements functionality for working with constants of aggregate
type, thereby closing #91.
  • Loading branch information
iamrecursion committed Jan 7, 2025
1 parent f43fcda commit e6a87dd
Show file tree
Hide file tree
Showing 26 changed files with 647 additions and 262 deletions.
158 changes: 67 additions & 91 deletions Cargo.lock

Large diffs are not rendered by default.

4 changes: 1 addition & 3 deletions crates/compiler/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,14 @@ rust-version.workspace = true
[dependencies]
bimap.workspace = true
chumsky = "0.9.3"
clap.workspace = true
derive_more = { version = "1.0.0", features = ["full"] }
downcast-rs = "1.2.1"
ethnum.workspace = true
inkwell.workspace = true
itertools.workspace = true
hieratika-errors.workspace = true
hieratika-flo.workspace = true
miette.workspace = true
ouroboros = "0.18.4"
tracing.workspace = true
rand = "0.8.5"

[dev-dependencies]
Expand Down
4 changes: 2 additions & 2 deletions crates/compiler/input/compilation/add.ll
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
; ModuleID = '9ox3ykpp0gbrqxqlz7ajwa9w6'
source_filename = "9ox3ykpp0gbrqxqlz7ajwa9w6"
target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128"
target triple = "aarch64-unknown-none"
target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128"
target triple = "riscv64"

@alloc_4190527422e5cc48a15bd1cb4f38f425 = private unnamed_addr constant <{ [33 x i8] }> <{ [33 x i8] c"crates/rust-test-input/src/lib.rs" }>, align 1
@alloc_5b4544c775a23c08ca70c48dd7be27fc = private unnamed_addr constant <{ ptr, [16 x i8] }> <{ ptr @alloc_4190527422e5cc48a15bd1cb4f38f425, [16 x i8] c"!\00\00\00\00\00\00\00\05\00\00\00\05\00\00\00" }>, align 8
Expand Down
10 changes: 10 additions & 0 deletions crates/compiler/input/compilation/bad_data_layout.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
; ModuleID = 'opcodes.ll'
source_filename = "opcodes.ll"
target datalayout = "E-m:e-p:64:64-i64:64-i128:128-n32:64-S128"
target triple = "riscv64"

define i64 @hieratika_test_add(i64 %left, i64 %right) unnamed_addr {
start:
%0 = add nuw nsw i64 %left, %right
ret i64 %0
}
10 changes: 10 additions & 0 deletions crates/compiler/input/compilation/bad_target_triple.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
; ModuleID = 'opcodes.ll'
source_filename = "opcodes.ll"
target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128"
target triple = "aarch64"

define i64 @hieratika_test_add(i64 %left, i64 %right) unnamed_addr {
start:
%0 = add nuw nsw i64 %left, %right
ret i64 %0
}
55 changes: 55 additions & 0 deletions crates/compiler/input/compilation/constants.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128"
target triple = "riscv64"

@test_const = constant { i1, [9 x i8] } { i1 0, [9 x i8] c"hieratika" }

; @constant_pointer_const = constant ptr @test_const
; @constant_pointer_const_in_struct = constant { i1, ptr } { i1 0, ptr @test_const }

; @function_pointer_const = constant ptr @hieratika_test_const_integer
; @function_pointer_const_in_struct = constant { i1, ptr } { i1 0, ptr @hieratika_test_const_integer }

define ptr @hieratika_test_reference_const() unnamed_addr {
start:
ret ptr @test_const
}

define i64 @hieratika_test_const_integer() unnamed_addr {
start:
ret i64 0
}

define double @hieratika_test_const_float() unnamed_addr {
start:
ret double 0.0
}

define void @hieratika_test_const_pointer() unnamed_addr {
start:
%addr = alloca ptr
store ptr blockaddress(@hieratika_test_const_pointer, %bb1), ptr %addr
ret void
bb1:
unreachable
}

define void @hieratika_test_const_array() unnamed_addr {
start:
%ptr = alloca ptr
store [2 x i8] [i8 0, i8 1], ptr %ptr
ret void
}

define void @hieratika_test_const_string() unnamed_addr {
start:
%ptr = alloca ptr
store [9 x i8] c"hieratika", ptr %ptr
ret void
}

define void @hieratika_test_const_struct() unnamed_addr {
start:
%ptr = alloca ptr
store { i8, i1 } { i8 0, i1 1 }, ptr %ptr
ret void
}
4 changes: 2 additions & 2 deletions crates/compiler/input/compilation/opcodes.ll
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
; ModuleID = 'opcodes.ll'
source_filename = "opcodes.ll"
target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128"
target triple = "aarch64-unknown-none"
target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128"
target triple = "riscv64"

; Arithmetic and logic operations

Expand Down
4 changes: 2 additions & 2 deletions crates/compiler/input/compilation/terminators.ll
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
; ModuleID = 'opcodes.ll'
source_filename = "opcodes.ll"
target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128"
target triple = "aarch64-unknown-none"
target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128"
target triple = "riscv64"

; Supported terminator instructions

Expand Down
8 changes: 8 additions & 0 deletions crates/compiler/src/constant.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
//! Useful constants for use within the compiler.
/// The expected target triple for our platform, intended to be used for
/// validation during compilation.
pub const TARGET_TRIPLE: &str = "riscv64";

/// The expected data layout for our platform, intended to be used for
/// validation during compilation.
pub const TARGET_DATA_LAYOUT: &str = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128";

/// The size of a byte on our architecture.
pub const BYTE_SIZE_BITS: usize = 8;

Expand Down
61 changes: 60 additions & 1 deletion crates/compiler/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,11 +113,13 @@ pub mod obj_gen;
pub mod pass;
pub mod polyfill;

use hieratika_errors::compile::llvm::Result;
use hieratika_errors::compile::llvm::{Error, Result};
use hieratika_flo::FlatLoweredObject;

use crate::{
constant::{TARGET_DATA_LAYOUT, TARGET_TRIPLE},
context::SourceContext,
llvm::data_layout::DataLayout,
obj_gen::ObjectGenerator,
pass::{PassManager, PassManagerReturnData, analysis::module_map::BuildModuleMap},
polyfill::PolyfillMap,
Expand Down Expand Up @@ -194,6 +196,10 @@ impl Compiler {
///
/// - If the module mapping pass has not been run.
pub fn run(mut self) -> Result<FlatLoweredObject> {
// Before we do anything, we need to validate that the input is compatible with
// our target.
self.is_compatible_target()?;

// First we have to run all the passes and collect their data.
let PassManagerReturnData { context, data } = self.passes.run(self.context)?;

Expand All @@ -208,6 +214,59 @@ impl Compiler {
let builder = ObjectGenerator::new(&mod_name, data, context, self.polyfill_map)?;
builder.run()
}

/// Validates that the context for the compilation contains an LLVM module
/// that has been compiled for a platform that is compatible with our target
/// platform, returning `true` if it is compatible and `false` otherwise.
///
/// # Errors
///
/// - [`Error::IncompatibleDataLayout`] if the module being compiled has a
/// data layout not compatible with the expected one.
/// - [`Error::IncompatibleTargetSpecification`] if the module being
/// compiled has a target triple not compatible with the expected one.
/// - [`Error::InvalidDataLayoutSpecification`] if the module being compiled
/// has a data layout string that cannot be parsed as a data layout
/// specification.
///
/// # Panics
///
/// - If the compiler's statically-known data layout string cannot be parsed
/// into a valid data layout.
pub fn is_compatible_target(&self) -> Result<()> {
// We start by grabbing the actual data layout and comparing it to the one we
// know is correct.
let (actual_layout_str, actual_target) = self.context.analyze_module(|module| {
let layout = module.get_data_layout().as_str().to_str()?.to_string();
let target = module.get_triple().as_str().to_str()?.to_string();

Ok((layout, target))
})?;
let actual_layout = DataLayout::new(&actual_layout_str)?;
let expected_data_layout = DataLayout::new(TARGET_DATA_LAYOUT)
.expect("Statically known data layout could not be parsed");

// The target being compatible currently means that it has the same target
// triple and the same data layout. This notion MAY be relaxed in the future.
let targets_valid = actual_target == TARGET_TRIPLE || actual_target.is_empty();
let layouts_valid = actual_layout_str.is_empty() || actual_layout == expected_data_layout;

if !targets_valid {
Err(Error::IncompatibleTargetSpecification(
actual_target,
TARGET_TRIPLE.to_string(),
))?;
}

if !layouts_valid {
Err(Error::IncompatibleDataLayout(
actual_layout_str,
TARGET_DATA_LAYOUT.to_string(),
))?;
}

Ok(())
}
}

/// Allows for building a [`Compiler`] instance while retaining the defaults for
Expand Down
10 changes: 9 additions & 1 deletion crates/compiler/src/llvm/typesystem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ pub enum LLVMType {
/// The 16-bit wide [integer type](https://llvm.org/docs/LangRef.html#integer-type).
i16,

/// The 24-bit wide [integer type](https://llvm.org/docs/LangRef.html#integer-type).
i24,

/// The 32-bit wide [integer type](https://llvm.org/docs/LangRef.html#integer-type).
i32,

Expand Down Expand Up @@ -156,6 +159,8 @@ impl LLVMType {
self,
Self::bool
| Self::i8
| Self::i16
| Self::i24
| Self::i32
| Self::i64
| Self::i128
Expand Down Expand Up @@ -224,14 +229,15 @@ impl LLVMType {
f64,
i8,
i16,
i24,
i32,
i64,
i128,
ptr,
void,
};
match self {
bool | i8 | i16 | i32 | i64 | i128 | f16 | f32 | f64 | ptr => 1,
bool | i8 | i16 | i24 | i32 | i64 | i128 | f16 | f32 | f64 | ptr => 1,
void | Metadata => 0,
Array(array_ty) => array_ty.size_of(),
Structure(struct_ty) => struct_ty.size_of(),
Expand All @@ -258,6 +264,7 @@ impl Display for LLVMType {
LLVMType::bool => "i1".to_string(),
LLVMType::i8 => "i8".to_string(),
LLVMType::i16 => "i16".to_string(),
LLVMType::i24 => "i24".to_string(),
LLVMType::i32 => "i32".to_string(),
LLVMType::i64 => "i64".to_string(),
LLVMType::i128 => "i128".to_string(),
Expand Down Expand Up @@ -412,6 +419,7 @@ impl<'ctx> TryFrom<&IntType<'ctx>> for LLVMType {
1 => Self::bool,
8 => Self::i8,
16 => Self::i16,
24 => Self::i24,
32 => Self::i32,
64 => Self::i64,
128 => Self::i128,
Expand Down
1 change: 1 addition & 0 deletions crates/compiler/src/obj_gen/data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ impl ObjectContext {
LLVMType::bool => Type::Bool,
LLVMType::i8 => Type::Signed8,
LLVMType::i16 => Type::Signed16,
LLVMType::i24 => Type::Signed24,
LLVMType::i32 => Type::Signed32,
LLVMType::i64 => Type::Signed64,
LLVMType::i128 => Type::Signed128,
Expand Down
Loading

0 comments on commit e6a87dd

Please sign in to comment.