Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 18 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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<u8> {
Expand All @@ -167,7 +169,7 @@ fn generate_add_function(a: i16, b: i16) -> Vec<u8> {
}

// Builder pattern for complex logic
fn generate_csr_routine() -> Vec<u8> {
fn generate_csr_routine() -> Result<Vec<u8>, BuildError> {
let mut builder = Riscv64InstructionBuilder::new();

builder
Expand All @@ -176,15 +178,15 @@ fn generate_csr_routine() -> Vec<u8> {
.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())
}
```

### AArch64 Usage

```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)
Expand All @@ -197,14 +199,14 @@ fn generate_aarch64_add_function_macro() -> Vec<u8> {
}

// Builder pattern style
fn generate_aarch64_add_function() -> Vec<u8> {
fn generate_aarch64_add_function() -> Result<Vec<u8>, 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)
Expand All @@ -218,7 +220,7 @@ fn generate_aarch64_calculation_macro() -> Vec<u8> {
}

// More complex AArch64 example with immediate values (builder style)
fn generate_aarch64_calculation() -> Vec<u8> {
fn generate_aarch64_calculation() -> Result<Vec<u8>, BuildError> {
let mut builder = Aarch64InstructionBuilder::new();

builder
Expand All @@ -227,7 +229,7 @@ fn generate_aarch64_calculation() -> Vec<u8> {
.addi(reg::X0, reg::X0, 100) // Add 100 to result
.ret(); // Return

builder.instructions().to_bytes()
Ok(builder.instructions()?.to_bytes())
}
```

Expand Down Expand Up @@ -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();
Expand All @@ -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()
);

Expand Down
87 changes: 45 additions & 42 deletions examples/instruction_collection_merge.rs
Original file line number Diff line number Diff line change
@@ -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();
Expand All @@ -76,60 +76,63 @@ 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());
println!(" Caller-saved: {:?}", usage.caller_saved_registers());
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()
);
}
Loading