diff --git a/examples/asm/alignof.c b/examples/asm/alignof.c deleted file mode 100644 index 5e36459..0000000 --- a/examples/asm/alignof.c +++ /dev/null @@ -1,3 +0,0 @@ -int main() { - return _Alignof(const int) == 4; -} diff --git a/examples/asm/bar.c b/examples/asm/bar.c deleted file mode 100644 index 4cd8b0b..0000000 --- a/examples/asm/bar.c +++ /dev/null @@ -1,10 +0,0 @@ -int bar(int x, int y, int z){ - int arith_mean = (x + y + z) / 3; - int ugly_mean = (((x + y) / 2) * 2 + z) / 3; - if (x == y) { return y; } - else { return z; } -} - -int main() { - return 1; -} diff --git a/examples/asm/cmp.c b/examples/asm/cmp.c deleted file mode 100644 index 4d68ddf..0000000 --- a/examples/asm/cmp.c +++ /dev/null @@ -1,18 +0,0 @@ -int int_greater_than(int i, unsigned int j) { - if (i > j) return 1; - else return 0; -} - -int char_greater_than(char i, unsigned char j) { - if (i > j) return 1; - else return 0; -} - -int main() { - // cmp ugt - int r1 = int_greater_than(-1, 1); - // cmp sgt - int r2 = char_greater_than(-1, 1); - - return r1 == 1 && r2 == 0; -} diff --git a/examples/asm/fibonacci.c b/examples/asm/fibonacci.c deleted file mode 100644 index a65fe3a..0000000 --- a/examples/asm/fibonacci.c +++ /dev/null @@ -1,11 +0,0 @@ -int fibonacci(int n) { - if (n < 2) { - return n; - } - - return fibonacci(n - 2) + fibonacci(n - 1); -} - -int main() { - return fibonacci(9) == 34; -} diff --git a/examples/asm/simple.c b/examples/asm/simple.c deleted file mode 100644 index 29fc1ed..0000000 --- a/examples/asm/simple.c +++ /dev/null @@ -1,5 +0,0 @@ -int main() -{ - int x = 1; - return 1; -} diff --git a/src/asm/mod.rs b/src/asm/mod.rs index 96701c6..67fb735 100644 --- a/src/asm/mod.rs +++ b/src/asm/mod.rs @@ -49,7 +49,14 @@ impl Function { #[derive(Debug, Clone, PartialEq)] pub struct Variable { - todo: TODO, + pub label: Label, + pub directives: Vec, +} + +impl Variable { + pub fn new(label: Label, directives: Vec) -> Self { + Self { label, directives } + } } #[derive(Debug, Clone, PartialEq)] @@ -74,8 +81,39 @@ impl Block { pub enum Directive { /// .globl symbol Globl(Label), + /// .section section_type + Section(SectionType), /// .type symbol, symbol_type Type(Label, SymbolType), + /// .byte value + Byte(u8), + /// .half value + Half(u16), + /// .word value + Word(u32), + /// .quad value + Quad(u64), +} + +impl Directive { + pub fn try_from_data_size(data_size: DataSize, value: u64) -> Self { + match data_size { + DataSize::Byte => Self::Byte(value as u8), + DataSize::Half => Self::Half(value as u16), + DataSize::Word => Self::Word(value as u32), + DataSize::Double => Self::Quad(value), + DataSize::SinglePrecision => Self::Word(value as u32), + DataSize::DoublePrecision => Self::Quad(value), + } + } +} + +#[derive(Debug, Clone, PartialEq)] +pub enum SectionType { + Text, + Data, + Rodata, + Bss, } #[derive(Debug, Clone, PartialEq)] @@ -92,7 +130,7 @@ pub enum Instruction { instr: RType, rd: Register, rs1: Register, - rs2: Register, + rs2: Option, }, /// I-type instruction format /// https://riscv.org/specifications/isa-spec-pdf/ (16p, 129p) @@ -100,7 +138,7 @@ pub enum Instruction { instr: IType, rd: Register, rs1: Register, - imm: isize, + imm: Immediate, }, /// S-type instruction format /// https://riscv.org/specifications/isa-spec-pdf/ (16p, 129p) @@ -108,7 +146,7 @@ pub enum Instruction { instr: SType, rs1: Register, rs2: Register, - imm: isize, + imm: Immediate, }, /// B-type instruction format /// https://riscv.org/specifications/isa-spec-pdf/ (16p, 129p) @@ -118,6 +156,11 @@ pub enum Instruction { rs2: Register, imm: Label, }, + UType { + instr: UType, + rd: Register, + imm: Immediate, + }, Pseudo(Pseudo), } @@ -136,59 +179,226 @@ pub enum Instruction { pub enum RType { Add(Option), Sub(Option), + Sll(Option), + Srl(Option), + Sra(Option), Mul(Option), - Div(Option, bool), - Slt(bool), + Div { + data_size: Option, + is_signed: bool, + }, + Rem { + data_size: Option, + is_signed: bool, + }, + Slt { + is_signed: bool, + }, Xor, + Or, + And, + Fadd(DataSize), + Fsub(DataSize), + Fmul(DataSize), + Fdiv(DataSize), + Feq(DataSize), + Flt(DataSize), + /// fmv.w.x or fmv.d.x + FmvIntToFloat { + float_data_size: DataSize, + }, + /// fmv.x.w or fmv.x.w + FmvFloatToInt { + float_data_size: DataSize, + }, + /// fcvt.s.l(u) or fcvt.d.l(u) + /// fcvt.s.w(u) or fcvt.d.w(u) + FcvtIntToFloat { + int_data_size: Option, + float_data_size: DataSize, + is_signed: bool, + }, + /// fcvt.l(u).s or fcvt.l(u).d + /// fcvt.w(u).s or fcvt.w(u).d + FcvtFloatToInt { + float_data_size: DataSize, + int_data_size: Option, + is_signed: bool, + }, + /// fcvt.s.d or fcvt.d.s + FcvtFloatToFloat { + from: DataSize, + to: DataSize, + }, } impl RType { pub fn add(dtype: ir::Dtype) -> Self { let data_size = DataSize::try_from(dtype).expect("`data_size` must be derived from `dtype`"); - let data_size = if data_size == DataSize::Word { - Some(data_size) - } else { - None - }; + assert!(data_size.is_integer()); - Self::Add(data_size) + Self::Add(data_size.word()) } pub fn sub(dtype: ir::Dtype) -> Self { let data_size = DataSize::try_from(dtype).expect("`data_size` must be derived from `dtype`"); - let data_size = if data_size == DataSize::Word { - Some(data_size) - } else { - None - }; + assert!(data_size.is_integer()); - Self::Sub(data_size) + Self::Sub(data_size.word()) + } + + pub fn sll(dtype: ir::Dtype) -> Self { + let data_size = + DataSize::try_from(dtype).expect("`data_size` must be derived from `dtype`"); + assert!(data_size.is_integer()); + + Self::Sll(data_size.word()) + } + + pub fn srl(dtype: ir::Dtype) -> Self { + let data_size = + DataSize::try_from(dtype).expect("`data_size` must be derived from `dtype`"); + assert!(data_size.is_integer()); + + Self::Srl(data_size.word()) + } + + pub fn sra(dtype: ir::Dtype) -> Self { + let data_size = + DataSize::try_from(dtype).expect("`data_size` must be derived from `dtype`"); + assert!(data_size.is_integer()); + + Self::Sra(data_size.word()) } pub fn mul(dtype: ir::Dtype) -> Self { let data_size = DataSize::try_from(dtype).expect("`data_size` must be derived from `dtype`"); - let data_size = if data_size == DataSize::Word { - Some(data_size) - } else { - None - }; + assert!(data_size.is_integer()); - Self::Mul(data_size) + Self::Mul(data_size.word()) } pub fn div(dtype: ir::Dtype, is_signed: bool) -> Self { let data_size = DataSize::try_from(dtype).expect("`data_size` must be derived from `dtype`"); - let data_size = if data_size == DataSize::Word { - Some(data_size) - } else { - None - }; + assert!(data_size.is_integer()); - Self::Div(data_size, is_signed) + Self::Div { + data_size: data_size.word(), + is_signed, + } + } + + pub fn rem(dtype: ir::Dtype, is_signed: bool) -> Self { + let data_size = + DataSize::try_from(dtype).expect("`data_size` must be derived from `dtype`"); + assert!(data_size.is_integer()); + + Self::Rem { + data_size: data_size.word(), + is_signed, + } + } + + pub fn fadd(dtype: ir::Dtype) -> Self { + let data_size = + DataSize::try_from(dtype).expect("`data_size` must be derived from `dtype`"); + assert!(data_size.is_floating_point()); + + Self::Fadd(data_size) + } + + pub fn fsub(dtype: ir::Dtype) -> Self { + let data_size = + DataSize::try_from(dtype).expect("`data_size` must be derived from `dtype`"); + assert!(data_size.is_floating_point()); + + Self::Fsub(data_size) + } + + pub fn fmul(dtype: ir::Dtype) -> Self { + let data_size = + DataSize::try_from(dtype).expect("`data_size` must be derived from `dtype`"); + assert!(data_size.is_floating_point()); + + Self::Fmul(data_size) + } + + pub fn fdiv(dtype: ir::Dtype) -> Self { + let data_size = + DataSize::try_from(dtype).expect("`data_size` must be derived from `dtype`"); + assert!(data_size.is_floating_point()); + + Self::Fdiv(data_size) + } + + pub fn feq(dtype: ir::Dtype) -> Self { + let data_size = + DataSize::try_from(dtype).expect("`data_size` must be derived from `dtype`"); + assert!(data_size.is_floating_point()); + + Self::Feq(data_size) + } + + pub fn flt(dtype: ir::Dtype) -> Self { + let data_size = + DataSize::try_from(dtype).expect("`data_size` must be derived from `dtype`"); + assert!(data_size.is_floating_point()); + + Self::Flt(data_size) + } + + pub fn fmv_int_to_float(dtype: ir::Dtype) -> Self { + let float_data_size = + DataSize::try_from(dtype).expect("`data_size` must be derived from `dtype`"); + assert!(float_data_size.is_floating_point()); + + Self::FmvIntToFloat { float_data_size } + } + + pub fn fmv_float_to_int(dtype: ir::Dtype) -> Self { + let float_data_size = + DataSize::try_from(dtype).expect("`data_size` must be derived from `dtype`"); + assert!(float_data_size.is_floating_point()); + + Self::FmvFloatToInt { float_data_size } + } + + pub fn fcvt_int_to_float(from: ir::Dtype, to: ir::Dtype) -> Self { + let is_signed = from.is_int_signed(); + let int_data_size = + DataSize::try_from(from).expect("`data_size` must be derived from `dtype`"); + assert!(int_data_size.is_integer()); + + let float_data_size = + DataSize::try_from(to).expect("`data_size` must be derived from `dtype`"); + assert!(float_data_size.is_floating_point()); + + Self::FcvtIntToFloat { + int_data_size: int_data_size.word(), + float_data_size, + is_signed, + } + } + + pub fn fcvt_float_to_int(from: ir::Dtype, to: ir::Dtype) -> Self { + let float_data_size = + DataSize::try_from(from).expect("`data_size` must be derived from `dtype`"); + assert!(float_data_size.is_floating_point()); + + let is_signed = to.is_int_signed(); + let int_data_size = + DataSize::try_from(to).expect("`data_size` must be derived from `dtype`"); + assert!(int_data_size.is_integer()); + + Self::FcvtFloatToInt { + float_data_size, + int_data_size: int_data_size.word(), + is_signed, + } } } @@ -201,22 +411,49 @@ impl RType { /// https://riscv.org/specifications/isa-spec-pdf/ (35p) #[derive(Debug, Clone, PartialEq)] pub enum IType { - Load(DataSize), + Load { + data_size: DataSize, + is_signed: bool, + }, Addi(Option), + Xori, + Ori, Andi, Slli, Srli, } impl IType { - pub const LW: Self = Self::Load(DataSize::Word); - pub const LD: Self = Self::Load(DataSize::Double); + pub const LW: Self = Self::Load { + data_size: DataSize::Word, + is_signed: true, + }; + pub const LD: Self = Self::Load { + data_size: DataSize::Double, + is_signed: true, + }; pub const ADDI: Self = Self::Addi(None); pub fn load(dtype: ir::Dtype) -> Self { let data_size = - DataSize::try_from(dtype).expect("`data_size` must be derived from `dtype`"); - Self::Load(data_size) + DataSize::try_from(dtype.clone()).expect("`data_size` must be derived from `dtype`"); + + let is_signed = if dtype.get_int_width().is_some() { + dtype.is_int_signed() + } else { + false + }; + + let is_signed = if data_size == DataSize::Double { + true + } else { + is_signed + }; + + Self::Load { + data_size, + is_signed, + } } } @@ -240,35 +477,54 @@ impl SType { pub enum BType { Beq, Bne, - Blt(bool), - Bge(bool), + Blt { is_signed: bool }, + Bge { is_signed: bool }, +} + +#[derive(Debug, Clone, PartialEq)] +pub enum UType { + 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. /// https://github.com/rv8-io/rv8-io.github.io/blob/master/asm.md#assembler-pseudo-instructions -/// https://content.riscv.org/wp-content/uploads/2017/05/riscv-spec-v2.2.pdf (110p) +/// https://riscv.org/specifications/isa-spec-pdf/ (139p) #[derive(Debug, Clone, PartialEq)] pub enum Pseudo { /// li rd, immediate - Li { rd: Register, imm: isize }, + Li { + rd: Register, + // TODO: consider architecture dependency (current: 64-bit architecture) + imm: u64, + }, /// mv rd, rs Mv { rd: Register, rs: Register }, /// neg(w) rd, rs Neg { data_size: Option, - rs: Register, rd: Register, + rs: Register, }, /// sext.w rd, rs SextW { rd: Register, rs: Register }, /// seqz rd, rs Seqz { rd: Register, rs: Register }, + /// snez rd, rs + Snez { rd: Register, rs: Register }, + /// fneg.s rd, rs or fneg.d rd, rs + Fneg { + data_size: DataSize, + rd: Register, + rs: Register, + }, /// j offset J { offset: Label }, /// jr rs Jr { rs: Register }, + /// jalr rs + Jalr { rs: Register }, /// ret Ret, /// call offset @@ -276,7 +532,7 @@ pub enum Pseudo { } impl Pseudo { - pub fn neg(dtype: ir::Dtype, rs: Register, rd: Register) -> Self { + pub fn neg(dtype: ir::Dtype, rd: Register, rs: Register) -> Self { let data_size = DataSize::try_from(dtype).expect("`data_size` must be derived from `dtype`"); let data_size = if data_size == DataSize::Word { @@ -285,8 +541,44 @@ impl Pseudo { None }; - Self::Neg { data_size, rs, rd } + Self::Neg { data_size, rd, rs } } + + pub fn fneg(dtype: ir::Dtype, rd: Register, rs: Register) -> Self { + let data_size = + DataSize::try_from(dtype).expect("`data_size` must be derived from `dtype`"); + assert!(data_size.is_floating_point()); + + Self::Fneg { data_size, rd, rs } + } +} + +#[derive(Debug, Clone, PartialEq)] +pub enum Immediate { + // TODO: consider architecture dependency (current: 64-bit architecture) + Value(u64), + /// %hi(symbol) or %lo(symbol) + Relocation { + relocation: RelocationFunction, + symbol: Label, + }, +} + +impl Immediate { + pub fn relocation(relocation: RelocationFunction, symbol: Label) -> Self { + Self::Relocation { 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. +/// https://github.com/riscv/riscv-asm-manual/blob/master/riscv-asm.md +#[derive(Debug, Clone, PartialEq)] +pub enum RelocationFunction { + /// %hi + HI20, + /// %lo + LO12, } /// `Label` is used as branch, unconditional jump targets and symbol offsets. @@ -307,23 +599,34 @@ pub enum DataSize { Half, Word, Double, + SinglePrecision, + DoublePrecision, } impl TryFrom for DataSize { type Error = (); fn try_from(dtype: ir::Dtype) -> Result { - let width = match dtype { - ir::Dtype::Int { width, .. } => width, - _ => todo!(), + let (size, is_float) = match dtype { + ir::Dtype::Int { width, .. } => { + let size = (width - 1) / ir::Dtype::BITS_OF_BYTE + 1; + (size, false) + } + ir::Dtype::Float { width, .. } => { + let size = (width - 1) / ir::Dtype::BITS_OF_BYTE + 1; + (size, true) + } + ir::Dtype::Pointer { .. } => (ir::Dtype::SIZE_OF_POINTER, false), + _ => todo!("DataSize::try_from: support dtype: {:?}", dtype), }; - let size = (width - 1) / ir::Dtype::BITS_OF_BYTE + 1; - let align = match size { - ir::Dtype::SIZE_OF_CHAR => Self::Byte, - ir::Dtype::SIZE_OF_SHORT => Self::Half, - ir::Dtype::SIZE_OF_INT => Self::Word, - ir::Dtype::SIZE_OF_LONG => Self::Double, + let align = match (size, is_float) { + (ir::Dtype::SIZE_OF_CHAR, false) => Self::Byte, + (ir::Dtype::SIZE_OF_SHORT, false) => Self::Half, + (ir::Dtype::SIZE_OF_INT, false) => Self::Word, + (ir::Dtype::SIZE_OF_LONG, false) => Self::Double, + (ir::Dtype::SIZE_OF_FLOAT, true) => Self::SinglePrecision, + (ir::Dtype::SIZE_OF_DOUBLE, true) => Self::DoublePrecision, _ => panic!("there is no other possible case"), }; @@ -331,6 +634,30 @@ impl TryFrom for DataSize { } } +impl DataSize { + pub fn is_integer(&self) -> bool { + match self { + Self::Byte | Self::Half | Self::Word | Self::Double => true, + _ => false, + } + } + + pub fn is_floating_point(&self) -> bool { + match self { + Self::SinglePrecision | Self::DoublePrecision => true, + _ => false, + } + } + + fn word(self) -> Option { + if self == DataSize::Word { + Some(self) + } else { + None + } + } +} + // TODO: Add calling convention information (caller/callee-save registers) /// ABI name for RISC-V integer and floating-point register /// https://content.riscv.org/wp-content/uploads/2017/05/riscv-spec-v2.2.pdf (155p) @@ -342,56 +669,101 @@ pub enum Register { Gp, Tp, /// E.g., t0 - Temp(usize), + Temp(RegisterType, usize), /// E.g., s0 - Saved(usize), + Saved(RegisterType, usize), /// E.g., a0 - Arg(usize), + Arg(RegisterType, usize), } impl Register { - pub const T0: Self = Self::Temp(0); - pub const T1: Self = Self::Temp(1); - pub const T2: Self = Self::Temp(2); - pub const T3: Self = Self::Temp(3); - pub const T4: Self = Self::Temp(4); - pub const T5: Self = Self::Temp(5); - pub const T6: Self = Self::Temp(6); + pub const T0: Self = Self::Temp(RegisterType::Integer, 0); + pub const T1: Self = Self::Temp(RegisterType::Integer, 1); + pub const T2: Self = Self::Temp(RegisterType::Integer, 2); + pub const T3: Self = Self::Temp(RegisterType::Integer, 3); + pub const T4: Self = Self::Temp(RegisterType::Integer, 4); + pub const T5: Self = Self::Temp(RegisterType::Integer, 5); + pub const T6: Self = Self::Temp(RegisterType::Integer, 6); - pub const S0: Self = Self::Saved(0); - pub const S1: Self = Self::Saved(1); - pub const S2: Self = Self::Saved(2); - pub const S3: Self = Self::Saved(3); - pub const S4: Self = Self::Saved(4); - pub const S5: Self = Self::Saved(5); - pub const S6: Self = Self::Saved(6); - pub const S7: Self = Self::Saved(7); - pub const S8: Self = Self::Saved(8); - pub const S9: Self = Self::Saved(9); - pub const S10: Self = Self::Saved(10); - pub const S11: Self = Self::Saved(11); + pub const S0: Self = Self::Saved(RegisterType::Integer, 0); + pub const S1: Self = Self::Saved(RegisterType::Integer, 1); + pub const S2: Self = Self::Saved(RegisterType::Integer, 2); + pub const S3: Self = Self::Saved(RegisterType::Integer, 3); + pub const S4: Self = Self::Saved(RegisterType::Integer, 4); + pub const S5: Self = Self::Saved(RegisterType::Integer, 5); + pub const S6: Self = Self::Saved(RegisterType::Integer, 6); + pub const S7: Self = Self::Saved(RegisterType::Integer, 7); + pub const S8: Self = Self::Saved(RegisterType::Integer, 8); + pub const S9: Self = Self::Saved(RegisterType::Integer, 9); + pub const S10: Self = Self::Saved(RegisterType::Integer, 10); + pub const S11: Self = Self::Saved(RegisterType::Integer, 11); - pub const A0: Self = Self::Arg(0); - pub const A1: Self = Self::Arg(1); - pub const A2: Self = Self::Arg(2); - pub const A3: Self = Self::Arg(3); - pub const A4: Self = Self::Arg(4); - pub const A5: Self = Self::Arg(5); - pub const A6: Self = Self::Arg(6); - pub const A7: Self = Self::Arg(7); + pub const A0: Self = Self::Arg(RegisterType::Integer, 0); + pub const A1: Self = Self::Arg(RegisterType::Integer, 1); + pub const A2: Self = Self::Arg(RegisterType::Integer, 2); + pub const A3: Self = Self::Arg(RegisterType::Integer, 3); + pub const A4: Self = Self::Arg(RegisterType::Integer, 4); + pub const A5: Self = Self::Arg(RegisterType::Integer, 5); + pub const A6: Self = Self::Arg(RegisterType::Integer, 6); + pub const A7: Self = Self::Arg(RegisterType::Integer, 7); - pub fn temp(id: usize) -> Self { - assert!(id <= 6); - Self::Temp(id) + pub const FT0: Self = Self::Temp(RegisterType::FloatingPoint, 0); + pub const FT1: Self = Self::Temp(RegisterType::FloatingPoint, 1); + pub const FT2: Self = Self::Temp(RegisterType::FloatingPoint, 2); + pub const FT3: Self = Self::Temp(RegisterType::FloatingPoint, 3); + pub const FT4: Self = Self::Temp(RegisterType::FloatingPoint, 4); + pub const FT5: Self = Self::Temp(RegisterType::FloatingPoint, 5); + pub const FT6: Self = Self::Temp(RegisterType::FloatingPoint, 6); + pub const FT7: Self = Self::Temp(RegisterType::FloatingPoint, 7); + pub const FT8: Self = Self::Temp(RegisterType::FloatingPoint, 8); + pub const FT9: Self = Self::Temp(RegisterType::FloatingPoint, 9); + pub const FT10: Self = Self::Temp(RegisterType::FloatingPoint, 10); + pub const FT11: Self = Self::Temp(RegisterType::FloatingPoint, 11); + + pub const FS0: Self = Self::Saved(RegisterType::FloatingPoint, 0); + pub const FS1: Self = Self::Saved(RegisterType::FloatingPoint, 1); + pub const FS2: Self = Self::Saved(RegisterType::FloatingPoint, 2); + pub const FS3: Self = Self::Saved(RegisterType::FloatingPoint, 3); + pub const FS4: Self = Self::Saved(RegisterType::FloatingPoint, 4); + pub const FS5: Self = Self::Saved(RegisterType::FloatingPoint, 5); + pub const FS6: Self = Self::Saved(RegisterType::FloatingPoint, 6); + pub const FS7: Self = Self::Saved(RegisterType::FloatingPoint, 7); + pub const FS8: Self = Self::Saved(RegisterType::FloatingPoint, 8); + pub const FS9: Self = Self::Saved(RegisterType::FloatingPoint, 9); + pub const FS10: Self = Self::Saved(RegisterType::FloatingPoint, 10); + pub const FS11: Self = Self::Saved(RegisterType::FloatingPoint, 11); + + pub const FA0: Self = Self::Arg(RegisterType::FloatingPoint, 0); + pub const FA1: Self = Self::Arg(RegisterType::FloatingPoint, 1); + pub const FA2: Self = Self::Arg(RegisterType::FloatingPoint, 2); + pub const FA3: Self = Self::Arg(RegisterType::FloatingPoint, 3); + pub const FA4: Self = Self::Arg(RegisterType::FloatingPoint, 4); + pub const FA5: Self = Self::Arg(RegisterType::FloatingPoint, 5); + pub const FA6: Self = Self::Arg(RegisterType::FloatingPoint, 6); + pub const FA7: Self = Self::Arg(RegisterType::FloatingPoint, 7); + + pub fn temp(register_type: RegisterType, id: usize) -> Self { + match register_type { + RegisterType::Integer => assert!(id <= 6), + RegisterType::FloatingPoint => assert!(id <= 11), + } + + Self::Temp(register_type, id) } - pub fn saved(id: usize) -> Self { + pub fn saved(register_type: RegisterType, id: usize) -> Self { assert!(id <= 11); - Self::Saved(id) + Self::Saved(register_type, id) } - pub fn arg(id: usize) -> Self { + pub fn arg(register_type: RegisterType, id: usize) -> Self { assert!(id <= 7); - Self::Arg(id) + Self::Arg(register_type, id) } } + +#[derive(Debug, Clone, PartialEq, Eq, Hash, Copy)] +pub enum RegisterType { + Integer, + FloatingPoint, +} diff --git a/src/asm/write_asm.rs b/src/asm/write_asm.rs index 68f42c2..4e97fde 100644 --- a/src/asm/write_asm.rs +++ b/src/asm/write_asm.rs @@ -48,8 +48,14 @@ impl WriteLine for Function { } impl WriteLine for Variable { - fn write_line(&self, _indent: usize, _write: &mut dyn Write) -> Result<()> { - todo!() + fn write_line(&self, indent: usize, write: &mut dyn Write) -> Result<()> { + writeln!(write, "{}:", self.label.0)?; + for directive in &self.directives { + write_indent(indent + INDENT, write)?; + writeln!(write, "{}", directive.write_string())?; + } + + Ok(()) } } @@ -75,10 +81,27 @@ impl WriteString for Directive { 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), } } } +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() + } +} + impl WriteString for SymbolType { fn write_string(&self) -> String { match self { @@ -97,25 +120,39 @@ impl WriteString for Instruction { rd, rs1, rs2, - } => format!( - "{}\t{},{},{}", - instr.write_string(), - rd.write_string(), - rs1.write_string(), - rs2.write_string() - ), + } => { + 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 { + if let IType::Load { .. } = instr { format!( "{}\t{},{}({})", instr.write_string(), rd.write_string(), - imm, + imm.write_string(), rs1.write_string() ) } else { @@ -124,7 +161,7 @@ impl WriteString for Instruction { instr.write_string(), rd.write_string(), rs1.write_string(), - imm + imm.write_string(), ) } } @@ -137,7 +174,7 @@ impl WriteString for Instruction { "{}\t{},{}({})", instr.write_string(), rs2.write_string(), - imm.to_string(), + imm.write_string(), rs1.write_string() ), Self::BType { @@ -152,6 +189,12 @@ impl WriteString for Instruction { 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(), } } @@ -162,14 +205,101 @@ impl WriteString for RType { match self { Self::Add(data_size) => format!("add{}", data_size.write_string()), Self::Sub(data_size) => format!("sub{}", data_size.write_string()), + Self::Sll(data_size) => format!("sll{}", data_size.write_string()), + Self::Srl(data_size) => format!("srl{}", data_size.write_string()), + Self::Sra(data_size) => format!("srl{}", data_size.write_string()), Self::Mul(data_size) => format!("mul{}", data_size.write_string()), - Self::Div(data_size, is_signed) => format!( + Self::Div { + data_size, + is_signed, + } => format!( "div{}{}", if *is_signed { "" } else { "u" }, data_size.write_string() ), - Self::Slt(is_signed) => format!("slt{}", if *is_signed { "" } else { "u" }), + Self::Rem { + data_size, + is_signed, + } => format!( + "rem{}{}", + if *is_signed { "" } else { "u" }, + data_size.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()); + format!( + "fcvt.{}{}.{}", + if let Some(int_data_size) = int_data_size { + assert_eq!(*int_data_size, DataSize::Word); + "w" + } else { + "l" + } + .to_string(), + 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()); + format!( + "fcvt.{}.{}{}", + float_data_size.write_string(), + if let Some(int_data_size) = int_data_size { + assert_eq!(*int_data_size, DataSize::Word); + "w" + } else { + "l" + } + .to_string(), + 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()) + } } } } @@ -177,8 +307,30 @@ impl WriteString for RType { impl WriteString for IType { fn write_string(&self) -> String { match self { - Self::Load(data_size) => format!("l{}", data_size.write_string()), + 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.write_string()), + Self::Xori => "xori".to_string(), + Self::Ori => "ori".to_string(), Self::Andi => "andi".to_string(), Self::Slli => "slli".to_string(), Self::Srli => "srli".to_string(), @@ -189,7 +341,20 @@ impl WriteString for IType { impl WriteString for SType { fn write_string(&self) -> String { match self { - Self::Store(data_size) => format!("s{}", data_size.write_string()), + 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" + } + ) + } + } } } } @@ -199,8 +364,16 @@ impl WriteString for BType { 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" }), + Self::Blt { is_signed } => format!("blt{}", if *is_signed { "" } else { "u" }), + Self::Bge { is_signed } => format!("bge{}", if *is_signed { "" } else { "u" }), + } + } +} + +impl WriteString for UType { + fn write_string(&self) -> String { + match self { + Self::Lui => "lui".to_string(), } } } @@ -208,9 +381,9 @@ impl WriteString for BType { impl WriteString for Pseudo { fn write_string(&self) -> String { match self { - Self::Li { rd, imm } => format!("li\t{},{}", rd.write_string(), imm), - Self::Mv { rs, rd } => format!("mv\t{},{}", rd.write_string(), rs.write_string()), - Self::Neg { data_size, rs, rd } => format!( + Self::Li { rd, imm } => format!("li\t{},{:#x?}", rd.write_string(), imm), + Self::Mv { rd, rs } => format!("mv\t{},{}", rd.write_string(), rs.write_string()), + Self::Neg { data_size, rd, rs } => format!( "neg{}\t{},{}", data_size.write_string(), rd.write_string(), @@ -219,15 +392,44 @@ impl WriteString for Pseudo { Self::SextW { rs, rd } => { format!("sext.w\t{},{}", rd.write_string(), rs.write_string()) } - Self::Seqz { rs, rd } => format!("seqz\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), } } } +impl WriteString for Immediate { + fn write_string(&self) -> String { + match self { + Self::Value(value) => format!("{:#x?}", value), + Self::Relocation { relocation, symbol } => { + format!("{}({})", relocation.write_string(), symbol.0) + } + } + } +} + +impl WriteString for RelocationFunction { + fn write_string(&self) -> String { + match self { + Self::HI20 => "%hi", + Self::LO12 => "%lo", + } + .to_string() + } +} + impl WriteString for DataSize { fn write_string(&self) -> String { match self { @@ -235,6 +437,8 @@ impl WriteString for DataSize { Self::Half => "h", Self::Word => "w", Self::Double => "d", + Self::SinglePrecision => "s", + Self::DoublePrecision => "d", } .to_string() } @@ -248,9 +452,33 @@ impl WriteString for Register { Self::Sp => "sp".to_string(), Self::Gp => "gp".to_string(), Self::Tp => "tp".to_string(), - Self::Temp(id) => format!("t{}", id), - Self::Saved(id) => format!("s{}", id), - Self::Arg(id) => format!("a{}", id), + 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 + ), } } } diff --git a/src/ir/dtype.rs b/src/ir/dtype.rs index 4c16bff..bbef9b7 100644 --- a/src/ir/dtype.rs +++ b/src/ir/dtype.rs @@ -466,7 +466,7 @@ impl TryFrom<&ast::ParameterDeclaration> for Dtype { impl Dtype { pub const BITS_OF_BYTE: usize = 8; pub const SIZE_OF_BYTE: usize = 1; - // TODO: consider architecture dependency in the future + // TODO: consider architecture dependency (current: 64-bit architecture) pub const SIZE_OF_POINTER: usize = 8; pub const SIZE_OF_CHAR: usize = 1; diff --git a/src/ir/interp.rs b/src/ir/interp.rs index 4b18f1d..f8a4ad6 100644 --- a/src/ir/interp.rs +++ b/src/ir/interp.rs @@ -807,23 +807,6 @@ mod calculator { (value, dtype) => todo!("calculate_typecast ({:?}) {:?}", value, dtype), } } - - #[inline] - fn sign_extension(value: u128, width: u128) -> u128 { - let base = 1u128 << (width - 1); - if value >= base { - let bit_mask = -1i128 << (width as i128); - value | bit_mask as u128 - } else { - value - } - } - - #[inline] - fn trim_unnecessary_bits(value: u128, width: u128) -> u128 { - let bit_mask = (1u128 << width) - 1; - value & bit_mask - } } // TODO: delete `allow(dead_code)` diff --git a/src/ir/mod.rs b/src/ir/mod.rs index 959b461..65b8fb5 100644 --- a/src/ir/mod.rs +++ b/src/ir/mod.rs @@ -719,6 +719,80 @@ impl Constant { false } } + + pub fn typecast(self, target_dtype: Dtype) -> Self { + if self.dtype() == target_dtype { + return self; + } + + match (&self, &target_dtype) { + ( + Constant::Int { value, width, .. }, + Dtype::Int { + width: target_width, + is_signed: target_signed, + .. + }, + ) => { + let result = if *target_signed { + if *width >= *target_width { + let value = trim_unnecessary_bits(*value, *target_width as u128); + sign_extension(value, *target_width as u128) + } else { + *value + } + } else { + trim_unnecessary_bits(*value, *target_width as u128) + }; + + Constant::int(result, target_dtype) + } + ( + Constant::Int { + value, is_signed, .. + }, + Dtype::Float { .. }, + ) => { + let casted_value = if *is_signed { + *value as i128 as f64 + } else { + *value as f64 + }; + + Constant::float(casted_value, target_dtype) + } + (Constant::Float { value, .. }, Dtype::Int { is_signed, .. }) => { + let casted_value = if *is_signed { + value.into_inner() as i128 as u128 + } else { + value.into_inner() as u128 + }; + + Constant::int(casted_value, target_dtype) + } + (Constant::Float { value, .. }, Dtype::Float { .. }) => { + Constant::float(value.into_inner(), target_dtype) + } + _ => todo!("typecast ({:?}) {:?}", self, target_dtype), + } + } +} + +#[inline] +pub fn sign_extension(value: u128, width: u128) -> u128 { + let base = 1u128 << (width - 1); + if value >= base { + let bit_mask = -1i128 << (width as i128); + value | bit_mask as u128 + } else { + value + } +} + +#[inline] +pub fn trim_unnecessary_bits(value: u128, width: u128) -> u128 { + let bit_mask = (1u128 << width) - 1; + value & bit_mask } impl fmt::Display for Constant { diff --git a/src/tests.rs b/src/tests.rs index f479ac0..387182b 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -222,6 +222,17 @@ pub fn test_opt, P2: AsRef, O: Optimize