diff --git a/dora-asm/src/lib.rs b/dora-asm/src/lib.rs index 699d32db..33f5813a 100644 --- a/dora-asm/src/lib.rs +++ b/dora-asm/src/lib.rs @@ -1,6 +1,7 @@ use byteorder::{LittleEndian, WriteBytesExt}; pub mod arm64; +pub mod rv64; pub mod x64; use std::convert::TryInto; diff --git a/dora-asm/src/rv64.rs b/dora-asm/src/rv64.rs new file mode 100644 index 00000000..2cd2a123 --- /dev/null +++ b/dora-asm/src/rv64.rs @@ -0,0 +1,1391 @@ +use crate::{AssemblerBuffer, Label}; + +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub struct Register(u8); + +impl Register { + pub fn new(value: u8) -> Register { + assert!(value < 31); + Register(value) + } + + fn rd(self) -> u32 { + (self.0 as u32) << 7 + } + + fn rs1(self) -> u32 { + (self.0 as u32) << 15 + } + + fn rs2(self) -> u32 { + (self.0 as u32) << 20 + } + + fn rs3(self) -> u32 { + (self.0 as u32) << 27 + } +} + +pub const R0: Register = Register(0); +pub const R1: Register = Register(1); +pub const R2: Register = Register(2); +pub const R3: Register = Register(3); +pub const R4: Register = Register(4); +pub const R5: Register = Register(5); +pub const R6: Register = Register(6); +pub const R7: Register = Register(7); +pub const R8: Register = Register(8); +pub const R9: Register = Register(9); +pub const R10: Register = Register(10); +pub const R11: Register = Register(11); +pub const R12: Register = Register(12); +pub const R13: Register = Register(13); +pub const R14: Register = Register(14); +pub const R15: Register = Register(15); +pub const R16: Register = Register(16); +pub const R17: Register = Register(17); +pub const R18: Register = Register(18); +pub const R19: Register = Register(19); +pub const R20: Register = Register(20); +pub const R21: Register = Register(21); +pub const R22: Register = Register(22); +pub const R23: Register = Register(23); +pub const R24: Register = Register(24); +pub const R25: Register = Register(25); +pub const R26: Register = Register(26); +pub const R27: Register = Register(27); +pub const R28: Register = Register(28); +pub const R29: Register = Register(29); +pub const R30: Register = Register(30); +pub const R31: Register = Register(31); +pub const REG_ZERO: Register = Register(0); +pub const REG_SP: Register = Register(2); + +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub struct FloatRegister(u8); + +impl FloatRegister { + pub fn new(value: u8) -> FloatRegister { + assert!(value < 32); + FloatRegister(value) + } + + fn encoding(self) -> u32 { + self.0 as u32 + } + + fn rd(self) -> u32 { + (self.0 as u32) << 7 + } + + fn rs1(self) -> u32 { + (self.0 as u32) << 15 + } + + fn rs2(self) -> u32 { + (self.0 as u32) << 20 + } + + fn rs3(self) -> u32 { + (self.0 as u32) << 27 + } +} + +pub const F0: FloatRegister = FloatRegister(0); +pub const F1: FloatRegister = FloatRegister(1); +pub const F2: FloatRegister = FloatRegister(2); +pub const F3: FloatRegister = FloatRegister(3); +pub const F4: FloatRegister = FloatRegister(4); +pub const F5: FloatRegister = FloatRegister(5); +pub const F6: FloatRegister = FloatRegister(6); +pub const F7: FloatRegister = FloatRegister(7); +pub const F8: FloatRegister = FloatRegister(8); +pub const F9: FloatRegister = FloatRegister(9); +pub const F10: FloatRegister = FloatRegister(10); +pub const F11: FloatRegister = FloatRegister(11); +pub const F12: FloatRegister = FloatRegister(12); +pub const F13: FloatRegister = FloatRegister(13); +pub const F14: FloatRegister = FloatRegister(14); +pub const F15: FloatRegister = FloatRegister(15); +pub const F16: FloatRegister = FloatRegister(16); +pub const F17: FloatRegister = FloatRegister(17); +pub const F18: FloatRegister = FloatRegister(18); +pub const F19: FloatRegister = FloatRegister(19); +pub const F20: FloatRegister = FloatRegister(20); +pub const F21: FloatRegister = FloatRegister(21); +pub const F22: FloatRegister = FloatRegister(22); +pub const F23: FloatRegister = FloatRegister(23); +pub const F24: FloatRegister = FloatRegister(24); +pub const F25: FloatRegister = FloatRegister(25); +pub const F26: FloatRegister = FloatRegister(26); +pub const F27: FloatRegister = FloatRegister(27); +pub const F28: FloatRegister = FloatRegister(28); +pub const F29: FloatRegister = FloatRegister(29); +pub const F30: FloatRegister = FloatRegister(30); +pub const F31: FloatRegister = FloatRegister(31); + +struct ForwardJump { + offset: u32, + label: Label, + kind: JumpKind, +} + +enum JumpKind { + Unconditional, + Conditional, /*(Cond)*/ + NonZero(bool, Register), + Zero(bool, Register), +} + +pub struct AssemblerRv64 { + unresolved_jumps: Vec, + buffer: AssemblerBuffer, +} + +impl AssemblerRv64 { + pub fn new() -> AssemblerRv64 { + AssemblerRv64 { + unresolved_jumps: Vec::new(), + buffer: AssemblerBuffer::new(), + } + } + + pub fn create_label(&mut self) -> Label { + self.buffer.create_label() + } + + pub fn create_and_bind_label(&mut self) -> Label { + self.buffer.create_and_bind_label() + } + + pub fn bind_label(&mut self, lbl: Label) { + self.buffer.bind_label(lbl); + } + + fn offset(&self, lbl: Label) -> Option { + self.buffer.offset(lbl) + } + + pub fn finalize(mut self, alignment: Option) -> Vec { + //self.resolve_jumps(); + if let Some(alignment) = alignment { + self.align_to(alignment); + } + self.buffer.code + } + + fn align_to(&mut self, alignment: usize) { + while self.buffer.code.len() % alignment != 0 { + //self.brk(0); + } + assert_eq!(self.buffer.code.len() % alignment, 0); + } + + pub fn position(&self) -> usize { + self.buffer.position() + } + + pub fn set_position(&mut self, pos: usize) { + self.buffer.set_position(pos); + } + + pub fn set_position_end(&mut self) { + self.buffer.set_position_end(); + } + + pub fn emit_u8(&mut self, value: u8) { + self.buffer.emit_u8(value); + } + + pub fn emit_u32(&mut self, value: u32) { + self.buffer.emit_u32(value); + } + + pub fn emit_u64(&mut self, value: u64) { + self.buffer.emit_u64(value); + } +} + +/** see unpriv-isa-asciidoc.html#opcodemap */ +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +#[repr(u8)] +enum Opcode { + Load = 0b0000011, + LoadFP = 0b0000111, + Custom0 = 0b0001011, + MiscMem = 0b0001111, + OpImm = 0b0010011, + AUIPC = 0b0010111, + OpImm32 = 0b0011011, + Store = 0b0100011, + StoreFp = 0b0100111, + Custom1 = 0b0101011, + AMO = 0b0101111, + Op = 0b0110011, + LUI = 0b0110111, + Op32 = 0b0111011, + MAdd = 0b1000011, + MSub = 0b1000111, + NMSub = 0b1001011, + NMAdd = 0b1001111, + OpFP = 0b1010011, + Reserved0 = 0b1010111, + RV128_0 = 0b1011011, + Branch = 0b1100011, + JALR = 0b1100111, + Reserved1 = 0b1101011, + JAL = 0b1101111, + System = 0b1110011, + Reserved2 = 0b1110111, + RV128_1 = 0b1111011, +} + +impl Opcode { + fn encoding(self: Opcode) -> u32 { + self as u32 + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +#[repr(u8)] +pub enum MemoryAccess { + None = 0b0000, + W = 0b0001, + R = 0b0010, + RW = 0b0011, + O = 0b0100, + I = 0b1000, + IORW = 0b1111, +} + +impl MemoryAccess { + pub fn pred(self) -> i32 { + (self as i32) << 20 + } + pub fn succ(self) -> i32 { + (self as i32) << 16 + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +#[repr(u8)] +pub enum MemoryOrdering { + None = 0b00, + Release = 0b01, + Acquire = 0b10, + AcquireRelease = 0b11, +} + +impl MemoryOrdering { + pub fn encoding(self) -> u32 { + (self as u32) << 25 + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +#[repr(u8)] +pub enum FloatFormat { + S = 0b00, + D = 0b01, + H = 0b10, + Q = 0b11, +} + +impl FloatFormat { + pub fn encoding(self) -> u32 { + (self as u32) << 26 + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +#[repr(u8)] +pub enum RoundingMode { + RNE = 0b000, + RTZ = 0b001, + RDN = 0b010, + RUP = 0b011, + RMM = 0b100, +} + +impl RoundingMode { + pub fn value(self) -> u32 { + self as u32 + } + pub fn encoding(self) -> u32 { + (self as u32) << 12 + } +} + +use crate::rv64::Opcode::*; + +impl AssemblerRv64 { + fn r( + &mut self, + major_opcode: Opcode, + funct3: u32, + funct7: u32, + rd: Register, + rs1: Register, + rs2: Register, + ) { + let instruction: u32 = (funct7 << 25) + | rs2.rs2() + | rs1.rs1() + | (funct3 << 12) + | rd.rd() + | major_opcode.encoding(); + self.emit_u32(instruction); + } + + fn r_aqrl( + &mut self, + funct3: u32, + funct5: u32, + mem_order: MemoryOrdering, + rd: Register, + rs1: Register, + rs2: Register, + ) { + let instruction = (funct5 << 27) + | mem_order.encoding() + | rs2.rs2() + | rs1.rs1() + | (funct3 << 12) + | rd.rd() + | AMO.encoding(); + self.emit_u32(instruction); + } + + fn r4rm( + &mut self, + major_opcode: Opcode, + fmt: FloatFormat, + rm: RoundingMode, + rd: Register, + rs1: Register, + rs2: Register, + rs3: Register, + ) { + let instruction: u32 = rs3.rs3() + | fmt.encoding() + | rs2.rs2() + | rs1.rs1() + | rm.encoding() + | rd.rd() + | major_opcode.encoding(); + self.emit_u32(instruction); + } + + fn r_fp( + &mut self, + funct3: u32, + funct7: u32, + rd: FloatRegister, + rs1: FloatRegister, + rs2: FloatRegister, + ) { + let instruction: u32 = + (funct7 << 25) | rs2.rs2() | rs1.rs1() | (funct3 << 12) | rd.rd() | OpFP.encoding(); + self.emit_u32(instruction); + } + + fn r_fp_to_int( + &mut self, + funct3: u32, + funct7: u32, + rd: Register, + rs1: FloatRegister, + rs2: FloatRegister, + ) { + let instruction: u32 = + (funct7 << 25) | rs2.rs2() | rs1.rs1() | (funct3 << 12) | rd.rd() | OpFP.encoding(); + self.emit_u32(instruction); + } + + fn r_int_to_fp( + &mut self, + funct3: u32, + funct7: u32, + rd: FloatRegister, + rs1: Register, + rs2: Register, + ) { + let instruction: u32 = + (funct7 << 25) | rs2.rs2() | rs1.rs1() | (funct3 << 12) | rd.rd() | OpFP.encoding(); + self.emit_u32(instruction); + } + + fn i(&mut self, major_opcode: Opcode, funct3: u32, rd: Register, rs1: Register, imm12: i32) { + let instruction = + ((imm12 as u32) << 20) | rs1.rs1() | (funct3 << 12) | rd.rd() | major_opcode.encoding(); + self.emit_u32(instruction); + } + fn s(&mut self, major_opcode: Opcode, funct3: u32, rs1: Register, rs2: Register, imm12: i32) { + let imm12 = imm12 as u32; + let imm7 = (imm12 & 0b111111100000) << 20; + let imm5 = (imm12 & 0b11111) << 7; + let instruction = + imm7 | rs2.rs2() | rs1.rs1() | (funct3 << 12) | imm5 | major_opcode.encoding(); + self.emit_u32(instruction); + } + fn b(&mut self, funct3: u32, rs1: Register, rs2: Register, imm12: i32) { + /** immediate is a signed offset in multiples of 2 bytes, so bit 0 is always 0 */ + let imm12 = imm12 as u32; + let imm7_1 = (imm12 & 0b1000000000000) << 19; + let imm7_2 = (imm12 & 0b0011111100000) << 20; + let imm5_1 = (imm12 & 0b0000000011110) << 7; + let imm5_2 = (imm12 & 0b0100000000000) >> 4; + let instruction = imm7_1 + | imm7_2 + | rs2.rs2() + | rs1.rs1() + | (funct3 << 12) + | imm5_1 + | imm5_2 + | Store.encoding(); + self.emit_u32(instruction); + } + fn u(&mut self, major_opcode: Opcode, rd: Register, imm20: i32) { + let instruction = ((imm20 as u32) & 0b11111111111111111111000000000000) + | rd.rd() + | major_opcode.encoding(); + self.emit_u32(instruction); + } + fn j(&mut self, rd: Register, imm20: i32) { + /** immediate is a signed offset in multiples of 2 bytes, so bit 0 is always 0 */ + let imm20_1 = (imm20 as u32 & 0b100000000000000000000) << 11; + let imm20_2 = (imm20 as u32 & 0b000000000011111111110) << 20; + let imm20_3 = (imm20 as u32 & 0b000000000100000000000) << 9; + let imm20_4 = (imm20 as u32 & 0b011111111000000000000); + let instruction = imm20_1 | imm20_2 | imm20_3 | imm20_4 | rd.rd() | JAL.encoding(); + self.emit_u32(instruction); + } +} + +impl AssemblerRv64 { + // ▼▼ RV32I Base Instruction Set ▼▼ + pub fn lui(&mut self, rd: Register, imm: i32) { + self.u(LUI, rd, imm) + } + pub fn auipc(&mut self, rd: Register, imm: i32) { + self.u(AUIPC, rd, imm) + } + pub fn jal(&mut self, rd: Register, imm: i32) { + self.j(rd, imm) + } + pub fn jalr(&mut self, rd: Register, rs1: Register, imm: i32) { + self.i(JALR, 0b000, rd, rs1, imm) + } + pub fn beq(&mut self, rs1: Register, rs2: Register, imm: i32) { + self.b(0b000, rs1, rs2, imm) + } + pub fn bne(&mut self, rs1: Register, rs2: Register, imm: i32) { + self.b(0b001, rs1, rs2, imm) + } + pub fn blt(&mut self, rs1: Register, rs2: Register, imm: i32) { + self.b(0b100, rs1, rs2, imm) + } + pub fn bge(&mut self, rs1: Register, rs2: Register, imm: i32) { + self.b(0b101, rs1, rs2, imm) + } + pub fn bltu(&mut self, rs1: Register, rs2: Register, imm: i32) { + self.b(0b110, rs1, rs2, imm) + } + pub fn bgeu(&mut self, rs1: Register, rs2: Register, imm: i32) { + self.b(0b111, rs1, rs2, imm) + } + pub fn lb(&mut self, rd: Register, rs1: Register, imm: i32) { + self.i(Load, 0b000, rd, rs1, imm) + } + pub fn lh(&mut self, rd: Register, rs1: Register, imm: i32) { + self.i(Load, 0b001, rd, rs1, imm) + } + pub fn lw(&mut self, rd: Register, rs1: Register, imm: i32) { + self.i(Load, 0b010, rd, rs1, imm) + } + pub fn lbu(&mut self, rd: Register, rs1: Register, imm: i32) { + self.i(Load, 0b100, rd, rs1, imm) + } + pub fn lhu(&mut self, rd: Register, rs1: Register, imm: i32) { + self.i(Load, 0b101, rd, rs1, imm) + } + pub fn sb(&mut self, rs1: Register, rs2: Register, imm: i32) { + self.s(Store, 0b000, rs1, rs2, imm) + } + pub fn sh(&mut self, rs1: Register, rs2: Register, imm: i32) { + self.s(Store, 0b001, rs1, rs2, imm) + } + pub fn sw(&mut self, rs1: Register, rs2: Register, imm: i32) { + self.s(Store, 0b010, rs1, rs2, imm) + } + pub fn addi(&mut self, rd: Register, rs1: Register, imm: i32) { + self.i(OpImm, 0b000, rd, rs1, imm) + } + /** pseudo-instruction */ + pub fn mv(&mut self, rd: Register, rs1: Register) { + self.addi(rd, rs1, 0) + } + pub fn slti(&mut self, rd: Register, rs1: Register, imm: i32) { + self.i(OpImm, 0b010, rd, rs1, imm) + } + pub fn sltiu(&mut self, rd: Register, rs1: Register, imm: i32) { + self.i(OpImm, 0b011, rd, rs1, imm) + } + /** pseudo-instruction */ + pub fn seqz(&mut self, rd: Register, rs1: Register) { + self.sltiu(rd, rs1, 1) + } + pub fn xori(&mut self, rd: Register, rs1: Register, imm: i32) { + self.i(OpImm, 0b100, rd, rs1, imm) + } + /** pseudo-instruction */ + pub fn not(&mut self, rd: Register, rs1: Register) { + self.xori(rd, rs1, -1) + } + pub fn ori(&mut self, rd: Register, rs1: Register, imm: i32) { + self.i(OpImm, 0b110, rd, rs1, imm) + } + pub fn andi(&mut self, rd: Register, rs1: Register, imm: i32) { + self.i(OpImm, 0b111, rd, rs1, imm) + } + pub fn slli(&mut self, rd: Register, rs1: Register, imm: i32) { + self.i(OpImm, 0b001, rd, rs1, imm) + } + + pub fn srli(&mut self, rd: Register, rs1: Register, imm: i32) { + self.i(OpImm, 0b101, rd, rs1, imm) + } + + pub fn srai(&mut self, rd: Register, rs1: Register, imm: i32) { + self.i(OpImm, 0b101, rd, rs1, imm | 0b0100000) + } + pub fn add(&mut self, rd: Register, rs1: Register, rs2: Register) { + self.r(Op, 0b000, 0b0000000, rd, rs1, rs2) + } + pub fn sub(&mut self, rd: Register, rs1: Register, rs2: Register) { + self.r(Op, 0b000, 0b0100000, rd, rs1, rs2) + } + /** pseudo-instruction */ + pub fn neg(&mut self, rd: Register, rs1: Register) { + self.sub(rd, REG_ZERO.into(), rs1) + } + pub fn sll(&mut self, rd: Register, rs1: Register, rs2: Register) { + self.r(Op, 0b001, 0b0000000, rd, rs1, rs2) + } + pub fn slt(&mut self, rd: Register, rs1: Register, rs2: Register) { + self.r(Op, 0b010, 0b0000000, rd, rs1, rs2) + } + /** pseudo-instruction */ + pub fn sltz(&mut self, rd: Register, rs1: Register) { + self.slt(rd, rs1, REG_ZERO) + } + /** pseudo-instruction */ + pub fn sgtz(&mut self, rd: Register, rs1: Register) { + self.slt(rd, REG_ZERO, rs1) + } + pub fn sltu(&mut self, rd: Register, rs1: Register, rs2: Register) { + self.r(Op, 0b011, 0b0000000, rd, rs1, rs2) + } + /** pseudo-instruction */ + pub fn snez(&mut self, rd: Register, rs1: Register) { + self.sltu(rd, REG_ZERO.into(), rs1); + } + pub fn xor(&mut self, rd: Register, rs1: Register, rs2: Register) { + self.r(Op, 0b100, 0b0000000, rd, rs1, rs2) + } + pub fn srl(&mut self, rd: Register, rs1: Register, rs2: Register) { + self.r(Op, 0b101, 0b0000000, rd, rs1, rs2) + } + pub fn sra(&mut self, rd: Register, rs1: Register, rs2: Register) { + self.r(Op, 0b101, 0b0100000, rd, rs1, rs2) + } + pub fn or(&mut self, rd: Register, rs1: Register, rs2: Register) { + self.r(Op, 0b110, 0b0000000, rd, rs1, rs2) + } + pub fn and(&mut self, rd: Register, rs1: Register, rs2: Register) { + self.r(Op, 0b111, 0b0000000, rd, rs1, rs2) + } + pub fn fence(&mut self, pred: MemoryAccess, succ: MemoryAccess) { + self.i( + MiscMem, + 0b000, + REG_ZERO, + REG_ZERO, + pred.pred() | succ.succ(), + ) + } + pub fn fence_tso(&mut self) { + let fm = 0b1000 << 28; + let pred = 0b0011 << 24; + let succ = 0b0011 << 24; + self.i(MiscMem, 0b000, REG_ZERO, REG_ZERO, fm | pred | succ) + } + pub fn ecall(&mut self) { + self.i(System, 0b000, REG_ZERO, REG_ZERO, 0) + } + pub fn ebreak(&mut self) { + self.i(System, 0b001, REG_ZERO, REG_ZERO, 1) + } + // ▲▲ RV32I Base Instruction Set ▲▲ + // ▼▼ RV64I Base Instruction Set (in addition to RV32I) ▼▼ + pub fn lwu(&mut self, rd: Register, rs1: Register, imm: i32) { + self.i(Load, 0b110, rd, rs1, imm) + } + pub fn ld(&mut self, rd: Register, rs1: Register, imm: i32) { + self.i(Load, 0b011, rd, rs1, imm) + } + pub fn sd(&mut self, rs1: Register, rs2: Register, imm: i32) { + self.s(Store, 0b011, rs1, rs2, imm) + } + pub fn addiw(&mut self, rd: Register, rs1: Register, imm: i32) { + self.i(OpImm32, 0b000, rd, rs1, imm) + } + pub fn slliw(&mut self, rd: Register, rs1: Register, imm: i32) { + self.i(OpImm32, 0b001, rd, rs1, imm) + } + pub fn srliw(&mut self, rd: Register, rs1: Register, imm: i32) { + self.i(OpImm32, 0b101, rd, rs1, imm) + } + pub fn sraiw(&mut self, rd: Register, rs1: Register, imm: i32) { + self.i(OpImm32, 0b101, rd, rs1, imm | 0b0100000) + } + pub fn addw(&mut self, rd: Register, rs1: Register, rs2: Register) { + self.r(Op32, 0b000, 0b0000000, rd, rs1, rs2) + } + pub fn subw(&mut self, rd: Register, rs1: Register, rs2: Register) { + self.r(Op32, 0b000, 0b0100000, rd, rs1, rs2) + } + pub fn sllw(&mut self, rd: Register, rs1: Register, rs2: Register) { + self.r(Op32, 0b001, 0b0000000, rd, rs1, rs2) + } + pub fn srlw(&mut self, rd: Register, rs1: Register, rs2: Register) { + self.r(Op32, 0b101, 0b0000000, rd, rs1, rs2) + } + pub fn sraw(&mut self, rd: Register, rs1: Register, rs2: Register) { + self.r(Op32, 0b101, 0b0100000, rd, rs1, rs2) + } + // ▲▲ RV64I Base Instruction Set (in addition to RV32I) ▲▲ + // ▼▼ RV32/RV64 Zifencei Standard Extension ▼▼ + pub fn fencei(&mut self) { + self.i(MiscMem, 0b001, REG_ZERO, REG_ZERO, 0) + } + // ▲▲ RV32/RV64 Zifencei Standard Extension ▲▲ + // ▼▼ RV32/RV64 Zicsr Standard Extension ▼▼ + pub fn csrrw(&mut self, rd: Register, rs1: Register, imm: i32) { + self.i(System, 0b001, rd, rs1, imm) + } + pub fn csrrs(&mut self, rd: Register, rs1: Register, imm: i32) { + self.i(System, 0b010, rd, rs1, imm) + } + pub fn csrrc(&mut self, rd: Register, rs1: Register, imm: i32) { + self.i(System, 0b011, rd, rs1, imm) + } + pub fn csrrwi(&mut self, rd: Register, imm: i32) { + self.u(System, rd, 0b101 << 12 | imm << 15) + } + pub fn csrrsi(&mut self, rd: Register, imm: i32) { + self.u(System, rd, 0b110 << 12 | imm << 15) + } + pub fn csrrci(&mut self, rd: Register, imm: i32) { + self.u(System, rd, 0b111 << 12 | imm << 15) + } + // ▲▲ RV32/RV64 Zicsr Standard Extension ▲▲ + // ▼▼ RV32M Standard Extension ▼▼ + pub fn mul(&mut self, rd: Register, rs1: Register, rs2: Register) { + self.r(Op, 0b000, 0b0000001, rd, rs1, rs2) + } + pub fn mulh(&mut self, rd: Register, rs1: Register, rs2: Register) { + self.r(Op, 0b001, 0b0000001, rd, rs1, rs2) + } + pub fn mulhsu(&mut self, rd: Register, rs1: Register, rs2: Register) { + self.r(Op, 0b010, 0b0000001, rd, rs1, rs2) + } + pub fn mulhu(&mut self, rd: Register, rs1: Register, rs2: Register) { + self.r(Op, 0b011, 0b0000001, rd, rs1, rs2) + } + pub fn div(&mut self, rd: Register, rs1: Register, rs2: Register) { + self.r(Op, 0b100, 0b0000001, rd, rs1, rs2) + } + pub fn divu(&mut self, rd: Register, rs1: Register, rs2: Register) { + self.r(Op, 0b101, 0b0000001, rd, rs1, rs2) + } + pub fn rem(&mut self, rd: Register, rs1: Register, rs2: Register) { + self.r(Op, 0b110, 0b0000001, rd, rs1, rs2) + } + pub fn remu(&mut self, rd: Register, rs1: Register, rs2: Register) { + self.r(Op, 0b111, 0b0000001, rd, rs1, rs2) + } + // ▲▲ RV32M Standard Extension ▲▲ + // ▼▼ RV64M Standard Extension (in addition to RV32M) ▼▼ + pub fn mulw(&mut self, rd: Register, rs1: Register, rs2: Register) { + self.r(Op32, 0b000, 0b0000001, rd, rs1, rs2) + } + pub fn divw(&mut self, rd: Register, rs1: Register, rs2: Register) { + self.r(Op32, 0b100, 0b0000001, rd, rs1, rs2) + } + pub fn divuw(&mut self, rd: Register, rs1: Register, rs2: Register) { + self.r(Op32, 0b101, 0b0000001, rd, rs1, rs2) + } + pub fn remw(&mut self, rd: Register, rs1: Register, rs2: Register) { + self.r(Op32, 0b110, 0b0000001, rd, rs1, rs2) + } + pub fn remuw(&mut self, rd: Register, rs1: Register, rs2: Register) { + self.r(Op32, 0b111, 0b0000001, rd, rs1, rs2) + } + // ▲▲ RV64M Standard Extension (in addition to RV32M) ▲▲ + // ▼▼ RV32A Standard Extension ▼▼ + pub fn lr_w(&mut self, rd: Register, rs1: Register, mem_order: MemoryOrdering) { + self.r_aqrl(0b010, 0b00010, mem_order, rd, rs1, REG_ZERO) + } + pub fn sc_w(&mut self, rd: Register, rs1: Register, rs2: Register, mem_order: MemoryOrdering) { + self.r_aqrl(0b010, 0b00011, mem_order, rd, rs1, rs2) + } + pub fn amoswap_w( + &mut self, + rd: Register, + rs1: Register, + rs2: Register, + mem_order: MemoryOrdering, + ) { + self.r_aqrl(0b010, 0b00001, mem_order, rd, rs1, rs2) + } + pub fn amoadd_w( + &mut self, + rd: Register, + rs1: Register, + rs2: Register, + mem_order: MemoryOrdering, + ) { + self.r_aqrl(0b010, 0b00000, mem_order, rd, rs1, rs2) + } + pub fn amoxor_w( + &mut self, + rd: Register, + rs1: Register, + rs2: Register, + mem_order: MemoryOrdering, + ) { + self.r_aqrl(0b010, 0b00100, mem_order, rd, rs1, rs2) + } + pub fn amoand_w( + &mut self, + rd: Register, + rs1: Register, + rs2: Register, + mem_order: MemoryOrdering, + ) { + self.r_aqrl(0b010, 0b01100, mem_order, rd, rs1, rs2) + } + pub fn amoor_w( + &mut self, + rd: Register, + rs1: Register, + rs2: Register, + mem_order: MemoryOrdering, + ) { + self.r_aqrl(0b010, 0b01000, mem_order, rd, rs1, rs2) + } + pub fn amomin_w( + &mut self, + rd: Register, + rs1: Register, + rs2: Register, + mem_order: MemoryOrdering, + ) { + self.r_aqrl(0b010, 0b10000, mem_order, rd, rs1, rs2) + } + pub fn amomax_w( + &mut self, + rd: Register, + rs1: Register, + rs2: Register, + mem_order: MemoryOrdering, + ) { + self.r_aqrl(0b010, 0b10100, mem_order, rd, rs1, rs2) + } + pub fn amominu_w( + &mut self, + rd: Register, + rs1: Register, + rs2: Register, + mem_order: MemoryOrdering, + ) { + self.r_aqrl(0b010, 0b11000, mem_order, rd, rs1, rs2) + } + pub fn amomaxu_w( + &mut self, + rd: Register, + rs1: Register, + rs2: Register, + mem_order: MemoryOrdering, + ) { + self.r_aqrl(0b010, 0b11100, mem_order, rd, rs1, rs2) + } + // ▲▲ RV32A Standard Extension ▲▲ + // ▼▼ RV64A Standard Extension (in addition to RV32A) ▼▼ + pub fn lr_d(&mut self, rd: Register, rs1: Register, mem_order: MemoryOrdering) { + self.r_aqrl(0b011, 0b00010, mem_order, rd, rs1, REG_ZERO) + } + pub fn sc_d(&mut self, rd: Register, rs1: Register, rs2: Register, mem_order: MemoryOrdering) { + self.r_aqrl(0b011, 0b00011, mem_order, rd, rs1, rs2) + } + pub fn amoswap_d( + &mut self, + rd: Register, + rs1: Register, + rs2: Register, + mem_order: MemoryOrdering, + ) { + self.r_aqrl(0b011, 0b00001, mem_order, rd, rs1, rs2) + } + pub fn amoadd_d( + &mut self, + rd: Register, + rs1: Register, + rs2: Register, + mem_order: MemoryOrdering, + ) { + self.r_aqrl(0b011, 0b00000, mem_order, rd, rs1, rs2) + } + pub fn amoxor_d( + &mut self, + rd: Register, + rs1: Register, + rs2: Register, + mem_order: MemoryOrdering, + ) { + self.r_aqrl(0b011, 0b00100, mem_order, rd, rs1, rs2) + } + pub fn amoand_d( + &mut self, + rd: Register, + rs1: Register, + rs2: Register, + mem_order: MemoryOrdering, + ) { + self.r_aqrl(0b011, 0b01100, mem_order, rd, rs1, rs2) + } + pub fn amoor_d( + &mut self, + rd: Register, + rs1: Register, + rs2: Register, + mem_order: MemoryOrdering, + ) { + self.r_aqrl(0b011, 0b01000, mem_order, rd, rs1, rs2) + } + pub fn amomin_d( + &mut self, + rd: Register, + rs1: Register, + rs2: Register, + mem_order: MemoryOrdering, + ) { + self.r_aqrl(0b011, 0b10000, mem_order, rd, rs1, rs2) + } + pub fn amomax_d( + &mut self, + rd: Register, + rs1: Register, + rs2: Register, + mem_order: MemoryOrdering, + ) { + self.r_aqrl(0b011, 0b10100, mem_order, rd, rs1, rs2) + } + pub fn amominu_d( + &mut self, + rd: Register, + rs1: Register, + rs2: Register, + mem_order: MemoryOrdering, + ) { + self.r_aqrl(0b011, 0b11000, mem_order, rd, rs1, rs2) + } + pub fn amomaxu_d( + &mut self, + rd: Register, + rs1: Register, + rs2: Register, + mem_order: MemoryOrdering, + ) { + self.r_aqrl(0b011, 0b11100, mem_order, rd, rs1, rs2) + } + // ▲▲ RV64A Standard Extension (in addition to RV32A) ▲▲ + // ▼▼ RV32F Standard Extension ▼▼ + pub fn flw(&mut self, rd: Register, rs1: Register, imm: i32) { + self.i(LoadFP, 0b010, rd, rs1, imm) + } + pub fn fsw(&mut self, rs1: Register, rs2: Register, imm: i32) { + self.s(StoreFp, 0b010, rs1, rs2, imm) + } + pub fn fmadd_s( + &mut self, + rm: RoundingMode, + rd: Register, + rs1: Register, + rs2: Register, + rs3: Register, + ) { + self.r4rm(MAdd, FloatFormat::S, rm, rd, rs1, rs2, rs3) + } + pub fn fmsub_s( + &mut self, + rm: RoundingMode, + rd: Register, + rs1: Register, + rs2: Register, + rs3: Register, + ) { + self.r4rm(MSub, FloatFormat::S, rm, rd, rs1, rs2, rs3) + } + pub fn fnmsub_s( + &mut self, + rm: RoundingMode, + rd: Register, + rs1: Register, + rs2: Register, + rs3: Register, + ) { + self.r4rm(NMSub, FloatFormat::S, rm, rd, rs1, rs2, rs3) + } + pub fn fnmadd_s( + &mut self, + rm: RoundingMode, + rd: Register, + rs1: Register, + rs2: Register, + rs3: Register, + ) { + self.r4rm(NMAdd, FloatFormat::S, rm, rd, rs1, rs2, rs3) + } + pub fn fadd_s( + &mut self, + rm: RoundingMode, + rd: FloatRegister, + rs1: FloatRegister, + rs2: FloatRegister, + ) { + self.r_fp(rm.value(), 0b0000000, rd, rs1, rs2) + } + pub fn fsub_s( + &mut self, + rm: RoundingMode, + rd: FloatRegister, + rs1: FloatRegister, + rs2: FloatRegister, + ) { + self.r_fp(rm.value(), 0b0000100, rd, rs1, rs2) + } + pub fn fmul_s( + &mut self, + rm: RoundingMode, + rd: FloatRegister, + rs1: FloatRegister, + rs2: FloatRegister, + ) { + self.r_fp(rm.value(), 0b0001000, rd, rs1, rs2) + } + pub fn fdiv_s( + &mut self, + rm: RoundingMode, + rd: FloatRegister, + rs1: FloatRegister, + rs2: FloatRegister, + ) { + self.r_fp(rm.value(), 0b0001100, rd, rs1, rs2) + } + pub fn fsqrt_s(&mut self, rm: RoundingMode, rd: FloatRegister, rs1: FloatRegister) { + self.r_fp(rm.value(), 0b0101100, rd, rs1, F0) + } + pub fn fsgnj_s(&mut self, rd: FloatRegister, rs1: FloatRegister, rs2: FloatRegister) { + self.r_fp(0b000, 0b0010000, rd, rs1, rs2) + } + pub fn fsgnjn_s(&mut self, rd: FloatRegister, rs1: FloatRegister, rs2: FloatRegister) { + self.r_fp(0b001, 0b0010000, rd, rs1, rs2) + } + pub fn fsgnjx_s(&mut self, rd: FloatRegister, rs1: FloatRegister, rs2: FloatRegister) { + self.r_fp(0b010, 0b0010000, rd, rs1, rs2) + } + pub fn fmin_s(&mut self, rd: FloatRegister, rs1: FloatRegister, rs2: FloatRegister) { + self.r_fp(0b000, 0b0010100, rd, rs1, rs2) + } + pub fn fmax_s(&mut self, rd: FloatRegister, rs1: FloatRegister, rs2: FloatRegister) { + self.r_fp(0b001, 0b0010100, rd, rs1, rs2) + } + pub fn fcvt_w_s(&mut self, rm: RoundingMode, rd: Register, rs1: FloatRegister) { + self.r_fp_to_int(rm.value(), 0b1100000, rd, rs1, F0) + } + pub fn fcvt_wu_s(&mut self, rm: RoundingMode, rd: Register, rs1: FloatRegister) { + self.r_fp_to_int(rm.value(), 0b1100000, rd, rs1, F1) + } + pub fn fmv_x_w(&mut self, rd: Register, rs1: FloatRegister) { + self.r_fp_to_int(0b000, 0b1110000, rd, rs1, F0) + } + pub fn feq_s(&mut self, rd: Register, rs1: FloatRegister, rs2: FloatRegister) { + self.r_fp_to_int(0b010, 0b10100000, rd, rs1, rs2) + } + pub fn flt_s(&mut self, rd: Register, rs1: FloatRegister, rs2: FloatRegister) { + self.r_fp_to_int(0b001, 0b10100000, rd, rs1, rs2) + } + pub fn fle_s(&mut self, rd: Register, rs1: FloatRegister, rs2: FloatRegister) { + self.r_fp_to_int(0b000, 0b10100000, rd, rs1, rs2) + } + pub fn fclass_s(&mut self, rd: Register, rs1: FloatRegister, rs2: FloatRegister) { + self.r_fp_to_int(0b001, 0b11100000, rd, rs1, rs2) + } + pub fn fcvt_s_w(&mut self, rm: RoundingMode, rd: FloatRegister, rs1: Register) { + self.r_int_to_fp(rm.value(), 0b1101000, rd, rs1, R0) + } + pub fn fcvt_s_wu(&mut self, rm: RoundingMode, rd: FloatRegister, rs1: Register) { + self.r_int_to_fp(rm.value(), 0b1101000, rd, rs1, R1) + } + pub fn fmv_w_x(&mut self, rd: FloatRegister, rs1: Register) { + self.r_int_to_fp(0b000, 0b1111000, rd, rs1, R0) + } + // ▲▲ RV32F Standard Extension ▲▲ + // ▼▼ RV64F Standard Extension (in addition to RV32F) ▼▼ + pub fn fcvt_l_s(&mut self, rm: RoundingMode, rd: Register, rs1: FloatRegister) { + self.r_fp_to_int(rm.value(), 0b1100000, rd, rs1, F2) + } + pub fn fcvt_lu_s(&mut self, rm: RoundingMode, rd: Register, rs1: FloatRegister) { + self.r_fp_to_int(rm.value(), 0b1100000, rd, rs1, F3) + } + pub fn fcvt_s_l(&mut self, rm: RoundingMode, rd: FloatRegister, rs1: Register) { + self.r_int_to_fp(rm.value(), 0b1101000, rd, rs1, R2) + } + pub fn fcvt_s_lu(&mut self, rm: RoundingMode, rd: FloatRegister, rs1: Register) { + self.r_int_to_fp(rm.value(), 0b1101000, rd, rs1, R3) + } + // ▲▲ RV64F Standard Extension (in addition to RV32F) ▲▲ + // ▼▼ RV32D Standard Extension ▼▼ + pub fn fld(&mut self, rd: Register, rs1: Register, imm: i32) { + self.i(LoadFP, 0b011, rd, rs1, imm) + } + pub fn fsd(&mut self, rs1: Register, rs2: Register, imm: i32) { + self.s(StoreFp, 0b011, rs1, rs2, imm) + } + pub fn fmadd_d( + &mut self, + rm: RoundingMode, + rd: Register, + rs1: Register, + rs2: Register, + rs3: Register, + ) { + self.r4rm(MAdd, FloatFormat::D, rm, rd, rs1, rs2, rs3) + } + pub fn fmsub_d( + &mut self, + rm: RoundingMode, + rd: Register, + rs1: Register, + rs2: Register, + rs3: Register, + ) { + self.r4rm(MSub, FloatFormat::D, rm, rd, rs1, rs2, rs3) + } + pub fn fnmsub_d( + &mut self, + rm: RoundingMode, + rd: Register, + rs1: Register, + rs2: Register, + rs3: Register, + ) { + self.r4rm(NMSub, FloatFormat::D, rm, rd, rs1, rs2, rs3) + } + pub fn fnmadd_d( + &mut self, + rm: RoundingMode, + rd: Register, + rs1: Register, + rs2: Register, + rs3: Register, + ) { + self.r4rm(NMAdd, FloatFormat::D, rm, rd, rs1, rs2, rs3) + } + pub fn fadd_d( + &mut self, + rm: RoundingMode, + rd: FloatRegister, + rs1: FloatRegister, + rs2: FloatRegister, + ) { + self.r_fp(rm.value(), 0b0000001, rd, rs1, rs2) + } + pub fn fsub_d( + &mut self, + rm: RoundingMode, + rd: FloatRegister, + rs1: FloatRegister, + rs2: FloatRegister, + ) { + self.r_fp(rm.value(), 0b0000101, rd, rs1, rs2) + } + pub fn fmul_d( + &mut self, + rm: RoundingMode, + rd: FloatRegister, + rs1: FloatRegister, + rs2: FloatRegister, + ) { + self.r_fp(rm.value(), 0b0001001, rd, rs1, rs2) + } + pub fn fdiv_d( + &mut self, + rm: RoundingMode, + rd: FloatRegister, + rs1: FloatRegister, + rs2: FloatRegister, + ) { + self.r_fp(rm.value(), 0b0001101, rd, rs1, rs2) + } + pub fn fsqrt_d(&mut self, rm: RoundingMode, rd: FloatRegister, rs1: FloatRegister) { + self.r_fp(rm.value(), 0b0101101, rd, rs1, F0) + } + pub fn fsgnj_d(&mut self, rd: FloatRegister, rs1: FloatRegister, rs2: FloatRegister) { + self.r_fp(0b000, 0b0010001, rd, rs1, rs2) + } + pub fn fsgnjn_d(&mut self, rd: FloatRegister, rs1: FloatRegister, rs2: FloatRegister) { + self.r_fp(0b001, 0b0010011, rd, rs1, rs2) + } + pub fn fsgnjx_d(&mut self, rd: FloatRegister, rs1: FloatRegister, rs2: FloatRegister) { + self.r_fp(0b010, 0b0010010, rd, rs1, rs2) + } + pub fn fmin_d(&mut self, rd: FloatRegister, rs1: FloatRegister, rs2: FloatRegister) { + self.r_fp(0b000, 0b0010101, rd, rs1, rs2) + } + pub fn fmax_d(&mut self, rd: FloatRegister, rs1: FloatRegister, rs2: FloatRegister) { + self.r_fp(0b001, 0b0010101, rd, rs1, rs2) + } + pub fn fcvt_s_d(&mut self, rm: RoundingMode, rd: FloatRegister, rs1: FloatRegister) { + self.r_fp(rm.value(), 0b0100000, rd, rs1, F1) + } + pub fn fcvt_d_s(&mut self, rm: RoundingMode, rd: FloatRegister, rs1: FloatRegister) { + self.r_fp(rm.value(), 0b0100001, rd, rs1, F0) + } + pub fn feq_d(&mut self, rd: Register, rs1: FloatRegister, rs2: FloatRegister) { + self.r_fp_to_int(0b010, 0b10100001, rd, rs1, rs2) + } + pub fn flt_d(&mut self, rd: Register, rs1: FloatRegister, rs2: FloatRegister) { + self.r_fp_to_int(0b001, 0b10100001, rd, rs1, rs2) + } + pub fn fle_d(&mut self, rd: Register, rs1: FloatRegister, rs2: FloatRegister) { + self.r_fp_to_int(0b000, 0b10100001, rd, rs1, rs2) + } + pub fn fclass_d(&mut self, rd: Register, rs1: FloatRegister, rs2: FloatRegister) { + self.r_fp_to_int(0b001, 0b11100001, rd, rs1, rs2) + } + pub fn fcvt_w_d(&mut self, rm: RoundingMode, rd: Register, rs1: FloatRegister) { + self.r_fp_to_int(rm.value(), 0b1100001, rd, rs1, F0) + } + pub fn fcvt_wu_d(&mut self, rm: RoundingMode, rd: Register, rs1: FloatRegister) { + self.r_fp_to_int(rm.value(), 0b1100001, rd, rs1, F1) + } + pub fn fcvt_d_w(&mut self, rm: RoundingMode, rd: FloatRegister, rs1: Register) { + self.r_int_to_fp(rm.value(), 0b1101001, rd, rs1, R0) + } + pub fn fcvt_d_wu(&mut self, rm: RoundingMode, rd: FloatRegister, rs1: Register) { + self.r_int_to_fp(rm.value(), 0b1101001, rd, rs1, R1) + } + // ▲▲ RV32D Standard Extension ▲▲ + // ▼▼ RV64D Standard Extension (in addition to RV32D) ▼▼ + pub fn fcvt_l_d(&mut self, rm: RoundingMode, rd: Register, rs1: FloatRegister) { + self.r_fp_to_int(rm.value(), 0b1100001, rd, rs1, F2) + } + pub fn fcvt_lu_d(&mut self, rm: RoundingMode, rd: Register, rs1: FloatRegister) { + self.r_fp_to_int(rm.value(), 0b1100001, rd, rs1, F3) + } + pub fn fmv_x_d(&mut self, rd: Register, rs1: FloatRegister) { + self.r_fp_to_int(0b000, 0b1110001, rd, rs1, F0) + } + pub fn fcvt_d_l(&mut self, rm: RoundingMode, rd: FloatRegister, rs1: Register) { + self.r_int_to_fp(rm.value(), 0b1101001, rd, rs1, R2) + } + pub fn fcvt_d_lu(&mut self, rm: RoundingMode, rd: FloatRegister, rs1: Register) { + self.r_int_to_fp(rm.value(), 0b1101001, rd, rs1, R3) + } + pub fn fmv_d_x(&mut self, rd: FloatRegister, rs1: Register) { + self.r_int_to_fp(0b000, 0b1111001, rd, rs1, R0) + } + // ▲▲ RV64D Standard Extension (in addition to RV32D) ▲▲ + // ▼▼ RV32B Standard Extension ▼▼ + // ▼ Zba: address generation ▼ + /** shift left by 1 and add */ + pub fn sh1add(&mut self, rd: Register, rs1: Register, rs2: Register) { + self.r(Op, 0b010, 0010000, rd, rs1, rs2) + } + /** shift left by 2 and add */ + pub fn sh2add(&mut self, rd: Register, rs1: Register, rs2: Register) { + self.r(Op, 0b100, 0010000, rd, rs1, rs2) + } + /** shift left by 3 and add */ + pub fn sh3add(&mut self, rd: Register, rs1: Register, rs2: Register) { + self.r(Op, 0b110, 0010000, rd, rs1, rs2) + } + // ▲ Zba: address generation ▲ + // ▼ Zbb: bit-manipulation ▼ + /** AND with inverted operand */ + pub fn andn(&mut self, rd: Register, rs1: Register, rs2: Register) { + self.r(Op, 0b111, 0b0100000, rd, rs1, rs2) + } + /** OR with inverted operand */ + pub fn orn(&mut self, rd: Register, rs1: Register, rs2: Register) { + self.r(Op, 0b110, 0b0100000, rd, rs1, rs2) + } + /** exclusive NOR */ + pub fn xnor(&mut self, rd: Register, rs1: Register, rs2: Register) { + self.r(Op, 0b100, 0b0100000, rd, rs1, rs2) + } + /** count leading zero bits */ + pub fn clz(&mut self, rd: Register, rs1: Register) { + self.i(OpImm, 0b001, rd, rs1, 0b0110000_00000) + } + /** count trailing zero bits */ + pub fn ctz(&mut self, rd: Register, rs1: Register) { + self.i(OpImm, 0b001, rd, rs1, 0b0110000_00001) + } + /** count set bits in word */ + pub fn cpop(&mut self, rd: Register, rs1: Register) { + self.i(OpImm, 0b001, rd, rs1, 0b0110000_00010) + } + /** maximum */ + pub fn max(&mut self, rd: Register, rs1: Register, rs2: Register) { + self.r(Op, 0b110, 0b0000101, rd, rs1, rs2) + } + /** unsigned maximum */ + pub fn maxu(&mut self, rd: Register, rs1: Register, rs2: Register) { + self.r(Op, 0b111, 0b0000101, rd, rs1, rs2) + } + /** minimum */ + pub fn min(&mut self, rd: Register, rs1: Register, rs2: Register) { + self.r(Op, 0b100, 0b0000101, rd, rs1, rs2) + } + /** unsigned minimum */ + pub fn minu(&mut self, rd: Register, rs1: Register, rs2: Register) { + self.r(Op, 0b101, 0b0000101, rd, rs1, rs2) + } + /** sign-extend byte */ + pub fn sext_b(&mut self, rd: Register, rs1: Register) { + self.i(OpImm, 0b001, rd, rs1, 0b0110000_00100) + } + /** sign-extend halfword */ + pub fn sext_h(&mut self, rd: Register, rs1: Register) { + self.i(OpImm, 0b001, rd, rs1, 0b0110000_00101) + } + /** Zero-extend halfword */ + pub fn zext_h_32(&mut self, rd: Register, rs1: Register) { + self.i(Op, 0b100, rd, rs1, 0b0000100_00000) + } + /** rotate left (register) */ + pub fn rol(&mut self, rd: Register, rs1: Register, rs2: Register) { + self.r(Op, 0b001, 0b0110000, rd, rs1, rs2) + } + /** rotate right (register) */ + pub fn ror(&mut self, rd: Register, rs1: Register, rs2: Register) { + self.r(Op, 0b101, 0b0110000, rd, rs1, rs2) + } + /** rotate right (immediate) */ + pub fn rori(&mut self, rd: Register, rs1: Register, imm: i32) { + self.i(OpImm, 0b101, rd, rs1, (imm & 0b111111) | 0b011000_000000); + } + /** bitwise OR-combine, byte granule */ + pub fn orc_b(&mut self, rd: Register, rs1: Register) { + self.i(OpImm, 0b101, rd, rs1, 0b001010000111); + } + /** byte-reverse register */ + pub fn rev8_32(&mut self, rd: Register, rs1: Register) { + self.i(OpImm, 0b101, rd, rs1, 0b011010011000); + } + // ▲ Zbb: bit-manipulation ▲ + // ▲▲ RV32B Standard Extension ▲▲ + // ▼▼ RV64B Standard Extension (in addition to RV32B) ▼▼ + // ▼ Zba: address generation ▼ + /** add unsigned word */ + pub fn add_uw(&mut self, rd: Register, rs1: Register, rs2: Register) { + self.r(Op32, 0b000, 0000100, rd, rs1, rs2) + } + /** shift unsigned word left by 1 and add */ + pub fn sh1add_uw(&mut self, rd: Register, rs1: Register, rs2: Register) { + self.r(Op32, 0b010, 0010000, rd, rs1, rs2) + } + /** shift unsigned word left by 2 and add */ + pub fn sh2add_uw(&mut self, rd: Register, rs1: Register, rs2: Register) { + self.r(Op32, 0b100, 0010000, rd, rs1, rs2) + } + /** shift unsigned word left by 3 and add */ + pub fn sh3add_uw(&mut self, rd: Register, rs1: Register, rs2: Register) { + self.r(Op32, 0b110, 0010000, rd, rs1, rs2) + } + /** shift-left unsigned word (immediate) */ + pub fn slli_uw(&mut self, rd: Register, rs1: Register, imm: i32) { + self.i(OpImm32, 0b001, rd, rs1, (imm & 0b111111) | 0b000010_000000); + } + // ▲ Zba: address generation ▲ + // ▼ Zbb: Basic bit-manipulation ▼ + /** count leading zero bits in word */ + pub fn clzw(&mut self, rd: Register, rs1: Register) { + self.i(OpImm32, 0b001, rd, rs1, 0b0110000_00000) + } + /** count trailing zero bits in word */ + pub fn ctzw(&mut self, rd: Register, rs1: Register) { + self.i(OpImm32, 0b001, rd, rs1, 0b0110000_00001) + } + /** count set bits in word */ + pub fn cpopw(&mut self, rd: Register, rs1: Register) { + self.i(OpImm32, 0b001, rd, rs1, 0b0110000_00010) + } + /** Zero-extend halfword */ + pub fn zext_h_64(&mut self, rd: Register, rs1: Register) { + self.i(Op32, 0b100, rd, rs1, 0b0000100_00000) + } + /** rotate left word (register) */ + pub fn rolw(&mut self, rd: Register, rs1: Register, rs2: Register) { + self.r(Op32, 0b001, 0b0110000, rd, rs1, rs2) + } + /** rotate right word (register) */ + pub fn rorw(&mut self, rd: Register, rs1: Register, rs2: Register) { + self.r(Op32, 0b101, 0b0110000, rd, rs1, rs2) + } + /** rotate right word (immediate) */ + pub fn roriw(&mut self, rd: Register, rs1: Register, imm: i32) { + self.i(OpImm32, 0b101, rd, rs1, (imm & 0b11111) | 0b0110000_00000); + } + /** byte-reverse register */ + pub fn rev8_64(&mut self, rd: Register, rs1: Register) { + self.i(OpImm, 0b101, rd, rs1, 0b011010111000); + } + // ▲ Zbb: Basic bit-manipulation ▲ + // ▲▲ RV64B Standard Extension (in addition to RV32B) ▲▲ +} + +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub enum Condition { + EQ, + NE, + GTU, + LEU, + GEU, + LTU, + GT, + LE, + GE, + LT, +} + +impl Condition { + pub fn invert(self) -> Self { + match self { + Condition::EQ => Condition::NE, + Condition::NE => Condition::EQ, + Condition::GTU => Condition::LEU, + Condition::LEU => Condition::GTU, + Condition::GEU => Condition::LTU, + Condition::LTU => Condition::GEU, + Condition::GT => Condition::LE, + Condition::LE => Condition::GT, + Condition::GE => Condition::LT, + Condition::LT => Condition::GE, + } + } +} diff --git a/dora/src/cpu.rs b/dora/src/cpu.rs index d3296a6a..fc7a0aae 100644 --- a/dora/src/cpu.rs +++ b/dora/src/cpu.rs @@ -12,6 +12,12 @@ pub use self::arm64::*; #[cfg(target_arch = "aarch64")] pub mod arm64; +#[cfg(target_arch = "riscv64")] +pub use self::rv64::*; + +#[cfg(target_arch = "riscv64")] +pub mod rv64; + #[derive(Copy, Clone, PartialEq, Eq, Debug)] pub struct Reg(pub u8); diff --git a/dora/src/cpu/rv64.rs b/dora/src/cpu/rv64.rs new file mode 100644 index 00000000..7c2753ba --- /dev/null +++ b/dora/src/cpu/rv64.rs @@ -0,0 +1,159 @@ +use crate::cpu::{FReg, Reg}; +use crate::masm::CondCode; +use dora_asm::rv64 as rv64_asm; +use dora_asm::rv64::{Condition, Register}; + +pub static REG_PARAMS: [Reg; 8] = [R10, R11, R12, R13, R14, R15, R16, R17]; +pub static FREG_PARAMS: [FReg; 12] = [F0, F1, F2, F3, F4, F5, F6, F7, F28, F29, F30, F31]; + +pub static CCALL_REG_PARAMS: [Reg; 8] = [R10, R11, R12, R13, R14, R15, R16, R17]; +pub static CCALL_FREG_PARAMS: [FReg; 12] = [F0, F1, F2, F3, F4, F5, F6, F7, F28, F29, F30, F31]; + +pub static SCRATCH: [Reg; 7] = [R5, R6, R7, R28, R29, R30, R31]; + +pub const REG_RESULT: Reg = R10; +pub const REG_TMP1: Reg = R5; +pub const REG_TMP2: Reg = R6; +pub const REG_TMP3: Reg = R7; +pub const REG_ZERO: Reg = Reg(0); +pub const REG_RA: Reg = Reg(1); +pub const REG_SP: Reg = Reg(2); +pub const REG_GP: Reg = Reg(3); +pub const REG_THREAD: Reg = Reg(4); +pub const REG_FP: Reg = Reg(8); + +pub const FREG_RESULT: FReg = F10; +pub const FREG_TMP1: FReg = F0; + +pub const STACK_FRAME_ALIGNMENT: usize = 16; + +pub const R0: Reg = Reg(0); +pub const R1: Reg = Reg(1); +pub const R2: Reg = Reg(2); +pub const R3: Reg = Reg(3); +pub const R4: Reg = Reg(4); +pub const R5: Reg = Reg(5); +pub const R6: Reg = Reg(6); +pub const R7: Reg = Reg(7); +#[allow(dead_code)] +pub const R8: Reg = Reg(8); +pub const R9: Reg = Reg(9); +pub const R10: Reg = Reg(10); +pub const R11: Reg = Reg(11); +pub const R12: Reg = Reg(12); +pub const R13: Reg = Reg(13); +pub const R14: Reg = Reg(14); +pub const R15: Reg = Reg(15); +#[allow(dead_code)] +pub const R16: Reg = Reg(16); +#[allow(dead_code)] +pub const R17: Reg = Reg(17); +#[allow(dead_code)] +pub const R18: Reg = Reg(18); +#[allow(dead_code)] +pub const R19: Reg = Reg(19); +#[allow(dead_code)] +pub const R20: Reg = Reg(20); +#[allow(dead_code)] +pub const R21: Reg = Reg(21); +#[allow(dead_code)] +pub const R22: Reg = Reg(22); +#[allow(dead_code)] +pub const R23: Reg = Reg(23); +#[allow(dead_code)] +pub const R24: Reg = Reg(24); +#[allow(dead_code)] +pub const R25: Reg = Reg(25); +#[allow(dead_code)] +pub const R26: Reg = Reg(26); +#[allow(dead_code)] +pub const R27: Reg = Reg(27); +pub const R28: Reg = Reg(28); +pub const R29: Reg = Reg(29); +pub const R30: Reg = Reg(30); +pub const R31: Reg = Reg(31); + +pub const F0: FReg = FReg(0); +pub const F1: FReg = FReg(1); +pub const F2: FReg = FReg(2); +pub const F3: FReg = FReg(3); +pub const F4: FReg = FReg(4); +pub const F5: FReg = FReg(5); +pub const F6: FReg = FReg(6); +pub const F7: FReg = FReg(7); +pub const F8: FReg = FReg(8); +pub const F9: FReg = FReg(9); +pub const F10: FReg = FReg(10); +pub const F11: FReg = FReg(11); +pub const F12: FReg = FReg(12); +pub const F13: FReg = FReg(13); +pub const F14: FReg = FReg(14); +pub const F15: FReg = FReg(15); +pub const F16: FReg = FReg(16); +pub const F17: FReg = FReg(17); +pub const F18: FReg = FReg(18); +pub const F19: FReg = FReg(19); +pub const F20: FReg = FReg(20); +pub const F21: FReg = FReg(21); +pub const F22: FReg = FReg(22); +pub const F23: FReg = FReg(23); +pub const F24: FReg = FReg(24); +pub const F25: FReg = FReg(25); +pub const F26: FReg = FReg(26); +pub const F27: FReg = FReg(27); +pub const F28: FReg = FReg(28); +pub const F29: FReg = FReg(29); +pub const F30: FReg = FReg(30); +pub const F31: FReg = FReg(31); + +pub fn flush_icache(_: *const u8, _: usize) {} + +pub fn has_round() -> bool { + true +} + +pub fn has_popcnt() -> bool { + false +} + +pub fn has_lzcnt() -> bool { + false +} + +pub fn has_tzcnt() -> bool { + false +} + +impl From for Register { + fn from(reg: Reg) -> Register { + if reg.0 < 31 { + Register::new(reg.0) + } else if reg == REG_ZERO { + rv64_asm::REG_ZERO + } else { + assert_eq!(reg, REG_SP); + rv64_asm::REG_SP + } + } +} + +pub static PARAM_OFFSET: i32 = 16; + +impl From for Condition { + fn from(c: CondCode) -> Condition { + match c { + CondCode::Zero => Condition::EQ, + CondCode::NonZero => Condition::NE, + CondCode::Equal => Condition::EQ, + CondCode::NotEqual => Condition::NE, + CondCode::Greater => Condition::GT, + CondCode::GreaterEq => Condition::GE, + CondCode::Less => Condition::LT, + CondCode::LessEq => Condition::LE, + CondCode::UnsignedGreater => Condition::GTU, + CondCode::UnsignedGreaterEq => Condition::GEU, + CondCode::UnsignedLess => Condition::LTU, + CondCode::UnsignedLessEq => Condition::LEU, + } + } +} diff --git a/dora/src/disassembler/capstone.rs b/dora/src/disassembler/capstone.rs index 60050296..1ae2a28b 100644 --- a/dora/src/disassembler/capstone.rs +++ b/dora/src/disassembler/capstone.rs @@ -131,3 +131,11 @@ fn get_engine(_asm_syntax: AsmSyntax) -> CsResult { .mode(arch::arm64::ArchMode::Arm) .build() } + +#[cfg(target_arch = "riscv64")] +fn get_engine(_asm_syntax: AsmSyntax) -> CsResult { + Capstone::new() + .riscv() + .mode(arch::riscv::ArchMode::RiscV64) + .build() +} diff --git a/dora/src/masm.rs b/dora/src/masm.rs index 20f2fdd7..b136c8d1 100644 --- a/dora/src/masm.rs +++ b/dora/src/masm.rs @@ -28,6 +28,12 @@ pub use self::arm64::*; #[cfg(target_arch = "aarch64")] pub mod arm64; +#[cfg(target_arch = "riscv64")] +pub use self::rv64::*; + +#[cfg(target_arch = "riscv64")] +pub mod rv64; + pub struct CodeDescriptor { pub constpool: ConstPool, pub code: Vec, diff --git a/dora/src/masm/rv64.rs b/dora/src/masm/rv64.rs new file mode 100644 index 00000000..bf1acb87 --- /dev/null +++ b/dora/src/masm/rv64.rs @@ -0,0 +1,861 @@ +use dora_parser::lexer::position::Position; + +use crate::compiler::codegen::AnyReg; +use crate::cpu::*; +use crate::gc::swiper::CARD_SIZE_BITS; +use crate::gc::Address; +use crate::language::sem_analysis::FctDefinitionId; +use crate::language::ty::SourceTypeArray; +use crate::masm::{CondCode, Label, MacroAssembler, Mem}; +use crate::mem::ptr_width; +use crate::mode::MachineMode; +use crate::object::{ + offset_of_array_data, offset_of_array_length, offset_of_string_data, Header, STR_LEN_MASK, +}; +use crate::threads::ThreadLocalData; +use crate::vm::{get_vm, LazyCompilationSite, Trap}; +use crate::vtable::VTable; +pub use dora_asm::rv64::AssemblerRv64 as Assembler; +use dora_asm::rv64::{self as asm, FloatRegister, RoundingMode}; + +impl MacroAssembler { + pub fn prolog(&mut self, stacksize: i32) { + self.asm.addi(REG_SP.into(), REG_SP.into(), -16); + self.asm.sd(REG_SP.into(), REG_FP.into(), 0); + self.asm.sd(REG_SP.into(), REG_RA.into(), 8); + self.asm.addi(REG_SP.into(), REG_FP.into(), 0); + } + + pub fn check_stack_pointer(&mut self, lbl_overflow: Label) {} + + pub fn safepoint(&mut self, lbl_safepoint: Label) {} + + pub fn fix_result(&mut self, _result: Reg, _mode: MachineMode) { + // nothing to do on RiscV, see version for x64 for more info. + } + + pub fn epilog(&mut self) { + self.asm.addi(REG_FP.into(), REG_SP.into(), 0); + self.asm.ld(REG_FP.into(), REG_SP.into(), 0); + self.asm.ld(REG_RA.into(), REG_SP.into(), 8); + self.asm.addi(REG_SP.into(), REG_SP.into(), 16); + } + + pub fn epilog_without_return(&mut self) {} + + pub fn increase_stack_frame(&mut self, size: i32) {} + + pub fn decrease_stack_frame(&mut self, size: i32) {} + + pub fn direct_call( + &mut self, + fct_id: FctDefinitionId, + ptr: Address, + type_params: SourceTypeArray, + ) { + } + + pub fn raw_call(&mut self, ptr: Address) {} + + pub fn virtual_call( + &mut self, + pos: Position, + vtable_index: u32, + self_index: u32, + lazy_compilation_site: LazyCompilationSite, + ) { + } + + pub fn load_array_elem(&mut self, mode: MachineMode, dest: AnyReg, array: Reg, index: Reg) {} + + pub fn load_string_elem(&mut self, mode: MachineMode, dest: AnyReg, array: Reg, index: Reg) {} + + pub fn set(&mut self, dest: Reg, op: CondCode) {} + + pub fn cmp_mem(&mut self, mode: MachineMode, mem: Mem, rhs: Reg) {} + + pub fn cmp_mem_imm(&mut self, mode: MachineMode, mem: Mem, imm: i32) {} + + pub fn cmp_reg(&mut self, mode: MachineMode, lhs: Reg, rhs: Reg) {} + + pub fn cmp_reg_imm(&mut self, mode: MachineMode, lhs: Reg, imm: i32) {} + + pub fn cmp_zero(&mut self, mode: MachineMode, lhs: Reg) { + self.asm.sltiu(lhs.into(), lhs.into(), 1) + } + + pub fn cmp_int(&mut self, mode: MachineMode, dest: Reg, lhs: Reg, rhs: Reg) {} + + pub fn test_and_jump_if(&mut self, cond: CondCode, reg: Reg, lbl: Label) {} + + pub fn jump_if(&mut self, cond: CondCode, target: Label) {} + + pub fn jump(&mut self, target: Label) {} + + pub fn jump_reg(&mut self, reg: Reg) {} + + pub fn int_div(&mut self, mode: MachineMode, dest: Reg, lhs: Reg, rhs: Reg, pos: Position) { + match mode { + MachineMode::Int64 => self.asm.div(dest.into(), lhs.into(), rhs.into()), + MachineMode::Int32 => self.asm.divw(dest.into(), lhs.into(), rhs.into()), + _ => unreachable!(), + } + } + + pub fn int_mod(&mut self, mode: MachineMode, dest: Reg, lhs: Reg, rhs: Reg, pos: Position) { + // todo: check semantics + match mode { + MachineMode::Int64 => self.asm.rem(dest.into(), lhs.into(), rhs.into()), + MachineMode::Int32 => self.asm.remw(dest.into(), lhs.into(), rhs.into()), + _ => unreachable!(), + } + } + + pub fn int_mul(&mut self, mode: MachineMode, dest: Reg, lhs: Reg, rhs: Reg) { + match mode { + MachineMode::Int64 => self.asm.mul(dest.into(), lhs.into(), rhs.into()), + MachineMode::Int32 => self.asm.mulw(dest.into(), lhs.into(), rhs.into()), + _ => unreachable!(), + } + } + + pub fn int_mul_checked( + &mut self, + mode: MachineMode, + dest: Reg, + lhs: Reg, + rhs: Reg, + pos: Position, + ) { + // fixme: implement check + match mode { + MachineMode::Int64 => self.asm.mul(dest.into(), lhs.into(), rhs.into()), + MachineMode::Int32 => self.asm.mulw(dest.into(), lhs.into(), rhs.into()), + _ => unreachable!(), + } + } + + pub fn int_add(&mut self, mode: MachineMode, dest: Reg, lhs: Reg, rhs: Reg) { + match mode { + MachineMode::Int64 => self.asm.add(dest.into(), lhs.into(), rhs.into()), + MachineMode::Int32 => self.asm.addw(dest.into(), lhs.into(), rhs.into()), + _ => unreachable!(), + } + } + + pub fn int_add_checked( + &mut self, + mode: MachineMode, + dest: Reg, + lhs: Reg, + rhs: Reg, + pos: Position, + ) { + // fixme: implement check + match mode { + MachineMode::Int64 => self.asm.add(dest.into(), lhs.into(), rhs.into()), + MachineMode::Int32 => self.asm.addw(dest.into(), lhs.into(), rhs.into()), + _ => unreachable!(), + } + } + + pub fn int_add_imm(&mut self, mode: MachineMode, dest: Reg, lhs: Reg, value: i64) { + match mode { + MachineMode::Int64 => self.asm.addi(dest.into(), lhs.into(), value as i32), + MachineMode::Int32 => self.asm.addiw(dest.into(), lhs.into(), value as i32), + _ => unreachable!(), + } + } + + pub fn int_sub(&mut self, mode: MachineMode, dest: Reg, lhs: Reg, rhs: Reg) { + match mode { + MachineMode::Int64 => self.asm.sub(dest.into(), lhs.into(), rhs.into()), + MachineMode::Int32 => self.asm.subw(dest.into(), lhs.into(), rhs.into()), + _ => unreachable!(), + } + } + + pub fn int_sub_checked( + &mut self, + mode: MachineMode, + dest: Reg, + lhs: Reg, + rhs: Reg, + pos: Position, + ) { + // fixme: implement check + match mode { + MachineMode::Int64 => self.asm.sub(dest.into(), lhs.into(), rhs.into()), + MachineMode::Int32 => self.asm.subw(dest.into(), lhs.into(), rhs.into()), + _ => unreachable!(), + } + } + + pub fn int_shl(&mut self, mode: MachineMode, dest: Reg, lhs: Reg, rhs: Reg) { + match mode { + MachineMode::Int64 => self.asm.sll(dest.into(), lhs.into(), rhs.into()), + MachineMode::Int32 => self.asm.sllw(dest.into(), lhs.into(), rhs.into()), + _ => unreachable!(), + } + } + + pub fn int_shr(&mut self, mode: MachineMode, dest: Reg, lhs: Reg, rhs: Reg) { + match mode { + MachineMode::Int64 => self.asm.srl(dest.into(), lhs.into(), rhs.into()), + MachineMode::Int32 => self.asm.srlw(dest.into(), lhs.into(), rhs.into()), + _ => unreachable!(), + } + } + + pub fn int_sar(&mut self, mode: MachineMode, dest: Reg, lhs: Reg, rhs: Reg) { + match mode { + MachineMode::Int64 => self.asm.sra(dest.into(), lhs.into(), rhs.into()), + MachineMode::Int32 => self.asm.sraw(dest.into(), lhs.into(), rhs.into()), + _ => unreachable!(), + } + } + + pub fn int_rol(&mut self, mode: MachineMode, dest: Reg, lhs: Reg, rhs: Reg) { + match mode { + // fixme: only use if bitmanip available + MachineMode::Int64 | MachineMode::Int32 => { + self.asm.rol(dest.into(), lhs.into(), rhs.into()) + } + _ => unreachable!(), + } + } + + pub fn int_ror(&mut self, mode: MachineMode, dest: Reg, lhs: Reg, rhs: Reg) { + match mode { + // fixme: only use if bitmanip available + MachineMode::Int64 | MachineMode::Int32 => { + self.asm.ror(dest.into(), lhs.into(), rhs.into()) + } + _ => unreachable!(), + } + } + + pub fn int_or(&mut self, mode: MachineMode, dest: Reg, lhs: Reg, rhs: Reg) { + match mode { + MachineMode::Int64 | MachineMode::Int32 => { + self.asm.or(dest.into(), lhs.into(), rhs.into()) + } + _ => unreachable!(), + } + } + + pub fn int_and(&mut self, mode: MachineMode, dest: Reg, lhs: Reg, rhs: Reg) { + match mode { + MachineMode::Int64 | MachineMode::Int32 => { + self.asm.and(dest.into(), lhs.into(), rhs.into()) + } + _ => unreachable!(), + } + } + + pub fn int_xor(&mut self, mode: MachineMode, dest: Reg, lhs: Reg, rhs: Reg) { + match mode { + MachineMode::Int64 | MachineMode::Int32 => { + self.asm.xor(dest.into(), lhs.into(), rhs.into()) + } + _ => unreachable!(), + } + } + + pub fn int_reverse_bits(&mut self, mode: MachineMode, dest: Reg, src: Reg) { + unimplemented!() + } + + pub fn int_reverse_bytes(&mut self, mode: MachineMode, dest: Reg, src: Reg) { + match mode { + // fixme: only use if bitmanip available + MachineMode::Int64 | MachineMode::Int32 => self.asm.rev8_64(dest.into(), src.into()), + _ => unreachable!(), + } + } + + pub fn count_bits(&mut self, mode: MachineMode, dest: Reg, src: Reg, count_one_bits: bool) { + if count_one_bits { + self.asm.xori(src.into(), src.into(), -1) + } + match mode { + // fixme: only use if bitmanip available + MachineMode::Int64 => self.asm.cpop(dest.into(), src.into()), + // fixme: only use if bitmanip available + MachineMode::Int32 => self.asm.cpopw(dest.into(), src.into()), + _ => unreachable!(), + } + } + + pub fn count_bits_leading( + &mut self, + mode: MachineMode, + dest: Reg, + src: Reg, + count_one_bits: bool, + ) { + if count_one_bits { + self.asm.xori(src.into(), src.into(), -1) + } + match mode { + MachineMode::Int64 => { + // fixme: only use if bitmanip available + self.asm.clz(dest.into(), src.into()) + } + MachineMode::Int32 => { + // fixme: only use if bitmanip available + self.asm.clzw(dest.into(), src.into()) + } + _ => unreachable!(), + } + } + + pub fn count_bits_trailing( + &mut self, + mode: MachineMode, + dest: Reg, + src: Reg, + count_one_bits: bool, + ) { + if count_one_bits { + self.asm.xori(src.into(), src.into(), -1) + } + match mode { + MachineMode::Int64 => { + // fixme: only use if bitmanip available + self.asm.ctz(dest.into(), src.into()) + } + MachineMode::Int32 => { + // fixme: only use if bitmanip available + self.asm.ctzw(dest.into(), src.into()) + } + _ => unreachable!(), + } + } + + pub fn int_to_float( + &mut self, + dest_mode: MachineMode, + dest: FReg, + src_mode: MachineMode, + src: Reg, + ) { + match (dest_mode, src_mode) { + (MachineMode::Float64, MachineMode::Int64) => { + self.asm + .fcvt_d_l(RoundingMode::RNE, dest.into(), src.into()) + } + (MachineMode::Float32, MachineMode::Int64) => { + self.asm + .fcvt_d_w(RoundingMode::RNE, dest.into(), src.into()) + } + (MachineMode::Float64, MachineMode::Int32) => { + self.asm + .fcvt_s_l(RoundingMode::RNE, dest.into(), src.into()) + } + (MachineMode::Float32, MachineMode::Int32) => { + self.asm + .fcvt_s_w(RoundingMode::RNE, dest.into(), src.into()) + } + _ => unreachable!(), + } + } + + pub fn float_to_int( + &mut self, + dest_mode: MachineMode, + dest: Reg, + src_mode: MachineMode, + src: FReg, + ) { + match (dest_mode, src_mode) { + (MachineMode::Float64, MachineMode::Int64) => { + self.asm + .fcvt_l_d(RoundingMode::RNE, dest.into(), src.into()) + } + (MachineMode::Float32, MachineMode::Int64) => { + self.asm + .fcvt_w_d(RoundingMode::RNE, dest.into(), src.into()) + } + (MachineMode::Float64, MachineMode::Int32) => { + self.asm + .fcvt_l_s(RoundingMode::RNE, dest.into(), src.into()) + } + (MachineMode::Float32, MachineMode::Int32) => { + self.asm + .fcvt_w_s(RoundingMode::RNE, dest.into(), src.into()) + } + _ => unreachable!(), + } + } + + pub fn float32_to_float64(&mut self, dest: FReg, src: FReg) { + self.asm + .fcvt_s_d(RoundingMode::RNE, dest.into(), src.into()) + } + + pub fn float64_to_float32(&mut self, dest: FReg, src: FReg) { + self.asm + .fcvt_d_s(RoundingMode::RNE, dest.into(), src.into()) + } + + pub fn int_as_float( + &mut self, + dest_mode: MachineMode, + dest: FReg, + src_mode: MachineMode, + src: Reg, + ) { + match (dest_mode, src_mode) { + (MachineMode::Float64, MachineMode::Int64) => self.asm.fmv_d_x(dest.into(), src.into()), + (MachineMode::Float32, MachineMode::Int32) => self.asm.fmv_w_x(dest.into(), src.into()), + _ => unreachable!(), + } + } + + pub fn float_as_int( + &mut self, + dest_mode: MachineMode, + dest: Reg, + src_mode: MachineMode, + src: FReg, + ) { + match (dest_mode, src_mode) { + (MachineMode::Int64, MachineMode::Float64) => self.asm.fmv_x_d(dest.into(), src.into()), + (MachineMode::Int32, MachineMode::Float32) => self.asm.fmv_x_w(dest.into(), src.into()), + _ => unreachable!(), + } + } + + pub fn float_add(&mut self, mode: MachineMode, dest: FReg, lhs: FReg, rhs: FReg) { + match mode { + MachineMode::Float64 => { + self.asm + .fadd_d(RoundingMode::RNE, dest.into(), lhs.into(), rhs.into()) + } + MachineMode::Float32 => { + self.asm + .fadd_s(RoundingMode::RNE, dest.into(), lhs.into(), rhs.into()) + } + _ => unreachable!(), + } + } + + pub fn float_sub(&mut self, mode: MachineMode, dest: FReg, lhs: FReg, rhs: FReg) { + match mode { + MachineMode::Float64 => { + self.asm + .fsub_d(RoundingMode::RNE, dest.into(), lhs.into(), rhs.into()) + } + MachineMode::Float32 => { + self.asm + .fsub_s(RoundingMode::RNE, dest.into(), lhs.into(), rhs.into()) + } + _ => unreachable!(), + } + } + + pub fn float_mul(&mut self, mode: MachineMode, dest: FReg, lhs: FReg, rhs: FReg) { + match mode { + MachineMode::Float64 => { + self.asm + .fmul_d(RoundingMode::RNE, dest.into(), lhs.into(), rhs.into()) + } + MachineMode::Float32 => { + self.asm + .fmul_s(RoundingMode::RNE, dest.into(), lhs.into(), rhs.into()) + } + _ => unreachable!(), + } + } + + pub fn float_div(&mut self, mode: MachineMode, dest: FReg, lhs: FReg, rhs: FReg) { + match mode { + MachineMode::Float64 => { + self.asm + .fdiv_d(RoundingMode::RNE, dest.into(), lhs.into(), rhs.into()) + } + MachineMode::Float32 => { + self.asm + .fdiv_s(RoundingMode::RNE, dest.into(), lhs.into(), rhs.into()) + } + _ => unreachable!(), + } + } + + pub fn float_abs(&mut self, mode: MachineMode, dest: FReg, src: FReg) { + match mode { + MachineMode::Float64 => self.asm.fsgnjx_d(dest.into(), src.into(), src.into()), + MachineMode::Float32 => self.asm.fsgnjx_s(dest.into(), src.into(), src.into()), + _ => unreachable!(), + } + } + + pub fn float_neg(&mut self, mode: MachineMode, dest: FReg, src: FReg) { + match mode { + MachineMode::Float64 => self.asm.fsgnjn_d(dest.into(), src.into(), src.into()), + MachineMode::Float32 => self.asm.fsgnjn_s(dest.into(), src.into(), src.into()), + _ => unreachable!(), + } + } + + pub fn float_round_tozero(&mut self, mode: MachineMode, dest: FReg, src: FReg) { + match mode { + MachineMode::Float64 => { + //self.asm + // .fround_d(RoundingMode::RNE, dest.into(), lhs.into(), rhs.into()) + } + MachineMode::Float32 => { + //self.asm + // .fround_s(RoundingMode::RNE, dest.into(), lhs.into(), rhs.into()) + } + _ => unreachable!(), + } + } + + pub fn float_round_up(&mut self, mode: MachineMode, dest: FReg, src: FReg) {} + + pub fn float_round_down(&mut self, mode: MachineMode, dest: FReg, src: FReg) {} + + pub fn float_round_halfeven(&mut self, mode: MachineMode, dest: FReg, src: FReg) {} + + pub fn float_sqrt(&mut self, mode: MachineMode, dest: FReg, src: FReg) { + match mode { + MachineMode::Float64 => self.asm.fsqrt_d(RoundingMode::RNE, dest.into(), src.into()), + MachineMode::Float32 => self.asm.fsqrt_s(RoundingMode::RNE, dest.into(), src.into()), + _ => unreachable!(), + } + } + + pub fn float_cmp_int(&mut self, mode: MachineMode, dest: Reg, lhs: FReg, rhs: FReg) {} + + pub fn float_cmp( + &mut self, + mode: MachineMode, + dest: Reg, + lhs: FReg, + rhs: FReg, + cond: CondCode, + ) { + } + + pub fn float_cmp_nan(&mut self, mode: MachineMode, dest: Reg, src: FReg) {} + + pub fn float_srt(&mut self, _mode: MachineMode, _dest: Reg, _lhs: FReg, _rhs: FReg) {} + + pub fn load_float_const(&mut self, mode: MachineMode, dest: FReg, imm: f64) {} + + pub fn determine_array_size( + &mut self, + dest: Reg, + length: Reg, + element_size: i32, + with_header: bool, + ) { + } + + pub fn array_address(&mut self, dest: Reg, obj: Reg, index: Reg, element_size: i32) {} + + pub fn check_index_out_of_bounds(&mut self, pos: Position, array: Reg, index: Reg) {} + + pub fn check_string_index_out_of_bounds(&mut self, pos: Position, array: Reg, index: Reg) {} + + pub fn load_nil(&mut self, dest: Reg) {} + + pub fn load_int32_synchronized(&mut self, dest: Reg, address: Reg) {} + + pub fn load_int64_synchronized(&mut self, dest: Reg, address: Reg) {} + + pub fn store_int32_synchronized(&mut self, dest: Reg, address: Reg) {} + + pub fn store_int64_synchronized(&mut self, dest: Reg, address: Reg) {} + + pub fn exchange_int32_synchronized(&mut self, old: Reg, new: Reg, address: Reg) {} + + pub fn exchange_int64_synchronized(&mut self, old: Reg, new: Reg, address: Reg) {} + + pub fn compare_exchange_int32_synchronized( + &mut self, + expected: Reg, + new: Reg, + address: Reg, + ) -> Reg { + panic!() + } + + pub fn compare_exchange_int64_synchronized( + &mut self, + expected: Reg, + new: Reg, + address: Reg, + ) -> Reg { + panic!() + } + + pub fn fetch_add_int32_synchronized(&mut self, previous: Reg, value: Reg, address: Reg) -> Reg { + panic!() + } + + pub fn fetch_add_int64_synchronized(&mut self, previous: Reg, value: Reg, address: Reg) -> Reg { + panic!() + } + + pub fn load_mem(&mut self, mode: MachineMode, dest: AnyReg, mem: Mem) {} + + fn common_load_base_with_offset( + &mut self, + mode: MachineMode, + dest: AnyReg, + base: Reg, + disp: i32, + ) { + } + + pub fn lea(&mut self, dest: Reg, mem: Mem) {} + + pub fn emit_barrier(&mut self, src: Reg, card_table_offset: usize) {} + + pub fn store_mem(&mut self, mode: MachineMode, mem: Mem, src: AnyReg) {} + + pub fn store_zero(&mut self, mode: MachineMode, mem: Mem) {} + + pub fn common_store_base_with_offset( + &mut self, + mode: MachineMode, + src: AnyReg, + base: Reg, + offset: i32, + ) { + } + + pub fn copy_reg(&mut self, mode: MachineMode, dest: Reg, src: Reg) {} + + pub fn copy_sp(&mut self, dest: Reg) {} + + pub fn set_sp(&mut self, src: Reg) {} + + pub fn copy_pc(&mut self, dest: Reg) {} + + pub fn copy_ra(&mut self, dest: Reg) {} + + pub fn copy_freg(&mut self, mode: MachineMode, dest: FReg, src: FReg) {} + + pub fn int32_to_int64(&mut self, dest: Reg, src: Reg) {} + + pub fn extend_byte(&mut self, mode: MachineMode, dest: Reg, src: Reg) {} + + pub fn load_constpool(&mut self, dest: Reg, disp: i32) {} + + pub fn call_reg(&mut self, reg: Reg) {} + + pub fn debug(&mut self) {} + + pub fn load_int_const(&mut self, mode: MachineMode, dest: Reg, imm: i64) {} + + pub fn load_true(&mut self, dest: Reg) { + self.asm.addi(dest.into(), REG_ZERO.into(), 1); + } + + pub fn load_false(&mut self, dest: Reg) { + self.asm.add(dest.into(), REG_ZERO.into(), REG_ZERO.into()) + } + + pub fn int_neg(&mut self, mode: MachineMode, dest: Reg, src: Reg) { + match mode { + MachineMode::Int64 => self.asm.sub(dest.into(), REG_ZERO.into(), src.into()), + MachineMode::Int32 => self.asm.subw(dest.into(), REG_ZERO.into(), src.into()), + _ => unreachable!(), + } + } + + pub fn int_not(&mut self, mode: MachineMode, dest: Reg, src: Reg) { + match mode { + MachineMode::Int64 => self.asm.xori(dest.into(), src.into(), -1), + MachineMode::Int32 => self.asm.xori(dest.into(), src.into(), -1), + _ => unreachable!(), + } + } + + pub fn bool_not(&mut self, dest: Reg, src: Reg) { + self.asm.xori(dest.into(), src.into(), 1); + } + + pub fn trap(&mut self, trap: Trap, pos: Position) {} + + pub fn nop(&mut self) { + self.asm.addi(REG_ZERO.into(), REG_ZERO.into(), 0) + } +} + +#[derive(Debug)] +pub struct ForwardJump { + at: usize, + to: Label, + ty: JumpType, +} + +#[derive(Debug)] +enum JumpType { + Jump, + JumpIf(CondCode), +} + +fn size_flag(mode: MachineMode) -> u32 { + match mode { + MachineMode::Int8 | MachineMode::Int32 => 0, + MachineMode::IntPtr | MachineMode::Ptr | MachineMode::Int64 => 1, + MachineMode::Float32 | MachineMode::Float64 => unimplemented!(), + } +} + +impl From for FloatRegister { + fn from(reg: FReg) -> FloatRegister { + FloatRegister::new(reg.0) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::mode::MachineMode::{Int32, Ptr}; + use byteorder::{LittleEndian, WriteBytesExt}; + + macro_rules! assert_emit { + ( + $($expr:expr),*; + $name:ident + ) => {{ + $name.finish(); + let expected: Vec = vec![$($expr,)*]; + let mut buffer: Vec = Vec::new(); + + for insn in expected { + buffer.write_u32::(insn).unwrap(); + } + + assert_eq!(buffer, $name.data()); + }}; + } + + #[test] + fn test_jump_forward() { + let mut masm = MacroAssembler::new(); + let lbl = masm.create_label(); + masm.jump(lbl); + masm.bind_label(lbl); + + assert_emit!(0x14000001; masm); + } + + #[test] + fn test_jump_if_forward() { + let mut masm = MacroAssembler::new(); + let lbl = masm.create_label(); + masm.jump_if(CondCode::Zero, lbl); + masm.bind_label(lbl); + + assert_emit!(0x54000020; masm); + } + + #[test] + fn test_jump_forward_with_gap() { + let mut masm = MacroAssembler::new(); + let lbl = masm.create_label(); + masm.jump(lbl); + masm.emit_u32(0); + masm.bind_label(lbl); + + assert_emit!(0x14000002, 0; masm); + } + + #[test] + fn test_jump_if_forward_with_gap() { + let mut masm = MacroAssembler::new(); + let lbl = masm.create_label(); + masm.jump_if(CondCode::NonZero, lbl); + masm.emit_u32(0); + masm.bind_label(lbl); + + assert_emit!(0x54000041, 0; masm); + } + + #[test] + fn test_jump_backward() { + let mut masm = MacroAssembler::new(); + let lbl = masm.create_label(); + masm.bind_label(lbl); + masm.jump(lbl); + + assert_emit!(0x14000000; masm); + } + + #[test] + fn test_jump_if_backward() { + let mut masm = MacroAssembler::new(); + let lbl = masm.create_label(); + masm.bind_label(lbl); + masm.jump_if(CondCode::Less, lbl); + + assert_emit!(0x5400000B; masm); + } + + #[test] + fn test_jump_backward_with_gap() { + let mut masm = MacroAssembler::new(); + let lbl = masm.create_label(); + masm.bind_label(lbl); + masm.emit_u32(0); + masm.jump(lbl); + + assert_emit!(0, 0x17FFFFFF; masm); + } + + #[test] + fn test_jump_if_backward_with_gap() { + let mut masm = MacroAssembler::new(); + let lbl = masm.create_label(); + masm.bind_label(lbl); + masm.emit_u32(0); + masm.jump_if(CondCode::LessEq, lbl); + + assert_emit!(0, 0x54FFFFED; masm); + } + + #[test] + fn test_load_int_const() { + let mut masm = MacroAssembler::new(); + masm.load_int_const(Int32, R0, 0); + assert_emit!(0x52800000; masm); + + let mut masm = MacroAssembler::new(); + masm.load_int_const(Int32, R0, 0xFFFF); + assert_emit!(0x529FFFE0; masm); + + let mut masm = MacroAssembler::new(); + masm.load_int_const(Int32, R0, 1i64 << 16); + assert_emit!(0x52a00020; masm); + + let mut masm = MacroAssembler::new(); + masm.load_int_const(Ptr, R0, 0); + assert_emit!(0xD2800000; masm); + + let mut masm = MacroAssembler::new(); + masm.load_int_const(Int32, R0, -1); + assert_emit!(0x12800000; masm); + + let mut masm = MacroAssembler::new(); + masm.load_int_const(Ptr, R0, -1); + assert_emit!(0x92800000; masm); + } + + #[test] + fn test_load_int_const_multiple_halfwords() { + let mut masm = MacroAssembler::new(); + masm.load_int_const(Int32, R0, 0x10001); + assert_emit!(0x52800020, 0x72a00020; masm); + + let mut masm = MacroAssembler::new(); + masm.load_int_const(Ptr, R0, !0x10001); + assert_emit!(0x92800020, 0xF2BFFFC0; masm); + } +}