From c526f75ce229c33f4ce539865d234ef795766b9b Mon Sep 17 00:00:00 2001 From: petitstrawberry Date: Fri, 27 Feb 2026 15:31:59 +0000 Subject: [PATCH] change: instructions() returns Result for error handling --- README.md | 34 +- examples/instruction_collection_merge.rs | 87 +- examples/jit_calculator.rs | 371 ++-- examples/jit_execution.rs | 46 +- examples/register_tracking_demo.rs | 30 +- src/aarch64/builder.rs | 103 +- src/aarch64/instruction.rs | 129 +- src/aarch64/macros.rs | 2 +- src/aarch64/mod.rs | 6 +- src/aarch64/tests.rs | 471 +++-- src/common/mod.rs | 623 +++--- src/common/register_usage.rs | 265 +-- src/lib.rs | 20 +- src/riscv64/builder.rs | 233 ++- src/riscv64/instruction.rs | 240 ++- src/riscv64/macros.rs | 2 +- src/riscv64/mod.rs | 8 +- src/riscv64/tests.rs | 2382 ++++++++++++++-------- 18 files changed, 3049 insertions(+), 2003 deletions(-) diff --git a/README.md b/README.md index 74afc01..294c2ca 100644 --- a/README.md +++ b/README.md @@ -54,14 +54,15 @@ let instructions2 = builder .beq(reg::A0, reg::A1, 8) // Branch if equal .jal(reg::RA, 0x1000) // Jump and link .ret() // Return instruction - .instructions(); + .instructions() + .unwrap(); // Traditional style let mut builder3 = Riscv64InstructionBuilder::new(); builder3.csrrw(reg::RA, csr::MSTATUS, reg::SP); builder3.addi(reg::A0, reg::ZERO, 100); builder3.ret(); -let instructions3 = builder3.instructions(); +let instructions3 = builder3.instructions().unwrap(); // Convert instructions to bytes easily let bytes = instructions.to_bytes(); // All instructions as one byte vector @@ -152,6 +153,7 @@ Support for additional architectures is planned: ```rust use jit_assembler::riscv::{reg, csr, Riscv64InstructionBuilder}; use jit_assembler::riscv64_asm; +use jit_assembler::common::BuildError; // Simple function generator with macro fn generate_add_function(a: i16, b: i16) -> Vec { @@ -167,7 +169,7 @@ fn generate_add_function(a: i16, b: i16) -> Vec { } // Builder pattern for complex logic -fn generate_csr_routine() -> Vec { +fn generate_csr_routine() -> Result, BuildError> { let mut builder = Riscv64InstructionBuilder::new(); builder @@ -176,7 +178,7 @@ fn generate_csr_routine() -> Vec { .csrrw(reg::A0, csr::MSTATUS, reg::T1); // Write back, old value in a0 // Convert to executable code - builder.instructions().to_bytes() + Ok(builder.instructions()?.to_bytes()) } ``` @@ -184,7 +186,7 @@ fn generate_csr_routine() -> Vec { ```rust use jit_assembler::aarch64::{reg, Aarch64InstructionBuilder}; -use jit_assembler::common::InstructionBuilder; +use jit_assembler::common::{InstructionBuilder, BuildError}; use jit_assembler::aarch64_asm; // Macro style (concise and assembly-like) @@ -197,14 +199,14 @@ fn generate_aarch64_add_function_macro() -> Vec { } // Builder pattern style -fn generate_aarch64_add_function() -> Vec { +fn generate_aarch64_add_function() -> Result, BuildError> { let mut builder = Aarch64InstructionBuilder::new(); builder .add(reg::X0, reg::X0, reg::X1) // Add first two arguments (X0 + X1 -> X0) .ret(); // Return - builder.instructions().to_bytes() + Ok(builder.instructions()?.to_bytes()) } // More complex AArch64 example with immediate values (macro style) @@ -218,7 +220,7 @@ fn generate_aarch64_calculation_macro() -> Vec { } // More complex AArch64 example with immediate values (builder style) -fn generate_aarch64_calculation() -> Vec { +fn generate_aarch64_calculation() -> Result, BuildError> { let mut builder = Aarch64InstructionBuilder::new(); builder @@ -227,7 +229,7 @@ fn generate_aarch64_calculation() -> Vec { .addi(reg::X0, reg::X0, 100) // Add 100 to result .ret(); // Return - builder.instructions().to_bytes() + Ok(builder.instructions()?.to_bytes()) } ``` @@ -367,12 +369,12 @@ epilogue .ret(); // Combine them using + operator: prologue + main + epilogue -let combined = prologue.instructions() + main_code.instructions() + epilogue.instructions(); +let combined = prologue.instructions().unwrap() + main_code.instructions().unwrap() + epilogue.instructions().unwrap(); // Or use method chaining -let mut combined = prologue.instructions(); -combined += main_code.instructions(); -combined += epilogue.instructions(); +let mut combined = prologue.instructions().unwrap(); +combined += main_code.instructions().unwrap(); +combined += epilogue.instructions().unwrap(); // Convert to executable code let bytes = combined.to_bytes(); @@ -398,15 +400,15 @@ epilogue.ld(reg::S0, reg::SP, -8); // Restore S0 // Create tracked collections let prologue_tracked = InstructionCollectionWithUsage::new( - prologue.instructions(), + prologue.instructions().unwrap(), prologue.register_usage().clone() ); let main_tracked = InstructionCollectionWithUsage::new( - main_code.instructions(), + main_code.instructions().unwrap(), main_code.register_usage().clone() ); let epilogue_tracked = InstructionCollectionWithUsage::new( - epilogue.instructions(), + epilogue.instructions().unwrap(), epilogue.register_usage().clone() ); diff --git a/examples/instruction_collection_merge.rs b/examples/instruction_collection_merge.rs index 5998907..1e3c0a0 100644 --- a/examples/instruction_collection_merge.rs +++ b/examples/instruction_collection_merge.rs @@ -1,73 +1,73 @@ //! Example demonstrating InstructionCollection merge functionality -//! +//! //! This example shows how to combine multiple instruction sequences, //! such as function prologue, main body, and epilogue, in arbitrary order. //! When the register-tracking feature is enabled, register usage information //! is also properly merged. -use jit_assembler::riscv64::{Riscv64InstructionBuilder, reg}; use jit_assembler::common::InstructionBuilder; +use jit_assembler::riscv64::{reg, Riscv64InstructionBuilder}; #[cfg(feature = "register-tracking")] use jit_assembler::common::InstructionCollectionWithUsage; fn main() { println!("=== Instruction Collection Merge Demo ===\n"); - + // Create three separate builders for different parts of the code - + // Builder 1: Main computation println!("1. Building main computation..."); let mut main_builder = Riscv64InstructionBuilder::new(); main_builder - .add(reg::A0, reg::A1, reg::A2) // a0 = a1 + a2 - .mul(reg::A0, reg::A0, reg::A3) // a0 = a0 * a3 - .addi(reg::A0, reg::A0, 100); // a0 = a0 + 100 - + .add(reg::A0, reg::A1, reg::A2) // a0 = a1 + a2 + .mul(reg::A0, reg::A0, reg::A3) // a0 = a0 * a3 + .addi(reg::A0, reg::A0, 100); // a0 = a0 + 100 + // Builder 2: Function prologue (register save) // This is typically built AFTER the main code to know which registers to save println!("2. Building prologue (register save)..."); let mut prologue_builder = Riscv64InstructionBuilder::new(); prologue_builder - .addi(reg::SP, reg::SP, -16) // Allocate stack space - .sd(reg::SP, reg::RA, 8) // Save return address - .sd(reg::SP, reg::S0, 0); // Save frame pointer - + .addi(reg::SP, reg::SP, -16) // Allocate stack space + .sd(reg::SP, reg::RA, 8) // Save return address + .sd(reg::SP, reg::S0, 0); // Save frame pointer + // Builder 3: Function epilogue (register restore) println!("3. Building epilogue (register restore)..."); let mut epilogue_builder = Riscv64InstructionBuilder::new(); epilogue_builder - .ld(reg::RA, reg::SP, 8) // Restore return address - .ld(reg::S0, reg::SP, 0) // Restore frame pointer - .addi(reg::SP, reg::SP, 16) // Deallocate stack space - .ret(); // Return - + .ld(reg::RA, reg::SP, 8) // Restore return address + .ld(reg::S0, reg::SP, 0) // Restore frame pointer + .addi(reg::SP, reg::SP, 16) // Deallocate stack space + .ret(); // Return + // Get instruction collections from each builder - let prologue = prologue_builder.instructions(); - let main_code = main_builder.instructions(); - let epilogue = epilogue_builder.instructions(); - + let prologue = prologue_builder.instructions().unwrap(); + let main_code = main_builder.instructions().unwrap(); + let epilogue = epilogue_builder.instructions().unwrap(); + println!("\nInstruction counts:"); println!(" Prologue: {} instructions", prologue.len()); println!(" Main: {} instructions", main_code.len()); println!(" Epilogue: {} instructions", epilogue.len()); - + // Combine them in the desired order: prologue + main + epilogue println!("\n4. Combining instruction collections..."); let combined = prologue + main_code + epilogue; - + println!(" Combined: {} instructions", combined.len()); - + // Display the combined instructions println!("\nCombined instructions:"); for (i, instr) in combined.iter().enumerate() { println!(" [{:2}] {}", i, instr); } - + // Convert to bytes for execution let bytes = combined.to_bytes(); println!("\nGenerated {} bytes of machine code", bytes.len()); - + // Demonstrate with register tracking (when feature is enabled) #[cfg(feature = "register-tracking")] demonstrate_with_register_tracking(); @@ -76,52 +76,52 @@ fn main() { #[cfg(feature = "register-tracking")] fn demonstrate_with_register_tracking() { println!("\n\n=== With Register Tracking ===\n"); - + // Create the same three builders let mut main_builder = Riscv64InstructionBuilder::new(); main_builder .add(reg::A0, reg::A1, reg::A2) .mul(reg::A0, reg::A0, reg::A3) .addi(reg::A0, reg::A0, 100); - + let mut prologue_builder = Riscv64InstructionBuilder::new(); prologue_builder .addi(reg::SP, reg::SP, -16) .sd(reg::SP, reg::RA, 8) .sd(reg::SP, reg::S0, 0); - + let mut epilogue_builder = Riscv64InstructionBuilder::new(); epilogue_builder .ld(reg::RA, reg::SP, 8) .ld(reg::S0, reg::SP, 0) .addi(reg::SP, reg::SP, 16) .ret(); - + // Create tracked collections that include register usage info let prologue = InstructionCollectionWithUsage::new( - prologue_builder.instructions(), - prologue_builder.register_usage().clone() + prologue_builder.instructions().unwrap(), + prologue_builder.register_usage().clone(), ); let main_code = InstructionCollectionWithUsage::new( - main_builder.instructions(), - main_builder.register_usage().clone() + main_builder.instructions().unwrap(), + main_builder.register_usage().clone(), ); let epilogue = InstructionCollectionWithUsage::new( - epilogue_builder.instructions(), - epilogue_builder.register_usage().clone() + epilogue_builder.instructions().unwrap(), + epilogue_builder.register_usage().clone(), ); - + println!("Register usage before merge:"); println!(" Prologue: {}", prologue.register_usage()); println!(" Main: {}", main_code.register_usage()); println!(" Epilogue: {}", epilogue.register_usage()); - + // Merge with register usage tracking let combined = prologue + main_code + epilogue; - + println!("\nRegister usage after merge:"); println!(" Combined: {}", combined.register_usage()); - + let usage = combined.register_usage(); println!("\nDetailed register analysis:"); println!(" Total registers used: {}", usage.register_count()); @@ -129,7 +129,10 @@ fn demonstrate_with_register_tracking() { println!(" Callee-saved: {:?}", usage.callee_saved_registers()); println!(" Special: {:?}", usage.special_registers()); println!(" Needs stack frame: {}", usage.needs_stack_frame()); - + // You can still access the instructions - println!("\nFinal instruction count: {}", combined.instructions().len()); + println!( + "\nFinal instruction count: {}", + combined.instructions().unwrap().len() + ); } diff --git a/examples/jit_calculator.rs b/examples/jit_calculator.rs index 498e280..650aa34 100644 --- a/examples/jit_calculator.rs +++ b/examples/jit_calculator.rs @@ -1,18 +1,18 @@ //! JIT Calculator with AST Example -//! +//! //! This example demonstrates a sophisticated JIT-compiled calculator that uses //! an Abstract Syntax Tree (AST) to represent mathematical expressions. //! The calculator compiles expressions to native machine code using //! either RISC-V or AArch64 depending on the target architecture. -//! +//! //! Features: //! - AST-based expression parsing and evaluation //! - Support for parentheses and operator precedence //! - JIT compilation to RISC-V or AArch64 machine code //! - Optimized code generation for complex expressions -//! +//! //! Supported operations: +, -, *, /, % (remainder), and parentheses -//! +//! //! Note: This example works on RISC-V or AArch64 hosts or in emulation. //! On other architectures, the functions will be created successfully //! but calling them will likely crash. @@ -26,8 +26,8 @@ use jit_assembler::aarch64::{reg, Aarch64InstructionBuilder}; use jit_assembler::common::InstructionBuilder; -use std::fmt; use std::env; +use std::fmt; /// Configuration for the JIT calculator #[derive(Debug, Clone)] @@ -133,13 +133,34 @@ impl Tokenizer { let ch = self.input[self.pos]; match ch { - '+' => { self.pos += 1; Ok(Token::Plus) } - '-' => { self.pos += 1; Ok(Token::Minus) } - '*' => { self.pos += 1; Ok(Token::Multiply) } - '/' => { self.pos += 1; Ok(Token::Divide) } - '%' => { self.pos += 1; Ok(Token::Remainder) } - '(' => { self.pos += 1; Ok(Token::LeftParen) } - ')' => { self.pos += 1; Ok(Token::RightParen) } + '+' => { + self.pos += 1; + Ok(Token::Plus) + } + '-' => { + self.pos += 1; + Ok(Token::Minus) + } + '*' => { + self.pos += 1; + Ok(Token::Multiply) + } + '/' => { + self.pos += 1; + Ok(Token::Divide) + } + '%' => { + self.pos += 1; + Ok(Token::Remainder) + } + '(' => { + self.pos += 1; + Ok(Token::LeftParen) + } + ')' => { + self.pos += 1; + Ok(Token::RightParen) + } '0'..='9' => self.parse_number(), _ => Err(format!("Unexpected character: {}", ch)), } @@ -156,9 +177,10 @@ impl Tokenizer { while self.pos < self.input.len() && self.input[self.pos].is_ascii_digit() { self.pos += 1; } - + let number_str: String = self.input[start..self.pos].iter().collect(); - number_str.parse::() + number_str + .parse::() .map(Token::Number) .map_err(|_| format!("Invalid number: {}", number_str)) } @@ -221,7 +243,10 @@ impl Parser { fn parse_multiplicative(&mut self) -> Result { let mut left = self.parse_primary()?; - while matches!(self.current_token, Token::Multiply | Token::Divide | Token::Remainder) { + while matches!( + self.current_token, + Token::Multiply | Token::Divide | Token::Remainder + ) { let op = match self.current_token { Token::Multiply => BinaryOperator::Multiply, Token::Divide => BinaryOperator::Divide, @@ -281,13 +306,25 @@ impl JitCompiler { /// Available temporary registers for computation (RISC-V) #[cfg(target_arch = "riscv64")] const TEMP_REGISTERS_RISCV: &'static [jit_assembler::riscv64::Register] = &[ - reg::T0, reg::T1, reg::T2, reg::T3, reg::T4, reg::T5, reg::T6, + reg::T0, + reg::T1, + reg::T2, + reg::T3, + reg::T4, + reg::T5, + reg::T6, ]; /// Available temporary registers for computation (AArch64) #[cfg(target_arch = "aarch64")] const TEMP_REGISTERS_AARCH64: &'static [jit_assembler::aarch64::Register] = &[ - reg::X9, reg::X10, reg::X11, reg::X12, reg::X13, reg::X14, reg::X15, + reg::X9, + reg::X10, + reg::X11, + reg::X12, + reg::X13, + reg::X14, + reg::X15, ]; pub fn new() -> Self { @@ -339,7 +376,7 @@ impl JitCompiler { } self.register_stack.pop(); } - + #[cfg(target_arch = "aarch64")] { if self.register_stack.is_empty() { @@ -347,27 +384,31 @@ impl JitCompiler { } self.register_stack.pop(); } - + self.next_temp_reg = self.next_temp_reg.saturating_sub(1); Ok(()) } /// Compile an AST to a JIT function /// The result is stored in the appropriate return register for each architecture - pub fn compile_expression(&mut self, ast: &AstNode, config: &CalculatorConfig) -> Result u64>, Box> { + pub fn compile_expression( + &mut self, + ast: &AstNode, + config: &CalculatorConfig, + ) -> Result u64>, Box> { // Generate code that computes the expression result in the return register #[cfg(target_arch = "riscv64")] { self.compile_node(ast, reg::A0)?; self.builder.ret(); } - + #[cfg(target_arch = "aarch64")] { self.compile_node(ast, reg::X0)?; self.builder.ret(); } - + #[cfg(not(any(target_arch = "riscv64", target_arch = "aarch64")))] { return Err("JIT compilation not supported on this architecture".into()); @@ -380,12 +421,10 @@ impl JitCompiler { #[cfg(any(target_arch = "riscv64", target_arch = "aarch64"))] { - let jit_func = unsafe { - self.builder.function:: u64>()? - }; + let jit_func = unsafe { self.builder.function:: u64>()? }; Ok(Box::new(move || jit_func.call())) } - + #[cfg(not(any(target_arch = "riscv64", target_arch = "aarch64")))] { // This will never be reached due to the early return above, but needed for type consistency @@ -397,43 +436,61 @@ impl JitCompiler { pub fn show_generated_code(&self) { #[cfg(target_arch = "riscv64")] { - let instructions = self.builder.instructions(); + let instructions = self.builder.instructions().unwrap(); let bytes = instructions.to_bytes(); - + println!("🤖 Generated Machine Code:"); - println!(" Instructions: {}, Total bytes: {}", instructions.len(), bytes.len()); - + println!( + " Instructions: {}, Total bytes: {}", + instructions.len(), + bytes.len() + ); + for (i, instr) in instructions.iter().enumerate() { let instr_bytes = instr.bytes(); - println!(" [{:2}]: {:02X?} ({})", - i + 1, - instr_bytes, - if instr.is_compressed() { "16-bit" } else { "32-bit" }); + println!( + " [{:2}]: {:02X?} ({})", + i + 1, + instr_bytes, + if instr.is_compressed() { + "16-bit" + } else { + "32-bit" + } + ); } - + println!(" Raw bytes: {:02X?}", bytes); } - + #[cfg(target_arch = "aarch64")] { - let instructions = self.builder.instructions(); + let instructions = self.builder.instructions().unwrap(); let bytes = instructions.to_bytes(); - + println!("🤖 Generated Machine Code:"); - println!(" Instructions: {}, Total bytes: {}", instructions.len(), bytes.len()); - + println!( + " Instructions: {}, Total bytes: {}", + instructions.len(), + bytes.len() + ); + for (i, instr) in instructions.iter().enumerate() { let instr_bytes = instr.bytes(); println!(" [{:2}]: {:02X?} (32-bit)", i + 1, instr_bytes); } - + println!(" Raw bytes: {:02X?}", bytes); } } /// Compile an AST node, storing the result in the specified register (RISC-V) #[cfg(target_arch = "riscv64")] - fn compile_node(&mut self, node: &AstNode, result_reg: jit_assembler::riscv64::Register) -> Result<(), String> { + fn compile_node( + &mut self, + node: &AstNode, + result_reg: jit_assembler::riscv64::Register, + ) -> Result<(), String> { match node { AstNode::Number(value) => { // Load immediate value into result register @@ -444,13 +501,13 @@ impl JitCompiler { // Large immediate: use LUI + ADDI with correct sign extension handling let lower = (*value & 0xFFF) as i16; let upper = if lower < 0 { - // If lower part is negative, we need to add 1 to upper part + // If lower part is negative, we need to add 1 to upper part // because LUI will be sign-extended ((*value + 0x800) >> 12) as u32 } else { (*value >> 12) as u32 }; - + self.builder.lui(result_reg, upper); if lower != 0 { self.builder.addi(result_reg, result_reg, lower); @@ -461,7 +518,7 @@ impl JitCompiler { AstNode::BinaryOp { left, op, right } => { // Use result_reg for left operand to save registers self.compile_node(left, result_reg)?; - + // Only allocate one temp register for right operand let right_reg = self.alloc_register()?; self.compile_node(right, right_reg)?; @@ -487,9 +544,9 @@ impl JitCompiler { } } - // Free the temporary register + // Free the temporary register self.free_register()?; // right_reg - + Ok(()) } } @@ -497,7 +554,11 @@ impl JitCompiler { /// Compile an AST node, storing the result in the specified register (AArch64) #[cfg(target_arch = "aarch64")] - fn compile_node(&mut self, node: &AstNode, result_reg: jit_assembler::aarch64::Register) -> Result<(), String> { + fn compile_node( + &mut self, + node: &AstNode, + result_reg: jit_assembler::aarch64::Register, + ) -> Result<(), String> { match node { AstNode::Number(value) => { // Load immediate value into result register @@ -507,7 +568,7 @@ impl JitCompiler { AstNode::BinaryOp { left, op, right } => { // Use result_reg for left operand to save registers self.compile_node(left, result_reg)?; - + // Only allocate one temp register for right operand let right_reg = self.alloc_register()?; self.compile_node(right, right_reg)?; @@ -531,21 +592,25 @@ impl JitCompiler { // Use X17 as temporary register (caller-saved) #[cfg(target_arch = "aarch64")] { - self.builder.udiv(reg::X17, result_reg, right_reg); // X17 = left / right - self.builder.msub(result_reg, reg::X17, right_reg, result_reg); // result = left - (X17 * right) + self.builder.udiv(reg::X17, result_reg, right_reg); // X17 = left / right + self.builder + .msub(result_reg, reg::X17, right_reg, result_reg); + // result = left - (X17 * right) } #[cfg(target_arch = "riscv64")] { // RISC-V has REM instruction, but for consistency we could implement it manually too // For now, this path should not be reached in RISC-V builds - unimplemented!("Remainder operation not implemented for RISC-V in this example"); + unimplemented!( + "Remainder operation not implemented for RISC-V in this example" + ); } } } - // Free the temporary register + // Free the temporary register self.free_register()?; // right_reg - + Ok(()) } } @@ -557,27 +622,30 @@ pub struct JitCalculator; impl JitCalculator { /// Parse and evaluate a mathematical expression using JIT compilation - pub fn evaluate(expression: &str, config: &CalculatorConfig) -> Result> { + pub fn evaluate( + expression: &str, + config: &CalculatorConfig, + ) -> Result> { println!("🔍 Parsing expression: {}", expression); - + // Parse expression into AST let mut parser = Parser::new(expression)?; let ast = parser.parse()?; - + println!("🌳 Generated AST: {}", ast); - + // Compile AST to JIT function or interpret #[cfg(any(target_arch = "riscv64", target_arch = "aarch64"))] { println!("🔧 Compiling to native machine code..."); let mut compiler = JitCompiler::new(); let jit_function = compiler.compile_expression(&ast, config)?; - + let result = jit_function(); println!("✅ JIT execution result: {}", result); Ok(result) } - + #[cfg(not(any(target_arch = "riscv64", target_arch = "aarch64")))] { println!("⚠️ Not on RISC-V or AArch64 platform, using AST interpreter"); @@ -594,7 +662,7 @@ impl JitCalculator { AstNode::BinaryOp { left, op, right } => { let left_val = Self::interpret_ast(left)?; let right_val = Self::interpret_ast(right)?; - + let result = match op { BinaryOperator::Add => left_val.wrapping_add(right_val), BinaryOperator::Subtract => left_val.wrapping_sub(right_val), @@ -627,7 +695,7 @@ impl JitCalculator { loop { println!("Enter expression:"); - + let mut input = String::new(); match std::io::stdin().read_line(&mut input) { Ok(0) => { @@ -671,25 +739,35 @@ impl JitCalculator { #[cfg(target_arch = "riscv64")] fn demonstrate_jit_compilation() { println!("Generating RISC-V machine code for multiplication (7 * 6)..."); - + // Create a multiply function and show its bytecode let mut builder = Riscv64InstructionBuilder::new(); builder.mul(reg::A0, reg::A0, reg::A1); // a0 = a0 * a1 builder.ret(); // Return - - let instructions = builder.instructions(); + + let instructions = builder.instructions().unwrap(); let bytes = instructions.to_bytes(); - - println!("📦 Generated {} instructions, {} bytes total:", instructions.len(), bytes.len()); - + + println!( + "📦 Generated {} instructions, {} bytes total:", + instructions.len(), + bytes.len() + ); + for (i, instr) in instructions.iter().enumerate() { let instr_bytes = instr.bytes(); - println!(" Instruction {}: {:02X?} ({})", - i + 1, - instr_bytes, - if instr.is_compressed() { "16-bit" } else { "32-bit" }); + println!( + " Instruction {}: {:02X?} ({})", + i + 1, + instr_bytes, + if instr.is_compressed() { + "16-bit" + } else { + "32-bit" + } + ); } - + println!("📋 Complete bytecode: {:02X?}", bytes); println!(); } @@ -698,22 +776,26 @@ fn demonstrate_jit_compilation() { #[cfg(target_arch = "aarch64")] fn demonstrate_jit_compilation() { println!("Generating AArch64 machine code for multiplication (7 * 6)..."); - + // Create a multiply function and show its bytecode let mut builder = Aarch64InstructionBuilder::new(); builder.mul(reg::X0, reg::X0, reg::X1); // X0 = X0 * X1 builder.ret(); // Return - - let instructions = builder.instructions(); + + let instructions = builder.instructions().unwrap(); let bytes = instructions.to_bytes(); - - println!("📦 Generated {} instructions, {} bytes total:", instructions.len(), bytes.len()); - + + println!( + "📦 Generated {} instructions, {} bytes total:", + instructions.len(), + bytes.len() + ); + for (i, instr) in instructions.iter().enumerate() { let instr_bytes = instr.bytes(); println!(" Instruction {}: {:02X?} (32-bit)", i + 1, instr_bytes); } - + println!("📋 Complete bytecode: {:02X?}", bytes); println!(); } @@ -730,7 +812,7 @@ fn main() { // Parse command line arguments let args: Vec = env::args().collect(); let mut config = CalculatorConfig::default(); - + // Check for --show-machine-code or -m flag for arg in &args[1..] { match arg.as_str() { @@ -748,17 +830,17 @@ fn main() { } } } - + println!("JIT Calculator with AST - M Extension Demo"); println!("=========================================="); if config.show_machine_code { println!("🤖 Machine code display: ENABLED"); } - + // Show JIT compilation details println!("\n🔍 JIT Compilation Details:"); demonstrate_jit_compilation(); - + // Demonstrate AST parsing and evaluation with various expressions let test_expressions = vec![ "42", @@ -775,7 +857,7 @@ fn main() { ]; println!("\n📋 Running predefined test expressions:\n"); - + for expression in test_expressions { match JitCalculator::evaluate(expression, &config) { Ok(result) => { @@ -811,7 +893,7 @@ fn print_help() { } /// RISC-V64 and AArch64 Integration Tests for JIT Calculator -/// +/// /// This module contains tests that only run on RISC-V64 or AArch64 platforms /// to verify that the JIT calculator works correctly with actual execution. @@ -824,23 +906,23 @@ mod tests { #[cfg(any(target_arch = "riscv64", target_arch = "aarch64"))] fn test_basic_arithmetic() { let config = CalculatorConfig::default(); - + // Test addition let result = JitCalculator::evaluate("10 + 5", &config).expect("Addition failed"); assert_eq!(result, 15); - + // Test subtraction let result = JitCalculator::evaluate("20 - 8", &config).expect("Subtraction failed"); assert_eq!(result, 12); - + // Test multiplication (M extension) let result = JitCalculator::evaluate("7 * 6", &config).expect("Multiplication failed"); assert_eq!(result, 42); - + // Test division (M extension) let result = JitCalculator::evaluate("84 / 12", &config).expect("Division failed"); assert_eq!(result, 7); - + // Test remainder (M extension) let result = JitCalculator::evaluate("23 % 7", &config).expect("Remainder failed"); assert_eq!(result, 2); @@ -851,25 +933,29 @@ mod tests { #[cfg(any(target_arch = "riscv64", target_arch = "aarch64"))] fn test_complex_expressions() { let config = CalculatorConfig::default(); - + // Test operator precedence let result = JitCalculator::evaluate("2 + 3 * 4", &config).expect("Precedence test failed"); assert_eq!(result, 14); - + // Test parentheses - let result = JitCalculator::evaluate("(2 + 3) * 4", &config).expect("Parentheses test failed"); + let result = + JitCalculator::evaluate("(2 + 3) * 4", &config).expect("Parentheses test failed"); assert_eq!(result, 20); - + // Test nested expressions - let result = JitCalculator::evaluate("100 / (10 - 5)", &config).expect("Nested expression failed"); + let result = + JitCalculator::evaluate("100 / (10 - 5)", &config).expect("Nested expression failed"); assert_eq!(result, 20); - + // Test complex nested expression - let result = JitCalculator::evaluate("((10 + 5) * 2) - 6", &config).expect("Complex expression failed"); + let result = JitCalculator::evaluate("((10 + 5) * 2) - 6", &config) + .expect("Complex expression failed"); assert_eq!(result, 24); - + // Test multiple operations - let result = JitCalculator::evaluate("2 * 3 + 4 * 5", &config).expect("Multiple operations failed"); + let result = + JitCalculator::evaluate("2 * 3 + 4 * 5", &config).expect("Multiple operations failed"); assert_eq!(result, 26); } @@ -878,21 +964,22 @@ mod tests { #[cfg(any(target_arch = "riscv64", target_arch = "aarch64"))] fn test_edge_cases() { let config = CalculatorConfig::default(); - + // Test single number let result = JitCalculator::evaluate("42", &config).expect("Single number failed"); assert_eq!(result, 42); - + // Test zero let result = JitCalculator::evaluate("0", &config).expect("Zero failed"); assert_eq!(result, 0); - + // Test large numbers let result = JitCalculator::evaluate("1000 + 2000", &config).expect("Large numbers failed"); assert_eq!(result, 3000); - + // Test moderately nested parentheses (reduced complexity) - let result = JitCalculator::evaluate("((2 + 3) * 4) + 5", &config).expect("Nested expression failed"); + let result = JitCalculator::evaluate("((2 + 3) * 4) + 5", &config) + .expect("Nested expression failed"); assert_eq!(result, 25); // (5 * 4) + 5 = 20 + 5 = 25 } @@ -901,19 +988,23 @@ mod tests { #[cfg(any(target_arch = "riscv64", target_arch = "aarch64"))] fn test_register_allocation() { let config = CalculatorConfig::default(); - + // Test expressions that would stress register allocation (simplified) let test_cases = vec![ - ("1 + 2 + 3", 6), // Simpler chain to reduce register usage - ("2 * 3 * 4", 24), // Simpler multiplication chain + ("1 + 2 + 3", 6), // Simpler chain to reduce register usage + ("2 * 3 * 4", 24), // Simpler multiplication chain ("(1 + 2) * (3 + 4)", 21), // Single nested expression - ("10 + 5 * 2 - 4", 16), // Mixed operations: 10 + 10 - 4 = 16 + ("10 + 5 * 2 - 4", 16), // Mixed operations: 10 + 10 - 4 = 16 ]; - + for (expression, expected) in test_cases { let result = JitCalculator::evaluate(expression, &config) .expect(&format!("Expression '{}' failed", expression)); - assert_eq!(result, expected, "Expression '{}' returned {} instead of {}", expression, result, expected); + assert_eq!( + result, expected, + "Expression '{}' returned {} instead of {}", + expression, result, expected + ); } } @@ -922,44 +1013,57 @@ mod tests { #[cfg(any(target_arch = "riscv64", target_arch = "aarch64"))] fn test_m_extension() { let config = CalculatorConfig::default(); - + // Test various multiplication cases let mul_cases = vec![ ("0 * 100", 0), ("1 * 1", 1), ("12 * 12", 144), - ("16 * 16", 256), // Use smaller numbers to avoid encoding issues + ("16 * 16", 256), // Use smaller numbers to avoid encoding issues ]; - + for (expr, expected) in mul_cases { - let result = JitCalculator::evaluate(expr, &config).expect("Multiplication test failed"); - assert_eq!(result, expected, "Multiplication: {} should equal {}", expr, expected); + let result = + JitCalculator::evaluate(expr, &config).expect("Multiplication test failed"); + assert_eq!( + result, expected, + "Multiplication: {} should equal {}", + expr, expected + ); } - + // Test division cases let div_cases = vec![ ("100 / 10", 10), ("1 / 1", 1), ("144 / 12", 12), - ("1000 / 10", 100), // Use smaller numbers to avoid immediate encoding issues + ("1000 / 10", 100), // Use smaller numbers to avoid immediate encoding issues ]; - + for (expr, expected) in div_cases { let result = JitCalculator::evaluate(expr, &config).expect("Division test failed"); - assert_eq!(result, expected, "Division: {} should equal {}", expr, expected); + assert_eq!( + result, expected, + "Division: {} should equal {}", + expr, expected + ); } - + // Test remainder cases let rem_cases = vec![ ("10 % 3", 1), ("100 % 7", 2), - ("50 % 16", 2), // 50 = 3 * 16 + 2 - ("123 % 10", 3), // 123 = 12 * 10 + 3 + ("50 % 16", 2), // 50 = 3 * 16 + 2 + ("123 % 10", 3), // 123 = 12 * 10 + 3 ]; - + for (expr, expected) in rem_cases { let result = JitCalculator::evaluate(expr, &config).expect("Remainder test failed"); - assert_eq!(result, expected, "Remainder: {} should equal {}", expr, expected); + assert_eq!( + result, expected, + "Remainder: {} should equal {}", + expr, expected + ); } } @@ -968,16 +1072,20 @@ mod tests { #[cfg(any(target_arch = "riscv64", target_arch = "aarch64"))] fn test_jit_compilation_performance() { let config = CalculatorConfig::default(); - + // Create the same calculation multiple times to ensure JIT is actually working let expression = "(123 + 456) * (789 - 456) + 999"; let expected = (123 + 456) * (789 - 456) + 999; // Calculate expected result - + // Run multiple times to verify consistency for i in 1..=10 { let result = JitCalculator::evaluate(expression, &config) .expect(&format!("JIT test iteration {} failed", i)); - assert_eq!(result, expected, "JIT compilation inconsistent at iteration {}", i); + assert_eq!( + result, expected, + "JIT compilation inconsistent at iteration {}", + i + ); } } @@ -987,13 +1095,14 @@ mod tests { fn test_machine_code_generation() { let mut config = CalculatorConfig::default(); config.show_machine_code = true; - + // Test that machine code display doesn't break execution let result = JitCalculator::evaluate("42 + 13", &config).expect("Machine code test failed"); assert_eq!(result, 55); - + // Test with complex expression - let result = JitCalculator::evaluate("(10 * 5) + (20 / 4)", &config).expect("Complex machine code test failed"); + let result = JitCalculator::evaluate("(10 * 5) + (20 / 4)", &config) + .expect("Complex machine code test failed"); assert_eq!(result, 55); // 50 + 5 = 55 } } diff --git a/examples/jit_execution.rs b/examples/jit_execution.rs index 7239c9e..5a402a2 100644 --- a/examples/jit_execution.rs +++ b/examples/jit_execution.rs @@ -1,15 +1,15 @@ //! JIT Execution Example -//! +//! //! This example demonstrates how to use the JIT execution functionality //! to create and execute functions at runtime. -//! +//! //! This example supports both RISC-V and AArch64 architectures depending //! on the enabled features. use jit_assembler::common::InstructionBuilder; #[cfg(feature = "riscv64")] -use jit_assembler::riscv64::{reg as riscv_reg, csr, Riscv64InstructionBuilder}; +use jit_assembler::riscv64::{csr, reg as riscv_reg, Riscv64InstructionBuilder}; #[cfg(feature = "aarch64")] use jit_assembler::aarch64::{reg as aarch64_reg, Aarch64InstructionBuilder}; @@ -25,7 +25,9 @@ fn main() { aarch64_examples(); #[cfg(not(any(feature = "riscv64", feature = "aarch64")))] - println!("No architecture features enabled. Enable 'riscv' or 'aarch64' features to see examples."); + println!( + "No architecture features enabled. Enable 'riscv' or 'aarch64' features to see examples." + ); } #[cfg(feature = "riscv64")] @@ -36,8 +38,8 @@ fn riscv_examples() { println!("\n1. Creating a function that returns 42..."); let constant_func = unsafe { Riscv64InstructionBuilder::new() - .addi(riscv_reg::A0, riscv_reg::ZERO, 42) // Load 42 into a0 (return value) - .ret() // Return + .addi(riscv_reg::A0, riscv_reg::ZERO, 42) // Load 42 into a0 (return value) + .ret() // Return .function:: u64>() }; @@ -58,8 +60,8 @@ fn riscv_examples() { println!("\n2. Creating a function that adds two numbers..."); let add_func = unsafe { Riscv64InstructionBuilder::new() - .add(riscv_reg::A0, riscv_reg::A0, riscv_reg::A1) // Add a0 + a1, result in a0 - .ret() // Return + .add(riscv_reg::A0, riscv_reg::A0, riscv_reg::A1) // Add a0 + a1, result in a0 + .ret() // Return .function:: u64>() }; @@ -80,9 +82,9 @@ fn riscv_examples() { println!("\n3. Creating a function that computes (x + 100) * 2..."); let complex_func = unsafe { Riscv64InstructionBuilder::new() - .addi(riscv_reg::A0, riscv_reg::A0, 100) // x + 100 - .slli(riscv_reg::A0, riscv_reg::A0, 1) // << 1 (multiply by 2) - .ret() // Return + .addi(riscv_reg::A0, riscv_reg::A0, 100) // x + 100 + .slli(riscv_reg::A0, riscv_reg::A0, 1) // << 1 (multiply by 2) + .ret() // Return .function:: u64>() }; @@ -103,8 +105,8 @@ fn riscv_examples() { println!("\n4. Creating a function that reads MEPC CSR..."); let csr_func = unsafe { Riscv64InstructionBuilder::new() - .csrr(riscv_reg::A0, csr::MEPC) // Read MEPC into a0 - .ret() // Return + .csrr(riscv_reg::A0, csr::MEPC) // Read MEPC into a0 + .ret() // Return .function:: u64>() }; @@ -129,8 +131,8 @@ fn aarch64_examples() { println!("\n1. Creating a function that returns 42..."); let constant_func = unsafe { Aarch64InstructionBuilder::new() - .mov_imm(aarch64_reg::X0, 42) // Load 42 into X0 (return value) - .ret() // Return + .mov_imm(aarch64_reg::X0, 42) // Load 42 into X0 (return value) + .ret() // Return .function:: u64>() }; @@ -151,8 +153,8 @@ fn aarch64_examples() { println!("\n2. Creating a function that adds two numbers..."); let add_func = unsafe { Aarch64InstructionBuilder::new() - .add(aarch64_reg::X0, aarch64_reg::X0, aarch64_reg::X1) // Add X0 + X1, result in X0 - .ret() // Return + .add(aarch64_reg::X0, aarch64_reg::X0, aarch64_reg::X1) // Add X0 + X1, result in X0 + .ret() // Return .function:: u64>() }; @@ -173,10 +175,10 @@ fn aarch64_examples() { println!("\n3. Creating a function that computes (x + 100) * 2..."); let complex_func = unsafe { Aarch64InstructionBuilder::new() - .addi(aarch64_reg::X0, aarch64_reg::X0, 100) // x + 100 - .mov_imm(aarch64_reg::X1, 2) // Load 2 into X1 - .mul(aarch64_reg::X0, aarch64_reg::X0, aarch64_reg::X1) // Multiply by 2 - .ret() // Return + .addi(aarch64_reg::X0, aarch64_reg::X0, 100) // x + 100 + .mov_imm(aarch64_reg::X1, 2) // Load 2 into X1 + .mul(aarch64_reg::X0, aarch64_reg::X0, aarch64_reg::X1) // Multiply by 2 + .ret() // Return .function:: u64>() }; @@ -195,4 +197,4 @@ fn aarch64_examples() { println!("\nAArch64 examples completed!"); println!("Note: To actually execute these functions, run this example on an AArch64 system."); -} \ No newline at end of file +} diff --git a/examples/register_tracking_demo.rs b/examples/register_tracking_demo.rs index 74175ed..928ad02 100644 --- a/examples/register_tracking_demo.rs +++ b/examples/register_tracking_demo.rs @@ -1,24 +1,30 @@ -use jit_assembler::riscv64::{Riscv64InstructionBuilder, reg}; use jit_assembler::common::InstructionBuilder; +use jit_assembler::riscv64::{reg, Riscv64InstructionBuilder}; #[cfg(feature = "register-tracking")] fn main() { let mut builder = Riscv64InstructionBuilder::new(); - + // Add some instructions builder - .add(reg::T0, reg::T1, reg::T2) // T0, T1, T2を使用 - .addi(reg::T3, reg::SP, 16) // T3, SPを使用 - .mul(reg::A0, reg::A1, reg::A2) // A0, A1, A2を使用 - .ld(reg::S0, reg::T0, 8) // S0, T0を使用 - .sd(reg::SP, reg::S1, -16); // SP, S1を使用 - + .add(reg::T0, reg::T1, reg::T2) // T0, T1, T2を使用 + .addi(reg::T3, reg::SP, 16) // T3, SPを使用 + .mul(reg::A0, reg::A1, reg::A2) // A0, A1, A2を使用 + .ld(reg::S0, reg::T0, 8) // S0, T0を使用 + .sd(reg::SP, reg::S1, -16); // SP, S1を使用 + let usage = builder.register_usage(); - + println!("=== Register Usage Analysis ==="); println!("Total registers used: {}", usage.register_count()); - println!("Caller-saved registers: {:?}", usage.caller_saved_registers()); - println!("Callee-saved registers: {:?}", usage.callee_saved_registers()); + println!( + "Caller-saved registers: {:?}", + usage.caller_saved_registers() + ); + println!( + "Callee-saved registers: {:?}", + usage.callee_saved_registers() + ); println!("Special registers: {:?}", usage.special_registers()); println!("Needs stack frame: {}", usage.needs_stack_frame()); println!("Usage info: {}", usage); @@ -27,4 +33,4 @@ fn main() { #[cfg(not(feature = "register-tracking"))] fn main() { println!("Register tracking feature is not enabled. Run with --features register-tracking"); -} \ No newline at end of file +} diff --git a/src/aarch64/builder.rs b/src/aarch64/builder.rs index 288ae18..2ac5010 100644 --- a/src/aarch64/builder.rs +++ b/src/aarch64/builder.rs @@ -1,15 +1,16 @@ /// Instruction builder interface for AArch64 assembly generation use super::instruction::*; -use crate::common::InstructionBuilder; +use crate::common::{BuildError, InstructionBuilder}; -#[cfg(feature = "std")] -use std::vec::Vec; #[cfg(not(feature = "std"))] use alloc::vec::Vec; +#[cfg(feature = "std")] +use std::vec::Vec; /// Instruction builder for generating AArch64 instructions pub struct Aarch64InstructionBuilder { instructions: Vec, + error: Option, #[cfg(feature = "register-tracking")] register_usage: crate::common::register_usage::RegisterUsageInfo, } @@ -18,23 +19,24 @@ impl Aarch64InstructionBuilder { pub fn new() -> Self { Self { instructions: Vec::new(), + error: None, #[cfg(feature = "register-tracking")] register_usage: crate::common::register_usage::RegisterUsageInfo::new(), } } - + /// Track a written register (register-tracking feature only) #[cfg(feature = "register-tracking")] fn track_written_register(&mut self, reg: Register) { self.register_usage.add_written_register(reg); } - + /// Track a read register (register-tracking feature only) #[cfg(feature = "register-tracking")] fn track_read_register(&mut self, reg: Register) { self.register_usage.add_read_register(reg); } - + /// Track multiple read registers at once (register-tracking feature only) #[cfg(feature = "register-tracking")] fn track_read_registers(&mut self, regs: &[Register]) { @@ -42,18 +44,18 @@ impl Aarch64InstructionBuilder { self.register_usage.add_read_register(reg); } } - + /// No-op versions for when register-tracking is disabled #[cfg(not(feature = "register-tracking"))] fn track_written_register(&mut self, _reg: Register) { // No-op } - + #[cfg(not(feature = "register-tracking"))] fn track_read_register(&mut self, _reg: Register) { // No-op } - + #[cfg(not(feature = "register-tracking"))] fn track_read_registers(&mut self, _regs: &[Register]) { // No-op @@ -166,22 +168,22 @@ impl Aarch64InstructionBuilder { // DEPRECATED: urem was using a hardcoded temporary register (X17) which is unsafe // Users should implement remainder manually using udiv + msub with their own temp register - // Example: - // .udiv(temp_reg, dividend, divisor) // temp = dividend / divisor + // Example: + // .udiv(temp_reg, dividend, divisor) // temp = dividend / divisor // .msub(result, temp, divisor, dividend) // result = dividend - (temp * divisor) // - // /// Generate remainder operation using MSUB after division + // /// Generate remainder operation using MSUB after division // /// This creates a sequence: UDIV tmp, rn, rm; MSUB rd, tmp, rm, rn // /// Result: rd = rn - (rn / rm) * rm = rn % rm // pub fn urem(&mut self, rd: Register, rn: Register, rm: Register) -> &mut Self { // // We need a temporary register - use X17 (IP1) which is caller-saved // let tmp = reg::X17; - // + // // // UDIV tmp, rn, rm // self.track_read_registers(&[rn, rm]); // let div_instr = encode_divide(1, 0b000010, rm, 1, rn, tmp); // opcode=000010 for UDIV // self.push(div_instr); - // + // // // MSUB rd, tmp, rm, rn (rd = rn - tmp * rm) // self.track_written_register(rd); // self.track_read_registers(&[tmp, rm, rn]); @@ -276,19 +278,19 @@ impl Aarch64InstructionBuilder { } // Break down the 64-bit immediate into 16-bit chunks - let chunk0 = (imm & 0xFFFF) as u16; // Bits 0-15 - let chunk1 = ((imm >> 16) & 0xFFFF) as u16; // Bits 16-31 + let chunk0 = (imm & 0xFFFF) as u16; // Bits 0-15 + let chunk1 = ((imm >> 16) & 0xFFFF) as u16; // Bits 16-31 let chunk2 = ((imm >> 32) & 0xFFFF) as u16; // Bits 32-47 let chunk3 = ((imm >> 48) & 0xFFFF) as u16; // Bits 48-63 // Find the first non-zero chunk to use MOVZ let mut first_movz_done = false; - + if chunk0 != 0 { self.movz(rd, chunk0, 0); // LSL #0 first_movz_done = true; } - + if chunk1 != 0 { if first_movz_done { self.movk(rd, chunk1, 1); // LSL #16 @@ -297,7 +299,7 @@ impl Aarch64InstructionBuilder { first_movz_done = true; } } - + if chunk2 != 0 { if first_movz_done { self.movk(rd, chunk2, 2); // LSL #32 @@ -306,7 +308,7 @@ impl Aarch64InstructionBuilder { first_movz_done = true; } } - + if chunk3 != 0 { if first_movz_done { self.movk(rd, chunk3, 3); // LSL #48 @@ -346,17 +348,31 @@ impl Aarch64InstructionBuilder { impl InstructionBuilder for Aarch64InstructionBuilder { type Register = Register; - + type Error = BuildError; + fn new() -> Self { Self { instructions: Vec::new(), + error: None, #[cfg(feature = "register-tracking")] register_usage: crate::common::register_usage::RegisterUsageInfo::new(), } } - fn instructions(&self) -> crate::common::InstructionCollection { - crate::common::InstructionCollection::from_slice(&self.instructions) + fn instructions( + &self, + ) -> Result, Self::Error> { + if let Some(ref error) = self.error { + Err(*error) + } else { + Ok(crate::common::InstructionCollection::from_slice( + &self.instructions, + )) + } + } + + fn set_error(&mut self, error: Self::Error) { + self.error = Some(error); } fn push(&mut self, instr: Instruction) { @@ -365,58 +381,65 @@ impl InstructionBuilder for Aarch64InstructionBuilder { fn clear(&mut self) { self.instructions.clear(); + self.error = None; #[cfg(feature = "register-tracking")] self.register_usage.clear(); } - + #[cfg(feature = "register-tracking")] fn register_usage(&self) -> &crate::common::register_usage::RegisterUsageInfo { &self.register_usage } - + #[cfg(feature = "register-tracking")] - fn register_usage_mut(&mut self) -> &mut crate::common::register_usage::RegisterUsageInfo { + fn register_usage_mut( + &mut self, + ) -> &mut crate::common::register_usage::RegisterUsageInfo { &mut self.register_usage } - + /// Create a JIT-compiled function from the assembled instructions (std-only) - /// + /// /// This method converts the assembled instructions into executable machine code /// that can be called directly as a function. The generic type parameter `F` /// specifies the function signature. - /// + /// /// # Safety - /// + /// /// This function is unsafe because: /// - It allocates executable memory /// - It assumes the assembled code follows the correct ABI /// - The caller must ensure the function signature matches the actual code - /// + /// /// # Examples - /// + /// /// ```rust,no_run /// use jit_assembler::aarch64::{reg, Aarch64InstructionBuilder}; /// use jit_assembler::common::InstructionBuilder; - /// + /// /// let add_func = unsafe { /// Aarch64InstructionBuilder::new() /// .add(reg::X0, reg::X0, reg::X1) // Add first two arguments /// .ret() /// .function:: u64>() /// }.expect("Failed to create JIT function"); - /// + /// /// // Call the JIT function directly (only works on AArch64 hosts) /// // let result = add_func.call(10, 20); // Returns 30 /// ``` #[cfg(feature = "std")] - unsafe fn function(&self) -> Result, crate::common::jit::JitError> { - let bytes = self.instructions().to_bytes(); + unsafe fn function( + &self, + ) -> Result, crate::common::jit::JitError> { + let bytes = self.instructions()?.to_bytes(); crate::common::jit::CallableJitFunction::::new(&bytes) } - + #[cfg(feature = "std")] - unsafe fn raw_function(&self) -> Result { - let bytes = self.instructions().to_bytes(); + unsafe fn raw_function( + &self, + ) -> Result { + let bytes = self.instructions()?.to_bytes(); crate::common::jit::RawCallableJitFunction::new(&bytes) } -} \ No newline at end of file +} diff --git a/src/aarch64/instruction.rs b/src/aarch64/instruction.rs index e05e8b5..93371b3 100644 --- a/src/aarch64/instruction.rs +++ b/src/aarch64/instruction.rs @@ -1,14 +1,11 @@ +use crate::common::{Instruction as InstructionTrait, Register as RegisterTrait}; /// AArch64 instruction formats and encoding use core::fmt; -use crate::common::{ - Instruction as InstructionTrait, - Register as RegisterTrait, -}; -#[cfg(feature = "std")] -use std::vec::Vec; #[cfg(not(feature = "std"))] use alloc::vec::Vec; +#[cfg(feature = "std")] +use std::vec::Vec; /// AArch64 register representation (32 general-purpose registers) #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -28,24 +25,24 @@ impl RegisterTrait for Register { fn id(&self) -> u32 { self.0 as u32 } - + fn abi_class(&self) -> crate::common::AbiClass { use crate::common::AbiClass; - + match self.0 { // Caller-saved registers (do not need to be preserved across calls) - 0..=7 => AbiClass::CallerSaved, // X0-X7: Argument/return value registers - 8..=15 => AbiClass::CallerSaved, // X8-X15: Caller-saved temporary registers - 16..=17 => AbiClass::CallerSaved, // X16-X17: Intra-procedure-call registers - 18 => AbiClass::CallerSaved, // X18: Platform register (caller-saved on most platforms) - + 0..=7 => AbiClass::CallerSaved, // X0-X7: Argument/return value registers + 8..=15 => AbiClass::CallerSaved, // X8-X15: Caller-saved temporary registers + 16..=17 => AbiClass::CallerSaved, // X16-X17: Intra-procedure-call registers + 18 => AbiClass::CallerSaved, // X18: Platform register (caller-saved on most platforms) + // Callee-saved registers (must be preserved across calls) - 19..=28 => AbiClass::CalleeSaved, // X19-X28: Callee-saved registers - + 19..=28 => AbiClass::CalleeSaved, // X19-X28: Callee-saved registers + // Special-purpose registers - 29 => AbiClass::Special, // X29: Frame pointer (FP) - 30 => AbiClass::Special, // X30: Link register (LR) - 31 => AbiClass::Special, // X31: Stack pointer (SP) or zero register (XZR) + 29 => AbiClass::Special, // X29: Frame pointer (FP) + 30 => AbiClass::Special, // X30: Link register (LR) + 31 => AbiClass::Special, // X31: Stack pointer (SP) or zero register (XZR) // Default to Special for any unhandled registers _ => AbiClass::Special, @@ -78,11 +75,11 @@ impl InstructionTrait for Instruction { fn value(&self) -> u64 { self.0 as u64 } - + fn bytes(&self) -> Vec { self.0.to_le_bytes().to_vec() } - + fn size(&self) -> usize { 4 } @@ -95,15 +92,23 @@ impl fmt::Display for Instruction { } /// Data Processing - Register instruction encoding (3-operand) -pub fn encode_add_sub_reg(sf: u8, op: u8, s: u8, rm: Register, imm6: u8, rn: Register, rd: Register) -> Instruction { +pub fn encode_add_sub_reg( + sf: u8, + op: u8, + s: u8, + rm: Register, + imm6: u8, + rn: Register, + rd: Register, +) -> Instruction { // ADD/SUB (shifted register) encoding according to AArch64 ISA // 31: sf (0=32-bit, 1=64-bit) - // 30: op (0=ADD, 1=SUB) + // 30: op (0=ADD, 1=SUB) // 29: s (0=don't set flags, 1=set flags) // 28-24: 01011 (fixed for shifted register) // 23-22: shift (00=LSL, 01=LSR, 10=ASR, 11=reserved) // 21: 0 (fixed) - // 20-16: Rm + // 20-16: Rm // 15-10: imm6 (shift amount) // 9-5: Rn // 4-0: Rd @@ -121,7 +126,15 @@ pub fn encode_add_sub_reg(sf: u8, op: u8, s: u8, rm: Register, imm6: u8, rn: Reg } /// Data Processing - Immediate instruction encoding (ADD/SUB immediate) -pub fn encode_add_sub_imm(sf: u8, op: u8, s: u8, sh: u8, imm12: u16, rn: Register, rd: Register) -> Instruction { +pub fn encode_add_sub_imm( + sf: u8, + op: u8, + s: u8, + sh: u8, + imm12: u16, + rn: Register, + rd: Register, +) -> Instruction { let instr = ((sf as u32) << 31) | ((op as u32) << 30) | ((s as u32) << 29) | @@ -134,7 +147,16 @@ pub fn encode_add_sub_imm(sf: u8, op: u8, s: u8, sh: u8, imm12: u16, rn: Registe } /// Logical instruction encoding (register) -pub fn encode_logical_reg(sf: u8, opc: u8, shift: u8, n: u8, rm: Register, imm6: u8, rn: Register, rd: Register) -> Instruction { +pub fn encode_logical_reg( + sf: u8, + opc: u8, + shift: u8, + n: u8, + rm: Register, + imm6: u8, + rn: Register, + rd: Register, +) -> Instruction { let instr = ((sf as u32) << 31) | ((opc as u32) << 29) | (0b01010 << 24) | // Fixed bits for logical register @@ -148,7 +170,15 @@ pub fn encode_logical_reg(sf: u8, opc: u8, shift: u8, n: u8, rm: Register, imm6: } /// Multiply instruction encoding -pub fn encode_multiply(sf: u8, op31: u8, rm: Register, o0: u8, ra: Register, rn: Register, rd: Register) -> Instruction { +pub fn encode_multiply( + sf: u8, + op31: u8, + rm: Register, + o0: u8, + ra: Register, + rn: Register, + rd: Register, +) -> Instruction { // For MUL x0, x1, x2 -> encoding should be 0x9b027c20 // AArch64 Data Processing -- 3 source format: // sf | op54 | 11011 | op31 | Rm | o0 | Ra | Rn | Rd @@ -161,12 +191,19 @@ pub fn encode_multiply(sf: u8, op31: u8, rm: Register, o0: u8, ra: Register, rn: ((o0 as u32) << 15) | // o0 (operation variant) ((ra.value() as u32) << 10) | // Ra (accumulator, XZR for MUL) ((rn.value() as u32) << 5) | // Rn (source register 1) - (rd.value() as u32); // Rd (destination) + (rd.value() as u32); // Rd (destination) Instruction::new(instr) } /// Division instruction encoding - Data Processing (2 source) -pub fn encode_divide(sf: u8, opcode: u8, rm: Register, _o0: u8, rn: Register, rd: Register) -> Instruction { +pub fn encode_divide( + sf: u8, + opcode: u8, + rm: Register, + _o0: u8, + rn: Register, + rd: Register, +) -> Instruction { // Data-processing (2 source) format: // sf | 0 | S | 11010110 | Rm | opcode | Rn | Rd // For UDIV: opcode = 000010 @@ -178,7 +215,7 @@ pub fn encode_divide(sf: u8, opcode: u8, rm: Register, _o0: u8, rn: Register, rd ((rm.value() as u32) << 16) | // Rm register ((opcode as u32) << 10) | // opcode (UDIV=000010, SDIV=000011) ((rn.value() as u32) << 5) | // Rn register - (rd.value() as u32); // Rd register + (rd.value() as u32); // Rd register Instruction::new(instr) } @@ -196,25 +233,25 @@ pub fn encode_ret(rn: Register) -> Instruction { // The 0x5f pattern is bits 16-22: 0101111 (bit pattern: 0x5f at positions 16-22) let instr = 0xd6000000 | // Base RET instruction pattern (0x5f << 16) | // bits 22-16: 0101111 (matches GNU assembler exactly) - ((rn.value() as u32) << 5); // bits 9-5: Rn + ((rn.value() as u32) << 5); // bits 9-5: Rn Instruction::new(instr) } /// Branch register instruction encoding (BR) pub fn encode_branch_reg(opc: u8, op2: u8, op3: u8, rn: Register, op4: u8) -> Instruction { - let instr = (0b1101011 << 25) | - ((opc as u32) << 21) | - ((op2 as u32) << 16) | - ((op3 as u32) << 10) | - ((rn.value() as u32) << 5) | - (op4 as u32); + let instr = (0b1101011 << 25) + | ((opc as u32) << 21) + | ((op2 as u32) << 16) + | ((op3 as u32) << 10) + | ((rn.value() as u32) << 5) + | (op4 as u32); Instruction::new(instr) } /// Common registers pub mod reg { use super::Register; - + // Standard register names (X0-X30) pub const X0: Register = Register::new(0); pub const X1: Register = Register::new(1); @@ -247,15 +284,15 @@ pub mod reg { pub const X28: Register = Register::new(28); pub const X29: Register = Register::new(29); pub const X30: Register = Register::new(30); - + // X31 is special - it's SP in some contexts, XZR/WZR in others - pub const SP: Register = Register::new(31); // Stack pointer - pub const XZR: Register = Register::new(31); // Zero register (64-bit) - pub const WZR: Register = Register::new(31); // Zero register (32-bit) + pub const SP: Register = Register::new(31); // Stack pointer + pub const XZR: Register = Register::new(31); // Zero register (64-bit) + pub const WZR: Register = Register::new(31); // Zero register (32-bit) // AArch64 ABI register aliases - pub const FP: Register = X29; // Frame pointer - pub const LR: Register = X30; // Link register + pub const FP: Register = X29; // Frame pointer + pub const LR: Register = X30; // Link register } /// MOVZ instruction encoding - Move immediate with zero @@ -268,7 +305,7 @@ pub fn encode_movz(sf: u8, hw: u8, imm16: u16, rd: Register) -> Instruction { (0b100101 << 23) | // Fixed bits for move wide immediate ((hw as u32) << 21) | // hw (shift amount / 16) ((imm16 as u32) << 5) | // imm16 (16-bit immediate) - (rd.value() as u32); // Rd (destination register) + (rd.value() as u32); // Rd (destination register) Instruction::new(instr) } @@ -282,6 +319,6 @@ pub fn encode_movk(sf: u8, hw: u8, imm16: u16, rd: Register) -> Instruction { (0b100101 << 23) | // Fixed bits for move wide immediate ((hw as u32) << 21) | // hw (shift amount / 16) ((imm16 as u32) << 5) | // imm16 (16-bit immediate) - (rd.value() as u32); // Rd (destination register) + (rd.value() as u32); // Rd (destination register) Instruction::new(instr) -} \ No newline at end of file +} diff --git a/src/aarch64/macros.rs b/src/aarch64/macros.rs index d37fc2e..2eb4637 100644 --- a/src/aarch64/macros.rs +++ b/src/aarch64/macros.rs @@ -24,4 +24,4 @@ macro_rules! aarch64_asm { ($($method:ident($($args:expr),*);)*) => {{ $crate::jit_asm_generic!($crate::aarch64::Aarch64InstructionBuilder, $($method($($args),*);)*) }}; -} \ No newline at end of file +} diff --git a/src/aarch64/mod.rs b/src/aarch64/mod.rs index f439386..bb56382 100644 --- a/src/aarch64/mod.rs +++ b/src/aarch64/mod.rs @@ -39,13 +39,13 @@ //! // let result = func.call(10, 20); // Returns 30 //! ``` -pub mod instruction; pub mod builder; +pub mod instruction; pub mod macros; #[cfg(test)] mod tests; // Re-export commonly used items -pub use instruction::{Register, Instruction, reg}; -pub use builder::Aarch64InstructionBuilder; \ No newline at end of file +pub use builder::Aarch64InstructionBuilder; +pub use instruction::{reg, Instruction, Register}; diff --git a/src/aarch64/tests.rs b/src/aarch64/tests.rs index d839def..f5d589a 100644 --- a/src/aarch64/tests.rs +++ b/src/aarch64/tests.rs @@ -1,13 +1,11 @@ -use crate::aarch64::{reg, Aarch64InstructionBuilder}; #[cfg(feature = "std")] use crate::aarch64::Instruction; - - +use crate::aarch64::{reg, Aarch64InstructionBuilder}; -#[cfg(feature = "std")] -use std::process::Command; #[cfg(feature = "std")] use std::fs; +#[cfg(feature = "std")] +use std::process::Command; use crate::common::InstructionBuilder; @@ -15,13 +13,15 @@ use crate::common::InstructionBuilder; #[cfg(feature = "std")] fn assemble_aarch64(assembly: &str) -> Vec { use std::io::Write; - + // Create temporary assembly file let asm_content = format!(".text\n{}", assembly); let mut asm_file = tempfile::NamedTempFile::new().expect("Failed to create temp file"); - asm_file.write_all(asm_content.as_bytes()).expect("Failed to write assembly"); + asm_file + .write_all(asm_content.as_bytes()) + .expect("Failed to write assembly"); asm_file.flush().expect("Failed to flush temp file"); - + // Assemble with GNU assembler let obj_file = tempfile::NamedTempFile::new().expect("Failed to create temp obj file"); let output = Command::new("aarch64-linux-gnu-as") @@ -29,7 +29,7 @@ fn assemble_aarch64(assembly: &str) -> Vec { .arg(obj_file.path()) .arg(asm_file.path()) .output(); - + match output { Ok(result) => { if !result.status.success() { @@ -45,7 +45,7 @@ fn assemble_aarch64(assembly: &str) -> Vec { return vec![]; } } - + // Check if object file exists and is not empty if let Ok(metadata) = fs::metadata(obj_file.path()) { if metadata.len() == 0 { @@ -53,7 +53,7 @@ fn assemble_aarch64(assembly: &str) -> Vec { return vec![]; } } - + // Extract binary data from object file using objcopy let bin_file = tempfile::NamedTempFile::new().expect("Failed to create temp bin file"); let objcopy_result = Command::new("aarch64-linux-gnu-objcopy") @@ -64,18 +64,23 @@ fn assemble_aarch64(assembly: &str) -> Vec { .arg(bin_file.path()) .output() .expect("Failed to run objcopy"); - + if !objcopy_result.status.success() { - println!("Warning: objcopy failed, skipping comparison test: {}", - String::from_utf8_lossy(&objcopy_result.stderr)); + println!( + "Warning: objcopy failed, skipping comparison test: {}", + String::from_utf8_lossy(&objcopy_result.stderr) + ); return vec![]; } - + // Read binary data match fs::read(bin_file.path()) { Ok(data) => data, Err(e) => { - println!("Warning: Failed to read binary file, skipping comparison test: {}", e); + println!( + "Warning: Failed to read binary file, skipping comparison test: {}", + e + ); vec![] } } @@ -86,18 +91,21 @@ fn assemble_aarch64(assembly: &str) -> Vec { fn compare_instruction(jit_instr: Instruction, gnu_assembly: &str) { let jit_bytes = jit_instr.bytes(); let gnu_bytes = assemble_aarch64(gnu_assembly); - + // Skip comparison if GNU assembler is not available if gnu_bytes.is_empty() { return; } - + // The GNU assembler output might have extra padding, so we only compare the first 4 bytes assert_eq!(jit_bytes.len(), 4, "JIT instruction should be 4 bytes"); - assert!(gnu_bytes.len() >= 4, "GNU assembler output should be at least 4 bytes"); - + assert!( + gnu_bytes.len() >= 4, + "GNU assembler output should be at least 4 bytes" + ); + assert_eq!( - jit_bytes, + jit_bytes, &gnu_bytes[0..4], "JIT assembler output does not match GNU assembler output\nJIT: {:02x?}\nGNU: {:02x?}\nAssembly: {}", jit_bytes, @@ -116,7 +124,7 @@ fn test_register_values() { assert_eq!(reg::X30.value(), 30); // LR assert_eq!(reg::SP.value(), 31); assert_eq!(reg::XZR.value(), 31); - + // Test aliases assert_eq!(reg::FP.value(), 29); assert_eq!(reg::LR.value(), 30); @@ -125,39 +133,39 @@ fn test_register_values() { #[test] fn test_basic_arithmetic_instructions() { let mut builder = Aarch64InstructionBuilder::new(); - + builder.add(reg::X0, reg::X1, reg::X2); builder.addi(reg::X3, reg::X4, 100); builder.sub(reg::X5, reg::X6, reg::X7); builder.subi(reg::X8, reg::X9, 50); builder.mul(reg::X10, reg::X11, reg::X12); builder.udiv(reg::X13, reg::X14, reg::X15); - - let instructions = builder.instructions(); + + let instructions = builder.instructions().unwrap(); assert_eq!(instructions.len(), 6); } #[test] fn test_logical_instructions() { let mut builder = Aarch64InstructionBuilder::new(); - + builder.and(reg::X0, reg::X1, reg::X2); builder.or(reg::X3, reg::X4, reg::X5); builder.xor(reg::X6, reg::X7, reg::X8); builder.mov(reg::X9, reg::X10); - - let instructions = builder.instructions(); + + let instructions = builder.instructions().unwrap(); assert_eq!(instructions.len(), 4); } #[test] fn test_control_flow_instructions() { let mut builder = Aarch64InstructionBuilder::new(); - + builder.ret(); builder.ret_reg(reg::X1); - - let instructions = builder.instructions(); + + let instructions = builder.instructions().unwrap(); assert_eq!(instructions.len(), 2); } @@ -166,23 +174,23 @@ fn test_instruction_encoding() { // Test a simple ADD instruction: ADD X0, X1, X2 let mut builder = Aarch64InstructionBuilder::new(); builder.add(reg::X0, reg::X1, reg::X2); - let instructions = builder.instructions(); - + let instructions = builder.instructions().unwrap(); + assert_eq!(instructions.len(), 1); - + // Check that the instruction has expected bits let instr = instructions[0]; let value = instr.value(); - + // For ADD X0, X1, X2: // - SF (bit 31) = 1 (64-bit) // - op (bit 30) = 0 (ADD) // - S (bit 29) = 0 (don't set flags) // - Fixed bits 28-24 = 01011 // - Rm (bits 20-16) = X2 = 2 - // - Rn (bits 9-5) = X1 = 1 + // - Rn (bits 9-5) = X1 = 1 // - Rd (bits 4-0) = X0 = 0 - + assert_eq!(value >> 31, 1, "SF bit should be 1 for 64-bit operation"); assert_eq!((value >> 30) & 1, 0, "op bit should be 0 for ADD"); assert_eq!((value >> 29) & 1, 0, "S bit should be 0"); @@ -198,19 +206,19 @@ fn test_binary_correctness_basic_arithmetic() { // Test ADD X0, X1, X2 let mut builder = Aarch64InstructionBuilder::new(); builder.add(reg::X0, reg::X1, reg::X2); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "add x0, x1, x2\n"); - + // Test SUB X3, X4, X5 let mut builder = Aarch64InstructionBuilder::new(); builder.sub(reg::X3, reg::X4, reg::X5); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "sub x3, x4, x5\n"); - + // Test ADD immediate: ADD X0, X1, #100 let mut builder = Aarch64InstructionBuilder::new(); builder.addi(reg::X0, reg::X1, 100); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "add x0, x1, #100\n"); } @@ -220,19 +228,19 @@ fn test_binary_correctness_logical() { // Test AND X0, X1, X2 let mut builder = Aarch64InstructionBuilder::new(); builder.and(reg::X0, reg::X1, reg::X2); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "and x0, x1, x2\n"); - + // Test ORR X3, X4, X5 let mut builder = Aarch64InstructionBuilder::new(); builder.or(reg::X3, reg::X4, reg::X5); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "orr x3, x4, x5\n"); - + // Test EOR X6, X7, X8 let mut builder = Aarch64InstructionBuilder::new(); builder.xor(reg::X6, reg::X7, reg::X8); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "eor x6, x7, x8\n"); } @@ -242,13 +250,13 @@ fn test_binary_correctness_multiply_divide() { // Test MUL X0, X1, X2 let mut builder = Aarch64InstructionBuilder::new(); builder.mul(reg::X0, reg::X1, reg::X2); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "mul x0, x1, x2\n"); - + // Test UDIV X3, X4, X5 let mut builder = Aarch64InstructionBuilder::new(); builder.udiv(reg::X3, reg::X4, reg::X5); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "udiv x3, x4, x5\n"); } @@ -258,7 +266,7 @@ fn test_binary_correctness_control_flow() { // Test RET let mut builder = Aarch64InstructionBuilder::new(); builder.ret(); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "ret\n"); } @@ -268,8 +276,8 @@ fn test_jit_add_function() { // Create a function that adds two numbers (X0 + X1 -> X0) let jit_func = unsafe { Aarch64InstructionBuilder::new() - .add(reg::X0, reg::X0, reg::X1) // Add X0 + X1, result in X0 - .ret() // Return + .add(reg::X0, reg::X0, reg::X1) // Add X0 + X1, result in X0 + .ret() // Return .function:: u64>() }; @@ -284,22 +292,24 @@ fn test_jit_execution() { // Test basic addition function let add_func = unsafe { Aarch64InstructionBuilder::new() - .add(reg::X0, reg::X0, reg::X1) // Add X0 + X1 -> X0 - .ret() // Return + .add(reg::X0, reg::X0, reg::X1) // Add X0 + X1 -> X0 + .ret() // Return .function:: u64>() - }.expect("Failed to create add function"); - + } + .expect("Failed to create add function"); + let result = add_func.call(10, 20); assert_eq!(result, 30, "10 + 20 should equal 30"); - + // Test subtraction function let sub_func = unsafe { Aarch64InstructionBuilder::new() - .sub(reg::X0, reg::X0, reg::X1) // Sub X0 - X1 -> X0 - .ret() // Return + .sub(reg::X0, reg::X0, reg::X1) // Sub X0 - X1 -> X0 + .ret() // Return .function:: u64>() - }.expect("Failed to create sub function"); - + } + .expect("Failed to create sub function"); + let result = sub_func.call(50, 30); assert_eq!(result, 20, "50 - 30 should equal 20"); } @@ -311,11 +321,12 @@ fn test_jit_multiplication() { // Test multiplication function let mul_func = unsafe { Aarch64InstructionBuilder::new() - .mul(reg::X0, reg::X0, reg::X1) // Mul X0 * X1 -> X0 - .ret() // Return + .mul(reg::X0, reg::X0, reg::X1) // Mul X0 * X1 -> X0 + .ret() // Return .function:: u64>() - }.expect("Failed to create mul function"); - + } + .expect("Failed to create mul function"); + let result = mul_func.call(7, 6); assert_eq!(result, 42, "7 * 6 should equal 42"); } @@ -327,11 +338,12 @@ fn test_jit_division() { // Test unsigned division function let div_func = unsafe { Aarch64InstructionBuilder::new() - .udiv(reg::X0, reg::X0, reg::X1) // UDiv X0 / X1 -> X0 - .ret() // Return + .udiv(reg::X0, reg::X0, reg::X1) // UDiv X0 / X1 -> X0 + .ret() // Return .function:: u64>() - }.expect("Failed to create div function"); - + } + .expect("Failed to create div function"); + let result = div_func.call(84, 12); assert_eq!(result, 7, "84 / 12 should equal 7"); } @@ -343,12 +355,13 @@ fn test_jit_complex_expression() { // Test complex expression: (a + b) * c let complex_func = unsafe { Aarch64InstructionBuilder::new() - .add(reg::X0, reg::X0, reg::X1) // X0 = X0 + X1 (a + b) - .mul(reg::X0, reg::X0, reg::X2) // X0 = X0 * X2 ((a + b) * c) - .ret() // Return + .add(reg::X0, reg::X0, reg::X1) // X0 = X0 + X1 (a + b) + .mul(reg::X0, reg::X0, reg::X2) // X0 = X0 * X2 ((a + b) * c) + .ret() // Return .function:: u64>() - }.expect("Failed to create complex function"); - + } + .expect("Failed to create complex function"); + let result = complex_func.call(10, 5, 3); assert_eq!(result, 45, "(10 + 5) * 3 should equal 45"); } @@ -362,10 +375,11 @@ fn test_jit_remainder_operation() { Aarch64InstructionBuilder::new() .udiv(reg::X2, reg::X0, reg::X1) // X2 = X0 / X1 (use X2 as temp) .msub(reg::X0, reg::X2, reg::X1, reg::X0) // X0 = X0 - (X2 * X1) = X0 % X1 - .ret() // Return + .ret() // Return .function:: u64>() - }.expect("Failed to create remainder function"); - + } + .expect("Failed to create remainder function"); + let result = rem_func.call(23, 7); assert_eq!(result, 2, "23 % 7 should equal 2"); } @@ -373,78 +387,78 @@ fn test_jit_remainder_operation() { #[test] fn test_immediate_move_operations() { let mut builder = Aarch64InstructionBuilder::new(); - + // Test small immediate builder.mov_imm(reg::X0, 42); - + // Test larger immediate that requires multiple instructions builder.mov_imm(reg::X1, 0x1234); - - let instructions = builder.instructions(); + + let instructions = builder.instructions().unwrap(); assert!(instructions.len() >= 2); } #[test] fn test_shift_operations() { let mut builder = Aarch64InstructionBuilder::new(); - + // Test left shift by 1 (multiply by 2) builder.shl(reg::X0, reg::X1, 1); - - // Test left shift by 3 (multiply by 8) + + // Test left shift by 3 (multiply by 8) builder.shl(reg::X2, reg::X3, 3); - - let instructions = builder.instructions(); + + let instructions = builder.instructions().unwrap(); assert_eq!(instructions.len(), 4); // Each shl generates 2 instructions } #[cfg(feature = "register-tracking")] mod register_tracking_tests { use super::*; - + #[test] fn test_basic_arithmetic_tracking() { let mut builder = Aarch64InstructionBuilder::new(); builder.add(reg::X0, reg::X1, reg::X2); - + let usage = builder.register_usage(); - + // Check written registers let written = usage.written_registers(); assert_eq!(written.len(), 1); assert!(usage.contains_written_register(®::X0)); - + // Check read registers let read = usage.read_registers(); assert_eq!(read.len(), 2); assert!(usage.contains_read_register(®::X1)); assert!(usage.contains_read_register(®::X2)); - + // Check total usage assert_eq!(usage.register_count(), 3); assert!(usage.has_used_registers()); } - + #[test] fn test_immediate_tracking() { let mut builder = Aarch64InstructionBuilder::new(); builder.addi(reg::X0, reg::X1, 100); - + let usage = builder.register_usage(); - + // Only X1 should be read, X0 should be written assert!(usage.contains_written_register(®::X0)); assert!(usage.contains_read_register(®::X1)); assert_eq!(usage.register_count(), 2); } - + #[test] fn test_move_tracking() { let mut builder = Aarch64InstructionBuilder::new(); builder.mov(reg::X0, reg::X1); - + let usage = builder.register_usage(); - + // X1 should be read, X0 should be written assert!(usage.contains_written_register(®::X0)); assert!(usage.contains_read_register(®::X1)); @@ -455,34 +469,42 @@ mod register_tracking_tests { #[test] fn test_movz_movk_instructions() { let mut builder = Aarch64InstructionBuilder::new(); - + // Test MOVZ instruction - builder.movz(reg::X0, 0x1234, 0); // MOVZ X0, #0x1234, LSL #0 - builder.movk(reg::X0, 0x5678, 1); // MOVK X0, #0x5678, LSL #16 - - let instructions = builder.instructions(); + builder.movz(reg::X0, 0x1234, 0); // MOVZ X0, #0x1234, LSL #0 + builder.movk(reg::X0, 0x5678, 1); // MOVK X0, #0x5678, LSL #16 + + let instructions = builder.instructions().unwrap(); assert_eq!(instructions.len(), 2); - + // Test MOVZ encoding: MOVZ X0, #0x1234, LSL #0 let movz_instr = instructions[0]; let movz_value = movz_instr.value(); - + // MOVZ: sf=1, opc=10, op=100101, hw=0, imm16=0x1234, rd=0 assert_eq!(movz_value >> 31, 1, "sf bit should be 1 for 64-bit"); assert_eq!((movz_value >> 29) & 0b11, 0b10, "opc should be 10 for MOVZ"); - assert_eq!((movz_value >> 23) & 0b111111, 0b100101, "op should be 100101"); + assert_eq!( + (movz_value >> 23) & 0b111111, + 0b100101, + "op should be 100101" + ); assert_eq!((movz_value >> 21) & 0b11, 0, "hw should be 0"); assert_eq!((movz_value >> 5) & 0xFFFF, 0x1234, "imm16 should be 0x1234"); assert_eq!(movz_value & 0x1F, 0, "rd should be X0 (0)"); - + // Test MOVK encoding: MOVK X0, #0x5678, LSL #16 let movk_instr = instructions[1]; let movk_value = movk_instr.value(); - + // MOVK: sf=1, opc=11, op=100101, hw=1, imm16=0x5678, rd=0 assert_eq!(movk_value >> 31, 1, "sf bit should be 1 for 64-bit"); assert_eq!((movk_value >> 29) & 0b11, 0b11, "opc should be 11 for MOVK"); - assert_eq!((movk_value >> 23) & 0b111111, 0b100101, "op should be 100101"); + assert_eq!( + (movk_value >> 23) & 0b111111, + 0b100101, + "op should be 100101" + ); assert_eq!((movk_value >> 21) & 0b11, 1, "hw should be 1"); assert_eq!((movk_value >> 5) & 0xFFFF, 0x5678, "imm16 should be 0x5678"); assert_eq!(movk_value & 0x1F, 0, "rd should be X0 (0)"); @@ -491,13 +513,13 @@ fn test_movz_movk_instructions() { #[test] fn test_mov_imm_instruction() { let mut builder = Aarch64InstructionBuilder::new(); - + // Test small immediate (should use single MOVZ) builder.mov_imm(reg::X1, 42); - - let instructions = builder.instructions(); + + let instructions = builder.instructions().unwrap(); assert_eq!(instructions.len(), 1); - + // Should be MOVZ X1, #42, LSL #0 let instr = instructions[0]; let value = instr.value(); @@ -505,16 +527,16 @@ fn test_mov_imm_instruction() { assert_eq!(value & 0x1F, 1, "rd should be X1 (1)"); } -#[test] +#[test] fn test_mov_imm_large_values() { let mut builder = Aarch64InstructionBuilder::new(); - + // Test 32-bit value that requires MOVZ + MOVK builder.mov_imm(reg::X2, 0x12345678); - - let instructions = builder.instructions(); + + let instructions = builder.instructions().unwrap(); assert_eq!(instructions.len(), 2); - + // First instruction: MOVZ X2, #0x5678, LSL #0 let movz_instr = instructions[0]; let movz_value = movz_instr.value(); @@ -522,8 +544,8 @@ fn test_mov_imm_large_values() { assert_eq!((movz_value >> 21) & 0b11, 0, "hw should be 0"); assert_eq!((movz_value >> 5) & 0xFFFF, 0x5678, "imm16 should be 0x5678"); assert_eq!(movz_value & 0x1F, 2, "rd should be X2 (2)"); - - // Second instruction: MOVK X2, #0x1234, LSL #16 + + // Second instruction: MOVK X2, #0x1234, LSL #16 let movk_instr = instructions[1]; let movk_value = movk_instr.value(); assert_eq!((movk_value >> 29) & 0b11, 0b11, "should be MOVK"); @@ -538,24 +560,27 @@ fn test_binary_correctness_move_immediate() { // Test MOVZ X0, #42, LSL #0 let mut builder = Aarch64InstructionBuilder::new(); builder.movz(reg::X0, 42, 0); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "movz x0, #42, lsl #0\n"); - + // Test MOVK X1, #0x1234, LSL #16 let mut builder = Aarch64InstructionBuilder::new(); builder.movk(reg::X1, 0x1234, 1); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "movk x1, #0x1234, lsl #16\n"); - + // Test larger immediate using mov_imm let mut builder = Aarch64InstructionBuilder::new(); builder.mov_imm(reg::X2, 0x123456789ABCDEF0); - let instructions = builder.instructions(); - + let instructions = builder.instructions().unwrap(); + // This should generate multiple instructions, let's test the first one if !instructions.is_empty() { // The exact sequence depends on implementation, but it should be valid - assert!(instructions.len() >= 1, "Should generate at least one instruction for large immediate"); + assert!( + instructions.len() >= 1, + "Should generate at least one instruction for large immediate" + ); } } @@ -566,7 +591,7 @@ fn test_binary_correctness_move_immediate() { #[test] fn test_all_arithmetic_instructions() { let mut builder = Aarch64InstructionBuilder::new(); - + // Test all arithmetic instructions with different register combinations builder.add(reg::X0, reg::X1, reg::X2); builder.addi(reg::X3, reg::X4, 123); @@ -576,12 +601,12 @@ fn test_all_arithmetic_instructions() { builder.udiv(reg::X13, reg::X14, reg::X15); builder.sdiv(reg::X16, reg::X17, reg::X18); // Manual remainder: X19 = X20 % X21 using X17 as temp - builder.udiv(reg::X17, reg::X20, reg::X21); // X17 = X20 / X21 + builder.udiv(reg::X17, reg::X20, reg::X21); // X17 = X20 / X21 builder.msub(reg::X19, reg::X17, reg::X21, reg::X20); // X19 = X20 - (X17 * X21) - - let instructions = builder.instructions(); + + let instructions = builder.instructions().unwrap(); assert_eq!(instructions.len(), 9); // manual remainder generates 2 instructions (udiv + msub), so total is 9 - + // Verify each instruction is encoded properly (basic check) for instr in instructions { let value = instr.value(); @@ -593,29 +618,29 @@ fn test_all_arithmetic_instructions() { #[test] fn test_all_logical_instructions() { let mut builder = Aarch64InstructionBuilder::new(); - + // Test all logical instructions builder.and(reg::X0, reg::X1, reg::X2); builder.or(reg::X3, reg::X4, reg::X5); builder.xor(reg::X6, reg::X7, reg::X8); builder.mov(reg::X9, reg::X10); - - let instructions = builder.instructions(); + + let instructions = builder.instructions().unwrap(); assert_eq!(instructions.len(), 4); - + // Test specific encodings - AArch64 logical instructions use different bit patterns let and_instr = instructions[0]; let and_value = and_instr.value(); // AND uses sf=1, opc=00, logical register format assert_eq!(and_value >> 31, 1, "AND should have sf=1 for 64-bit"); assert_eq!((and_value >> 29) & 0b11, 0b00, "AND should have opc=00"); - + let or_instr = instructions[1]; let or_value = or_instr.value(); - // ORR uses sf=1, opc=01, logical register format + // ORR uses sf=1, opc=01, logical register format assert_eq!(or_value >> 31, 1, "ORR should have sf=1 for 64-bit"); assert_eq!((or_value >> 29) & 0b11, 0b01, "ORR should have opc=01"); - + let xor_instr = instructions[2]; let xor_value = xor_instr.value(); // EOR uses sf=1, opc=10, logical register format @@ -626,39 +651,50 @@ fn test_all_logical_instructions() { #[test] fn test_move_instructions_comprehensive() { let mut builder = Aarch64InstructionBuilder::new(); - + // Test different move variants - builder.mov(reg::X0, reg::X1); // Register to register - builder.movz(reg::X2, 0x1234, 0); // Move zero with immediate - builder.movk(reg::X3, 0x5678, 1); // Move keep with immediate - builder.mov_imm(reg::X4, 42); // Small immediate - builder.mov_imm(reg::X5, 0x123456789ABCDEF0); // Large immediate - - let instructions = builder.instructions(); - assert!(instructions.len() >= 5, "Should generate at least 5 instructions"); - + builder.mov(reg::X0, reg::X1); // Register to register + builder.movz(reg::X2, 0x1234, 0); // Move zero with immediate + builder.movk(reg::X3, 0x5678, 1); // Move keep with immediate + builder.mov_imm(reg::X4, 42); // Small immediate + builder.mov_imm(reg::X5, 0x123456789ABCDEF0); // Large immediate + + let instructions = builder.instructions().unwrap(); + assert!( + instructions.len() >= 5, + "Should generate at least 5 instructions" + ); + // Test MOV encoding (ORR with XZR) let mov_instr = instructions[0]; let mov_value = mov_instr.value(); - assert_eq!((mov_value >> 5) & 0x1F, 31, "MOV should use XZR as source register"); + assert_eq!( + (mov_value >> 5) & 0x1F, + 31, + "MOV should use XZR as source register" + ); } #[test] fn test_control_flow_instructions_comprehensive() { let mut builder = Aarch64InstructionBuilder::new(); - + // Test different return variants - builder.ret(); // Default return (X30) - builder.ret_reg(reg::X1); // Return to specific register - - let instructions = builder.instructions(); + builder.ret(); // Default return (X30) + builder.ret_reg(reg::X1); // Return to specific register + + let instructions = builder.instructions().unwrap(); assert_eq!(instructions.len(), 2); - + // Test RET encodings let ret_default = instructions[0]; let ret_default_value = ret_default.value(); - assert_eq!((ret_default_value >> 5) & 0x1F, 30, "Default RET should use X30 (LR)"); - + assert_eq!( + (ret_default_value >> 5) & 0x1F, + 30, + "Default RET should use X30 (LR)" + ); + let ret_reg = instructions[1]; let ret_reg_value = ret_reg.value(); assert_eq!((ret_reg_value >> 5) & 0x1F, 1, "RET X1 should use X1"); @@ -667,45 +703,57 @@ fn test_control_flow_instructions_comprehensive() { #[test] fn test_immediate_value_ranges() { let mut builder = Aarch64InstructionBuilder::new(); - + // Test edge cases for immediate values - builder.addi(reg::X0, reg::X1, 0); // Minimum immediate - builder.addi(reg::X2, reg::X3, 4095); // Maximum 12-bit immediate - builder.movz(reg::X4, 0, 0); // Zero immediate - builder.movz(reg::X5, 0xFFFF, 0); // Maximum 16-bit immediate - builder.movz(reg::X6, 0x1234, 3); // Maximum shift (48 bits) - - let instructions = builder.instructions(); + builder.addi(reg::X0, reg::X1, 0); // Minimum immediate + builder.addi(reg::X2, reg::X3, 4095); // Maximum 12-bit immediate + builder.movz(reg::X4, 0, 0); // Zero immediate + builder.movz(reg::X5, 0xFFFF, 0); // Maximum 16-bit immediate + builder.movz(reg::X6, 0x1234, 3); // Maximum shift (48 bits) + + let instructions = builder.instructions().unwrap(); assert_eq!(instructions.len(), 5); - + // Verify immediate encodings let addi_max = instructions[1]; let addi_max_value = addi_max.value(); - assert_eq!((addi_max_value >> 10) & 0xFFF, 4095, "Should encode maximum 12-bit immediate"); - + assert_eq!( + (addi_max_value >> 10) & 0xFFF, + 4095, + "Should encode maximum 12-bit immediate" + ); + let movz_max = instructions[3]; let movz_max_value = movz_max.value(); - assert_eq!((movz_max_value >> 5) & 0xFFFF, 0xFFFF, "Should encode maximum 16-bit immediate"); + assert_eq!( + (movz_max_value >> 5) & 0xFFFF, + 0xFFFF, + "Should encode maximum 16-bit immediate" + ); } #[test] fn test_register_combinations() { let mut builder = Aarch64InstructionBuilder::new(); - + // Test with all special registers - builder.add(reg::X0, reg::XZR, reg::X1); // Zero register as source - builder.add(reg::SP, reg::X1, reg::X2); // Stack pointer as destination - builder.add(reg::X3, reg::FP, reg::X4); // Frame pointer as source - builder.add(reg::X5, reg::X6, reg::LR); // Link register as source - - let instructions = builder.instructions(); + builder.add(reg::X0, reg::XZR, reg::X1); // Zero register as source + builder.add(reg::SP, reg::X1, reg::X2); // Stack pointer as destination + builder.add(reg::X3, reg::FP, reg::X4); // Frame pointer as source + builder.add(reg::X5, reg::X6, reg::LR); // Link register as source + + let instructions = builder.instructions().unwrap(); assert_eq!(instructions.len(), 4); - + // Verify register encodings let zero_reg_instr = instructions[0]; let zero_reg_value = zero_reg_instr.value(); - assert_eq!((zero_reg_value >> 5) & 0x1F, 31, "Should use XZR (31) as Rn"); - + assert_eq!( + (zero_reg_value >> 5) & 0x1F, + 31, + "Should use XZR (31) as Rn" + ); + let sp_instr = instructions[1]; let sp_value = sp_instr.value(); assert_eq!(sp_value & 0x1F, 31, "Should use SP (31) as Rd"); @@ -721,13 +769,13 @@ fn test_binary_correctness_comprehensive_arithmetic() { // Test SUB immediate let mut builder = Aarch64InstructionBuilder::new(); builder.subi(reg::X1, reg::X2, 100); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "sub x1, x2, #100\n"); - + // Test SDIV let mut builder = Aarch64InstructionBuilder::new(); builder.sdiv(reg::X0, reg::X1, reg::X2); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "sdiv x0, x1, x2\n"); } @@ -737,17 +785,17 @@ fn test_binary_correctness_comprehensive_logical() { // Test different register combinations for logical operations let mut builder = Aarch64InstructionBuilder::new(); builder.and(reg::X10, reg::X11, reg::X12); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "and x10, x11, x12\n"); - + let mut builder = Aarch64InstructionBuilder::new(); builder.or(reg::X20, reg::X21, reg::X22); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "orr x20, x21, x22\n"); - + let mut builder = Aarch64InstructionBuilder::new(); builder.xor(reg::X30, reg::XZR, reg::X1); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "eor x30, xzr, x1\n"); } @@ -757,19 +805,19 @@ fn test_binary_correctness_move_comprehensive() { // Test MOV register to register let mut builder = Aarch64InstructionBuilder::new(); builder.mov(reg::X15, reg::X16); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "mov x15, x16\n"); - + // Test MOVZ with different shifts let mut builder = Aarch64InstructionBuilder::new(); - builder.movz(reg::X5, 0xABCD, 2); // LSL #32 - let instructions = builder.instructions(); + builder.movz(reg::X5, 0xABCD, 2); // LSL #32 + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "movz x5, #0xabcd, lsl #32\n"); - + // Test MOVK let mut builder = Aarch64InstructionBuilder::new(); - builder.movk(reg::X7, 0x1234, 3); // LSL #48 - let instructions = builder.instructions(); + builder.movk(reg::X7, 0x1234, 3); // LSL #48 + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "movk x7, #0x1234, lsl #48\n"); } @@ -778,14 +826,14 @@ fn test_binary_correctness_move_comprehensive() { fn test_binary_correctness_edge_cases() { // Test with maximum immediate values let mut builder = Aarch64InstructionBuilder::new(); - builder.addi(reg::X0, reg::X1, 4095); // Maximum 12-bit immediate - let instructions = builder.instructions(); + builder.addi(reg::X0, reg::X1, 4095); // Maximum 12-bit immediate + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "add x0, x1, #4095\n"); - + // Test with zero immediate let mut builder = Aarch64InstructionBuilder::new(); builder.addi(reg::X2, reg::X3, 0); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "add x2, x3, #0\n"); } @@ -803,18 +851,22 @@ fn test_jit_large_immediate_values() { (0x123456789ABCDEFu64, "56-bit value"), (0xFFFFFFFFFFFFFFFFu64, "Maximum 64-bit value"), ]; - + for (value, description) in test_cases { let constant_func = unsafe { Aarch64InstructionBuilder::new() .mov_imm(reg::X0, value) .ret() .function:: u64>() - }.expect(&format!("Failed to create function for {}", description)); - + } + .expect(&format!("Failed to create function for {}", description)); + let result = constant_func.call(); - assert_eq!(result, value, "JIT execution failed for {}: expected 0x{:016x}, got 0x{:016x}", - description, value, result); + assert_eq!( + result, value, + "JIT execution failed for {}: expected 0x{:016x}, got 0x{:016x}", + description, value, result + ); } } @@ -824,12 +876,13 @@ fn test_jit_arithmetic_with_large_constants() { // Test arithmetic operations with large constants let add_large_func = unsafe { Aarch64InstructionBuilder::new() - .mov_imm(reg::X1, 0x123456789ABCDEF0) // Load large constant - .add(reg::X0, reg::X0, reg::X1) // Add to input + .mov_imm(reg::X1, 0x123456789ABCDEF0) // Load large constant + .add(reg::X0, reg::X0, reg::X1) // Add to input .ret() .function:: u64>() - }.expect("Failed to create large constant addition function"); - + } + .expect("Failed to create large constant addition function"); + let result = add_large_func.call(0x10); assert_eq!(result, 0x123456789ABCDF00, "Large constant addition failed"); -} \ No newline at end of file +} diff --git a/src/common/mod.rs b/src/common/mod.rs index 9685b7c..187e847 100644 --- a/src/common/mod.rs +++ b/src/common/mod.rs @@ -9,48 +9,48 @@ pub mod register_usage; #[cfg(feature = "register-tracking")] use register_usage::RegisterUsageInfo; -#[cfg(feature = "std")] -use std::vec::Vec; #[cfg(not(feature = "std"))] use alloc::vec::Vec; +#[cfg(feature = "std")] +use std::vec::Vec; /// A collection of instructions with associated register usage information. -/// +/// /// This struct combines an `InstructionCollection` with `RegisterUsageInfo`, /// allowing you to merge both instructions and their usage statistics together. /// This is useful when combining code sequences from multiple builders while /// preserving register usage information. -/// +/// /// # Example -/// +/// /// ```rust,ignore /// // Create three builders for different parts of the code /// let mut prologue = Riscv64InstructionBuilder::new(); /// prologue.sd(reg::SP, reg::S0, -8); // Save S0 -/// +/// /// let mut main = Riscv64InstructionBuilder::new(); /// main.add(reg::S0, reg::A0, reg::A1); // Main computation -/// +/// /// let mut epilogue = Riscv64InstructionBuilder::new(); /// epilogue.ld(reg::S0, reg::SP, -8); // Restore S0 -/// +/// /// // Create tracked collections /// let prologue_tracked = InstructionCollectionWithUsage::new( -/// prologue.instructions(), +/// prologue.instructions().unwrap(), /// prologue.register_usage().clone() /// ); /// let main_tracked = InstructionCollectionWithUsage::new( -/// main.instructions(), +/// main.instructions().unwrap(), /// main.register_usage().clone() /// ); /// let epilogue_tracked = InstructionCollectionWithUsage::new( -/// epilogue.instructions(), +/// epilogue.instructions().unwrap(), /// epilogue.register_usage().clone() /// ); -/// +/// /// // Merge them: prologue + main + epilogue /// let combined = prologue_tracked + main_tracked + epilogue_tracked; -/// +/// /// // Now we have the complete function with accurate register usage /// let instructions = combined.instructions(); /// let usage = combined.register_usage(); @@ -65,64 +65,71 @@ pub struct InstructionCollectionWithUsage { #[cfg(feature = "register-tracking")] impl InstructionCollectionWithUsage { /// Create a new tracked instruction collection. - pub fn new(instructions: InstructionCollection, register_usage: RegisterUsageInfo) -> Self { + pub fn new( + instructions: InstructionCollection, + register_usage: RegisterUsageInfo, + ) -> Self { Self { instructions, register_usage, } } - + /// Create from raw parts. - pub fn from_parts(instructions: InstructionCollection, register_usage: RegisterUsageInfo) -> Self { + pub fn from_parts( + instructions: InstructionCollection, + register_usage: RegisterUsageInfo, + ) -> Self { Self::new(instructions, register_usage) } - + /// Get a reference to the instructions. pub fn instructions(&self) -> &InstructionCollection { &self.instructions } - + /// Get a mutable reference to the instructions. pub fn instructions_mut(&mut self) -> &mut InstructionCollection { &mut self.instructions } - + /// Get a reference to the register usage information. pub fn register_usage(&self) -> &RegisterUsageInfo { &self.register_usage } - + /// Get a mutable reference to the register usage information. pub fn register_usage_mut(&mut self) -> &mut RegisterUsageInfo { &mut self.register_usage } - + /// Consume this collection and return the instructions and register usage. pub fn into_parts(self) -> (InstructionCollection, RegisterUsageInfo) { (self.instructions, self.register_usage) } - + /// Consume this collection and return just the instructions. pub fn into_instructions(self) -> InstructionCollection { self.instructions } - + /// Merge another tracked collection into this one. - /// + /// /// This appends the instructions and merges the register usage information. pub fn append(&mut self, other: InstructionCollectionWithUsage) { self.instructions.append(other.instructions); self.register_usage.merge(&other.register_usage); } - + /// Extend this collection with instructions and register usage from another. - /// + /// /// This clones instructions and merges the register usage information. pub fn extend_from(&mut self, other: &InstructionCollectionWithUsage) { - self.instructions.extend_from_collection(&other.instructions); + self.instructions + .extend_from_collection(&other.instructions); self.register_usage.merge(&other.register_usage); } - + /// Concatenate two tracked collections, consuming both. pub fn concat(mut self, other: InstructionCollectionWithUsage) -> Self { self.instructions.append(other.instructions); @@ -134,9 +141,12 @@ impl InstructionCollectionWithUsage { #[cfg(feature = "register-tracking")] impl core::ops::Add for InstructionCollectionWithUsage { type Output = InstructionCollectionWithUsage; - + /// Concatenate two tracked instruction collections using the `+` operator. - fn add(self, other: InstructionCollectionWithUsage) -> InstructionCollectionWithUsage { + fn add( + self, + other: InstructionCollectionWithUsage, + ) -> InstructionCollectionWithUsage { self.concat(other) } } @@ -153,35 +163,35 @@ impl core::ops::AddAssign for InstructionCollection pub trait Instruction: Copy + Clone + fmt::Debug + fmt::Display { /// Get the instruction as a 32-bit or 64-bit value fn value(&self) -> u64; - + /// Get the instruction as bytes (little-endian) fn bytes(&self) -> Vec; - + /// Get the size of this instruction in bytes fn size(&self) -> usize; } - + /// ABI classification for registers based on preservation requirements. -/// +/// /// This simplified classification focuses on whether registers need to be /// preserved across function calls, which is the most critical information /// for JIT compilation and register allocation. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum AbiClass { /// Caller-saved registers that don't need to be preserved across calls. - /// + /// /// These registers can be freely used by JIT-compiled functions without /// saving/restoring their values. Examples: argument registers, temp registers. CallerSaved, - + /// Callee-saved registers that must be preserved across calls. - /// + /// /// If a JIT-compiled function uses these registers, it must save their /// values on entry and restore them before returning. Examples: saved registers. CalleeSaved, - + /// Special-purpose registers with specific ABI requirements. - /// + /// /// These registers have specific roles (stack pointer, frame pointer, zero register, etc.) /// and require careful handling. Generally should be avoided in JIT code /// unless specifically needed for their intended purpose. @@ -202,14 +212,14 @@ impl fmt::Display for AbiClass { pub trait Register: Copy + Clone + fmt::Debug + core::hash::Hash + Eq { /// Get the register number/identifier fn id(&self) -> u32; - + /// Get the ABI classification for this register. - /// + /// /// This method should return the appropriate `AbiClass` based on the /// target architecture's calling convention. - /// + /// /// # Example - /// + /// /// ```rust,ignore /// // For RISC-V /// match self { @@ -220,23 +230,23 @@ pub trait Register: Copy + Clone + fmt::Debug + core::hash::Hash + Eq { /// } /// ``` fn abi_class(&self) -> AbiClass; - + /// Check if this register is caller-saved. - /// + /// /// Convenience method equivalent to `self.abi_class() == AbiClass::CallerSaved`. fn is_caller_saved(&self) -> bool { self.abi_class() == AbiClass::CallerSaved } - + /// Check if this register is callee-saved. - /// + /// /// Convenience method equivalent to `self.abi_class() == AbiClass::CalleeSaved`. fn is_callee_saved(&self) -> bool { self.abi_class() == AbiClass::CalleeSaved } - + /// Check if this register is special-purpose. - /// + /// /// Convenience method equivalent to `self.abi_class() == AbiClass::Special`. fn is_special(&self) -> bool { self.abi_class() == AbiClass::Special @@ -247,87 +257,99 @@ pub trait Register: Copy + Clone + fmt::Debug + core::hash::Hash + Eq { pub trait InstructionBuilder { /// The register type used by this architecture type Register: Register; - + + /// The error type for this builder + type Error; + /// Create a new instruction builder fn new() -> Self; - + /// Get the generated instructions - fn instructions(&self) -> InstructionCollection; - + fn instructions(&self) -> Result, Self::Error>; + + /// Set an error without breaking the fluent chain + fn set_error(&mut self, error: Self::Error); + /// Add an instruction to the builder fn push(&mut self, instr: I); - + /// Clear all instructions fn clear(&mut self); - + /// Get register usage information (register-tracking feature only) - /// + /// /// This method returns information about which registers have been used /// by the instructions in this builder, enabling register allocation /// analysis and ABI compliance checking. #[cfg(feature = "register-tracking")] fn register_usage(&self) -> &crate::common::register_usage::RegisterUsageInfo; - + /// Get mutable register usage information (register-tracking feature only) - /// + /// /// This allows direct manipulation of the usage tracking, which can be /// useful for advanced use cases or manual register tracking. #[cfg(feature = "register-tracking")] - fn register_usage_mut(&mut self) -> &mut crate::common::register_usage::RegisterUsageInfo; - + fn register_usage_mut( + &mut self, + ) -> &mut crate::common::register_usage::RegisterUsageInfo; + /// Clear register usage information (register-tracking feature only) - /// + /// /// This resets the usage tracking to an empty state, which can be /// useful when reusing a builder for multiple functions. #[cfg(feature = "register-tracking")] fn clear_register_usage(&mut self) { self.register_usage_mut().clear(); } - + /// Create a JIT-compiled function from the assembled instructions (std-only) - /// + /// /// This method converts the assembled instructions into executable machine code /// that can be called directly as a function. The generic type parameter `F` /// specifies the function signature. - /// + /// /// # ABI Compatibility - /// + /// /// While you specify a Rust function type like `fn() -> u64`, the actual JIT /// code uses C ABI internally for stability across Rust versions. This conversion /// is handled transparently by the `call()` methods. - /// + /// /// # Limitations - /// + /// /// Currently supports function signatures with up to 7 arguments. For functions /// with more arguments or complex calling conventions, use manual function pointer /// conversion. - /// + /// /// # Safety - /// + /// /// This function is unsafe because: /// - It allocates executable memory /// - It assumes the assembled code follows the correct C ABI /// - The caller must ensure the function signature matches the actual code /// - The assembled code must be valid for the target architecture - /// + /// #[cfg(feature = "std")] - unsafe fn function(&self) -> Result, crate::common::jit::JitError>; + unsafe fn function( + &self, + ) -> Result, crate::common::jit::JitError>; /// Create a raw JIT-compiled function for manual type conversion (std-only) - /// + /// /// This method is similar to `function()` but returns a type-erased function /// that allows manual conversion to any function signature. Use this for /// function signatures with more than 7 arguments or custom calling conventions. - /// + /// /// # Safety - /// + /// /// This function is unsafe because: /// - It allocates executable memory /// - The caller must manually ensure type safety when calling `as_fn()` /// - The assembled code must be valid for the target architecture - /// + /// #[cfg(feature = "std")] - unsafe fn raw_function(&self) -> Result; + unsafe fn raw_function( + &self, + ) -> Result; } /// Convenience functions for instruction collections @@ -358,95 +380,95 @@ impl InstructionCollection { instructions: Vec::new(), } } - + /// Create from a vector of instructions pub fn from_vec(instructions: Vec) -> Self { Self { instructions } } - + /// Create from a slice of instructions pub fn from_slice(instructions: &[I]) -> Self { Self { instructions: instructions.to_vec(), } } - + /// Get the instructions as a slice pub fn as_slice(&self) -> &[I] { &self.instructions } - + /// Get the instructions as a mutable slice pub fn as_mut_slice(&mut self) -> &mut [I] { &mut self.instructions } - + /// Convert instructions to a single byte vector pub fn to_bytes(&self) -> Vec { instructions_to_bytes(&self.instructions) } - + /// Get the total size in bytes of all instructions pub fn total_size(&self) -> usize { instructions_total_size(&self.instructions) } - + /// Get the number of instructions pub fn len(&self) -> usize { self.instructions.len() } - + /// Check if the collection is empty pub fn is_empty(&self) -> bool { self.instructions.is_empty() } - + /// Add an instruction to the collection pub fn push(&mut self, instruction: I) { self.instructions.push(instruction); } - + /// Remove all instructions pub fn clear(&mut self) { self.instructions.clear(); } - + /// Get an iterator over the instructions pub fn iter(&self) -> core::slice::Iter<'_, I> { self.instructions.iter() } - + /// Get a mutable iterator over the instructions pub fn iter_mut(&mut self) -> core::slice::IterMut<'_, I> { self.instructions.iter_mut() } - + /// Convert to a Vec (consumes self) pub fn into_vec(self) -> Vec { self.instructions } - + /// Create a Vec by cloning the instructions pub fn to_vec(&self) -> Vec { self.instructions.clone() } - + /// Get a reference to the instruction at the given index pub fn get(&self, index: usize) -> Option<&I> { self.instructions.get(index) } - + /// Get a mutable reference to the instruction at the given index pub fn get_mut(&mut self, index: usize) -> Option<&mut I> { self.instructions.get_mut(index) } - + /// Append another instruction collection to this one (consumes other). - /// + /// /// This moves all instructions from `other` into this collection. - /// + /// /// # Example - /// + /// /// ```rust,ignore /// let mut collection1 = InstructionCollection::from_slice(&[instr1, instr2]); /// let collection2 = InstructionCollection::from_slice(&[instr3, instr4]); @@ -456,13 +478,13 @@ impl InstructionCollection { pub fn append(&mut self, mut other: InstructionCollection) { self.instructions.append(&mut other.instructions); } - + /// Extend this collection with instructions from another collection. - /// + /// /// This clones instructions from `other` into this collection. - /// + /// /// # Example - /// + /// /// ```rust,ignore /// let mut collection1 = InstructionCollection::from_slice(&[instr1, instr2]); /// let collection2 = InstructionCollection::from_slice(&[instr3, instr4]); @@ -473,14 +495,14 @@ impl InstructionCollection { pub fn extend_from_collection(&mut self, other: &InstructionCollection) { self.instructions.extend_from_slice(&other.instructions); } - + /// Concatenate two instruction collections, consuming both. - /// - /// Creates a new collection containing all instructions from `self` + /// + /// Creates a new collection containing all instructions from `self` /// followed by all instructions from `other`. - /// + /// /// # Example - /// + /// /// ```rust,ignore /// let collection1 = InstructionCollection::from_slice(&[instr1, instr2]); /// let collection2 = InstructionCollection::from_slice(&[instr3, instr4]); @@ -525,7 +547,7 @@ impl AsMut<[I]> for InstructionCollection { impl core::ops::Index for InstructionCollection { type Output = I; - + fn index(&self, index: usize) -> &Self::Output { &self.instructions[index] } @@ -540,7 +562,7 @@ impl core::ops::IndexMut for InstructionCollection { impl IntoIterator for InstructionCollection { type Item = I; type IntoIter = as IntoIterator>::IntoIter; - + fn into_iter(self) -> Self::IntoIter { self.instructions.into_iter() } @@ -549,7 +571,7 @@ impl IntoIterator for InstructionCollection { impl<'a, I: Instruction> IntoIterator for &'a InstructionCollection { type Item = &'a I; type IntoIter = core::slice::Iter<'a, I>; - + fn into_iter(self) -> Self::IntoIter { self.instructions.iter() } @@ -558,7 +580,7 @@ impl<'a, I: Instruction> IntoIterator for &'a InstructionCollection { impl<'a, I: Instruction> IntoIterator for &'a mut InstructionCollection { type Item = &'a mut I; type IntoIter = core::slice::IterMut<'a, I>; - + fn into_iter(self) -> Self::IntoIter { self.instructions.iter_mut() } @@ -566,7 +588,7 @@ impl<'a, I: Instruction> IntoIterator for &'a mut InstructionCollection { impl core::ops::Deref for InstructionCollection { type Target = [I]; - + fn deref(&self) -> &Self::Target { &self.instructions } @@ -580,11 +602,11 @@ impl core::ops::DerefMut for InstructionCollection { impl core::ops::Add for InstructionCollection { type Output = InstructionCollection; - + /// Concatenate two instruction collections using the `+` operator. - /// + /// /// # Example - /// + /// /// ```rust,ignore /// let collection1 = InstructionCollection::from_slice(&[instr1, instr2]); /// let collection2 = InstructionCollection::from_slice(&[instr3, instr4]); @@ -598,9 +620,9 @@ impl core::ops::Add for InstructionCollection { impl core::ops::AddAssign for InstructionCollection { /// Append another instruction collection to this one using the `+=` operator. - /// + /// /// # Example - /// + /// /// ```rust,ignore /// let mut collection1 = InstructionCollection::from_slice(&[instr1, instr2]); /// let collection2 = InstructionCollection::from_slice(&[instr3, instr4]); @@ -617,7 +639,7 @@ impl core::ops::AddAssign for InstructionCollection { pub trait InstructionCollectionExt { /// Convert instructions to a single byte vector fn to_bytes(&self) -> Vec; - + /// Get the total size in bytes of all instructions fn total_size(&self) -> usize; } @@ -626,7 +648,7 @@ impl InstructionCollectionExt for [I] { fn to_bytes(&self) -> Vec { instructions_to_bytes(self) } - + fn total_size(&self) -> usize { instructions_total_size(self) } @@ -636,7 +658,7 @@ impl InstructionCollectionExt for Vec { fn to_bytes(&self) -> Vec { instructions_to_bytes(self) } - + fn total_size(&self) -> usize { instructions_total_size(self) } @@ -681,24 +703,25 @@ impl std::error::Error for BuildError {} /// JIT execution functionality (std-only) #[cfg(feature = "std")] pub mod jit { - use std::marker::PhantomData; + use super::BuildError; use jit_allocator2::JitAllocator; + use std::marker::PhantomData; /// A raw JIT-compiled function for manual type conversion - /// + /// /// This is a type-erased version of `CallableJitFunction` that allows /// manual function pointer conversion for cases not covered by the /// predefined `call()` methods. - /// + /// /// # Usage - /// + /// /// ```rust,no_run /// # use jit_assembler::common::jit::*; /// # let code = &[0u8; 4]; /// let raw_func = RawCallableJitFunction::new(code)?; - /// + /// /// // Manual conversion to any function type - /// let func: extern "C" fn(u64, u64, u64, u64, u64, u64, u64, u64) -> u64 = + /// let func: extern "C" fn(u64, u64, u64, u64, u64, u64, u64, u64) -> u64 = /// unsafe { raw_func.as_fn() }; /// let result = func(1, 2, 3, 4, 5, 6, 7, 8); /// # Ok::<(), JitError>(()) @@ -712,8 +735,10 @@ pub mod jit { /// Create a new raw callable JIT function from instruction bytes pub fn new(code: &[u8]) -> Result { let mut allocator = JitAllocator::new(Default::default()); - let (exec_ptr, mut_ptr) = allocator.alloc(code.len()).map_err(JitError::AllocationFailed)?; - + let (exec_ptr, mut_ptr) = allocator + .alloc(code.len()) + .map_err(JitError::AllocationFailed)?; + unsafe { std::ptr::copy_nonoverlapping(code.as_ptr(), mut_ptr, code.len()); } @@ -725,23 +750,23 @@ pub mod jit { } /// Convert to a function pointer of the specified type - /// + /// /// # Safety - /// + /// /// The caller must ensure that: /// - The function signature `F` matches the actual assembled code /// - The assembled code follows the expected calling convention (usually C ABI) /// - The function pointer is called with valid arguments - /// + /// /// # Example - /// + /// /// ```rust,no_run /// # use jit_assembler::common::jit::*; /// # let code = &[0u8; 4]; /// let raw_func = RawCallableJitFunction::new(code)?; - /// + /// /// // For complex signatures not covered by CallableJitFunction - /// let func: extern "C" fn(u64, u64, u64, u64, u64, u64, u64, u64) -> u64 = + /// let func: extern "C" fn(u64, u64, u64, u64, u64, u64, u64, u64) -> u64 = /// unsafe { raw_func.as_fn() }; /// let result = func(1, 2, 3, 4, 5, 6, 7, 8); /// # Ok::<(), JitError>(()) @@ -752,19 +777,19 @@ pub mod jit { } /// A JIT-compiled function that can be called directly - /// + /// /// This structure wraps executable machine code and provides type-safe /// calling methods. While the type parameter `F` represents a Rust function /// signature, the actual execution uses C ABI for stability. - /// + /// /// # Supported Signatures - /// + /// /// Currently supports function signatures with 0-7 arguments: /// - `fn() -> R` /// - `fn(A1) -> R` /// - `fn(A1, A2) -> R` /// - ... up to `fn(A1, A2, A3, A4, A5, A6, A7) -> R` - /// + /// /// For unsupported signatures, use `RawCallableJitFunction` instead. pub struct CallableJitFunction { _allocator: Box, @@ -776,8 +801,10 @@ pub mod jit { /// Create a new callable JIT function from instruction bytes pub fn new(code: &[u8]) -> Result { let mut allocator = JitAllocator::new(Default::default()); - let (exec_ptr, mut_ptr) = allocator.alloc(code.len()).map_err(JitError::AllocationFailed)?; - + let (exec_ptr, mut_ptr) = allocator + .alloc(code.len()) + .map_err(JitError::AllocationFailed)?; + unsafe { std::ptr::copy_nonoverlapping(code.as_ptr(), mut_ptr, code.len()); } @@ -791,13 +818,13 @@ pub mod jit { } /// Direct call methods based on function signature - /// + /// /// These methods provide type-safe calling with automatic ABI conversion from /// Rust function types to C ABI. Currently supports 0-7 arguments. - /// + /// /// The conversion from `fn(...)` to `extern "C" fn(...)` is handled internally /// for ABI stability across Rust versions. - + impl CallableJitFunction R> { /// Call with no arguments - natural syntax: func.call() pub fn call(&self) -> R { @@ -817,7 +844,8 @@ pub mod jit { impl CallableJitFunction R> { /// Call with two arguments - natural syntax: func.call(arg1, arg2) pub fn call(&self, arg1: A1, arg2: A2) -> R { - let func: extern "C" fn(A1, A2) -> R = unsafe { std::mem::transmute_copy(&self.exec_ptr) }; + let func: extern "C" fn(A1, A2) -> R = + unsafe { std::mem::transmute_copy(&self.exec_ptr) }; func(arg1, arg2) } } @@ -825,7 +853,8 @@ pub mod jit { impl CallableJitFunction R> { /// Call with three arguments - natural syntax: func.call(arg1, arg2, arg3) pub fn call(&self, arg1: A1, arg2: A2, arg3: A3) -> R { - let func: extern "C" fn(A1, A2, A3) -> R = unsafe { std::mem::transmute_copy(&self.exec_ptr) }; + let func: extern "C" fn(A1, A2, A3) -> R = + unsafe { std::mem::transmute_copy(&self.exec_ptr) }; func(arg1, arg2, arg3) } } @@ -833,7 +862,8 @@ pub mod jit { impl CallableJitFunction R> { /// Call with four arguments - natural syntax: func.call(arg1, arg2, arg3, arg4) pub fn call(&self, arg1: A1, arg2: A2, arg3: A3, arg4: A4) -> R { - let func: extern "C" fn(A1, A2, A3, A4) -> R = unsafe { std::mem::transmute_copy(&self.exec_ptr) }; + let func: extern "C" fn(A1, A2, A3, A4) -> R = + unsafe { std::mem::transmute_copy(&self.exec_ptr) }; func(arg1, arg2, arg3, arg4) } } @@ -841,7 +871,8 @@ pub mod jit { impl CallableJitFunction R> { /// Call with five arguments pub fn call(&self, arg1: A1, arg2: A2, arg3: A3, arg4: A4, arg5: A5) -> R { - let func: extern "C" fn(A1, A2, A3, A4, A5) -> R = unsafe { std::mem::transmute_copy(&self.exec_ptr) }; + let func: extern "C" fn(A1, A2, A3, A4, A5) -> R = + unsafe { std::mem::transmute_copy(&self.exec_ptr) }; func(arg1, arg2, arg3, arg4, arg5) } } @@ -849,213 +880,212 @@ pub mod jit { impl CallableJitFunction R> { /// Call with six arguments pub fn call(&self, arg1: A1, arg2: A2, arg3: A3, arg4: A4, arg5: A5, arg6: A6) -> R { - let func: extern "C" fn(A1, A2, A3, A4, A5, A6) -> R = unsafe { std::mem::transmute_copy(&self.exec_ptr) }; + let func: extern "C" fn(A1, A2, A3, A4, A5, A6) -> R = + unsafe { std::mem::transmute_copy(&self.exec_ptr) }; func(arg1, arg2, arg3, arg4, arg5, arg6) } } impl CallableJitFunction R> { /// Call with seven arguments - pub fn call(&self, arg1: A1, arg2: A2, arg3: A3, arg4: A4, arg5: A5, arg6: A6, arg7: A7) -> R { - let func: extern "C" fn(A1, A2, A3, A4, A5, A6, A7) -> R = unsafe { std::mem::transmute_copy(&self.exec_ptr) }; + pub fn call( + &self, + arg1: A1, + arg2: A2, + arg3: A3, + arg4: A4, + arg5: A5, + arg6: A6, + arg7: A7, + ) -> R { + let func: extern "C" fn(A1, A2, A3, A4, A5, A6, A7) -> R = + unsafe { std::mem::transmute_copy(&self.exec_ptr) }; func(arg1, arg2, arg3, arg4, arg5, arg6, arg7) } } - // Note: For void return functions, we don't generate them here + // Note: For void return functions, we don't generate them here // as they would need special handling with unit type () /// Errors that can occur during JIT execution #[derive(Debug)] pub enum JitError { AllocationFailed(jit_allocator2::Error), + BuildError(BuildError), } impl std::fmt::Display for JitError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - JitError::AllocationFailed(e) => write!(f, "Failed to allocate JIT memory: {:?}", e), + JitError::AllocationFailed(e) => { + write!(f, "Failed to allocate JIT memory: {:?}", e) + } + JitError::BuildError(e) => { + write!(f, "Build error: {}", e) + } } } } + impl From for JitError { + fn from(error: BuildError) -> Self { + JitError::BuildError(error) + } + } + impl std::error::Error for JitError {} } #[cfg(test)] mod tests { use super::*; - + // Test instruction type for unit tests #[derive(Debug, Clone, Copy, PartialEq, Eq)] struct TestInstruction(u32); - + impl fmt::Display for TestInstruction { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "TestInstruction(0x{:08x})", self.0) } } - + impl Instruction for TestInstruction { fn value(&self) -> u64 { self.0 as u64 } - + fn bytes(&self) -> Vec { self.0.to_le_bytes().to_vec() } - + fn size(&self) -> usize { 4 } } - + #[test] fn test_instruction_collection_append() { - let mut collection1 = InstructionCollection::from_slice(&[ - TestInstruction(1), - TestInstruction(2), - ]); - let collection2 = InstructionCollection::from_slice(&[ - TestInstruction(3), - TestInstruction(4), - ]); - + let mut collection1 = + InstructionCollection::from_slice(&[TestInstruction(1), TestInstruction(2)]); + let collection2 = + InstructionCollection::from_slice(&[TestInstruction(3), TestInstruction(4)]); + collection1.append(collection2); - + assert_eq!(collection1.len(), 4); assert_eq!(collection1[0], TestInstruction(1)); assert_eq!(collection1[1], TestInstruction(2)); assert_eq!(collection1[2], TestInstruction(3)); assert_eq!(collection1[3], TestInstruction(4)); } - + #[test] fn test_instruction_collection_extend_from_collection() { - let mut collection1 = InstructionCollection::from_slice(&[ - TestInstruction(1), - TestInstruction(2), - ]); - let collection2 = InstructionCollection::from_slice(&[ - TestInstruction(3), - TestInstruction(4), - ]); - + let mut collection1 = + InstructionCollection::from_slice(&[TestInstruction(1), TestInstruction(2)]); + let collection2 = + InstructionCollection::from_slice(&[TestInstruction(3), TestInstruction(4)]); + collection1.extend_from_collection(&collection2); - + // collection1 should have all 4 instructions assert_eq!(collection1.len(), 4); assert_eq!(collection1[0], TestInstruction(1)); assert_eq!(collection1[1], TestInstruction(2)); assert_eq!(collection1[2], TestInstruction(3)); assert_eq!(collection1[3], TestInstruction(4)); - + // collection2 should still be valid assert_eq!(collection2.len(), 2); assert_eq!(collection2[0], TestInstruction(3)); assert_eq!(collection2[1], TestInstruction(4)); } - + #[test] fn test_instruction_collection_concat() { - let collection1 = InstructionCollection::from_slice(&[ - TestInstruction(1), - TestInstruction(2), - ]); - let collection2 = InstructionCollection::from_slice(&[ - TestInstruction(3), - TestInstruction(4), - ]); - + let collection1 = + InstructionCollection::from_slice(&[TestInstruction(1), TestInstruction(2)]); + let collection2 = + InstructionCollection::from_slice(&[TestInstruction(3), TestInstruction(4)]); + let combined = collection1.concat(collection2); - + assert_eq!(combined.len(), 4); assert_eq!(combined[0], TestInstruction(1)); assert_eq!(combined[1], TestInstruction(2)); assert_eq!(combined[2], TestInstruction(3)); assert_eq!(combined[3], TestInstruction(4)); } - + #[test] fn test_instruction_collection_add_operator() { - let collection1 = InstructionCollection::from_slice(&[ - TestInstruction(1), - TestInstruction(2), - ]); - let collection2 = InstructionCollection::from_slice(&[ - TestInstruction(3), - TestInstruction(4), - ]); - + let collection1 = + InstructionCollection::from_slice(&[TestInstruction(1), TestInstruction(2)]); + let collection2 = + InstructionCollection::from_slice(&[TestInstruction(3), TestInstruction(4)]); + let combined = collection1 + collection2; - + assert_eq!(combined.len(), 4); assert_eq!(combined[0], TestInstruction(1)); assert_eq!(combined[1], TestInstruction(2)); assert_eq!(combined[2], TestInstruction(3)); assert_eq!(combined[3], TestInstruction(4)); } - + #[test] fn test_instruction_collection_add_assign_operator() { - let mut collection1 = InstructionCollection::from_slice(&[ - TestInstruction(1), - TestInstruction(2), - ]); - let collection2 = InstructionCollection::from_slice(&[ - TestInstruction(3), - TestInstruction(4), - ]); - + let mut collection1 = + InstructionCollection::from_slice(&[TestInstruction(1), TestInstruction(2)]); + let collection2 = + InstructionCollection::from_slice(&[TestInstruction(3), TestInstruction(4)]); + collection1 += collection2; - + assert_eq!(collection1.len(), 4); assert_eq!(collection1[0], TestInstruction(1)); assert_eq!(collection1[1], TestInstruction(2)); assert_eq!(collection1[2], TestInstruction(3)); assert_eq!(collection1[3], TestInstruction(4)); } - + #[test] fn test_instruction_collection_multiple_merge() { let collection1 = InstructionCollection::from_slice(&[TestInstruction(1)]); let collection2 = InstructionCollection::from_slice(&[TestInstruction(2)]); let collection3 = InstructionCollection::from_slice(&[TestInstruction(3)]); - + // Test chaining with + operator let combined = collection1 + collection2 + collection3; - + assert_eq!(combined.len(), 3); assert_eq!(combined[0], TestInstruction(1)); assert_eq!(combined[1], TestInstruction(2)); assert_eq!(combined[2], TestInstruction(3)); } - + #[test] fn test_instruction_collection_merge_empty() { - let mut collection1 = InstructionCollection::from_slice(&[ - TestInstruction(1), - TestInstruction(2), - ]); + let mut collection1 = + InstructionCollection::from_slice(&[TestInstruction(1), TestInstruction(2)]); let collection2 = InstructionCollection::::new(); - + collection1.append(collection2); - + assert_eq!(collection1.len(), 2); assert_eq!(collection1[0], TestInstruction(1)); assert_eq!(collection1[1], TestInstruction(2)); } - + #[test] fn test_instruction_collection_merge_into_empty() { let mut collection1 = InstructionCollection::::new(); - let collection2 = InstructionCollection::from_slice(&[ - TestInstruction(3), - TestInstruction(4), - ]); - + let collection2 = + InstructionCollection::from_slice(&[TestInstruction(3), TestInstruction(4)]); + collection1.append(collection2); - + assert_eq!(collection1.len(), 2); assert_eq!(collection1[0], TestInstruction(3)); assert_eq!(collection1[1], TestInstruction(4)); @@ -1065,38 +1095,41 @@ mod tests { #[cfg(all(test, feature = "register-tracking"))] mod register_tracking_tests { use super::*; - + // Test instruction and register types for unit tests #[derive(Debug, Clone, Copy, PartialEq, Eq)] struct TestInstruction(u32); - + impl fmt::Display for TestInstruction { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "TestInstruction(0x{:08x})", self.0) } } - + impl Instruction for TestInstruction { fn value(&self) -> u64 { self.0 as u64 } - + fn bytes(&self) -> Vec { self.0.to_le_bytes().to_vec() } - + fn size(&self) -> usize { 4 } } - + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] enum TestRegister { - T0, T1, // Caller-saved - S0, S1, // Callee-saved - SP, FP, // Special + T0, + T1, // Caller-saved + S0, + S1, // Callee-saved + SP, + FP, // Special } - + impl Register for TestRegister { fn id(&self) -> u32 { match self { @@ -1108,7 +1141,7 @@ mod register_tracking_tests { TestRegister::FP => 5, } } - + fn abi_class(&self) -> AbiClass { match self { TestRegister::T0 | TestRegister::T1 => AbiClass::CallerSaved, @@ -1117,173 +1150,181 @@ mod register_tracking_tests { } } } - + #[test] fn test_instruction_collection_with_usage_new() { - let instructions = InstructionCollection::from_slice(&[ - TestInstruction(1), - TestInstruction(2), - ]); + let instructions = + InstructionCollection::from_slice(&[TestInstruction(1), TestInstruction(2)]); let mut usage = register_usage::RegisterUsageInfo::new(); usage.add_written_register(TestRegister::T0); usage.add_read_register(TestRegister::T1); - + let tracked = InstructionCollectionWithUsage::new(instructions.clone(), usage.clone()); - + assert_eq!(tracked.instructions().len(), 2); assert_eq!(tracked.register_usage().register_count(), 2); } - + #[test] fn test_instruction_collection_with_usage_append() { let instructions1 = InstructionCollection::from_slice(&[TestInstruction(1)]); let mut usage1 = register_usage::RegisterUsageInfo::new(); usage1.add_written_register(TestRegister::T0); - + let instructions2 = InstructionCollection::from_slice(&[TestInstruction(2)]); let mut usage2 = register_usage::RegisterUsageInfo::new(); usage2.add_read_register(TestRegister::S0); - + let mut tracked1 = InstructionCollectionWithUsage::new(instructions1, usage1); let tracked2 = InstructionCollectionWithUsage::new(instructions2, usage2); - + tracked1.append(tracked2); - + assert_eq!(tracked1.instructions().len(), 2); assert_eq!(tracked1.register_usage().register_count(), 2); - assert!(tracked1.register_usage().contains_written_register(&TestRegister::T0)); - assert!(tracked1.register_usage().contains_read_register(&TestRegister::S0)); + assert!(tracked1 + .register_usage() + .contains_written_register(&TestRegister::T0)); + assert!(tracked1 + .register_usage() + .contains_read_register(&TestRegister::S0)); } - + #[test] fn test_instruction_collection_with_usage_concat() { let instructions1 = InstructionCollection::from_slice(&[TestInstruction(1)]); let mut usage1 = register_usage::RegisterUsageInfo::new(); usage1.add_written_register(TestRegister::T0); - + let instructions2 = InstructionCollection::from_slice(&[TestInstruction(2)]); let mut usage2 = register_usage::RegisterUsageInfo::new(); usage2.add_read_register(TestRegister::S0); - + let tracked1 = InstructionCollectionWithUsage::new(instructions1, usage1); let tracked2 = InstructionCollectionWithUsage::new(instructions2, usage2); - + let combined = tracked1.concat(tracked2); - + assert_eq!(combined.instructions().len(), 2); assert_eq!(combined.register_usage().register_count(), 2); - assert!(combined.register_usage().contains_written_register(&TestRegister::T0)); - assert!(combined.register_usage().contains_read_register(&TestRegister::S0)); + assert!(combined + .register_usage() + .contains_written_register(&TestRegister::T0)); + assert!(combined + .register_usage() + .contains_read_register(&TestRegister::S0)); } - + #[test] fn test_instruction_collection_with_usage_add_operator() { let instructions1 = InstructionCollection::from_slice(&[TestInstruction(1)]); let mut usage1 = register_usage::RegisterUsageInfo::new(); usage1.add_written_register(TestRegister::T0); - + let instructions2 = InstructionCollection::from_slice(&[TestInstruction(2)]); let mut usage2 = register_usage::RegisterUsageInfo::new(); usage2.add_read_register(TestRegister::S0); - + let tracked1 = InstructionCollectionWithUsage::new(instructions1, usage1); let tracked2 = InstructionCollectionWithUsage::new(instructions2, usage2); - + let combined = tracked1 + tracked2; - + assert_eq!(combined.instructions().len(), 2); assert_eq!(combined.register_usage().register_count(), 2); - assert!(combined.register_usage().contains_written_register(&TestRegister::T0)); - assert!(combined.register_usage().contains_read_register(&TestRegister::S0)); + assert!(combined + .register_usage() + .contains_written_register(&TestRegister::T0)); + assert!(combined + .register_usage() + .contains_read_register(&TestRegister::S0)); } - + #[test] fn test_instruction_collection_with_usage_multiple_merge() { // Simulate the use case from the issue: prologue + main + epilogue - + // Prologue: save registers let prologue_instrs = InstructionCollection::from_slice(&[TestInstruction(0x10)]); let mut prologue_usage = register_usage::RegisterUsageInfo::new(); prologue_usage.add_read_register(TestRegister::SP); prologue_usage.add_read_register(TestRegister::S0); - + // Main: actual computation - let main_instrs = InstructionCollection::from_slice(&[ - TestInstruction(0x20), - TestInstruction(0x21), - ]); + let main_instrs = + InstructionCollection::from_slice(&[TestInstruction(0x20), TestInstruction(0x21)]); let mut main_usage = register_usage::RegisterUsageInfo::new(); main_usage.add_written_register(TestRegister::T0); main_usage.add_written_register(TestRegister::S0); main_usage.add_read_register(TestRegister::T1); - + // Epilogue: restore registers let epilogue_instrs = InstructionCollection::from_slice(&[TestInstruction(0x30)]); let mut epilogue_usage = register_usage::RegisterUsageInfo::new(); epilogue_usage.add_read_register(TestRegister::SP); epilogue_usage.add_written_register(TestRegister::S0); - + let prologue = InstructionCollectionWithUsage::new(prologue_instrs, prologue_usage); let main = InstructionCollectionWithUsage::new(main_instrs, main_usage); let epilogue = InstructionCollectionWithUsage::new(epilogue_instrs, epilogue_usage); - + // Merge: prologue + main + epilogue let combined = prologue + main + epilogue; - + // Check instructions assert_eq!(combined.instructions().len(), 4); assert_eq!(combined.instructions()[0], TestInstruction(0x10)); assert_eq!(combined.instructions()[1], TestInstruction(0x20)); assert_eq!(combined.instructions()[2], TestInstruction(0x21)); assert_eq!(combined.instructions()[3], TestInstruction(0x30)); - + // Check register usage is properly merged let usage = combined.register_usage(); - assert_eq!(usage.register_count(), 4); // T0, T1, S0, SP + assert_eq!(usage.register_count(), 4); // T0, T1, S0, SP assert!(usage.contains_register(&TestRegister::T0)); assert!(usage.contains_register(&TestRegister::T1)); assert!(usage.contains_register(&TestRegister::S0)); assert!(usage.contains_register(&TestRegister::SP)); - + // Check that S0 appears as both written and read assert!(usage.contains_written_register(&TestRegister::S0)); assert!(usage.contains_read_register(&TestRegister::S0)); } - + #[test] fn test_instruction_collection_with_usage_into_parts() { let instructions = InstructionCollection::from_slice(&[TestInstruction(1)]); let mut usage = register_usage::RegisterUsageInfo::new(); usage.add_written_register(TestRegister::T0); - + let tracked = InstructionCollectionWithUsage::new(instructions.clone(), usage.clone()); let (instrs, reg_usage) = tracked.into_parts(); - + assert_eq!(instrs.len(), 1); assert_eq!(reg_usage.register_count(), 1); } - + #[test] fn test_instruction_collection_with_usage_extend_from() { let instructions1 = InstructionCollection::from_slice(&[TestInstruction(1)]); let mut usage1 = register_usage::RegisterUsageInfo::new(); usage1.add_written_register(TestRegister::T0); - + let instructions2 = InstructionCollection::from_slice(&[TestInstruction(2)]); let mut usage2 = register_usage::RegisterUsageInfo::new(); usage2.add_read_register(TestRegister::S0); - + let mut tracked1 = InstructionCollectionWithUsage::new(instructions1, usage1); let tracked2 = InstructionCollectionWithUsage::new(instructions2, usage2); - + tracked1.extend_from(&tracked2); - + // tracked1 should be extended assert_eq!(tracked1.instructions().len(), 2); assert_eq!(tracked1.register_usage().register_count(), 2); - + // tracked2 should still exist assert_eq!(tracked2.instructions().len(), 1); assert_eq!(tracked2.register_usage().register_count(), 1); } -} \ No newline at end of file +} diff --git a/src/common/register_usage.rs b/src/common/register_usage.rs index 980f66f..25f4151 100644 --- a/src/common/register_usage.rs +++ b/src/common/register_usage.rs @@ -1,38 +1,38 @@ //! Register usage tracking functionality for JIT assembly. -//! -//! This module provides utilities to track which registers are used by -//! assembled instructions, enabling better register allocation and ABI +//! +//! This module provides utilities to track which registers are used by +//! assembled instructions, enabling better register allocation and ABI //! compliance analysis. -use core::fmt; use crate::common::{AbiClass, Register}; +use core::fmt; -#[cfg(feature = "std")] -use std::collections::HashSet; #[cfg(not(feature = "std"))] use hashbrown::HashSet; - #[cfg(feature = "std")] -use std::vec::Vec; +use std::collections::HashSet; + #[cfg(not(feature = "std"))] use alloc::vec::Vec; +#[cfg(feature = "std")] +use std::vec::Vec; /// Information about register usage in a collection of instructions. -/// +/// /// This structure tracks which registers are used and provides analysis /// methods for ABI compliance and register allocation decisions. -/// +/// /// # Type Parameter -/// +/// /// - `R`: The register type, must implement `Register` -/// +/// /// # Example -/// +/// /// ```rust,ignore /// let mut usage_info = RegisterUsageInfo::new(); /// usage_info.add_written_register(Register::T0); /// usage_info.add_read_register(Register::T1); -/// +/// /// println!("Written (def): {:?}", usage_info.written_registers()); /// println!("Read (use): {:?}", usage_info.read_registers()); /// println!("Needs stack frame: {}", usage_info.needs_stack_frame()); @@ -53,38 +53,38 @@ impl RegisterUsageInfo { read_registers: HashSet::new(), } } - + /// Add a register that is written to (destination register). - /// + /// /// This should be called for rd registers that receive new values. pub fn add_written_register(&mut self, register: R) { self.written_registers.insert(register); } - + /// Add a register that is read from (source register). - /// + /// /// This should be called for rs1, rs2 registers that provide input values. pub fn add_read_register(&mut self, register: R) { self.read_registers.insert(register); } - + /// Get all written registers (def). - /// + /// /// These are registers that receive new values and may need to be /// preserved if they are callee-saved. pub fn written_registers(&self) -> Vec { self.written_registers.iter().copied().collect() } - + /// Get all read registers (use). - /// + /// /// These are registers that provide input values. pub fn read_registers(&self) -> Vec { self.read_registers.iter().copied().collect() } - + /// Get all used registers (def ∪ use). - /// + /// /// This returns the union of written and read registers. /// The order of registers in the returned vector is not guaranteed. pub fn used_registers(&self) -> Vec { @@ -92,9 +92,9 @@ impl RegisterUsageInfo { all_registers.extend(&self.read_registers); all_registers.iter().copied().collect() } - + /// Get all caller-saved registers that are written to. - /// + /// /// These registers don't need special preservation handling in JIT code. pub fn caller_saved_written(&self) -> Vec { self.written_registers @@ -103,9 +103,9 @@ impl RegisterUsageInfo { .copied() .collect() } - + /// Get all caller-saved registers that are used (written or read). - /// + /// /// These registers don't need special preservation handling in JIT code. pub fn caller_saved_registers(&self) -> Vec { let mut all_registers: HashSet = self.written_registers.clone(); @@ -116,9 +116,9 @@ impl RegisterUsageInfo { .copied() .collect() } - + /// Get all callee-saved registers that are written to. - /// + /// /// These registers must be saved on function entry and restored on exit /// if used by JIT-compiled code. This is the primary list for determining /// which registers need preservation. @@ -129,9 +129,9 @@ impl RegisterUsageInfo { .copied() .collect() } - + /// Get all callee-saved registers that are used (written or read). - /// + /// /// For register preservation, prefer callee_saved_written() which only /// includes registers that actually need to be saved. pub fn callee_saved_registers(&self) -> Vec { @@ -143,9 +143,9 @@ impl RegisterUsageInfo { .copied() .collect() } - + /// Get all special-purpose registers that are used. - /// + /// /// These registers require careful handling and may indicate /// advanced usage patterns. pub fn special_registers(&self) -> Vec { @@ -157,39 +157,41 @@ impl RegisterUsageInfo { .copied() .collect() } - + /// Get the total number of unique registers used. pub fn register_count(&self) -> usize { let mut all_registers: HashSet = self.written_registers.clone(); all_registers.extend(&self.read_registers); all_registers.len() } - + /// Check if any registers are used. pub fn has_used_registers(&self) -> bool { !self.written_registers.is_empty() || !self.read_registers.is_empty() } - + /// Check if any callee-saved registers are written to. - /// + /// /// If this returns `true`, the JIT-compiled function needs to implement /// a proper function prologue/epilogue to save and restore these registers. /// Only written registers need to be preserved, not read-only ones. pub fn needs_stack_frame(&self) -> bool { - self.written_registers.iter().any(|reg| reg.is_callee_saved()) + self.written_registers + .iter() + .any(|reg| reg.is_callee_saved()) } - + /// Get a count of registers by ABI class. - /// + /// /// Returns a tuple of (caller_saved_count, callee_saved_count, special_count). pub fn count_by_abi_class(&self) -> (usize, usize, usize) { let mut caller_saved = 0; let mut callee_saved = 0; let mut special = 0; - + let mut all_registers: HashSet = self.written_registers.clone(); all_registers.extend(&self.read_registers); - + for register in &all_registers { match register.abi_class() { AbiClass::CallerSaved => caller_saved += 1, @@ -197,33 +199,33 @@ impl RegisterUsageInfo { AbiClass::Special => special += 1, } } - + (caller_saved, callee_saved, special) } - + /// Clear all register usage information. pub fn clear(&mut self) { self.written_registers.clear(); self.read_registers.clear(); } - + /// Check if a specific register is used (written or read). pub fn contains_register(&self, register: &R) -> bool { self.written_registers.contains(register) || self.read_registers.contains(register) } - + /// Check if a specific register is written to. pub fn contains_written_register(&self, register: &R) -> bool { self.written_registers.contains(register) } - + /// Check if a specific register is read from. pub fn contains_read_register(&self, register: &R) -> bool { self.read_registers.contains(register) } - + /// Merge another register usage info into this one. - /// + /// /// This is useful for combining usage information from multiple /// instruction sequences. pub fn merge(&mut self, other: &RegisterUsageInfo) { @@ -234,7 +236,7 @@ impl RegisterUsageInfo { self.read_registers.insert(*register); } } - + /// Create a new register usage info by merging two existing ones. pub fn merged(mut self, other: &RegisterUsageInfo) -> Self { self.merge(other); @@ -251,38 +253,44 @@ impl Default for RegisterUsageInfo { impl fmt::Display for RegisterUsageInfo { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let (caller_saved, callee_saved, special) = self.count_by_abi_class(); - - write!(f, "RegisterUsage(total: {}, caller-saved: {}, callee-saved: {}, special: {})", - self.register_count(), caller_saved, callee_saved, special)?; - + + write!( + f, + "RegisterUsage(total: {}, caller-saved: {}, callee-saved: {}, special: {})", + self.register_count(), + caller_saved, + callee_saved, + special + )?; + if self.needs_stack_frame() { write!(f, " [needs stack frame]")?; } - + Ok(()) } } /// Trait for tracking register usage in instruction builders. -/// +/// /// This trait provides methods for instruction builders to track which /// registers are used during code generation, enabling register usage /// analysis and optimization. pub trait RegisterUsageTracker { /// Get the current register usage information. - /// + /// /// This returns a snapshot of all registers that have been used /// by instructions added to the builder. fn register_usage(&self) -> &RegisterUsageInfo; - + /// Get a mutable reference to the register usage information. - /// + /// /// This allows direct manipulation of the usage tracking, which /// can be useful for advanced use cases. fn register_usage_mut(&mut self) -> &mut RegisterUsageInfo; - + /// Clear all register usage information. - /// + /// /// This resets the usage tracking to an empty state, which can be /// useful when reusing a builder for multiple functions. fn clear_register_usage(&mut self) { @@ -293,15 +301,18 @@ pub trait RegisterUsageTracker { #[cfg(test)] mod tests { use super::*; - + // Test register type for unit tests #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] enum TestRegister { - T0, T1, // Caller-saved - S0, S1, // Callee-saved - SP, FP, // Special + T0, + T1, // Caller-saved + S0, + S1, // Callee-saved + SP, + FP, // Special } - + impl crate::common::Register for TestRegister { fn id(&self) -> u32 { match self { @@ -313,7 +324,7 @@ mod tests { TestRegister::FP => 5, } } - + fn abi_class(&self) -> crate::common::AbiClass { match self { TestRegister::T0 | TestRegister::T1 => crate::common::AbiClass::CallerSaved, @@ -322,39 +333,45 @@ mod tests { } } } - + #[test] fn test_abi_class_display() { - assert_eq!(crate::common::AbiClass::CallerSaved.to_string(), "caller-saved"); - assert_eq!(crate::common::AbiClass::CalleeSaved.to_string(), "callee-saved"); + assert_eq!( + crate::common::AbiClass::CallerSaved.to_string(), + "caller-saved" + ); + assert_eq!( + crate::common::AbiClass::CalleeSaved.to_string(), + "callee-saved" + ); assert_eq!(crate::common::AbiClass::Special.to_string(), "special"); } - + #[test] fn test_register_trait() { - use crate::common::{Register, AbiClass}; - + use crate::common::{AbiClass, Register}; + assert!(TestRegister::T0.is_caller_saved()); assert!(!TestRegister::T0.is_callee_saved()); assert_eq!(TestRegister::T0.abi_class(), AbiClass::CallerSaved); - + assert!(!TestRegister::S0.is_caller_saved()); assert!(TestRegister::S0.is_callee_saved()); assert_eq!(TestRegister::S0.abi_class(), AbiClass::CalleeSaved); - + assert!(!TestRegister::SP.is_caller_saved()); assert!(!TestRegister::SP.is_callee_saved()); assert_eq!(TestRegister::SP.abi_class(), AbiClass::Special); assert!(TestRegister::SP.is_special()); } - + #[test] fn test_register_usage_info() { let mut info = RegisterUsageInfo::new(); assert_eq!(info.register_count(), 0); assert!(!info.has_used_registers()); assert!(!info.needs_stack_frame()); - + info.add_written_register(TestRegister::T0); info.add_written_register(TestRegister::T1); info.add_written_register(TestRegister::S0); @@ -362,47 +379,47 @@ mod tests { assert_eq!(info.register_count(), 3); assert!(info.has_used_registers()); assert!(info.needs_stack_frame()); - + let caller_saved = info.caller_saved_registers(); assert_eq!(caller_saved.len(), 2); assert!(caller_saved.contains(&TestRegister::T0)); assert!(caller_saved.contains(&TestRegister::T1)); - + let callee_saved = info.callee_saved_registers(); assert_eq!(callee_saved.len(), 1); assert!(callee_saved.contains(&TestRegister::S0)); - + let (caller_count, callee_count, special_count) = info.count_by_abi_class(); assert_eq!(caller_count, 2); assert_eq!(callee_count, 1); assert_eq!(special_count, 0); } - + #[test] fn test_register_usage_merge() { let mut info1 = RegisterUsageInfo::new(); info1.add_written_register(TestRegister::T0); info1.add_read_register(TestRegister::S0); - + let mut info2 = RegisterUsageInfo::new(); info2.add_read_register(TestRegister::T1); - info2.add_read_register(TestRegister::S0); // Duplicate - + info2.add_read_register(TestRegister::S0); // Duplicate + info1.merge(&info2); - + assert_eq!(info1.register_count(), 3); assert!(info1.contains_register(&TestRegister::T0)); assert!(info1.contains_register(&TestRegister::T1)); assert!(info1.contains_register(&TestRegister::S0)); } - + #[test] fn test_register_usage_display() { let mut info = RegisterUsageInfo::new(); info.add_written_register(TestRegister::T0); info.add_written_register(TestRegister::S0); info.add_read_register(TestRegister::SP); - + let display = info.to_string(); assert!(display.contains("total: 3")); assert!(display.contains("caller-saved: 1")); @@ -410,31 +427,31 @@ mod tests { assert!(display.contains("special: 1")); assert!(display.contains("needs stack frame")); } - + #[test] fn test_written_vs_read_registers() { let mut info = RegisterUsageInfo::new(); - + // Add some written registers info.add_written_register(TestRegister::T0); info.add_written_register(TestRegister::S0); - + // Add some read registers info.add_read_register(TestRegister::T1); info.add_read_register(TestRegister::SP); - + // Test written registers let written = info.written_registers(); assert_eq!(written.len(), 2); assert!(written.contains(&TestRegister::T0)); assert!(written.contains(&TestRegister::S0)); - + // Test read registers let read = info.read_registers(); assert_eq!(read.len(), 2); assert!(read.contains(&TestRegister::T1)); assert!(read.contains(&TestRegister::SP)); - + // Test combined used registers let used = info.used_registers(); assert_eq!(used.len(), 4); @@ -443,122 +460,122 @@ mod tests { assert!(used.contains(&TestRegister::T1)); assert!(used.contains(&TestRegister::SP)); } - + #[test] fn test_abi_class_specific_methods() { let mut info = RegisterUsageInfo::new(); - + // Mix of written and read registers - info.add_written_register(TestRegister::T0); // caller-saved written - info.add_read_register(TestRegister::T1); // caller-saved read - info.add_written_register(TestRegister::S0); // callee-saved written - info.add_read_register(TestRegister::S1); // callee-saved read - info.add_read_register(TestRegister::SP); // special read - + info.add_written_register(TestRegister::T0); // caller-saved written + info.add_read_register(TestRegister::T1); // caller-saved read + info.add_written_register(TestRegister::S0); // callee-saved written + info.add_read_register(TestRegister::S1); // callee-saved read + info.add_read_register(TestRegister::SP); // special read + // Test caller-saved written (only T0) let caller_written = info.caller_saved_written(); assert_eq!(caller_written.len(), 1); assert!(caller_written.contains(&TestRegister::T0)); - + // Test callee-saved written (only S0) let callee_written = info.callee_saved_written(); assert_eq!(callee_written.len(), 1); assert!(callee_written.contains(&TestRegister::S0)); - + // Test all caller-saved (T0 and T1) let caller_all = info.caller_saved_registers(); assert_eq!(caller_all.len(), 2); assert!(caller_all.contains(&TestRegister::T0)); assert!(caller_all.contains(&TestRegister::T1)); - + // Test all callee-saved (S0 and S1) let callee_all = info.callee_saved_registers(); assert_eq!(callee_all.len(), 2); assert!(callee_all.contains(&TestRegister::S0)); assert!(callee_all.contains(&TestRegister::S1)); - + // Stack frame needed because S0 is written assert!(info.needs_stack_frame()); } - + #[test] fn test_contains_methods() { let mut info = RegisterUsageInfo::new(); info.add_written_register(TestRegister::T0); info.add_read_register(TestRegister::S0); - + // Test general contains assert!(info.contains_register(&TestRegister::T0)); assert!(info.contains_register(&TestRegister::S0)); assert!(!info.contains_register(&TestRegister::T1)); - + // Test specific contains assert!(info.contains_written_register(&TestRegister::T0)); assert!(!info.contains_written_register(&TestRegister::S0)); - + assert!(info.contains_read_register(&TestRegister::S0)); assert!(!info.contains_read_register(&TestRegister::T0)); } - + #[test] fn test_stack_frame_requirements() { let mut info = RegisterUsageInfo::new(); - + // Only caller-saved registers - no stack frame needed info.add_written_register(TestRegister::T0); info.add_written_register(TestRegister::T1); assert!(!info.needs_stack_frame()); - + // Add callee-saved read - still no stack frame needed info.add_read_register(TestRegister::S0); assert!(!info.needs_stack_frame()); - + // Add callee-saved write - now stack frame needed info.add_written_register(TestRegister::S1); assert!(info.needs_stack_frame()); } - + #[test] fn test_register_overlap() { let mut info = RegisterUsageInfo::new(); - + // Same register used as both written and read info.add_written_register(TestRegister::T0); info.add_read_register(TestRegister::T0); - + // Should appear in both lists assert!(info.contains_written_register(&TestRegister::T0)); assert!(info.contains_read_register(&TestRegister::T0)); - + // But only once in combined list let used = info.used_registers(); assert_eq!(used.len(), 1); assert!(used.contains(&TestRegister::T0)); - + let written = info.written_registers(); let read = info.read_registers(); assert_eq!(written.len(), 1); assert_eq!(read.len(), 1); } - + #[test] fn test_empty_register_usage() { let info = RegisterUsageInfo::::new(); - + assert_eq!(info.register_count(), 0); assert!(!info.has_used_registers()); assert!(!info.needs_stack_frame()); - + assert!(info.written_registers().is_empty()); assert!(info.read_registers().is_empty()); assert!(info.used_registers().is_empty()); assert!(info.caller_saved_registers().is_empty()); assert!(info.callee_saved_registers().is_empty()); assert!(info.special_registers().is_empty()); - + let (caller, callee, special) = info.count_by_abi_class(); assert_eq!(caller, 0); assert_eq!(callee, 0); assert_eq!(special, 0); } -} \ No newline at end of file +} diff --git a/src/lib.rs b/src/lib.rs index ffd1f14..9cbaf46 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -33,7 +33,8 @@ //! .addi(reg::A0, reg::ZERO, 100) //! .add(reg::A1, reg::A0, reg::SP) //! .ret() -//! .instructions(); +//! .instructions() +//! .unwrap(); //! //! // Macro style (concise and assembly-like) //! let instructions3 = jit_assembler::riscv64_asm! { @@ -48,7 +49,7 @@ //! builder2.csrrw(reg::RA, csr::MSTATUS, reg::SP); //! builder2.addi(reg::A0, reg::ZERO, 100); //! builder2.ret(); -//! let instructions2 = builder2.instructions(); +//! let instructions2 = builder2.instructions().unwrap(); //! // InstructionCollection provides convenient methods //! let bytes = instructions.to_bytes(); // Convert all to bytes //! let size = instructions.total_size(); // Get total size @@ -75,7 +76,8 @@ //! let instructions = builder //! .add(reg::X0, reg::X0, reg::X1) // Add first two arguments (X0 + X1 -> X0) //! .ret() // Return -//! .instructions(); +//! .instructions() +//! .unwrap(); //! //! // Macro style (concise and assembly-like) //! let instructions3 = jit_assembler::aarch64_asm! { @@ -92,7 +94,8 @@ //! .mul(reg::X0, reg::X0, reg::X1) // Multiply X0 by 42 //! .addi(reg::X0, reg::X0, 100) // Add 100 to result //! .ret() // Return -//! .instructions(); +//! .instructions() +//! .unwrap(); //! # } //! ``` //! @@ -103,7 +106,7 @@ //! # { //! use jit_assembler::riscv64::{reg, Riscv64InstructionBuilder}; //! use jit_assembler::common::InstructionBuilder; -//! +//! //! // Create a JIT function that adds two numbers //! let add_func = unsafe { //! Riscv64InstructionBuilder::new() @@ -132,9 +135,6 @@ pub use common::jit::{CallableJitFunction, JitError}; #[cfg(feature = "riscv64")] pub mod riscv64; -#[cfg(feature = "x86_64")] -pub mod x86_64; - #[cfg(feature = "aarch64")] pub mod aarch64; @@ -164,6 +164,6 @@ macro_rules! jit_asm_generic { $( builder.$method($($args),*); )* - builder.instructions().to_vec() + builder.instructions().unwrap().to_vec() }}; -} \ No newline at end of file +} diff --git a/src/riscv64/builder.rs b/src/riscv64/builder.rs index 020a4bc..d175322 100644 --- a/src/riscv64/builder.rs +++ b/src/riscv64/builder.rs @@ -1,15 +1,16 @@ /// Instruction builder interface for RISC-V assembly generation use super::instruction::*; -use crate::common::InstructionBuilder; +use crate::common::{BuildError, InstructionBuilder}; -#[cfg(feature = "std")] -use std::vec::Vec; #[cfg(not(feature = "std"))] use alloc::vec::Vec; +#[cfg(feature = "std")] +use std::vec::Vec; /// Instruction builder for generating RISC-V instructions pub struct Riscv64InstructionBuilder { instructions: Vec, + error: Option, #[cfg(feature = "register-tracking")] register_usage: crate::common::register_usage::RegisterUsageInfo, } @@ -18,23 +19,24 @@ impl Riscv64InstructionBuilder { pub fn new() -> Self { Self { instructions: Vec::new(), + error: None, #[cfg(feature = "register-tracking")] register_usage: crate::common::register_usage::RegisterUsageInfo::new(), } } - + /// Track a written register (register-tracking feature only) #[cfg(feature = "register-tracking")] fn track_written_register(&mut self, reg: Register) { self.register_usage.add_written_register(reg); } - + /// Track a read register (register-tracking feature only) #[cfg(feature = "register-tracking")] fn track_read_register(&mut self, reg: Register) { self.register_usage.add_read_register(reg); } - + /// Track multiple read registers at once (register-tracking feature only) #[cfg(feature = "register-tracking")] fn track_read_registers(&mut self, regs: &[Register]) { @@ -42,72 +44,115 @@ impl Riscv64InstructionBuilder { self.register_usage.add_read_register(reg); } } - + /// No-op versions for when register-tracking is disabled #[cfg(not(feature = "register-tracking"))] fn track_written_register(&mut self, _reg: Register) { // No-op } - + #[cfg(not(feature = "register-tracking"))] fn track_read_register(&mut self, _reg: Register) { // No-op } - + #[cfg(not(feature = "register-tracking"))] fn track_read_registers(&mut self, _regs: &[Register]) { // No-op } - + // Register tracking wrapper functions for encode_* functions - + /// R-type instruction with register tracking: rd = f(rs1, rs2) - fn encode_r_type_tracked(&mut self, opcode: u8, rd: Register, funct3: u8, rs1: Register, rs2: Register, funct7: u8) -> Instruction { + fn encode_r_type_tracked( + &mut self, + opcode: u8, + rd: Register, + funct3: u8, + rs1: Register, + rs2: Register, + funct7: u8, + ) -> Instruction { self.track_written_register(rd); self.track_read_registers(&[rs1, rs2]); encode_r_type(opcode, rd, funct3, rs1, rs2, funct7) } - + /// I-type instruction with register tracking: rd = f(rs1, imm) - fn encode_i_type_tracked(&mut self, opcode: u8, rd: Register, funct3: u8, rs1: Register, imm: i16) -> Instruction { + fn encode_i_type_tracked( + &mut self, + opcode: u8, + rd: Register, + funct3: u8, + rs1: Register, + imm: i16, + ) -> Instruction { self.track_written_register(rd); self.track_read_register(rs1); encode_i_type(opcode, rd, funct3, rs1, imm) } - + /// S-type instruction with register tracking: MEM[rs1 + imm] = rs2 - fn encode_s_type_tracked(&mut self, opcode: u8, funct3: u8, rs1: Register, rs2: Register, imm: i16) -> Instruction { + fn encode_s_type_tracked( + &mut self, + opcode: u8, + funct3: u8, + rs1: Register, + rs2: Register, + imm: i16, + ) -> Instruction { self.track_read_registers(&[rs1, rs2]); encode_s_type(opcode, funct3, rs1, rs2, imm) } - + /// B-type instruction with register tracking: branch if f(rs1, rs2) - fn encode_b_type_tracked(&mut self, opcode: u8, funct3: u8, rs1: Register, rs2: Register, imm: i16) -> Instruction { + fn encode_b_type_tracked( + &mut self, + opcode: u8, + funct3: u8, + rs1: Register, + rs2: Register, + imm: i16, + ) -> Instruction { self.track_read_registers(&[rs1, rs2]); encode_b_type(opcode, funct3, rs1, rs2, imm) } - + /// U-type instruction with register tracking: rd = imm << 12 fn encode_u_type_tracked(&mut self, opcode: u8, rd: Register, imm: u32) -> Instruction { self.track_written_register(rd); encode_u_type(opcode, rd, imm) } - + /// J-type instruction with register tracking: rd = PC + 4, PC += imm fn encode_j_type_tracked(&mut self, opcode: u8, rd: Register, imm: i32) -> Instruction { self.track_written_register(rd); encode_j_type(opcode, rd, imm) } - + /// CSR-type instruction with register tracking: rd = CSR, CSR = f(CSR, rs1) - fn encode_csr_type_tracked(&mut self, opcode: u8, rd: Register, funct3: u8, rs1: Register, csr: Csr) -> Instruction { + fn encode_csr_type_tracked( + &mut self, + opcode: u8, + rd: Register, + funct3: u8, + rs1: Register, + csr: Csr, + ) -> Instruction { self.track_written_register(rd); self.track_read_register(rs1); encode_csr_type(opcode, rd, funct3, rs1, csr) } - + /// CSR immediate-type instruction with register tracking: rd = CSR, CSR = f(CSR, uimm) - fn encode_csr_imm_type_tracked(&mut self, opcode: u8, rd: Register, funct3: u8, uimm: u8, csr: Csr) -> Instruction { + fn encode_csr_imm_type_tracked( + &mut self, + opcode: u8, + rd: Register, + funct3: u8, + uimm: u8, + csr: Csr, + ) -> Instruction { self.track_written_register(rd); encode_csr_imm_type(opcode, rd, funct3, uimm, csr) } @@ -138,17 +183,31 @@ impl Riscv64InstructionBuilder { impl InstructionBuilder for Riscv64InstructionBuilder { type Register = Register; - + type Error = BuildError; + fn new() -> Self { Self { instructions: Vec::new(), + error: None, #[cfg(feature = "register-tracking")] register_usage: crate::common::register_usage::RegisterUsageInfo::new(), } } - fn instructions(&self) -> crate::common::InstructionCollection { - crate::common::InstructionCollection::from_slice(&self.instructions) + fn instructions( + &self, + ) -> Result, Self::Error> { + if let Some(ref error) = self.error { + Err(*error) + } else { + Ok(crate::common::InstructionCollection::from_slice( + &self.instructions, + )) + } + } + + fn set_error(&mut self, error: Self::Error) { + self.error = Some(error); } fn push(&mut self, instr: Instruction) { @@ -157,59 +216,65 @@ impl InstructionBuilder for Riscv64InstructionBuilder { fn clear(&mut self) { self.instructions.clear(); + self.error = None; #[cfg(feature = "register-tracking")] self.register_usage.clear(); } - + #[cfg(feature = "register-tracking")] fn register_usage(&self) -> &crate::common::register_usage::RegisterUsageInfo { &self.register_usage } - + #[cfg(feature = "register-tracking")] - fn register_usage_mut(&mut self) -> &mut crate::common::register_usage::RegisterUsageInfo { + fn register_usage_mut( + &mut self, + ) -> &mut crate::common::register_usage::RegisterUsageInfo { &mut self.register_usage } - + /// Create a JIT-compiled function from the assembled instructions (std-only) - /// + /// /// This method converts the assembled instructions into executable machine code /// that can be called directly as a function. The generic type parameter `F` /// specifies the function signature. - /// + /// /// # Safety - /// + /// /// This function is unsafe because: /// - It allocates executable memory /// - It assumes the assembled code follows the correct ABI /// - The caller must ensure the function signature matches the actual code - /// + /// /// # Examples - /// + /// /// ```rust,no_run /// use jit_assembler::riscv64::{reg, Riscv64InstructionBuilder}; /// use jit_assembler::common::InstructionBuilder; - /// + /// /// let add_func = unsafe { /// Riscv64InstructionBuilder::new() /// .add(reg::A0, reg::A0, reg::A1) // Add first two arguments /// .ret() /// .function:: u64>() /// }.expect("Failed to create JIT function"); - /// + /// /// // Call the JIT function directly (only works on RISC-V hosts) /// let result = add_func.call(10, 20); // Should return 30 /// ``` #[cfg(feature = "std")] - unsafe fn function(&self) -> Result, crate::common::jit::JitError> { - // Convert instructions to bytes using the new Instructions struct - let code = self.instructions().to_bytes(); + unsafe fn function( + &self, + ) -> Result, crate::common::jit::JitError> { + let code = self.instructions()?.to_bytes(); crate::common::jit::CallableJitFunction::::new(&code) } #[cfg(feature = "std")] - unsafe fn raw_function(&self) -> Result { - let code = self.instructions().to_bytes(); + unsafe fn raw_function( + &self, + ) -> Result { + let code = self.instructions()?.to_bytes(); crate::common::jit::RawCallableJitFunction::new(&code) } } @@ -217,42 +282,48 @@ impl InstructionBuilder for Riscv64InstructionBuilder { impl Riscv64InstructionBuilder { /// Generate CSR read-write instruction pub fn csrrw(&mut self, rd: Register, csr: Csr, rs1: Register) -> &mut Self { - let instr = self.encode_csr_type_tracked(opcodes::SYSTEM, rd, system_funct3::CSRRW, rs1, csr); + let instr = + self.encode_csr_type_tracked(opcodes::SYSTEM, rd, system_funct3::CSRRW, rs1, csr); self.push(instr); self } /// Generate CSR read-set instruction pub fn csrrs(&mut self, rd: Register, csr: Csr, rs1: Register) -> &mut Self { - let instr = self.encode_csr_type_tracked(opcodes::SYSTEM, rd, system_funct3::CSRRS, rs1, csr); + let instr = + self.encode_csr_type_tracked(opcodes::SYSTEM, rd, system_funct3::CSRRS, rs1, csr); self.push(instr); self } /// Generate CSR read-clear instruction pub fn csrrc(&mut self, rd: Register, csr: Csr, rs1: Register) -> &mut Self { - let instr = self.encode_csr_type_tracked(opcodes::SYSTEM, rd, system_funct3::CSRRC, rs1, csr); + let instr = + self.encode_csr_type_tracked(opcodes::SYSTEM, rd, system_funct3::CSRRC, rs1, csr); self.push(instr); self } /// Generate CSR read-write immediate instruction pub fn csrrwi(&mut self, rd: Register, csr: Csr, uimm: u8) -> &mut Self { - let instr = self.encode_csr_imm_type_tracked(opcodes::SYSTEM, rd, system_funct3::CSRRWI, uimm, csr); + let instr = + self.encode_csr_imm_type_tracked(opcodes::SYSTEM, rd, system_funct3::CSRRWI, uimm, csr); self.push(instr); self } /// Generate CSR read-set immediate instruction pub fn csrrsi(&mut self, rd: Register, csr: Csr, uimm: u8) -> &mut Self { - let instr = self.encode_csr_imm_type_tracked(opcodes::SYSTEM, rd, system_funct3::CSRRSI, uimm, csr); + let instr = + self.encode_csr_imm_type_tracked(opcodes::SYSTEM, rd, system_funct3::CSRRSI, uimm, csr); self.push(instr); self } /// Generate CSR read-clear immediate instruction pub fn csrrci(&mut self, rd: Register, csr: Csr, uimm: u8) -> &mut Self { - let instr = self.encode_csr_imm_type_tracked(opcodes::SYSTEM, rd, system_funct3::CSRRCI, uimm, csr); + let instr = + self.encode_csr_imm_type_tracked(opcodes::SYSTEM, rd, system_funct3::CSRRCI, uimm, csr); self.push(instr); self } @@ -316,15 +387,14 @@ impl Riscv64InstructionBuilder { self } - /// Generate subtract instruction pub fn sub(&mut self, rd: Register, rs1: Register, rs2: Register) -> &mut Self { - let instr = self.encode_r_type_tracked(opcodes::OP, rd, alu_funct3::ADD_SUB, rs1, rs2, 0x20); + let instr = + self.encode_r_type_tracked(opcodes::OP, rd, alu_funct3::ADD_SUB, rs1, rs2, 0x20); self.push(instr); self } - /// Generate subtract immediate instruction pub fn subi(&mut self, rd: Register, rs1: Register, imm: i16) -> &mut Self { let instr = self.encode_i_type_tracked(opcodes::OP_IMM, rd, alu_funct3::ADD_SUB, rs1, -imm); @@ -404,28 +474,37 @@ impl Riscv64InstructionBuilder { /// Generate SRA (Shift Right Arithmetic) instruction pub fn sra(&mut self, rd: Register, rs1: Register, rs2: Register) -> &mut Self { - let instr = self.encode_r_type_tracked(opcodes::OP, rd, alu_funct3::SRL_SRA, rs1, rs2, 0x20); + let instr = + self.encode_r_type_tracked(opcodes::OP, rd, alu_funct3::SRL_SRA, rs1, rs2, 0x20); self.push(instr); self } /// Generate SLLI (Shift Left Logical Immediate) instruction pub fn slli(&mut self, rd: Register, rs1: Register, shamt: u8) -> &mut Self { - let instr = self.encode_i_type_tracked(opcodes::OP_IMM, rd, alu_funct3::SLL, rs1, shamt as i16); + let instr = + self.encode_i_type_tracked(opcodes::OP_IMM, rd, alu_funct3::SLL, rs1, shamt as i16); self.push(instr); self } /// Generate SRLI (Shift Right Logical Immediate) instruction pub fn srli(&mut self, rd: Register, rs1: Register, shamt: u8) -> &mut Self { - let instr = self.encode_i_type_tracked(opcodes::OP_IMM, rd, alu_funct3::SRL_SRA, rs1, shamt as i16); + let instr = + self.encode_i_type_tracked(opcodes::OP_IMM, rd, alu_funct3::SRL_SRA, rs1, shamt as i16); self.push(instr); self } /// Generate SRAI (Shift Right Arithmetic Immediate) instruction pub fn srai(&mut self, rd: Register, rs1: Register, shamt: u8) -> &mut Self { - let instr = self.encode_i_type_tracked(opcodes::OP_IMM, rd, alu_funct3::SRL_SRA, rs1, (shamt as i16) | 0x400); + let instr = self.encode_i_type_tracked( + opcodes::OP_IMM, + rd, + alu_funct3::SRL_SRA, + rs1, + (shamt as i16) | 0x400, + ); self.push(instr); self } @@ -449,7 +528,8 @@ impl Riscv64InstructionBuilder { /// Generate MUL (Multiply) instruction /// Performs signed multiplication and returns the lower 64 bits of the result pub fn mul(&mut self, rd: Register, rs1: Register, rs2: Register) -> &mut Self { - let instr = self.encode_r_type_tracked(opcodes::OP, rd, m_funct3::MUL, rs1, rs2, m_funct7::M_EXT); + let instr = + self.encode_r_type_tracked(opcodes::OP, rd, m_funct3::MUL, rs1, rs2, m_funct7::M_EXT); self.push(instr); self } @@ -457,7 +537,8 @@ impl Riscv64InstructionBuilder { /// Generate MULH (Multiply High) instruction /// Performs signed × signed multiplication and returns the upper 64 bits of the result pub fn mulh(&mut self, rd: Register, rs1: Register, rs2: Register) -> &mut Self { - let instr = self.encode_r_type_tracked(opcodes::OP, rd, m_funct3::MULH, rs1, rs2, m_funct7::M_EXT); + let instr = + self.encode_r_type_tracked(opcodes::OP, rd, m_funct3::MULH, rs1, rs2, m_funct7::M_EXT); self.push(instr); self } @@ -465,7 +546,14 @@ impl Riscv64InstructionBuilder { /// Generate MULHSU (Multiply High Signed × Unsigned) instruction /// Performs signed × unsigned multiplication and returns the upper 64 bits of the result pub fn mulhsu(&mut self, rd: Register, rs1: Register, rs2: Register) -> &mut Self { - let instr = self.encode_r_type_tracked(opcodes::OP, rd, m_funct3::MULHSU, rs1, rs2, m_funct7::M_EXT); + let instr = self.encode_r_type_tracked( + opcodes::OP, + rd, + m_funct3::MULHSU, + rs1, + rs2, + m_funct7::M_EXT, + ); self.push(instr); self } @@ -473,7 +561,8 @@ impl Riscv64InstructionBuilder { /// Generate MULHU (Multiply High Unsigned) instruction /// Performs unsigned × unsigned multiplication and returns the upper 64 bits of the result pub fn mulhu(&mut self, rd: Register, rs1: Register, rs2: Register) -> &mut Self { - let instr = self.encode_r_type_tracked(opcodes::OP, rd, m_funct3::MULHU, rs1, rs2, m_funct7::M_EXT); + let instr = + self.encode_r_type_tracked(opcodes::OP, rd, m_funct3::MULHU, rs1, rs2, m_funct7::M_EXT); self.push(instr); self } @@ -481,7 +570,8 @@ impl Riscv64InstructionBuilder { /// Generate DIV (Divide) instruction /// Performs signed division: rs1 ÷ rs2 pub fn div(&mut self, rd: Register, rs1: Register, rs2: Register) -> &mut Self { - let instr = self.encode_r_type_tracked(opcodes::OP, rd, m_funct3::DIV, rs1, rs2, m_funct7::M_EXT); + let instr = + self.encode_r_type_tracked(opcodes::OP, rd, m_funct3::DIV, rs1, rs2, m_funct7::M_EXT); self.push(instr); self } @@ -489,7 +579,8 @@ impl Riscv64InstructionBuilder { /// Generate DIVU (Divide Unsigned) instruction /// Performs unsigned division: rs1 ÷ rs2 pub fn divu(&mut self, rd: Register, rs1: Register, rs2: Register) -> &mut Self { - let instr = self.encode_r_type_tracked(opcodes::OP, rd, m_funct3::DIVU, rs1, rs2, m_funct7::M_EXT); + let instr = + self.encode_r_type_tracked(opcodes::OP, rd, m_funct3::DIVU, rs1, rs2, m_funct7::M_EXT); self.push(instr); self } @@ -497,7 +588,8 @@ impl Riscv64InstructionBuilder { /// Generate REM (Remainder) instruction /// Computes signed remainder: rs1 % rs2 pub fn rem(&mut self, rd: Register, rs1: Register, rs2: Register) -> &mut Self { - let instr = self.encode_r_type_tracked(opcodes::OP, rd, m_funct3::REM, rs1, rs2, m_funct7::M_EXT); + let instr = + self.encode_r_type_tracked(opcodes::OP, rd, m_funct3::REM, rs1, rs2, m_funct7::M_EXT); self.push(instr); self } @@ -505,7 +597,8 @@ impl Riscv64InstructionBuilder { /// Generate REMU (Remainder Unsigned) instruction /// Computes unsigned remainder: rs1 % rs2 pub fn remu(&mut self, rd: Register, rs1: Register, rs2: Register) -> &mut Self { - let instr = self.encode_r_type_tracked(opcodes::OP, rd, m_funct3::REMU, rs1, rs2, m_funct7::M_EXT); + let instr = + self.encode_r_type_tracked(opcodes::OP, rd, m_funct3::REMU, rs1, rs2, m_funct7::M_EXT); self.push(instr); self } @@ -690,7 +783,7 @@ impl Riscv64InstructionBuilder { pub fn sret(&mut self) -> &mut Self { let instr = super::instruction::encode_privileged_type( super::instruction::opcodes::SYSTEM, - super::instruction::privileged_funct12::SRET + super::instruction::privileged_funct12::SRET, ); self.push(instr); self @@ -702,7 +795,7 @@ impl Riscv64InstructionBuilder { pub fn mret(&mut self) -> &mut Self { let instr = super::instruction::encode_privileged_type( super::instruction::opcodes::SYSTEM, - super::instruction::privileged_funct12::MRET + super::instruction::privileged_funct12::MRET, ); self.push(instr); self @@ -713,7 +806,7 @@ impl Riscv64InstructionBuilder { pub fn ecall(&mut self) -> &mut Self { let instr = super::instruction::encode_privileged_type( super::instruction::opcodes::SYSTEM, - super::instruction::privileged_funct12::ECALL + super::instruction::privileged_funct12::ECALL, ); self.push(instr); self @@ -724,7 +817,7 @@ impl Riscv64InstructionBuilder { pub fn ebreak(&mut self) -> &mut Self { let instr = super::instruction::encode_privileged_type( super::instruction::opcodes::SYSTEM, - super::instruction::privileged_funct12::EBREAK + super::instruction::privileged_funct12::EBREAK, ); self.push(instr); self @@ -736,7 +829,7 @@ impl Riscv64InstructionBuilder { pub fn wfi(&mut self) -> &mut Self { let instr = super::instruction::encode_privileged_type( super::instruction::opcodes::SYSTEM, - super::instruction::privileged_funct12::WFI + super::instruction::privileged_funct12::WFI, ); self.push(instr); self @@ -747,4 +840,4 @@ impl Riscv64InstructionBuilder { pub fn ret(&mut self) -> &mut Self { self.jalr(super::reg::X0, super::reg::X1, 0) } -} \ No newline at end of file +} diff --git a/src/riscv64/instruction.rs b/src/riscv64/instruction.rs index 0363583..fe6258b 100644 --- a/src/riscv64/instruction.rs +++ b/src/riscv64/instruction.rs @@ -1,14 +1,11 @@ +use crate::common::{Instruction as InstructionTrait, Register as RegisterTrait}; /// RISC-V instruction formats and encoding use core::fmt; -use crate::common::{ - Instruction as InstructionTrait, - Register as RegisterTrait, -}; -#[cfg(feature = "std")] -use std::vec::Vec; #[cfg(not(feature = "std"))] use alloc::vec::Vec; +#[cfg(feature = "std")] +use std::vec::Vec; /// RISC-V register representation #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -28,24 +25,24 @@ impl RegisterTrait for Register { fn id(&self) -> u32 { self.0 as u32 } - + fn abi_class(&self) -> crate::common::AbiClass { use crate::common::AbiClass; - + match self.0 { // Caller-saved registers (do not need to be preserved across calls) - 1 => AbiClass::CallerSaved, // RA (return address) - caller-saved - 5..=7 | 28..=31 => AbiClass::CallerSaved, // T0-T2, T3-T6 (temporaries) - 10..=17 => AbiClass::CallerSaved, // A0-A7 (arguments/return values) - + 1 => AbiClass::CallerSaved, // RA (return address) - caller-saved + 5..=7 | 28..=31 => AbiClass::CallerSaved, // T0-T2, T3-T6 (temporaries) + 10..=17 => AbiClass::CallerSaved, // A0-A7 (arguments/return values) + // Callee-saved registers (must be preserved across calls) - 2 => AbiClass::CalleeSaved, // SP (stack pointer) - callee-saved - 8..=9 | 18..=27 => AbiClass::CalleeSaved, // S0-S1, S2-S11 (saved registers) - + 2 => AbiClass::CalleeSaved, // SP (stack pointer) - callee-saved + 8..=9 | 18..=27 => AbiClass::CalleeSaved, // S0-S1, S2-S11 (saved registers) + // Special-purpose registers - 0 => AbiClass::Special, // X0 (zero register) - hardwired to zero - 3 => AbiClass::Special, // GP (global pointer) - special purpose - 4 => AbiClass::Special, // TP (thread pointer) - special purpose + 0 => AbiClass::Special, // X0 (zero register) - hardwired to zero + 3 => AbiClass::Special, // GP (global pointer) - special purpose + 4 => AbiClass::Special, // TP (thread pointer) - special purpose // Default to Special for any unhandled registers _ => AbiClass::Special, @@ -81,7 +78,7 @@ impl Instruction { pub fn new(value: u32) -> Self { Self::Standard(value) } - + /// Create a new 16-bit compressed instruction pub fn new_compressed(value: u16) -> Self { Self::Compressed(value) @@ -102,7 +99,7 @@ impl Instruction { Self::Compressed(val) => val.to_le_bytes().to_vec(), } } - + /// Get the size of this instruction in bytes pub fn size(&self) -> usize { match self { @@ -152,21 +149,45 @@ impl fmt::Display for Instruction { /// I-type instruction encoding pub fn encode_i_type(opcode: u8, rd: Register, funct3: u8, rs1: Register, imm: i16) -> Instruction { let imm = imm as u32 & 0xfff; - let instr = (imm << 20) | ((rs1.value() as u32) << 15) | ((funct3 as u32) << 12) | ((rd.value() as u32) << 7) | (opcode as u32); + let instr = (imm << 20) + | ((rs1.value() as u32) << 15) + | ((funct3 as u32) << 12) + | ((rd.value() as u32) << 7) + | (opcode as u32); Instruction::Standard(instr) } /// CSR instruction encoding (I-type variant) -pub fn encode_csr_type(opcode: u8, rd: Register, funct3: u8, rs1: Register, csr: Csr) -> Instruction { +pub fn encode_csr_type( + opcode: u8, + rd: Register, + funct3: u8, + rs1: Register, + csr: Csr, +) -> Instruction { let csr_val = csr.value() as u32; - let instr = (csr_val << 20) | ((rs1.value() as u32) << 15) | ((funct3 as u32) << 12) | ((rd.value() as u32) << 7) | (opcode as u32); + let instr = (csr_val << 20) + | ((rs1.value() as u32) << 15) + | ((funct3 as u32) << 12) + | ((rd.value() as u32) << 7) + | (opcode as u32); Instruction::Standard(instr) } /// CSR immediate instruction encoding -pub fn encode_csr_imm_type(opcode: u8, rd: Register, funct3: u8, uimm: u8, csr: Csr) -> Instruction { +pub fn encode_csr_imm_type( + opcode: u8, + rd: Register, + funct3: u8, + uimm: u8, + csr: Csr, +) -> Instruction { let csr_val = csr.value() as u32; - let instr = (csr_val << 20) | ((uimm as u32) << 15) | ((funct3 as u32) << 12) | ((rd.value() as u32) << 7) | (opcode as u32); + let instr = (csr_val << 20) + | ((uimm as u32) << 15) + | ((funct3 as u32) << 12) + | ((rd.value() as u32) << 7) + | (opcode as u32); Instruction::Standard(instr) } @@ -177,28 +198,64 @@ pub fn encode_privileged_type(opcode: u8, funct12: u16) -> Instruction { } /// R-type instruction encoding -pub fn encode_r_type(opcode: u8, rd: Register, funct3: u8, rs1: Register, rs2: Register, funct7: u8) -> Instruction { - let instr = ((funct7 as u32) << 25) | ((rs2.value() as u32) << 20) | ((rs1.value() as u32) << 15) | ((funct3 as u32) << 12) | ((rd.value() as u32) << 7) | (opcode as u32); +pub fn encode_r_type( + opcode: u8, + rd: Register, + funct3: u8, + rs1: Register, + rs2: Register, + funct7: u8, +) -> Instruction { + let instr = ((funct7 as u32) << 25) + | ((rs2.value() as u32) << 20) + | ((rs1.value() as u32) << 15) + | ((funct3 as u32) << 12) + | ((rd.value() as u32) << 7) + | (opcode as u32); Instruction::Standard(instr) } /// S-type instruction encoding (Store) -pub fn encode_s_type(opcode: u8, funct3: u8, rs1: Register, rs2: Register, imm: i16) -> Instruction { +pub fn encode_s_type( + opcode: u8, + funct3: u8, + rs1: Register, + rs2: Register, + imm: i16, +) -> Instruction { let imm = imm as u32 & 0xfff; let imm_11_5 = (imm >> 5) & 0x7f; let imm_4_0 = imm & 0x1f; - let instr = (imm_11_5 << 25) | ((rs2.value() as u32) << 20) | ((rs1.value() as u32) << 15) | ((funct3 as u32) << 12) | (imm_4_0 << 7) | (opcode as u32); + let instr = (imm_11_5 << 25) + | ((rs2.value() as u32) << 20) + | ((rs1.value() as u32) << 15) + | ((funct3 as u32) << 12) + | (imm_4_0 << 7) + | (opcode as u32); Instruction::Standard(instr) } /// B-type instruction encoding (Branch) -pub fn encode_b_type(opcode: u8, funct3: u8, rs1: Register, rs2: Register, imm: i16) -> Instruction { +pub fn encode_b_type( + opcode: u8, + funct3: u8, + rs1: Register, + rs2: Register, + imm: i16, +) -> Instruction { let imm = (imm as u32) & 0x1ffe; // 13-bit signed immediate let imm_12 = (imm >> 12) & 0x1; let imm_10_5 = (imm >> 5) & 0x3f; let imm_4_1 = (imm >> 1) & 0xf; let imm_11 = (imm >> 11) & 0x1; - let instr = (imm_12 << 31) | (imm_10_5 << 25) | ((rs2.value() as u32) << 20) | ((rs1.value() as u32) << 15) | ((funct3 as u32) << 12) | (imm_4_1 << 8) | (imm_11 << 7) | (opcode as u32); + let instr = (imm_12 << 31) + | (imm_10_5 << 25) + | ((rs2.value() as u32) << 20) + | ((rs1.value() as u32) << 15) + | ((funct3 as u32) << 12) + | (imm_4_1 << 8) + | (imm_11 << 7) + | (opcode as u32); Instruction::Standard(instr) } @@ -216,7 +273,12 @@ pub fn encode_j_type(opcode: u8, rd: Register, imm: i32) -> Instruction { let imm_10_1 = (imm >> 1) & 0x3ff; let imm_11 = (imm >> 11) & 0x1; let imm_19_12 = (imm >> 12) & 0xff; - let instr = (imm_20 << 31) | (imm_10_1 << 21) | (imm_11 << 20) | (imm_19_12 << 12) | ((rd.value() as u32) << 7) | (opcode as u32); + let instr = (imm_20 << 31) + | (imm_10_1 << 21) + | (imm_11 << 20) + | (imm_19_12 << 12) + | ((rd.value() as u32) << 7) + | (opcode as u32); Instruction::Standard(instr) } @@ -248,11 +310,11 @@ pub mod system_funct3 { /// Privileged instruction function codes (funct12 field) pub mod privileged_funct12 { - pub const ECALL: u16 = 0x000; // Environment call + pub const ECALL: u16 = 0x000; // Environment call pub const EBREAK: u16 = 0x001; // Environment break - pub const SRET: u16 = 0x102; // Supervisor return - pub const MRET: u16 = 0x302; // Machine return - pub const WFI: u16 = 0x105; // Wait for interrupt + pub const SRET: u16 = 0x102; // Supervisor return + pub const MRET: u16 = 0x302; // Machine return + pub const WFI: u16 = 0x105; // Wait for interrupt } /// Branch instruction function codes @@ -299,14 +361,14 @@ pub mod alu_funct3 { /// M Extension (Multiply/Divide) instruction function codes /// All M-extension instructions use funct7 = 0x01 and opcode = OP (0x33) pub mod m_funct3 { - pub const MUL: u8 = 0x0; // Multiply (low 64 bits) - pub const MULH: u8 = 0x1; // Multiply high (signed × signed) - pub const MULHSU: u8 = 0x2; // Multiply high (signed × unsigned) - pub const MULHU: u8 = 0x3; // Multiply high (unsigned × unsigned) - pub const DIV: u8 = 0x4; // Divide (signed) - pub const DIVU: u8 = 0x5; // Divide (unsigned) - pub const REM: u8 = 0x6; // Remainder (signed) - pub const REMU: u8 = 0x7; // Remainder (unsigned) + pub const MUL: u8 = 0x0; // Multiply (low 64 bits) + pub const MULH: u8 = 0x1; // Multiply high (signed × signed) + pub const MULHSU: u8 = 0x2; // Multiply high (signed × unsigned) + pub const MULHU: u8 = 0x3; // Multiply high (unsigned × unsigned) + pub const DIV: u8 = 0x4; // Divide (signed) + pub const DIVU: u8 = 0x5; // Divide (unsigned) + pub const REM: u8 = 0x6; // Remainder (signed) + pub const REMU: u8 = 0x7; // Remainder (unsigned) } /// M Extension funct7 code @@ -317,14 +379,14 @@ pub mod m_funct7 { /// Common CSR addresses pub mod csr { use super::Csr; - + // Machine Information Registers pub const MVENDORID: Csr = Csr::new(0xf11); pub const MARCHID: Csr = Csr::new(0xf12); pub const MIMPID: Csr = Csr::new(0xf13); pub const MHARTID: Csr = Csr::new(0xf14); pub const MCONFIGPTR: Csr = Csr::new(0xf15); - + // Machine Trap Setup pub const MSTATUS: Csr = Csr::new(0x300); pub const MISA: Csr = Csr::new(0x301); @@ -334,7 +396,7 @@ pub mod csr { pub const MTVEC: Csr = Csr::new(0x305); pub const MCOUNTEREN: Csr = Csr::new(0x306); pub const MSTATUSH: Csr = Csr::new(0x310); - + // Machine Trap Handling pub const MSCRATCH: Csr = Csr::new(0x340); pub const MEPC: Csr = Csr::new(0x341); @@ -343,13 +405,13 @@ pub mod csr { pub const MIP: Csr = Csr::new(0x344); pub const MTINST: Csr = Csr::new(0x34a); pub const MTVAL2: Csr = Csr::new(0x34b); - + // Machine Configuration pub const MENVCFG: Csr = Csr::new(0x30a); pub const MENVCFGH: Csr = Csr::new(0x31a); pub const MSECCFG: Csr = Csr::new(0x747); pub const MSECCFGH: Csr = Csr::new(0x757); - + // Machine Memory Protection - Configuration pub const PMPCFG0: Csr = Csr::new(0x3a0); pub const PMPCFG1: Csr = Csr::new(0x3a1); @@ -367,7 +429,7 @@ pub mod csr { pub const PMPCFG13: Csr = Csr::new(0x3ad); pub const PMPCFG14: Csr = Csr::new(0x3ae); pub const PMPCFG15: Csr = Csr::new(0x3af); - + // Machine Memory Protection - Address pub const PMPADDR0: Csr = Csr::new(0x3b0); pub const PMPADDR1: Csr = Csr::new(0x3b1); @@ -433,7 +495,7 @@ pub mod csr { pub const PMPADDR61: Csr = Csr::new(0x3ed); pub const PMPADDR62: Csr = Csr::new(0x3ee); pub const PMPADDR63: Csr = Csr::new(0x3ef); - + // Machine Counter/Timers pub const MCYCLE: Csr = Csr::new(0xb00); pub const MINSTRET: Csr = Csr::new(0xb02); @@ -466,7 +528,7 @@ pub mod csr { pub const MHPMCOUNTER29: Csr = Csr::new(0xb1d); pub const MHPMCOUNTER30: Csr = Csr::new(0xb1e); pub const MHPMCOUNTER31: Csr = Csr::new(0xb1f); - + // Machine Counter/Timers - High (for RV32) pub const MCYCLEH: Csr = Csr::new(0xb80); pub const MINSTRETH: Csr = Csr::new(0xb82); @@ -499,7 +561,7 @@ pub mod csr { pub const MHPMCOUNTER29H: Csr = Csr::new(0xb9d); pub const MHPMCOUNTER30H: Csr = Csr::new(0xb9e); pub const MHPMCOUNTER31H: Csr = Csr::new(0xb9f); - + // Machine Counter Setup pub const MCOUNTINHIBIT: Csr = Csr::new(0x320); pub const MHPMEVENT3: Csr = Csr::new(0x323); @@ -531,7 +593,7 @@ pub mod csr { pub const MHPMEVENT29: Csr = Csr::new(0x33d); pub const MHPMEVENT30: Csr = Csr::new(0x33e); pub const MHPMEVENT31: Csr = Csr::new(0x33f); - + // Supervisor-mode CSRs pub const SSTATUS: Csr = Csr::new(0x100); pub const SIE: Csr = Csr::new(0x104); @@ -546,7 +608,7 @@ pub mod csr { /// Common registers pub mod reg { use super::Register; - + // Standard register names (x0-x31) pub const X0: Register = Register::new(0); pub const X1: Register = Register::new(1); @@ -582,37 +644,37 @@ pub mod reg { pub const X31: Register = Register::new(31); // RISC-V ABI register aliases - pub const ZERO: Register = X0; // Hard-wired zero - pub const RA: Register = X1; // Return address - pub const SP: Register = X2; // Stack pointer - pub const GP: Register = X3; // Global pointer - pub const TP: Register = X4; // Thread pointer - pub const T0: Register = X5; // Temporary register 0 - pub const T1: Register = X6; // Temporary register 1 - pub const T2: Register = X7; // Temporary register 2 - pub const S0: Register = X8; // Saved register 0 / Frame pointer - pub const FP: Register = X8; // Frame pointer (alias for s0) - pub const S1: Register = X9; // Saved register 1 - pub const A0: Register = X10; // Function argument 0 / Return value 0 - pub const A1: Register = X11; // Function argument 1 / Return value 1 - pub const A2: Register = X12; // Function argument 2 - pub const A3: Register = X13; // Function argument 3 - pub const A4: Register = X14; // Function argument 4 - pub const A5: Register = X15; // Function argument 5 - pub const A6: Register = X16; // Function argument 6 - pub const A7: Register = X17; // Function argument 7 - pub const S2: Register = X18; // Saved register 2 - pub const S3: Register = X19; // Saved register 3 - pub const S4: Register = X20; // Saved register 4 - pub const S5: Register = X21; // Saved register 5 - pub const S6: Register = X22; // Saved register 6 - pub const S7: Register = X23; // Saved register 7 - pub const S8: Register = X24; // Saved register 8 - pub const S9: Register = X25; // Saved register 9 - pub const S10: Register = X26; // Saved register 10 - pub const S11: Register = X27; // Saved register 11 - pub const T3: Register = X28; // Temporary register 3 - pub const T4: Register = X29; // Temporary register 4 - pub const T5: Register = X30; // Temporary register 5 - pub const T6: Register = X31; // Temporary register 6 -} \ No newline at end of file + pub const ZERO: Register = X0; // Hard-wired zero + pub const RA: Register = X1; // Return address + pub const SP: Register = X2; // Stack pointer + pub const GP: Register = X3; // Global pointer + pub const TP: Register = X4; // Thread pointer + pub const T0: Register = X5; // Temporary register 0 + pub const T1: Register = X6; // Temporary register 1 + pub const T2: Register = X7; // Temporary register 2 + pub const S0: Register = X8; // Saved register 0 / Frame pointer + pub const FP: Register = X8; // Frame pointer (alias for s0) + pub const S1: Register = X9; // Saved register 1 + pub const A0: Register = X10; // Function argument 0 / Return value 0 + pub const A1: Register = X11; // Function argument 1 / Return value 1 + pub const A2: Register = X12; // Function argument 2 + pub const A3: Register = X13; // Function argument 3 + pub const A4: Register = X14; // Function argument 4 + pub const A5: Register = X15; // Function argument 5 + pub const A6: Register = X16; // Function argument 6 + pub const A7: Register = X17; // Function argument 7 + pub const S2: Register = X18; // Saved register 2 + pub const S3: Register = X19; // Saved register 3 + pub const S4: Register = X20; // Saved register 4 + pub const S5: Register = X21; // Saved register 5 + pub const S6: Register = X22; // Saved register 6 + pub const S7: Register = X23; // Saved register 7 + pub const S8: Register = X24; // Saved register 8 + pub const S9: Register = X25; // Saved register 9 + pub const S10: Register = X26; // Saved register 10 + pub const S11: Register = X27; // Saved register 11 + pub const T3: Register = X28; // Temporary register 3 + pub const T4: Register = X29; // Temporary register 4 + pub const T5: Register = X30; // Temporary register 5 + pub const T6: Register = X31; // Temporary register 6 +} diff --git a/src/riscv64/macros.rs b/src/riscv64/macros.rs index 7bda485..f841de5 100644 --- a/src/riscv64/macros.rs +++ b/src/riscv64/macros.rs @@ -27,4 +27,4 @@ macro_rules! riscv64_asm { } // The complex jit_asm_chain! macro has been removed. -// Now we use direct method calls on InstructionBuilder for better IDE support and maintainability. \ No newline at end of file +// Now we use direct method calls on InstructionBuilder for better IDE support and maintainability. diff --git a/src/riscv64/mod.rs b/src/riscv64/mod.rs index c4e278e..1343578 100644 --- a/src/riscv64/mod.rs +++ b/src/riscv64/mod.rs @@ -3,15 +3,13 @@ //! This module provides RISC-V specific instruction encoding and a macro-based //! DSL for generating RISC-V machine code at runtime. - - -pub mod instruction; pub mod builder; +pub mod instruction; pub mod macros; #[cfg(test)] mod tests; // Re-export commonly used items -pub use instruction::{Register, Csr, Instruction, reg, csr}; -pub use builder::Riscv64InstructionBuilder; \ No newline at end of file +pub use builder::Riscv64InstructionBuilder; +pub use instruction::{csr, reg, Csr, Instruction, Register}; diff --git a/src/riscv64/tests.rs b/src/riscv64/tests.rs index 81dad8b..18ca4c7 100644 --- a/src/riscv64/tests.rs +++ b/src/riscv64/tests.rs @@ -1,22 +1,22 @@ -use crate::riscv64::{reg, csr, Instruction, Riscv64InstructionBuilder}; +use crate::riscv64::{csr, reg, Instruction, Riscv64InstructionBuilder}; -#[cfg(feature = "std")] -use std::vec; #[cfg(not(feature = "std"))] use alloc::vec; +#[cfg(feature = "std")] +use std::vec; #[cfg(feature = "std")] -use std::process::Command; +use crate::common::InstructionBuilder; #[cfg(feature = "std")] use std::fs; #[cfg(feature = "std")] -use crate::common::InstructionBuilder; +use std::process::Command; /// Helper function to assemble RISC-V assembly and extract binary data #[cfg(feature = "std")] fn assemble_riscv(assembly: &str) -> Vec { use std::io::Write; - + // Use tempfile crate for better temporary file management let mut asm_file = tempfile::NamedTempFile::new().expect("Failed to create temp assembly file"); writeln!(asm_file, ".section .text").expect("Failed to write section directive"); @@ -24,9 +24,9 @@ fn assemble_riscv(assembly: &str) -> Vec { writeln!(asm_file, "_start:").expect("Failed to write label"); write!(asm_file, "{}", assembly).expect("Failed to write assembly"); asm_file.flush().expect("Failed to flush assembly file"); - + let obj_file = tempfile::NamedTempFile::new().expect("Failed to create temp object file"); - + // Assemble the file let output = Command::new("riscv64-linux-gnu-as") .args(&["-march=rv64im"]) @@ -34,7 +34,7 @@ fn assemble_riscv(assembly: &str) -> Vec { .arg("-o") .arg(obj_file.path()) .output(); - + match output { Ok(result) => { if !result.status.success() { @@ -50,7 +50,7 @@ fn assemble_riscv(assembly: &str) -> Vec { return vec![]; } } - + // Check if object file exists and is not empty if let Ok(metadata) = fs::metadata(obj_file.path()) { if metadata.len() == 0 { @@ -58,9 +58,9 @@ fn assemble_riscv(assembly: &str) -> Vec { return vec![]; } } - + let bin_file = tempfile::NamedTempFile::new().expect("Failed to create temp binary file"); - + // Extract the binary data using objcopy let objcopy_result = Command::new("riscv64-linux-gnu-objcopy") .arg("-O") @@ -70,18 +70,23 @@ fn assemble_riscv(assembly: &str) -> Vec { .arg(bin_file.path()) .output() .expect("Failed to run objcopy"); - + if !objcopy_result.status.success() { - println!("Warning: objcopy failed, skipping comparison test: {}", - String::from_utf8_lossy(&objcopy_result.stderr)); + println!( + "Warning: objcopy failed, skipping comparison test: {}", + String::from_utf8_lossy(&objcopy_result.stderr) + ); return vec![]; } - + // Read the binary data match fs::read(bin_file.path()) { Ok(data) => data, Err(e) => { - println!("Warning: Failed to read binary file, skipping comparison test: {}", e); + println!( + "Warning: Failed to read binary file, skipping comparison test: {}", + e + ); vec![] } } @@ -92,18 +97,21 @@ fn assemble_riscv(assembly: &str) -> Vec { fn compare_instruction(jit_instr: Instruction, gnu_assembly: &str) { let jit_bytes = jit_instr.bytes(); let gnu_bytes = assemble_riscv(gnu_assembly); - + // Skip comparison if GNU assembler is not available or returned empty if gnu_bytes.is_empty() { return; } - + // The GNU assembler output might have extra padding, so we only compare the first 4 bytes assert_eq!(jit_bytes.len(), 4, "JIT instruction should be 4 bytes"); - assert!(gnu_bytes.len() >= 4, "GNU assembler output should be at least 4 bytes"); - + assert!( + gnu_bytes.len() >= 4, + "GNU assembler output should be at least 4 bytes" + ); + assert_eq!( - jit_bytes, + jit_bytes, &gnu_bytes[0..4], "JIT assembler output does not match GNU assembler output\nJIT: {:02x?}\nGNU: {:02x?}\nAssembly: {}", jit_bytes, @@ -115,12 +123,12 @@ fn compare_instruction(jit_instr: Instruction, gnu_assembly: &str) { #[test] fn test_csr_instructions() { let mut builder = Riscv64InstructionBuilder::new(); - + // Test CSR instructions builder.csrrw(reg::X1, csr::MSTATUS, reg::X2); builder.csrrs(reg::X3, csr::MEPC, reg::X4); builder.csrrwi(reg::X5, csr::MTVEC, 0x10); - + // Test CSR pseudo-instructions builder.csrr(reg::X6, csr::MHARTID); builder.csrw(csr::MEPC, reg::X7); @@ -130,9 +138,9 @@ fn test_csr_instructions() { builder.csrsi(csr::MIE, 0x08); builder.csrci(csr::MIP, 0x04); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); assert_eq!(instructions.len(), 10); - + // Verify first instruction (csrrw x1, mstatus, x2) // The actual encoding value is 805376243 (0x30021073 in hex) // Let's just check that we get a reasonable value for now @@ -142,32 +150,32 @@ fn test_csr_instructions() { #[test] fn test_comprehensive_csr_support() { let mut builder = Riscv64InstructionBuilder::new(); - + // Test all M-mode CSRs - builder.csrr(reg::X1, csr::MSTATUS); // 0x300 - builder.csrr(reg::X2, csr::MISA); // 0x301 - builder.csrr(reg::X3, csr::MIE); // 0x304 - builder.csrr(reg::X4, csr::MTVEC); // 0x305 - builder.csrr(reg::X5, csr::MSCRATCH); // 0x340 - builder.csrr(reg::X6, csr::MEPC); // 0x341 - builder.csrr(reg::X7, csr::MCAUSE); // 0x342 - builder.csrr(reg::X8, csr::MTVAL); // 0x343 - builder.csrr(reg::X9, csr::MIP); // 0x344 - builder.csrr(reg::X10, csr::MHARTID); // 0xf14 - + builder.csrr(reg::X1, csr::MSTATUS); // 0x300 + builder.csrr(reg::X2, csr::MISA); // 0x301 + builder.csrr(reg::X3, csr::MIE); // 0x304 + builder.csrr(reg::X4, csr::MTVEC); // 0x305 + builder.csrr(reg::X5, csr::MSCRATCH); // 0x340 + builder.csrr(reg::X6, csr::MEPC); // 0x341 + builder.csrr(reg::X7, csr::MCAUSE); // 0x342 + builder.csrr(reg::X8, csr::MTVAL); // 0x343 + builder.csrr(reg::X9, csr::MIP); // 0x344 + builder.csrr(reg::X10, csr::MHARTID); // 0xf14 + // Test all S-mode CSRs - builder.csrr(reg::X11, csr::SSTATUS); // 0x100 - builder.csrr(reg::X12, csr::SIE); // 0x104 - builder.csrr(reg::X13, csr::STVEC); // 0x105 - builder.csrr(reg::X14, csr::SSCRATCH); // 0x140 - builder.csrr(reg::X15, csr::SEPC); // 0x141 - builder.csrr(reg::X16, csr::SCAUSE); // 0x142 - builder.csrr(reg::X17, csr::STVAL); // 0x143 - builder.csrr(reg::X18, csr::SIP); // 0x144 - - let instructions = builder.instructions(); + builder.csrr(reg::X11, csr::SSTATUS); // 0x100 + builder.csrr(reg::X12, csr::SIE); // 0x104 + builder.csrr(reg::X13, csr::STVEC); // 0x105 + builder.csrr(reg::X14, csr::SSCRATCH); // 0x140 + builder.csrr(reg::X15, csr::SEPC); // 0x141 + builder.csrr(reg::X16, csr::SCAUSE); // 0x142 + builder.csrr(reg::X17, csr::STVAL); // 0x143 + builder.csrr(reg::X18, csr::SIP); // 0x144 + + let instructions = builder.instructions().unwrap(); assert_eq!(instructions.len(), 18); // 10 M-mode + 8 S-mode - + // Verify all instructions are non-zero (properly encoded) for (i, instr) in instructions.iter().enumerate() { assert!(instr.value() != 0, "Instruction {} should be non-zero", i); @@ -177,7 +185,7 @@ fn test_comprehensive_csr_support() { #[test] fn test_csr_write_operations() { let mut builder = Riscv64InstructionBuilder::new(); - + // Test write operations on both M-mode and S-mode CSRs builder.csrrw(reg::X1, csr::MSTATUS, reg::X2); builder.csrrw(reg::X3, csr::SSTATUS, reg::X4); @@ -185,7 +193,7 @@ fn test_csr_write_operations() { builder.csrrs(reg::X7, csr::SIE, reg::X8); builder.csrrc(reg::X9, csr::MTVEC, reg::X10); builder.csrrc(reg::X11, csr::STVEC, reg::X12); - + // Test immediate variants builder.csrrwi(reg::X13, csr::MSCRATCH, 0x1f); builder.csrrwi(reg::X14, csr::SSCRATCH, 0x0f); @@ -194,9 +202,9 @@ fn test_csr_write_operations() { builder.csrrci(reg::X17, csr::MCAUSE, 0x04); builder.csrrci(reg::X18, csr::SCAUSE, 0x02); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); assert_eq!(instructions.len(), 12); - + // Verify all instructions are properly encoded for instr in instructions { assert!(instr.value() != 0); @@ -216,7 +224,7 @@ fn test_csr_addresses() { assert_eq!(csr::MTVAL.value(), 0x343); assert_eq!(csr::MIP.value(), 0x344); assert_eq!(csr::MHARTID.value(), 0xf14); - + // Verify S-mode CSR addresses match RISC-V specification assert_eq!(csr::SSTATUS.value(), 0x100); assert_eq!(csr::SIE.value(), 0x104); @@ -237,13 +245,13 @@ fn test_csr_with_macro() { csrr(reg::T1, csr::MISA); // Read machine ISA csrrw(reg::T2, csr::MTVEC, reg::T0); // Write machine trap vector csrrs(reg::T3, csr::MIE, reg::ZERO); // Read machine interrupt enable - - // S-mode CSR operations + + // S-mode CSR operations csrr(reg::T4, csr::SSTATUS); // Read supervisor status csrr(reg::T5, csr::STVEC); // Read supervisor trap vector csrrw(reg::T6, csr::SSCRATCH, reg::T4); // Write supervisor scratch csrrs(reg::A0, csr::SIE, reg::ZERO); // Read supervisor interrupt enable - + // Immediate variants csrrwi(reg::A1, csr::SEPC, 0x10); // Write supervisor exception PC csrrsi(reg::A2, csr::SCAUSE, 0x08); // Set supervisor cause bits @@ -251,7 +259,7 @@ fn test_csr_with_macro() { }; assert_eq!(instructions.len(), 11); - + // Verify all instructions are properly encoded for (i, instr) in instructions.iter().enumerate() { assert!(instr.value() != 0, "Instruction {} should be non-zero", i); @@ -261,23 +269,23 @@ fn test_csr_with_macro() { #[test] fn test_csr_encoding_verification() { let mut builder = Riscv64InstructionBuilder::new(); - + // Test specific CSR encodings - builder.csrr(reg::X1, csr::SSTATUS); // Should encode 0x100 CSR address - builder.csrr(reg::X2, csr::MSTATUS); // Should encode 0x300 CSR address - builder.csrr(reg::X3, csr::SEPC); // Should encode 0x141 CSR address - builder.csrr(reg::X4, csr::MHARTID); // Should encode 0xf14 CSR address - - let instructions = builder.instructions(); + builder.csrr(reg::X1, csr::SSTATUS); // Should encode 0x100 CSR address + builder.csrr(reg::X2, csr::MSTATUS); // Should encode 0x300 CSR address + builder.csrr(reg::X3, csr::SEPC); // Should encode 0x141 CSR address + builder.csrr(reg::X4, csr::MHARTID); // Should encode 0xf14 CSR address + + let instructions = builder.instructions().unwrap(); assert_eq!(instructions.len(), 4); - + // Extract and verify CSR addresses from instruction encoding // CSR address is in bits [31:20] of the instruction let sstatus_csr = (instructions[0].value() >> 20) & 0xfff; let mstatus_csr = (instructions[1].value() >> 20) & 0xfff; let sepc_csr = (instructions[2].value() >> 20) & 0xfff; let mhartid_csr = (instructions[3].value() >> 20) & 0xfff; - + assert_eq!(sstatus_csr, 0x100, "SSTATUS CSR address should be 0x100"); assert_eq!(mstatus_csr, 0x300, "MSTATUS CSR address should be 0x300"); assert_eq!(sepc_csr, 0x141, "SEPC CSR address should be 0x141"); @@ -288,7 +296,7 @@ fn test_csr_encoding_verification() { fn test_issue_requested_csrs() { // Test all CSRs specifically mentioned in the GitHub issue let mut builder = Riscv64InstructionBuilder::new(); - + // M-mode CSRs from issue: mscratch, mhartid, misa, mstatus, mie, mip, mtvec, mcause, mepc, mtval builder.csrr(reg::X1, csr::MSCRATCH); builder.csrr(reg::X2, csr::MHARTID); @@ -300,7 +308,7 @@ fn test_issue_requested_csrs() { builder.csrr(reg::X8, csr::MCAUSE); builder.csrr(reg::X9, csr::MEPC); builder.csrr(reg::X10, csr::MTVAL); - + // S-mode CSRs from issue: sscratch, sstatus, sie, sip, stvec, scause, sepc, stval builder.csrr(reg::X11, csr::SSCRATCH); builder.csrr(reg::X12, csr::SSTATUS); @@ -310,25 +318,29 @@ fn test_issue_requested_csrs() { builder.csrr(reg::X16, csr::SCAUSE); builder.csrr(reg::X17, csr::SEPC); builder.csrr(reg::X18, csr::STVAL); - - let instructions = builder.instructions(); - assert_eq!(instructions.len(), 18, "Should have 18 CSR instructions (10 M-mode + 8 S-mode)"); - + + let instructions = builder.instructions().unwrap(); + assert_eq!( + instructions.len(), + 18, + "Should have 18 CSR instructions (10 M-mode + 8 S-mode)" + ); + // Verify all instructions are properly encoded for (i, instr) in instructions.iter().enumerate() { assert!(instr.value() != 0, "Instruction {} should be non-zero", i); } - + // Test that we can also perform write operations on these CSRs let mut builder2 = Riscv64InstructionBuilder::new(); builder2.csrrw(reg::A0, csr::MSCRATCH, reg::A1); builder2.csrrw(reg::A2, csr::SSCRATCH, reg::A3); builder2.csrrsi(reg::A4, csr::MSTATUS, 0x08); builder2.csrrci(reg::A5, csr::SSTATUS, 0x02); - - let write_instructions = builder2.instructions(); + + let write_instructions = builder2.instructions().unwrap(); assert_eq!(write_instructions.len(), 4); - + for instr in write_instructions { assert!(instr.value() != 0, "Write instruction should be non-zero"); } @@ -337,7 +349,7 @@ fn test_issue_requested_csrs() { #[test] fn test_arithmetic_instructions() { let mut builder = Riscv64InstructionBuilder::new(); - + builder.addi(reg::X1, reg::X2, 100); builder.add(reg::X3, reg::X1, reg::X2); builder.sub(reg::X4, reg::X3, reg::X1); @@ -352,31 +364,31 @@ fn test_arithmetic_instructions() { builder.slt(reg::X13, reg::X1, reg::X2); // Test slt instruction builder.sltu(reg::X14, reg::X1, reg::X2); // Test sltu instruction - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); assert_eq!(instructions.len(), 13); } #[test] fn test_m_extension_instructions() { let mut builder = Riscv64InstructionBuilder::new(); - + // Test multiplication instructions - builder.mul(reg::X1, reg::X2, reg::X3); // MUL - builder.mulh(reg::X4, reg::X5, reg::X6); // MULH - builder.mulhsu(reg::X7, reg::X8, reg::X9); // MULHSU - builder.mulhu(reg::X10, reg::X11, reg::X12); // MULHU - + builder.mul(reg::X1, reg::X2, reg::X3); // MUL + builder.mulh(reg::X4, reg::X5, reg::X6); // MULH + builder.mulhsu(reg::X7, reg::X8, reg::X9); // MULHSU + builder.mulhu(reg::X10, reg::X11, reg::X12); // MULHU + // Test division instructions - builder.div(reg::X13, reg::X14, reg::X15); // DIV - builder.divu(reg::X16, reg::X17, reg::X18); // DIVU - + builder.div(reg::X13, reg::X14, reg::X15); // DIV + builder.divu(reg::X16, reg::X17, reg::X18); // DIVU + // Test remainder instructions - builder.rem(reg::X19, reg::X20, reg::X21); // REM - builder.remu(reg::X22, reg::X23, reg::X24); // REMU + builder.rem(reg::X19, reg::X20, reg::X21); // REM + builder.remu(reg::X22, reg::X23, reg::X24); // REMU - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); assert_eq!(instructions.len(), 8); - + // Test that all instructions are properly encoded for instr in instructions.iter() { assert_eq!(instr.size(), 4); // All M extension instructions are 32-bit @@ -385,28 +397,28 @@ fn test_m_extension_instructions() { } #[cfg(feature = "std")] -#[test] +#[test] fn test_m_extension_binary_correctness() { let mut builder = Riscv64InstructionBuilder::new(); - + // Test MUL instruction: mul x1, x2, x3 builder.mul(reg::X1, reg::X2, reg::X3); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); let bytes = instructions.to_bytes(); - + // Expected encoding for MUL x1, x2, x3: // opcode=0x33 (OP), rd=1, funct3=0x0 (MUL), rs1=2, rs2=3, funct7=0x01 (M_EXT) // 31-25: funct7=0x01, 24-20: rs2=3, 19-15: rs1=2, 14-12: funct3=0x0, 11-7: rd=1, 6-0: opcode=0x33 // 0000001 00011 00010 000 00001 0110011 = 0x023100B3 let expected_mul = vec![0xB3, 0x00, 0x31, 0x02]; // little-endian assert_eq!(bytes, expected_mul); - - // Test DIV instruction: div x4, x5, x6 + + // Test DIV instruction: div x4, x5, x6 let mut builder2 = Riscv64InstructionBuilder::new(); builder2.div(reg::X4, reg::X5, reg::X6); - let instructions2 = builder2.instructions(); + let instructions2 = builder2.instructions().unwrap(); let bytes2 = instructions2.to_bytes(); - + // Expected encoding for DIV x4, x5, x6: // 31-25: funct7=0x01, 24-20: rs2=6, 19-15: rs1=5, 14-12: funct3=0x4, 11-7: rd=4, 6-0: opcode=0x33 // 0000001 00110 00101 100 00100 0110011 = 0x0262C233 @@ -420,49 +432,49 @@ fn test_binary_correctness_m_extension() { // Test MUL instruction let mut builder = Riscv64InstructionBuilder::new(); builder.mul(reg::X1, reg::X2, reg::X3); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "mul x1, x2, x3\n"); - + // Test MULH instruction let mut builder = Riscv64InstructionBuilder::new(); builder.mulh(reg::X4, reg::X5, reg::X6); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "mulh x4, x5, x6\n"); - + // Test MULHSU instruction let mut builder = Riscv64InstructionBuilder::new(); builder.mulhsu(reg::X7, reg::X8, reg::X9); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "mulhsu x7, x8, x9\n"); - + // Test MULHU instruction let mut builder = Riscv64InstructionBuilder::new(); builder.mulhu(reg::X10, reg::X11, reg::X12); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "mulhu x10, x11, x12\n"); - + // Test DIV instruction let mut builder = Riscv64InstructionBuilder::new(); builder.div(reg::X13, reg::X14, reg::X15); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "div x13, x14, x15\n"); - + // Test DIVU instruction let mut builder = Riscv64InstructionBuilder::new(); builder.divu(reg::X16, reg::X17, reg::X18); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "divu x16, x17, x18\n"); - + // Test REM instruction let mut builder = Riscv64InstructionBuilder::new(); builder.rem(reg::X19, reg::X20, reg::X21); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "rem x19, x20, x21\n"); - + // Test REMU instruction let mut builder = Riscv64InstructionBuilder::new(); builder.remu(reg::X22, reg::X23, reg::X24); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "remu x22, x23, x24\n"); } @@ -475,8 +487,8 @@ fn test_binary_correctness_multiline_m_extension() { builder.div(reg::X4, reg::X1, reg::X2); builder.rem(reg::X5, reg::X4, reg::X3); builder.mulh(reg::X6, reg::X5, reg::X4); - - let instructions = builder.instructions(); + + let instructions = builder.instructions().unwrap(); let assembly = r" mul x1, x2, x3 div x4, x1, x2 @@ -494,19 +506,19 @@ fn test_instruction_encoding() { assert_eq!(instr.value(), 0x12345678); assert_eq!(instr.size(), 4); assert!(!instr.is_compressed()); - + let bytes = instr.bytes(); assert_eq!(bytes, vec![0x78, 0x56, 0x34, 0x12]); // little-endian } #[test] fn test_compressed_instruction() { - // Test 16-bit compressed instruction creation + // Test 16-bit compressed instruction creation let instr = Instruction::Compressed(0x1234); assert_eq!(instr.value(), 0x1234); assert_eq!(instr.size(), 2); assert!(instr.is_compressed()); - + let bytes = instr.bytes(); assert_eq!(bytes, vec![0x34, 0x12]); // little-endian } @@ -515,23 +527,24 @@ fn test_compressed_instruction() { fn test_method_chaining() { // Test that builder methods can be chained let mut builder = Riscv64InstructionBuilder::new(); - + builder .csrrw(reg::X1, csr::MSTATUS, reg::X2) .addi(reg::X3, reg::X1, 100) .add(reg::X4, reg::X1, reg::X2) .sub(reg::X5, reg::X4, reg::X3); - - let instructions = builder.instructions(); + + let instructions = builder.instructions().unwrap(); assert_eq!(instructions.len(), 4); - + // Test fluent interface pattern let mut builder2 = Riscv64InstructionBuilder::new(); let instructions2 = builder2 .csrrs(reg::X10, csr::MEPC, reg::X0) // csrr equivalent .addi(reg::X11, reg::X10, 42) - .instructions(); - + .instructions() + .unwrap(); + assert_eq!(instructions2.len(), 2); } @@ -578,21 +591,21 @@ fn test_register_aliases_usage() { // Test using aliases in actual instructions let mut builder = Riscv64InstructionBuilder::new(); builder - .addi(reg::A0, reg::ZERO, 42) // Load 42 into a0 - .add(reg::A1, reg::A0, reg::SP) // Add a0 and sp, store in a1 + .addi(reg::A0, reg::ZERO, 42) // Load 42 into a0 + .add(reg::A1, reg::A0, reg::SP) // Add a0 and sp, store in a1 .sub(reg::T0, reg::A1, reg::A0); // Subtract a0 from a1, store in t0 - - let instructions = builder.instructions(); + + let instructions = builder.instructions().unwrap(); assert_eq!(instructions.len(), 3); - + // Verify instructions are equivalent to using x registers let mut builder2 = Riscv64InstructionBuilder::new(); builder2 - .addi(reg::X10, reg::X0, 42) // a0 = x10, zero = x0 - .add(reg::X11, reg::X10, reg::X2) // a1 = x11, sp = x2 + .addi(reg::X10, reg::X0, 42) // a0 = x10, zero = x0 + .add(reg::X11, reg::X10, reg::X2) // a1 = x11, sp = x2 .sub(reg::X5, reg::X11, reg::X10); // t0 = x5 - - let instructions2 = builder2.instructions(); + + let instructions2 = builder2.instructions().unwrap(); assert_eq!(instructions, instructions2); } @@ -601,15 +614,15 @@ fn test_ret_instruction() { // Test ret instruction (should be equivalent to jalr x0, x1, 0) let mut builder = Riscv64InstructionBuilder::new(); builder.ret(); - - let instructions = builder.instructions(); + + let instructions = builder.instructions().unwrap(); assert_eq!(instructions.len(), 1); - + // Compare with explicit jalr x0, x1, 0 let mut builder2 = Riscv64InstructionBuilder::new(); builder2.jalr(reg::X0, reg::X1, 0); - - let instructions2 = builder2.instructions(); + + let instructions2 = builder2.instructions().unwrap(); assert_eq!(instructions, instructions2); } @@ -618,32 +631,32 @@ fn test_ret_instruction_with_aliases() { // Test ret using register aliases let mut builder = Riscv64InstructionBuilder::new(); builder.ret(); - - let instructions = builder.instructions(); + + let instructions = builder.instructions().unwrap(); assert_eq!(instructions.len(), 1); - + // Compare with explicit jalr using aliases let mut builder2 = Riscv64InstructionBuilder::new(); builder2.jalr(reg::ZERO, reg::RA, 0); - - let instructions2 = builder2.instructions(); + + let instructions2 = builder2.instructions().unwrap(); assert_eq!(instructions, instructions2); } #[test] fn test_privileged_instructions() { let mut builder = Riscv64InstructionBuilder::new(); - + // Test privileged instructions - builder.sret(); // Supervisor return - builder.mret(); // Machine return - builder.ecall(); // Environment call - builder.ebreak(); // Environment break - builder.wfi(); // Wait for interrupt - - let instructions = builder.instructions(); + builder.sret(); // Supervisor return + builder.mret(); // Machine return + builder.ecall(); // Environment call + builder.ebreak(); // Environment break + builder.wfi(); // Wait for interrupt + + let instructions = builder.instructions().unwrap(); assert_eq!(instructions.len(), 5); - + // Verify all instructions are non-zero for instr in instructions { assert!(instr.value() != 0); @@ -661,7 +674,7 @@ fn test_aliases_with_macro() { }; assert_eq!(instructions.len(), 4); - + // Verify macro produces same results as builder with aliases let builder_instructions = Riscv64InstructionBuilder::new() .addi(reg::A0, reg::ZERO, 42) @@ -669,8 +682,9 @@ fn test_aliases_with_macro() { .sub(reg::T0, reg::A1, reg::A0) .ret() .instructions() + .unwrap() .to_vec(); - + assert_eq!(instructions, builder_instructions); } @@ -694,10 +708,10 @@ fn test_comprehensive_alias_demo() { sd(reg::RA, reg::SP, 8); // Save return address sd(reg::S0, reg::SP, 0); // Save frame pointer addi(reg::S0, reg::SP, 16); // Set frame pointer - + // Function body addi(reg::A0, reg::ZERO, 42); // Load 42 into a0 (return value) - + // Function epilogue ld(reg::S0, reg::SP, 0); // Restore frame pointer ld(reg::RA, reg::SP, 8); // Restore return address @@ -706,15 +720,18 @@ fn test_comprehensive_alias_demo() { }; assert_eq!(instructions.len(), 9); - + // Verify instructions contain expected values - println!("Generated {} instructions using aliases", instructions.len()); + println!( + "Generated {} instructions using aliases", + instructions.len() + ); for (i, instr) in instructions.iter().enumerate() { println!(" {}: {}", i, instr); } } -#[test] +#[test] fn test_macro_chaining() { let instructions = crate::riscv64_asm! { addi(reg::X1, reg::X0, 10); @@ -723,15 +740,16 @@ fn test_macro_chaining() { }; assert_eq!(instructions.len(), 3); - + // Verify macro produces same results as builder let builder_instructions = Riscv64InstructionBuilder::new() .addi(reg::X1, reg::X0, 10) .add(reg::X2, reg::X1, reg::X0) .csrrw(reg::X3, csr::MSTATUS, reg::X2) .instructions() + .unwrap() .to_vec(); - + assert_eq!(instructions, builder_instructions); } @@ -748,7 +766,7 @@ fn test_macro_comprehensive() { }; assert_eq!(instructions.len(), 6); - + // Compare with builder version and verify comprehensive functionality let builder_instructions = Riscv64InstructionBuilder::new() .lui(reg::X1, 0x12345) @@ -756,12 +774,13 @@ fn test_macro_comprehensive() { .add(reg::X3, reg::X1, reg::X2) .beq(reg::X1, reg::X2, 8) .jal(reg::X1, 16) - .csrrs(reg::X4, csr::MSTATUS, reg::X0) // csrr is csrrs with x0 + .csrrs(reg::X4, csr::MSTATUS, reg::X0) // csrr is csrrs with x0 .instructions() + .unwrap() .to_vec(); - + assert_eq!(instructions, builder_instructions); - + // Each instruction should be non-zero for instr in &instructions { assert!(instr.value() != 0); @@ -775,19 +794,19 @@ fn test_binary_correctness_arithmetic() { // Test ADDI instruction let mut builder = Riscv64InstructionBuilder::new(); builder.addi(reg::X1, reg::X0, 100); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "addi x1, x0, 100\n"); - + // Test ADD instruction - use fresh builder let mut builder = Riscv64InstructionBuilder::new(); builder.add(reg::X3, reg::X1, reg::X2); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "add x3, x1, x2\n"); - - // Test SUB instruction - use fresh builder + + // Test SUB instruction - use fresh builder let mut builder = Riscv64InstructionBuilder::new(); builder.sub(reg::X4, reg::X3, reg::X1); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "sub x4, x3, x1\n"); } @@ -801,79 +820,79 @@ fn test_binary_correctness_immediate_arithmetic() { (-1, "addi x1, x0, -1\n"), (100, "addi x1, x0, 100\n"), (-100, "addi x1, x0, -100\n"), - (2047, "addi x1, x0, 2047\n"), // Max positive 12-bit immediate + (2047, "addi x1, x0, 2047\n"), // Max positive 12-bit immediate (-2048, "addi x1, x0, -2048\n"), // Min negative 12-bit immediate ]; - + for (imm, assembly) in test_cases { let mut builder = Riscv64InstructionBuilder::new(); builder.addi(reg::X1, reg::X0, imm); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], assembly); } } #[cfg(feature = "std")] -#[test] +#[test] fn test_binary_correctness_logical() { // Test XOR let mut builder = Riscv64InstructionBuilder::new(); builder.xor(reg::X5, reg::X1, reg::X2); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "xor x5, x1, x2\n"); - + // Test OR let mut builder = Riscv64InstructionBuilder::new(); builder.or(reg::X6, reg::X3, reg::X4); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "or x6, x3, x4\n"); - + // Test AND let mut builder = Riscv64InstructionBuilder::new(); builder.and(reg::X7, reg::X5, reg::X6); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "and x7, x5, x6\n"); - + // Test ANDI (AND immediate) let mut builder = Riscv64InstructionBuilder::new(); builder.andi(reg::X8, reg::X1, 0xFF); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "andi x8, x1, 255\n"); - + // Test ORI (OR immediate) let mut builder = Riscv64InstructionBuilder::new(); builder.ori(reg::X9, reg::X2, 0x100); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "ori x9, x2, 256\n"); - + // Test XORI (XOR immediate) let mut builder = Riscv64InstructionBuilder::new(); builder.xori(reg::X10, reg::X3, 0x200); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "xori x10, x3, 512\n"); - + // Test SLTI (Set Less Than immediate) let mut builder = Riscv64InstructionBuilder::new(); builder.slti(reg::X11, reg::X4, 50); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "slti x11, x4, 50\n"); - + // Test SLTIU (Set Less Than immediate Unsigned) let mut builder = Riscv64InstructionBuilder::new(); builder.sltiu(reg::X12, reg::X5, 200); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "sltiu x12, x5, 200\n"); - + // Test SLT (Set Less Than) let mut builder = Riscv64InstructionBuilder::new(); builder.slt(reg::X13, reg::X6, reg::X7); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "slt x13, x6, x7\n"); - + // Test SLTU (Set Less Than Unsigned) let mut builder = Riscv64InstructionBuilder::new(); builder.sltu(reg::X14, reg::X8, reg::X9); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "sltu x14, x8, x9\n"); } @@ -883,37 +902,37 @@ fn test_binary_correctness_shifts() { // Test shift left logical let mut builder = Riscv64InstructionBuilder::new(); builder.sll(reg::X8, reg::X1, reg::X2); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "sll x8, x1, x2\n"); - + // Test shift right logical let mut builder = Riscv64InstructionBuilder::new(); builder.srl(reg::X9, reg::X3, reg::X4); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "srl x9, x3, x4\n"); - + // Test shift right arithmetic let mut builder = Riscv64InstructionBuilder::new(); builder.sra(reg::X10, reg::X5, reg::X6); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "sra x10, x5, x6\n"); - + // Test shift left logical immediate let mut builder = Riscv64InstructionBuilder::new(); builder.slli(reg::X11, reg::X1, 5); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "slli x11, x1, 5\n"); - + // Test shift right logical immediate let mut builder = Riscv64InstructionBuilder::new(); builder.srli(reg::X12, reg::X2, 10); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "srli x12, x2, 10\n"); - + // Test shift right arithmetic immediate let mut builder = Riscv64InstructionBuilder::new(); builder.srai(reg::X13, reg::X3, 15); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "srai x13, x3, 15\n"); } @@ -923,13 +942,13 @@ fn test_binary_correctness_upper_immediate() { // Test LUI instruction let mut builder = Riscv64InstructionBuilder::new(); builder.lui(reg::X1, 0x12345); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "lui x1, 0x12345\n"); - + // Test AUIPC instruction let mut builder = Riscv64InstructionBuilder::new(); builder.auipc(reg::X2, 0x6789A); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "auipc x2, 0x6789A\n"); } @@ -939,61 +958,61 @@ fn test_binary_correctness_csr() { // Test CSRRW instruction let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X1, csr::MSTATUS, reg::X2); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "csrrw x1, mstatus, x2\n"); - + // Test CSRRS instruction let mut builder = Riscv64InstructionBuilder::new(); builder.csrrs(reg::X3, csr::MEPC, reg::X4); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "csrrs x3, mepc, x4\n"); - + // Test CSRRWI instruction let mut builder = Riscv64InstructionBuilder::new(); builder.csrrwi(reg::X5, csr::MTVEC, 0x10); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "csrrwi x5, mtvec, 0x10\n"); - + // Test CSRR instruction (alias for csrrs with x0) let mut builder = Riscv64InstructionBuilder::new(); builder.csrr(reg::X4, csr::MSTATUS); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "csrr x4, mstatus\n"); - + // Test CSRW instruction (alias for csrrw with x0) let mut builder = Riscv64InstructionBuilder::new(); builder.csrw(csr::MEPC, reg::X1); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "csrw mepc, x1\n"); - + // Test CSRS instruction (alias for csrrs with x0) let mut builder = Riscv64InstructionBuilder::new(); builder.csrs(csr::MIE, reg::X2); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "csrs mie, x2\n"); - + // Test CSRC instruction (alias for csrrc with x0) let mut builder = Riscv64InstructionBuilder::new(); builder.csrc(csr::MIP, reg::X3); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "csrc mip, x3\n"); - + // Test CSRWI instruction (alias for csrrwi with x0) let mut builder = Riscv64InstructionBuilder::new(); builder.csrwi(csr::MTVEC, 0x8); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "csrwi mtvec, 0x8\n"); - + // Test CSRSI instruction (alias for csrrsi with x0) let mut builder = Riscv64InstructionBuilder::new(); builder.csrsi(csr::MIE, 0x4); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "csrsi mie, 0x4\n"); - + // Test CSRCI instruction (alias for csrrci with x0) let mut builder = Riscv64InstructionBuilder::new(); builder.csrci(csr::MIP, 0x2); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "csrci mip, 0x2\n"); } @@ -1003,42 +1022,42 @@ fn test_binary_correctness_s_mode_csrs() { // Test S-mode CSR read operations let mut builder = Riscv64InstructionBuilder::new(); builder.csrr(reg::X1, csr::SSTATUS); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "csrr x1, sstatus\n"); - + let mut builder = Riscv64InstructionBuilder::new(); builder.csrr(reg::X2, csr::SIE); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "csrr x2, sie\n"); - + let mut builder = Riscv64InstructionBuilder::new(); builder.csrr(reg::X3, csr::STVEC); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "csrr x3, stvec\n"); - - let mut builder = Riscv64InstructionBuilder::new(); + + let mut builder = Riscv64InstructionBuilder::new(); builder.csrr(reg::X4, csr::SSCRATCH); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "csrr x4, sscratch\n"); - + let mut builder = Riscv64InstructionBuilder::new(); builder.csrr(reg::X5, csr::SEPC); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "csrr x5, sepc\n"); - + let mut builder = Riscv64InstructionBuilder::new(); builder.csrr(reg::X6, csr::SCAUSE); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "csrr x6, scause\n"); - + let mut builder = Riscv64InstructionBuilder::new(); builder.csrr(reg::X7, csr::STVAL); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "csrr x7, stval\n"); - + let mut builder = Riscv64InstructionBuilder::new(); builder.csrr(reg::X8, csr::SIP); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "csrr x8, sip\n"); } @@ -1048,33 +1067,33 @@ fn test_binary_correctness_s_mode_csr_write_operations() { // Test S-mode CSR write operations let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X1, csr::SSTATUS, reg::X2); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "csrrw x1, sstatus, x2\n"); - + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrs(reg::X3, csr::SIE, reg::X4); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "csrrs x3, sie, x4\n"); - + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrc(reg::X5, csr::STVEC, reg::X6); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "csrrc x5, stvec, x6\n"); - + // Test immediate variants let mut builder = Riscv64InstructionBuilder::new(); builder.csrrwi(reg::X7, csr::SSCRATCH, 0x1f); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "csrrwi x7, sscratch, 0x1f\n"); - + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrsi(reg::X8, csr::SEPC, 0x10); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "csrrsi x8, sepc, 0x10\n"); - + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrci(reg::X9, csr::SIP, 0x08); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "csrrci x9, sip, 0x08\n"); } @@ -1084,31 +1103,31 @@ fn test_binary_correctness_privileged() { // Test SRET instruction let mut builder = Riscv64InstructionBuilder::new(); builder.sret(); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "sret\n"); - + // Test MRET instruction let mut builder = Riscv64InstructionBuilder::new(); builder.mret(); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "mret\n"); - + // Test ECALL instruction let mut builder = Riscv64InstructionBuilder::new(); builder.ecall(); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "ecall\n"); - + // Test EBREAK instruction let mut builder = Riscv64InstructionBuilder::new(); builder.ebreak(); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "ebreak\n"); - + // Test WFI instruction let mut builder = Riscv64InstructionBuilder::new(); builder.wfi(); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "wfi\n"); } @@ -1118,37 +1137,37 @@ fn test_binary_correctness_branches() { // Test BEQ instruction let mut builder = Riscv64InstructionBuilder::new(); builder.beq(reg::X1, reg::X2, 0); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "beq x1, x2, .\n"); - + // Test BNE instruction let mut builder = Riscv64InstructionBuilder::new(); builder.bne(reg::X3, reg::X4, 0); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "bne x3, x4, .\n"); - + // Test BLT instruction let mut builder = Riscv64InstructionBuilder::new(); builder.blt(reg::X5, reg::X6, 0); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "blt x5, x6, .\n"); - + // Test BGE instruction let mut builder = Riscv64InstructionBuilder::new(); builder.bge(reg::X7, reg::X8, 0); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "bge x7, x8, .\n"); - + // Test BLTU instruction let mut builder = Riscv64InstructionBuilder::new(); builder.bltu(reg::X9, reg::X10, 0); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "bltu x9, x10, .\n"); - + // Test BGEU instruction let mut builder = Riscv64InstructionBuilder::new(); builder.bgeu(reg::X11, reg::X12, 0); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "bgeu x11, x12, .\n"); } @@ -1157,37 +1176,37 @@ fn test_binary_correctness_branches() { fn test_jump_instructions_comprehensive() { // Test JAL instruction with various immediate values let test_cases = vec![ - (0, "jal x1, ."), // Zero offset (branch to self) - (4, "jal x1, .+4"), // Small positive offset - (100, "jal x1, .+100"), // Medium positive offset - (1000, "jal x1, .+1000"), // Larger positive offset - (-4, "jal x1, .-4"), // Small negative offset - (-100, "jal x1, .-100"), // Medium negative offset - (-1000, "jal x1, .-1000"), // Larger negative offset + (0, "jal x1, ."), // Zero offset (branch to self) + (4, "jal x1, .+4"), // Small positive offset + (100, "jal x1, .+100"), // Medium positive offset + (1000, "jal x1, .+1000"), // Larger positive offset + (-4, "jal x1, .-4"), // Small negative offset + (-100, "jal x1, .-100"), // Medium negative offset + (-1000, "jal x1, .-1000"), // Larger negative offset ]; - + for (imm, assembly) in test_cases { let mut builder = Riscv64InstructionBuilder::new(); builder.jal(reg::X1, imm); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], &format!("{}\n", assembly)); } - + // Test JALR instruction with various immediate values let jalr_test_cases = vec![ - (0, "jalr x0, x1, 0"), // Zero offset - (4, "jalr x0, x1, 4"), // Small positive offset - (100, "jalr x0, x1, 100"), // Medium positive offset - (1000, "jalr x0, x1, 1000"), // Large positive offset - (-4, "jalr x0, x1, -4"), // Small negative offset - (-100, "jalr x0, x1, -100"), // Medium negative offset - (-1000, "jalr x0, x1, -1000"), // Large negative offset + (0, "jalr x0, x1, 0"), // Zero offset + (4, "jalr x0, x1, 4"), // Small positive offset + (100, "jalr x0, x1, 100"), // Medium positive offset + (1000, "jalr x0, x1, 1000"), // Large positive offset + (-4, "jalr x0, x1, -4"), // Small negative offset + (-100, "jalr x0, x1, -100"), // Medium negative offset + (-1000, "jalr x0, x1, -1000"), // Large negative offset ]; - + for (imm, assembly) in jalr_test_cases { let mut builder = Riscv64InstructionBuilder::new(); builder.jalr(reg::X0, reg::X1, imm); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], &format!("{}\n", assembly)); } } @@ -1196,35 +1215,35 @@ fn test_jump_instructions_comprehensive() { #[test] fn test_jump_instruction_encoding_correctness() { // Test specific known encodings to verify correctness using GNU assembler comparison - + // JAL x1, 0 - compare with GNU assembler let mut builder = Riscv64InstructionBuilder::new(); builder.jal(reg::X1, 0); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "jal x1, .\n"); - + // JALR x0, x1, 0 - compare with GNU assembler let mut builder2 = Riscv64InstructionBuilder::new(); builder2.jalr(reg::X0, reg::X1, 0); - let instructions2 = builder2.instructions(); + let instructions2 = builder2.instructions().unwrap(); compare_instruction(instructions2[0], "jalr x0, x1, 0\n"); - + // Test JAL with small positive offset let mut builder3 = Riscv64InstructionBuilder::new(); builder3.jal(reg::X1, 4); - let instructions3 = builder3.instructions(); + let instructions3 = builder3.instructions().unwrap(); compare_instruction(instructions3[0], "jal x1, .+4\n"); - + // Test JALR with positive offset let mut builder4 = Riscv64InstructionBuilder::new(); builder4.jalr(reg::X2, reg::X3, 8); - let instructions4 = builder4.instructions(); + let instructions4 = builder4.instructions().unwrap(); compare_instruction(instructions4[0], "jalr x2, x3, 8\n"); - + // Test negative offset encoding let mut builder5 = Riscv64InstructionBuilder::new(); builder5.jal(reg::X1, -4); - let instructions5 = builder5.instructions(); + let instructions5 = builder5.instructions().unwrap(); compare_instruction(instructions5[0], "jal x1, .-4\n"); } @@ -1232,41 +1251,41 @@ fn test_jump_instruction_encoding_correctness() { #[test] fn test_jump_branch_edge_cases() { // Test edge cases near the limits of immediate ranges - + // JAL large positive offset (21-bit signed max: +1,048,575) let mut builder = Riscv64InstructionBuilder::new(); builder.jal(reg::X1, 1048575); // Max positive J-type immediate (2^20 - 1) - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "jal x1, .+1048575\n"); - + // JAL large negative offset (21-bit signed min: -1,048,576) let mut builder2 = Riscv64InstructionBuilder::new(); builder2.jal(reg::X1, -1048576); // Min negative J-type immediate (-2^20) - let instructions2 = builder2.instructions(); + let instructions2 = builder2.instructions().unwrap(); compare_instruction(instructions2[0], "jal x1, .-1048576\n"); - + // JALR maximum positive immediate (12-bit signed max: +2,047) let mut builder3 = Riscv64InstructionBuilder::new(); builder3.jalr(reg::X0, reg::X1, 2047); // Max positive JALR immediate - let instructions3 = builder3.instructions(); + let instructions3 = builder3.instructions().unwrap(); compare_instruction(instructions3[0], "jalr x0, x1, 2047\n"); - + // JALR maximum negative immediate (12-bit signed min: -2,048) let mut builder4 = Riscv64InstructionBuilder::new(); builder4.jalr(reg::X0, reg::X1, -2048); // Min negative JALR immediate - let instructions4 = builder4.instructions(); + let instructions4 = builder4.instructions().unwrap(); compare_instruction(instructions4[0], "jalr x0, x1, -2048\n"); - + // Branch maximum positive offset (13-bit signed max: +4,095) let mut builder5 = Riscv64InstructionBuilder::new(); builder5.beq(reg::X1, reg::X2, 4095); // Max positive branch immediate - let instructions5 = builder5.instructions(); + let instructions5 = builder5.instructions().unwrap(); compare_instruction(instructions5[0], "beq x1, x2, .+4095\n"); - + // Branch maximum negative offset (13-bit signed min: -4,096) let mut builder6 = Riscv64InstructionBuilder::new(); - builder6.beq(reg::X1, reg::X2, -4096); // Min negative branch immediate - let instructions6 = builder6.instructions(); + builder6.beq(reg::X1, reg::X2, -4096); // Min negative branch immediate + let instructions6 = builder6.instructions().unwrap(); compare_instruction(instructions6[0], "beq x1, x2, .-4096\n"); } @@ -1275,53 +1294,56 @@ fn test_jump_branch_edge_cases() { fn test_branch_instructions_comprehensive() { // Test branch instruction edge cases and ranges // Branch instructions have 13-bit signed immediate range: -4096 to +4095 (even only) - + let branch_test_cases = vec![ - (0, "."), // Zero offset (branch to self) - (4, ".+4"), // Small positive offset - (100, ".+100"), // Medium positive offset - (1000, ".+1000"), // Large positive offset - (-4, ".-4"), // Small negative offset - (-100, ".-100"), // Medium negative offset - (-1000, ".-1000"), // Large negative offset + (0, "."), // Zero offset (branch to self) + (4, ".+4"), // Small positive offset + (100, ".+100"), // Medium positive offset + (1000, ".+1000"), // Large positive offset + (-4, ".-4"), // Small negative offset + (-100, ".-100"), // Medium negative offset + (-1000, ".-1000"), // Large negative offset ]; - + for (imm, offset_str) in branch_test_cases { // Test BEQ instruction (funct3 = 0x0) let mut builder = Riscv64InstructionBuilder::new(); builder.beq(reg::X1, reg::X2, imm); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], &format!("beq x1, x2, {}\n", offset_str)); - + // Test BNE instruction (funct3 = 0x1) let mut builder2 = Riscv64InstructionBuilder::new(); builder2.bne(reg::X3, reg::X4, imm); - let instructions2 = builder2.instructions(); + let instructions2 = builder2.instructions().unwrap(); compare_instruction(instructions2[0], &format!("bne x3, x4, {}\n", offset_str)); - + // Test BLT instruction (funct3 = 0x4) let mut builder3 = Riscv64InstructionBuilder::new(); builder3.blt(reg::X5, reg::X6, imm); - let instructions3 = builder3.instructions(); + let instructions3 = builder3.instructions().unwrap(); compare_instruction(instructions3[0], &format!("blt x5, x6, {}\n", offset_str)); - + // Test BGE instruction (funct3 = 0x5) let mut builder4 = Riscv64InstructionBuilder::new(); builder4.bge(reg::X7, reg::X8, imm); - let instructions4 = builder4.instructions(); + let instructions4 = builder4.instructions().unwrap(); compare_instruction(instructions4[0], &format!("bge x7, x8, {}\n", offset_str)); - + // Test BLTU instruction (funct3 = 0x6) let mut builder5 = Riscv64InstructionBuilder::new(); builder5.bltu(reg::X9, reg::X10, imm); - let instructions5 = builder5.instructions(); + let instructions5 = builder5.instructions().unwrap(); compare_instruction(instructions5[0], &format!("bltu x9, x10, {}\n", offset_str)); - + // Test BGEU instruction (funct3 = 0x7) let mut builder6 = Riscv64InstructionBuilder::new(); builder6.bgeu(reg::X11, reg::X12, imm); - let instructions6 = builder6.instructions(); - compare_instruction(instructions6[0], &format!("bgeu x11, x12, {}\n", offset_str)); + let instructions6 = builder6.instructions().unwrap(); + compare_instruction( + instructions6[0], + &format!("bgeu x11, x12, {}\n", offset_str), + ); } } @@ -1331,13 +1353,13 @@ fn test_binary_correctness_jumps() { // Test JAL instruction with zero offset let mut builder = Riscv64InstructionBuilder::new(); builder.jal(reg::X1, 0); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "jal x1, .\n"); - + // Test JALR instruction let mut builder = Riscv64InstructionBuilder::new(); builder.jalr(reg::X0, reg::X1, 0); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "jalr x0, x1, 0\n"); } @@ -1347,67 +1369,67 @@ fn test_binary_correctness_memory() { // Test LD instruction let mut builder = Riscv64InstructionBuilder::new(); builder.ld(reg::X1, reg::X2, 8); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "ld x1, 8(x2)\n"); - + // Test LW instruction let mut builder = Riscv64InstructionBuilder::new(); builder.lw(reg::X3, reg::X4, 4); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "lw x3, 4(x4)\n"); - + // Test LH instruction let mut builder = Riscv64InstructionBuilder::new(); builder.lh(reg::X5, reg::X6, 2); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "lh x5, 2(x6)\n"); - + // Test LB instruction let mut builder = Riscv64InstructionBuilder::new(); builder.lb(reg::X7, reg::X8, 1); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "lb x7, 1(x8)\n"); - + // Test LBU instruction (Load Byte Unsigned) let mut builder = Riscv64InstructionBuilder::new(); builder.lbu(reg::X9, reg::X10, 1); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "lbu x9, 1(x10)\n"); - + // Test LHU instruction (Load Halfword Unsigned) let mut builder = Riscv64InstructionBuilder::new(); builder.lhu(reg::X11, reg::X12, 2); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "lhu x11, 2(x12)\n"); - + // Test LWU instruction (Load Word Unsigned) let mut builder = Riscv64InstructionBuilder::new(); builder.lwu(reg::X13, reg::X14, 4); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "lwu x13, 4(x14)\n"); - + // Test SD instruction let mut builder = Riscv64InstructionBuilder::new(); builder.sd(reg::X9, reg::X10, 8); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "sd x10, 8(x9)\n"); - + // Test SW instruction let mut builder = Riscv64InstructionBuilder::new(); builder.sw(reg::X11, reg::X12, 4); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "sw x12, 4(x11)\n"); - + // Test SH instruction let mut builder = Riscv64InstructionBuilder::new(); builder.sh(reg::X13, reg::X14, 2); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "sh x14, 2(x13)\n"); - + // Test SB instruction let mut builder = Riscv64InstructionBuilder::new(); builder.sb(reg::X15, reg::X16, 1); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "sb x16, 1(x15)\n"); } @@ -1415,23 +1437,31 @@ fn test_binary_correctness_memory() { #[cfg(feature = "std")] fn compare_instructions(jit_instrs: &[Instruction], gnu_assembly: &str) { let gnu_bytes = assemble_riscv(gnu_assembly); - + // Skip comparison if GNU assembler is not available or returned empty if gnu_bytes.is_empty() { return; } - + let mut jit_bytes = Vec::new(); for instr in jit_instrs { jit_bytes.extend_from_slice(&instr.bytes()); } - + // The GNU assembler output might have extra padding, so we only compare the first N bytes - assert_eq!(jit_bytes.len(), jit_instrs.len() * 4, "JIT instructions should be 4 bytes each"); - assert!(gnu_bytes.len() >= jit_bytes.len(), "GNU assembler output should be at least {} bytes", jit_bytes.len()); - assert_eq!( - jit_bytes, + jit_bytes.len(), + jit_instrs.len() * 4, + "JIT instructions should be 4 bytes each" + ); + assert!( + gnu_bytes.len() >= jit_bytes.len(), + "GNU assembler output should be at least {} bytes", + jit_bytes.len() + ); + + assert_eq!( + jit_bytes, &gnu_bytes[0..jit_bytes.len()], "JIT assembler output does not match GNU assembler output\nJIT: {:02x?}\nGNU: {:02x?}\nAssembly:\n{}", jit_bytes, @@ -1449,10 +1479,12 @@ fn test_binary_correctness_multiline_arithmetic() { builder.addi(reg::X2, reg::X0, 20); builder.add(reg::X3, reg::X1, reg::X2); builder.sub(reg::X4, reg::X3, reg::X1); - - let instructions = builder.instructions(); - compare_instructions(&instructions, - "addi x1, x0, 10\naddi x2, x0, 20\nadd x3, x1, x2\nsub x4, x3, x1\n"); + + let instructions = builder.instructions().unwrap(); + compare_instructions( + &instructions, + "addi x1, x0, 10\naddi x2, x0, 20\nadd x3, x1, x2\nsub x4, x3, x1\n", + ); } #[cfg(feature = "std")] @@ -1465,10 +1497,12 @@ fn test_binary_correctness_multiline_logic_shifts() { builder.xor(reg::X3, reg::X1, reg::X2); builder.slli(reg::X4, reg::X3, 4); builder.srli(reg::X5, reg::X4, 2); - - let instructions = builder.instructions(); - compare_instructions(&instructions, - "lui x1, 0x12345\naddi x2, x1, 0x678\nxor x3, x1, x2\nslli x4, x3, 4\nsrli x5, x4, 2\n"); + + let instructions = builder.instructions().unwrap(); + compare_instructions( + &instructions, + "lui x1, 0x12345\naddi x2, x1, 0x678\nxor x3, x1, x2\nslli x4, x3, 4\nsrli x5, x4, 2\n", + ); } #[cfg(feature = "std")] @@ -1480,10 +1514,12 @@ fn test_binary_correctness_multiline_csr_operations() { builder.addi(reg::X2, reg::X1, 1); builder.csrrw(reg::X3, csr::MSTATUS, reg::X2); builder.csrrs(reg::X4, csr::MEPC, reg::X0); - - let instructions = builder.instructions(); - compare_instructions(&instructions, - "csrr x1, mstatus\naddi x2, x1, 1\ncsrrw x3, mstatus, x2\ncsrrs x4, mepc, x0\n"); + + let instructions = builder.instructions().unwrap(); + compare_instructions( + &instructions, + "csrr x1, mstatus\naddi x2, x1, 1\ncsrrw x3, mstatus, x2\ncsrrs x4, mepc, x0\n", + ); } #[cfg(feature = "std")] @@ -1491,14 +1527,14 @@ fn test_binary_correctness_multiline_csr_operations() { fn test_binary_correctness_multiline_mixed_mode_csr_operations() { // Test a sequence mixing M-mode and S-mode CSR operations let mut builder = Riscv64InstructionBuilder::new(); - builder.csrr(reg::X1, csr::MSTATUS); // M-mode - builder.csrr(reg::X2, csr::SSTATUS); // S-mode - builder.csrrw(reg::X3, csr::MTVEC, reg::X1); // M-mode write - builder.csrrw(reg::X4, csr::STVEC, reg::X2); // S-mode write - builder.csrrs(reg::X5, csr::MIE, reg::X0); // M-mode read - builder.csrrs(reg::X6, csr::SIE, reg::X0); // S-mode read - - let instructions = builder.instructions(); + builder.csrr(reg::X1, csr::MSTATUS); // M-mode + builder.csrr(reg::X2, csr::SSTATUS); // S-mode + builder.csrrw(reg::X3, csr::MTVEC, reg::X1); // M-mode write + builder.csrrw(reg::X4, csr::STVEC, reg::X2); // S-mode write + builder.csrrs(reg::X5, csr::MIE, reg::X0); // M-mode read + builder.csrrs(reg::X6, csr::SIE, reg::X0); // S-mode read + + let instructions = builder.instructions().unwrap(); compare_instructions(&instructions, "csrr x1, mstatus\ncsrr x2, sstatus\ncsrrw x3, mtvec, x1\ncsrrw x4, stvec, x2\ncsrrs x5, mie, x0\ncsrrs x6, sie, x0\n"); } @@ -1508,14 +1544,14 @@ fn test_binary_correctness_multiline_mixed_mode_csr_operations() { fn test_binary_correctness_multiline_s_mode_supervisor_context_switch() { // Test S-mode context switch sequence let mut builder = Riscv64InstructionBuilder::new(); - builder.csrr(reg::X1, csr::SSTATUS); // Read supervisor status - builder.csrr(reg::X2, csr::SEPC); // Read supervisor exception PC - builder.csrr(reg::X3, csr::SCAUSE); // Read supervisor cause - builder.csrr(reg::X4, csr::STVAL); // Read supervisor trap value - builder.csrrw(reg::X5, csr::SSCRATCH, reg::SP); // Save stack pointer - builder.csrrwi(reg::X6, csr::SIE, 0x0); // Disable supervisor interrupts - - let instructions = builder.instructions(); + builder.csrr(reg::X1, csr::SSTATUS); // Read supervisor status + builder.csrr(reg::X2, csr::SEPC); // Read supervisor exception PC + builder.csrr(reg::X3, csr::SCAUSE); // Read supervisor cause + builder.csrr(reg::X4, csr::STVAL); // Read supervisor trap value + builder.csrrw(reg::X5, csr::SSCRATCH, reg::SP); // Save stack pointer + builder.csrrwi(reg::X6, csr::SIE, 0x0); // Disable supervisor interrupts + + let instructions = builder.instructions().unwrap(); compare_instructions(&instructions, "csrr x1, sstatus\ncsrr x2, sepc\ncsrr x3, scause\ncsrr x4, stval\ncsrrw x5, sscratch, x2\ncsrrwi x6, sie, 0x0\n"); } @@ -1531,8 +1567,8 @@ fn test_binary_correctness_multiline_memory_operations() { builder.lw(reg::X3, reg::X1, 0); builder.addi(reg::X4, reg::X3, 1); builder.sw(reg::X1, reg::X4, 4); - - let instructions = builder.instructions(); + + let instructions = builder.instructions().unwrap(); compare_instructions(&instructions, "lui x1, 0x10000\naddi x2, x0, 42\nsw x2, 0(x1)\nlw x3, 0(x1)\naddi x4, x3, 1\nsw x4, 4(x1)\n"); } @@ -1544,13 +1580,15 @@ fn test_binary_correctness_multiline_control_flow() { let mut builder = Riscv64InstructionBuilder::new(); builder.addi(reg::X1, reg::X0, 5); builder.addi(reg::X2, reg::X0, 10); - builder.beq(reg::X1, reg::X2, 0); // Branch to self (zero offset) - builder.bne(reg::X1, reg::X2, 0); // Branch to self (zero offset) - builder.jal(reg::X3, 0); // Jump to self (zero offset) - - let instructions = builder.instructions(); - compare_instructions(&instructions, - "addi x1, x0, 5\naddi x2, x0, 10\nbeq x1, x2, .\nbne x1, x2, .\njal x3, .\n"); + builder.beq(reg::X1, reg::X2, 0); // Branch to self (zero offset) + builder.bne(reg::X1, reg::X2, 0); // Branch to self (zero offset) + builder.jal(reg::X3, 0); // Jump to self (zero offset) + + let instructions = builder.instructions().unwrap(); + compare_instructions( + &instructions, + "addi x1, x0, 5\naddi x2, x0, 10\nbeq x1, x2, .\nbne x1, x2, .\njal x3, .\n", + ); } #[cfg(feature = "std")] @@ -1558,17 +1596,17 @@ fn test_binary_correctness_multiline_control_flow() { fn test_binary_correctness_multiline_comprehensive() { // Test a comprehensive sequence mixing different instruction types let mut builder = Riscv64InstructionBuilder::new(); - builder.lui(reg::X1, 0x12345); // Upper immediate - builder.addi(reg::X1, reg::X1, 0x678); // Immediate arithmetic - builder.add(reg::X2, reg::X1, reg::X0); // Register arithmetic - builder.slli(reg::X3, reg::X2, 1); // Shift immediate - builder.xor(reg::X4, reg::X2, reg::X3); // Logical operation - builder.csrr(reg::X5, csr::MSTATUS); // CSR operation - builder.sw(reg::X1, reg::X4, 8); // Store operation - builder.lw(reg::X6, reg::X1, 8); // Load operation - builder.beq(reg::X4, reg::X6, 0); // Branch operation - - let instructions = builder.instructions(); + builder.lui(reg::X1, 0x12345); // Upper immediate + builder.addi(reg::X1, reg::X1, 0x678); // Immediate arithmetic + builder.add(reg::X2, reg::X1, reg::X0); // Register arithmetic + builder.slli(reg::X3, reg::X2, 1); // Shift immediate + builder.xor(reg::X4, reg::X2, reg::X3); // Logical operation + builder.csrr(reg::X5, csr::MSTATUS); // CSR operation + builder.sw(reg::X1, reg::X4, 8); // Store operation + builder.lw(reg::X6, reg::X1, 8); // Load operation + builder.beq(reg::X4, reg::X6, 0); // Branch operation + + let instructions = builder.instructions().unwrap(); compare_instructions(&instructions, "lui x1, 0x12345\naddi x1, x1, 0x678\nadd x2, x1, x0\nslli x3, x2, 1\nxor x4, x2, x3\ncsrr x5, mstatus\nsw x4, 8(x1)\nlw x6, 8(x1)\nbeq x4, x6, .\n"); } @@ -1584,25 +1622,35 @@ fn test_binary_correctness_multiline_macro_comparison() { sub(reg::X4, reg::X3, reg::X1); xor(reg::X5, reg::X3, reg::X4); }; - + let mut builder = Riscv64InstructionBuilder::new(); builder.lui(reg::X1, 0x12345); builder.addi(reg::X2, reg::X1, 100); builder.add(reg::X3, reg::X1, reg::X2); builder.sub(reg::X4, reg::X3, reg::X1); builder.xor(reg::X5, reg::X3, reg::X4); - let builder_instructions = builder.instructions(); - + let builder_instructions = builder.instructions().unwrap(); + // Both should match assert_eq!(macro_instructions.len(), builder_instructions.len()); - for (i, (macro_instr, builder_instr)) in macro_instructions.iter().zip(builder_instructions.iter()).enumerate() { - assert_eq!(macro_instr.bytes(), builder_instr.bytes(), - "Instruction {} differs between macro and builder", i); + for (i, (macro_instr, builder_instr)) in macro_instructions + .iter() + .zip(builder_instructions.iter()) + .enumerate() + { + assert_eq!( + macro_instr.bytes(), + builder_instr.bytes(), + "Instruction {} differs between macro and builder", + i + ); } - + // Both should match GNU assembler - compare_instructions(¯o_instructions, - "lui x1, 0x12345\naddi x2, x1, 100\nadd x3, x1, x2\nsub x4, x3, x1\nxor x5, x3, x4\n"); + compare_instructions( + ¯o_instructions, + "lui x1, 0x12345\naddi x2, x1, 100\nadd x3, x1, x2\nsub x4, x3, x1\nxor x5, x3, x4\n", + ); } // Test li pseudo-instruction @@ -1612,8 +1660,8 @@ fn test_li_pseudo_instruction() { // Test li with imm=0 (should generate "addi rd, x0, 0", not "addi rd, rd, 0") let mut builder = Riscv64InstructionBuilder::new(); builder.li(reg::X1, 0); - let instructions = builder.instructions(); - + let instructions = builder.instructions().unwrap(); + // Debug: print what was actually generated println!("li(x1, 0) generated {} instructions:", instructions.len()); for (i, instr) in instructions.iter().enumerate() { @@ -1623,41 +1671,44 @@ fn test_li_pseudo_instruction() { let rd = (val >> 7) & 0x1F; let rs1 = (val >> 15) & 0x1F; let imm = (val as i32) >> 20; - println!(" opcode=0x{:02x}, rd=x{}, rs1=x{}, imm={}", opcode, rd, rs1, imm); + println!( + " opcode=0x{:02x}, rd=x{}, rs1=x{}, imm={}", + opcode, rd, rs1, imm + ); } - + compare_instruction(instructions[0], "addi x1, x0, 0\n"); - + // Test li with small positive immediate let mut builder = Riscv64InstructionBuilder::new(); builder.li(reg::X2, 100); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "addi x2, x0, 100\n"); - + // Test li with small negative immediate let mut builder = Riscv64InstructionBuilder::new(); builder.li(reg::X3, -50); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "addi x3, x0, -50\n"); - + // Test li with large immediate requiring lui+addi let mut builder = Riscv64InstructionBuilder::new(); builder.li(reg::X4, 0x12345678); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); // Large immediates should use lui followed by addi assert_eq!(instructions.len(), 2); compare_instructions(&instructions, "lui x4, 0x12345\naddi x4, x4, 0x678\n"); - + // Test li with max 12-bit immediate (fits in single addi) let mut builder = Riscv64InstructionBuilder::new(); builder.li(reg::X5, 2047); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "addi x5, x0, 2047\n"); - + // Test li with min 12-bit immediate (fits in single addi) let mut builder = Riscv64InstructionBuilder::new(); builder.li(reg::X6, -2048); - let instructions = builder.instructions(); + let instructions = builder.instructions().unwrap(); compare_instruction(instructions[0], "addi x6, x0, -2048\n"); } @@ -1668,8 +1719,8 @@ fn test_jit_basic_function_creation() { // Create a simple function that returns 42 let jit_func = unsafe { Riscv64InstructionBuilder::new() - .addi(reg::A0, reg::ZERO, 42) // Load 42 into a0 (return value) - .ret() // Return + .addi(reg::A0, reg::ZERO, 42) // Load 42 into a0 (return value) + .ret() // Return .function:: u64>() }; @@ -1682,8 +1733,8 @@ fn test_jit_add_function() { // Create a function that adds two numbers (a0 + a1 -> a0) let jit_func = unsafe { Riscv64InstructionBuilder::new() - .add(reg::A0, reg::A0, reg::A1) // Add a0 + a1, result in a0 - .ret() // Return + .add(reg::A0, reg::A0, reg::A1) // Add a0 + a1, result in a0 + .ret() // Return .function:: u64>() }; @@ -1691,18 +1742,21 @@ fn test_jit_add_function() { } #[cfg(feature = "std")] -#[test] +#[test] fn test_jit_constant_function() { // Test that we can create a function that returns a constant let jit_func = unsafe { Riscv64InstructionBuilder::new() - .lui(reg::A0, 0x12345) // Load upper immediate - .addi(reg::A0, reg::A0, 0x678) // Add lower bits - .ret() // Return + .lui(reg::A0, 0x12345) // Load upper immediate + .addi(reg::A0, reg::A0, 0x678) // Add lower bits + .ret() // Return .function:: u64>() }; - assert!(jit_func.is_ok(), "JIT constant function creation should succeed"); + assert!( + jit_func.is_ok(), + "JIT constant function creation should succeed" + ); } #[cfg(feature = "std")] @@ -1718,7 +1772,10 @@ fn test_jit_function_chaining() { .function:: u64>() }; - assert!(result.is_ok(), "Chained JIT function creation should succeed"); + assert!( + result.is_ok(), + "Chained JIT function creation should succeed" + ); } #[cfg(feature = "std")] @@ -1730,8 +1787,9 @@ fn test_natural_call_syntax() { .addi(reg::A0, reg::ZERO, 42) .ret() .function:: u64>() - }.expect("Failed to create function"); - + } + .expect("Failed to create function"); + // Only execute on RISC-V to avoid SIGILL #[cfg(target_arch = "riscv64")] { @@ -1741,19 +1799,20 @@ fn test_natural_call_syntax() { // Four arguments - THE ORIGINAL PROBLEM IS SOLVED! let _func4 = unsafe { Riscv64InstructionBuilder::new() - .add(reg::A0, reg::A0, reg::A1) // a0 += a1 - .add(reg::A0, reg::A0, reg::A2) // a0 += a2 - .add(reg::A0, reg::A0, reg::A3) // a0 += a3 + .add(reg::A0, reg::A0, reg::A1) // a0 += a1 + .add(reg::A0, reg::A0, reg::A2) // a0 += a2 + .add(reg::A0, reg::A0, reg::A3) // a0 += a3 .ret() .function:: u64>() - }.expect("Failed to create function"); - + } + .expect("Failed to create function"); + // Beautiful natural function call syntax! (Only execute on RISC-V) // Seven arguments - ultimate power demonstration let _func7 = unsafe { Riscv64InstructionBuilder::new() - .add(reg::A0, reg::A0, reg::A1) // Sum all arguments + .add(reg::A0, reg::A0, reg::A1) // Sum all arguments .add(reg::A0, reg::A0, reg::A2) .add(reg::A0, reg::A0, reg::A3) .add(reg::A0, reg::A0, reg::A4) @@ -1761,15 +1820,16 @@ fn test_natural_call_syntax() { .add(reg::A0, reg::A0, reg::A6) .ret() .function:: u64>() - }.expect("Failed to create function"); - + } + .expect("Failed to create function"); + #[cfg(target_arch = "riscv64")] { let r4 = _func4.call(10u64, 20u64, 30u64, 40u64); let r8 = _func7.call(1u64, 2u64, 3u64, 4u64, 5u64, 6u64, 7u64); - - assert_eq!(r4, 100); // 10 + 20 + 30 + 40 = 100 - assert_eq!(r8, 28); // 1+2+3+4+5+6+7 = 28 + + assert_eq!(r4, 100); // 10 + 20 + 30 + 40 = 100 + assert_eq!(r8, 28); // 1+2+3+4+5+6+7 = 28 } } @@ -1778,69 +1838,69 @@ fn test_natural_call_syntax() { mod register_tracking_tests { use super::*; use crate::common::InstructionBuilder; - + #[test] fn test_basic_r_type_tracking() { let mut builder = Riscv64InstructionBuilder::new(); builder.add(reg::T0, reg::T1, reg::T2); - + let usage = builder.register_usage(); - + // Check written registers let written = usage.written_registers(); assert_eq!(written.len(), 1); assert!(usage.contains_written_register(®::T0)); - + // Check read registers let read = usage.read_registers(); assert_eq!(read.len(), 2); assert!(usage.contains_read_register(®::T1)); assert!(usage.contains_read_register(®::T2)); - + // Check total usage assert_eq!(usage.register_count(), 3); assert!(usage.has_used_registers()); } - + #[test] fn test_i_type_tracking() { let mut builder = Riscv64InstructionBuilder::new(); builder.addi(reg::A0, reg::SP, 16); - + let usage = builder.register_usage(); - + // A0 is written, SP is read assert!(usage.contains_written_register(®::A0)); assert!(usage.contains_read_register(®::SP)); assert_eq!(usage.register_count(), 2); - + // SP is callee-saved, so stack frame needed if written to assert!(!usage.needs_stack_frame()); // SP only read, not written } - + #[test] fn test_stack_frame_detection() { let mut builder = Riscv64InstructionBuilder::new(); - + // Only caller-saved registers builder.add(reg::T0, reg::T1, reg::T2); assert!(!builder.register_usage().needs_stack_frame()); - + // Add callee-saved write builder.add(reg::S0, reg::T0, reg::T1); assert!(builder.register_usage().needs_stack_frame()); } - + #[test] fn test_load_store_tracking() { let mut builder = Riscv64InstructionBuilder::new(); - + // Load: rd written, rs1 read builder.ld(reg::T0, reg::SP, 8); let usage = builder.register_usage(); assert!(usage.contains_written_register(®::T0)); assert!(usage.contains_read_register(®::SP)); - + // Store: rs1 and rs2 read, nothing written builder.clear(); builder.sd(reg::SP, reg::T1, -16); @@ -1850,24 +1910,24 @@ mod register_tracking_tests { assert!(usage.contains_read_register(®::SP)); assert!(usage.contains_read_register(®::T1)); } - + #[test] fn test_m_extension_tracking() { let mut builder = Riscv64InstructionBuilder::new(); builder.mul(reg::A0, reg::A1, reg::A2); - + let usage = builder.register_usage(); assert!(usage.contains_written_register(®::A0)); assert!(usage.contains_read_register(®::A1)); assert!(usage.contains_read_register(®::A2)); assert_eq!(usage.register_count(), 3); } - - #[test] + + #[test] fn test_branch_tracking() { let mut builder = Riscv64InstructionBuilder::new(); builder.beq(reg::T0, reg::T1, 100); - + let usage = builder.register_usage(); // Branch instructions only read, don't write assert_eq!(usage.written_registers().len(), 0); @@ -1875,100 +1935,100 @@ mod register_tracking_tests { assert!(usage.contains_read_register(®::T0)); assert!(usage.contains_read_register(®::T1)); } - + #[test] fn test_u_type_tracking() { let mut builder = Riscv64InstructionBuilder::new(); builder.lui(reg::T0, 0x12345); - + let usage = builder.register_usage(); // U-type only writes to rd, no reads assert_eq!(usage.written_registers().len(), 1); assert_eq!(usage.read_registers().len(), 0); assert!(usage.contains_written_register(®::T0)); } - + #[test] fn test_j_type_tracking() { let mut builder = Riscv64InstructionBuilder::new(); builder.jal(reg::RA, 1000); - + let usage = builder.register_usage(); // JAL writes to rd (return address) assert_eq!(usage.written_registers().len(), 1); assert!(usage.contains_written_register(®::RA)); } - + #[test] fn test_csr_tracking() { let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::T0, csr::MSTATUS, reg::T1); - + let usage = builder.register_usage(); // CSR instructions: rd written, rs1 read assert!(usage.contains_written_register(®::T0)); assert!(usage.contains_read_register(®::T1)); assert_eq!(usage.register_count(), 2); } - + #[test] fn test_complex_function_tracking() { let mut builder = Riscv64InstructionBuilder::new(); - + // Complex function with mixed register usage builder - .addi(reg::SP, reg::SP, -16) // SP written & read (stack allocation) - .sd(reg::SP, reg::S0, 8) // SP, S0 read (save S0) + .addi(reg::SP, reg::SP, -16) // SP written & read (stack allocation) + .sd(reg::SP, reg::S0, 8) // SP, S0 read (save S0) .add(reg::S0, reg::A0, reg::A1) // S0 written, A0, A1 read .mul(reg::T0, reg::S0, reg::A2) // T0 written, S0, A2 read - .ld(reg::S0, reg::SP, 8) // S0 written, SP read (restore S0) - .addi(reg::SP, reg::SP, 16) // SP written & read (stack deallocation) + .ld(reg::S0, reg::SP, 8) // S0 written, SP read (restore S0) + .addi(reg::SP, reg::SP, 16) // SP written & read (stack deallocation) .add(reg::A0, reg::T0, reg::ZERO); // A0 written, T0, ZERO read - + let usage = builder.register_usage(); - + // Verify comprehensive tracking assert!(usage.has_used_registers()); assert!(usage.needs_stack_frame()); // SP and S0 are written - + let written = usage.written_registers(); let read = usage.read_registers(); - + // Check critical registers are tracked assert!(usage.contains_written_register(®::SP)); assert!(usage.contains_written_register(®::S0)); assert!(usage.contains_written_register(®::T0)); assert!(usage.contains_written_register(®::A0)); - - assert!(usage.contains_read_register(®::A0)); // Original A0 value + + assert!(usage.contains_read_register(®::A0)); // Original A0 value assert!(usage.contains_read_register(®::A1)); assert!(usage.contains_read_register(®::A2)); - + // Verify ABI classification let caller_saved_written = usage.caller_saved_written(); let callee_saved_written = usage.callee_saved_written(); - + assert!(!caller_saved_written.is_empty()); assert!(!callee_saved_written.is_empty()); - + println!("Complex function register usage: {}", usage); } - + #[test] fn test_register_reuse_tracking() { let mut builder = Riscv64InstructionBuilder::new(); - + // Same register used multiple times in different roles builder - .addi(reg::T0, reg::ZERO, 10) // T0 written, ZERO read + .addi(reg::T0, reg::ZERO, 10) // T0 written, ZERO read .add(reg::T1, reg::T0, reg::T0); // T1 written, T0 read twice - + let usage = builder.register_usage(); - + // T0 appears as both written and read assert!(usage.contains_written_register(®::T0)); assert!(usage.contains_read_register(®::T0)); - + // But should only count once in total let used = usage.used_registers(); let t0_count = used.iter().filter(|&&r| r == reg::T0).count(); @@ -1984,7 +2044,7 @@ fn test_all_m_mode_csr_addresses() { assert_eq!(csr::MIMPID.value(), 0xf13); assert_eq!(csr::MHARTID.value(), 0xf14); assert_eq!(csr::MCONFIGPTR.value(), 0xf15); - + // Machine Trap Setup assert_eq!(csr::MSTATUS.value(), 0x300); assert_eq!(csr::MISA.value(), 0x301); @@ -1994,7 +2054,7 @@ fn test_all_m_mode_csr_addresses() { assert_eq!(csr::MTVEC.value(), 0x305); assert_eq!(csr::MCOUNTEREN.value(), 0x306); assert_eq!(csr::MSTATUSH.value(), 0x310); - + // Machine Trap Handling assert_eq!(csr::MSCRATCH.value(), 0x340); assert_eq!(csr::MEPC.value(), 0x341); @@ -2003,13 +2063,13 @@ fn test_all_m_mode_csr_addresses() { assert_eq!(csr::MIP.value(), 0x344); assert_eq!(csr::MTINST.value(), 0x34a); assert_eq!(csr::MTVAL2.value(), 0x34b); - + // Machine Configuration assert_eq!(csr::MENVCFG.value(), 0x30a); assert_eq!(csr::MENVCFGH.value(), 0x31a); assert_eq!(csr::MSECCFG.value(), 0x747); assert_eq!(csr::MSECCFGH.value(), 0x757); - + // Machine Memory Protection - Configuration assert_eq!(csr::PMPCFG0.value(), 0x3a0); assert_eq!(csr::PMPCFG1.value(), 0x3a1); @@ -2027,27 +2087,27 @@ fn test_all_m_mode_csr_addresses() { assert_eq!(csr::PMPCFG13.value(), 0x3ad); assert_eq!(csr::PMPCFG14.value(), 0x3ae); assert_eq!(csr::PMPCFG15.value(), 0x3af); - + // Machine Memory Protection - Address (sample checks) assert_eq!(csr::PMPADDR0.value(), 0x3b0); assert_eq!(csr::PMPADDR1.value(), 0x3b1); assert_eq!(csr::PMPADDR15.value(), 0x3bf); assert_eq!(csr::PMPADDR31.value(), 0x3cf); assert_eq!(csr::PMPADDR63.value(), 0x3ef); - + // Machine Counter/Timers assert_eq!(csr::MCYCLE.value(), 0xb00); assert_eq!(csr::MINSTRET.value(), 0xb02); assert_eq!(csr::MHPMCOUNTER3.value(), 0xb03); assert_eq!(csr::MHPMCOUNTER4.value(), 0xb04); assert_eq!(csr::MHPMCOUNTER31.value(), 0xb1f); - + // Machine Counter/Timers - High assert_eq!(csr::MCYCLEH.value(), 0xb80); assert_eq!(csr::MINSTRETH.value(), 0xb82); assert_eq!(csr::MHPMCOUNTER3H.value(), 0xb83); assert_eq!(csr::MHPMCOUNTER31H.value(), 0xb9f); - + // Machine Counter Setup assert_eq!(csr::MCOUNTINHIBIT.value(), 0x320); assert_eq!(csr::MHPMEVENT3.value(), 0x323); @@ -2058,7 +2118,7 @@ fn test_all_m_mode_csr_addresses() { #[test] fn test_m_mode_csr_usage() { let mut builder = Riscv64InstructionBuilder::new(); - + // Test a selection of M-mode CSRs to verify they can be used builder.csrr(reg::X1, csr::MVENDORID); builder.csrr(reg::X2, csr::MARCHID); @@ -2076,10 +2136,10 @@ fn test_m_mode_csr_usage() { builder.csrr(reg::X14, csr::MHPMCOUNTER3); builder.csrr(reg::X15, csr::MCOUNTINHIBIT); builder.csrr(reg::X16, csr::MHPMEVENT3); - - let instructions = builder.instructions(); + + let instructions = builder.instructions().unwrap(); assert_eq!(instructions.len(), 16); - + // Verify all instructions are properly encoded for (i, instr) in instructions.iter().enumerate() { assert!(instr.value() != 0, "Instruction {} should be non-zero", i); @@ -2091,749 +2151,1289 @@ fn test_m_mode_csr_usage() { fn test_binary_correctness_m_mode_csrs() { // Test all M-mode CSRs with GNU assembler comparison // Using CSRRW x0, csr, x0 pattern as requested - + // Machine Information Registers let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MVENDORID, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mvendorid, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mvendorid, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MARCHID, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, marchid, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, marchid, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MIMPID, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mimpid, x0\n"); - + compare_instruction(builder.instructions().unwrap()[0], "csrrw x0, mimpid, x0\n"); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MCONFIGPTR, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mconfigptr, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mconfigptr, x0\n", + ); + // Machine Trap Setup let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MCOUNTEREN, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mcounteren, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mcounteren, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MSTATUSH, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mstatush, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mstatush, x0\n", + ); + // Machine Trap Handling let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MTINST, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mtinst, x0\n"); - + compare_instruction(builder.instructions().unwrap()[0], "csrrw x0, mtinst, x0\n"); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MTVAL2, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mtval2, x0\n"); - + compare_instruction(builder.instructions().unwrap()[0], "csrrw x0, mtval2, x0\n"); + // Machine Configuration let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MENVCFG, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, menvcfg, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, menvcfg, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MENVCFGH, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, menvcfgh, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, menvcfgh, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MSECCFG, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mseccfg, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mseccfg, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MSECCFGH, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mseccfgh, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mseccfgh, x0\n", + ); + // Physical Memory Protection - Configuration let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::PMPCFG0, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, pmpcfg0, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, pmpcfg0, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::PMPCFG1, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, pmpcfg1, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, pmpcfg1, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::PMPCFG2, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, pmpcfg2, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, pmpcfg2, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::PMPCFG3, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, pmpcfg3, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, pmpcfg3, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::PMPCFG4, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, pmpcfg4, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, pmpcfg4, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::PMPCFG5, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, pmpcfg5, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, pmpcfg5, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::PMPCFG6, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, pmpcfg6, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, pmpcfg6, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::PMPCFG7, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, pmpcfg7, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, pmpcfg7, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::PMPCFG8, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, pmpcfg8, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, pmpcfg8, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::PMPCFG9, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, pmpcfg9, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, pmpcfg9, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::PMPCFG10, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, pmpcfg10, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, pmpcfg10, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::PMPCFG11, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, pmpcfg11, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, pmpcfg11, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::PMPCFG12, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, pmpcfg12, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, pmpcfg12, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::PMPCFG13, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, pmpcfg13, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, pmpcfg13, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::PMPCFG14, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, pmpcfg14, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, pmpcfg14, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::PMPCFG15, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, pmpcfg15, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, pmpcfg15, x0\n", + ); + // Physical Memory Protection - Address (test all 64) let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::PMPADDR0, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, pmpaddr0, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, pmpaddr0, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::PMPADDR1, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, pmpaddr1, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, pmpaddr1, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::PMPADDR2, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, pmpaddr2, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, pmpaddr2, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::PMPADDR3, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, pmpaddr3, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, pmpaddr3, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::PMPADDR4, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, pmpaddr4, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, pmpaddr4, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::PMPADDR5, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, pmpaddr5, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, pmpaddr5, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::PMPADDR6, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, pmpaddr6, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, pmpaddr6, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::PMPADDR7, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, pmpaddr7, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, pmpaddr7, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::PMPADDR8, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, pmpaddr8, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, pmpaddr8, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::PMPADDR9, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, pmpaddr9, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, pmpaddr9, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::PMPADDR10, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, pmpaddr10, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, pmpaddr10, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::PMPADDR11, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, pmpaddr11, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, pmpaddr11, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::PMPADDR12, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, pmpaddr12, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, pmpaddr12, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::PMPADDR13, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, pmpaddr13, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, pmpaddr13, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::PMPADDR14, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, pmpaddr14, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, pmpaddr14, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::PMPADDR15, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, pmpaddr15, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, pmpaddr15, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::PMPADDR16, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, pmpaddr16, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, pmpaddr16, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::PMPADDR17, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, pmpaddr17, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, pmpaddr17, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::PMPADDR18, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, pmpaddr18, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, pmpaddr18, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::PMPADDR19, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, pmpaddr19, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, pmpaddr19, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::PMPADDR20, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, pmpaddr20, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, pmpaddr20, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::PMPADDR21, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, pmpaddr21, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, pmpaddr21, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::PMPADDR22, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, pmpaddr22, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, pmpaddr22, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::PMPADDR23, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, pmpaddr23, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, pmpaddr23, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::PMPADDR24, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, pmpaddr24, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, pmpaddr24, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::PMPADDR25, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, pmpaddr25, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, pmpaddr25, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::PMPADDR26, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, pmpaddr26, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, pmpaddr26, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::PMPADDR27, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, pmpaddr27, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, pmpaddr27, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::PMPADDR28, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, pmpaddr28, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, pmpaddr28, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::PMPADDR29, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, pmpaddr29, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, pmpaddr29, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::PMPADDR30, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, pmpaddr30, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, pmpaddr30, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::PMPADDR31, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, pmpaddr31, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, pmpaddr31, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::PMPADDR32, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, pmpaddr32, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, pmpaddr32, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::PMPADDR33, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, pmpaddr33, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, pmpaddr33, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::PMPADDR34, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, pmpaddr34, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, pmpaddr34, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::PMPADDR35, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, pmpaddr35, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, pmpaddr35, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::PMPADDR36, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, pmpaddr36, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, pmpaddr36, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::PMPADDR37, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, pmpaddr37, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, pmpaddr37, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::PMPADDR38, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, pmpaddr38, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, pmpaddr38, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::PMPADDR39, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, pmpaddr39, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, pmpaddr39, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::PMPADDR40, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, pmpaddr40, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, pmpaddr40, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::PMPADDR41, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, pmpaddr41, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, pmpaddr41, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::PMPADDR42, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, pmpaddr42, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, pmpaddr42, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::PMPADDR43, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, pmpaddr43, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, pmpaddr43, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::PMPADDR44, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, pmpaddr44, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, pmpaddr44, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::PMPADDR45, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, pmpaddr45, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, pmpaddr45, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::PMPADDR46, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, pmpaddr46, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, pmpaddr46, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::PMPADDR47, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, pmpaddr47, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, pmpaddr47, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::PMPADDR48, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, pmpaddr48, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, pmpaddr48, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::PMPADDR49, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, pmpaddr49, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, pmpaddr49, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::PMPADDR50, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, pmpaddr50, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, pmpaddr50, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::PMPADDR51, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, pmpaddr51, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, pmpaddr51, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::PMPADDR52, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, pmpaddr52, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, pmpaddr52, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::PMPADDR53, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, pmpaddr53, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, pmpaddr53, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::PMPADDR54, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, pmpaddr54, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, pmpaddr54, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::PMPADDR55, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, pmpaddr55, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, pmpaddr55, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::PMPADDR56, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, pmpaddr56, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, pmpaddr56, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::PMPADDR57, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, pmpaddr57, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, pmpaddr57, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::PMPADDR58, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, pmpaddr58, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, pmpaddr58, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::PMPADDR59, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, pmpaddr59, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, pmpaddr59, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::PMPADDR60, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, pmpaddr60, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, pmpaddr60, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::PMPADDR61, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, pmpaddr61, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, pmpaddr61, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::PMPADDR62, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, pmpaddr62, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, pmpaddr62, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::PMPADDR63, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, pmpaddr63, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, pmpaddr63, x0\n", + ); + // Machine Counter/Timers let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MCYCLE, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mcycle, x0\n"); - + compare_instruction(builder.instructions().unwrap()[0], "csrrw x0, mcycle, x0\n"); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MINSTRET, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, minstret, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, minstret, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MHPMCOUNTER3, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mhpmcounter3, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mhpmcounter3, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MHPMCOUNTER4, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mhpmcounter4, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mhpmcounter4, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MHPMCOUNTER5, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mhpmcounter5, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mhpmcounter5, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MHPMCOUNTER6, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mhpmcounter6, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mhpmcounter6, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MHPMCOUNTER7, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mhpmcounter7, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mhpmcounter7, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MHPMCOUNTER8, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mhpmcounter8, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mhpmcounter8, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MHPMCOUNTER9, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mhpmcounter9, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mhpmcounter9, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MHPMCOUNTER10, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mhpmcounter10, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mhpmcounter10, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MHPMCOUNTER11, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mhpmcounter11, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mhpmcounter11, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MHPMCOUNTER12, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mhpmcounter12, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mhpmcounter12, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MHPMCOUNTER13, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mhpmcounter13, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mhpmcounter13, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MHPMCOUNTER14, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mhpmcounter14, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mhpmcounter14, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MHPMCOUNTER15, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mhpmcounter15, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mhpmcounter15, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MHPMCOUNTER16, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mhpmcounter16, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mhpmcounter16, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MHPMCOUNTER17, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mhpmcounter17, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mhpmcounter17, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MHPMCOUNTER18, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mhpmcounter18, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mhpmcounter18, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MHPMCOUNTER19, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mhpmcounter19, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mhpmcounter19, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MHPMCOUNTER20, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mhpmcounter20, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mhpmcounter20, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MHPMCOUNTER21, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mhpmcounter21, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mhpmcounter21, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MHPMCOUNTER22, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mhpmcounter22, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mhpmcounter22, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MHPMCOUNTER23, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mhpmcounter23, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mhpmcounter23, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MHPMCOUNTER24, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mhpmcounter24, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mhpmcounter24, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MHPMCOUNTER25, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mhpmcounter25, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mhpmcounter25, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MHPMCOUNTER26, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mhpmcounter26, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mhpmcounter26, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MHPMCOUNTER27, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mhpmcounter27, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mhpmcounter27, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MHPMCOUNTER28, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mhpmcounter28, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mhpmcounter28, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MHPMCOUNTER29, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mhpmcounter29, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mhpmcounter29, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MHPMCOUNTER30, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mhpmcounter30, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mhpmcounter30, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MHPMCOUNTER31, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mhpmcounter31, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mhpmcounter31, x0\n", + ); + // Machine Counter/Timers - High (for RV32) let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MCYCLEH, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mcycleh, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mcycleh, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MINSTRETH, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, minstreth, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, minstreth, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MHPMCOUNTER3H, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mhpmcounter3h, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mhpmcounter3h, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MHPMCOUNTER4H, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mhpmcounter4h, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mhpmcounter4h, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MHPMCOUNTER5H, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mhpmcounter5h, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mhpmcounter5h, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MHPMCOUNTER6H, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mhpmcounter6h, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mhpmcounter6h, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MHPMCOUNTER7H, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mhpmcounter7h, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mhpmcounter7h, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MHPMCOUNTER8H, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mhpmcounter8h, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mhpmcounter8h, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MHPMCOUNTER9H, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mhpmcounter9h, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mhpmcounter9h, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MHPMCOUNTER10H, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mhpmcounter10h, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mhpmcounter10h, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MHPMCOUNTER11H, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mhpmcounter11h, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mhpmcounter11h, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MHPMCOUNTER12H, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mhpmcounter12h, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mhpmcounter12h, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MHPMCOUNTER13H, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mhpmcounter13h, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mhpmcounter13h, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MHPMCOUNTER14H, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mhpmcounter14h, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mhpmcounter14h, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MHPMCOUNTER15H, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mhpmcounter15h, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mhpmcounter15h, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MHPMCOUNTER16H, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mhpmcounter16h, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mhpmcounter16h, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MHPMCOUNTER17H, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mhpmcounter17h, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mhpmcounter17h, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MHPMCOUNTER18H, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mhpmcounter18h, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mhpmcounter18h, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MHPMCOUNTER19H, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mhpmcounter19h, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mhpmcounter19h, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MHPMCOUNTER20H, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mhpmcounter20h, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mhpmcounter20h, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MHPMCOUNTER21H, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mhpmcounter21h, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mhpmcounter21h, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MHPMCOUNTER22H, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mhpmcounter22h, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mhpmcounter22h, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MHPMCOUNTER23H, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mhpmcounter23h, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mhpmcounter23h, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MHPMCOUNTER24H, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mhpmcounter24h, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mhpmcounter24h, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MHPMCOUNTER25H, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mhpmcounter25h, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mhpmcounter25h, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MHPMCOUNTER26H, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mhpmcounter26h, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mhpmcounter26h, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MHPMCOUNTER27H, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mhpmcounter27h, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mhpmcounter27h, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MHPMCOUNTER28H, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mhpmcounter28h, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mhpmcounter28h, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MHPMCOUNTER29H, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mhpmcounter29h, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mhpmcounter29h, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MHPMCOUNTER30H, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mhpmcounter30h, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mhpmcounter30h, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MHPMCOUNTER31H, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mhpmcounter31h, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mhpmcounter31h, x0\n", + ); + // Machine Counter Setup let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MCOUNTINHIBIT, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mcountinhibit, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mcountinhibit, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MHPMEVENT3, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mhpmevent3, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mhpmevent3, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MHPMEVENT4, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mhpmevent4, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mhpmevent4, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MHPMEVENT5, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mhpmevent5, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mhpmevent5, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MHPMEVENT6, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mhpmevent6, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mhpmevent6, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MHPMEVENT7, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mhpmevent7, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mhpmevent7, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MHPMEVENT8, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mhpmevent8, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mhpmevent8, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MHPMEVENT9, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mhpmevent9, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mhpmevent9, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MHPMEVENT10, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mhpmevent10, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mhpmevent10, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MHPMEVENT11, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mhpmevent11, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mhpmevent11, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MHPMEVENT12, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mhpmevent12, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mhpmevent12, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MHPMEVENT13, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mhpmevent13, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mhpmevent13, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MHPMEVENT14, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mhpmevent14, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mhpmevent14, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MHPMEVENT15, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mhpmevent15, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mhpmevent15, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MHPMEVENT16, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mhpmevent16, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mhpmevent16, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MHPMEVENT17, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mhpmevent17, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mhpmevent17, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MHPMEVENT18, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mhpmevent18, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mhpmevent18, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MHPMEVENT19, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mhpmevent19, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mhpmevent19, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MHPMEVENT20, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mhpmevent20, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mhpmevent20, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MHPMEVENT21, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mhpmevent21, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mhpmevent21, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MHPMEVENT22, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mhpmevent22, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mhpmevent22, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MHPMEVENT23, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mhpmevent23, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mhpmevent23, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MHPMEVENT24, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mhpmevent24, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mhpmevent24, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MHPMEVENT25, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mhpmevent25, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mhpmevent25, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MHPMEVENT26, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mhpmevent26, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mhpmevent26, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MHPMEVENT27, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mhpmevent27, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mhpmevent27, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MHPMEVENT28, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mhpmevent28, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mhpmevent28, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MHPMEVENT29, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mhpmevent29, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mhpmevent29, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MHPMEVENT30, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mhpmevent30, x0\n"); - + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mhpmevent30, x0\n", + ); + let mut builder = Riscv64InstructionBuilder::new(); builder.csrrw(reg::X0, csr::MHPMEVENT31, reg::X0); - compare_instruction(builder.instructions()[0], "csrrw x0, mhpmevent31, x0\n"); + compare_instruction( + builder.instructions().unwrap()[0], + "csrrw x0, mhpmevent31, x0\n", + ); }