From 9f0e2c4a3132fb8ce50aed7f1671c840480fe988 Mon Sep 17 00:00:00 2001 From: Janis Date: Thu, 29 Aug 2024 21:34:05 +0200 Subject: [PATCH] mir to asm translation WORKS!! --- src/asm/amd64.rs | 39 +- src/ast.rs | 15 +- src/bin/main.rs | 93 +++ src/mir.rs | 1407 ++++++++++++++++++++++++++++++++-- src/parser.rs | 5 +- src/triples.rs | 138 ++-- tests/legal/inverse_sqrt.sea | 7 + tests/legal/simple_math.sea | 5 + 8 files changed, 1557 insertions(+), 152 deletions(-) create mode 100644 src/bin/main.rs create mode 100644 tests/legal/inverse_sqrt.sea create mode 100644 tests/legal/simple_math.sea diff --git a/src/asm/amd64.rs b/src/asm/amd64.rs index b55a17f..1baca03 100644 --- a/src/asm/amd64.rs +++ b/src/asm/amd64.rs @@ -245,11 +245,11 @@ impl Register { } } - const fn byte_size(self) -> u32 { + pub const fn byte_size(self) -> u32 { self.bit_size().div_ceil(8) } - const fn into_parent_register(self) -> Self { + pub const fn parent_reg(self) -> Self { use Register::*; match self { rax | eax | ax | ah | al => rax, @@ -279,20 +279,20 @@ impl Register { unsafe { *(&val as *const u8 as *const Self) } } - const fn into_qword(self) -> Self { - Self::from_u8(self.into_parent_register() as u8) + pub const fn into_qword(self) -> Self { + Self::from_u8(self.parent_reg() as u8) } - const fn into_dword(self) -> Self { - Self::from_u8(self.into_parent_register() as u8 + 1) + pub const fn into_dword(self) -> Self { + Self::from_u8(self.parent_reg() as u8 + 1) } - const fn into_word(self) -> Self { - Self::from_u8(self.into_parent_register() as u8 + 2) + pub const fn into_word(self) -> Self { + Self::from_u8(self.parent_reg() as u8 + 2) } - const fn into_byte(self) -> Self { - Self::from_u8(self.into_parent_register() as u8 + 3) + pub const fn into_byte(self) -> Self { + Self::from_u8(self.parent_reg() as u8 + 3) } fn into_bitsize(self, size: u32) -> Self { @@ -305,6 +305,14 @@ impl Register { _ => panic!("unsupported bitsize {size}"), } } + pub fn into_bytesize(self, size: u32) -> Self { + self.into_bitsize(size * 8) + } + + pub const SYSV_CALLEE_SAVED: [Register; 5] = { + use Register::*; + [r12, r13, r14, r15, rbx] + }; pub const GPR: [Register; 14] = { use Register::*; @@ -320,7 +328,7 @@ impl Register { ] }; - const fn is_gp(self) -> bool { + pub const fn is_gp(self) -> bool { use Register::*; match self { rax | eax | ax | ah | al | rbx | ebx | bx | bh | bl | rcx | ecx | cx | ch | cl @@ -331,6 +339,15 @@ impl Register { _ => false, } } + pub const fn is_sse(self) -> bool { + use Register::*; + match self { + xmm0 | xmm1 | xmm2 | xmm3 | xmm4 | xmm5 | xmm6 | xmm7 | xmm8 | xmm9 | xmm10 | xmm11 + | xmm12 | xmm13 | xmm14 | xmm15 | ymm0 | ymm1 | ymm2 | ymm3 | ymm4 | ymm5 | ymm6 + | ymm7 | ymm8 | ymm9 | ymm10 | ymm11 | ymm12 | ymm13 | ymm14 | ymm15 => true, + _ => false, + } + } } pub enum Operands { diff --git a/src/ast.rs b/src/ast.rs index eb37dcb..a7f6ea3 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -221,6 +221,12 @@ impl core::fmt::Display for FloatingType { } impl IntegralType { + pub fn new(signed: bool, bits: u16) -> Self { + Self { signed, bits } + } + pub fn u1() -> Self { + Self::new(false, 1) + } pub fn u32() -> IntegralType { Self { signed: false, @@ -543,15 +549,6 @@ pub mod tree_visitor { Post(Node), } - impl PrePost { - fn node(self) -> Node { - match self { - PrePost::Pre(n) => n, - PrePost::Post(n) => n, - } - } - } - /// Don't modify `node` in `pre()` /// Don't modify `children` in `pre()` pub struct Visitor { diff --git a/src/bin/main.rs b/src/bin/main.rs new file mode 100644 index 0000000..dcd6f4d --- /dev/null +++ b/src/bin/main.rs @@ -0,0 +1,93 @@ +use std::{io::Read, path::PathBuf}; + +use clap::{Arg, Command}; +use compiler::{ + lexer::Tokenizer, + parser::Tree, + triples::{MirBuilder, IR}, +}; + +fn main() { + let cmd = clap::Command::new("sea") + .bin_name("sea") + .arg( + clap::Arg::new("input") + .short('i') + .help("sea source file.") + .value_parser(clap::builder::PathBufValueParser::new()), + ) + .subcommands([ + Command::new("ast").about("output AST."), + Command::new("mir").about("output machine-level intermediate representation."), + Command::new("ir").about("output intermediate representation."), + Command::new("asm").about("output x86-64 assembly (intel syntax)."), + ]); + + let matches = cmd.get_matches(); + let path = matches.get_one::("input"); + let source = path + .and_then(|p| std::fs::read(p).ok()) + .or_else(|| { + let mut buf = Vec::new(); + std::io::stdin().read(&mut buf).ok()?; + Some(buf) + }) + .expect("no source bytes."); + + let tokens = Tokenizer::new(&source).unwrap(); + + let mut tree = Tree::new(); + tree.parse(tokens.iter()).unwrap(); + tree.fold_comptime(); + + if let Some((cmd, _matches)) = matches.subcommand() { + match cmd { + "ast" => { + let mut buf = String::new(); + tree.render(&mut buf).unwrap(); + println!("AST:\n{buf}"); + } + "ir" => { + 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}"); + } + "mir" => { + let mut ir = IR::new(); + ir.build(&mut tree); + + let mut mir = MirBuilder::new(&ir, tree.strings); + mir.build(); + + let MirBuilder { + strings, functions, .. + } = mir; + + for (name, mir) in functions { + println!("{}:\n{}", strings.get_str(name), mir.display(&strings)); + } + } + "asm" => { + let mut ir = IR::new(); + ir.build(&mut tree); + + let mut mir = MirBuilder::new(&ir, tree.strings); + mir.build(); + + let MirBuilder { + strings, functions, .. + } = mir; + + println!(".intel_syntax"); + println!(".text"); + for (_name, mir) in functions { + let assembly = mir.assemble(&strings).unwrap(); + println!("{assembly}"); + } + } + _ => {} + } + } +} diff --git a/src/mir.rs b/src/mir.rs index 14d4577..f2ea2f0 100644 --- a/src/mir.rs +++ b/src/mir.rs @@ -2,9 +2,12 @@ use std::collections::btree_map::Entry; use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; +use std::hash::{Hash, Hasher}; use itertools::Itertools; +use crate::asm::amd64::{self, Register}; +use crate::ast::IntegralType; use crate::string_table::{Index as StringsIndex, StringTable}; #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)] @@ -42,6 +45,22 @@ impl core::fmt::Display for Type { } impl Type { + pub fn int_repr(self) -> Self { + match self { + Type::SinglePrecision => Self::DWord, + Type::DoublePrecision => Self::QWord, + _ => self, + } + } + pub fn register_width(&self, reg: Register) -> Register { + match self { + Type::Byte => reg.into_byte(), + Type::Word => reg.into_word(), + Type::DWord => reg.into_dword(), + Type::QWord => reg.into_qword(), + Type::SinglePrecision | Type::DoublePrecision => reg, + } + } pub const fn is_floating(self) -> bool { match self { Type::SinglePrecision | Type::DoublePrecision => true, @@ -108,6 +127,10 @@ pub enum Inst { ConstantDWord, /// imm64 ConstantQWord, + /// imm16fp + ConstantSinglePrecision, + /// imm32fp + ConstantDoublePrecision, /// src LoadRegister(Type), // hint for loading value into register /// ast-node @@ -152,8 +175,15 @@ pub enum Inst { /// lhs, rhs ShiftRightUnsigned(Type), /// lhs - ReturnValue, + ReturnValue(Type), Return, + /// lhs + SignExtend(Type), + /// lhs + ZeroExtend(Type), + // type specifies input type + /// lhs + IsZero(Type), //FunctionStart, } @@ -164,12 +194,14 @@ impl Inst { | Inst::ConstantBytes | Inst::ExternRef | Inst::Alloca - | Inst::ReturnValue + | Inst::ReturnValue(_) | Inst::Store(_) | Inst::ConstantByte | Inst::ConstantWord | Inst::ConstantDWord | Inst::ConstantQWord + | Inst::ConstantSinglePrecision + | Inst::ConstantDoublePrecision | Inst::Return => None, Inst::GetElementPtr(ty) | Inst::Load(ty) @@ -187,12 +219,15 @@ impl Inst { | Inst::BitOr(ty) | Inst::BitXOr(ty) | Inst::Negate(ty) + | Inst::SignExtend(ty) + | Inst::ZeroExtend(ty) | Inst::ShiftLeft(ty) | Inst::ShiftRightSigned(ty) | Inst::ShiftRightUnsigned(ty) => Some(*ty), + Inst::IsZero(_) => Some(Type::Byte), } } - fn has_value(&self) -> bool { + fn result_is_register(&self) -> bool { // basically, when an arithmetic instruction has two immediates, then just replace it with a mov into the dst reg // TODO: need to account for spilled values eventually; probably move this to `Mir`. match self { @@ -202,10 +237,12 @@ impl Inst { | Inst::ConstantWord | Inst::ConstantDWord | Inst::ConstantQWord + | Inst::ConstantSinglePrecision + | Inst::ConstantDoublePrecision | Inst::ExternRef | Inst::Alloca | Inst::Store(_) - | Inst::ReturnValue + | Inst::ReturnValue(_) | Inst::Return => false, Inst::GetElementPtr(_) | Inst::Load(_) @@ -223,6 +260,9 @@ impl Inst { | Inst::BitOr(_) | Inst::BitXOr(_) | Inst::Negate(_) + | Inst::SignExtend(_) + | Inst::ZeroExtend(_) + | Inst::IsZero(_) | Inst::ShiftLeft(_) | Inst::ShiftRightSigned(_) | Inst::ShiftRightUnsigned(_) => true, @@ -365,7 +405,7 @@ impl OperandKinds { fn sse() -> Self { Self::RegMem | Self::RegReg } - /// works for: shl,shr,sar,sal + /// works for: shl,shr,sar,sal,test fn shift() -> Self { Self::RegReg | Self::RegImm | Self::MemImm | Self::MemReg } @@ -415,6 +455,8 @@ impl OperandKinds { } out } + + #[allow(dead_code)] fn reduce_with_lhs(self, lhs: BinaryOperandFlags) -> OperandKinds { let mut out = self; if !lhs.contains(BinaryOperandFlags::LhsMem) { @@ -463,6 +505,7 @@ struct BinaryOperandsRunner<'a> { } impl<'a> BinaryOperandsRunner<'a> { + #[allow(dead_code)] fn new(inner: BinaryOperands<'a>, lhs: u32, lhs_type: Type, rhs: u32, rhs_type: Type) -> Self { Self { inner, @@ -482,6 +525,7 @@ impl<'a> BinaryOperandsRunner<'a> { fn rhs(&self) -> u32 { self.rhs.0 } + #[allow(dead_code)] fn lhs_type(&self) -> Type { self.lhs.1 } @@ -588,13 +632,15 @@ impl<'a> BinaryOperands<'a> { } pub struct Mir { + name: StringsIndex, pub nodes: Vec, pub data: Vec, } impl Mir { - pub fn new() -> Mir { + pub fn new(name: StringsIndex) -> Mir { Self { + name, nodes: Vec::new(), data: Vec::new(), } @@ -649,9 +695,12 @@ impl Mir { pub fn is_imm(&self, node: u32) -> bool { match self.nodes[node as usize] { - Inst::ConstantByte | Inst::ConstantWord | Inst::ConstantDWord | Inst::ConstantQWord => { - true - } + Inst::ConstantByte + | Inst::ConstantWord + | Inst::ConstantDWord + | Inst::ConstantQWord + | Inst::ConstantSinglePrecision + | Inst::ConstantDoublePrecision => true, _ => false, } } @@ -676,6 +725,21 @@ impl Mir { pub fn gen_u64(&mut self, value: u64) -> u32 { self.push(Inst::ConstantQWord, Data::imm64(value)) } + pub fn gen_f32(&mut self, bits: u32) -> u32 { + self.push(Inst::ConstantSinglePrecision, Data::imm32(bits)) + } + pub fn gen_f64(&mut self, bits: u64) -> u32 { + self.push(Inst::ConstantDoublePrecision, Data::imm64(bits)) + } + pub fn gen_zero_extend(&mut self, ty: Type, src: u32) -> u32 { + self.push(Inst::ZeroExtend(ty), Data::node(src)) + } + pub fn gen_sign_extend(&mut self, ty: Type, src: u32) -> u32 { + self.push(Inst::SignExtend(ty), Data::node(src)) + } + pub fn gen_is_zero(&mut self, ty: Type, src: u32) -> u32 { + self.push(Inst::IsZero(ty), Data::node(src)) + } pub fn gen_load_register(&mut self, ty: Type, src: u32) -> u32 { self.push(Inst::LoadRegister(ty), Data::node(src)) } @@ -706,9 +770,9 @@ impl Mir { self.push(Inst::Parameter(ty), Data::none()) } - pub fn gen_truncate(&mut self, src: u32, ty: Type, signed: bool, bits: u16) -> u32 { - // example: u4 - // ty = byte + /// truncates a value `src` of `mir::Type` `ty` to `bits` bits, while preserving the sign + pub fn gen_truncate_integer(&mut self, src: u32, ty: Type, signed: bool, bits: u16) -> u32 { + // example: u4 -> (byte, false, 4) // 1111_1111 << 4 = 1111_0000 // mask = 0000_1111; // shift = 8 - 4; @@ -725,6 +789,7 @@ impl Mir { let masked = self.gen_bitand(ty, src, mask); if signed { + // sign extend the high bits of the mir::Type let shift = self.gen_u8(shift); let tmp = self.gen_shl(ty, masked, shift); let tmp = self.gen_sar(ty, tmp, shift); @@ -733,6 +798,27 @@ impl Mir { masked } } + + // go from a lower bit type to a higher bit type + // if both types fit into the same mir::Type, then this is a noop + // if signed sign extend, otherwise zero-extend (or zero the register) + // `ty` is the output type + pub fn gen_extend_integer(&mut self, src: u32, from: IntegralType, to: IntegralType) -> u32 { + // example: u4 -> (byte, false, 4) + // 1111_1111 << 4 = 1111_0000 + // mask = 0000_1111; + // shift = 8 - 4; + let src_ty = self.type_of_node(src).unwrap(); + let truncated = self.gen_truncate_integer(src, src_ty, from.signed, from.bits); + + let dst_ty = Type::from_bitsize_int(to.bits as u32); + if to.signed { + self.gen_sign_extend(dst_ty, truncated) + } else { + self.gen_zero_extend(dst_ty, truncated) + } + } + fn imm_to_reg(&mut self, src: u32) -> u32 { if self.is_imm(src) { // SAFETY: imms have values and thus types @@ -841,21 +927,21 @@ impl Mir { #[doc(alias = "gen_shift_left")] pub fn gen_shl(&mut self, ty: Type, src: u32, shift: u32) -> u32 { - let (src, shift) = BinaryOperands::new_shift(self).wrangle(src, ty, shift, ty); + let (src, shift) = BinaryOperands::new_shift(self).wrangle(src, ty, shift, Type::Byte); self.push(Inst::ShiftLeft(ty), Data::binary(src, shift)) } #[doc(alias = "gen_shift_right")] pub fn gen_shr(&mut self, ty: Type, src: u32, shift: u32) -> u32 { - let (src, shift) = BinaryOperands::new_shift(self).wrangle(src, ty, shift, ty); + let (src, shift) = BinaryOperands::new_shift(self).wrangle(src, ty, shift, Type::Byte); self.push(Inst::ShiftRightUnsigned(ty), Data::binary(src, shift)) } #[doc(alias = "gen_shift_right_signed")] pub fn gen_sar(&mut self, ty: Type, src: u32, shift: u32) -> u32 { - let (src, shift) = BinaryOperands::new_shift(self).wrangle(src, ty, shift, ty); + let (src, shift) = BinaryOperands::new_shift(self).wrangle(src, ty, shift, Type::Byte); self.push(Inst::ShiftRightSigned(ty), Data::binary(src, shift)) } - pub fn gen_ret_val(&mut self, val: u32) -> u32 { - self.push(Inst::ReturnValue, Data::node(val)) + pub fn gen_ret_val(&mut self, ty: Type, val: u32) -> u32 { + self.push(Inst::ReturnValue(ty), Data::node(val)) } pub fn gen_ret(&mut self) -> u32 { self.push(Inst::Return, Data::none()) @@ -867,7 +953,7 @@ impl Mir { &self, w: &mut W, strings: &StringTable, - reg_alloc: &BTreeMap, + reg_alloc: &BTreeMap, node: u32, ) -> core::fmt::Result { let idx = node as usize; @@ -879,7 +965,7 @@ impl Mir { } match inst { - Inst::Label => writeln!(w, "%{node} = {}", strings.get_str(data.as_index())), + Inst::Label => writeln!(w, "%{node} = label {}", strings.get_str(data.as_index())), Inst::ConstantBytes => writeln!( w, "%{node} = bytes({:x?})", @@ -889,6 +975,16 @@ impl Mir { Inst::ConstantWord => writeln!(w, "%{node} = imm16({:x?})", data.as_imm16()), Inst::ConstantDWord => writeln!(w, "%{node} = imm32({:x?})", data.as_imm32()), Inst::ConstantQWord => writeln!(w, "%{node} = imm64({:x?})", data.as_imm64()), + Inst::ConstantSinglePrecision => writeln!( + w, + "%{node} = imm32fp({:x?})", + f32::from_bits(data.as_imm32()) + ), + Inst::ConstantDoublePrecision => writeln!( + w, + "%{node} = imm64fp({:x?})", + f64::from_bits(data.as_imm64()) + ), Inst::LoadRegister(ty) => { let src = data.as_node(); writeln!(w, "%{node} = load register {ty} %{src}") @@ -960,6 +1056,18 @@ impl Mir { let lhs = data.as_node(); writeln!(w, "%{node} = negate {ty} %{lhs}") } + Inst::SignExtend(ty) => { + let lhs = data.as_node(); + writeln!(w, "%{node} = sign extend {ty} %{lhs}") + } + Inst::ZeroExtend(ty) => { + let lhs = data.as_node(); + writeln!(w, "%{node} = zero extend {ty} %{lhs}") + } + Inst::IsZero(ty) => { + let lhs = data.as_node(); + writeln!(w, "%{node} = is zero {ty} %{lhs}") + } Inst::ShiftLeft(ty) => { let (lhs, rhs) = data.as_binary(); writeln!(w, "%{node} = shift left {ty} %{lhs}, {ty} %{rhs}") @@ -972,9 +1080,9 @@ impl Mir { let (lhs, rhs) = data.as_binary(); writeln!(w, "%{node} = signed shift right {ty} %{lhs}, {ty} %{rhs}") } - Inst::ReturnValue => { + Inst::ReturnValue(ty) => { let lhs = data.as_node(); - writeln!(w, "%{node} = return %{lhs}") + writeln!(w, "%{node} = return {ty} %{lhs}") } Inst::Return => { writeln!(w, "%{node} = return") @@ -1000,14 +1108,14 @@ impl Mir { } impl Mir { - pub fn build_liveness(&self) -> BTreeMap { + pub fn build_liveness(&self) -> BTreeMap { struct Interval { start: u32, end: u32, } let mut references = BTreeMap::>::new(); - let mut preferred_colors = BTreeMap::::new(); + let mut preferred_colors = BTreeMap::::new(); use amd64::Register::*; let mut in_colors = [r9, r8, rcx, rdx, rdi, rsi].to_vec(); let mut in_colors_sse = [xmm7, xmm6, xmm5, xmm4, xmm3, xmm2, xmm1, xmm0].to_vec(); @@ -1032,13 +1140,21 @@ impl Mir { inouts.push(node); } // return is thru rax. - Inst::ReturnValue => { + Inst::ReturnValue(ty) => { let val = data.as_node(); inouts.push(val); references.get_mut(&val).unwrap().push(node); - _ = preferred_colors.try_insert(val, amd64::Register::rax); + if ty.is_floating() { + _ = preferred_colors.try_insert(val, Register::xmm0); + } else { + _ = preferred_colors.try_insert(val, Register::rax); + } } - Inst::Negate(_) | Inst::Load(_) => { + Inst::SignExtend(_) + | Inst::ZeroExtend(_) + | Inst::IsZero(_) + | Inst::Negate(_) + | Inst::Load(_) => { references.get_mut(&data.as_node()).unwrap().push(node); } Inst::GetElementPtr(_) => { @@ -1053,7 +1169,7 @@ impl Mir { references.get_mut(&rhs).unwrap().push(node); if !ty.is_floating() { - _ = preferred_colors.try_insert(lhs, amd64::Register::rax); + _ = preferred_colors.try_insert(lhs, Register::rax); } } // div wants lhs to be rax, idiv can do either. @@ -1064,7 +1180,7 @@ impl Mir { references.get_mut(&rhs).unwrap().push(node); if !ty.is_floating() { - _ = preferred_colors.try_insert(lhs, amd64::Register::rax); + _ = preferred_colors.try_insert(lhs, Register::rax); } } // div wants lhs to be rax, idiv can do either. @@ -1075,7 +1191,7 @@ impl Mir { references.get_mut(&rhs).unwrap().push(node); if !ty.is_floating() { - _ = preferred_colors.try_insert(lhs, amd64::Register::rdx); + _ = preferred_colors.try_insert(lhs, Register::rdx); } } // shr,shl,sar want the shift to be in cl. @@ -1083,7 +1199,7 @@ impl Mir { let (lhs, rhs) = data.as_binary(); references.get_mut(&lhs).unwrap().push(node); references.get_mut(&rhs).unwrap().push(node); - _ = preferred_colors.try_insert(rhs, amd64::Register::rcx); + _ = preferred_colors.try_insert(rhs, Register::rcx); } // add,adc,sub,sbb,or,and,xor and mov don't care much about their source registers Inst::Add(_) @@ -1100,8 +1216,10 @@ impl Mir { } } - references.retain(|&node, refs| !refs.is_empty() && self.nodes[node as usize].has_value()); - preferred_colors.retain(|&node, _| self.nodes[node as usize].has_value()); + references.retain(|&node, refs| { + !refs.is_empty() && self.nodes[node as usize].result_is_register() + }); + preferred_colors.retain(|&node, _| self.nodes[node as usize].result_is_register()); let intervals = references .iter() @@ -1128,12 +1246,12 @@ impl Mir { enum Color { #[default] Unassigned, - Tentative(amd64::Register), - Final(amd64::Register), + Tentative(Register), + Final(Register), } impl Color { - fn color(self) -> Option { + fn color(self) -> Option { match self { Color::Unassigned => None, Color::Tentative(color) | Color::Final(color) => Some(color), @@ -1145,19 +1263,19 @@ impl Mir { mir: &'a Mir, graph: petgraph::graph::UnGraph<(), ()>, colors: BTreeMap, - preferred: BTreeMap, + preferred: BTreeMap, } impl<'a> Colorizer<'a> { - fn node_colors(&self, node: petgraph::graph::NodeIndex) -> &[amd64::Register] { + fn node_colors(&self, node: petgraph::graph::NodeIndex) -> &[Register] { let colors = if self.mir.nodes[node.index()] .value_type() .map(|t| t.is_floating()) == Some(true) { - &amd64::Register::SSE[..] + &Register::SSE[..] } else { - &amd64::Register::GPR[..] + &Register::GPR[..] }; colors } @@ -1240,7 +1358,7 @@ impl Mir { } } - fn finalise(self) -> BTreeMap { + fn finalise(self) -> BTreeMap { self.colors .into_iter() .filter_map(|(node, c)| match c { @@ -1263,7 +1381,7 @@ impl Mir { colorizer.prepass(inouts); for &node in references.keys().rev() { - if !self.nodes[node as usize].has_value() { + if !self.nodes[node as usize].result_is_register() { continue; } colorizer.color_node(node.into()); @@ -1295,68 +1413,1193 @@ impl Mir { } } -use crate::asm::amd64::{self, Mnemonic, Operand, Operands}; use crate::variant; -type Asm = Vec<(Mnemonic, Operands)>; + +#[derive(Debug, PartialEq, Eq)] +struct StackMem { + offset: u32, + size: u32, +} + +impl StackMem { + fn new(offset: u32, size: u32) -> Self { + Self { offset, size } + } +} + +impl core::fmt::Display for StackMem { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{} ptr [rbp - {}]", + Type::from_bytesize_int(self.size), + self.offset + ) + } +} + +#[derive(Debug, PartialEq, Eq)] +enum ImmRegMem { + Byte(u8), + Word(u16), + DWord(u32), + QWord(u64), + Mem(StackMem), + Rip(RipRelative), + Reg(Register), +} + +impl ImmRegMem { + fn is_floating(&self) -> bool { + match self { + ImmRegMem::Reg(reg) => reg.is_sse(), + _ => false, + } + } + fn byte_width(&self) -> u32 { + match self { + ImmRegMem::Byte(_) => 1, + ImmRegMem::Word(_) => 2, + ImmRegMem::DWord(_) => 4, + ImmRegMem::QWord(_) => 8, + ImmRegMem::Mem(mem) => mem.size, + ImmRegMem::Rip(rip) => rip.ty().bytes(), + ImmRegMem::Reg(reg) => reg.byte_size(), + } + } + fn occupy_same_register(&self, reg: Register) -> bool { + match self { + &ImmRegMem::Reg(r) => r.parent_reg() == reg.parent_reg(), + _ => false, + } + } +} + +impl From for ImmRegMem { + fn from(value: Register) -> Self { + Self::Reg(value) + } +} + +impl core::fmt::Display for ImmRegMem { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + use ImmRegMem::*; + match self { + Byte(v) => write!(f, "0x{v:x}"), + Word(v) => write!(f, "0x{v:x}"), + DWord(v) => write!(f, "0x{v:x}"), + QWord(v) => write!(f, "0x{v:x}"), + Rip(rip) => write!(f, "{rip}"), + Mem(mem) => write!(f, "{mem}"), + Reg(reg) => write!(f, "{reg}"), + } + } +} + +#[derive(Debug, PartialEq, Eq)] +enum RipRelative { + Label(Type, String), + #[allow(dead_code)] + Offset(Type, i32), +} + +impl RipRelative { + fn ty(&self) -> Type { + match self { + RipRelative::Label(ty, _) => *ty, + RipRelative::Offset(ty, _) => *ty, + } + } +} + +impl core::fmt::Display for RipRelative { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + RipRelative::Label(ty, label) => write!(f, "{} ptr [rip + {label}]", ty.int_repr()), + RipRelative::Offset(ty, offset) => { + write!(f, "{} ptr [rip + {offset}]", ty.int_repr()) + } + } + } +} + +#[derive(Debug)] +pub struct Function { + name: StringsIndex, + constants: BTreeMap, + branches: HashMap, + current_branch: StringsIndex, + stack_offset: u32, + dirty_registers: BTreeSet, +} + +// predefined constants: 0F32, 0F64, NEG1F32, NEG1F64 + +impl Function { + fn new(name: StringsIndex) -> Self { + let current_branch = StringsIndex::none(); + let branches = HashMap::from([(current_branch, String::new())]); + Self { + name, + constants: BTreeMap::new(), + branches, + current_branch, + stack_offset: 0, + dirty_registers: BTreeSet::new(), + } + } + + #[allow(dead_code)] + fn dirty_register(&mut self, reg: Register) { + self.dirty_registers.insert(reg); + } + fn create_new_branch(&mut self, index: StringsIndex) { + self.current_branch = index; + self.branches.insert(index, String::new()); + } + + fn get_constant_label(&self, i: usize) -> String { + // TODO: make this use the ST to retrieve the function name, + // or intern constants. + let mut hasher = std::hash::DefaultHasher::new(); + self.constants.get(&i).unwrap().hash(&mut hasher); + let constant = hasher.finish() as u16; + format!(".{i}_{:x}", constant) + } + + fn add_constant(&mut self, i: usize, content: String) { + _ = self.constants.try_insert(i, content); + } + + fn add_constant_from_inst_and_data( + &mut self, + i: usize, + inst: Inst, + data: Data, + strings: &StringTable, + ) { + match inst { + Inst::ConstantBytes => { + _ = strings; + todo!() + } + Inst::ConstantByte => { + self.add_constant(i, format!(".byte {}", data.as_imm8())); + } + Inst::ConstantWord => { + self.add_constant(i, format!(".2byte {}", data.as_imm16())); + } + Inst::ConstantSinglePrecision | Inst::ConstantDWord => { + self.add_constant(i, format!(".4byte {}", data.as_imm32())); + } + Inst::ConstantDoublePrecision | Inst::ConstantQWord => { + self.add_constant(i, format!(".8byte {}", data.as_imm64())); + } + _ => unreachable!(), + } + } + fn alloca(&mut self, size: u32, align: u32) -> u32 { + self.stack_offset += size; + self.stack_offset = self.stack_offset.next_multiple_of(align); + self.stack_offset + } + fn current_branch(&mut self) -> &mut String { + self.branches.get_mut(&self.current_branch).unwrap() + } + + pub fn finish( + mut self, + w: &mut W, + strings: &StringTable, + ) -> core::fmt::Result { + let name = strings.get_str(self.name).to_owned(); + writeln!(w, ".globl {name}")?; + writeln!(w, "{name}:")?; + + let saved_registers = self + .dirty_registers + .intersection(&BTreeSet::from_iter(Register::SYSV_CALLEE_SAVED)) + .cloned() + .collect::>(); + + for reg in saved_registers.iter() { + writeln!(w, "push {reg}")?; + } + + writeln!(w, "push rbp")?; + writeln!(w, "mov rbp, rsp")?; + writeln!(w, "sub rsp, {}", self.stack_offset)?; + + write!( + w, + "{}", + self.branches.remove(&StringsIndex::none()).unwrap() + )?; + + for (branch, content) in &self.branches { + if name != "main" { + writeln!(w, "{name}_{}:", strings.get_str(*branch))?; + write!(w, "{content}")?; + } + } + + writeln!(w, "{name}__epilogue:")?; + writeln!(w, "mov rsp, rbp")?; + writeln!(w, "pop rbp")?; + + for reg in saved_registers.iter().rev() { + writeln!(w, "pop {reg}")?; + } + writeln!(w, "ret")?; + + Ok(()) + } +} + +type Liveness = BTreeMap; #[allow(dead_code, unused)] impl Mir { - fn assemble(&self, strings: &StringTable) { - let mut mapping = HashMap::::new(); - let mut entry = Asm::new(); - let mut branches = HashMap::::new(); - let mut stack_offset = 0; - let mut current_branch = StringsIndex::none(); - branches.insert(current_branch, Asm::new()); + fn node_as_operand( + &self, + liveness: &Liveness, + mapping: &BTreeMap, + func: &mut Function, + strings: &StringTable, + node: u32, + ) -> ImmRegMem { + let inst = self.nodes[node as usize]; + let data = self.data[node as usize]; + match inst { + Inst::Label => todo!(), + Inst::ConstantBytes => todo!(), + Inst::ConstantByte => ImmRegMem::Byte(data.as_imm8()), + Inst::ConstantWord => ImmRegMem::Word(data.as_imm16()), + Inst::ConstantDWord => ImmRegMem::DWord(data.as_imm32()), + Inst::ConstantQWord => ImmRegMem::QWord(data.as_imm64()), + Inst::ConstantSinglePrecision => { + func.add_constant_from_inst_and_data( + node as usize, + self.nodes[node as usize], + self.data[node as usize], + strings, + ); + let label = func.get_constant_label(node as usize); + ImmRegMem::Rip(RipRelative::Label(Type::DWord, label)) + } + Inst::ConstantDoublePrecision => { + func.add_constant_from_inst_and_data( + node as usize, + self.nodes[node as usize], + self.data[node as usize], + strings, + ); + let label = func.get_constant_label(node as usize); + ImmRegMem::Rip(RipRelative::Label(Type::QWord, label)) + } + Inst::GetElementPtr(ty) => liveness.get(&node).unwrap().clone().into(), + Inst::Parameter(ty) + | Inst::Add(ty) + | Inst::Sub(ty) + | Inst::Mul(ty) + | Inst::MulSigned(ty) + | Inst::Div(ty) + | Inst::DivSigned(ty) + | Inst::Rem(ty) + | Inst::RemSigned(ty) + | Inst::BitAnd(ty) + | Inst::BitOr(ty) + | Inst::BitXOr(ty) + | Inst::Negate(ty) + | Inst::ShiftLeft(ty) + | Inst::ShiftRightSigned(ty) + | Inst::ShiftRightUnsigned(ty) + | Inst::SignExtend(ty) + | Inst::ZeroExtend(ty) + | Inst::IsZero(ty) + | Inst::Load(ty) + | Inst::LoadRegister(ty) => ty.register_width(*liveness.get(&node).unwrap()).into(), + Inst::Alloca => { + let (offset, size) = *mapping.get(&(node as usize)).unwrap(); + ImmRegMem::Mem(StackMem::new(offset, size)) + } + Inst::ExternRef => todo!(), + _ => { + unreachable!() + } + } + } + + pub fn assemble(&self, strings: &StringTable) -> Result { + use core::fmt::Write; + // mapping if (i, (stack_offset, bytes)) for local stack variables + let mut mapping = BTreeMap::::new(); + let mut func = Function::new(self.name); + let name = strings.get_str(self.name).to_owned(); + let liveness = self.build_liveness(); + func.dirty_registers.extend(liveness.values().cloned()); + + let mut float_params = 0; + let mut int_params = 0; for i in 0..self.nodes.len() { + let node = i as u32; let inst = self.nodes[i]; let data = self.data[i]; - let branch = branches.get_mut(¤t_branch).unwrap(); + write!(func.current_branch(), "// "); + self.render_node(func.current_branch(), strings, &liveness, node); match inst { - Inst::Label => todo!(), - Inst::ConstantBytes => todo!(), - Inst::ConstantByte => todo!(), - Inst::ConstantWord => todo!(), - Inst::ConstantDWord => todo!(), - Inst::ConstantQWord => todo!(), - Inst::LoadRegister(ty) => todo!(), + Inst::Label => { + func.create_new_branch(data.as_index()); + } + Inst::ConstantBytes + | Inst::ConstantByte + | Inst::ConstantWord + | Inst::ConstantDWord + | Inst::ConstantQWord => {} + Inst::ConstantSinglePrecision => { + let bits = data.as_imm32(); + func.add_constant(i, format!(".long {bits}")); + } + Inst::ConstantDoublePrecision => { + let bits = data.as_imm64(); + func.add_constant(i, format!(".quad {bits}")); + } + Inst::LoadRegister(ty) => { + let src = data.as_node(); + let dst = ty.register_width(*liveness.get(&node).unwrap()); + let src = self.node_as_operand(&liveness, &mapping, &mut func, strings, src); + if ty.is_floating() { + match src { + ImmRegMem::Byte(_) + | ImmRegMem::Word(_) + | ImmRegMem::DWord(_) + | ImmRegMem::QWord(_) => { + writeln!(func.current_branch(), "push rax")?; + + writeln!( + func.current_branch(), + "mov {}, {src}", + ty.register_width(Register::rax) + )?; + writeln!( + func.current_branch(), + "movd {dst}, {}", + ty.register_width(Register::rax) + )?; + writeln!(func.current_branch(), "pop rax")?; + } + ImmRegMem::Mem(_) | ImmRegMem::Rip(_) => { + writeln!(func.current_branch(), "movss {dst}, {src}",)?; + } + ImmRegMem::Reg(_) => { + writeln!(func.current_branch(), "movd {dst}, {src}",)?; + } + } + } else { + writeln!(func.current_branch(), "mov {dst}, {src}",)?; + } + } Inst::ExternRef => todo!(), Inst::Alloca => { let (size, align) = data.as_binary(); - let size = size.next_multiple_of(align); - stack_offset += size; - mapping.insert(i, (current_branch, branch.len())); - branch.push((Mnemonic::alloca, Operands::One(Operand::imm32(size)))) + let offset = func.alloca(size, align); + mapping.insert(i, (offset, size)); } Inst::Load(ty) => { + let dst = ty.register_width(*liveness.get(&node).unwrap()); + let src = data.as_node(); + let src = self.node_as_operand(&liveness, &mapping, &mut func, strings, src); + match src { + ImmRegMem::Reg(_) => { + writeln!(func.current_branch(), "mov {}, {ty} ptr [{}]", dst, src,)?; + } + ImmRegMem::Mem(ref mem) => { + let tmp_reg = if dst.parent_reg() == Register::rax { + Register::rcx + } else { + Register::rax + }; + writeln!(func.current_branch(), "push {tmp_reg}")?; + writeln!(func.current_branch(), "mov {}, {}", tmp_reg, src)?; + writeln!(func.current_branch(), "mov {}, [{}]", dst, tmp_reg)?; + writeln!(func.current_branch(), "pop {tmp_reg}")?; + } + _ => {} + } // stuff // branch.push((Mnemonic::mov, Operands::One(Operand::imm32(size)))) } - Inst::Store(ty) => todo!(), - Inst::GetElementPtr(ty) => todo!(), - Inst::Parameter(ty) => todo!(), - Inst::Add(_) => todo!(), - Inst::Sub(_) => todo!(), - Inst::Mul(_) => todo!(), - Inst::MulSigned(_) => todo!(), - Inst::Div(_) => todo!(), - Inst::DivSigned(_) => todo!(), - Inst::Rem(_) => todo!(), - Inst::RemSigned(_) => todo!(), - Inst::BitAnd(_) => todo!(), - Inst::BitOr(_) => todo!(), - Inst::BitXOr(_) => todo!(), - Inst::Negate(_) => todo!(), - Inst::ShiftLeft(_) => todo!(), - Inst::ShiftRightSigned(_) => todo!(), - Inst::ShiftRightUnsigned(_) => todo!(), - Inst::ReturnValue => todo!(), - Inst::Return => todo!(), + Inst::Store(ty) => { + let (src, dst) = data.as_binary(); + let src = self.node_as_operand(&liveness, &mapping, &mut func, strings, src); + let dst = self.node_as_operand(&liveness, &mapping, &mut func, strings, dst); + if src.is_floating() { + writeln!(func.current_branch(), "movss {dst}, {src}")?; + } else { + writeln!(func.current_branch(), "mov {dst}, {src}")?; + } + } + Inst::GetElementPtr(ty) => { + let dst = *liveness.get(&node).unwrap(); + let (src, idx) = data.as_binary(); + let src = self.node_as_operand(&liveness, &mapping, &mut func, strings, src); + + if let ImmRegMem::Mem(_) = &src { + writeln!(func.current_branch(), "lea {dst}, {src}",)?; + } + + let offset = idx * ty.bytes(); + if offset != 0 { + writeln!(func.current_branch(), "lea {dst}, [{dst} + {offset}]",)?; + } + } + Inst::Parameter(ty) => { + if ty.is_floating() { + float_params += 1; + if float_params > 4 { + eprintln!("more than 4 int params, we dont handle stack params yet!"); + } + } else { + int_params += 1; + if int_params > 4 { + eprintln!("more than 4 float params, we dont handle stack params yet!"); + } + } + } + Inst::Add(ty) => { + let dst = ty.register_width(*liveness.get(&node).unwrap()); + let (lhs, rhs) = data.as_binary(); + let lhs = self.node_as_operand(&liveness, &mapping, &mut func, strings, lhs); + let rhs = self.node_as_operand(&liveness, &mapping, &mut func, strings, rhs); + + if ty.is_floating() { + writeln!(func.current_branch(), "addss {lhs}, {rhs}")?; + if !lhs.occupy_same_register(dst) { + writeln!(func.current_branch(), "movss {dst}, {lhs}")?; + } + } else { + writeln!(func.current_branch(), "add {lhs}, {rhs}")?; + if !lhs.occupy_same_register(dst) { + writeln!(func.current_branch(), "mov {dst}, {lhs}")?; + } + } + } + Inst::Sub(ty) => { + let dst = ty.register_width(*liveness.get(&node).unwrap()); + let (lhs, rhs) = data.as_binary(); + let lhs = self.node_as_operand(&liveness, &mapping, &mut func, strings, lhs); + let rhs = self.node_as_operand(&liveness, &mapping, &mut func, strings, rhs); + if ty.is_floating() { + writeln!(func.current_branch(), "subss {lhs}, {rhs}")?; + if !lhs.occupy_same_register(dst) { + writeln!(func.current_branch(), "movss {dst}, {lhs}")?; + } + } else { + writeln!(func.current_branch(), "sub {lhs}, {rhs}")?; + if !lhs.occupy_same_register(dst) { + writeln!(func.current_branch(), "mov {dst}, {lhs}")?; + } + } + } + Inst::Mul(ty) => { + let dst = ty.register_width(*liveness.get(&node).unwrap()); + let (lhs, rhs) = data.as_binary(); + let lhs = self.node_as_operand(&liveness, &mapping, &mut func, strings, lhs); + let rhs = self.node_as_operand(&liveness, &mapping, &mut func, strings, rhs); + + if ty.is_floating() { + writeln!(func.current_branch(), "mulss {lhs}, {rhs}")?; + if !lhs.occupy_same_register(dst) { + writeln!(func.current_branch(), "movss {dst}, {lhs}")?; + } + } else { + let spill_rax = !(lhs.occupy_same_register(Register::rax) + || rhs.occupy_same_register(Register::rax) + || dst.parent_reg() == Register::rax); + let spill_rdx = !(lhs.occupy_same_register(Register::rdx) + || rhs.occupy_same_register(Register::rdx) + || dst.parent_reg() == Register::rdx); + + if spill_rax { + writeln!(func.current_branch(), "push rax")?; + } + if spill_rdx { + writeln!(func.current_branch(), "push rdx")?; + } + + if !lhs.occupy_same_register(Register::rax) { + writeln!( + func.current_branch(), + "mov {}, {lhs}", + ty.register_width(Register::rax) + )?; + } + + writeln!(func.current_branch(), "mul {rhs}")?; + + if dst.parent_reg() != Register::rax { + writeln!( + func.current_branch(), + "mov {dst}, {}", + ty.register_width(Register::rax) + )?; + } + + if spill_rdx { + writeln!(func.current_branch(), "pop rdx")?; + } + if spill_rax { + writeln!(func.current_branch(), "pop rax")?; + } + } + } + Inst::MulSigned(ty) => { + let dst = ty.register_width(*liveness.get(&node).unwrap()); + let (lhs, rhs) = data.as_binary(); + let lhs = self.node_as_operand(&liveness, &mapping, &mut func, strings, lhs); + let rhs = self.node_as_operand(&liveness, &mapping, &mut func, strings, rhs); + + let spill_rax = !(lhs.occupy_same_register(Register::rax) + || rhs.occupy_same_register(Register::rax) + || dst.parent_reg() == Register::rax); + let spill_rdx = !(lhs.occupy_same_register(Register::rdx) + || rhs.occupy_same_register(Register::rdx) + || dst.parent_reg() == Register::rdx); + + if spill_rax { + writeln!(func.current_branch(), "push rax")?; + } + if spill_rdx { + writeln!(func.current_branch(), "push rdx")?; + } + + writeln!(func.current_branch(), "imul {lhs}, {rhs}")?; + + if !lhs.occupy_same_register(dst.parent_reg()) { + writeln!(func.current_branch(), "mov {dst}, {lhs}",)?; + } + + if spill_rdx { + writeln!(func.current_branch(), "pop rdx")?; + } + if spill_rax { + writeln!(func.current_branch(), "pop rax")?; + } + } + Inst::Div(ty) => { + let dst = ty.register_width(*liveness.get(&node).unwrap()); + let (lhs, rhs) = data.as_binary(); + let lhs = self.node_as_operand(&liveness, &mapping, &mut func, strings, lhs); + let rhs = self.node_as_operand(&liveness, &mapping, &mut func, strings, rhs); + + if ty.is_floating() { + writeln!(func.current_branch(), "divss {lhs}, {rhs}")?; + if lhs.occupy_same_register(dst) { + writeln!(func.current_branch(), "movss {dst}, {lhs}")?; + } + } else { + let spill_rax = !(lhs.occupy_same_register(Register::rax) + || rhs.occupy_same_register(Register::rax) + || dst.parent_reg() == Register::rax); + let spill_rdx = !(lhs.occupy_same_register(Register::rdx) + || rhs.occupy_same_register(Register::rdx) + || dst.parent_reg() == Register::rdx); + + if spill_rax { + writeln!(func.current_branch(), "push rax")?; + } + if spill_rdx { + writeln!(func.current_branch(), "push rdx")?; + } + + if !lhs.occupy_same_register(Register::rax) { + writeln!( + func.current_branch(), + "mov {}, {lhs}", + ty.register_width(Register::rax) + )?; + } + + writeln!(func.current_branch(), "div {rhs}")?; + + if dst.parent_reg() != Register::rax { + writeln!( + func.current_branch(), + "mov {dst}, {}", + ty.register_width(Register::rax) + )?; + } + + if spill_rdx { + writeln!(func.current_branch(), "pop rdx")?; + } + if spill_rax { + writeln!(func.current_branch(), "pop rax")?; + } + } + } + Inst::DivSigned(ty) => { + let dst = ty.register_width(*liveness.get(&node).unwrap()); + let (lhs, rhs) = data.as_binary(); + let lhs = self.node_as_operand(&liveness, &mapping, &mut func, strings, lhs); + let rhs = self.node_as_operand(&liveness, &mapping, &mut func, strings, rhs); + + let spill_rax = !(lhs.occupy_same_register(Register::rax) + || rhs.occupy_same_register(Register::rax) + || dst.parent_reg() == Register::rax); + let spill_rdx = !(lhs.occupy_same_register(Register::rdx) + || rhs.occupy_same_register(Register::rdx) + || dst.parent_reg() == Register::rdx); + + if spill_rax { + writeln!(func.current_branch(), "push rax")?; + } + if spill_rdx { + writeln!(func.current_branch(), "push rdx")?; + } + + if !lhs.occupy_same_register(Register::rax) { + writeln!( + func.current_branch(), + "mov {}, {lhs}", + ty.register_width(Register::rax) + )?; + } + + writeln!(func.current_branch(), "idiv {rhs}")?; + + if dst.parent_reg() != Register::rax { + writeln!( + func.current_branch(), + "mov {dst}, {}", + ty.register_width(Register::rax) + )?; + } + + if spill_rdx { + writeln!(func.current_branch(), "pop rdx")?; + } + if spill_rax { + writeln!(func.current_branch(), "pop rax")?; + } + } + Inst::Rem(ty) => { + let dst = ty.register_width(*liveness.get(&node).unwrap()); + let (lhs, rhs) = data.as_binary(); + let lhs = self.node_as_operand(&liveness, &mapping, &mut func, strings, lhs); + let rhs = self.node_as_operand(&liveness, &mapping, &mut func, strings, rhs); + + if ty.is_floating() { + let ty = ty.int_repr(); + let size = ty.bytes(); + writeln!(func.current_branch(), "sub rsp, 0x{:x}", size * 2)?; + writeln!(func.current_branch(), "movss {ty} ptr [rsp], {lhs}")?; + writeln!( + func.current_branch(), + "movss {ty} ptr [rsp + {size}], {rhs}" + )?; + writeln!(func.current_branch(), "fld {ty} ptr [rsp + {size}]")?; + writeln!(func.current_branch(), "fld {ty} ptr [rsp]")?; + writeln!(func.current_branch(), "fprem")?; + writeln!(func.current_branch(), "fst {ty} ptr [rsp]")?; + writeln!(func.current_branch(), "movss {dst}, {ty} ptr [rsp]")?; + writeln!(func.current_branch(), "add rsp, 0x{:x}", size * 2)?; + } else { + let spill_rax = !(lhs.occupy_same_register(Register::rax) + || rhs.occupy_same_register(Register::rax) + || dst.parent_reg() == Register::rax); + let spill_rdx = !(lhs.occupy_same_register(Register::rdx) + || rhs.occupy_same_register(Register::rdx) + || dst.parent_reg() == Register::rdx); + + if spill_rax { + writeln!(func.current_branch(), "push rax")?; + } + if spill_rdx { + writeln!(func.current_branch(), "push rdx")?; + } + + if !lhs.occupy_same_register(Register::rax) { + writeln!( + func.current_branch(), + "mov {}, {lhs}", + ty.register_width(Register::rax) + )?; + } + + writeln!(func.current_branch(), "div {rhs}")?; + + if dst.parent_reg() != Register::rdx { + writeln!( + func.current_branch(), + "mov {dst}, {}", + ty.register_width(Register::rdx) + )?; + } + + if spill_rdx { + writeln!(func.current_branch(), "pop rdx")?; + } + if spill_rax { + writeln!(func.current_branch(), "pop rax")?; + } + } + } + Inst::RemSigned(ty) => { + let dst = ty.register_width(*liveness.get(&node).unwrap()); + let (lhs, rhs) = data.as_binary(); + let lhs = self.node_as_operand(&liveness, &mapping, &mut func, strings, lhs); + let rhs = self.node_as_operand(&liveness, &mapping, &mut func, strings, rhs); + + let spill_rax = !(lhs.occupy_same_register(Register::rax) + || rhs.occupy_same_register(Register::rax) + || dst.parent_reg() == Register::rax); + let spill_rdx = !(lhs.occupy_same_register(Register::rdx) + || rhs.occupy_same_register(Register::rdx) + || dst.parent_reg() == Register::rdx); + + if spill_rax { + writeln!(func.current_branch(), "push rax")?; + } + if spill_rdx { + writeln!(func.current_branch(), "push rdx")?; + } + + if !lhs.occupy_same_register(Register::rax) { + writeln!( + func.current_branch(), + "mov {}, {lhs}", + ty.register_width(Register::rax) + )?; + } + + writeln!(func.current_branch(), "idiv {rhs}")?; + + if dst.parent_reg() != Register::rdx { + writeln!( + func.current_branch(), + "mov {dst}, {}", + ty.register_width(Register::rdx) + )?; + } + + if spill_rdx { + writeln!(func.current_branch(), "pop rdx")?; + } + if spill_rax { + writeln!(func.current_branch(), "pop rax")?; + } + } + Inst::BitAnd(ty) => { + let dst = ty.register_width(*liveness.get(&node).unwrap()); + let (lhs, rhs) = data.as_binary(); + let lhs = self.node_as_operand(&liveness, &mapping, &mut func, strings, lhs); + let rhs = self.node_as_operand(&liveness, &mapping, &mut func, strings, rhs); + writeln!(func.current_branch(), "and {lhs}, {rhs}")?; + if !lhs.occupy_same_register(dst) { + writeln!(func.current_branch(), "mov {dst}, {lhs}")?; + } + } + Inst::BitOr(ty) => { + let dst = ty.register_width(*liveness.get(&node).unwrap()); + let (lhs, rhs) = data.as_binary(); + let lhs = self.node_as_operand(&liveness, &mapping, &mut func, strings, lhs); + let rhs = self.node_as_operand(&liveness, &mapping, &mut func, strings, rhs); + writeln!(func.current_branch(), "or {lhs}, {rhs}")?; + if !lhs.occupy_same_register(dst) { + writeln!(func.current_branch(), "mov {dst}, {lhs}")?; + } + } + Inst::BitXOr(ty) => { + let dst = ty.register_width(*liveness.get(&node).unwrap()); + let (lhs, rhs) = data.as_binary(); + let lhs = self.node_as_operand(&liveness, &mapping, &mut func, strings, lhs); + let rhs = self.node_as_operand(&liveness, &mapping, &mut func, strings, rhs); + writeln!(func.current_branch(), "xor {lhs}, {rhs}")?; + if !lhs.occupy_same_register(dst) { + writeln!(func.current_branch(), "mov {dst}, {lhs}")?; + } + } + Inst::ShiftLeft(ty) => { + let dst = ty.register_width(*liveness.get(&node).unwrap()); + let (lhs, rhs) = data.as_binary(); + let lhs = self.node_as_operand(&liveness, &mapping, &mut func, strings, lhs); + let rhs = self.node_as_operand(&liveness, &mapping, &mut func, strings, rhs); + + let lhs_is_rcx = lhs.occupy_same_register(Register::rcx); + let rhs_is_rcx = rhs.occupy_same_register(Register::rcx); + let dst_is_rcx = dst.parent_reg() == Register::rcx; + + match rhs { + ImmRegMem::Byte(v) => { + writeln!(func.current_branch(), "shl {lhs}, {}", v as u8)?; + } + ImmRegMem::DWord(v) => { + writeln!(func.current_branch(), "shl {lhs}, {}", v as u8)?; + } + ImmRegMem::QWord(v) => { + writeln!(func.current_branch(), "shl {lhs}, {}", v as u8)?; + } + ImmRegMem::Word(v) => { + writeln!(func.current_branch(), "shl {lhs}, {}", v as u8)?; + } + ImmRegMem::Reg(reg) => { + // reg needs to be moved to CL + // if lhs is in rcx, lhs needs to move to rax and we spill rax temporarily + // if neither lhs nor rhx nor dst are rcx, spill rcx temporarily + + let spill_rcx = !(lhs_is_rcx || rhs_is_rcx || dst_is_rcx); + + if spill_rcx { + writeln!(func.current_branch(), "push rcx")?; + } + + if !rhs_is_rcx { + writeln!( + func.current_branch(), + "mov {}, {rhs}", + Type::from_bytesize_int(rhs.byte_width()) + .register_width(Register::rcx) + )?; + } + + if lhs_is_rcx { + writeln!(func.current_branch(), "push rax")?; + writeln!(func.current_branch(), "test rax,rax")?; + writeln!( + func.current_branch(), + "mov {}, {lhs}", + Type::from_bytesize_int(lhs.byte_width()) + .register_width(Register::rax) + )?; + writeln!(func.current_branch(), "shl rax, cl")?; + writeln!(func.current_branch(), "mov {dst}, rax")?; + writeln!(func.current_branch(), "pop rax")?; + } else { + writeln!(func.current_branch(), "shl {lhs}, cl")?; + if lhs.occupy_same_register(dst) { + writeln!(func.current_branch(), "mov {dst}, {lhs}")?; + } + } + + if spill_rcx { + writeln!(func.current_branch(), "pop rcx")?; + } + } + _ => unreachable!(), + } + if !lhs.occupy_same_register(dst) && !lhs_is_rcx { + writeln!(func.current_branch(), "mov {dst}, {lhs}")?; + } + } + Inst::ShiftRightSigned(ty) => { + let dst = ty.register_width(*liveness.get(&node).unwrap()); + let (lhs, rhs) = data.as_binary(); + let lhs = self.node_as_operand(&liveness, &mapping, &mut func, strings, lhs); + let rhs = self.node_as_operand(&liveness, &mapping, &mut func, strings, rhs); + + let lhs_is_rcx = lhs.occupy_same_register(Register::rcx); + let rhs_is_rcx = rhs.occupy_same_register(Register::rcx); + let dst_is_rcx = dst.parent_reg() == Register::rcx; + + match rhs { + ImmRegMem::Byte(v) => { + writeln!(func.current_branch(), "sar {lhs}, {}", v as u8)?; + } + ImmRegMem::DWord(v) => { + writeln!(func.current_branch(), "sar {lhs}, {}", v as u8)?; + } + ImmRegMem::QWord(v) => { + writeln!(func.current_branch(), "sar {lhs}, {}", v as u8)?; + } + ImmRegMem::Word(v) => { + writeln!(func.current_branch(), "sar {lhs}, {}", v as u8)?; + } + ImmRegMem::Reg(reg) => { + // reg needs to be moved to CL + // if lhs is in rcx, lhs needs to move to rax and we spill rax temporarily + // if neither lhs nor rhx nor dst are rcx, spill rcx temporarily + + let spill_rcx = !(lhs_is_rcx || rhs_is_rcx || dst_is_rcx); + + if spill_rcx { + writeln!(func.current_branch(), "push rcx")?; + } + + if !rhs_is_rcx { + writeln!( + func.current_branch(), + "mov {}, {rhs}", + Type::from_bytesize_int(rhs.byte_width()) + .register_width(Register::rcx) + )?; + } + + if lhs_is_rcx { + writeln!(func.current_branch(), "push rax")?; + writeln!(func.current_branch(), "test rax,rax")?; + writeln!( + func.current_branch(), + "mov {}, {lhs}", + Type::from_bytesize_int(lhs.byte_width()) + .register_width(Register::rax) + )?; + writeln!(func.current_branch(), "sar rax, cl")?; + writeln!(func.current_branch(), "mov {dst}, rax")?; + writeln!(func.current_branch(), "pop rax")?; + } else { + writeln!(func.current_branch(), "sar {lhs}, cl")?; + if lhs.occupy_same_register(dst) { + writeln!(func.current_branch(), "mov {dst}, {lhs}")?; + } + } + + if spill_rcx { + writeln!(func.current_branch(), "pop rcx")?; + } + } + _ => unreachable!(), + } + if !lhs.occupy_same_register(dst) && !lhs_is_rcx { + writeln!(func.current_branch(), "mov {dst}, {lhs}")?; + } + } + Inst::ShiftRightUnsigned(ty) => { + let dst = ty.register_width(*liveness.get(&node).unwrap()); + let (lhs, rhs) = data.as_binary(); + let lhs = self.node_as_operand(&liveness, &mapping, &mut func, strings, lhs); + let rhs = self.node_as_operand(&liveness, &mapping, &mut func, strings, rhs); + + let lhs_is_rcx = lhs.occupy_same_register(Register::rcx); + let rhs_is_rcx = rhs.occupy_same_register(Register::rcx); + let dst_is_rcx = dst.parent_reg() == Register::rcx; + + match rhs { + ImmRegMem::Byte(v) => { + writeln!(func.current_branch(), "shr {lhs}, {}", v as u8)?; + } + ImmRegMem::DWord(v) => { + writeln!(func.current_branch(), "shr {lhs}, {}", v as u8)?; + } + ImmRegMem::QWord(v) => { + writeln!(func.current_branch(), "shr {lhs}, {}", v as u8)?; + } + ImmRegMem::Word(v) => { + writeln!(func.current_branch(), "shr {lhs}, {}", v as u8)?; + } + ImmRegMem::Reg(reg) => { + // reg needs to be moved to CL + // if lhs is in rcx, lhs needs to move to rax and we spill rax temporarily + // if neither lhs nor rhx nor dst are rcx, spill rcx temporarily + + let spill_rcx = !(lhs_is_rcx || rhs_is_rcx || dst_is_rcx); + + if spill_rcx { + writeln!(func.current_branch(), "push rcx")?; + } + + if !rhs_is_rcx { + writeln!( + func.current_branch(), + "mov {}, {rhs}", + Type::from_bytesize_int(rhs.byte_width()) + .register_width(Register::rcx) + )?; + } + + if lhs_is_rcx { + writeln!(func.current_branch(), "push rax")?; + writeln!(func.current_branch(), "test rax,rax")?; + writeln!( + func.current_branch(), + "mov {}, {lhs}", + Type::from_bytesize_int(lhs.byte_width()) + .register_width(Register::rax) + )?; + writeln!(func.current_branch(), "shr rax, cl")?; + writeln!(func.current_branch(), "mov {dst}, rax")?; + writeln!(func.current_branch(), "pop rax")?; + } else { + writeln!(func.current_branch(), "shr {lhs}, cl")?; + if lhs.occupy_same_register(dst) { + writeln!(func.current_branch(), "mov {dst}, {lhs}")?; + } + } + + if spill_rcx { + writeln!(func.current_branch(), "pop rcx")?; + } + } + _ => unreachable!(), + } + if !lhs.occupy_same_register(dst) && !lhs_is_rcx { + writeln!(func.current_branch(), "mov {dst}, {lhs}")?; + } + } + Inst::Negate(ty) => { + let dst = ty.register_width(*liveness.get(&node).unwrap()); + let lhs = data.as_node(); + let lhs = self.node_as_operand(&liveness, &mapping, &mut func, strings, lhs); + + if ty.is_floating() { + writeln!(func.current_branch(), "mulss {lhs}, [rip + .NEG1F32]")?; + if lhs != ImmRegMem::Reg(dst) { + writeln!(func.current_branch(), "movss {dst}, {lhs}")?; + } + } else { + writeln!(func.current_branch(), "neg {lhs}")?; + if lhs != ImmRegMem::Reg(dst) { + writeln!(func.current_branch(), "mov {dst}, {lhs}")?; + } + } + } + Inst::SignExtend(ty) => { + let dst = ty.register_width(*liveness.get(&node).unwrap()); + let lhs = data.as_node(); + let lhs_ty = self.type_of_node(lhs).unwrap(); + let lhs = self.node_as_operand(&liveness, &mapping, &mut func, strings, lhs); + + if ty == lhs_ty { + writeln!(func.current_branch(), "test {dst}, {dst}")?; + writeln!(func.current_branch(), "mov {dst}, {lhs}")?; + } else { + match lhs { + ImmRegMem::Byte(v) => { + writeln!(func.current_branch(), "mov {}, {v}", dst.into_byte())?; + writeln!( + func.current_branch(), + "movsx {dst}, {}", + dst.into_byte() + )?; + } + ImmRegMem::Word(v) => { + writeln!(func.current_branch(), "mov {},{v}", dst.into_word())?; + writeln!( + func.current_branch(), + "movsx {dst}, {}", + dst.into_byte() + )?; + } + ImmRegMem::DWord(v) => { + writeln!(func.current_branch(), "mov {},{v}", dst.into_dword())?; + writeln!( + func.current_branch(), + "movsxd {dst}, {}", + dst.into_byte() + )?; + } + ImmRegMem::QWord(v) => { + writeln!(func.current_branch(), "mov {},{v}", dst.into_qword())?; + } + ImmRegMem::Mem(mem) => match lhs_ty { + Type::Byte | Type::Word => { + writeln!(func.current_branch(), "movsx {dst}, {mem}")?; + } + Type::DWord => { + writeln!(func.current_branch(), "movsxd {dst}, {mem}")?; + } + Type::QWord => { + writeln!(func.current_branch(), "movs {dst}, {mem}")?; + } + _ => { + panic!("cannot sign-extend a floating register") + } + }, + ImmRegMem::Reg(reg) => match lhs_ty { + Type::Byte | Type::Word => { + writeln!(func.current_branch(), "movsx {dst}, {reg}")?; + } + Type::DWord => { + writeln!(func.current_branch(), "movsxd {dst}, {reg}")?; + } + Type::QWord => { + writeln!(func.current_branch(), "mov {dst}, {reg}")?; + } + _ => { + panic!("cannot sign-extend a floating register") + } + }, + _ => unreachable!(), + } + } + } + Inst::ZeroExtend(ty) => { + let dst = ty.register_width(*liveness.get(&node).unwrap()); + let lhs = data.as_node(); + let lhs_ty = self.type_of_node(lhs).unwrap(); + let lhs = self.node_as_operand(&liveness, &mapping, &mut func, strings, lhs); + + if ty == lhs_ty { + writeln!(func.current_branch(), "test {dst}, {dst}")?; + writeln!(func.current_branch(), "mov {dst}, {lhs}")?; + } else { + writeln!(func.current_branch(), "test {0}, {0}", dst.parent_reg())?; + match lhs { + ImmRegMem::Byte(v) => { + writeln!(func.current_branch(), "mov {}, {v}", dst.into_byte())?; + } + ImmRegMem::Word(v) => { + writeln!(func.current_branch(), "mov {}, {v}", dst.into_word())?; + } + ImmRegMem::DWord(v) => { + writeln!(func.current_branch(), "mov {}, {v}", dst.into_dword())?; + } + ImmRegMem::QWord(v) => { + writeln!(func.current_branch(), "mov {}, {v}", dst.into_qword())?; + } + ImmRegMem::Mem(mem) => { + writeln!( + func.current_branch(), + "mov {}, {mem}", + lhs_ty.register_width(dst) + )?; + } + ImmRegMem::Reg(reg) => { + writeln!( + func.current_branch(), + "mov {}, {reg}", + lhs_ty.register_width(dst) + )?; + } + _ => unreachable!(), + } + } + } + Inst::IsZero(ty) => { + let dst = ty.register_width(*liveness.get(&node).unwrap()); + let lhs = data.as_node(); + let lhs = self.node_as_operand(&liveness, &mapping, &mut func, strings, lhs); + writeln!(func.current_branch(), "mov {dst}, {lhs}")?; + writeln!(func.current_branch(), "test {dst}, {dst}")?; + writeln!(func.current_branch(), "setz {}", dst.into_byte())?; + } + Inst::ReturnValue(ty) => { + let lhs = data.as_node(); + let lhs = self.node_as_operand(&liveness, &mapping, &mut func, strings, lhs); + if ty.is_floating() { + writeln!(func.current_branch(), "movss xmm0, {lhs}",)?; + } else { + writeln!( + func.current_branch(), + "mov {}, {lhs}", + Register::rax.into_bytesize(lhs.byte_width()) + )?; + } + writeln!(func.current_branch(), "jmp {name}__epilogue")?; + } + Inst::Return => { + writeln!(func.current_branch(), "jmp {name}__epilogue")?; + } } } + + let mut buf = String::new(); + func.finish(&mut buf, strings)?; + + Ok(buf) } } diff --git a/src/parser.rs b/src/parser.rs index a011706..a404e7b 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1,4 +1,4 @@ -use std::{collections::HashMap, fmt::Display}; +use std::collections::HashMap; use itertools::Itertools; use num_bigint::{BigInt, BigUint}; @@ -12,7 +12,6 @@ use crate::{ string_table::{ImmOrIndex, Index, StringTable}, symbol_table::{SymbolKind, SymbolTable}, tokens::Token, - variant, }; #[derive(Debug, thiserror::Error)] @@ -1950,7 +1949,7 @@ impl Tree { ast::tree_visitor::Visitor::new_seek( self,start, - |_: &Tree, node| { + |_: &Tree, _| { }, |tree: &Tree, node| match tree.nodes.get_node(node) { &Tag::Assign { lhs, rhs } => { diff --git a/src/triples.rs b/src/triples.rs index b11ad93..5a13177 100644 --- a/src/triples.rs +++ b/src/triples.rs @@ -3,7 +3,7 @@ use std::collections::{hash_map::Entry, BTreeMap, BTreeSet, HashMap}; use crate::{ - ast::{Node as AstNode, Tag, Type}, + ast::{IntegralType, Node as AstNode, Tag, Type}, parser::Tree, string_table::{ImmOrIndex, Index as StringsIndex, StringTable}, variant, writeln_indented, @@ -18,7 +18,7 @@ enum NodeOrList { } #[derive(Debug, PartialEq, Eq, Clone, Copy)] -enum Type2 { +pub enum Type2 { Integral(bool, u16), Binary32, Binary64, @@ -108,7 +108,7 @@ impl From<&Type> for Type2 { } #[derive(Debug, PartialEq, Eq, Clone, Copy)] -enum Inst { +pub enum Inst { /// index Label, /// index @@ -154,7 +154,9 @@ enum Inst { /// lhs Negate(Type2), /// lhs - ReturnValue, + ExplicitCast(Type2, Type2), + /// lhs + ReturnValue(Type2), /// no parameters Return, } @@ -233,7 +235,7 @@ impl From for Data { } } -struct IRBuilder<'tree, 'ir> { +pub struct IRBuilder<'tree, 'ir> { ir: &'ir mut IR, tree: &'tree mut Tree, type_map: HashMap, @@ -297,7 +299,9 @@ impl<'tree, 'ir> IRBuilder<'tree, 'ir> { // TODO: return value of body expression self.tree.st.into_parent(); if value != !0 { - self.ir.push(Inst::ReturnValue, Some(Data::lhs(value))) + let ty = self.tree.type_of_node(*body); + self.ir + .push(Inst::ReturnValue(ty.into()), Some(Data::lhs(value))) } else { !0 } @@ -334,8 +338,10 @@ impl<'tree, 'ir> IRBuilder<'tree, 'ir> { } 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, Some(Data::lhs(expr))) + self.ir + .push(Inst::ReturnValue(ty.into()), Some(Data::lhs(expr))) } else { self.ir.push(Inst::Return, None) } @@ -428,14 +434,10 @@ impl<'tree, 'ir> IRBuilder<'tree, 'ir> { //noop? lhs } else { - let alloc = self.ir.push( - Inst::Alloca, - Some(Data::new(r_ty.size_of(), r_ty.align_of())), - ); - let load = self - .ir - .push(Inst::Load(l_ty.into()), Some(Data::lhs(alloc))); - load + self.ir.push( + Inst::ExplicitCast(l_ty.into(), r_ty.into()), + Some(Data::lhs(lhs)), + ) } } _ => { @@ -446,7 +448,7 @@ impl<'tree, 'ir> IRBuilder<'tree, 'ir> { } } -struct IR { +pub struct IR { nodes: Vec, data: Vec>, } @@ -533,6 +535,9 @@ impl<'tree, 'ir> IRBuilder<'tree, 'ir> { Inst::Negate(ty) => { writeln_indented!(indent, w, "%{} = negate_{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, @@ -553,8 +558,8 @@ impl<'tree, 'ir> IRBuilder<'tree, 'ir> { data.rhs )?; } - Inst::ReturnValue => { - writeln_indented!(indent, w, "%{} = return %{}", node, data.lhs)?; + Inst::ReturnValue(ty) => { + writeln_indented!(indent, w, "%{} = return {ty} %{}", node, data.lhs)?; } Inst::Return => { writeln_indented!(indent, w, "%{} = return", node)?; @@ -1200,7 +1205,8 @@ impl<'a> Assembler<'a> { Inst::BitOr(_) => todo!(), Inst::BitXOr(_) => todo!(), Inst::Negate(_) => todo!(), - Inst::ReturnValue => { + Inst::ExplicitCast(_, _) => todo!(), + Inst::ReturnValue(_) => { let val = data.unwrap().lhs; let (®, _) = registers.iter().find(|(_, node)| node == &&val) .expect(&format!( @@ -1265,14 +1271,14 @@ impl<'a> Assembler<'a> { use crate::mir; -struct MirBuilder<'a> { +pub struct MirBuilder<'a> { ir: IRIter<'a>, - strings: StringTable, - mir: mir::Mir, + pub strings: StringTable, + pub functions: HashMap, } impl<'a> MirBuilder<'a> { - fn new(ir: &'a IR, strings: StringTable) -> MirBuilder<'a> { + pub fn new(ir: &'a IR, strings: StringTable) -> MirBuilder<'a> { Self { ir: IRIter { ir, @@ -1280,12 +1286,12 @@ impl<'a> MirBuilder<'a> { item: None, }, strings, - mir: mir::Mir::new(), + functions: HashMap::new(), } } fn build_function(&mut self, name: StringsIndex) { - let mut mir = mir::Mir::new(); + let mut mir = mir::Mir::new(name); let mut mapping = BTreeMap::::new(); loop { @@ -1298,9 +1304,7 @@ impl<'a> MirBuilder<'a> { self.ir.offset -= 1; break; } - Inst::Label => { - mir.push(mir::Inst::Label, mir::Data::index(data.unwrap().as_index())) - } + Inst::Label => mir.gen_label(data.unwrap().as_index()), Inst::ConstantU32 => mir.push( mir::Inst::ConstantDWord, mir::Data::imm32(data.unwrap().as_u32()), @@ -1375,7 +1379,7 @@ impl<'a> MirBuilder<'a> { let ty = mir::Type::from_bitsize_int(bits as u32); let sum = mir.gen_add(ty, lhs, rhs); - mir.gen_truncate(sum, ty, signed, bits) + mir.gen_truncate_integer(sum, ty, signed, bits) } }, Type2::Binary32 => mir.gen_add(mir::Type::SinglePrecision, lhs, rhs), @@ -1394,7 +1398,7 @@ impl<'a> MirBuilder<'a> { let sum = mir.gen_sub(ty, lhs, rhs); if let Some((signed, bits)) = unalignment { - mir.gen_truncate(sum, ty, signed, bits) + mir.gen_truncate_integer(sum, ty, signed, bits) } else { sum } @@ -1411,7 +1415,7 @@ impl<'a> MirBuilder<'a> { let sum = mir.gen_mul(ty, signed, lhs, rhs); if let Some((signed, bits)) = unalignment { - mir.gen_truncate(sum, ty, signed, bits) + mir.gen_truncate_integer(sum, ty, signed, bits) } else { sum } @@ -1428,7 +1432,7 @@ impl<'a> MirBuilder<'a> { let sum = mir.gen_div(ty, signed, lhs, rhs); if let Some((signed, bits)) = unalignment { - mir.gen_truncate(sum, ty, signed, bits) + mir.gen_truncate_integer(sum, ty, signed, bits) } else { sum } @@ -1445,7 +1449,7 @@ impl<'a> MirBuilder<'a> { let sum = mir.gen_rem(ty, signed, lhs, rhs); if let Some((signed, bits)) = unalignment { - mir.gen_truncate(sum, ty, signed, bits) + mir.gen_truncate_integer(sum, ty, signed, bits) } else { sum } @@ -1467,7 +1471,7 @@ impl<'a> MirBuilder<'a> { let sum = mir.gen_bitand(ty, lhs, rhs); if let Some((signed, bits)) = unalignment { - mir.gen_truncate(sum, ty, signed, bits) + mir.gen_truncate_integer(sum, ty, signed, bits) } else { sum } @@ -1489,7 +1493,7 @@ impl<'a> MirBuilder<'a> { let sum = mir.gen_bitor(ty, lhs, rhs); if let Some((signed, bits)) = unalignment { - mir.gen_truncate(sum, ty, signed, bits) + mir.gen_truncate_integer(sum, ty, signed, bits) } else { sum } @@ -1511,7 +1515,7 @@ impl<'a> MirBuilder<'a> { let sum = mir.gen_bitxor(ty, lhs, rhs); if let Some((signed, bits)) = unalignment { - mir.gen_truncate(sum, ty, signed, bits) + mir.gen_truncate_integer(sum, ty, signed, bits) } else { sum } @@ -1522,6 +1526,7 @@ impl<'a> MirBuilder<'a> { let rhs = *mapping.get(&dst).unwrap(); // 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), @@ -1535,7 +1540,7 @@ impl<'a> MirBuilder<'a> { let ty = mir::Type::from_bitsize_int(bits as u32); let sum = mir.gen_shl(ty, lhs, rhs); - mir.gen_truncate(sum, ty, signed, bits) + mir.gen_truncate_integer(sum, ty, signed, bits) } }, _ => unreachable!(), @@ -1568,7 +1573,7 @@ impl<'a> MirBuilder<'a> { mir.gen_shr(ty, lhs, rhs) }; - mir.gen_truncate(sum, ty, signed, bits) + mir.gen_truncate_integer(sum, ty, signed, bits) } }, _ => unreachable!(), @@ -1584,16 +1589,49 @@ impl<'a> MirBuilder<'a> { let sum = mir.gen_negate(ty, lhs); if let Some((signed, bits)) = unalignment { - mir.gen_truncate(sum, ty, signed, bits) + mir.gen_truncate_integer(sum, ty, signed, bits) } else { sum } } - Inst::ReturnValue => { + 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(&lhs).unwrap(); + + 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(&src).unwrap(); - mir.gen_ret_val(src) + mir.gen_ret_val(ty.mir_type(), src) } Inst::Return => mir.gen_ret(), #[allow(unreachable_patterns)] @@ -1605,14 +1643,10 @@ impl<'a> MirBuilder<'a> { mapping.insert(ir_node, node); } - println!( - "{} mir:\n{}", - self.strings.get_str(name), - mir.display(&self.strings) - ); + self.functions.insert(name, mir); } - fn build(&mut self) { + pub fn build(&mut self) { loop { let Some((inst, data)) = self.ir.next() else { break; @@ -1664,6 +1698,16 @@ fn inverse_sqrt(x: f32) -> f32 { let mut mir = MirBuilder::new(&ir, tree.strings); mir.build(); + + let MirBuilder { + strings, functions, .. + } = mir; + + for (_name, mir) in functions { + let assembly = mir.assemble(&strings).unwrap(); + println!("mir:\n{}", mir.display(&strings)); + println!("assembly:\n{assembly}"); + } } #[test] diff --git a/tests/legal/inverse_sqrt.sea b/tests/legal/inverse_sqrt.sea new file mode 100644 index 0000000..f704fcd --- /dev/null +++ b/tests/legal/inverse_sqrt.sea @@ -0,0 +1,7 @@ +fn inverse_sqrt(n: f32) -> f32 { + let x = n; + var i = *(&x as *i32); + i = 0x5f3759dfi32 - (i >> 1u8); + let y = *(&i as *f32); + y * (1.5f32 - (x * 0.5f32 * y * y)) +} \ No newline at end of file diff --git a/tests/legal/simple_math.sea b/tests/legal/simple_math.sea new file mode 100644 index 0000000..1520bf7 --- /dev/null +++ b/tests/legal/simple_math.sea @@ -0,0 +1,5 @@ +fn main() -> u32 { + let a: u32 = 0u32 + 3u32; + let b = &a; + return *b * 2u32; +} \ No newline at end of file