diff --git a/.gitignore b/.gitignore index af1043f..92bc8d3 100644 --- a/.gitignore +++ b/.gitignore @@ -26,3 +26,5 @@ Cargo.lock # temporary files *.bin + +.vscode/ diff --git a/README.md b/README.md index 2ee8076..e14c446 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,14 @@ # binary-room + +[![Tests](https://github.com/Samir-Rashid/binary-room/actions/workflows/cargo_test.yml/badge.svg)](https://github.com/Samir-Rashid/binary-room/actions/workflows/cargo_test.yml) + Binary translator from RISC-V to ARM written in Rust + + +## Testing + +To get all the cross-compiling dependencies, enter the nix shell using `nix develop`. + +`cargo test -- --nocapture` + +Then run `./run.sh filename` to assemble and run the file. diff --git a/flake.nix b/flake.nix index 9633d6d..cdc6e88 100644 --- a/flake.nix +++ b/flake.nix @@ -1,3 +1,5 @@ +# dual cross compile toolchain +# https://github.com/noteed/riscv-hello-asm/blob/main/shell.nix { description = "binary-room"; @@ -35,6 +37,8 @@ ]; packages = with pkgs; [ rust-analyzer-nightly + clang-tools + hyperfine # benchmarking tool ]; }; } diff --git a/run.sh b/run.sh new file mode 100755 index 0000000..45ea602 --- /dev/null +++ b/run.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash + +ASM_FILE=$1 + + +aarch64-linux-gnu-as $ASM_FILE -o $ASM_FILE.as +aarch64-linux-gnu-ld $ASM_FILE.as -o $ASM_FILE.bin +./$ASM_FILE.bin +echo $? diff --git a/src/instruction.rs b/src/instruction.rs index 564e52a..67d8789 100644 --- a/src/instruction.rs +++ b/src/instruction.rs @@ -1,54 +1,839 @@ +use std::convert::Into; +use std::default; +use std::fmt::{format, write, Display}; + /// This file defines all the supported ARM and RISC-V instructions we support. /// We use `strum` to assist in serializing asm files to our [`Instruction`] enum. /// /// We do not aim for completness of translating every possible instruction, /// but we do want to thoroughly test for correctness. +/// +/// Some relevant references for making enums of instructions +/// https://github.com/lmcad-unicamp/riscv-sbt/blob/93bd48525362d00c6a2d7b320dc9cd9e62bc8fa9/sbt/Instruction.h#L62 +/// https://github.com/nbdd0121/r2vm/blob/5118be6b9e757c6fef2f019385873f403c23c548/lib/riscv/src/op.rs#L30 use strum_macros::EnumString; +pub enum RiscVSyscalls { + WRITE, + EXIT +} + +impl RiscVSyscalls { + const fn value(&self) -> i32 { + match self { + Self::WRITE => 64, + Self::EXIT => 0, + } + } +} + +#[derive(Debug, Default)] +pub enum RiscVWidth { + Word, + #[default] + Double, +} + +/// RISC-V Instructions +/// https://msyksphinz-self.github.io/riscv-isadoc/html/rvi.html +/// +/// To make a function call in RISC-V you use the `jal` (jump and link) +/// instruction. This would require us ensure that we translate the RISC-V +/// calling convention into ARM. (`https://riscv.org/wp-content/uploads/2024/12/riscv-calling.pdf) #[derive(Debug, EnumString)] -pub enum Instruction { - // RISC-V Instructions +pub enum RiscVInstruction { + /// add immediate + /// + /// `x[rd] = x[rs1] + sext(immediate)` #[strum(serialize = "addi")] - Addi, + Addi { + dest: RiscVRegister, + src: RiscVRegister, + imm: i32, + }, + /// add label/offset addr (not a real RISC-V instr) + Addl { + dest: RiscVRegister, + src: RiscVRegister, + label: RiscVVal + }, + /// add register + /// either add or addw + /// (addw is 32 bits on 64 bit riscv) + /// + /// `x[rd] = sext((x[rs1] + x[rs2])[31:0])` + #[strum(serialize = "addw")] + Add { + // dest = arg1 + arg2 + width: RiscVWidth, + dest: RiscVRegister, + arg1: RiscVRegister, + arg2: RiscVRegister, + }, + /// branch if less than or equal + #[strum(serialize = "call")] + Ble { + arg1: RiscVRegister, + arg2: RiscVRegister, + target: RiscVVal + }, + /// call label + #[strum(serialize = "call")] + Call { + label: RiscVVal + }, + /// Store values from register rs2 to memory. + /// + /// `M[x[rs1] + sext(offset)] = x[rs2]` #[strum(serialize = "sd")] - Sd, + S { + width: RiscVWidth, + src: RiscVRegister, + dest: RiscVVal, + }, + /// Loads a value from memory into register rd for RV64I. + /// + /// `x[rd] = M[x[rs1] + sext(offset)]` #[strum(serialize = "ld")] - Ld, - #[strum(serialize = "sw")] - Sw, - #[strum(serialize = "lw")] - Lw, + L { + width: RiscVWidth, + dest: RiscVRegister, + src: RiscVVal, + }, + Directive { + name: String, + operands: String + }, + Label { + name: String + }, + #[strum(serialize = "lui")] + Lui { + dest: RiscVRegister, + src: RiscVVal + }, + // Copy register + // `mv rd, rs1` expands to `addi rd, rs, 0` #[strum(serialize = "mv")] - Mv, - #[strum(serialize = "addw")] - Addw, + Mv { + dest: RiscVRegister, + src: RiscVRegister, + }, + // Copy immediate + // `mv rd, rs1` expands to `addi rd, rs, 0` + #[strum(serialize = "mvi")] + Mvi { + dest: RiscVRegister, + imm: i32, + }, + /// Sign extend Word + /// + /// psuedo instruction which translates to `addiw rd, rs, 0` #[strum(serialize = "sext.w")] - SextW, + SextW { + dest: RiscVRegister, + src: RiscVRegister, + }, + /// Jump label + #[strum(serialize = "j")] + J { target: RiscVVal }, + /// Jump Register + /// Jump to address and place return address in rd. + /// jal rd,offset + /// + /// Psuedo instruction: + /// jr offset => jal x1, offset + /// #[strum(serialize = "jr")] - Jr, + Jr { target: RiscVRegister }, + /// Load Immediate + /// This is a pseudo instruction, so it's not a real instruction + /// + /// Assembler Pseudo-instructions + /// 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://michaeljclark.github.io/asm.html #[strum(serialize = "li")] - Li, + Li { dest: RiscVRegister, imm: i32 }, + /// System Call + #[strum(serialize = "ecall")] + ECall, + #[strum(serialize = "verbatim")] + Verbatim { text: String }, +} + +impl Default for RiscVInstruction { + fn default() -> Self { + Self::Li { + dest: RiscVRegister::X0, + imm: 0, + } + } +} + +pub enum ArmSyscalls { + WRITE, + EXIT +} - // ARM Instructions +impl ArmSyscalls { + const fn value(&self) -> i32 { + match self { + Self::WRITE => 64, + Self::EXIT => 0, + } + } +} + +#[derive(Debug)] +pub enum ArmVal { + Reg(ArmRegister), + Imm(i32), + RegOffset(ArmRegister, i32), + LabelOffset(String, i32) +} + +impl Default for ArmVal { + fn default() -> Self { + todo!() + } +} + +#[derive(Debug, Copy, Clone)] +pub enum ArmWidth { + Byte, + SignedByte, + Half, + SignedHalf, + Word, + Double, +} + +impl Default for ArmWidth { + fn default() -> Self { + todo!() + } +} + +/// ARM Instructions +/// `https://iitd-plos.github.io/col718/ref/arm-instructionset.pdf#page=3` +#[derive(Debug, EnumString)] +pub enum ArmInstruction { + /// ADC Add with carry + /// + /// `Rd := Rn + Op2 + Carry` + #[strum(serialize = "adc")] + Adc, + /// ADD Add Rd := Rn + Op2 #[strum(serialize = "add")] - Add, - #[strum(serialize = "sub")] - Sub, - #[strum(serialize = "mov")] - Mov, - #[strum(serialize = "ldr")] - Ldr, - #[strum(serialize = "str")] - Str, + Add { + dest: ArmRegister, + arg1: ArmRegister, + arg2: ArmVal, + }, + /// AND AND Rd := Rn AND Op2 + #[strum(serialize = "and")] + And, + /// ADRP Rd := page_addr(label) + #[strum(serialize = "adrp")] + Adrp { + dest: ArmRegister, + label: ArmVal + }, + /// B Branch R15 := address #[strum(serialize = "b")] - B, + B { target: ArmVal }, + /// BLR Xn + #[strum(serialize = "blr")] + Blr { target: ArmRegisterName }, + /// BLE label + #[strum(serialize = "ble")] + Ble { arg1: ArmRegister, arg2: ArmRegister, target: ArmVal }, + /// BL label #[strum(serialize = "bl")] - Bl, - #[strum(serialize = "bx")] - Bx, - #[strum(serialize = "cmp")] - Cmp, - #[strum(serialize = "beq")] - Beq, + Bl {target: ArmVal}, + /// label: + Label { name: String }, + /// .directive operands + Directive { + name: String, + operands: String + }, + #[strum(serialize = "ldr")] + Ldr { + width: ArmWidth, + dest: ArmRegister, + src: ArmVal, + }, + #[strum(serialize = "mov")] + Mov { + width: ArmWidth, + dest: ArmRegister, + src: ArmVal + }, + #[strum(serialize = "ret")] + Ret, + /// Str [r2 + offset] = r1 + #[strum(serialize = "str")] + Str { + width: ArmWidth, + src: ArmRegister, + dest: ArmVal, + }, + /// Sub Sub Rd := Rn - Op2 + #[strum(serialize = "sub")] + Sub { + dest: ArmRegister, + arg1: ArmRegister, + arg2: ArmVal, + }, + /// sign extend to word + #[strum(serialize = "sxtw")] + Sxtw { dest: ArmRegister, src: ArmRegister }, + /// service call + #[strum(serialize = "svc")] + Svc { id: i32 }, + /// compare + Cmp { op1: ArmRegister, op2: ArmVal }, + Verbatim { text: String }, +} + +impl Default for ArmInstruction { + fn default() -> Self { + ArmInstruction::Mov { + width: ArmWidth::Double, + dest: ArmRegister { width: ArmWidth::Double, name: ArmRegisterName::X0 }, + src: ArmVal::Reg(ArmRegister { width: ArmWidth::Double, name: ArmRegisterName::X0 }) + } + } +} + +#[derive(Debug)] +pub enum RiscVVal { + RiscVRegister(RiscVRegister), + Immediate(i32), + /// This is for arguments to opcodes which have an offset + Offset { + register: RiscVRegister, + offset: i32, + }, + LabelOffset { + label: String, + offset: i32 + } +} + +impl Default for RiscVVal { + fn default() -> Self { + Self::Immediate(0) + } +} + +/// RISC-V Registers +/// https://msyksphinz-self.github.io/riscv-isadoc/html/regs.html +#[derive(Debug, EnumString, Default)] +pub enum RiscVRegister { + #[default] + #[strum(serialize = "x0")] + /// Hard-wired zero + X0, + #[strum(serialize = "ra")] + /// Return address + RA, + #[strum(serialize = "sp")] + /// Stack pointer + SP, + #[strum(serialize = "gp")] + /// Global pointer + GP, + #[strum(serialize = "tp")] + /// Thread pointer + TP, + #[strum(serialize = "t0")] + /// Temporary/alternate link register + T0, + #[strum(serialize = "t1")] + /// Temporaries + T1, + #[strum(serialize = "t2")] + /// Temporaries + T2, + #[strum(serialize = "s0", serialize = "fp")] + /// Saved register/frame pointer R29 + S0FP, + #[strum(serialize = "s1")] + /// Saved registers + S1, + #[strum(serialize = "a0")] + /// Function arguments/return values + A0, + #[strum(serialize = "a1")] + /// Function arguments/return values + A1, + #[strum(serialize = "a2")] + /// Function arguments + A2, + #[strum(serialize = "a3")] + /// Function arguments + A3, + #[strum(serialize = "a4")] + /// Function arguments + A4, + #[strum(serialize = "a5")] + /// Function arguments + A5, + #[strum(serialize = "a6")] + /// Function arguments + A6, + #[strum(serialize = "a7")] + /// Function arguments + A7, + #[strum(serialize = "s2")] + /// Saved registers + S2, + #[strum(serialize = "s3")] + /// Saved registers + S3, + #[strum(serialize = "s4")] + /// Saved registers + S4, + #[strum(serialize = "s5")] + /// Saved registers + S5, + #[strum(serialize = "s6")] + /// Saved registers + S6, + #[strum(serialize = "s7")] + /// Saved registers + S7, + #[strum(serialize = "s8")] + /// Saved registers + S8, + #[strum(serialize = "s9")] + /// Saved registers + S9, + #[strum(serialize = "s10")] + /// Saved registers + S10, + #[strum(serialize = "s11")] + /// Saved registers + S11, + #[strum(serialize = "t3")] + /// Temporaries + T3, + #[strum(serialize = "t4")] + /// Temporaries + T4, + #[strum(serialize = "t5")] + /// Temporaries + T5, + #[strum(serialize = "t6")] + /// Temporaries + T6, +} + +#[derive(Debug, Copy, Clone)] +pub struct ArmRegister { + pub width: ArmWidth, + pub name: ArmRegisterName, +} + +impl Default for ArmRegister { + fn default() -> Self { + todo!() + } } +/// ARM Registers +/// https://developer.arm.com/documentation/dui0056/d/using-the-procedure-call-standard/register-roles-and-names/register-names +/// Image of instructions https://duetorun.com/blog/arm/images/AArch64-registers.png +/// - https://duetorun.com/blog/20230601/a64-regs/#user_program_registers +#[derive(Debug, EnumString, Copy, Clone)] +pub enum ArmRegisterName { + #[strum(serialize = "wzr", serialize = "xzr")] + /// Zero register. Hardware special. + Zero, + #[strum(serialize = "pc")] + /// Program counter. Hardware special register. + Pc, + #[strum(serialize = "sp")] + /// Stack pointer. Hardware special register. + Sp, + #[strum(serialize = "lr")] + /// Link register. X30. Hardware special register. + Lr, + // Parameter passing and/or scratch registers (volatile) + X0, + X1, + X2, + X3, + X4, + X5, + X6, + X7, + // Caller-Saved scratch registers (volatile) + /// XR + X8, + X9, + X10, + X11, + X12, + X13, + X14, + X15, + /// IP0 + X16, + /// IP1 + X17, + /// PR + X18, + // Caller-Saved registers (non-volatile) + X19, + X20, + X21, + X22, + X23, + X24, + X25, + X26, + X27, + X28, + /// FP + X29, +} + +impl Default for ArmRegisterName { + fn default() -> Self { + todo!() + } +} + +/// Parse a text file into our enum. +pub fn parse_asm(asm: &str) -> Vec { + asm.lines() + .filter_map(|line| { + // TODO (Samir): Not sure that this will handle assembly labels + // We probably need to construct a map for those to find the + // original instruction they map to. + let parts: Vec<&str> = line.split_whitespace().collect(); + if parts.is_empty() { + None + } else { + // RiscVInstruction::from_str(parts[0]).ok() + todo!() + } + }) + .collect() +} + +impl Into for ArmInstruction { + fn into(self) -> String { + match self { + ArmInstruction::Adc => todo!(), + ArmInstruction::Add { dest, arg1, arg2 } => { + format!("add {}, {}, {}", dest, arg1, arg2) + }, + ArmInstruction::And => todo!(), + ArmInstruction::Adrp { dest, label } => { + format!("adrp {}, {}", dest, label) + } + ArmInstruction::B { target } => { + format!("b {}", target) + }, + ArmInstruction::Ble { arg1, arg2, target } => { + format!("cmp {}, {}\nble {}", arg1, arg2, target) + }, + ArmInstruction::Blr { target } => { + format!("blr {}", Into::::into(target)) + }, + ArmInstruction::Ldr { width, dest, src } => { + match width { + ArmWidth::Word | ArmWidth::Double => format!("ldr {}, {}", dest, src), + _ => todo!() + } + }, + ArmInstruction::Mov { width, dest, src } => { + format!("mov {}, {}", dest, src) + }, + ArmInstruction::Ret => todo!(), + ArmInstruction::Str { width, src, dest } => { + match width { + ArmWidth::Word => format!("str {}, {}", src, dest), + ArmWidth::Double => format!("str {}, {}", src, dest), + _ => todo!("{:?}", width) + } + }, + ArmInstruction::Sub { dest, arg1, arg2 } => + { + format!("sub {}, {}, {}", dest, arg1, arg2) + }, + ArmInstruction::Sxtw { dest, src } => { + format!("sxtw {}, {}", dest, src) + }, + ArmInstruction::Bl { target } => { + format!("bl {}", target) + }, + ArmInstruction::Label { name } => { + format!("{}:", name) + }, + ArmInstruction::Directive { name, operands } => { + format!(".{} {}", name, operands) + } + ArmInstruction::Svc { id } => { + format!("svc {}", id) + }, + ArmInstruction::Cmp { op1, op2 } => { + format!("cmp {}, {}", op1, op2) + } + ArmInstruction::Verbatim { text } => text + } + } +} + + +impl Into for ArmRegister { + fn into(self) -> String { + let s: &str = match (self.name, self.width) { + (ArmRegisterName::Zero, ArmWidth::Word) => "wzr", + (ArmRegisterName::Zero, ArmWidth::Double) => "xzr", + (ArmRegisterName::Zero, _) => panic!("invalid width for zero register"), + (ArmRegisterName::Pc, ArmWidth::Byte) => todo!(), + (ArmRegisterName::Pc, ArmWidth::SignedByte) => todo!(), + (ArmRegisterName::Pc, ArmWidth::Half) => todo!(), + (ArmRegisterName::Pc, ArmWidth::SignedHalf) => todo!(), + (ArmRegisterName::Pc, ArmWidth::Word) => todo!(), + (ArmRegisterName::Pc, ArmWidth::Double) => todo!(), + (ArmRegisterName::Sp, ArmWidth::Word) => "wsp", + (ArmRegisterName::Sp, ArmWidth::Double) => "sp", + (ArmRegisterName::Sp, _) => todo!(), + (ArmRegisterName::Lr, ArmWidth::Byte) => todo!(), + (ArmRegisterName::Lr, ArmWidth::SignedByte) => todo!(), + (ArmRegisterName::Lr, ArmWidth::Half) => todo!(), + (ArmRegisterName::Lr, ArmWidth::SignedHalf) => todo!(), + (ArmRegisterName::Lr, ArmWidth::Word) => todo!(), + (ArmRegisterName::Lr, ArmWidth::Double) => "lr", + (ArmRegisterName::X0, ArmWidth::Byte) => todo!(), + (ArmRegisterName::X0, ArmWidth::SignedByte) => todo!(), + (ArmRegisterName::X0, ArmWidth::Half) => todo!(), + (ArmRegisterName::X0, ArmWidth::SignedHalf) => todo!(), + (ArmRegisterName::X0, ArmWidth::Word) => "w0", + (ArmRegisterName::X0, ArmWidth::Double) => "x0", + (ArmRegisterName::X1, ArmWidth::Byte) => todo!(), + (ArmRegisterName::X1, ArmWidth::SignedByte) => todo!(), + (ArmRegisterName::X1, ArmWidth::Half) => todo!(), + (ArmRegisterName::X1, ArmWidth::SignedHalf) => todo!(), + (ArmRegisterName::X1, ArmWidth::Word) => "w1", + (ArmRegisterName::X1, ArmWidth::Double) => "x1", + (ArmRegisterName::X2, ArmWidth::Byte) => todo!(), + (ArmRegisterName::X2, ArmWidth::SignedByte) => todo!(), + (ArmRegisterName::X2, ArmWidth::Half) => todo!(), + (ArmRegisterName::X2, ArmWidth::SignedHalf) => todo!(), + (ArmRegisterName::X2, ArmWidth::Word) => "w2", + (ArmRegisterName::X2, ArmWidth::Double) => "x2", + (ArmRegisterName::X3, ArmWidth::Byte) => todo!(), + (ArmRegisterName::X3, ArmWidth::SignedByte) => todo!(), + (ArmRegisterName::X3, ArmWidth::Half) => todo!(), + (ArmRegisterName::X3, ArmWidth::SignedHalf) => todo!(), + (ArmRegisterName::X3, ArmWidth::Word) => "w3", + (ArmRegisterName::X3, ArmWidth::Double) => "x3", + (ArmRegisterName::X4, ArmWidth::Byte) => todo!(), + (ArmRegisterName::X4, ArmWidth::SignedByte) => todo!(), + (ArmRegisterName::X4, ArmWidth::Half) => todo!(), + (ArmRegisterName::X4, ArmWidth::SignedHalf) => todo!(), + (ArmRegisterName::X4, ArmWidth::Word) => "w4", + (ArmRegisterName::X4, ArmWidth::Double) => "x4", + (ArmRegisterName::X5, ArmWidth::Byte) => todo!(), + (ArmRegisterName::X5, ArmWidth::SignedByte) => todo!(), + (ArmRegisterName::X5, ArmWidth::Half) => todo!(), + (ArmRegisterName::X5, ArmWidth::SignedHalf) => todo!(), + (ArmRegisterName::X5, ArmWidth::Word) => "w5", + (ArmRegisterName::X5, ArmWidth::Double) => "x5", + (ArmRegisterName::X6, ArmWidth::Byte) => todo!(), + (ArmRegisterName::X6, ArmWidth::SignedByte) => todo!(), + (ArmRegisterName::X6, ArmWidth::Half) => todo!(), + (ArmRegisterName::X6, ArmWidth::SignedHalf) => todo!(), + (ArmRegisterName::X6, ArmWidth::Word) => todo!(), + (ArmRegisterName::X6, ArmWidth::Double) => todo!(), + (ArmRegisterName::X7, ArmWidth::Byte) => todo!(), + (ArmRegisterName::X7, ArmWidth::SignedByte) => todo!(), + (ArmRegisterName::X7, ArmWidth::Half) => todo!(), + (ArmRegisterName::X7, ArmWidth::SignedHalf) => todo!(), + (ArmRegisterName::X7, ArmWidth::Word) => "w7", + (ArmRegisterName::X7, ArmWidth::Double) => "x7", + (ArmRegisterName::X8, ArmWidth::Byte) => todo!(), + (ArmRegisterName::X8, ArmWidth::SignedByte) => todo!(), + (ArmRegisterName::X8, ArmWidth::Half) => todo!(), + (ArmRegisterName::X8, ArmWidth::SignedHalf) => todo!(), + (ArmRegisterName::X8, ArmWidth::Word) => todo!(), + (ArmRegisterName::X8, ArmWidth::Double) => "x8", + (ArmRegisterName::X9, ArmWidth::Byte) => todo!(), + (ArmRegisterName::X9, ArmWidth::SignedByte) => todo!(), + (ArmRegisterName::X9, ArmWidth::Half) => todo!(), + (ArmRegisterName::X9, ArmWidth::SignedHalf) => todo!(), + (ArmRegisterName::X9, ArmWidth::Word) => todo!(), + (ArmRegisterName::X9, ArmWidth::Double) => todo!(), + (ArmRegisterName::X10, ArmWidth::Byte) => todo!(), + (ArmRegisterName::X10, ArmWidth::SignedByte) => todo!(), + (ArmRegisterName::X10, ArmWidth::Half) => todo!(), + (ArmRegisterName::X10, ArmWidth::SignedHalf) => todo!(), + (ArmRegisterName::X10, ArmWidth::Word) => todo!(), + (ArmRegisterName::X10, ArmWidth::Double) => todo!(), + (ArmRegisterName::X11, ArmWidth::Byte) => todo!(), + (ArmRegisterName::X11, ArmWidth::SignedByte) => todo!(), + (ArmRegisterName::X11, ArmWidth::Half) => todo!(), + (ArmRegisterName::X11, ArmWidth::SignedHalf) => todo!(), + (ArmRegisterName::X11, ArmWidth::Word) => "w11", + (ArmRegisterName::X11, ArmWidth::Double) => "x11", + (ArmRegisterName::X12, ArmWidth::Byte) => todo!(), + (ArmRegisterName::X12, ArmWidth::SignedByte) => todo!(), + (ArmRegisterName::X12, ArmWidth::Half) => todo!(), + (ArmRegisterName::X12, ArmWidth::SignedHalf) => todo!(), + (ArmRegisterName::X12, ArmWidth::Word) => "w12", + (ArmRegisterName::X12, ArmWidth::Double) => "x12", + (ArmRegisterName::X13, ArmWidth::Byte) => todo!(), + (ArmRegisterName::X13, ArmWidth::SignedByte) => todo!(), + (ArmRegisterName::X13, ArmWidth::Half) => todo!(), + (ArmRegisterName::X13, ArmWidth::SignedHalf) => todo!(), + (ArmRegisterName::X13, ArmWidth::Word) => todo!(), + (ArmRegisterName::X13, ArmWidth::Double) => todo!(), + (ArmRegisterName::X14, ArmWidth::Byte) => todo!(), + (ArmRegisterName::X14, ArmWidth::SignedByte) => todo!(), + (ArmRegisterName::X14, ArmWidth::Half) => todo!(), + (ArmRegisterName::X14, ArmWidth::SignedHalf) => todo!(), + (ArmRegisterName::X14, ArmWidth::Word) => todo!(), + (ArmRegisterName::X14, ArmWidth::Double) => todo!(), + (ArmRegisterName::X15, ArmWidth::Byte) => todo!(), + (ArmRegisterName::X15, ArmWidth::SignedByte) => todo!(), + (ArmRegisterName::X15, ArmWidth::Half) => todo!(), + (ArmRegisterName::X15, ArmWidth::SignedHalf) => todo!(), + (ArmRegisterName::X15, ArmWidth::Word) => todo!(), + (ArmRegisterName::X15, ArmWidth::Double) => todo!(), + (ArmRegisterName::X16, ArmWidth::Byte) => todo!(), + (ArmRegisterName::X16, ArmWidth::SignedByte) => todo!(), + (ArmRegisterName::X16, ArmWidth::Half) => todo!(), + (ArmRegisterName::X16, ArmWidth::SignedHalf) => todo!(), + (ArmRegisterName::X16, ArmWidth::Word) => todo!(), + (ArmRegisterName::X16, ArmWidth::Double) => todo!(), + (ArmRegisterName::X17, ArmWidth::Byte) => todo!(), + (ArmRegisterName::X17, ArmWidth::SignedByte) => todo!(), + (ArmRegisterName::X17, ArmWidth::Half) => todo!(), + (ArmRegisterName::X17, ArmWidth::SignedHalf) => todo!(), + (ArmRegisterName::X17, ArmWidth::Word) => todo!(), + (ArmRegisterName::X17, ArmWidth::Double) => todo!(), + (ArmRegisterName::X18, ArmWidth::Byte) => todo!(), + (ArmRegisterName::X18, ArmWidth::SignedByte) => todo!(), + (ArmRegisterName::X18, ArmWidth::Half) => todo!(), + (ArmRegisterName::X18, ArmWidth::SignedHalf) => todo!(), + (ArmRegisterName::X18, ArmWidth::Word) => todo!(), + (ArmRegisterName::X18, ArmWidth::Double) => todo!(), + (ArmRegisterName::X19, ArmWidth::Byte) => todo!(), + (ArmRegisterName::X19, ArmWidth::SignedByte) => todo!(), + (ArmRegisterName::X19, ArmWidth::Half) => todo!(), + (ArmRegisterName::X19, ArmWidth::SignedHalf) => todo!(), + (ArmRegisterName::X19, ArmWidth::Word) => todo!(), + (ArmRegisterName::X19, ArmWidth::Double) => todo!(), + (ArmRegisterName::X20, ArmWidth::Byte) => todo!(), + (ArmRegisterName::X20, ArmWidth::SignedByte) => todo!(), + (ArmRegisterName::X20, ArmWidth::Half) => todo!(), + (ArmRegisterName::X20, ArmWidth::SignedHalf) => todo!(), + (ArmRegisterName::X20, ArmWidth::Word) => todo!(), + (ArmRegisterName::X20, ArmWidth::Double) => todo!(), + (ArmRegisterName::X21, ArmWidth::Byte) => todo!(), + (ArmRegisterName::X21, ArmWidth::SignedByte) => todo!(), + (ArmRegisterName::X21, ArmWidth::Half) => todo!(), + (ArmRegisterName::X21, ArmWidth::SignedHalf) => todo!(), + (ArmRegisterName::X21, ArmWidth::Word) => todo!(), + (ArmRegisterName::X21, ArmWidth::Double) => todo!(), + (ArmRegisterName::X22, ArmWidth::Byte) => todo!(), + (ArmRegisterName::X22, ArmWidth::SignedByte) => todo!(), + (ArmRegisterName::X22, ArmWidth::Half) => todo!(), + (ArmRegisterName::X22, ArmWidth::SignedHalf) => todo!(), + (ArmRegisterName::X22, ArmWidth::Word) => todo!(), + (ArmRegisterName::X22, ArmWidth::Double) => todo!(), + (ArmRegisterName::X23, ArmWidth::Byte) => todo!(), + (ArmRegisterName::X23, ArmWidth::SignedByte) => todo!(), + (ArmRegisterName::X23, ArmWidth::Half) => todo!(), + (ArmRegisterName::X23, ArmWidth::SignedHalf) => todo!(), + (ArmRegisterName::X23, ArmWidth::Word) => todo!(), + (ArmRegisterName::X23, ArmWidth::Double) => todo!(), + (ArmRegisterName::X24, ArmWidth::Byte) => todo!(), + (ArmRegisterName::X24, ArmWidth::SignedByte) => todo!(), + (ArmRegisterName::X24, ArmWidth::Half) => todo!(), + (ArmRegisterName::X24, ArmWidth::SignedHalf) => todo!(), + (ArmRegisterName::X24, ArmWidth::Word) => todo!(), + (ArmRegisterName::X24, ArmWidth::Double) => todo!(), + (ArmRegisterName::X25, ArmWidth::Byte) => todo!(), + (ArmRegisterName::X25, ArmWidth::SignedByte) => todo!(), + (ArmRegisterName::X25, ArmWidth::Half) => todo!(), + (ArmRegisterName::X25, ArmWidth::SignedHalf) => todo!(), + (ArmRegisterName::X25, ArmWidth::Word) => todo!(), + (ArmRegisterName::X25, ArmWidth::Double) => todo!(), + (ArmRegisterName::X26, ArmWidth::Byte) => todo!(), + (ArmRegisterName::X26, ArmWidth::SignedByte) => todo!(), + (ArmRegisterName::X26, ArmWidth::Half) => todo!(), + (ArmRegisterName::X26, ArmWidth::SignedHalf) => todo!(), + (ArmRegisterName::X26, ArmWidth::Word) => todo!(), + (ArmRegisterName::X26, ArmWidth::Double) => todo!(), + (ArmRegisterName::X27, ArmWidth::Byte) => todo!(), + (ArmRegisterName::X27, ArmWidth::SignedByte) => todo!(), + (ArmRegisterName::X27, ArmWidth::Half) => todo!(), + (ArmRegisterName::X27, ArmWidth::SignedHalf) => todo!(), + (ArmRegisterName::X27, ArmWidth::Word) => todo!(), + (ArmRegisterName::X27, ArmWidth::Double) => todo!(), + (ArmRegisterName::X28, ArmWidth::Byte) => todo!(), + (ArmRegisterName::X28, ArmWidth::SignedByte) => todo!(), + (ArmRegisterName::X28, ArmWidth::Half) => todo!(), + (ArmRegisterName::X28, ArmWidth::SignedHalf) => todo!(), + (ArmRegisterName::X28, ArmWidth::Word) => todo!(), + (ArmRegisterName::X28, ArmWidth::Double) => todo!(), + (ArmRegisterName::X29, ArmWidth::Byte) => todo!(), + (ArmRegisterName::X29, ArmWidth::SignedByte) => todo!(), + (ArmRegisterName::X29, ArmWidth::Half) => todo!(), + (ArmRegisterName::X29, ArmWidth::SignedHalf) => todo!(), + (ArmRegisterName::X29, ArmWidth::Word) => "w29", + (ArmRegisterName::X29, ArmWidth::Double) => "x29", + }; + s.to_string() + } +} + +impl Display for ArmRegister { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let x: String = self.clone().into(); + write!(f, "{}", x) + // let s: String = self.into(); + // write!(f, "{}", s) + } +} + +impl Display for ArmVal { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + ArmVal::Reg(arm_register) => arm_register.fmt(f), + ArmVal::Imm(x) => write!(f, "{}", x), + ArmVal::RegOffset(arm_register, offset) => { + let double_reg = ArmRegister { + name: arm_register.name, + width: ArmWidth::Double + }; + write!(f, "[{}, {}]", double_reg, offset) + }, + ArmVal::LabelOffset(name, offset) => { + match offset { + 0 => write!(f, "{}", name), + 9998 => write!(f, "{}", name), // %hi in riscv is adrp with no offset in arm + 9999 => write!(f, ":lo12:{}", name), // reserved for 12 low bits of label addr + _ => write!(f, "[{}, {}]", name, offset) + } + } + } + } +} + +impl Into for ArmRegisterName { + fn into(self) -> ArmRegister { + ArmRegister { width: ArmWidth::Double, name: self } + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..1f3c912 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,3 @@ +pub mod instruction; +pub mod translate; +pub mod utils; diff --git a/src/main.rs b/src/main.rs index 87c6374..f2648a5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,35 +1,10 @@ use std::fs; use std::str::FromStr; -mod instruction; -use instruction::Instruction; - -/// Parse a text file into our enum. -fn parse_asm(asm: &str) -> Vec { - asm.lines() - .filter_map(|line| { - // TODO (Samir): Not sure that this will handle assembly labels - // We probably need to construct a map for those to find the - // original instruction they map to. - let parts: Vec<&str> = line.split_whitespace().collect(); - if parts.is_empty() { - None - } else { - Instruction::from_str(parts[0]).ok() - } - }) - .collect() -} - -/// Runs binary translation -/// text file -> [`Instruction`] enum array -> text file -fn binary_translate(riscv_asm: &str) -> String { - let instructions = parse_asm(riscv_asm); - instructions - .into_iter() - .map(|instr| format!("{:?}", instr)) - .collect::>() - .join("\n") -} +pub mod instruction; +pub mod translate; +pub mod utils; +use instruction::RiscVInstruction; +use translate::binary_translate; // Samir: I am using main for testing, but it not needed since you can run // `cargo test` instead. diff --git a/src/translate.rs b/src/translate.rs new file mode 100644 index 0000000..cd62f02 --- /dev/null +++ b/src/translate.rs @@ -0,0 +1,272 @@ +use core::panic; + +use crate::instruction::{ + parse_asm, ArmInstruction, ArmRegister, ArmRegisterName, ArmVal, ArmWidth, RiscVInstruction, + RiscVRegister, RiscVVal, RiscVWidth, +}; + +macro_rules! sorry { + () => { + todo!() + }; +} + +/// Run the core logic to match from RISC-V to ARM Instructions. + +/// Translate one instruction at a time. +pub fn translate(riscv_instr: RiscVInstruction) -> Vec { + match riscv_instr { + RiscVInstruction::Addi { dest, src, imm } => { + if let RiscVRegister::X0 = src { + return translate(RiscVInstruction::Mvi { dest, imm }); + } + + let width = RiscVWidth::Double; + if imm >= 0 { + vec![ArmInstruction::Add { + dest: map_register(dest, &width), + arg1: map_register(src, &width), + arg2: ArmVal::Imm(imm), + }] + } else { + vec![ArmInstruction::Sub { + dest: map_register(dest, &width), + arg1: map_register(src, &width), + arg2: ArmVal::Imm(imm.abs()), + }] + } + }, + RiscVInstruction::Ble { arg1, arg2, target } => vec![{ + let width = RiscVWidth::Double; + ArmInstruction::Ble { + arg1: map_register(arg1, &width), + arg2: map_register(arg2, &width), + target: map_val(target, &width) + } + }], + RiscVInstruction::J { target } => vec![ArmInstruction::B { + target: map_val(target, &RiscVWidth::Double) + }], + RiscVInstruction::S { width, src, dest } => vec![ArmInstruction::Str { + width: map_width(&width), + src: map_register(src, &width), + dest: map_val(dest, &width), + }], + RiscVInstruction::L { width, dest, src } => vec![ArmInstruction::Ldr { + width: map_width(&width), + dest: map_register(dest, &width), + src: map_val(src, &width), + }], + RiscVInstruction::Directive { name, operands } => { + let arm_operands = operands.replace("@", "%"); + vec![ArmInstruction::Directive { name, operands: arm_operands }] + } + RiscVInstruction::Label { name } => vec![ArmInstruction::Label { name }], + RiscVInstruction::Mv { dest, src } => { + let width = RiscVWidth::Double; + vec![ArmInstruction::Add { + dest: map_register(dest, &width), + arg1: map_register(src, &width), + arg2: ArmVal::Imm(0), + }] + }, + RiscVInstruction::Mvi { dest, imm } => { + let width = RiscVWidth::Double; + vec![ArmInstruction::Mov { + width: map_width(&width), + dest: map_register(dest, &width), + src: ArmVal::Imm(imm) + }] + }, + RiscVInstruction::Add { + width, + dest, + arg1, + arg2, + } => match width { + RiscVWidth::Word => vec![ArmInstruction::Add { + dest: ArmRegister { + width: ArmWidth::Word, + name: map_register_name(dest), + }, + arg1: ArmRegister { + width: ArmWidth::Word, + name: map_register_name(arg1), + }, + arg2: ArmVal::Reg(ArmRegister { + width: ArmWidth::Word, + name: map_register_name(arg2), + }), + }], + RiscVWidth::Double => sorry!(), + }, + RiscVInstruction::SextW { dest, src } => vec![ArmInstruction::Sxtw { + dest: ArmRegister { + width: ArmWidth::Double, + name: map_register_name(dest), + }, + src: ArmRegister { + width: ArmWidth::Word, + name: map_register_name(src), + }, + }], + RiscVInstruction::Jr { target } => vec![ArmInstruction::Blr { + target: map_register_name(target), + }], + RiscVInstruction::Li { dest, imm } => { + if imm > 4095 || imm < 0 { + panic!("Li with imm out of range"); + } + + let width = RiscVWidth::Double; + vec![ArmInstruction::Mov { + width: map_width(&width), + dest: map_register(dest, &width), + src: ArmVal::Imm(imm), + }] + // ArmInstruction::Add { + // dest: map_register(dest, &RiscVWidth::Double), + // arg1: ArmRegister { + // width: ArmWidth::Double, + // name: ArmRegisterName::Zero, + // }, + // arg2: ArmVal::Imm(imm), + // } + }, + RiscVInstruction::Addl { dest, src, label } => { + let width = RiscVWidth::Double; + vec![ArmInstruction::Add { + dest: map_register(dest, &width), + arg1: map_register(src, &width), + arg2: map_val(label, &width), + }] + }, + RiscVInstruction::Lui { dest, src } => { + // only used to load upper bits or adrp in arm + let width = RiscVWidth::Double; + vec![ArmInstruction::Adrp { + dest: map_register(dest, &width), + label: map_val(src, &width), + }] + }, + RiscVInstruction::Call { label } => { + let width = RiscVWidth::Double; + vec![ArmInstruction::Bl { + target: map_val(label, &width), + }] + } + RiscVInstruction::ECall => { + let syscall_num_reg = ArmRegister{ + width: ArmWidth::Double, + name: ArmRegisterName::X8 + }; + vec![ + // ArmInstruction::Cmp(syscall_num_reg, ArmVal::Imm(RISCV_WRITE)), // if (x8 == RISCV_WRITE) { + // ArmInstruction::Bne("else"), + // ArmInstruction::Mov { width: ArmWidth::Double, dest: x8, src: ArmVal::Imm(SYS_WRITE) }, // x8 = ARM_WRITE; + // ArmInstruction::B("done"), + // ArmInstruction::Label("else"), // } else { + // ArmInstruction::Mov { width: ArmWidth::Double, dest: x8, src: ArmVal::Imm(__) }, // x8 = ARM_EXIT + // // } + // ArmInstruction::Label("done"), + ArmInstruction::Svc { id: 0 } + ] + } + RiscVInstruction::Verbatim { text } => vec![ArmInstruction::Verbatim { text }] + } +} + +fn map_register(riscv_reg: RiscVRegister, riscv_width: &RiscVWidth) -> ArmRegister { + ArmRegister { + width: map_width(riscv_width), + name: map_register_name(riscv_reg), + } +} + +/// Semantic meaning of registers +/// https://riscv.org/wp-content/uploads/2024/12/riscv-calling.pdf#page=3 +fn map_register_name(riscv_reg: RiscVRegister) -> ArmRegisterName { + match riscv_reg { + RiscVRegister::X0 => ArmRegisterName::Zero, + RiscVRegister::RA => ArmRegisterName::Lr, + RiscVRegister::SP => ArmRegisterName::Sp, + RiscVRegister::GP => ArmRegisterName::X12, + RiscVRegister::TP => ArmRegisterName::X14, + RiscVRegister::T0 => ArmRegisterName::X9, + RiscVRegister::T1 => ArmRegisterName::X10, + RiscVRegister::T2 => ArmRegisterName::X11, + // skipped X5 +// RiscVRegister::S1 => ArmRegisterName::X6, +// RiscVRegister::A0 => ArmRegisterName::X0, +// RiscVRegister::A1 => ArmRegisterName::X1, +// RiscVRegister::A2 => ArmRegisterName::X2, +// RiscVRegister::A3 => ArmRegisterName::X3, +// RiscVRegister::A4 => ArmRegisterName::X4, +// RiscVRegister::A5 => ArmRegisterName::X5, +// RiscVRegister::A6 => ArmRegisterName::X6, +// RiscVRegister::A7 => ArmRegisterName::X7, + RiscVRegister::S1 => ArmRegisterName::X13, + RiscVRegister::A0 => ArmRegisterName::X0, // return value/syscall arg 0 + RiscVRegister::A1 => ArmRegisterName::X1, // syscall arg 1 + RiscVRegister::A2 => ArmRegisterName::X2, // syscall arg 2 + RiscVRegister::A3 => ArmRegisterName::X3, // syscall arg 3 + RiscVRegister::A4 => ArmRegisterName::X4, // syscall arg 4 + RiscVRegister::A5 => ArmRegisterName::X5, // syscall arg 5 + RiscVRegister::A6 => ArmRegisterName::X6, // syscall arg 6 + RiscVRegister::A7 => ArmRegisterName::X8, // syscall number + RiscVRegister::S2 => ArmRegisterName::X15, + RiscVRegister::S3 => ArmRegisterName::X16, + RiscVRegister::S4 => ArmRegisterName::X17, + RiscVRegister::S5 => ArmRegisterName::X18, + RiscVRegister::S6 => ArmRegisterName::X19, + RiscVRegister::S7 => ArmRegisterName::X20, + RiscVRegister::S8 => ArmRegisterName::X21, + RiscVRegister::S9 => ArmRegisterName::X22, + RiscVRegister::S10 => ArmRegisterName::X23, + RiscVRegister::S11 => ArmRegisterName::X24, + RiscVRegister::T3 => ArmRegisterName::X25, + RiscVRegister::T4 => ArmRegisterName::X26, + RiscVRegister::T5 => ArmRegisterName::X27, + RiscVRegister::T6 => ArmRegisterName::X28, + RiscVRegister::S0FP => ArmRegisterName::X29, + } +} + +fn map_val(riscv_val: RiscVVal, riscv_width: &RiscVWidth) -> ArmVal { + match riscv_val { + RiscVVal::RiscVRegister(riscv_reg) => ArmVal::Reg(map_register(riscv_reg, riscv_width)), + RiscVVal::Immediate(imm) => ArmVal::Imm(imm), + RiscVVal::Offset { register, offset } => ArmVal::RegOffset(map_register(register, riscv_width), offset), + RiscVVal::LabelOffset { label, offset } => ArmVal::LabelOffset(label, offset), + } +} + +fn map_width(riscv_width: &RiscVWidth) -> ArmWidth { + // todo!() + // FIXME: do real implementation + match riscv_width { + RiscVWidth::Double => ArmWidth::Double, + RiscVWidth::Word => ArmWidth::Word, + } +} + +// Translate every instruction 1:1 +pub fn translate_instrs(riscv_instrs: Vec) -> Vec { + riscv_instrs + .into_iter() + .map(translate).fold(vec![], |mut acc, x|{ + acc.extend(x); + acc + }) +} + +/// Runs binary translation +/// text file -> [`Instruction`] enum array -> text file +pub fn binary_translate(riscv_asm: &str) -> String { + let instructions = parse_asm(riscv_asm); + instructions + .into_iter() + .map(|instr| format!("{:?}", instr)) + .collect::>() + .join("\n") +} diff --git a/src/utils.rs b/src/utils.rs new file mode 100644 index 0000000..e1fe8f8 --- /dev/null +++ b/src/utils.rs @@ -0,0 +1,28 @@ +use std::fs; + +use crate::{instruction::RiscVInstruction, translate::translate_instrs}; + +pub const START: &str = r#" +.text + +.global _start + +_start: +bl main +mov x8, #93 +svc #0 + +main: +"#; + + +pub fn translate_to_file(instrs: Vec, path: String) { + let arm_instrs = translate_instrs(instrs); + let mut contents = String::new(); + for instr in arm_instrs { + let x: String = instr.into(); + contents.push_str(&x); + contents.push_str("\n"); + } + fs::write(path, contents).expect("Unable to write file"); +} \ No newline at end of file diff --git a/test/binaries/add.c b/test/binaries/add.c deleted file mode 100644 index c8fff0f..0000000 --- a/test/binaries/add.c +++ /dev/null @@ -1,7 +0,0 @@ -int main(void) { - int x = 3; - int y = 4; - - return x + y; -} - diff --git a/test/binaries/flake.nix b/test/binaries/flake.nix deleted file mode 100644 index 451d912..0000000 --- a/test/binaries/flake.nix +++ /dev/null @@ -1,24 +0,0 @@ -{ - description = "A very basic flake"; - - inputs = { - nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable"; - }; - - - outputs = { self, nixpkgs }: let - pkgs = import nixpkgs { - # inherit system; - system = "x86_64-linux"; - crossSystem.config = "riscv64-linux-gnu"; - }; - in { - devShells.x86_64-linux.default = pkgs.mkShell { - # Use the same mkShell as documented above - packages = with pkgs; [ -gcc - # pkgs.clang-tools - ]; - }; - }; -} diff --git a/test/test_parse_asm.rs b/test/test_parse_asm.rs deleted file mode 100644 index 6c6050a..0000000 --- a/test/test_parse_asm.rs +++ /dev/null @@ -1,48 +0,0 @@ -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_parse_asm() { - let asm = " - addi sp,sp,-32 - sd ra,24(sp) - ld s0,16(sp) - addi s0,sp,32 - li a5,3 - sw a5,-20(s0) - li a5,4 - sw a5,-24(s0) - lw a5,-20(s0) - mv a4,a5 - lw a5,-24(s0) - addw a5,a4,a5 - sext.w a5,a5 - mv a0,a5 - ld ra,24(sp) - ld s0,16(sp) - addi sp,sp,32 - jr ra - "; - let instructions = parse_asm(asm); - assert_eq!(instructions.len(), 17); - assert_eq!(instructions[0], Instruction::Addi); - assert_eq!(instructions[1], Instruction::Sd); - assert_eq!(instructions[2], Instruction::Ld); - assert_eq!(instructions[3], Instruction::Addi); - assert_eq!(instructions[4], Instruction::Li); - assert_eq!(instructions[5], Instruction::Sw); - assert_eq!(instructions[6], Instruction::Li); - assert_eq!(instructions[7], Instruction::Sw); - assert_eq!(instructions[8], Instruction::Lw); - assert_eq!(instructions[9], Instruction::Mv); - assert_eq!(instructions[10], Instruction::Lw); - assert_eq!(instructions[11], Instruction::Addw); - assert_eq!(instructions[12], Instruction::SextW); - assert_eq!(instructions[13], Instruction::Mv); - assert_eq!(instructions[14], Instruction::Ld); - assert_eq!(instructions[15], Instruction::Ld); - assert_eq!(instructions[16], Instruction::Addi); - assert_eq!(instructions[17], Instruction::Jr); - } -} diff --git a/test/test_translation.rs b/test/test_translation.rs deleted file mode 100644 index a652bd4..0000000 --- a/test/test_translation.rs +++ /dev/null @@ -1,50 +0,0 @@ -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_binary_translate() { - let riscv_asm = " - addi sp,sp,-32 - sd ra,24(sp) - ld s0,16(sp) - addi s0,sp,32 - li a5,3 - sw a5,-20(s0) - li a5,4 - sw a5,-24(s0) - lw a5,-20(s0) - mv a4,a5 - lw a5,-24(s0) - addw a5,a4,a5 - sext.w a5,a5 - mv a0,a5 - ld ra,24(sp) - ld s0,16(sp) - addi sp,sp,32 - jr ra - "; - let translated_asm = binary_translate(riscv_asm); - let expected_output = " - Addi - Sd - Ld - Addi - Li - Sw - Li - Sw - Lw - Mv - Lw - Addw - SextW - Mv - Ld - Ld - Addi - Jr - "; - assert_eq!(translated_asm, expected_output); - } -} diff --git a/test_binary_translate_add.S b/test_binary_translate_add.S new file mode 100644 index 0000000..e18b534 --- /dev/null +++ b/test_binary_translate_add.S @@ -0,0 +1,29 @@ + +.text + +.global _start + +_start: +bl main +mov x8, #93 +svc #0 + +main: +sub sp, sp, 32 +str lr, [sp, 24] +str x29, [sp, 16] +add x29, sp, 32 +mov x5, 3 +str w5, [x29, -20] +mov x5, 4 +str w5, [x29, -24] +ldr w5, [x29, -20] +add x4, x5, 0 +ldr w5, [x29, -24] +add w5, w4, w5 +sxtw x5, w5 +add x0, x5, 0 +ldr lr, [sp, 24] +ldr x29, [sp, 16] +add sp, sp, 32 +blr lr diff --git a/test/binaries/Makefile b/tests/binaries/Makefile similarity index 100% rename from test/binaries/Makefile rename to tests/binaries/Makefile diff --git a/test/binaries/README.md b/tests/binaries/README.md similarity index 100% rename from test/binaries/README.md rename to tests/binaries/README.md diff --git a/tests/binaries/add.c b/tests/binaries/add.c new file mode 100644 index 0000000..8485f1d --- /dev/null +++ b/tests/binaries/add.c @@ -0,0 +1,40 @@ +int main(void) { + int x = 3; + int y = 4; + + return x + y; +} +//////////// riscv +// https://godbolt.org/#g:!((g:!((g:!((h:codeEditor,i:(filename:'1',fontScale:14,fontUsePx:'0',j:1,lang:c%2B%2B,selection:(endColumn:1,endLineNumber:8,positionColumn:1,positionLineNumber:8,selectionStartColumn:1,selectionStartLineNumber:8,startColumn:1,startLineNumber:8),source:'int+main(void)+%7B%0A%09int+x+%3D+3%3B%0A%09int+y+%3D+4%3B%0A%0A%09return+x+%2B+y%3B%0A%7D%0A%0A'),l:'5',n:'0',o:'C%2B%2B+source+%231',t:'0')),k:50,l:'4',n:'0',o:'',s:0,t:'0'),(g:!((h:compiler,i:(compiler:rv64-gcc1420,filters:(b:'0',binary:'1',binaryObject:'1',commentOnly:'0',debugCalls:'1',demangle:'0',directives:'0',execute:'1',intel:'0',libraryCode:'0',trim:'1',verboseDemangling:'0'),flagsViewOpen:'1',fontScale:14,fontUsePx:'0',j:1,lang:c%2B%2B,libs:!(),options:'',overrides:!(),selection:(endColumn:19,endLineNumber:19,positionColumn:19,positionLineNumber:19,selectionStartColumn:1,selectionStartLineNumber:1,startColumn:1,startLineNumber:1),source:1),l:'5',n:'0',o:'+RISC-V+(64-bits)+gcc+14.2.0+(Editor+%231)',t:'0')),k:50,l:'4',n:'0',o:'',s:0,t:'0')),l:'2',n:'0',o:'',t:'0')),version:4 +// main: +// addi sp,sp,-32 +// sd ra,24(sp) +// sd s0,16(sp) +// addi s0,sp,32 +// li a5,3 +// sw a5,-20(s0) +// li a5,4 +// sw a5,-24(s0) +// lw a5,-20(s0) +// mv a4,a5 +// lw a5,-24(s0) +// addw a5,a4,a5 +// sext.w a5,a5 +// mv a0,a5 +// ld ra,24(sp) +// ld s0,16(sp) +// addi sp,sp,32 +// jr ra + +//////////////// arm +// main: +// sub sp, sp, #16 +// mov w0, 3 +// str w0, [sp, 12] +// mov w0, 4 +// str w0, [sp, 8] +// ldr w1, [sp, 12] +// ldr w0, [sp, 8] +// add w0, w1, w0 +// add sp, sp, 16 +// ret diff --git a/test/binaries/add.riscv.s b/tests/binaries/add.riscv.s similarity index 100% rename from test/binaries/add.riscv.s rename to tests/binaries/add.riscv.s diff --git a/test/binaries/add.x86.s b/tests/binaries/add.x86.s similarity index 100% rename from test/binaries/add.x86.s rename to tests/binaries/add.x86.s diff --git a/tests/binaries/add_aarch64_assembled.out b/tests/binaries/add_aarch64_assembled.out new file mode 100755 index 0000000..746730f Binary files /dev/null and b/tests/binaries/add_aarch64_assembled.out differ diff --git a/test/binaries/flake.lock b/tests/binaries/flake.lock similarity index 64% rename from test/binaries/flake.lock rename to tests/binaries/flake.lock index 3bc2a40..24105b1 100644 --- a/test/binaries/flake.lock +++ b/tests/binaries/flake.lock @@ -2,16 +2,16 @@ "nodes": { "nixpkgs": { "locked": { - "lastModified": 1737885589, - "narHash": "sha256-Zf0hSrtzaM1DEz8//+Xs51k/wdSajticVrATqDrfQjg=", + "lastModified": 1739923778, + "narHash": "sha256-BqUY8tz0AQ4to2Z4+uaKczh81zsGZSYxjgvtw+fvIfM=", "owner": "nixos", "repo": "nixpkgs", - "rev": "852ff1d9e153d8875a83602e03fdef8a63f0ecf8", + "rev": "36864ed72f234b9540da4cf7a0c49e351d30d3f1", "type": "github" }, "original": { "owner": "nixos", - "ref": "nixos-unstable", + "ref": "nixos-24.11", "repo": "nixpkgs", "type": "github" } diff --git a/tests/binaries/flake.nix b/tests/binaries/flake.nix new file mode 100644 index 0000000..d408be5 --- /dev/null +++ b/tests/binaries/flake.nix @@ -0,0 +1,26 @@ +{ + description = "A very basic flake"; + + inputs = { + nixpkgs.url = "github:nixos/nixpkgs/nixos-24.11"; + }; + + + outputs = { self, nixpkgs }: + let + pkgs = import nixpkgs { + # inherit system; + system = "x86_64-linux"; + crossSystem.config = "aarch64-linux-gnu"; + }; + in + { + devShells.x86_64-linux.default = pkgs.mkShell { + # Use the same mkShell as documented above + packages = with pkgs; [ + gcc + # pkgs.clang-tools + ]; + }; + }; +} diff --git a/test/binaries/hello_world.c b/tests/binaries/hello_world.c similarity index 100% rename from test/binaries/hello_world.c rename to tests/binaries/hello_world.c diff --git a/tests/binaries/jump.c b/tests/binaries/jump.c new file mode 100644 index 0000000..d5f3e73 --- /dev/null +++ b/tests/binaries/jump.c @@ -0,0 +1,17 @@ +#include + +void fn(void){} +void fn2(void){} + +int main(int x) { + if (x == 5) { + fn(); + } else { + fn2(); + } + printf("hello there"); + + return 0; +} + + diff --git a/tests/binaries/patched_binary_translate_add.S b/tests/binaries/patched_binary_translate_add.S new file mode 100644 index 0000000..f791d64 --- /dev/null +++ b/tests/binaries/patched_binary_translate_add.S @@ -0,0 +1,29 @@ +.text + +.global _start + +_start: +bl main +mov x8, #93 +svc #0 + +main: +sub sp, sp, 32 +str lr, [sp, 24] +str fp, [sp, 16] +add fp, sp, 32 +mov x12, 3 +str w12, [fp, -20] +mov x12, 4 +str w12, [fp, -24] +ldr w12, [fp, -20] +add x11, x12, 0 +ldr w12, [fp, -24] +add w12, w11, w12 +sxtw x12, w12 +add x0, x12, 0 +ldr lr, [sp, 24] +ldr fp, [sp, 16] +add sp, sp, 32 +blr lr + diff --git a/tests/binaries/riscv/flake.lock b/tests/binaries/riscv/flake.lock new file mode 100644 index 0000000..eeec239 --- /dev/null +++ b/tests/binaries/riscv/flake.lock @@ -0,0 +1,27 @@ +{ + "nodes": { + "nixpkgs": { + "locked": { + "lastModified": 1741600792, + "narHash": "sha256-yfDy6chHcM7pXpMF4wycuuV+ILSTG486Z/vLx/Bdi6Y=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "ebe2788eafd539477f83775ef93c3c7e244421d3", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixos-24.11", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/tests/binaries/riscv/flake.nix b/tests/binaries/riscv/flake.nix new file mode 100644 index 0000000..3a391c7 --- /dev/null +++ b/tests/binaries/riscv/flake.nix @@ -0,0 +1,26 @@ +{ + description = "A very basic flake"; + + inputs = { + nixpkgs.url = "github:nixos/nixpkgs/nixos-24.11"; + }; + + + outputs = { self, nixpkgs }: + let + pkgs = import nixpkgs { + # inherit system; + system = "x86_64-linux"; + crossSystem.config = "riscv64-linux-gnu"; + }; + in + { + devShells.x86_64-linux.default = pkgs.mkShell { + # Use the same mkShell as documented above + packages = with pkgs; [ + gcc + # pkgs.clang-tools + ]; + }; + }; +} diff --git a/tests/test_echo.rs b/tests/test_echo.rs new file mode 100644 index 0000000..46f37e1 --- /dev/null +++ b/tests/test_echo.rs @@ -0,0 +1,40 @@ +#[cfg(test)] +mod tests { + use binary_room::instruction::*; + use binary_room::translate::*; + use binary_room::utils; + use binary_room::utils::translate_to_file; + use binary_room::utils::START; + +const buf: &str = r#" +.buf: + .string "hello world" +"#; + + #[test] + fn test_print_translate() { + let riscv_asm: Vec = vec![ + // RiscVInstruction::Verbatim { text: buf.to_string() }, + RiscVInstruction::Verbatim { text: START.to_string() }, + // read syscall + RiscVInstruction::Addi { dest: RiscVRegister::SP, src: RiscVRegister::SP, imm: -32 }, // sub stack pointer + RiscVInstruction::Li { dest: RiscVRegister::A7, imm: 63 }, // read syscall # + RiscVInstruction::Li { dest: RiscVRegister::A2, imm: 32 }, // read 5 bytes + RiscVInstruction::Mv { dest: RiscVRegister::A1, src: RiscVRegister::SP }, + RiscVInstruction::Li { dest: RiscVRegister::A0, imm: 0 }, + RiscVInstruction::ECall, + // write syscall + RiscVInstruction::Li { dest: RiscVRegister::A7, imm: 64 }, + RiscVInstruction::Li { dest: RiscVRegister::A2, imm: 14 }, + RiscVInstruction::Mv { dest: RiscVRegister::A1, src: RiscVRegister::SP }, + RiscVInstruction::Li { dest: RiscVRegister::A0, imm: 1 }, + RiscVInstruction::ECall, + // exit syscall + RiscVInstruction::Li { dest: RiscVRegister::A7, imm: 93 }, + // RiscVInstruction::Li { dest: RiscVRegister::A0, imm: 0 }, + RiscVInstruction::ECall + ]; + + translate_to_file(riscv_asm, "test_echo.S".to_string()); + } +} diff --git a/tests/test_parse_asm.rs b/tests/test_parse_asm.rs new file mode 100644 index 0000000..535fc3d --- /dev/null +++ b/tests/test_parse_asm.rs @@ -0,0 +1,48 @@ +// #[cfg(test)] +// mod tests { +// use binary_room::instruction::parse_asm; + +// #[test] +// fn test_parse_asm() { +// let asm = " +// addi sp,sp,-32 +// sd ra,24(sp) +// ld s0,16(sp) +// addi s0,sp,32 +// li a5,3 +// sw a5,-20(s0) +// li a5,4 +// sw a5,-24(s0) +// lw a5,-20(s0) +// mv a4,a5 +// lw a5,-24(s0) +// addw a5,a4,a5 +// sext.w a5,a5 +// mv a0,a5 +// ld ra,24(sp) +// ld s0,16(sp) +// addi sp,sp,32 +// jr ra +// "; +// let instructions = parse_asm(asm); +// assert_eq!(instructions.len(), 17); +// assert_eq!(instructions[0], RiscVInstruction::Addi); +// assert_eq!(instructions[1], RiscVInstruction::Sd); +// assert_eq!(instructions[2], RiscVInstruction::Ld); +// assert_eq!(instructions[3], RiscVInstruction::Addi); +// assert_eq!(instructions[4], RiscVInstruction::Li); +// assert_eq!(instructions[5], RiscVInstruction::Sw); +// assert_eq!(instructions[6], RiscVInstruction::Li); +// assert_eq!(instructions[7], RiscVInstruction::Sw); +// assert_eq!(instructions[8], RiscVInstruction::Lw); +// assert_eq!(instructions[9], RiscVInstruction::Mv); +// assert_eq!(instructions[10], RiscVInstruction::Lw); +// assert_eq!(instructions[11], RiscVInstruction::Addw); +// assert_eq!(instructions[12], RiscVInstruction::SextW); +// assert_eq!(instructions[13], RiscVInstruction::Mv); +// assert_eq!(instructions[14], RiscVInstruction::Ld); +// assert_eq!(instructions[15], RiscVInstruction::Ld); +// assert_eq!(instructions[16], RiscVInstruction::Addi); +// assert_eq!(instructions[17], RiscVInstruction::Jr); +// } +// } diff --git a/tests/test_print.rs b/tests/test_print.rs new file mode 100644 index 0000000..e073b44 --- /dev/null +++ b/tests/test_print.rs @@ -0,0 +1,41 @@ +#[cfg(test)] +mod tests { + use binary_room::instruction::*; + use binary_room::translate::*; + use binary_room::utils; + use binary_room::utils::translate_to_file; + use binary_room::utils::START; + +const buf: &str = r#" +.buf: + .string "hello world\n" +"#; + + #[test] + fn test_print_translate() { + let riscv_asm: Vec = vec![ + RiscVInstruction::Verbatim { text: buf.to_string() }, + RiscVInstruction::Verbatim { text: START.to_string() }, + // write syscall + RiscVInstruction::Li { dest: RiscVRegister::A7, imm: 64 }, + RiscVInstruction::Li { dest: RiscVRegister::A2, imm: 14 }, + RiscVInstruction::Lui { dest: RiscVRegister::A0, src: RiscVVal::LabelOffset { label: ".buf".to_string(), offset: 9998 } }, + RiscVInstruction::Addl { + dest: RiscVRegister::A1, + src: RiscVRegister::A0, + label: RiscVVal::LabelOffset { + label: ".buf".to_string(), + offset: 9999 + } + }, + RiscVInstruction::Li { dest: RiscVRegister::A0, imm: 1 }, + RiscVInstruction::ECall, + // exit syscall + RiscVInstruction::Li { dest: RiscVRegister::A7, imm: 93 }, + // RiscVInstruction::Li { dest: RiscVRegister::A0, imm: 0 }, + RiscVInstruction::ECall + ]; + + translate_to_file(riscv_asm, "test_print.S".to_string()); + } +} diff --git a/tests/test_translation.rs b/tests/test_translation.rs new file mode 100644 index 0000000..6780d8c --- /dev/null +++ b/tests/test_translation.rs @@ -0,0 +1,264 @@ +#[cfg(test)] +mod tests { + use binary_room::instruction::*; + use binary_room::translate::*; + use binary_room::utils; + use binary_room::utils::translate_to_file; + use binary_room::utils::START; + + #[test] + fn test_binary_translate() { + let riscv_asm: Vec = vec![ + RiscVInstruction::Addi { + dest: RiscVRegister::SP, + src: RiscVRegister::SP, + imm: -32, + }, + RiscVInstruction::S { + width: RiscVWidth::Double, + src: RiscVRegister::RA, + dest: RiscVVal::Offset { + register: RiscVRegister::SP, + offset: 24, + }, + }, + RiscVInstruction::S { + width: RiscVWidth::Double, + src: RiscVRegister::S0FP, + dest: RiscVVal::Offset { + register: RiscVRegister::SP, + offset: 16, + }, + }, + RiscVInstruction::Addi { + dest: RiscVRegister::S0FP, + src: RiscVRegister::SP, + imm: 32, + }, + RiscVInstruction::Li { + dest: RiscVRegister::A5, + imm: 3, + }, + RiscVInstruction::S { + width: RiscVWidth::Word, + src: RiscVRegister::A5, + dest: RiscVVal::Offset { + register: RiscVRegister::S0FP, + offset: -20, + }, + }, + RiscVInstruction::Li { + dest: RiscVRegister::A5, + imm: 4, + }, + RiscVInstruction::S { + width: RiscVWidth::Word, + src: RiscVRegister::A5, + dest: RiscVVal::Offset { + register: RiscVRegister::S0FP, + offset: -24, + }, + }, + RiscVInstruction::L { + width: RiscVWidth::Word, + dest: RiscVRegister::A5, + src: RiscVVal::Offset { + register: RiscVRegister::S0FP, + offset: -20, + }, + }, + RiscVInstruction::Mv { + dest: RiscVRegister::A4, + src: RiscVRegister::A5, + }, + RiscVInstruction::L { + width: RiscVWidth::Word, + dest: RiscVRegister::A5, + src: RiscVVal::Offset { + register: RiscVRegister::S0FP, + offset: -24, + }, + }, + RiscVInstruction::Add { + width: RiscVWidth::Word, + dest: RiscVRegister::A5, + arg1: RiscVRegister::A4, + arg2: RiscVRegister::A5, + }, + RiscVInstruction::SextW { + dest: RiscVRegister::A5, + src: RiscVRegister::A5, + }, + RiscVInstruction::Mv { + dest: RiscVRegister::A0, + src: RiscVRegister::A5, + }, + RiscVInstruction::L { + width: RiscVWidth::Double, + dest: RiscVRegister::RA, + src: RiscVVal::Offset { + register: RiscVRegister::SP, + offset: 24, + }, + }, + RiscVInstruction::L { + width: RiscVWidth::Double, + dest: RiscVRegister::S0FP, + src: RiscVVal::Offset { + register: RiscVRegister::SP, + offset: 16, + }, + }, + RiscVInstruction::Addi { + dest: RiscVRegister::SP, + src: RiscVRegister::SP, + imm: 32, + }, + RiscVInstruction::Jr { + target: RiscVRegister::RA, + }, + ]; + + translate_to_file(riscv_asm, "test_binary_translate_add.S".to_string()); + } + + #[test] + fn test_syscall_translate() { + let riscv_asm: Vec = vec![ + RiscVInstruction::Label { name: ".LC0".to_string() }, + RiscVInstruction::Directive { + name: "string".to_string(), + operands: "\"hello, world!\\n\"" .to_string() + }, + RiscVInstruction::Directive { + name: "align".to_string(), + operands: "2".to_string() + }, + RiscVInstruction::Directive { + name: "global".to_string(), + operands: "main".to_string() + }, + RiscVInstruction::Directive { + name: "type".to_string(), + operands: "main, @function".to_string() + }, + RiscVInstruction::Label { name: "main".to_string() }, + RiscVInstruction::Addi { + dest: RiscVRegister::SP, + src: RiscVRegister::SP, + imm: -16, + }, + RiscVInstruction::S { + width: RiscVWidth::Double, + src: RiscVRegister::RA, + dest: RiscVVal::Offset { + register: RiscVRegister::SP, + offset: 8, + }, + }, + RiscVInstruction::S { + width: RiscVWidth::Double, + src: RiscVRegister::S0FP, + dest: RiscVVal::Offset { + register: RiscVRegister::SP, + offset: 0 + }, + }, + RiscVInstruction::Addi { + dest: RiscVRegister::S0FP, + src: RiscVRegister::SP, + imm: 16, + }, + RiscVInstruction::Li { + dest: RiscVRegister::A3, + imm: 14, + }, + RiscVInstruction::Lui { + dest: RiscVRegister::A5, + src: RiscVVal::LabelOffset { + label: ".LC0".to_string(), + offset: 9998, // %hi riscv + }, + }, + RiscVInstruction::Addl { + dest: RiscVRegister::A2, + src: RiscVRegister::A5, + label: RiscVVal::LabelOffset { + label: ".LC0".to_string(), + offset: 9999, // %lo riscv, :lo12: arm + }, + }, + RiscVInstruction::Li { + dest: RiscVRegister::A1, + imm: 1, + }, + RiscVInstruction::Li { + dest: RiscVRegister::A0, + imm: 64, + }, + RiscVInstruction::Call { + label: RiscVVal::LabelOffset { + label: "syscall".to_string(), + offset: 0, + }, + }, + RiscVInstruction::L { + width: RiscVWidth::Double, + dest: RiscVRegister::RA, + src: RiscVVal::Offset { + register: RiscVRegister::SP, + offset: 8, + }, + }, + RiscVInstruction::L { + width: RiscVWidth::Double, + dest: RiscVRegister::S0FP, + src: RiscVVal::Offset { + register: RiscVRegister::SP, + offset: 0, + }, + }, + RiscVInstruction::Addi { + dest: RiscVRegister::SP, + src: RiscVRegister::SP, + imm: 16, + }, + RiscVInstruction::Jr { + target: RiscVRegister::RA, + }, + ]; + + translate_to_file(riscv_asm, "test_binary_translate_write.S".to_string()); + } + + #[test] + fn test_loop() { + let riscv_asm: Vec = vec![ + RiscVInstruction::Verbatim { text: START.to_string() }, + RiscVInstruction::Label { name: "main".to_string() }, + RiscVInstruction::Addi { dest: RiscVRegister::SP, src: RiscVRegister::SP, imm: -32 }, + RiscVInstruction::S { width: RiscVWidth::Double, src: RiscVRegister::S0FP, dest: RiscVVal::Offset { register: RiscVRegister::SP, offset: 24 } }, + RiscVInstruction::Addi { dest: RiscVRegister::S0FP, src: RiscVRegister::SP, imm: 32 }, + RiscVInstruction::S { width: RiscVWidth::Word, src: RiscVRegister::X0, dest: RiscVVal::Offset { register: RiscVRegister::S0FP, offset: -20 } }, + RiscVInstruction::J { target: RiscVVal::LabelOffset { label: ".L2".to_string(), offset: 0 }}, + RiscVInstruction::Label { name: ".L3".to_string() }, + RiscVInstruction::L { width: RiscVWidth::Word, dest: RiscVRegister::A5, src: RiscVVal::Offset { register:RiscVRegister::S0FP, offset: -24 } }, + RiscVInstruction::Addi { dest: RiscVRegister::A5, src: RiscVRegister::A5, imm: 1 }, + RiscVInstruction::S { width: RiscVWidth::Word, src: RiscVRegister::A5, dest: RiscVVal::Offset { register: RiscVRegister::S0FP, offset: -24 } }, + RiscVInstruction::L { width: RiscVWidth::Word, dest: RiscVRegister::A5, src: RiscVVal::Offset { register: RiscVRegister::S0FP, offset: -20 } }, + RiscVInstruction::Addi { dest: RiscVRegister::A5, src: RiscVRegister::A5, imm: 1 }, + RiscVInstruction::S { width: RiscVWidth::Word, src: RiscVRegister::A5, dest: RiscVVal::Offset { register: RiscVRegister::S0FP, offset: -20 } }, + RiscVInstruction::Label { name: ".L2".to_string() }, + RiscVInstruction::L { width: RiscVWidth::Word, dest: RiscVRegister::A5, src: RiscVVal::Offset { register:RiscVRegister::S0FP, offset: -20 } }, + RiscVInstruction::SextW { dest: RiscVRegister::A4, src: RiscVRegister::A5 }, + RiscVInstruction::Li { dest:RiscVRegister::A5, imm: 4 }, + RiscVInstruction::Ble { arg1: RiscVRegister::A4, arg2: RiscVRegister::A5, target: RiscVVal::LabelOffset { label: ".L3".to_string(), offset: 0 } }, + RiscVInstruction::L { width: RiscVWidth::Word, dest: RiscVRegister::A5, src: RiscVVal::Offset { register: RiscVRegister::S0FP, offset: -24 } }, + RiscVInstruction::Mv { dest: RiscVRegister::A0, src: RiscVRegister::A5 }, + RiscVInstruction::L { width: RiscVWidth::Double, dest: RiscVRegister::S0FP, src: RiscVVal::Offset { register: RiscVRegister::S0FP, offset: 24 } }, + RiscVInstruction::Addi { dest: RiscVRegister::SP, src: RiscVRegister::SP, imm: 32 }, + RiscVInstruction::Jr { target: RiscVRegister::RA }, + ]; + translate_to_file(riscv_asm, "test_binary_translate_loop.S".to_string()); + } +}