1648 lines
59 KiB
Rust
1648 lines
59 KiB
Rust
#![allow(dead_code)]
|
|
|
|
use std::{
|
|
cmp::Ordering,
|
|
collections::{BTreeMap, BTreeSet, HashMap},
|
|
};
|
|
|
|
use crate::{
|
|
ast::{IntegralType, Node as AstNode, Tag, Type},
|
|
parser::Tree,
|
|
string_table::{ImmOrIndex, Index as StringsIndex, StringTable},
|
|
variant, write_indented, writeln_indented,
|
|
};
|
|
|
|
type Node = u32;
|
|
|
|
#[derive(Debug)]
|
|
enum NodeOrList {
|
|
Node(Node), // node of alloca location
|
|
List(Vec<Node>), // list of references to `Node(_)`
|
|
}
|
|
|
|
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
|
pub enum Type2 {
|
|
Integral(bool, u16),
|
|
Binary32,
|
|
Binary64,
|
|
Bool,
|
|
Pointer,
|
|
}
|
|
|
|
impl Into<mir::Type> for Type2 {
|
|
fn into(self) -> mir::Type {
|
|
self.mir_type()
|
|
}
|
|
}
|
|
|
|
impl Type2 {
|
|
fn mir_type(self) -> mir::Type {
|
|
match self {
|
|
Type2::Integral(_, bits) => mir::Type::from_bitsize_int(bits as u32),
|
|
Type2::Binary32 => mir::Type::SinglePrecision,
|
|
Type2::Binary64 => mir::Type::DoublePrecision,
|
|
Type2::Bool => mir::Type::from_bitsize_int(1),
|
|
Type2::Pointer => mir::Type::QWord,
|
|
}
|
|
}
|
|
fn is_signed(self) -> bool {
|
|
match self {
|
|
Type2::Integral(signed, _) => signed,
|
|
_ => false,
|
|
}
|
|
}
|
|
fn mir_unalignment(self) -> Option<(bool, u16)> {
|
|
match self {
|
|
Type2::Integral(signed, bits) => match bits {
|
|
8 | 16 | 32 | 64 => None,
|
|
bits => Some((signed, bits)),
|
|
},
|
|
_ => None,
|
|
}
|
|
}
|
|
fn size(&self) -> u32 {
|
|
match self {
|
|
Type2::Integral(_signed, bits) => bits.div_ceil(8) as u32,
|
|
Type2::Binary32 => 4,
|
|
Type2::Binary64 => 8,
|
|
Type2::Bool => 1,
|
|
Type2::Pointer => 8,
|
|
}
|
|
}
|
|
|
|
fn align(&self) -> u32 {
|
|
self.size()
|
|
}
|
|
|
|
fn try_from_ast_type(ty: &Type) -> Option<Self> {
|
|
match ty {
|
|
Type::Bool => Some(Type2::Bool),
|
|
Type::Integer(i) => Some(Type2::Integral(i.signed, i.bits)),
|
|
Type::Floating(f) => match f {
|
|
crate::ast::FloatingType::Binary32 => Some(Type2::Binary32),
|
|
crate::ast::FloatingType::Binary64 => Some(Type2::Binary64),
|
|
},
|
|
Type::Pointer { .. } => Some(Type2::Pointer),
|
|
_ => {
|
|
None
|
|
//unimplemented!("conversion from {value:?} to triples type not implemented")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
impl core::fmt::Display for Type2 {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
match self {
|
|
Type2::Integral(signed, bits) => write!(f, "{}{bits}", if *signed { "i" } else { "u" }),
|
|
Type2::Binary32 => write!(f, "f32"),
|
|
Type2::Binary64 => write!(f, "f64"),
|
|
Type2::Bool => write!(f, "bool"),
|
|
Type2::Pointer => write!(f, "ptr"),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<Type> for Type2 {
|
|
fn from(value: Type) -> Self {
|
|
(&value).into()
|
|
}
|
|
}
|
|
|
|
impl From<&Type> for Type2 {
|
|
fn from(value: &Type) -> Self {
|
|
match value {
|
|
Type::Bool => Type2::Bool,
|
|
Type::Integer(i) => Type2::Integral(i.signed, i.bits),
|
|
Type::Floating(f) => match f {
|
|
crate::ast::FloatingType::Binary32 => Type2::Binary32,
|
|
crate::ast::FloatingType::Binary64 => Type2::Binary64,
|
|
},
|
|
Type::Pointer { .. } => Type2::Pointer,
|
|
Type::Fn { .. } => Type2::Pointer,
|
|
_ => {
|
|
unimplemented!("conversion from {value:?} to triples type not implemented")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
|
pub enum Inst {
|
|
/// index
|
|
Label,
|
|
/// ast-node -> index
|
|
ExternRef(Type2),
|
|
/// index
|
|
FunctionStart,
|
|
/// None
|
|
FunctionEnd, // marker
|
|
/// lhs
|
|
Argument(Type2),
|
|
InlineType(Type2),
|
|
/// list of arguments
|
|
/// start, (inlinetype)end
|
|
Call(Node),
|
|
/// Value
|
|
/// lhs
|
|
GlobalConstant(StringsIndex, Type2),
|
|
/// u32
|
|
ConstantU32,
|
|
/// lo, hi
|
|
ConstantU64,
|
|
/// index
|
|
ConstantMultiByte,
|
|
/// size, align
|
|
Alloca,
|
|
/// src
|
|
Load(Type2),
|
|
/// src, dst
|
|
Store(Type2),
|
|
/// ptr, index,
|
|
GetElementPtr(Type2),
|
|
/// size, align
|
|
Parameter(Type2),
|
|
/// lhs, rhs
|
|
Add(Type2),
|
|
/// lhs, rhs
|
|
Sub(Type2),
|
|
/// lhs, rhs
|
|
Mul(Type2),
|
|
/// lhs, rhs
|
|
Div(Type2),
|
|
/// lhs, rhs
|
|
Rem(Type2),
|
|
/// lhs, rhs
|
|
BitAnd(Type2),
|
|
/// lhs, rhs
|
|
BitOr(Type2),
|
|
/// lhs, rhs
|
|
BitXOr(Type2),
|
|
/// lhs, rhs
|
|
ShiftLeft(Type2),
|
|
/// lhs, rhs
|
|
ShiftRight(Type2),
|
|
/// lhs
|
|
Negate(Type2),
|
|
/// lhs
|
|
Not(Type2),
|
|
/// lhs, rhs
|
|
Eq(Type2),
|
|
/// lhs, rhs
|
|
Neq(Type2),
|
|
/// lhs, rhs
|
|
Gt(Type2),
|
|
/// lhs, rhs
|
|
Lt(Type2),
|
|
/// lhs, rhs
|
|
Ge(Type2),
|
|
/// lhs, rhs
|
|
Le(Type2),
|
|
/// lhs
|
|
ExplicitCast(Type2, Type2),
|
|
/// lhs
|
|
ReturnValue(Type2),
|
|
/// no parameters
|
|
Return,
|
|
/// Node is a bool
|
|
/// two labels
|
|
/// lhs, rhs
|
|
Branch(Node),
|
|
/// lhs: Label
|
|
Jump,
|
|
/// lhs, rhs
|
|
Phi2(Type2),
|
|
}
|
|
|
|
impl Inst {
|
|
fn is_constant(self) -> bool {
|
|
match self {
|
|
Inst::ConstantU32 | Inst::ConstantU64 | Inst::ConstantMultiByte => true,
|
|
|
|
_ => false,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, Copy)]
|
|
struct Data {
|
|
lhs: u32,
|
|
rhs: u32,
|
|
}
|
|
|
|
impl Data {
|
|
fn new(lhs: u32, rhs: u32) -> Self {
|
|
Self { lhs, rhs }
|
|
}
|
|
|
|
fn lhs(lhs: u32) -> Data {
|
|
Self { lhs, rhs: 0 }
|
|
}
|
|
|
|
fn as_u32(&self) -> u32 {
|
|
self.lhs
|
|
}
|
|
fn as_u64(&self) -> u64 {
|
|
self.lhs as u64 | (self.rhs as u64) << u32::BITS as u64
|
|
}
|
|
fn as_index(&self) -> StringsIndex {
|
|
crate::string_table::Index {
|
|
start: self.lhs,
|
|
end: self.rhs,
|
|
}
|
|
}
|
|
fn as_lhs_rhs(&self) -> (u32, u32) {
|
|
(self.lhs, self.rhs)
|
|
}
|
|
}
|
|
|
|
impl From<u32> for Data {
|
|
fn from(value: u32) -> Self {
|
|
Self { lhs: value, rhs: 0 }
|
|
}
|
|
}
|
|
|
|
impl From<u64> for Data {
|
|
fn from(value: u64) -> Self {
|
|
let (lo, hi) = { (value as u32, (value >> u32::BITS as u64) as u32) };
|
|
Self { lhs: lo, rhs: hi }
|
|
}
|
|
}
|
|
|
|
impl From<crate::string_table::ImmOrIndex> for Data {
|
|
fn from(value: crate::string_table::ImmOrIndex) -> Self {
|
|
match value {
|
|
ImmOrIndex::U64(v) => v.into(),
|
|
ImmOrIndex::U32(v) => v.into(),
|
|
ImmOrIndex::Index(v) => v.into(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<crate::string_table::Index> for Data {
|
|
fn from(value: crate::string_table::Index) -> Self {
|
|
Self {
|
|
lhs: value.start,
|
|
rhs: value.end,
|
|
}
|
|
}
|
|
}
|
|
|
|
pub struct IRBuilder<'tree, 'ir> {
|
|
ir: &'ir mut IR,
|
|
tree: &'tree mut Tree,
|
|
type_map: HashMap<AstNode, Type>,
|
|
lookup: HashMap<AstNode, NodeOrList>,
|
|
fixup: Vec<Node>,
|
|
}
|
|
|
|
impl core::ops::Index<Node> for IR {
|
|
type Output = Inst;
|
|
|
|
fn index(&self, index: Node) -> &Self::Output {
|
|
&self.nodes[index as usize]
|
|
}
|
|
}
|
|
|
|
impl<'tree, 'ir> IRBuilder<'tree, 'ir> {
|
|
fn new(ir: &'ir mut IR, tree: &'tree mut Tree) -> Self {
|
|
Self {
|
|
ir,
|
|
tree,
|
|
type_map: HashMap::new(),
|
|
lookup: HashMap::new(),
|
|
fixup: vec![],
|
|
}
|
|
}
|
|
|
|
fn peer_type_of_nodes(&self, lhs: AstNode, rhs: AstNode) -> Option<Type> {
|
|
let lty = self.tree.type_of_node(lhs);
|
|
let rty = self.tree.type_of_node(rhs);
|
|
|
|
let peer = lty
|
|
.equal_type(&rty)
|
|
.expect(&format!("types {lty} and {rty} are incompatible!"));
|
|
|
|
if lty == Type::ComptimeNumber {
|
|
eprintln!(
|
|
"%{lhs} is comptime number: {:?}",
|
|
self.tree.nodes.get_node(lhs)
|
|
);
|
|
let value = self.tree.value_of_comptime_node(lhs)?;
|
|
if value.bit_count() > rty.bit_width() {
|
|
panic!("comptime number is incompatible with type {rty}!");
|
|
}
|
|
}
|
|
if rty == Type::ComptimeNumber {
|
|
eprintln!("%{rhs} is comptime number");
|
|
let value = self.tree.value_of_comptime_node(rhs)?;
|
|
if value.bit_count() > lty.bit_width() {
|
|
panic!("comptime number is incompatible with type {lty}!");
|
|
}
|
|
}
|
|
|
|
Some(peer)
|
|
}
|
|
|
|
fn visit(&mut self, node: AstNode) -> Node {
|
|
let tag = &self.tree.nodes[node].clone();
|
|
match tag {
|
|
Tag::FunctionDecl { proto, body } => {
|
|
variant!(
|
|
Tag::FunctionProto {
|
|
name,
|
|
parameters,
|
|
..
|
|
} = self.tree.nodes.get_node(*proto)
|
|
);
|
|
|
|
self.ir.push(Inst::FunctionStart, {
|
|
variant!(Tag::Ident { name } = self.tree.nodes.get_node(*name));
|
|
Some((*name).into())
|
|
});
|
|
|
|
if let Some(parameters) = parameters {
|
|
variant!(
|
|
Tag::ParameterList { parameters } = self.tree.nodes.get_node(*parameters)
|
|
);
|
|
|
|
for param in parameters {
|
|
variant!(Tag::Parameter { ty, .. } = self.tree.nodes.get_node(*param));
|
|
let ty = self.tree.type_of_node(*ty);
|
|
let size = ty.size_of();
|
|
let align = ty.align_of();
|
|
let ir = self
|
|
.ir
|
|
.push(Inst::Parameter(ty.into()), Some(Data::new(size, align)));
|
|
|
|
self.lookup.insert(*param, NodeOrList::Node(ir));
|
|
}
|
|
}
|
|
|
|
self.tree.st.into_child(node);
|
|
let value = self.visit(*body);
|
|
self.tree.st.into_parent();
|
|
if value != !0 {
|
|
let ty = self.tree.type_of_node(*body);
|
|
self.ir
|
|
.push(Inst::ReturnValue(ty.into()), Some(Data::lhs(value)));
|
|
}
|
|
|
|
self.ir.push(Inst::FunctionEnd, None)
|
|
}
|
|
Tag::CallExpr { lhs, rhs } => {
|
|
let ty = self.tree.type_of_node(*lhs).return_type().unwrap().clone();
|
|
let args =
|
|
if let Some(args) = *rhs {
|
|
variant!(
|
|
self.tree.nodes.get_node(args) => Tag::ArgumentList { arguments }
|
|
);
|
|
|
|
let args = arguments.clone().iter().map(|arg| {
|
|
variant!(*self.tree.nodes.get_node(*arg) => Tag::Argument { expr ,..});
|
|
(self.visit(expr), self.tree.type_of_node(expr))
|
|
}).collect::<Vec<_>>();
|
|
|
|
let start = self.ir.nodes.len();
|
|
for (arg, ty) in args {
|
|
_ = self
|
|
.ir
|
|
.push(Inst::Argument(ty.into()), Some(Data::lhs(arg)));
|
|
}
|
|
let end = self.ir.nodes.len();
|
|
|
|
Some((start as u32, end as u32))
|
|
} else {
|
|
None
|
|
};
|
|
let (start, end) =
|
|
args.unwrap_or((self.ir.nodes.len() as u32, self.ir.nodes.len() as u32));
|
|
self.ir.push(Inst::InlineType(ty.into()), None);
|
|
|
|
let func = self.visit(*lhs);
|
|
|
|
self.ir
|
|
.push(Inst::Call(func), Some(Data::new(start, end + 1)))
|
|
}
|
|
Tag::Block {
|
|
statements,
|
|
trailing_expr,
|
|
} => {
|
|
for stmt in statements {
|
|
self.visit(*stmt);
|
|
}
|
|
|
|
if let Some(expr) = trailing_expr {
|
|
self.visit(*expr)
|
|
} else {
|
|
!0
|
|
}
|
|
}
|
|
Tag::VarDecl { assignment, .. } => {
|
|
let ty = self.tree.type_of_node(node);
|
|
let alloca = self
|
|
.ir
|
|
.push(Inst::Alloca, Some(Data::new(ty.size_of(), ty.align_of())));
|
|
|
|
if let Some(assignment) = assignment {
|
|
let value = self.visit(*assignment);
|
|
// discard store
|
|
let _ = self
|
|
.ir
|
|
.push(Inst::Store(ty.into()), Some(Data::new(value, alloca)));
|
|
}
|
|
self.lookup.insert(node, NodeOrList::Node(alloca));
|
|
alloca
|
|
}
|
|
Tag::GlobalDecl {
|
|
name, assignment, ..
|
|
} => {
|
|
let ty = self.tree.type_of_node(node);
|
|
let value = self.visit(*assignment);
|
|
assert!(self.tree.is_node_comptime(*assignment, true));
|
|
|
|
variant!(self.tree.nodes.get_node(*name) => Tag::Ident { name });
|
|
|
|
let global = self.ir.push(
|
|
Inst::GlobalConstant(*name, ty.into()),
|
|
Some(Data::lhs(value)),
|
|
);
|
|
|
|
self.lookup.insert(node, NodeOrList::Node(global));
|
|
|
|
global
|
|
}
|
|
Tag::GlobalRef(decl) => {
|
|
let ty = self.tree.type_of_node(*decl);
|
|
let node = self
|
|
.ir
|
|
.push(Inst::ExternRef(ty.into()), Some(Data::lhs(decl.get())));
|
|
self.fixup.push(node);
|
|
node
|
|
}
|
|
Tag::DeclRef(decl) => match self.lookup.get_mut(decl) {
|
|
Some(NodeOrList::Node(decl)) => *decl,
|
|
lookup => {
|
|
println!("lookup for ast decl %{}", decl.get());
|
|
println!("{lookup:?}");
|
|
panic!("should not have any unresolved lookups")
|
|
}
|
|
},
|
|
Tag::Assign { lhs, rhs } => {
|
|
let ty = self.tree.type_of_node(node);
|
|
let dest = self.visit(*lhs);
|
|
let source = self.visit(*rhs);
|
|
|
|
self.ir
|
|
.push(Inst::Store(ty.into()), Some(Data::new(source, dest)))
|
|
}
|
|
Tag::ReturnStmt { expr } => {
|
|
if let Some(expr) = expr {
|
|
let ty = self.tree.type_of_node(*expr);
|
|
let expr = self.visit(*expr);
|
|
self.ir
|
|
.push(Inst::ReturnValue(ty.into()), Some(Data::lhs(expr)))
|
|
} else {
|
|
self.ir.push(Inst::Return, None)
|
|
}
|
|
}
|
|
Tag::ExprStmt { expr } => self.visit(*expr),
|
|
Tag::Deref { lhs } => {
|
|
let ty = self.tree.type_of_node(*lhs).pointee().unwrap().clone();
|
|
let lhs = self.visit(*lhs);
|
|
self.ir.push(Inst::Load(ty.into()), Some(Data::lhs(lhs)))
|
|
}
|
|
Tag::Add { lhs, rhs } => {
|
|
let ty = self.tree.type_of_node(node);
|
|
let lhs = self.visit(*lhs);
|
|
let rhs = self.visit(*rhs);
|
|
self.ir
|
|
.push(Inst::Add(ty.into()), Some(Data::new(lhs, rhs)))
|
|
}
|
|
Tag::Sub { lhs, rhs } => {
|
|
let ty = self.tree.type_of_node(node);
|
|
let lhs = self.visit(*lhs);
|
|
let rhs = self.visit(*rhs);
|
|
self.ir
|
|
.push(Inst::Sub(ty.into()), Some(Data::new(lhs, rhs)))
|
|
}
|
|
Tag::BitAnd { lhs, rhs } => {
|
|
let ty = self.tree.type_of_node(node);
|
|
let lhs = self.visit(*lhs);
|
|
let rhs = self.visit(*rhs);
|
|
self.ir
|
|
.push(Inst::BitAnd(ty.into()), Some(Data::new(lhs, rhs)))
|
|
}
|
|
Tag::BitOr { lhs, rhs } => {
|
|
let ty = self.tree.type_of_node(node);
|
|
let lhs = self.visit(*lhs);
|
|
let rhs = self.visit(*rhs);
|
|
self.ir
|
|
.push(Inst::BitOr(ty.into()), Some(Data::new(lhs, rhs)))
|
|
}
|
|
Tag::BitXOr { lhs, rhs } => {
|
|
let ty = self.tree.type_of_node(node);
|
|
let lhs = self.visit(*lhs);
|
|
let rhs = self.visit(*rhs);
|
|
self.ir
|
|
.push(Inst::BitXOr(ty.into()), Some(Data::new(lhs, rhs)))
|
|
}
|
|
Tag::Lt { lhs, rhs }
|
|
| Tag::Ge { lhs, rhs }
|
|
| Tag::Le { lhs, rhs }
|
|
| Tag::Eq { lhs, rhs }
|
|
| Tag::NEq { lhs, rhs }
|
|
| Tag::Gt { lhs, rhs } => {
|
|
let ty = self
|
|
.tree
|
|
.peer_type_of_nodes(*lhs, *rhs)
|
|
.expect({
|
|
let at = self.tree.type_of_node(*lhs);
|
|
let bt = self.tree.type_of_node(*rhs);
|
|
&format!("incompatible types for %{lhs}({at}) and %{rhs}({bt})")
|
|
})
|
|
.into();
|
|
|
|
let inst = match tag {
|
|
Tag::Eq { .. } => Inst::Eq(ty),
|
|
Tag::NEq { .. } => Inst::Neq(ty),
|
|
Tag::Gt { .. } => Inst::Gt(ty),
|
|
Tag::Lt { .. } => Inst::Lt(ty),
|
|
Tag::Ge { .. } => Inst::Ge(ty),
|
|
Tag::Le { .. } => Inst::Le(ty),
|
|
_ => unreachable!(),
|
|
};
|
|
|
|
let lhs = self.visit(*lhs);
|
|
let rhs = self.visit(*rhs);
|
|
self.ir.push(inst, Some(Data::new(lhs, rhs)))
|
|
}
|
|
Tag::Mul { lhs, rhs } => {
|
|
let ty = self.tree.type_of_node(node);
|
|
let lhs = self.visit(*lhs);
|
|
let rhs = self.visit(*rhs);
|
|
self.ir
|
|
.push(Inst::Mul(ty.into()), Some(Data::new(lhs, rhs)))
|
|
}
|
|
Tag::Negate { lhs } => {
|
|
let ty = self.tree.type_of_node(node);
|
|
let lhs = self.visit(*lhs);
|
|
self.ir.push(Inst::Negate(ty.into()), Some(Data::lhs(lhs)))
|
|
}
|
|
Tag::Not { lhs } => {
|
|
let ty = self.tree.type_of_node(node);
|
|
let lhs = self.visit(*lhs);
|
|
self.ir.push(Inst::Not(ty.into()), Some(Data::lhs(lhs)))
|
|
}
|
|
Tag::Shl { lhs, rhs } => {
|
|
let ty = self.tree.type_of_node(node);
|
|
let lhs = self.visit(*lhs);
|
|
let rhs = self.visit(*rhs);
|
|
self.ir
|
|
.push(Inst::ShiftLeft(ty.into()), Some(Data::new(lhs, rhs)))
|
|
}
|
|
Tag::Shr { lhs, rhs } => {
|
|
let ty = self.tree.type_of_node(node);
|
|
let lhs = self.visit(*lhs);
|
|
let rhs = self.visit(*rhs);
|
|
self.ir
|
|
.push(Inst::ShiftRight(ty.into()), Some(Data::new(lhs, rhs)))
|
|
}
|
|
Tag::Ref { lhs } => {
|
|
let ty = self.tree.type_of_node(*lhs);
|
|
let lhs = self.visit(*lhs);
|
|
// self.ir.push(Inst::Load(ty.into()), Some(Data::lhs(lhs)))
|
|
// nothing happens here because lhs is of type pointer
|
|
self.ir
|
|
.push(Inst::GetElementPtr(ty.into()), Some(Data::new(lhs, 0)))
|
|
}
|
|
Tag::Constant { bytes, .. } => match bytes {
|
|
ImmOrIndex::U64(v) => self.ir.push(Inst::ConstantU64, Some((*v).into())),
|
|
ImmOrIndex::U32(v) => self.ir.push(Inst::ConstantU32, Some((*v).into())),
|
|
ImmOrIndex::Index(idx) => {
|
|
self.ir.push(Inst::ConstantMultiByte, Some((*idx).into()))
|
|
}
|
|
},
|
|
Tag::ExplicitCast { lhs, typename } => {
|
|
let l_ty = self.tree.type_of_node(*lhs).clone();
|
|
let r_ty = self.tree.type_of_node(*typename).clone();
|
|
|
|
let lhs = self.visit(*lhs);
|
|
|
|
if l_ty.bit_width() == r_ty.bit_width() {
|
|
//noop?
|
|
lhs
|
|
} else {
|
|
self.ir.push(
|
|
Inst::ExplicitCast(l_ty.into(), r_ty.into()),
|
|
Some(Data::lhs(lhs)),
|
|
)
|
|
}
|
|
}
|
|
Tag::IfExpr { condition, body } => {
|
|
assert_eq!(self.tree.type_of_node(*condition), Type::Bool);
|
|
|
|
let condition = self.visit(*condition);
|
|
let br = self.ir.push(Inst::Branch(condition), None);
|
|
|
|
let label_lhs = self.ir.push(Inst::Label, Some(StringsIndex::none().into()));
|
|
let _ = self.visit(*body);
|
|
let jmp = self.ir.push(Inst::Jump, None);
|
|
let nojump = self.ir.push(Inst::Label, Some(StringsIndex::none().into()));
|
|
|
|
self.ir.data[br as usize] = Some(Data::new(label_lhs, nojump));
|
|
self.ir.data[jmp as usize] = Some(Data::lhs(nojump));
|
|
br
|
|
}
|
|
Tag::IfElseExpr {
|
|
condition,
|
|
body,
|
|
else_expr,
|
|
} => {
|
|
assert_eq!(self.tree.type_of_node(*condition), Type::Bool);
|
|
let ty = self.tree.peer_type_of_nodes_unwrap(*body, *else_expr);
|
|
|
|
let condition = self.visit(*condition);
|
|
let br = self.ir.push(Inst::Branch(condition), None);
|
|
|
|
let label_lhs = self.ir.push(Inst::Label, Some(StringsIndex::none().into()));
|
|
let lhs = self.visit(*body);
|
|
let ljmp = self.ir.push(Inst::Jump, None);
|
|
let label_rhs = self.ir.push(Inst::Label, Some(StringsIndex::none().into()));
|
|
let rhs = self.visit(*else_expr);
|
|
let rjmp = self.ir.push(Inst::Jump, None);
|
|
|
|
let nojump = self.ir.push(Inst::Label, Some(StringsIndex::none().into()));
|
|
let phi = if let Some(ty) = Type2::try_from_ast_type(&ty) {
|
|
self.ir.push(Inst::Phi2(ty), Some(Data::new(lhs, rhs)))
|
|
} else {
|
|
br
|
|
};
|
|
|
|
self.ir.data[br as usize] = Some(Data::new(label_lhs, label_rhs));
|
|
self.ir.data[ljmp as usize] = Some(Data::lhs(nojump));
|
|
self.ir.data[rjmp as usize] = Some(Data::lhs(nojump));
|
|
phi
|
|
}
|
|
_ => {
|
|
dbg!(&self.tree.nodes[node]);
|
|
todo!()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pub struct IR {
|
|
nodes: Vec<Inst>,
|
|
data: Vec<Option<Data>>,
|
|
}
|
|
|
|
impl IR {
|
|
pub fn new() -> Self {
|
|
Self {
|
|
nodes: Vec::new(),
|
|
data: Vec::new(),
|
|
}
|
|
}
|
|
|
|
fn push(&mut self, inst: Inst, data: Option<Data>) -> u32 {
|
|
let node = self.nodes.len() as u32;
|
|
self.nodes.push(inst);
|
|
self.data.push(data);
|
|
node
|
|
}
|
|
|
|
pub fn build<'a, 'tree>(&'a mut self, tree: &'tree mut Tree) -> IRBuilder<'tree, 'a> {
|
|
let global_decls = tree.global_decls.clone();
|
|
let mut builder = IRBuilder::new(self, tree);
|
|
|
|
for node in &global_decls {
|
|
builder.visit(*node);
|
|
}
|
|
|
|
for &fix in builder.fixup.iter() {
|
|
let ast_node = builder.ir.data[fix as usize].unwrap().lhs;
|
|
|
|
let idx = match builder.tree.nodes.get_node(AstNode::new(ast_node).unwrap()) {
|
|
Tag::FunctionDecl { proto, .. } => {
|
|
variant!(builder.tree.nodes.get_node(*proto) => Tag::FunctionProto { name,..});
|
|
variant!(builder.tree.nodes.get_node(*name) => Tag::Ident { name });
|
|
*name
|
|
}
|
|
Tag::GlobalDecl { name, .. } => {
|
|
variant!(builder.tree.nodes.get_node(*name) => Tag::Ident { name });
|
|
*name
|
|
}
|
|
_ => {
|
|
unreachable!()
|
|
}
|
|
};
|
|
builder.ir.data[fix as usize] = Some(idx.into());
|
|
}
|
|
|
|
builder
|
|
}
|
|
}
|
|
|
|
impl<'tree, 'ir> IRBuilder<'tree, 'ir> {
|
|
fn render_node<W: core::fmt::Write>(
|
|
&self,
|
|
w: &mut W,
|
|
node: Node,
|
|
indent: u32,
|
|
) -> core::fmt::Result {
|
|
let data = self.ir.data[node as usize]
|
|
.clone()
|
|
.unwrap_or(Data::new(0, 0));
|
|
let inst = self.ir.nodes[node as usize];
|
|
match inst {
|
|
Inst::Label => {
|
|
let label = self.tree.strings.get_str(data.as_index());
|
|
writeln_indented!(indent - 1, w, "%{} = label \"{label}\":", node)?;
|
|
}
|
|
Inst::FunctionStart => {
|
|
let label = self.tree.strings.get_str(data.as_index());
|
|
writeln_indented!(indent - 1, w, "%{} = func {label}:", node)?;
|
|
}
|
|
Inst::FunctionEnd => {
|
|
writeln_indented!(indent - 1, w, "end func")?;
|
|
}
|
|
Inst::Parameter(ty) => {
|
|
let (size, align) = data.as_lhs_rhs();
|
|
writeln_indented!(
|
|
indent,
|
|
w,
|
|
"%{} = param {ty} (size: {}, align: {})",
|
|
node,
|
|
size,
|
|
align
|
|
)?;
|
|
}
|
|
Inst::Argument(ty) => {
|
|
let lhs = data.lhs;
|
|
writeln_indented!(indent, w, "%{node} = argument {ty} %{lhs}",)?;
|
|
}
|
|
Inst::InlineType(_) => {}
|
|
Inst::Call(func) => {
|
|
let (start, end) = data.as_lhs_rhs();
|
|
variant!(&self.ir.nodes[end as usize -1] => Inst::InlineType(ty));
|
|
|
|
write_indented!(indent, w, "%{node} = {ty} call %{func}(",)?;
|
|
if start + 1 == end {
|
|
} else {
|
|
for arg in start..(end - 1) {
|
|
let arg = self.ir.data[arg as usize].unwrap().lhs;
|
|
write_indented!(indent, w, "%{arg}, ")?;
|
|
}
|
|
}
|
|
writeln_indented!(indent, w, ")")?;
|
|
}
|
|
Inst::ConstantU32 => {
|
|
writeln_indented!(indent, w, "%{} = const i32 {}", node, data.as_u32())?;
|
|
}
|
|
Inst::ConstantU64 => {
|
|
writeln_indented!(indent, w, "%{} = const i64 {}", node, data.as_u64())?;
|
|
}
|
|
Inst::ConstantMultiByte => {
|
|
let value = self.tree.strings.get_bytes(data.as_index());
|
|
writeln_indented!(indent, w, "%{} = const bytes {:x?}", node, value)?;
|
|
}
|
|
Inst::Add(ty) => {
|
|
let (lhs, rhs) = data.as_lhs_rhs();
|
|
writeln_indented!(indent, w, "%{} = add_{ty}(%{} + %{})", node, lhs, rhs)?;
|
|
}
|
|
Inst::Sub(ty) => {
|
|
let (lhs, rhs) = data.as_lhs_rhs();
|
|
writeln_indented!(indent, w, "%{} = sub_{ty}(%{} - %{})", node, lhs, rhs)?;
|
|
}
|
|
Inst::Eq(ty) => {
|
|
let (lhs, rhs) = data.as_lhs_rhs();
|
|
writeln_indented!(indent, w, "%{} = eq_{ty}(%{} == %{})", node, lhs, rhs)?;
|
|
}
|
|
Inst::Neq(ty) => {
|
|
let (lhs, rhs) = data.as_lhs_rhs();
|
|
writeln_indented!(indent, w, "%{} = neq_{ty}(%{} != %{})", node, lhs, rhs)?;
|
|
}
|
|
Inst::Gt(ty) => {
|
|
let (lhs, rhs) = data.as_lhs_rhs();
|
|
writeln_indented!(indent, w, "%{} = gt_{ty}(%{} > %{})", node, lhs, rhs)?;
|
|
}
|
|
Inst::Lt(ty) => {
|
|
let (lhs, rhs) = data.as_lhs_rhs();
|
|
writeln_indented!(indent, w, "%{} = lt_{ty}(%{} < %{})", node, lhs, rhs)?;
|
|
}
|
|
Inst::Ge(ty) => {
|
|
let (lhs, rhs) = data.as_lhs_rhs();
|
|
writeln_indented!(indent, w, "%{} = ge_{ty}(%{} >= %{})", node, lhs, rhs)?;
|
|
}
|
|
Inst::Le(ty) => {
|
|
let (lhs, rhs) = data.as_lhs_rhs();
|
|
writeln_indented!(indent, w, "%{} = le_{ty}(%{} <= %{})", node, lhs, rhs)?;
|
|
}
|
|
Inst::Mul(ty) => {
|
|
let (lhs, rhs) = data.as_lhs_rhs();
|
|
writeln_indented!(indent, w, "%{} = mul_{ty}(%{} * %{})", node, lhs, rhs)?;
|
|
}
|
|
Inst::Negate(ty) => {
|
|
writeln_indented!(indent, w, "%{} = negate_{ty}(%{})", node, data.lhs)?;
|
|
}
|
|
Inst::Not(ty) => {
|
|
writeln_indented!(indent, w, "%{} = bitwise_not_{ty}(%{})", node, data.lhs)?;
|
|
}
|
|
Inst::ExplicitCast(from, to) => {
|
|
writeln_indented!(indent, w, "%{} = cast_{from}_to_{to}(%{})", node, data.lhs)?;
|
|
}
|
|
Inst::ShiftLeft(ty) => {
|
|
writeln_indented!(
|
|
indent,
|
|
w,
|
|
"%{} = shl_{ty}(%{} << %{})",
|
|
node,
|
|
data.lhs,
|
|
data.rhs
|
|
)?;
|
|
}
|
|
Inst::ShiftRight(ty) => {
|
|
writeln_indented!(
|
|
indent,
|
|
w,
|
|
"%{} = shr_{ty}(%{} >> %{})",
|
|
node,
|
|
data.lhs,
|
|
data.rhs
|
|
)?;
|
|
}
|
|
Inst::ReturnValue(ty) => {
|
|
writeln_indented!(indent, w, "%{} = return {ty} %{}", node, data.lhs)?;
|
|
}
|
|
Inst::Return => {
|
|
writeln_indented!(indent, w, "%{} = return", node)?;
|
|
}
|
|
Inst::Alloca => {
|
|
let (size, align) = data.as_lhs_rhs();
|
|
writeln_indented!(indent, w, "%{} = alloca {size} (align: {align})", node)?;
|
|
}
|
|
Inst::GetElementPtr(ty) => {
|
|
let (ptr, idx) = data.as_lhs_rhs();
|
|
writeln_indented!(
|
|
indent,
|
|
w,
|
|
"%{node} = getelementptr {ty}, ptr: %{}, idx: {}",
|
|
ptr,
|
|
idx
|
|
)?;
|
|
}
|
|
Inst::Load(ty) => {
|
|
let source = data.lhs;
|
|
writeln_indented!(indent, w, "%{} = load {ty}, %{source}", node)?;
|
|
}
|
|
Inst::Store(ty) => {
|
|
let (src, dst) = data.as_lhs_rhs();
|
|
writeln_indented!(indent, w, "%{} = store {ty}, ptr %{dst}, %{src}", node)?;
|
|
}
|
|
Inst::ExternRef(ty) => {
|
|
let idx = data.as_index();
|
|
writeln_indented!(
|
|
indent,
|
|
w,
|
|
"%{node} = extern reference {ty} '{}'",
|
|
self.tree.strings.get_str(idx)
|
|
)?;
|
|
}
|
|
Inst::Branch(condition) => {
|
|
let (lhs, rhs) = data.as_lhs_rhs();
|
|
writeln_indented!(
|
|
indent,
|
|
w,
|
|
"%{node} = br bool %{condition}, [%{lhs}, %{rhs}]"
|
|
)?;
|
|
}
|
|
Inst::Jump => {
|
|
let lhs = data.lhs;
|
|
writeln_indented!(indent, w, "%{node} = jmp %{lhs}")?;
|
|
}
|
|
Inst::Phi2(ty) => {
|
|
let (lhs, rhs) = data.as_lhs_rhs();
|
|
writeln_indented!(indent, w, "%{node} = phi [{ty} %{lhs}, {ty} %{rhs}]")?;
|
|
}
|
|
Inst::GlobalConstant(name, ty) => {
|
|
let label = self.tree.strings.get_str(name);
|
|
let value = data.lhs;
|
|
writeln_indented!(indent, w, "%{node} = global const '{label}' {ty} %{value}")?;
|
|
}
|
|
_ => {
|
|
unimplemented!("{inst:?} rendering unimplemented")
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
pub fn render<W: core::fmt::Write>(&self, w: &mut W) -> core::fmt::Result {
|
|
for node in 0..self.ir.nodes.len() {
|
|
self.render_node(w, node as u32, 1)?;
|
|
}
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
struct IRIter<'a> {
|
|
ir: &'a IR,
|
|
offset: usize,
|
|
item: Option<(Inst, Option<Data>)>,
|
|
}
|
|
|
|
impl<'a> IRIter<'a> {
|
|
fn node(&self) -> u32 {
|
|
self.offset as Node
|
|
}
|
|
}
|
|
impl<'a> Iterator for IRIter<'a> {
|
|
type Item = (Inst, Option<Data>);
|
|
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
let inst = self.ir.nodes.get(self.offset)?;
|
|
let data = self.ir.data.get(self.offset)?;
|
|
self.offset += 1;
|
|
self.item = Some((*inst, *data));
|
|
Some((*inst, *data))
|
|
}
|
|
}
|
|
|
|
use crate::mir;
|
|
|
|
pub struct MirBuilder<'a> {
|
|
ir: IRIter<'a>,
|
|
pub strings: StringTable,
|
|
pub functions: HashMap<StringsIndex, mir::Mir>,
|
|
pub globals: HashMap<StringsIndex, mir::Mir>,
|
|
}
|
|
|
|
struct IrToMirMapping<'a> {
|
|
ir: &'a IR,
|
|
mapping: BTreeMap<Node, mir::NodeRef>,
|
|
}
|
|
|
|
impl<'a> IrToMirMapping<'a> {
|
|
fn new(ir: &'a IR) -> Self {
|
|
Self {
|
|
ir,
|
|
mapping: BTreeMap::new(),
|
|
}
|
|
}
|
|
fn insert(&mut self, ir: Node, mir: mir::NodeRef) {
|
|
self.mapping.insert(ir, mir);
|
|
}
|
|
|
|
fn get(&mut self, mir: &mut mir::Mir, ir: Node) -> Option<mir::NodeRef> {
|
|
if let Some(&mir) = self.mapping.get(&ir) {
|
|
return Some(mir);
|
|
}
|
|
|
|
match self.ir.nodes[ir as usize] {
|
|
Inst::GlobalConstant(name, ty) => {
|
|
eprintln!("does this even get hit anymore???????????????????//");
|
|
let ext = mir::NodeRef(mir.gen_extern(Some(ty.mir_type()), name));
|
|
self.insert(ir, ext);
|
|
Some(ext)
|
|
}
|
|
_ => None,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<'a> MirBuilder<'a> {
|
|
pub fn new(ir: &'a IR, strings: StringTable) -> MirBuilder<'a> {
|
|
Self {
|
|
ir: IRIter {
|
|
ir,
|
|
offset: 0,
|
|
item: None,
|
|
},
|
|
strings,
|
|
functions: HashMap::new(),
|
|
globals: HashMap::new(),
|
|
}
|
|
}
|
|
|
|
fn build_function(&mut self, name: StringsIndex) {
|
|
let mut mir = mir::Mir::new(name);
|
|
let mut mapping = IrToMirMapping::new(&self.ir.ir);
|
|
// map of label -> unresolved mir jump or branch instruction
|
|
// stored as a tree of (label, unresolved)
|
|
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
|
|
enum LeftRight {
|
|
Left(mir::NodeRef),
|
|
Right(mir::NodeRef),
|
|
}
|
|
impl LeftRight {
|
|
fn noderef(self) -> mir::NodeRef {
|
|
match self {
|
|
LeftRight::Left(noderef) => noderef,
|
|
LeftRight::Right(noderef) => noderef,
|
|
}
|
|
}
|
|
}
|
|
let mut unresolved_jumps_branches = BTreeSet::<(Node, LeftRight)>::new();
|
|
|
|
loop {
|
|
let ir_node = self.ir.node();
|
|
let Some((inst, data)) = self.ir.next() else {
|
|
break;
|
|
};
|
|
let node = match inst {
|
|
Inst::FunctionStart => {
|
|
self.ir.offset -= 1;
|
|
break;
|
|
}
|
|
Inst::FunctionEnd => {
|
|
break;
|
|
}
|
|
Inst::Label => {
|
|
let label = mir.gen_label(data.unwrap().as_index());
|
|
let range = unresolved_jumps_branches
|
|
.range(
|
|
(ir_node, LeftRight::Left(mir::NodeRef::MIN))
|
|
..=(ir_node, LeftRight::Right(mir::NodeRef::MAX)),
|
|
)
|
|
.map(|(_, n)| n)
|
|
.cloned()
|
|
.collect::<Vec<_>>();
|
|
|
|
for unresolved in range {
|
|
unresolved_jumps_branches.remove(&(ir_node, unresolved));
|
|
|
|
let mir_node = unresolved.noderef();
|
|
let (inst, data) = mir.get_node_mut(mir_node);
|
|
|
|
match inst {
|
|
mir::Inst::Jump => {
|
|
*data = mir::Data::node(label);
|
|
}
|
|
mir::Inst::Branch(_) => {
|
|
let (lhs, rhs) = data.as_binary_noderefs();
|
|
|
|
*data = match unresolved {
|
|
LeftRight::Left(_) => mir::Data::binary(label, rhs.0),
|
|
LeftRight::Right(_) => mir::Data::binary(lhs.0, label),
|
|
};
|
|
}
|
|
_ => {
|
|
unreachable!()
|
|
}
|
|
}
|
|
}
|
|
|
|
label
|
|
}
|
|
Inst::ConstantU32 => mir.push(
|
|
mir::Inst::ConstantDWord,
|
|
mir::Data::imm32(data.unwrap().as_u32()),
|
|
),
|
|
Inst::ConstantU64 => mir.push(
|
|
mir::Inst::ConstantQWord,
|
|
mir::Data::imm64(data.unwrap().as_u64()),
|
|
),
|
|
Inst::ConstantMultiByte => {
|
|
let bytes = self.strings.get_bytes(data.unwrap().as_index());
|
|
let mut buf = [0u8; 8];
|
|
match bytes.len() {
|
|
1 => mir.gen_u8(bytes[0]),
|
|
2 => mir.gen_u16(u16::from_le_bytes(bytes[..2].try_into().unwrap())),
|
|
3..=4 => {
|
|
buf[..bytes.len()].copy_from_slice(bytes);
|
|
mir.gen_u32(u32::from_le_bytes(buf[..4].try_into().unwrap()))
|
|
}
|
|
5..=8 => {
|
|
buf[..bytes.len()].copy_from_slice(bytes);
|
|
mir.gen_u64(u64::from_le_bytes(buf[..8].try_into().unwrap()))
|
|
}
|
|
_ => {
|
|
unimplemented!(
|
|
"constants larger than 8 bytes are not currently supported!"
|
|
)
|
|
}
|
|
}
|
|
}
|
|
Inst::Alloca => {
|
|
let (l, r) = data.unwrap().as_lhs_rhs();
|
|
mir.gen_alloca(l, r)
|
|
}
|
|
Inst::Load(ty) => {
|
|
let ty = mir::Type::from_bytesize_int(ty.size());
|
|
let src = mapping.get(&mut mir, data.unwrap().as_u32()).unwrap().0;
|
|
mir.gen_load(ty, src)
|
|
}
|
|
Inst::Store(ty) => {
|
|
let ty = mir::Type::from_bytesize_int(ty.size());
|
|
let (src, dst) = data.unwrap().as_lhs_rhs();
|
|
let src = mapping.get(&mut mir, src).unwrap().0;
|
|
let dst = mapping.get(&mut mir, dst).unwrap().0;
|
|
mir.gen_store(ty, src, dst)
|
|
}
|
|
Inst::GetElementPtr(ty) => {
|
|
let ty = mir::Type::from_bytesize_int(ty.size());
|
|
let (ptr, idx) = data.unwrap().as_lhs_rhs();
|
|
let src = mapping.get(&mut mir, ptr).unwrap().0;
|
|
mir.gen_get_element_ptr(ty, src, idx)
|
|
}
|
|
Inst::Parameter(ty) => {
|
|
// let (size, _) = data.unwrap().as_lhs_rhs();
|
|
mir.gen_param(ty.into())
|
|
}
|
|
Inst::Eq(ty)
|
|
| Inst::Neq(ty)
|
|
| Inst::Gt(ty)
|
|
| Inst::Lt(ty)
|
|
| Inst::Ge(ty)
|
|
| Inst::Le(ty) => {
|
|
let (src, dst) = data.unwrap().as_lhs_rhs();
|
|
let lhs = mapping.get(&mut mir, src).unwrap().0;
|
|
let rhs = mapping.get(&mut mir, dst).unwrap().0;
|
|
|
|
#[cfg_attr(rustfmt, rustfmt::skip)]
|
|
let (ord, invert)= match inst {
|
|
Inst::Eq(_) => (Ordering::Equal, false),
|
|
Inst::Neq(_) => (Ordering::Equal, true),
|
|
Inst::Gt(_) => (Ordering::Greater, false),
|
|
Inst::Le(_) => (Ordering::Greater, true),
|
|
Inst::Lt(_) => (Ordering::Less, false),
|
|
Inst::Ge(_) => (Ordering::Less, true),
|
|
_ => unreachable!(),
|
|
};
|
|
|
|
mir.gen_cmp_byte(ty.mir_type(), ty.is_signed(), ord, invert, lhs, rhs)
|
|
}
|
|
Inst::Add(ty) => {
|
|
let (src, dst) = data.unwrap().as_lhs_rhs();
|
|
let lhs = mapping.get(&mut mir, src).unwrap().0;
|
|
let rhs = mapping.get(&mut mir, dst).unwrap().0;
|
|
|
|
match ty {
|
|
Type2::Integral(signed, bits) => match bits {
|
|
8 => mir.gen_add(mir::Type::Byte, lhs, rhs),
|
|
16 => mir.gen_add(mir::Type::Word, lhs, rhs),
|
|
32 => mir.gen_add(mir::Type::DWord, lhs, rhs),
|
|
64 => mir.gen_add(mir::Type::QWord, lhs, rhs),
|
|
64.. => {
|
|
unimplemented!()
|
|
}
|
|
bits => {
|
|
let ty = mir::Type::from_bitsize_int(bits as u32);
|
|
|
|
let sum = mir.gen_add(ty, lhs, rhs);
|
|
mir.gen_truncate_integer(sum, ty, signed, bits)
|
|
}
|
|
},
|
|
Type2::Binary32 => mir.gen_add(mir::Type::SinglePrecision, lhs, rhs),
|
|
Type2::Binary64 => mir.gen_add(mir::Type::DoublePrecision, lhs, rhs),
|
|
Type2::Pointer => mir.gen_add(mir::Type::QWord, lhs, rhs),
|
|
_ => unreachable!(),
|
|
}
|
|
}
|
|
Inst::Sub(ty) => {
|
|
let (src, dst) = data.unwrap().as_lhs_rhs();
|
|
let lhs = mapping.get(&mut mir, src).unwrap().0;
|
|
let rhs = mapping.get(&mut mir, dst).unwrap().0;
|
|
|
|
let unalignment = ty.mir_unalignment();
|
|
let ty = ty.mir_type();
|
|
|
|
let sum = mir.gen_sub(ty, lhs, rhs);
|
|
if let Some((signed, bits)) = unalignment {
|
|
mir.gen_truncate_integer(sum, ty, signed, bits)
|
|
} else {
|
|
sum
|
|
}
|
|
}
|
|
Inst::Mul(ty) => {
|
|
let (lhs, rhs) = data.unwrap().as_lhs_rhs();
|
|
|
|
let unalignment = ty.mir_unalignment();
|
|
let signed = ty.is_signed();
|
|
let ty = ty.mir_type();
|
|
|
|
let lhs = mapping.get(&mut mir, lhs).unwrap().0;
|
|
let rhs = mapping.get(&mut mir, rhs).unwrap().0;
|
|
|
|
let sum = mir.gen_mul(ty, signed, lhs, rhs);
|
|
if let Some((signed, bits)) = unalignment {
|
|
mir.gen_truncate_integer(sum, ty, signed, bits)
|
|
} else {
|
|
sum
|
|
}
|
|
}
|
|
Inst::Div(ty) => {
|
|
let (lhs, rhs) = data.unwrap().as_lhs_rhs();
|
|
|
|
let unalignment = ty.mir_unalignment();
|
|
let signed = ty.is_signed();
|
|
let ty = ty.mir_type();
|
|
|
|
let lhs = mapping.get(&mut mir, lhs).unwrap().0;
|
|
let rhs = mapping.get(&mut mir, rhs).unwrap().0;
|
|
|
|
let sum = mir.gen_div(ty, signed, lhs, rhs);
|
|
if let Some((signed, bits)) = unalignment {
|
|
mir.gen_truncate_integer(sum, ty, signed, bits)
|
|
} else {
|
|
sum
|
|
}
|
|
}
|
|
Inst::Rem(ty) => {
|
|
let (lhs, rhs) = data.unwrap().as_lhs_rhs();
|
|
|
|
let unalignment = ty.mir_unalignment();
|
|
let signed = ty.is_signed();
|
|
let ty = ty.mir_type();
|
|
|
|
let lhs = mapping.get(&mut mir, lhs).unwrap().0;
|
|
let rhs = mapping.get(&mut mir, rhs).unwrap().0;
|
|
|
|
let sum = mir.gen_rem(ty, signed, lhs, rhs);
|
|
if let Some((signed, bits)) = unalignment {
|
|
mir.gen_truncate_integer(sum, ty, signed, bits)
|
|
} else {
|
|
sum
|
|
}
|
|
}
|
|
Inst::BitAnd(ty) => {
|
|
let (lhs, rhs) = data.unwrap().as_lhs_rhs();
|
|
|
|
let unalignment = ty.mir_unalignment();
|
|
let ty = ty.mir_type();
|
|
|
|
let (lhs, rhs) = if self.ir.ir.nodes[lhs as usize].is_constant() {
|
|
(rhs, lhs)
|
|
} else {
|
|
(lhs, rhs)
|
|
};
|
|
|
|
let lhs = mapping.get(&mut mir, lhs).unwrap().0;
|
|
let rhs = mapping.get(&mut mir, rhs).unwrap().0;
|
|
|
|
let sum = mir.gen_bitand(ty, lhs, rhs);
|
|
if let Some((signed, bits)) = unalignment {
|
|
mir.gen_truncate_integer(sum, ty, signed, bits)
|
|
} else {
|
|
sum
|
|
}
|
|
}
|
|
Inst::BitOr(ty) => {
|
|
let (lhs, rhs) = data.unwrap().as_lhs_rhs();
|
|
|
|
let unalignment = ty.mir_unalignment();
|
|
let ty = ty.mir_type();
|
|
|
|
let (lhs, rhs) = if self.ir.ir.nodes[lhs as usize].is_constant() {
|
|
(rhs, lhs)
|
|
} else {
|
|
(lhs, rhs)
|
|
};
|
|
|
|
let lhs = mapping.get(&mut mir, lhs).unwrap().0;
|
|
let rhs = mapping.get(&mut mir, rhs).unwrap().0;
|
|
|
|
let sum = mir.gen_bitor(ty, lhs, rhs);
|
|
if let Some((signed, bits)) = unalignment {
|
|
mir.gen_truncate_integer(sum, ty, signed, bits)
|
|
} else {
|
|
sum
|
|
}
|
|
}
|
|
Inst::BitXOr(ty) => {
|
|
let (lhs, rhs) = data.unwrap().as_lhs_rhs();
|
|
|
|
let unalignment = ty.mir_unalignment();
|
|
let ty = ty.mir_type();
|
|
|
|
let (lhs, rhs) = if self.ir.ir.nodes[lhs as usize].is_constant() {
|
|
(rhs, lhs)
|
|
} else {
|
|
(lhs, rhs)
|
|
};
|
|
|
|
let lhs = mapping.get(&mut mir, lhs).unwrap().0;
|
|
let rhs = mapping.get(&mut mir, rhs).unwrap().0;
|
|
|
|
let sum = mir.gen_bitxor(ty, lhs, rhs);
|
|
if let Some((signed, bits)) = unalignment {
|
|
mir.gen_truncate_integer(sum, ty, signed, bits)
|
|
} else {
|
|
sum
|
|
}
|
|
}
|
|
Inst::ShiftLeft(ty) => {
|
|
let (src, dst) = data.unwrap().as_lhs_rhs();
|
|
let lhs = mapping.get(&mut mir, src).unwrap().0;
|
|
let rhs = mapping.get(&mut mir, dst).unwrap().0;
|
|
|
|
// TODO: check rhs type and pass it to gen_sh{l,r}?
|
|
let rhs = mir.gen_truncate_integer(rhs, ty.into(), false, 8);
|
|
match ty {
|
|
Type2::Integral(signed, bits) => match bits {
|
|
8 => mir.gen_shl(mir::Type::Byte, lhs, rhs),
|
|
16 => mir.gen_shl(mir::Type::Word, lhs, rhs),
|
|
32 => mir.gen_shl(mir::Type::DWord, lhs, rhs),
|
|
64 => mir.gen_shl(mir::Type::QWord, lhs, rhs),
|
|
64.. => {
|
|
unimplemented!()
|
|
}
|
|
bits => {
|
|
let ty = mir::Type::from_bitsize_int(bits as u32);
|
|
|
|
let sum = mir.gen_shl(ty, lhs, rhs);
|
|
mir.gen_truncate_integer(sum, ty, signed, bits)
|
|
}
|
|
},
|
|
_ => unreachable!(),
|
|
}
|
|
}
|
|
Inst::ShiftRight(ty) => {
|
|
let (src, dst) = data.unwrap().as_lhs_rhs();
|
|
let lhs = mapping.get(&mut mir, src).unwrap().0;
|
|
let rhs = mapping.get(&mut mir, dst).unwrap().0;
|
|
|
|
match ty {
|
|
Type2::Integral(signed, bits) => match bits {
|
|
8 | 16 | 32 | 64 => {
|
|
let ty = mir::Type::from_bitsize_int(bits as u32);
|
|
if signed {
|
|
mir.gen_sar(ty, lhs, rhs)
|
|
} else {
|
|
mir.gen_shr(ty, lhs, rhs)
|
|
}
|
|
}
|
|
64.. => {
|
|
unimplemented!()
|
|
}
|
|
bits => {
|
|
let ty = mir::Type::from_bitsize_int(bits as u32);
|
|
|
|
let sum = if signed {
|
|
mir.gen_sar(ty, lhs, rhs)
|
|
} else {
|
|
mir.gen_shr(ty, lhs, rhs)
|
|
};
|
|
|
|
mir.gen_truncate_integer(sum, ty, signed, bits)
|
|
}
|
|
},
|
|
_ => unreachable!(),
|
|
}
|
|
}
|
|
Inst::Not(ty) => {
|
|
let lhs = data.unwrap().as_u32();
|
|
|
|
let unalignment = ty.mir_unalignment();
|
|
let ty = ty.mir_type();
|
|
|
|
let lhs = mapping.get(&mut mir, lhs).unwrap().0;
|
|
|
|
let sum = mir.gen_not(ty, lhs);
|
|
if let Some((signed, bits)) = unalignment {
|
|
mir.gen_truncate_integer(sum, ty, signed, bits)
|
|
} else {
|
|
sum
|
|
}
|
|
}
|
|
Inst::Negate(ty) => {
|
|
let lhs = data.unwrap().as_u32();
|
|
|
|
let unalignment = ty.mir_unalignment();
|
|
let ty = ty.mir_type();
|
|
|
|
let lhs = mapping.get(&mut mir, lhs).unwrap().0;
|
|
|
|
let sum = mir.gen_negate(ty, lhs);
|
|
if let Some((signed, bits)) = unalignment {
|
|
mir.gen_truncate_integer(sum, ty, signed, bits)
|
|
} else {
|
|
sum
|
|
}
|
|
}
|
|
Inst::ExplicitCast(from, to) => {
|
|
let lhs = data.unwrap().as_u32();
|
|
let from_mir = from.mir_type();
|
|
let to_mir = to.mir_type();
|
|
|
|
let lhs = mapping.get(&mut mir, lhs).unwrap().0;
|
|
|
|
match (from, to) {
|
|
(Type2::Integral(a_signed, a), Type2::Integral(b_signed, b)) => {
|
|
if a > b {
|
|
mir.gen_truncate_integer(lhs, to_mir, b_signed, b)
|
|
} else if a < b {
|
|
mir.gen_extend_integer(
|
|
lhs,
|
|
IntegralType::new(a_signed, a),
|
|
IntegralType::new(b_signed, b),
|
|
)
|
|
} else {
|
|
unreachable!()
|
|
}
|
|
}
|
|
(Type2::Integral(_, _), Type2::Bool) => {
|
|
let is_zero = mir.gen_is_zero(from_mir, lhs);
|
|
mir.gen_negate(mir::Type::Byte, is_zero)
|
|
}
|
|
(Type2::Bool, Type2::Integral(b_signed, b)) => mir.gen_extend_integer(
|
|
lhs,
|
|
IntegralType::u1(),
|
|
IntegralType::new(b_signed, b),
|
|
),
|
|
_ => unimplemented!(),
|
|
}
|
|
}
|
|
Inst::ReturnValue(ty) => {
|
|
let src = data.unwrap().as_u32();
|
|
let src = mapping.get(&mut mir, src).unwrap().0;
|
|
|
|
mir.gen_ret_val(ty.mir_type(), src)
|
|
}
|
|
Inst::Return => mir.gen_ret(),
|
|
Inst::Jump => {
|
|
let label = data.unwrap().as_u32();
|
|
|
|
let jmp = mir.gen_jmp(label);
|
|
|
|
let label = match mapping.get(&mut mir, label) {
|
|
Some(label) => label.0,
|
|
None => {
|
|
unresolved_jumps_branches
|
|
.insert((label, LeftRight::Left(mir::NodeRef(jmp))));
|
|
0
|
|
}
|
|
};
|
|
|
|
mir.set_node_data(mir::NodeRef(jmp), mir::Data::node(label));
|
|
|
|
jmp
|
|
}
|
|
Inst::Branch(condition) => {
|
|
let condition = mapping.get(&mut mir, condition).unwrap().0;
|
|
let (lhs, rhs) = data.unwrap().as_lhs_rhs();
|
|
|
|
let br = mir.gen_branch(condition, lhs, rhs);
|
|
|
|
let lhs = match mapping.get(&mut mir, lhs) {
|
|
Some(n) => n.0,
|
|
None => {
|
|
unresolved_jumps_branches
|
|
.insert((lhs, LeftRight::Left(mir::NodeRef(br))));
|
|
0
|
|
}
|
|
};
|
|
let rhs = match mapping.get(&mut mir, rhs) {
|
|
Some(n) => n.0,
|
|
None => {
|
|
unresolved_jumps_branches
|
|
.insert((rhs, LeftRight::Right(mir::NodeRef(br))));
|
|
0
|
|
}
|
|
};
|
|
|
|
mir.set_node_data(mir::NodeRef(br), mir::Data::binary(lhs, rhs));
|
|
|
|
br
|
|
}
|
|
Inst::Call(func) => {
|
|
let f = mapping.get(&mut mir, func).unwrap().0;
|
|
|
|
let ir = self.ir.ir;
|
|
let (start, end) = data.unwrap().as_lhs_rhs();
|
|
|
|
variant!(&ir.nodes[end as usize -1] => Inst::InlineType(ty));
|
|
|
|
let args = (start..(end - 1))
|
|
.map(|arg| {
|
|
variant!(&ir.nodes[arg as usize] => Inst::Argument(ty));
|
|
let arg = ir.data[arg as usize].unwrap().lhs;
|
|
let arg = mapping.get(&mut mir, arg).unwrap().0;
|
|
(ty.mir_type(), arg)
|
|
})
|
|
.collect::<Vec<_>>();
|
|
|
|
mir.gen_call(ty.mir_type(), f, args)
|
|
}
|
|
Inst::Phi2(ty) => {
|
|
let (src, dst) = data.unwrap().as_lhs_rhs();
|
|
let lhs = mapping.get(&mut mir, src).unwrap().0;
|
|
let rhs = mapping.get(&mut mir, dst).unwrap().0;
|
|
|
|
mir.gen_phi2(ty.mir_type(), lhs, rhs)
|
|
}
|
|
Inst::GlobalConstant(name, ty) => {
|
|
let ext = mir.gen_extern(Some(ty.mir_type()), name);
|
|
|
|
ext
|
|
}
|
|
Inst::Argument(_) | Inst::InlineType(_) => {
|
|
continue;
|
|
}
|
|
Inst::ExternRef(ty) => {
|
|
mir.gen_extern(Some(ty.mir_type()), data.unwrap().as_index())
|
|
}
|
|
#[allow(unreachable_patterns)]
|
|
_ => {
|
|
eprintln!("ir inst {inst:?} not supported in mir translation");
|
|
unimplemented!()
|
|
}
|
|
};
|
|
|
|
mapping.insert(ir_node, mir::NodeRef(node));
|
|
}
|
|
|
|
self.functions.insert(name, mir);
|
|
}
|
|
|
|
pub fn build(&mut self) {
|
|
loop {
|
|
let Some((inst, data)) = self.ir.next() else {
|
|
break;
|
|
};
|
|
match inst {
|
|
Inst::FunctionStart => self.build_function(data.unwrap().as_index()),
|
|
Inst::GlobalConstant(name, ..) => {
|
|
let mut mir = mir::Mir::new(name);
|
|
let value = data.unwrap().lhs;
|
|
let data = self.ir.ir.data[value as usize];
|
|
let inst = self.ir.ir.nodes[value as usize];
|
|
let _value = match inst {
|
|
Inst::ConstantU32 => mir.push(
|
|
mir::Inst::ConstantDWord,
|
|
mir::Data::imm32(data.unwrap().as_u32()),
|
|
),
|
|
Inst::ConstantU64 => mir.push(
|
|
mir::Inst::ConstantQWord,
|
|
mir::Data::imm64(data.unwrap().as_u64()),
|
|
),
|
|
Inst::ConstantMultiByte => {
|
|
let bytes = self.strings.get_bytes(data.unwrap().as_index());
|
|
let mut buf = [0u8; 8];
|
|
match bytes.len() {
|
|
1 => mir.gen_u8(bytes[0]),
|
|
2 => {
|
|
mir.gen_u16(u16::from_le_bytes(bytes[..2].try_into().unwrap()))
|
|
}
|
|
3..=4 => {
|
|
buf[..bytes.len()].copy_from_slice(bytes);
|
|
mir.gen_u32(u32::from_le_bytes(buf[..4].try_into().unwrap()))
|
|
}
|
|
5..=8 => {
|
|
buf[..bytes.len()].copy_from_slice(bytes);
|
|
mir.gen_u64(u64::from_le_bytes(buf[..8].try_into().unwrap()))
|
|
}
|
|
_ => {
|
|
unimplemented!(
|
|
"constants larger than 8 bytes are not currently supported!"
|
|
)
|
|
}
|
|
}
|
|
}
|
|
_ => {
|
|
unimplemented!()
|
|
}
|
|
};
|
|
|
|
self.globals.insert(name, mir);
|
|
}
|
|
_ => {}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use crate::lexer::Tokenizer;
|
|
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn mir_u10() {
|
|
let src = "
|
|
fn u10(x: i10) -> i10 {
|
|
5i10 * 3i10
|
|
}
|
|
";
|
|
|
|
let tokens = Tokenizer::new(src.as_bytes()).unwrap();
|
|
|
|
let mut tree = Tree::new();
|
|
tree.parse(tokens.iter()).unwrap();
|
|
|
|
let mut buf = String::new();
|
|
tree.render(&mut buf).unwrap();
|
|
println!("AST:\n{buf}");
|
|
|
|
let mut ir = IR::new();
|
|
let builder = ir.build(&mut tree);
|
|
let mut buf = String::new();
|
|
builder.render(&mut buf).unwrap();
|
|
println!("IR:\n{buf}");
|
|
|
|
let mut mir = MirBuilder::new(&ir, tree.strings);
|
|
mir.build();
|
|
}
|
|
}
|