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