1317 lines
48 KiB
Rust
1317 lines
48 KiB
Rust
use std::{collections::HashMap, mem};
|
|
|
|
use reid_lib::{
|
|
builder::{InstructionValue, TypeValue},
|
|
compile::CompiledModule,
|
|
debug_information::{
|
|
DebugArrayType, DebugBasicType, DebugFieldType, DebugFileData, DebugInformation,
|
|
DebugLocalVariable, DebugLocation, DebugMetadata, DebugParamVariable, DebugPointerType,
|
|
DebugProgramValue, DebugRecordKind, DebugStructType, DebugSubprogramData,
|
|
DebugSubprogramOptionals, DebugSubprogramType, DebugTypeData, DebugTypeValue,
|
|
DwarfEncoding, DwarfFlags, InstructionDebugRecordData,
|
|
},
|
|
Block, CmpPredicate, ConstValue, Context, CustomTypeKind, Function, FunctionFlags, Instr,
|
|
Module, NamedStruct, TerminatorKind as Term, Type,
|
|
};
|
|
|
|
use crate::{
|
|
error_raporting::ModuleMap,
|
|
lexer::{FullToken, Position},
|
|
mir::{
|
|
self, ExprKind, Metadata, NamedVariableRef, StructField, StructType, TypeDefinition,
|
|
TypeDefinitionKind, TypeKind, VagueLiteral,
|
|
},
|
|
};
|
|
|
|
/// Context that contains all of the given modules as complete codegenerated
|
|
/// LLIR that can then be finally compiled into LLVM IR.
|
|
#[derive(Debug)]
|
|
pub struct CodegenContext<'ctx> {
|
|
context: &'ctx Context,
|
|
}
|
|
|
|
impl<'ctx> CodegenContext<'ctx> {
|
|
/// Compile contained LLIR into LLVM IR and produce `hello.o` and
|
|
/// `hello.asm`
|
|
pub fn compile(&self) -> CompiledModule {
|
|
self.context.compile()
|
|
}
|
|
}
|
|
|
|
impl mir::Context {
|
|
/// Compile MIR [`Context`] into [`CodegenContext`] containing LLIR.
|
|
pub fn codegen<'ctx>(
|
|
&self,
|
|
context: &'ctx Context,
|
|
mod_map: &ModuleMap,
|
|
) -> CodegenContext<'ctx> {
|
|
let mut modules = Vec::new();
|
|
for module in &self.modules {
|
|
modules.push(
|
|
module.codegen(
|
|
context,
|
|
mod_map
|
|
.module(&module.module_id)
|
|
.unwrap()
|
|
.tokens
|
|
.as_ref()
|
|
.unwrap(),
|
|
),
|
|
);
|
|
}
|
|
CodegenContext { context }
|
|
}
|
|
}
|
|
|
|
struct ModuleCodegen<'ctx> {
|
|
module: Module<'ctx>,
|
|
}
|
|
|
|
impl<'ctx> std::fmt::Debug for ModuleCodegen<'ctx> {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
std::fmt::Debug::fmt(&self.module.as_printable(), f)
|
|
}
|
|
}
|
|
|
|
pub struct Scope<'ctx, 'a> {
|
|
context: &'ctx Context,
|
|
tokens: &'ctx Vec<FullToken>,
|
|
module: &'ctx Module<'ctx>,
|
|
function: &'ctx StackFunction<'ctx>,
|
|
block: Block<'ctx>,
|
|
types: &'a HashMap<TypeValue, TypeDefinition>,
|
|
type_values: &'a HashMap<String, TypeValue>,
|
|
functions: &'a HashMap<String, StackFunction<'ctx>>,
|
|
stack_values: HashMap<String, StackValue>,
|
|
debug: Option<Debug<'ctx>>,
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct Debug<'ctx> {
|
|
info: &'ctx DebugInformation,
|
|
scope: DebugProgramValue,
|
|
types: &'ctx HashMap<TypeKind, DebugTypeValue>,
|
|
}
|
|
|
|
pub struct StackFunction<'ctx> {
|
|
ir: Function<'ctx>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
pub struct StackValue(StackValueKind, TypeKind);
|
|
|
|
impl StackValue {
|
|
fn instr(&self) -> InstructionValue {
|
|
self.0.instr()
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
pub enum StackValueKind {
|
|
Immutable(InstructionValue),
|
|
Mutable(InstructionValue),
|
|
Literal(InstructionValue),
|
|
}
|
|
|
|
impl StackValueKind {
|
|
fn instr(&self) -> InstructionValue {
|
|
match &self {
|
|
StackValueKind::Immutable(val) => *val,
|
|
StackValueKind::Mutable(val) => *val,
|
|
StackValueKind::Literal(val) => *val,
|
|
}
|
|
}
|
|
|
|
fn derive(&self, instr: InstructionValue) -> StackValueKind {
|
|
match &self {
|
|
StackValueKind::Immutable(_) => StackValueKind::Immutable(instr),
|
|
StackValueKind::Mutable(_) => StackValueKind::Mutable(instr),
|
|
StackValueKind::Literal(_) => StackValueKind::Literal(instr),
|
|
}
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
fn map<F>(&self, lambda: F) -> StackValueKind
|
|
where
|
|
F: FnOnce(InstructionValue) -> InstructionValue,
|
|
{
|
|
self.derive(lambda(self.instr()))
|
|
}
|
|
}
|
|
|
|
impl<'ctx, 'a> Scope<'ctx, 'a> {
|
|
fn with_block(&self, block: Block<'ctx>) -> Scope<'ctx, 'a> {
|
|
Scope {
|
|
block,
|
|
tokens: self.tokens,
|
|
function: self.function,
|
|
context: self.context,
|
|
module: self.module,
|
|
functions: self.functions,
|
|
types: self.types,
|
|
type_values: self.type_values,
|
|
stack_values: self.stack_values.clone(),
|
|
debug: self.debug.clone(),
|
|
}
|
|
}
|
|
|
|
/// Takes the block out from this scope, swaps the given block in it's place
|
|
/// and returns the old block.
|
|
fn swap_block(&mut self, block: Block<'ctx>) -> Block<'ctx> {
|
|
let mut old_block = block;
|
|
mem::swap(&mut self.block, &mut old_block);
|
|
old_block
|
|
}
|
|
|
|
fn get_typedef(&self, name: &String) -> Option<&TypeDefinition> {
|
|
self.type_values.get(name).and_then(|v| self.types.get(v))
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
|
|
struct State {
|
|
should_load: bool,
|
|
}
|
|
|
|
impl State {
|
|
/// Sets should load, returning a new state
|
|
fn load(self, should: bool) -> State {
|
|
State {
|
|
should_load: should,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Default for State {
|
|
fn default() -> Self {
|
|
Self { should_load: true }
|
|
}
|
|
}
|
|
|
|
impl mir::Module {
|
|
fn codegen<'ctx>(
|
|
&self,
|
|
context: &'ctx Context,
|
|
tokens: &Vec<FullToken>,
|
|
) -> ModuleCodegen<'ctx> {
|
|
let mut module = context.module(&self.name, self.is_main);
|
|
|
|
let (debug, compile_unit) = if let Some(path) = &self.path {
|
|
module.create_debug_info(DebugFileData {
|
|
name: path.file_name().unwrap().to_str().unwrap().to_owned(),
|
|
directory: path.parent().unwrap().to_str().unwrap().to_owned(),
|
|
})
|
|
} else {
|
|
module.create_debug_info(DebugFileData {
|
|
name: self.name.clone(),
|
|
directory: String::new(),
|
|
})
|
|
};
|
|
|
|
let mut types = HashMap::new();
|
|
let mut type_values = HashMap::new();
|
|
let mut debug_types = HashMap::new();
|
|
|
|
macro_rules! insert_debug {
|
|
($kind:expr) => {
|
|
debug_types.insert(
|
|
$kind.clone(),
|
|
$kind.get_debug_type_hard(
|
|
compile_unit,
|
|
&debug,
|
|
&debug_types,
|
|
&type_values,
|
|
&types,
|
|
tokens,
|
|
),
|
|
)
|
|
};
|
|
}
|
|
|
|
insert_debug!(&TypeKind::Bool);
|
|
insert_debug!(&TypeKind::U8);
|
|
insert_debug!(&TypeKind::U16);
|
|
insert_debug!(&TypeKind::U32);
|
|
insert_debug!(&TypeKind::U64);
|
|
insert_debug!(&TypeKind::U128);
|
|
insert_debug!(&TypeKind::I8);
|
|
insert_debug!(&TypeKind::I16);
|
|
insert_debug!(&TypeKind::I32);
|
|
insert_debug!(&TypeKind::I64);
|
|
insert_debug!(&TypeKind::I128);
|
|
insert_debug!(&TypeKind::Void);
|
|
insert_debug!(&TypeKind::StringPtr);
|
|
|
|
for typedef in &self.typedefs {
|
|
let type_value = match &typedef.kind {
|
|
TypeDefinitionKind::Struct(StructType(fields)) => {
|
|
module.custom_type(CustomTypeKind::NamedStruct(NamedStruct(
|
|
typedef.name.clone(),
|
|
fields
|
|
.iter()
|
|
// TODO: Reorder custom-type definitions such that
|
|
// inner types get evaluated first. Otherwise this
|
|
// will cause a panic!
|
|
.map(|StructField(_, t, _)| t.get_type(&type_values, &types))
|
|
.collect(),
|
|
)))
|
|
}
|
|
};
|
|
types.insert(type_value, typedef.clone());
|
|
type_values.insert(typedef.name.clone(), type_value);
|
|
insert_debug!(&TypeKind::CustomType(typedef.name.clone()));
|
|
}
|
|
|
|
let mut functions = HashMap::new();
|
|
|
|
for function in &self.functions {
|
|
let param_types: Vec<Type> = function
|
|
.parameters
|
|
.iter()
|
|
.map(|(_, p)| p.get_type(&type_values, &types))
|
|
.collect();
|
|
|
|
let is_main = self.is_main && function.name == "main";
|
|
let func = match &function.kind {
|
|
mir::FunctionDefinitionKind::Local(_, _) => module.function(
|
|
&function.name,
|
|
function.return_type.get_type(&type_values, &types),
|
|
param_types,
|
|
FunctionFlags {
|
|
is_pub: function.is_pub || is_main,
|
|
is_main,
|
|
is_imported: function.is_imported,
|
|
..FunctionFlags::default()
|
|
},
|
|
),
|
|
mir::FunctionDefinitionKind::Extern(imported) => module.function(
|
|
&function.name,
|
|
function.return_type.get_type(&type_values, &types),
|
|
param_types,
|
|
FunctionFlags {
|
|
is_extern: true,
|
|
is_imported: *imported,
|
|
..FunctionFlags::default()
|
|
},
|
|
),
|
|
};
|
|
|
|
functions.insert(function.name.clone(), StackFunction { ir: func });
|
|
}
|
|
|
|
for mir_function in &self.functions {
|
|
let function = functions.get(&mir_function.name).unwrap();
|
|
let mut entry = function.ir.block("entry");
|
|
|
|
// Insert debug information
|
|
let debug_scope = if let Some(location) = mir_function.signature().into_debug(tokens) {
|
|
// let debug_scope = debug.inner_scope(&outer_scope, location);
|
|
|
|
let fn_param_ty = &mir_function.return_type.get_debug_type_hard(
|
|
compile_unit,
|
|
&debug,
|
|
&debug_types,
|
|
&type_values,
|
|
&types,
|
|
tokens,
|
|
);
|
|
|
|
let debug_ty = debug.debug_type(DebugTypeData::Subprogram(DebugSubprogramType {
|
|
parameters: vec![*fn_param_ty],
|
|
flags: DwarfFlags,
|
|
}));
|
|
|
|
let subprogram = debug.subprogram(DebugSubprogramData {
|
|
name: mir_function.name.clone(),
|
|
outer_scope: compile_unit.clone(),
|
|
location,
|
|
ty: debug_ty,
|
|
opts: DebugSubprogramOptionals {
|
|
is_local: !mir_function.is_pub,
|
|
is_definition: true,
|
|
..DebugSubprogramOptionals::default()
|
|
},
|
|
});
|
|
|
|
function.ir.set_debug(subprogram);
|
|
|
|
Some(subprogram)
|
|
} else {
|
|
None
|
|
};
|
|
|
|
// Compile actual IR part
|
|
let mut stack_values = HashMap::new();
|
|
for (i, (p_name, p_ty)) in mir_function.parameters.iter().enumerate() {
|
|
// Codegen actual parameters
|
|
let arg_name = format!("arg.{}", p_name);
|
|
let param = entry
|
|
.build(format!("{}.get", arg_name), Instr::Param(i))
|
|
.unwrap();
|
|
let alloca = entry
|
|
.build(
|
|
&arg_name,
|
|
Instr::Alloca(p_ty.get_type(&type_values, &types)),
|
|
)
|
|
.unwrap();
|
|
entry
|
|
.build(format!("{}.store", arg_name), Instr::Store(alloca, param))
|
|
.unwrap();
|
|
stack_values.insert(
|
|
p_name.clone(),
|
|
StackValue(
|
|
StackValueKind::Immutable(alloca),
|
|
TypeKind::Ptr(Box::new(p_ty.clone())),
|
|
),
|
|
);
|
|
|
|
// Generate debug info
|
|
if let (Some(debug_scope), Some(location)) =
|
|
(&debug_scope, mir_function.signature().into_debug(tokens))
|
|
{
|
|
debug.metadata(
|
|
&debug_scope,
|
|
DebugMetadata::ParamVar(DebugParamVariable {
|
|
name: p_name.clone(),
|
|
arg_idx: i as u32,
|
|
location,
|
|
ty: p_ty.get_debug_type_hard(
|
|
*debug_scope,
|
|
&debug,
|
|
&debug_types,
|
|
&type_values,
|
|
&types,
|
|
tokens,
|
|
),
|
|
always_preserve: true,
|
|
flags: DwarfFlags,
|
|
}),
|
|
);
|
|
}
|
|
}
|
|
|
|
let mut scope = Scope {
|
|
context,
|
|
tokens,
|
|
module: &module,
|
|
function,
|
|
block: entry,
|
|
functions: &functions,
|
|
types: &types,
|
|
type_values: &type_values,
|
|
stack_values,
|
|
debug: debug_scope.and_then(|scope| {
|
|
Some(Debug {
|
|
info: &debug,
|
|
scope,
|
|
types: &debug_types,
|
|
})
|
|
}),
|
|
};
|
|
|
|
match &mir_function.kind {
|
|
mir::FunctionDefinitionKind::Local(block, _) => {
|
|
let state = State::default();
|
|
if let Some(ret) = block.codegen(&mut scope, &state) {
|
|
scope.block.terminate(Term::Ret(ret.instr())).unwrap();
|
|
} else {
|
|
if !scope.block.delete_if_unused().unwrap() {
|
|
// Add a void return just in case if the block
|
|
// wasn't unused but didn't have a terminator yet
|
|
scope.block.terminate(Term::RetVoid).ok();
|
|
}
|
|
}
|
|
|
|
if let Some(debug) = scope.debug {
|
|
let location = &block.return_meta().into_debug(tokens).unwrap();
|
|
let location = debug.info.location(&debug.scope, *location);
|
|
scope.block.set_terminator_location(location).unwrap();
|
|
}
|
|
}
|
|
mir::FunctionDefinitionKind::Extern(_) => {}
|
|
}
|
|
}
|
|
|
|
ModuleCodegen { module }
|
|
}
|
|
}
|
|
|
|
impl mir::Block {
|
|
fn codegen<'ctx, 'a>(
|
|
&self,
|
|
mut scope: &mut Scope<'ctx, 'a>,
|
|
state: &State,
|
|
) -> Option<StackValue> {
|
|
for stmt in &self.statements {
|
|
stmt.codegen(&mut scope, state).map(|s| {
|
|
if let Some(debug) = &scope.debug {
|
|
let location = stmt.1.into_debug(scope.tokens).unwrap();
|
|
let loc_val = debug.info.location(&debug.scope, location);
|
|
s.instr().with_location(&mut scope.block, loc_val);
|
|
}
|
|
});
|
|
}
|
|
|
|
if let Some((kind, expr)) = &self.return_expression {
|
|
let ret = expr.codegen(&mut scope, &mut state.load(true));
|
|
match kind {
|
|
mir::ReturnKind::Hard => {
|
|
scope.block.terminate(Term::Ret(ret?.instr())).unwrap();
|
|
None
|
|
}
|
|
mir::ReturnKind::Soft => ret,
|
|
}
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
}
|
|
|
|
impl mir::Statement {
|
|
fn codegen<'ctx, 'a>(&self, scope: &mut Scope<'ctx, 'a>, state: &State) -> Option<StackValue> {
|
|
let location = self.1.into_debug(scope.tokens).unwrap();
|
|
let location = scope
|
|
.debug
|
|
.as_ref()
|
|
.map(|d| d.info.location(&d.scope, location));
|
|
|
|
match &self.0 {
|
|
mir::StmtKind::Let(NamedVariableRef(ty, name, _), mutable, expression) => {
|
|
let value = expression.codegen(scope, &state).unwrap();
|
|
|
|
let stack_value = if let mir::Expression(ExprKind::Borrow(_), _) = expression {
|
|
value
|
|
} else {
|
|
let alloca = scope
|
|
.block
|
|
.build(
|
|
name,
|
|
Instr::Alloca(ty.get_type(scope.type_values, scope.types)),
|
|
)
|
|
.unwrap()
|
|
.maybe_location(&mut scope.block, location);
|
|
|
|
scope
|
|
.block
|
|
.build(
|
|
format!("{}.store", name),
|
|
Instr::Store(alloca, value.instr()),
|
|
)
|
|
.unwrap()
|
|
.maybe_location(&mut scope.block, location);
|
|
|
|
StackValue(
|
|
match mutable {
|
|
true => StackValueKind::Mutable(alloca),
|
|
false => StackValueKind::Immutable(alloca),
|
|
},
|
|
TypeKind::Ptr(Box::new(value.clone().1)),
|
|
)
|
|
};
|
|
|
|
scope.stack_values.insert(name.clone(), stack_value.clone());
|
|
if let Some(debug) = &scope.debug {
|
|
let location = self.1.into_debug(scope.tokens).unwrap();
|
|
let var = debug.info.metadata(
|
|
&debug.scope,
|
|
DebugMetadata::LocalVar(DebugLocalVariable {
|
|
name: name.clone(),
|
|
location,
|
|
ty: TypeKind::Ptr(Box::new(ty.clone())).get_debug_type(debug, scope),
|
|
always_preserve: true,
|
|
flags: DwarfFlags,
|
|
}),
|
|
);
|
|
// value.instr().add_record(
|
|
// &mut scope.block,
|
|
// InstructionDebugRecordData {
|
|
// variable: var,
|
|
// location,
|
|
// kind: DebugRecordKind::Declare(value.instr()),
|
|
// scope: debug.scope,
|
|
// },
|
|
// );
|
|
}
|
|
None
|
|
}
|
|
mir::StmtKind::Set(lhs, rhs) => {
|
|
let lhs_value = lhs
|
|
.codegen(scope, &state.load(false))
|
|
.expect("non-returning LHS snuck into codegen!");
|
|
|
|
let rhs_value = rhs.codegen(scope, state)?;
|
|
|
|
let backing_var = if let Some(var) = lhs.backing_var() {
|
|
&format!("store.{}", var.1)
|
|
} else {
|
|
"store"
|
|
};
|
|
|
|
match lhs_value.0 {
|
|
StackValueKind::Immutable(_) => {
|
|
panic!("Tried to assign to immutable!")
|
|
}
|
|
StackValueKind::Mutable(instr) => {
|
|
scope
|
|
.block
|
|
.build(backing_var, Instr::Store(instr, rhs_value.instr()))
|
|
.unwrap()
|
|
.maybe_location(&mut scope.block, location);
|
|
}
|
|
StackValueKind::Literal(_) => {
|
|
panic!("Tried to assign to a literal!")
|
|
}
|
|
};
|
|
|
|
None
|
|
}
|
|
mir::StmtKind::Import(_) => todo!(),
|
|
mir::StmtKind::Expression(expression) => expression.codegen(scope, state),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl mir::Expression {
|
|
fn codegen<'ctx, 'a>(&self, scope: &mut Scope<'ctx, 'a>, state: &State) -> Option<StackValue> {
|
|
let location = if let Some(debug) = &scope.debug {
|
|
Some(
|
|
debug
|
|
.info
|
|
.location(&debug.scope, self.1.into_debug(scope.tokens).unwrap()),
|
|
)
|
|
} else {
|
|
None
|
|
};
|
|
|
|
let value = match &self.0 {
|
|
mir::ExprKind::Variable(varref) => {
|
|
varref.0.known().expect("variable type unknown");
|
|
let v = scope
|
|
.stack_values
|
|
.get(&varref.1)
|
|
.expect("Variable reference not found?!");
|
|
Some({
|
|
if state.should_load {
|
|
if let TypeKind::Ptr(inner) = &v.1 {
|
|
StackValue(
|
|
v.0.derive(
|
|
scope
|
|
.block
|
|
.build(
|
|
format!("{}", varref.1),
|
|
Instr::Load(
|
|
v.0.instr(),
|
|
inner.get_type(scope.type_values, scope.types),
|
|
),
|
|
)
|
|
.unwrap(),
|
|
),
|
|
*inner.clone(),
|
|
)
|
|
} else {
|
|
panic!("Variable was not a pointer?!?")
|
|
}
|
|
} else {
|
|
v.clone()
|
|
}
|
|
})
|
|
}
|
|
mir::ExprKind::Literal(lit) => Some(StackValue(
|
|
StackValueKind::Literal(lit.as_const(&mut scope.block)),
|
|
lit.as_type(),
|
|
)),
|
|
mir::ExprKind::BinOp(binop, lhs_exp, rhs_exp) => {
|
|
let lhs = lhs_exp
|
|
.codegen(scope, state)
|
|
.expect("lhs has no return value")
|
|
.instr();
|
|
let rhs = rhs_exp
|
|
.codegen(scope, state)
|
|
.expect("rhs has no return value")
|
|
.instr();
|
|
Some(StackValue(
|
|
StackValueKind::Immutable(match binop {
|
|
mir::BinaryOperator::Add => {
|
|
scope.block.build("add", Instr::Add(lhs, rhs)).unwrap()
|
|
}
|
|
mir::BinaryOperator::Minus => {
|
|
scope.block.build("sub", Instr::Sub(lhs, rhs)).unwrap()
|
|
}
|
|
mir::BinaryOperator::Mult => {
|
|
scope.block.build("mul", Instr::Mult(lhs, rhs)).unwrap()
|
|
}
|
|
mir::BinaryOperator::And => {
|
|
scope.block.build("and", Instr::And(lhs, rhs)).unwrap()
|
|
}
|
|
mir::BinaryOperator::Cmp(l) => scope
|
|
.block
|
|
.build("cmp", Instr::ICmp(l.int_predicate(), lhs, rhs))
|
|
.unwrap(),
|
|
}),
|
|
TypeKind::U32,
|
|
))
|
|
}
|
|
mir::ExprKind::FunctionCall(call) => {
|
|
let ret_type_kind = call
|
|
.return_type
|
|
.known()
|
|
.expect("function return type unknown");
|
|
|
|
let ret_type = ret_type_kind.get_type(scope.type_values, scope.types);
|
|
|
|
let params = call
|
|
.parameters
|
|
.iter()
|
|
.map(|e| e.codegen(scope, &mut state.load(true)).unwrap().instr())
|
|
.collect();
|
|
let callee = scope
|
|
.functions
|
|
.get(&call.name)
|
|
.expect("function not found!");
|
|
|
|
let val = scope
|
|
.block
|
|
.build(
|
|
call.name.clone(),
|
|
Instr::FunctionCall(callee.ir.value(), params),
|
|
)
|
|
.unwrap();
|
|
|
|
if let Some(debug) = &scope.debug {
|
|
let location = call.meta.into_debug(scope.tokens).unwrap();
|
|
let location_val = debug.info.location(&debug.scope, location);
|
|
val.with_location(&mut scope.block, location_val);
|
|
}
|
|
|
|
let ptr = if ret_type_kind != TypeKind::Void {
|
|
let ptr = scope
|
|
.block
|
|
.build(&call.name, Instr::Alloca(ret_type.clone()))
|
|
.unwrap();
|
|
scope
|
|
.block
|
|
.build(format!("{}.store", call.name), Instr::Store(ptr, val))
|
|
.unwrap();
|
|
|
|
Some(ptr)
|
|
} else {
|
|
None
|
|
};
|
|
|
|
if let Some(ptr) = ptr {
|
|
if state.should_load {
|
|
Some(StackValue(
|
|
StackValueKind::Immutable(
|
|
scope
|
|
.block
|
|
.build(call.name.clone(), Instr::Load(ptr, ret_type))
|
|
.unwrap(),
|
|
),
|
|
ret_type_kind,
|
|
))
|
|
} else {
|
|
Some(StackValue(
|
|
StackValueKind::Immutable(ptr),
|
|
TypeKind::Ptr(Box::new(ret_type_kind)),
|
|
))
|
|
}
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
mir::ExprKind::If(if_expression) => if_expression.codegen(scope, state),
|
|
mir::ExprKind::Block(block) => {
|
|
let mut inner_scope = scope.with_block(scope.function.ir.block("inner"));
|
|
if let Some(ret) = block.codegen(&mut inner_scope, state) {
|
|
inner_scope
|
|
.block
|
|
.terminate(Term::Br(scope.block.value()))
|
|
.unwrap();
|
|
Some(ret)
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
mir::ExprKind::Indexed(expression, val_t, idx_expr) => {
|
|
let StackValue(kind, array_ty) = expression
|
|
.codegen(scope, &state.load(false))
|
|
.expect("array returned none!");
|
|
let idx = idx_expr
|
|
.codegen(scope, &state.load(true))
|
|
.expect("index returned none!")
|
|
.instr();
|
|
|
|
let first = scope
|
|
.block
|
|
.build("array.zero", Instr::Constant(ConstValue::U32(0)))
|
|
.unwrap();
|
|
|
|
let ptr = scope
|
|
.block
|
|
.build(
|
|
format!("array.gep"),
|
|
Instr::GetElemPtr(kind.instr(), vec![first, idx]),
|
|
)
|
|
.unwrap()
|
|
.maybe_location(&mut scope.block, location);
|
|
|
|
let TypeKind::Ptr(inner) = array_ty else {
|
|
panic!();
|
|
};
|
|
let TypeKind::Array(elem_ty, _) = *inner else {
|
|
panic!();
|
|
};
|
|
|
|
if state.should_load {
|
|
Some(StackValue(
|
|
kind.derive(
|
|
scope
|
|
.block
|
|
.build(
|
|
"array.load",
|
|
Instr::Load(
|
|
ptr,
|
|
val_t.get_type(scope.type_values, scope.types),
|
|
),
|
|
)
|
|
.unwrap()
|
|
.maybe_location(&mut scope.block, location),
|
|
),
|
|
*elem_ty,
|
|
))
|
|
} else {
|
|
Some(StackValue(kind.derive(ptr), TypeKind::Ptr(elem_ty)))
|
|
}
|
|
}
|
|
mir::ExprKind::Array(expressions) => {
|
|
let stack_value_list = expressions
|
|
.iter()
|
|
.map(|e| e.codegen(scope, state).unwrap())
|
|
.collect::<Vec<_>>();
|
|
let instr_list = stack_value_list
|
|
.iter()
|
|
.map(|s| s.instr())
|
|
.collect::<Vec<_>>();
|
|
|
|
let elem_ty_kind = stack_value_list
|
|
.iter()
|
|
.map(|s| s.1.clone())
|
|
.next()
|
|
.unwrap_or(TypeKind::Void);
|
|
|
|
let array_ty = Type::Array(
|
|
Box::new(elem_ty_kind.get_type(scope.type_values, scope.types)),
|
|
instr_list.len() as u64,
|
|
);
|
|
let array_name = format!("{}.{}", elem_ty_kind, instr_list.len());
|
|
let load_n = format!("{}.load", array_name);
|
|
|
|
let array = scope
|
|
.block
|
|
.build(&array_name, Instr::Alloca(array_ty.clone()))
|
|
.unwrap()
|
|
.maybe_location(&mut scope.block, location);
|
|
|
|
for (index, instr) in instr_list.iter().enumerate() {
|
|
let gep_n = format!("{}.{}.gep", array_name, index);
|
|
let store_n = format!("{}.{}.store", array_name, index);
|
|
|
|
let index_expr = scope
|
|
.block
|
|
.build(
|
|
index.to_string(),
|
|
Instr::Constant(ConstValue::U32(index as u32)),
|
|
)
|
|
.unwrap();
|
|
let first = scope
|
|
.block
|
|
.build("zero", Instr::Constant(ConstValue::U32(0)))
|
|
.unwrap();
|
|
let ptr = scope
|
|
.block
|
|
.build(gep_n, Instr::GetElemPtr(array, vec![first, index_expr]))
|
|
.unwrap()
|
|
.maybe_location(&mut scope.block, location);
|
|
scope
|
|
.block
|
|
.build(store_n, Instr::Store(ptr, *instr))
|
|
.unwrap()
|
|
.maybe_location(&mut scope.block, location);
|
|
}
|
|
|
|
let array_val = scope
|
|
.block
|
|
.build(load_n, Instr::Load(array, array_ty))
|
|
.unwrap()
|
|
.maybe_location(&mut scope.block, location);
|
|
|
|
Some(StackValue(
|
|
StackValueKind::Literal(array_val),
|
|
TypeKind::Array(Box::new(elem_ty_kind), instr_list.len() as u64),
|
|
))
|
|
}
|
|
mir::ExprKind::Accessed(expression, type_kind, field) => {
|
|
let struct_val = expression.codegen(scope, &state.load(false)).unwrap();
|
|
|
|
let TypeKind::Ptr(inner) = &struct_val.1 else {
|
|
panic!("tried accessing non-pointer");
|
|
};
|
|
let TypeKind::CustomType(name) = *inner.clone() else {
|
|
panic!("tried accessing non-custom-type");
|
|
};
|
|
let TypeDefinitionKind::Struct(struct_ty) =
|
|
scope.get_typedef(&name).unwrap().kind.clone();
|
|
let idx = struct_ty.find_index(field).unwrap();
|
|
|
|
let gep_n = format!("{}.{}.gep", name, field);
|
|
let load_n = format!("{}.{}.load", name, field);
|
|
|
|
let value = scope
|
|
.block
|
|
.build(
|
|
gep_n,
|
|
Instr::GetStructElemPtr(struct_val.instr(), idx as u32),
|
|
)
|
|
.unwrap();
|
|
|
|
// value.maybe_location(&mut scope.block, location);
|
|
|
|
if state.should_load {
|
|
Some(StackValue(
|
|
struct_val.0.derive(
|
|
scope
|
|
.block
|
|
.build(
|
|
load_n,
|
|
Instr::Load(
|
|
value,
|
|
type_kind.get_type(scope.type_values, scope.types),
|
|
),
|
|
)
|
|
.unwrap(),
|
|
),
|
|
struct_ty.get_field_ty(&field).unwrap().clone(),
|
|
))
|
|
} else {
|
|
Some(StackValue(
|
|
struct_val.0.derive(value),
|
|
TypeKind::Ptr(Box::new(struct_ty.get_field_ty(&field).unwrap().clone())),
|
|
))
|
|
}
|
|
}
|
|
mir::ExprKind::Struct(name, items) => {
|
|
let struct_ty = Type::CustomType(*scope.type_values.get(name)?);
|
|
|
|
let load_n = format!("{}.load", name);
|
|
|
|
let struct_ptr = scope
|
|
.block
|
|
.build(name, Instr::Alloca(struct_ty.clone()))
|
|
.unwrap()
|
|
.maybe_location(&mut scope.block, location);
|
|
|
|
for (i, (field_n, exp)) in items.iter().enumerate() {
|
|
let gep_n = format!("{}.{}.gep", name, field_n);
|
|
let store_n = format!("{}.{}.store", name, field_n);
|
|
|
|
let elem_ptr = scope
|
|
.block
|
|
.build(gep_n, Instr::GetStructElemPtr(struct_ptr, i as u32))
|
|
.unwrap()
|
|
.maybe_location(&mut scope.block, location);
|
|
if let Some(val) = exp.codegen(scope, state) {
|
|
scope
|
|
.block
|
|
.build(store_n, Instr::Store(elem_ptr, val.instr()))
|
|
.unwrap()
|
|
.maybe_location(&mut scope.block, location);
|
|
}
|
|
}
|
|
|
|
let struct_val = scope
|
|
.block
|
|
.build(load_n, Instr::Load(struct_ptr, struct_ty))
|
|
.unwrap();
|
|
|
|
Some(StackValue(
|
|
StackValueKind::Literal(struct_val),
|
|
TypeKind::CustomType(name.clone()),
|
|
))
|
|
}
|
|
mir::ExprKind::Borrow(varref) => {
|
|
varref.0.known().expect("variable type unknown");
|
|
let v = scope
|
|
.stack_values
|
|
.get(&varref.1)
|
|
.expect("Variable reference not found?!");
|
|
Some(v.clone())
|
|
}
|
|
mir::ExprKind::Deref(varref) => {
|
|
varref.0.known().expect("variable type unknown");
|
|
let v = scope
|
|
.stack_values
|
|
.get(&varref.1)
|
|
.expect("Variable reference not found?!");
|
|
|
|
Some({
|
|
if state.should_load {
|
|
if let TypeKind::Ptr(inner) = &v.1 {
|
|
StackValue(
|
|
v.0.derive(
|
|
scope
|
|
.block
|
|
.build(
|
|
format!("{}.load2", varref.1),
|
|
Instr::Load(
|
|
v.instr(),
|
|
inner.get_type(scope.type_values, scope.types),
|
|
),
|
|
)
|
|
.unwrap(),
|
|
),
|
|
*inner.clone(),
|
|
)
|
|
} else {
|
|
panic!("Variable was not a pointer?!?")
|
|
}
|
|
} else {
|
|
v.clone()
|
|
}
|
|
})
|
|
}
|
|
};
|
|
if let Some(value) = &value {
|
|
value.instr().maybe_location(&mut scope.block, location);
|
|
}
|
|
value
|
|
}
|
|
}
|
|
|
|
impl mir::IfExpression {
|
|
fn codegen<'ctx, 'a>(&self, scope: &mut Scope<'ctx, 'a>, state: &State) -> Option<StackValue> {
|
|
let condition = self.0.codegen(scope, state).unwrap();
|
|
|
|
// Create blocks
|
|
let mut then_b = scope.function.ir.block("then");
|
|
let mut else_b = scope.function.ir.block("else");
|
|
let after_b = scope.function.ir.block("after");
|
|
|
|
if let Some(debug) = &scope.debug {
|
|
let before_location = self.0 .1.into_debug(scope.tokens).unwrap();
|
|
let before_v = debug.info.location(&debug.scope, before_location);
|
|
scope.block.set_terminator_location(before_v).unwrap();
|
|
|
|
let then_location = self.1.return_meta().into_debug(scope.tokens).unwrap();
|
|
let then_v = debug.info.location(&debug.scope, then_location);
|
|
then_b.set_terminator_location(then_v).unwrap();
|
|
|
|
let else_location = if let Some(else_block) = &self.2 {
|
|
else_block.return_meta().into_debug(scope.tokens).unwrap()
|
|
} else {
|
|
then_location
|
|
};
|
|
let else_v = debug.info.location(&debug.scope, else_location);
|
|
else_b.set_terminator_location(else_v).unwrap();
|
|
}
|
|
|
|
// Store for convenience
|
|
let then_bb = then_b.value();
|
|
let else_bb = else_b.value();
|
|
let after_bb = after_b.value();
|
|
|
|
// Generate then-block content
|
|
let mut then_scope = scope.with_block(then_b);
|
|
let then_res = self.1.codegen(&mut then_scope, state);
|
|
then_scope.block.terminate(Term::Br(after_bb)).ok();
|
|
|
|
let else_res = if let Some(else_block) = &self.2 {
|
|
let mut else_scope = scope.with_block(else_b);
|
|
|
|
scope
|
|
.block
|
|
.terminate(Term::CondBr(condition.instr(), then_bb, else_bb))
|
|
.unwrap();
|
|
|
|
let opt = else_block.codegen(&mut else_scope, state);
|
|
|
|
if let Some(ret) = opt {
|
|
else_scope.block.terminate(Term::Br(after_bb)).ok();
|
|
Some(ret)
|
|
} else {
|
|
None
|
|
}
|
|
} else {
|
|
else_b.terminate(Term::Br(after_bb)).unwrap();
|
|
scope
|
|
.block
|
|
.terminate(Term::CondBr(condition.instr(), then_bb, after_bb))
|
|
.unwrap();
|
|
None
|
|
};
|
|
|
|
// Swap block to the after-block so that construction can continue correctly
|
|
scope.swap_block(after_b);
|
|
|
|
if then_res.is_none() && else_res.is_none() {
|
|
None
|
|
} else {
|
|
let mut incoming = Vec::from(then_res.as_slice());
|
|
incoming.extend(else_res.clone());
|
|
let instr = scope
|
|
.block
|
|
.build(
|
|
"phi",
|
|
Instr::Phi(incoming.iter().map(|i| i.instr()).collect()),
|
|
)
|
|
.unwrap();
|
|
|
|
use StackValueKind::*;
|
|
let value = match (then_res, else_res) {
|
|
(None, None) => StackValue(StackValueKind::Immutable(instr), TypeKind::Void),
|
|
(Some(val), None) | (None, Some(val)) => StackValue(val.0.derive(instr), val.1),
|
|
(Some(lhs_val), Some(rhs_val)) => match (lhs_val.0, rhs_val.0) {
|
|
(Immutable(_), Immutable(_))
|
|
| (Immutable(_), Mutable(_))
|
|
| (Mutable(_), Immutable(_))
|
|
| (Immutable(_), Literal(_))
|
|
| (Literal(_), Immutable(_))
|
|
| (Mutable(_), Literal(_))
|
|
| (Literal(_), Mutable(_))
|
|
| (Literal(_), Literal(_)) => StackValue(Immutable(instr), lhs_val.1),
|
|
(Mutable(_), Mutable(_)) => StackValue(Mutable(instr), lhs_val.1),
|
|
},
|
|
};
|
|
Some(value)
|
|
}
|
|
}
|
|
}
|
|
impl mir::CmpOperator {
|
|
fn int_predicate(&self) -> CmpPredicate {
|
|
match self {
|
|
mir::CmpOperator::LT => CmpPredicate::LT,
|
|
mir::CmpOperator::GT => CmpPredicate::GT,
|
|
mir::CmpOperator::LE => CmpPredicate::LE,
|
|
mir::CmpOperator::GE => CmpPredicate::GE,
|
|
mir::CmpOperator::EQ => CmpPredicate::EQ,
|
|
mir::CmpOperator::NE => CmpPredicate::NE,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl mir::Literal {
|
|
fn as_const(&self, block: &mut Block) -> InstructionValue {
|
|
block
|
|
.build(format!("{}", self), self.as_const_kind())
|
|
.unwrap()
|
|
}
|
|
|
|
fn as_const_kind(&self) -> Instr {
|
|
Instr::Constant(match self.clone() {
|
|
mir::Literal::I8(val) => ConstValue::I8(val),
|
|
mir::Literal::I16(val) => ConstValue::I16(val),
|
|
mir::Literal::I32(val) => ConstValue::I32(val),
|
|
mir::Literal::I64(val) => ConstValue::I64(val),
|
|
mir::Literal::I128(val) => ConstValue::I128(val),
|
|
mir::Literal::U8(val) => ConstValue::U8(val),
|
|
mir::Literal::U16(val) => ConstValue::U16(val),
|
|
mir::Literal::U32(val) => ConstValue::U32(val),
|
|
mir::Literal::U64(val) => ConstValue::U64(val),
|
|
mir::Literal::U128(val) => ConstValue::U128(val),
|
|
mir::Literal::Bool(val) => ConstValue::Bool(val),
|
|
mir::Literal::String(val) => ConstValue::StringPtr(val.clone()),
|
|
mir::Literal::Vague(VagueLiteral::Number(val)) => ConstValue::I32(val as i32),
|
|
})
|
|
}
|
|
}
|
|
|
|
impl TypeKind {
|
|
fn get_type(
|
|
&self,
|
|
type_vals: &HashMap<String, TypeValue>,
|
|
typedefs: &HashMap<TypeValue, TypeDefinition>,
|
|
) -> Type {
|
|
match &self {
|
|
TypeKind::I8 => Type::I8,
|
|
TypeKind::I16 => Type::I16,
|
|
TypeKind::I32 => Type::I32,
|
|
TypeKind::I64 => Type::I64,
|
|
TypeKind::I128 => Type::I128,
|
|
TypeKind::U8 => Type::U8,
|
|
TypeKind::U16 => Type::U16,
|
|
TypeKind::U32 => Type::U32,
|
|
TypeKind::U64 => Type::U64,
|
|
TypeKind::U128 => Type::U128,
|
|
TypeKind::Bool => Type::Bool,
|
|
TypeKind::StringPtr => Type::Ptr(Box::new(Type::I8)),
|
|
TypeKind::Array(elem_t, len) => {
|
|
Type::Array(Box::new(elem_t.get_type(type_vals, typedefs)), *len)
|
|
}
|
|
TypeKind::Void => Type::Void,
|
|
TypeKind::Vague(_) => panic!("Tried to compile a vague type!"),
|
|
TypeKind::CustomType(n) => {
|
|
let type_val = type_vals.get(n).unwrap().clone();
|
|
Type::CustomType(type_val)
|
|
}
|
|
TypeKind::Ptr(type_kind) => {
|
|
Type::Ptr(Box::new(type_kind.get_type(type_vals, typedefs)))
|
|
}
|
|
TypeKind::Borrow(type_kind) => {
|
|
Type::Ptr(Box::new(type_kind.get_type(type_vals, typedefs)))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
impl TypeKind {
|
|
fn get_debug_type(&self, debug: &Debug, scope: &Scope) -> DebugTypeValue {
|
|
self.get_debug_type_hard(
|
|
debug.scope,
|
|
debug.info,
|
|
debug.types,
|
|
scope.type_values,
|
|
scope.types,
|
|
scope.tokens,
|
|
)
|
|
}
|
|
|
|
fn get_debug_type_hard(
|
|
&self,
|
|
scope: DebugProgramValue,
|
|
debug_info: &DebugInformation,
|
|
debug_types: &HashMap<TypeKind, DebugTypeValue>,
|
|
type_values: &HashMap<String, TypeValue>,
|
|
types: &HashMap<TypeValue, TypeDefinition>,
|
|
tokens: &Vec<FullToken>,
|
|
) -> DebugTypeValue {
|
|
if let Some(ty) = debug_types.get(self) {
|
|
return *ty;
|
|
}
|
|
|
|
let name = format!("{}", self);
|
|
|
|
let data = match self {
|
|
TypeKind::StringPtr => DebugTypeData::Pointer(DebugPointerType {
|
|
name,
|
|
pointee: TypeKind::I8.get_debug_type_hard(
|
|
scope,
|
|
debug_info,
|
|
debug_types,
|
|
type_values,
|
|
types,
|
|
tokens,
|
|
),
|
|
size_bits: self.size_of(),
|
|
}),
|
|
TypeKind::Ptr(inner) | TypeKind::Borrow(inner) => {
|
|
DebugTypeData::Pointer(DebugPointerType {
|
|
name,
|
|
pointee: inner.get_debug_type_hard(
|
|
scope,
|
|
debug_info,
|
|
debug_types,
|
|
type_values,
|
|
types,
|
|
tokens,
|
|
),
|
|
size_bits: self.size_of(),
|
|
})
|
|
}
|
|
TypeKind::Array(elem_ty, len) => {
|
|
let elem_ty = elem_ty.clone().get_debug_type_hard(
|
|
scope,
|
|
debug_info,
|
|
debug_types,
|
|
type_values,
|
|
types,
|
|
tokens,
|
|
);
|
|
DebugTypeData::Array(DebugArrayType {
|
|
size_bits: self.size_of(),
|
|
align_bits: self.alignment(),
|
|
element_type: elem_ty,
|
|
length: *len,
|
|
})
|
|
}
|
|
TypeKind::CustomType(name) => {
|
|
let typedef = types.get(type_values.get(name).unwrap()).unwrap();
|
|
|
|
match &typedef.kind {
|
|
TypeDefinitionKind::Struct(struct_type) => {
|
|
let mut fields = Vec::new();
|
|
let mut size_bits = 0;
|
|
for field in &struct_type.0 {
|
|
fields.push(DebugFieldType {
|
|
name: field.0.clone(),
|
|
location: field.2.into_debug(tokens).unwrap(),
|
|
size_bits: field.1.size_of(),
|
|
offset: size_bits,
|
|
flags: DwarfFlags,
|
|
ty: field.1.get_debug_type_hard(
|
|
scope,
|
|
debug_info,
|
|
debug_types,
|
|
type_values,
|
|
types,
|
|
tokens,
|
|
),
|
|
});
|
|
size_bits += field.1.size_of();
|
|
}
|
|
{
|
|
DebugTypeData::Struct(DebugStructType {
|
|
name: name.clone(),
|
|
scope,
|
|
location: typedef.meta.into_debug(tokens).unwrap(),
|
|
size_bits,
|
|
flags: DwarfFlags,
|
|
fields,
|
|
})
|
|
}
|
|
}
|
|
}
|
|
}
|
|
_ => DebugTypeData::Basic(DebugBasicType {
|
|
name,
|
|
size_bits: self.size_of(),
|
|
encoding: match self {
|
|
TypeKind::Bool => DwarfEncoding::Boolean,
|
|
TypeKind::I8 => DwarfEncoding::SignedChar,
|
|
TypeKind::U8 => DwarfEncoding::UnsignedChar,
|
|
TypeKind::I16 | TypeKind::I32 | TypeKind::I64 | TypeKind::I128 => {
|
|
DwarfEncoding::Signed
|
|
}
|
|
TypeKind::U16 | TypeKind::U32 | TypeKind::U64 | TypeKind::U128 => {
|
|
DwarfEncoding::Unsigned
|
|
}
|
|
TypeKind::Void => DwarfEncoding::Address,
|
|
TypeKind::StringPtr => DwarfEncoding::Address,
|
|
TypeKind::Array(_, _) => DwarfEncoding::Address,
|
|
TypeKind::CustomType(_) => DwarfEncoding::Address,
|
|
_ => panic!("tried fetching debug-type for non-supported type!"),
|
|
},
|
|
flags: DwarfFlags,
|
|
}),
|
|
};
|
|
debug_info.debug_type(data)
|
|
}
|
|
}
|
|
|
|
impl Metadata {
|
|
pub fn into_debug(&self, tokens: &Vec<FullToken>) -> Option<DebugLocation> {
|
|
if let Some((start, _)) = self.into_positions(tokens) {
|
|
Some(start.into())
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Into<DebugLocation> for Position {
|
|
fn into(self) -> DebugLocation {
|
|
DebugLocation {
|
|
line: self.1,
|
|
column: self.0,
|
|
}
|
|
}
|
|
}
|