use core::fmt;
#[cfg(feature = "register-tracking")]
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 = "register-tracking")]
#[derive(Debug, Clone)]
pub struct InstructionCollectionWithUsage<I: Instruction, R: Register> {
instructions: InstructionCollection<I>,
register_usage: RegisterUsageInfo<R>,
}
#[cfg(feature = "register-tracking")]
impl<I: Instruction, R: Register> InstructionCollectionWithUsage<I, R> {
pub fn new(instructions: InstructionCollection<I>, register_usage: RegisterUsageInfo<R>) -> Self {
Self {
instructions,
register_usage,
}
}
pub fn from_parts(instructions: InstructionCollection<I>, register_usage: RegisterUsageInfo<R>) -> Self {
Self::new(instructions, register_usage)
}
pub fn instructions(&self) -> &InstructionCollection<I> {
&self.instructions
}
pub fn instructions_mut(&mut self) -> &mut InstructionCollection<I> {
&mut self.instructions
}
pub fn register_usage(&self) -> &RegisterUsageInfo<R> {
&self.register_usage
}
pub fn register_usage_mut(&mut self) -> &mut RegisterUsageInfo<R> {
&mut self.register_usage
}
pub fn into_parts(self) -> (InstructionCollection<I>, RegisterUsageInfo<R>) {
(self.instructions, self.register_usage)
}
pub fn into_instructions(self) -> InstructionCollection<I> {
self.instructions
}
pub fn append(&mut self, other: InstructionCollectionWithUsage<I, R>) {
self.instructions.append(other.instructions);
self.register_usage.merge(&other.register_usage);
}
pub fn extend_from(&mut self, other: &InstructionCollectionWithUsage<I, R>) {
self.instructions.extend_from_collection(&other.instructions);
self.register_usage.merge(&other.register_usage);
}
pub fn concat(mut self, other: InstructionCollectionWithUsage<I, R>) -> Self {
self.instructions.append(other.instructions);
self.register_usage.merge(&other.register_usage);
self
}
}
#[cfg(feature = "register-tracking")]
impl<I: Instruction, R: Register> core::ops::Add for InstructionCollectionWithUsage<I, R> {
type Output = InstructionCollectionWithUsage<I, R>;
fn add(self, other: InstructionCollectionWithUsage<I, R>) -> InstructionCollectionWithUsage<I, R> {
self.concat(other)
}
}
#[cfg(feature = "register-tracking")]
impl<I: Instruction, R: Register> core::ops::AddAssign for InstructionCollectionWithUsage<I, R> {
fn add_assign(&mut self, other: InstructionCollectionWithUsage<I, R>) {
self.append(other);
}
}
pub trait Instruction: Copy + Clone + fmt::Debug + fmt::Display {
fn value(&self) -> u64;
fn bytes(&self) -> Vec<u8>;
fn size(&self) -> usize;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum AbiClass {
CallerSaved,
CalleeSaved,
Special,
}
impl fmt::Display for AbiClass {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
AbiClass::CallerSaved => write!(f, "caller-saved"),
AbiClass::CalleeSaved => write!(f, "callee-saved"),
AbiClass::Special => write!(f, "special"),
}
}
}
pub trait Register: Copy + Clone + fmt::Debug + core::hash::Hash + Eq {
fn id(&self) -> u32;
fn abi_class(&self) -> AbiClass;
fn is_caller_saved(&self) -> bool {
self.abi_class() == AbiClass::CallerSaved
}
fn is_callee_saved(&self) -> bool {
self.abi_class() == AbiClass::CalleeSaved
}
fn is_special(&self) -> bool {
self.abi_class() == AbiClass::Special
}
}
pub trait InstructionBuilder<I: Instruction> {
type Register: Register;
fn new() -> Self;
fn instructions(&self) -> InstructionCollection<I>;
fn push(&mut self, instr: I);
fn clear(&mut self);
#[cfg(feature = "register-tracking")]
fn register_usage(&self) -> &crate::common::register_usage::RegisterUsageInfo<Self::Register>;
#[cfg(feature = "register-tracking")]
fn register_usage_mut(&mut self) -> &mut crate::common::register_usage::RegisterUsageInfo<Self::Register>;
#[cfg(feature = "register-tracking")]
fn clear_register_usage(&mut self) {
self.register_usage_mut().clear();
}
#[cfg(feature = "std")]
unsafe fn function<F>(&self) -> Result<crate::common::jit::CallableJitFunction<F>, crate::common::jit::JitError>;
#[cfg(feature = "std")]
unsafe fn raw_function(&self) -> Result<crate::common::jit::RawCallableJitFunction, crate::common::jit::JitError>;
}
pub fn instructions_to_bytes<I: Instruction>(instructions: &[I]) -> Vec<u8> {
let mut result = Vec::new();
for instr in instructions {
result.extend_from_slice(&instr.bytes());
}
result
}
pub fn instructions_total_size<I: Instruction>(instructions: &[I]) -> usize {
instructions.iter().map(|i| i.size()).sum()
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct InstructionCollection<I: Instruction> {
instructions: Vec<I>,
}
impl<I: Instruction> InstructionCollection<I> {
pub fn new() -> Self {
Self {
instructions: Vec::new(),
}
}
pub fn from_vec(instructions: Vec<I>) -> Self {
Self { instructions }
}
pub fn from_slice(instructions: &[I]) -> Self {
Self {
instructions: instructions.to_vec(),
}
}
pub fn as_slice(&self) -> &[I] {
&self.instructions
}
pub fn as_mut_slice(&mut self) -> &mut [I] {
&mut self.instructions
}
pub fn to_bytes(&self) -> Vec<u8> {
instructions_to_bytes(&self.instructions)
}
pub fn total_size(&self) -> usize {
instructions_total_size(&self.instructions)
}
pub fn len(&self) -> usize {
self.instructions.len()
}
pub fn is_empty(&self) -> bool {
self.instructions.is_empty()
}
pub fn push(&mut self, instruction: I) {
self.instructions.push(instruction);
}
pub fn clear(&mut self) {
self.instructions.clear();
}
pub fn iter(&self) -> core::slice::Iter<'_, I> {
self.instructions.iter()
}
pub fn iter_mut(&mut self) -> core::slice::IterMut<'_, I> {
self.instructions.iter_mut()
}
pub fn into_vec(self) -> Vec<I> {
self.instructions
}
pub fn to_vec(&self) -> Vec<I> {
self.instructions.clone()
}
pub fn get(&self, index: usize) -> Option<&I> {
self.instructions.get(index)
}
pub fn get_mut(&mut self, index: usize) -> Option<&mut I> {
self.instructions.get_mut(index)
}
pub fn append(&mut self, mut other: InstructionCollection<I>) {
self.instructions.append(&mut other.instructions);
}
pub fn extend_from_collection(&mut self, other: &InstructionCollection<I>) {
self.instructions.extend_from_slice(&other.instructions);
}
pub fn concat(mut self, mut other: InstructionCollection<I>) -> Self {
self.instructions.append(&mut other.instructions);
self
}
}
impl<I: Instruction> Default for InstructionCollection<I> {
fn default() -> Self {
Self::new()
}
}
impl<I: Instruction> From<Vec<I>> for InstructionCollection<I> {
fn from(instructions: Vec<I>) -> Self {
Self::from_vec(instructions)
}
}
impl<I: Instruction> From<&[I]> for InstructionCollection<I> {
fn from(instructions: &[I]) -> Self {
Self::from_slice(instructions)
}
}
impl<I: Instruction> AsRef<[I]> for InstructionCollection<I> {
fn as_ref(&self) -> &[I] {
&self.instructions
}
}
impl<I: Instruction> AsMut<[I]> for InstructionCollection<I> {
fn as_mut(&mut self) -> &mut [I] {
&mut self.instructions
}
}
impl<I: Instruction> core::ops::Index<usize> for InstructionCollection<I> {
type Output = I;
fn index(&self, index: usize) -> &Self::Output {
&self.instructions[index]
}
}
impl<I: Instruction> core::ops::IndexMut<usize> for InstructionCollection<I> {
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
&mut self.instructions[index]
}
}
impl<I: Instruction> IntoIterator for InstructionCollection<I> {
type Item = I;
type IntoIter = <Vec<I> as IntoIterator>::IntoIter;
fn into_iter(self) -> Self::IntoIter {
self.instructions.into_iter()
}
}
impl<'a, I: Instruction> IntoIterator for &'a InstructionCollection<I> {
type Item = &'a I;
type IntoIter = core::slice::Iter<'a, I>;
fn into_iter(self) -> Self::IntoIter {
self.instructions.iter()
}
}
impl<'a, I: Instruction> IntoIterator for &'a mut InstructionCollection<I> {
type Item = &'a mut I;
type IntoIter = core::slice::IterMut<'a, I>;
fn into_iter(self) -> Self::IntoIter {
self.instructions.iter_mut()
}
}
impl<I: Instruction> core::ops::Deref for InstructionCollection<I> {
type Target = [I];
fn deref(&self) -> &Self::Target {
&self.instructions
}
}
impl<I: Instruction> core::ops::DerefMut for InstructionCollection<I> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.instructions
}
}
impl<I: Instruction> core::ops::Add for InstructionCollection<I> {
type Output = InstructionCollection<I>;
fn add(self, other: InstructionCollection<I>) -> InstructionCollection<I> {
self.concat(other)
}
}
impl<I: Instruction> core::ops::AddAssign for InstructionCollection<I> {
fn add_assign(&mut self, other: InstructionCollection<I>) {
self.append(other);
}
}
pub trait InstructionCollectionExt<I: Instruction> {
fn to_bytes(&self) -> Vec<u8>;
fn total_size(&self) -> usize;
}
impl<I: Instruction> InstructionCollectionExt<I> for [I] {
fn to_bytes(&self) -> Vec<u8> {
instructions_to_bytes(self)
}
fn total_size(&self) -> usize {
instructions_total_size(self)
}
}
impl<I: Instruction> InstructionCollectionExt<I> for Vec<I> {
fn to_bytes(&self) -> Vec<u8> {
instructions_to_bytes(self)
}
fn total_size(&self) -> usize {
instructions_total_size(self)
}
}
pub trait ArchitectureEncoder<I: Instruction> {
fn encode(&self, opcode: u32, operands: &[u32]) -> I;
}
pub type BuildResult<I> = Result<I, BuildError>;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BuildError {
InvalidRegister(u32),
InvalidImmediate(i64),
UnsupportedInstruction,
InvalidOperands,
}
impl fmt::Display for BuildError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
BuildError::InvalidRegister(id) => write!(f, "Invalid register ID: {}", id),
BuildError::InvalidImmediate(val) => write!(f, "Invalid immediate value: {}", val),
BuildError::UnsupportedInstruction => write!(f, "Unsupported instruction"),
BuildError::InvalidOperands => write!(f, "Invalid operand combination"),
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for BuildError {}
#[cfg(feature = "std")]
pub mod jit {
use std::marker::PhantomData;
use jit_allocator2::JitAllocator;
pub struct RawCallableJitFunction {
_allocator: Box<JitAllocator>,
exec_ptr: *const u8,
}
impl RawCallableJitFunction {
pub fn new(code: &[u8]) -> Result<Self, JitError> {
let mut allocator = JitAllocator::new(Default::default());
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());
}
Ok(RawCallableJitFunction {
_allocator: allocator,
exec_ptr,
})
}
pub unsafe fn as_fn<F>(&self) -> F {
std::mem::transmute_copy(&self.exec_ptr)
}
}
pub struct CallableJitFunction<F> {
_allocator: Box<JitAllocator>,
exec_ptr: *const u8,
_phantom: PhantomData<F>,
}
impl<F> CallableJitFunction<F> {
pub fn new(code: &[u8]) -> Result<Self, JitError> {
let mut allocator = JitAllocator::new(Default::default());
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());
}
Ok(CallableJitFunction {
_allocator: allocator,
exec_ptr,
_phantom: PhantomData,
})
}
}
impl<R> CallableJitFunction<fn() -> R> {
pub fn call(&self) -> R {
let func: extern "C" fn() -> R = unsafe { std::mem::transmute_copy(&self.exec_ptr) };
func()
}
}
impl<A1, R> CallableJitFunction<fn(A1) -> R> {
pub fn call(&self, arg1: A1) -> R {
let func: extern "C" fn(A1) -> R = unsafe { std::mem::transmute_copy(&self.exec_ptr) };
func(arg1)
}
}
impl<A1, A2, R> CallableJitFunction<fn(A1, A2) -> R> {
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) };
func(arg1, arg2)
}
}
impl<A1, A2, A3, R> CallableJitFunction<fn(A1, A2, A3) -> R> {
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) };
func(arg1, arg2, arg3)
}
}
impl<A1, A2, A3, A4, R> CallableJitFunction<fn(A1, A2, A3, A4) -> R> {
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) };
func(arg1, arg2, arg3, arg4)
}
}
impl<A1, A2, A3, A4, A5, R> CallableJitFunction<fn(A1, A2, A3, A4, A5) -> R> {
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) };
func(arg1, arg2, arg3, arg4, arg5)
}
}
impl<A1, A2, A3, A4, A5, A6, R> CallableJitFunction<fn(A1, A2, A3, A4, A5, A6) -> R> {
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) };
func(arg1, arg2, arg3, arg4, arg5, arg6)
}
}
impl<A1, A2, A3, A4, A5, A6, A7, R> CallableJitFunction<fn(A1, A2, A3, A4, A5, A6, A7) -> R> {
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)
}
}
#[derive(Debug)]
pub enum JitError {
AllocationFailed(jit_allocator2::Error),
}
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),
}
}
}
impl std::error::Error for JitError {}
}
#[cfg(test)]
mod tests {
use super::*;
#[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<u8> {
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),
]);
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),
]);
collection1.extend_from_collection(&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));
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 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 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),
]);
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)]);
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 collection2 = InstructionCollection::<TestInstruction>::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::<TestInstruction>::new();
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));
}
}
#[cfg(all(test, feature = "register-tracking"))]
mod register_tracking_tests {
use super::*;
#[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<u8> {
self.0.to_le_bytes().to_vec()
}
fn size(&self) -> usize {
4
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
enum TestRegister {
T0, T1, S0, S1, SP, FP, }
impl Register for TestRegister {
fn id(&self) -> u32 {
match self {
TestRegister::T0 => 0,
TestRegister::T1 => 1,
TestRegister::S0 => 2,
TestRegister::S1 => 3,
TestRegister::SP => 4,
TestRegister::FP => 5,
}
}
fn abi_class(&self) -> AbiClass {
match self {
TestRegister::T0 | TestRegister::T1 => AbiClass::CallerSaved,
TestRegister::S0 | TestRegister::S1 => AbiClass::CalleeSaved,
TestRegister::SP | TestRegister::FP => AbiClass::Special,
}
}
}
#[test]
fn test_instruction_collection_with_usage_new() {
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));
}
#[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));
}
#[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));
}
#[test]
fn test_instruction_collection_with_usage_multiple_merge() {
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);
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);
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);
let combined = prologue + main + epilogue;
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));
let usage = combined.register_usage();
assert_eq!(usage.register_count(), 4); assert!(usage.contains_register(&TestRegister::T0));
assert!(usage.contains_register(&TestRegister::T1));
assert!(usage.contains_register(&TestRegister::S0));
assert!(usage.contains_register(&TestRegister::SP));
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);
assert_eq!(tracked1.instructions().len(), 2);
assert_eq!(tracked1.register_usage().register_count(), 2);
assert_eq!(tracked2.instructions().len(), 1);
assert_eq!(tracked2.register_usage().register_count(), 1);
}
}