diff --git a/src/asm/mod.rs b/src/asm/mod.rs index 1ac10f5..c488274 100644 --- a/src/asm/mod.rs +++ b/src/asm/mod.rs @@ -1,8 +1,10 @@ mod write_asm; use crate::ir; +use crate::write_base::*; use core::convert::TryFrom; +use core::fmt; #[derive(Debug, Clone, PartialEq, Eq)] pub struct Todo {} @@ -21,7 +23,7 @@ pub struct TranslationUnit { #[derive(Debug, Clone, PartialEq, Eq)] pub struct Section { - /// Section Headers provice size, offset, type, alignment and flags of the sections + /// Section Headers provide size, offset, type, alignment and flags of the sections /// /// For more details: pub header: Vec, @@ -115,6 +117,24 @@ impl Directive { } } +impl fmt::Display for Directive { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Align(value) => write!(f, ".align\t{}", value), + Self::Globl(label) => write!(f, ".globl\t{}", label), + Self::Type(symbol, symbol_type) => { + write!(f, ".type\t{}, {}", symbol, symbol_type) + } + Self::Section(section_type) => write!(f, ".section\t{}", section_type), + Self::Byte(value) => write!(f, ".byte\t{:#x?}", value), + Self::Half(value) => write!(f, ".half\t{:#x?}", value), + Self::Word(value) => write!(f, ".word\t{:#x?}", value), + Self::Quad(value) => write!(f, ".quad\t{:#x?}", value), + Self::Zero(bytes) => write!(f, ".zero\t{:#x?}", bytes), + } + } +} + #[derive(Debug, Clone, PartialEq, Eq)] pub enum SectionType { Text, @@ -123,12 +143,40 @@ pub enum SectionType { Bss, } +impl fmt::Display for SectionType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "{}", + match self { + Self::Text => ".text", + Self::Data => ".data", + Self::Rodata => ".rodata", + Self::Bss => ".bss", + } + ) + } +} + #[derive(Debug, Clone, PartialEq, Eq)] pub enum SymbolType { Function, Object, } +impl fmt::Display for SymbolType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "{}", + match self { + Self::Function => "@function", + Self::Object => "@object", + } + ) + } +} + #[derive(Debug, Clone, PartialEq, Eq)] pub enum Instruction { /// R-type instruction format @@ -175,6 +223,66 @@ pub enum Instruction { Pseudo(Pseudo), } +impl fmt::Display for Instruction { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::RType { + instr, + rd, + rs1, + rs2, + } => { + let rounding_mode = if let RType::FcvtFloatToInt { .. } = instr { + ",rtz" + } else { + "" + } + .to_string(); + + write!( + f, + "{}\t{},{}{}{}", + instr, + rd, + rs1, + if let Some(rs2) = rs2 { + format!(",{}", rs2) + } else { + "".to_string() + }, + rounding_mode + ) + } + Self::IType { + instr, + rd, + rs1, + imm, + } => { + if let IType::Load { .. } = instr { + write!(f, "{}\t{},{}({})", instr, rd, imm, rs1) + } else { + write!(f, "{}\t{},{},{}", instr, rd, rs1, imm,) + } + } + Self::SType { + instr, + rs1, + rs2, + imm, + } => write!(f, "{}\t{},{}({})", instr, rs2, imm, rs1), + Self::BType { + instr, + rs1, + rs2, + imm, + } => write!(f, "{}\t{},{}, {}", instr, rs1, rs2, imm.0,), + Self::UType { instr, rd, imm } => write!(f, "{}\t{}, {}", instr, rd, imm,), + Self::Pseudo(pseudo) => write!(f, "{}", pseudo), + } + } +} + /// If the enum variant contains `bool`, /// It means that different instructions exist /// depending on whether the operand is signed or not. @@ -405,6 +513,114 @@ impl RType { } } +impl fmt::Display for RType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Add(data_size) => write!(f, "add{}", data_size.word().write_string()), + Self::Sub(data_size) => write!(f, "sub{}", data_size.word().write_string()), + Self::Sll(data_size) => write!(f, "sll{}", data_size.word().write_string()), + Self::Srl(data_size) => write!(f, "srl{}", data_size.word().write_string()), + Self::Sra(data_size) => write!(f, "sra{}", data_size.word().write_string()), + Self::Mul(data_size) => write!(f, "mul{}", data_size.word().write_string()), + Self::Div { + data_size, + is_signed, + } => write!( + f, + "div{}{}", + if *is_signed { "" } else { "u" }, + data_size.word().write_string() + ), + Self::Rem { + data_size, + is_signed, + } => write!( + f, + "rem{}{}", + if *is_signed { "" } else { "u" }, + data_size.word().write_string() + ), + Self::Slt { is_signed } => write!(f, "slt{}", if *is_signed { "" } else { "u" }), + Self::Xor => write!(f, "xor"), + Self::Or => write!(f, "or"), + Self::And => write!(f, "and"), + Self::Fadd(data_size) => write!(f, "fadd.{}", data_size), + Self::Fsub(data_size) => write!(f, "fsub.{}", data_size), + Self::Fmul(data_size) => write!(f, "fmul.{}", data_size), + Self::Fdiv(data_size) => write!(f, "fdiv.{}", data_size), + Self::Feq(data_size) => write!(f, "feq.{}", data_size), + Self::Flt(data_size) => write!(f, "flt.{}", data_size), + Self::FmvFloatToInt { float_data_size } => { + assert!(float_data_size.is_floating_point()); + write!( + f, + "fmv.x.{}", + if *float_data_size == DataSize::SinglePrecision { + "w" + } else { + "d" + } + ) + } + Self::FmvIntToFloat { float_data_size } => { + assert!(float_data_size.is_floating_point()); + write!( + f, + "fmv.{}.x", + if *float_data_size == DataSize::SinglePrecision { + "w" + } else { + "d" + } + ) + } + Self::FcvtFloatToInt { + float_data_size, + int_data_size, + is_signed, + } => { + assert!(float_data_size.is_floating_point()); + assert!(matches!(int_data_size, DataSize::Word | DataSize::Double)); + write!( + f, + "fcvt.{}{}.{}", + if matches!(int_data_size, DataSize::Word) { + "w" + } else { + "l" + }, + if *is_signed { "" } else { "u" }, + float_data_size + ) + } + Self::FcvtIntToFloat { + int_data_size, + float_data_size, + is_signed, + } => { + assert!(float_data_size.is_floating_point()); + assert!(matches!(int_data_size, DataSize::Word | DataSize::Double)); + write!( + f, + "fcvt.{}.{}{}", + float_data_size, + if matches!(int_data_size, DataSize::Word) { + "w" + } else { + "l" + }, + if *is_signed { "" } else { "u" } + ) + } + Self::FcvtFloatToFloat { from, to } => { + assert!(from.is_floating_point()); + assert!(to.is_floating_point()); + write!(f, "fcvt.{}.{}", to, from) + } + } + } +} + #[derive(Debug, Clone, PartialEq, Eq)] pub enum IType { Load { @@ -481,6 +697,39 @@ impl IType { } } +impl fmt::Display for IType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Load { + data_size, + is_signed, + } => { + if data_size.is_integer() { + write!(f, "l{}{}", data_size, if *is_signed { "" } else { "u" }) + } else { + write!( + f, + "fl{}", + if *data_size == DataSize::SinglePrecision { + "w" + } else { + "d" + } + ) + } + } + Self::Addi(data_size) => write!(f, "addi{}", data_size.word().write_string()), + Self::Xori => write!(f, "xori"), + Self::Ori => write!(f, "ori"), + Self::Andi => write!(f, "andi"), + Self::Slli(data_size) => write!(f, "slli{}", data_size.word().write_string()), + Self::Srli(data_size) => write!(f, "srli{}", data_size.word().write_string()), + Self::Srai(data_size) => write!(f, "srai{}", data_size.word().write_string()), + Self::Slti { is_signed } => write!(f, "slti{}", if *is_signed { "" } else { "u" }), + } + } +} + #[derive(Debug, Clone, PartialEq, Eq)] pub enum SType { Store(DataSize), @@ -497,6 +746,28 @@ impl SType { } } +impl fmt::Display for SType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Store(data_size) => { + if data_size.is_integer() { + write!(f, "s{}", data_size) + } else { + write!( + f, + "fs{}", + if *data_size == DataSize::SinglePrecision { + "w" + } else { + "d" + } + ) + } + } + } + } +} + #[derive(Debug, Clone, PartialEq, Eq)] pub enum BType { Beq, @@ -505,11 +776,34 @@ pub enum BType { Bge { is_signed: bool }, } +impl fmt::Display for BType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "{}", + match self { + Self::Beq => "beq".to_string(), + Self::Bne => "bne".to_string(), + Self::Blt { is_signed } => format!("blt{}", if *is_signed { "" } else { "u" }), + Self::Bge { is_signed } => format!("bge{}", if *is_signed { "" } else { "u" }), + } + ) + } +} + #[derive(Debug, Clone, PartialEq, Eq)] pub enum UType { Lui, } +impl fmt::Display for UType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Lui => write!(f, "lui"), + } + } +} + /// The assembler implements a number of convenience psuedo-instructions that are formed from /// instructions in the base ISA, but have implicit arguments or in some case reversed arguments, /// that result in distinct semantics. @@ -583,6 +877,31 @@ impl Pseudo { } } +impl fmt::Display for Pseudo { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::La { rd, symbol } => write!(f, "la\t{},{}", rd, symbol), + Self::Li { rd, imm } => write!(f, "li\t{},{}", rd, *imm as i64), + Self::Mv { rd, rs } => write!(f, "mv\t{},{}", rd, rs), + Self::Fmv { data_size, rd, rs } => write!(f, "fmv.{}\t{},{}", data_size, rd, rs), + Self::Neg { data_size, rd, rs } => { + write!(f, "neg{}\t{},{}", data_size.word().write_string(), rd, rs) + } + Self::SextW { rs, rd } => { + write!(f, "sext.w\t{},{}", rd, rs) + } + Self::Seqz { rd, rs } => write!(f, "seqz\t{},{}", rd, rs), + Self::Snez { rd, rs } => write!(f, "snez\t{},{}", rd, rs), + Self::Fneg { data_size, rd, rs } => write!(f, "fneg.{}\t{},{}", data_size, rd, rs), + Self::J { offset } => write!(f, "j\t{}", offset), + Self::Jr { rs } => write!(f, "jr\t{}", rs), + Self::Jalr { rs } => write!(f, "jalr\t{}", rs), + Self::Ret => write!(f, "ret"), + Self::Call { offset } => write!(f, "call\t{}", offset), + } + } +} + #[derive(Debug, Clone, PartialEq, Eq)] pub enum Immediate { // TODO: consider architecture dependency (current: 64-bit architecture) @@ -600,6 +919,21 @@ impl Immediate { } } +impl fmt::Display for Immediate { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "{}", + match self { + Self::Value(value) => format!("{}", *value as i64), + Self::Relocation { relocation, symbol } => { + format!("{}({})", relocation, symbol) + } + } + ) + } +} + /// The relocation function creates synthesize operand values that are resolved /// at program link time and are used as immediate parameters to specific instructions. /// @@ -612,6 +946,19 @@ pub enum RelocationFunction { Lo12, } +impl fmt::Display for RelocationFunction { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "{}", + match self { + Self::Hi20 => "%hi", + Self::Lo12 => "%lo", + } + ) + } +} + /// `Label` is used as branch, unconditional jump targets and symbol offsets. /// /// For more details: @@ -625,6 +972,12 @@ impl Label { } } +impl fmt::Display for Label { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0) + } +} + #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum DataSize { Byte, @@ -684,6 +1037,23 @@ impl DataSize { } } +impl fmt::Display for DataSize { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "{}", + match self { + Self::Byte => "b", + Self::Half => "h", + Self::Word => "w", + Self::Double => "d", + Self::SinglePrecision => "s", + Self::DoublePrecision => "d", + } + ) + } +} + // TODO: Add calling convention information (caller/callee-save registers) /// ABI name for RISC-V integer and floating-point register /// @@ -789,8 +1159,37 @@ impl Register { } } +impl fmt::Display for Register { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let register = match self { + Self::Zero => "zero".to_string(), + Self::Ra => "ra".to_string(), + Self::Sp => "sp".to_string(), + Self::Gp => "gp".to_string(), + Self::Tp => "tp".to_string(), + Self::Temp(register_type, id) => format!("{}t{}", register_type, id), + Self::Saved(register_type, id) => format!("{}s{}", register_type, id), + Self::Arg(register_type, id) => format!("{}a{}", register_type, id), + }; + write!(f, "{}", register) + } +} + #[derive(Debug, Clone, PartialEq, Eq, Hash, Copy)] pub enum RegisterType { Integer, FloatingPoint, } + +impl fmt::Display for RegisterType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "{}", + match self { + Self::FloatingPoint => "f", + Self::Integer => "", + }, + ) + } +} diff --git a/src/asm/write_asm.rs b/src/asm/write_asm.rs index 4b61735..e73ca8c 100644 --- a/src/asm/write_asm.rs +++ b/src/asm/write_asm.rs @@ -76,418 +76,84 @@ impl WriteLine for Block { impl WriteString for Directive { fn write_string(&self) -> String { - match self { - Self::Align(value) => format!(".align\t{}", value), - Self::Globl(label) => format!(".globl\t{}", label.0), - Self::Type(symbol, symbol_type) => { - format!(".type\t{}, {}", symbol.0, symbol_type.write_string()) - } - Self::Section(section_type) => format!(".section\t{}", section_type.write_string()), - Self::Byte(value) => format!(".byte\t{:#x?}", value), - Self::Half(value) => format!(".half\t{:#x?}", value), - Self::Word(value) => format!(".word\t{:#x?}", value), - Self::Quad(value) => format!(".quad\t{:#x?}", value), - Self::Zero(bytes) => format!(".zero\t{:#x?}", bytes), - } + format!("{}", self) } } impl WriteString for SectionType { fn write_string(&self) -> String { - match self { - Self::Text => ".text", - Self::Data => ".data", - Self::Rodata => ".rodata", - Self::Bss => ".bss", - } - .to_string() + format!("{}", self) } } impl WriteString for SymbolType { fn write_string(&self) -> String { - match self { - Self::Function => "@function", - Self::Object => "@object", - } - .to_string() + format!("{}", self) } } impl WriteString for Instruction { fn write_string(&self) -> String { - match self { - Self::RType { - instr, - rd, - rs1, - rs2, - } => { - let rounding_mode = if let RType::FcvtFloatToInt { .. } = instr { - ",rtz" - } else { - "" - } - .to_string(); - - format!( - "{}\t{},{}{}{}", - instr.write_string(), - rd.write_string(), - rs1.write_string(), - if let Some(rs2) = rs2 { - format!(",{}", rs2.write_string()) - } else { - "".to_string() - }, - rounding_mode - ) - } - Self::IType { - instr, - rd, - rs1, - imm, - } => { - if let IType::Load { .. } = instr { - format!( - "{}\t{},{}({})", - instr.write_string(), - rd.write_string(), - imm.write_string(), - rs1.write_string() - ) - } else { - format!( - "{}\t{},{},{}", - instr.write_string(), - rd.write_string(), - rs1.write_string(), - imm.write_string(), - ) - } - } - Self::SType { - instr, - rs1, - rs2, - imm, - } => format!( - "{}\t{},{}({})", - instr.write_string(), - rs2.write_string(), - imm.write_string(), - rs1.write_string() - ), - Self::BType { - instr, - rs1, - rs2, - imm, - } => format!( - "{}\t{},{}, {}", - instr.write_string(), - rs1.write_string(), - rs2.write_string(), - imm.0, - ), - Self::UType { instr, rd, imm } => format!( - "{}\t{}, {}", - instr.write_string(), - rd.write_string(), - imm.write_string(), - ), - Self::Pseudo(pseudo) => pseudo.write_string(), - } + format!("{}", self) } } impl WriteString for RType { fn write_string(&self) -> String { - match self { - Self::Add(data_size) => format!("add{}", data_size.word().write_string()), - Self::Sub(data_size) => format!("sub{}", data_size.word().write_string()), - Self::Sll(data_size) => format!("sll{}", data_size.word().write_string()), - Self::Srl(data_size) => format!("srl{}", data_size.word().write_string()), - Self::Sra(data_size) => format!("sra{}", data_size.word().write_string()), - Self::Mul(data_size) => format!("mul{}", data_size.word().write_string()), - Self::Div { - data_size, - is_signed, - } => format!( - "div{}{}", - if *is_signed { "" } else { "u" }, - data_size.word().write_string() - ), - Self::Rem { - data_size, - is_signed, - } => format!( - "rem{}{}", - if *is_signed { "" } else { "u" }, - data_size.word().write_string() - ), - Self::Slt { is_signed } => format!("slt{}", if *is_signed { "" } else { "u" }), - Self::Xor => "xor".to_string(), - Self::Or => "or".to_string(), - Self::And => "and".to_string(), - Self::Fadd(data_size) => format!("fadd.{}", data_size.write_string()), - Self::Fsub(data_size) => format!("fsub.{}", data_size.write_string()), - Self::Fmul(data_size) => format!("fmul.{}", data_size.write_string()), - Self::Fdiv(data_size) => format!("fdiv.{}", data_size.write_string()), - Self::Feq(data_size) => format!("feq.{}", data_size.write_string()), - Self::Flt(data_size) => format!("flt.{}", data_size.write_string()), - Self::FmvFloatToInt { float_data_size } => { - assert!(float_data_size.is_floating_point()); - format!( - "fmv.x.{}", - if *float_data_size == DataSize::SinglePrecision { - "w" - } else { - "d" - } - ) - } - Self::FmvIntToFloat { float_data_size } => { - assert!(float_data_size.is_floating_point()); - format!( - "fmv.{}.x", - if *float_data_size == DataSize::SinglePrecision { - "w" - } else { - "d" - } - ) - } - Self::FcvtFloatToInt { - float_data_size, - int_data_size, - is_signed, - } => { - assert!(float_data_size.is_floating_point()); - assert!(matches!(int_data_size, DataSize::Word | DataSize::Double)); - format!( - "fcvt.{}{}.{}", - if matches!(int_data_size, DataSize::Word) { - "w" - } else { - "l" - }, - if *is_signed { "" } else { "u" }, - float_data_size.write_string() - ) - } - Self::FcvtIntToFloat { - int_data_size, - float_data_size, - is_signed, - } => { - assert!(float_data_size.is_floating_point()); - assert!(matches!(int_data_size, DataSize::Word | DataSize::Double)); - format!( - "fcvt.{}.{}{}", - float_data_size.write_string(), - if matches!(int_data_size, DataSize::Word) { - "w" - } else { - "l" - }, - if *is_signed { "" } else { "u" } - ) - } - Self::FcvtFloatToFloat { from, to } => { - assert!(from.is_floating_point()); - assert!(to.is_floating_point()); - format!("fcvt.{}.{}", to.write_string(), from.write_string()) - } - } + format!("{}", self) } } impl WriteString for IType { fn write_string(&self) -> String { - match self { - Self::Load { - data_size, - is_signed, - } => { - if data_size.is_integer() { - format!( - "l{}{}", - data_size.write_string(), - if *is_signed { "" } else { "u" } - ) - } else { - format!( - "fl{}", - if *data_size == DataSize::SinglePrecision { - "w" - } else { - "d" - } - ) - } - } - Self::Addi(data_size) => format!("addi{}", data_size.word().write_string()), - Self::Xori => "xori".to_string(), - Self::Ori => "ori".to_string(), - Self::Andi => "andi".to_string(), - Self::Slli(data_size) => format!("slli{}", data_size.word().write_string()), - Self::Srli(data_size) => format!("srli{}", data_size.word().write_string()), - Self::Srai(data_size) => format!("srai{}", data_size.word().write_string()), - Self::Slti { is_signed } => format!("slti{}", if *is_signed { "" } else { "u" }), - } + format!("{}", self) } } impl WriteString for SType { fn write_string(&self) -> String { - match self { - Self::Store(data_size) => { - if data_size.is_integer() { - format!("s{}", data_size.write_string()) - } else { - format!( - "fs{}", - if *data_size == DataSize::SinglePrecision { - "w" - } else { - "d" - } - ) - } - } - } + format!("{}", self) } } impl WriteString for BType { fn write_string(&self) -> String { - match self { - Self::Beq => "beq".to_string(), - Self::Bne => "bne".to_string(), - Self::Blt { is_signed } => format!("blt{}", if *is_signed { "" } else { "u" }), - Self::Bge { is_signed } => format!("bge{}", if *is_signed { "" } else { "u" }), - } + format!("{}", self) } } impl WriteString for UType { fn write_string(&self) -> String { - match self { - Self::Lui => "lui".to_string(), - } + format!("{}", self) } } impl WriteString for Pseudo { fn write_string(&self) -> String { - match self { - Self::La { rd, symbol } => format!("la\t{},{}", rd.write_string(), symbol.0), - Self::Li { rd, imm } => format!("li\t{},{}", rd.write_string(), *imm as i64), - Self::Mv { rd, rs } => format!("mv\t{},{}", rd.write_string(), rs.write_string()), - Self::Fmv { data_size, rd, rs } => format!( - "fmv.{}\t{},{}", - data_size.write_string(), - rd.write_string(), - rs.write_string() - ), - Self::Neg { data_size, rd, rs } => format!( - "neg{}\t{},{}", - data_size.word().write_string(), - rd.write_string(), - rs.write_string() - ), - Self::SextW { rs, rd } => { - format!("sext.w\t{},{}", rd.write_string(), rs.write_string()) - } - Self::Seqz { rd, rs } => format!("seqz\t{},{}", rd.write_string(), rs.write_string()), - Self::Snez { rd, rs } => format!("snez\t{},{}", rd.write_string(), rs.write_string()), - Self::Fneg { data_size, rd, rs } => format!( - "fneg.{}\t{},{}", - data_size.write_string(), - rd.write_string(), - rs.write_string() - ), - Self::J { offset } => format!("j\t{}", offset.0), - Self::Jr { rs } => format!("jr\t{}", rs.write_string()), - Self::Jalr { rs } => format!("jalr\t{}", rs.write_string()), - Self::Ret => "ret".to_string(), - Self::Call { offset } => format!("call\t{}", offset.0), - } + format!("{}", self) } } impl WriteString for Immediate { fn write_string(&self) -> String { - match self { - Self::Value(value) => format!("{}", *value as i64), - Self::Relocation { relocation, symbol } => { - format!("{}({})", relocation.write_string(), symbol.0) - } - } + format!("{}", self) } } impl WriteString for RelocationFunction { fn write_string(&self) -> String { - match self { - Self::Hi20 => "%hi", - Self::Lo12 => "%lo", - } - .to_string() + format!("{}", self) } } impl WriteString for DataSize { fn write_string(&self) -> String { - match self { - Self::Byte => "b", - Self::Half => "h", - Self::Word => "w", - Self::Double => "d", - Self::SinglePrecision => "s", - Self::DoublePrecision => "d", - } - .to_string() + format!("{}", self) } } impl WriteString for Register { fn write_string(&self) -> String { - match self { - Self::Zero => "zero".to_string(), - Self::Ra => "ra".to_string(), - Self::Sp => "sp".to_string(), - Self::Gp => "gp".to_string(), - Self::Tp => "tp".to_string(), - Self::Temp(registr_type, id) => format!( - "{}t{}", - if *registr_type == RegisterType::FloatingPoint { - "f" - } else { - "" - }, - id - ), - Self::Saved(registr_type, id) => format!( - "{}s{}", - if *registr_type == RegisterType::FloatingPoint { - "f" - } else { - "" - }, - id - ), - Self::Arg(registr_type, id) => format!( - "{}a{}", - if *registr_type == RegisterType::FloatingPoint { - "f" - } else { - "" - }, - id - ), - } + format!("{}", self) } } diff --git a/src/ir/dtype.rs b/src/ir/dtype.rs index a83861d..2e51ed4 100644 --- a/src/ir/dtype.rs +++ b/src/ir/dtype.rs @@ -1366,21 +1366,17 @@ impl fmt::Display for Dtype { .. } => { let fields = if let Some(fields) = fields { - let fields = fields - .iter() - .map(|f| { - format!( - "{}:{}", - if let Some(name) = f.name() { - name - } else { - "%anon" - }, - f.deref() - ) - }) - .collect::>() - .join(", "); + let fields = fields.iter().format_with(", ", |field, f| { + f(&format_args!( + "{}:{}", + if let Some(name) = field.name() { + name + } else { + "%anon" + }, + field.deref() + )) + }); format!(":<{}>", fields) } else { "".to_string() @@ -1393,16 +1389,9 @@ impl fmt::Display for Dtype { fields ) } - Self::Function { ret, params } => write!( - f, - "[ret:{} params:({})]", - ret, - params - .iter() - .map(|p| p.to_string()) - .collect::>() - .join(", ") - ), + Self::Function { ret, params } => { + write!(f, "[ret:{} params:({})]", ret, params.iter().format(", ")) + } Self::Typedef { name, is_const } => { write!(f, "{}{}", if *is_const { "const " } else { "" }, name) } diff --git a/src/ir/mod.rs b/src/ir/mod.rs index 518e275..c0dd9e0 100644 --- a/src/ir/mod.rs +++ b/src/ir/mod.rs @@ -12,11 +12,13 @@ use core::convert::TryFrom; use core::fmt; use core::ops::{Deref, DerefMut}; use hexf_parse::{parse_hexf32, parse_hexf64}; +use itertools::Itertools; use lang_c::ast; use ordered_float::OrderedFloat; use std::collections::{BTreeMap, HashMap}; use std::hash::{Hash, Hasher}; +use crate::write_base::*; pub use dtype::{Dtype, DtypeError, HasDtype}; pub use interp::{interp, Value}; pub use parse::Parse; @@ -257,6 +259,96 @@ impl Instruction { } } +impl WriteOp for ast::BinaryOperator { + fn write_operation(&self) -> String { + match self { + Self::Multiply => "mul", + Self::Divide => "div", + Self::Modulo => "mod", + Self::Plus => "add", + Self::Minus => "sub", + Self::ShiftLeft => "shl", + Self::ShiftRight => "shr", + Self::Equals => "cmp eq", + Self::NotEquals => "cmp ne", + Self::Less => "cmp lt", + Self::LessOrEqual => "cmp le", + Self::Greater => "cmp gt", + Self::GreaterOrEqual => "cmp ge", + Self::BitwiseAnd => "and", + Self::BitwiseXor => "xor", + Self::BitwiseOr => "or", + _ => todo!( + "ast::BinaryOperator::WriteOp: write operation for {:?} is needed", + self + ), + } + .to_string() + } +} + +impl WriteOp for ast::UnaryOperator { + fn write_operation(&self) -> String { + match self { + Self::Plus => "plus", + Self::Minus => "minus", + Self::Negate => "negate", + _ => todo!( + "ast::UnaryOperator::WriteOp: write operation for {:?} is needed", + self + ), + } + .to_string() + } +} + +impl fmt::Display for Instruction { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Instruction::Nop => write!(f, "nop"), + Instruction::BinOp { op, lhs, rhs, .. } => { + write!( + f, + "{} {} {}", + op.write_operation(), + lhs.write_string(), + rhs.write_string() + ) + } + Instruction::UnaryOp { op, operand, .. } => { + write!(f, "{} {}", op.write_operation(), operand.write_string()) + } + Instruction::Store { ptr, value } => { + write!(f, "store {} {}", value.write_string(), ptr.write_string()) + } + Instruction::Load { ptr } => write!(f, "load {}", ptr.write_string()), + Instruction::Call { callee, args, .. } => { + write!( + f, + "call {}({})", + callee.write_string(), + args.iter().format_with(", ", |operand, f| f(&format_args!( + "{}", + operand.write_string() + ))) + ) + } + Instruction::TypeCast { + value, + target_dtype, + } => write!(f, "typecast {} to {}", value.write_string(), target_dtype), + Instruction::GetElementPtr { ptr, offset, .. } => { + write!( + f, + "getelementptr {} offset {}", + ptr.write_string(), + offset.write_string() + ) + } + } + } +} + #[derive(Debug, Clone, PartialEq)] pub enum BlockExit { Jump { @@ -302,6 +394,43 @@ impl BlockExit { } } +impl fmt::Display for BlockExit { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + BlockExit::Jump { arg } => write!(f, "j {}", arg), + BlockExit::ConditionalJump { + condition, + arg_then, + arg_else, + } => write!( + f, + "br {}, {}, {}", + condition.write_string(), + arg_then, + arg_else + ), + BlockExit::Switch { + value, + default, + cases, + } => write!( + f, + "switch {} default {} [\n{}\n ]", + value.write_string(), + default, + cases.iter().format_with("\n", |(v, b), f| f(&format_args!( + " {}:{} {}", + v, + v.dtype(), + b + ))) + ), + BlockExit::Return { value } => write!(f, "ret {}", value.write_string()), + BlockExit::Unreachable => write!(f, "\t\t\t\t; error state"), + } + } +} + #[derive(Debug, Clone, PartialEq)] pub struct JumpArg { pub bid: BlockId, @@ -322,9 +451,7 @@ impl fmt::Display for JumpArg { self.bid, self.args .iter() - .map(|a| format!("{}:{}", a, a.dtype())) - .collect::>() - .join(", ") + .format_with(", ", |a, f| f(&format_args!("{}:{}", a, a.dtype()))) ) } } @@ -880,3 +1007,9 @@ impl Named { self.name.as_ref() } } + +impl fmt::Display for Named { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.inner) + } +} diff --git a/src/ir/write_ir.rs b/src/ir/write_ir.rs index 454d075..91ff463 100644 --- a/src/ir/write_ir.rs +++ b/src/ir/write_ir.rs @@ -5,8 +5,6 @@ use std::io::{Result, Write}; use crate::write_base::*; use crate::*; -use lang_c::ast; - impl WriteLine for TranslationUnit { fn write_line(&self, indent: usize, write: &mut dyn Write) -> Result<()> { // TODO: consider KECC IR parser in the future. @@ -18,21 +16,17 @@ impl WriteLine for TranslationUnit { .as_ref() .expect("`fields` must be `Some`"); - let fields = fields - .iter() - .map(|f| { - format!( - "{}:{}", - if let Some(name) = f.name() { - name - } else { - "%anon" - }, - f.deref() - ) - }) - .collect::>() - .join(", "); + let fields = fields.iter().format_with(", ", |field, f| { + f(&format_args!( + "{}:{}", + if let Some(name) = field.name() { + name + } else { + "%anon" + }, + field.deref() + )) + }); format!("{{ {} }}", fields) } else { @@ -80,12 +74,7 @@ impl WriteLine for (&String, &Declaration) { signature, definition, } => { - let params = signature - .params - .iter() - .map(|p| p.to_string()) - .collect::>() - .join(", "); + let params = signature.params.iter().format(", "); if let Some(definition) = definition.as_ref() { // print function definition @@ -99,7 +88,7 @@ impl WriteLine for (&String, &Declaration) { .allocations .iter() .enumerate() - .map(|(i, a)| format!( + .format_with("\n", |(i, a), f| f(&format_args!( " %l{}:{}{}", i, a.deref(), @@ -108,9 +97,7 @@ impl WriteLine for (&String, &Declaration) { } else { "".into() } - )) - .collect::>() - .join("\n") + ))) )?; for (id, block) in &definition.blocks { @@ -173,39 +160,7 @@ impl WriteLine for (&BlockId, &Block) { impl WriteString for Instruction { fn write_string(&self) -> String { - match self { - Instruction::Nop => "nop".into(), - Instruction::BinOp { op, lhs, rhs, .. } => format!( - "{} {} {}", - op.write_operation(), - lhs.write_string(), - rhs.write_string() - ), - Instruction::UnaryOp { op, operand, .. } => { - format!("{} {}", op.write_operation(), operand.write_string(),) - } - Instruction::Store { ptr, value } => { - format!("store {} {}", value.write_string(), ptr.write_string()) - } - Instruction::Load { ptr } => format!("load {}", ptr.write_string()), - Instruction::Call { callee, args, .. } => format!( - "call {}({})", - callee.write_string(), - args.iter() - .map(WriteString::write_string) - .collect::>() - .join(", ") - ), - Instruction::TypeCast { - value, - target_dtype, - } => format!("typecast {} to {}", value.write_string(), target_dtype), - Instruction::GetElementPtr { ptr, offset, .. } => format!( - "getelementptr {} offset {}", - ptr.write_string(), - offset.write_string() - ), - } + format!("{}", self) } } @@ -215,79 +170,8 @@ impl WriteString for Operand { } } -impl WriteOp for ast::BinaryOperator { - fn write_operation(&self) -> String { - match self { - Self::Multiply => "mul", - Self::Divide => "div", - Self::Modulo => "mod", - Self::Plus => "add", - Self::Minus => "sub", - Self::ShiftLeft => "shl", - Self::ShiftRight => "shr", - Self::Equals => "cmp eq", - Self::NotEquals => "cmp ne", - Self::Less => "cmp lt", - Self::LessOrEqual => "cmp le", - Self::Greater => "cmp gt", - Self::GreaterOrEqual => "cmp ge", - Self::BitwiseAnd => "and", - Self::BitwiseXor => "xor", - Self::BitwiseOr => "or", - _ => todo!( - "ast::BinaryOperator::WriteOp: write operation for {:?} is needed", - self - ), - } - .to_string() - } -} - -impl WriteOp for ast::UnaryOperator { - fn write_operation(&self) -> String { - match self { - Self::Plus => "plus", - Self::Minus => "minus", - Self::Negate => "negate", - _ => todo!( - "ast::UnaryOperator::WriteOp: write operation for {:?} is needed", - self - ), - } - .to_string() - } -} - impl WriteString for BlockExit { fn write_string(&self) -> String { - match self { - BlockExit::Jump { arg } => format!("j {}", arg), - BlockExit::ConditionalJump { - condition, - arg_then, - arg_else, - } => format!( - "br {}, {}, {}", - condition.write_string(), - arg_then, - arg_else - ), - BlockExit::Switch { - value, - default, - cases, - } => format!( - "switch {} default {} [\n{}\n ]", - value.write_string(), - default, - cases - .iter() - .map(|(v, b)| format!(" {}:{} {}", v, v.dtype(), b)) - .collect::>() - .join("\n") - ), - BlockExit::Return { value } => format!("ret {}", value.write_string()), - BlockExit::Unreachable => "\t\t\t\t; error state".to_string(), - } + format!("{}", self) } }