jit_assembler/common/
mod.rs

1//! Common types and traits shared across all target architectures.
2
3use core::fmt;
4
5/// Register usage tracking functionality
6#[cfg(feature = "register-tracking")]
7pub mod register_usage;
8
9#[cfg(feature = "std")]
10use std::vec::Vec;
11#[cfg(not(feature = "std"))]
12use alloc::vec::Vec;
13
14/// A machine instruction that can be encoded to bytes
15pub trait Instruction: Copy + Clone + fmt::Debug + fmt::Display {
16    /// Get the instruction as a 32-bit or 64-bit value
17    fn value(&self) -> u64;
18    
19    /// Get the instruction as bytes (little-endian)
20    fn bytes(&self) -> Vec<u8>;
21    
22    /// Get the size of this instruction in bytes
23    fn size(&self) -> usize;
24}
25    
26/// ABI classification for registers based on preservation requirements.
27/// 
28/// This simplified classification focuses on whether registers need to be
29/// preserved across function calls, which is the most critical information
30/// for JIT compilation and register allocation.
31#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
32pub enum AbiClass {
33    /// Caller-saved registers that don't need to be preserved across calls.
34    /// 
35    /// These registers can be freely used by JIT-compiled functions without
36    /// saving/restoring their values. Examples: argument registers, temp registers.
37    CallerSaved,
38    
39    /// Callee-saved registers that must be preserved across calls.
40    /// 
41    /// If a JIT-compiled function uses these registers, it must save their
42    /// values on entry and restore them before returning. Examples: saved registers.
43    CalleeSaved,
44    
45    /// Special-purpose registers with specific ABI requirements.
46    /// 
47    /// These registers have specific roles (stack pointer, frame pointer, zero register, etc.)
48    /// and require careful handling. Generally should be avoided in JIT code
49    /// unless specifically needed for their intended purpose.
50    Special,
51}
52
53impl fmt::Display for AbiClass {
54    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
55        match self {
56            AbiClass::CallerSaved => write!(f, "caller-saved"),
57            AbiClass::CalleeSaved => write!(f, "callee-saved"),
58            AbiClass::Special => write!(f, "special"),
59        }
60    }
61}
62
63/// A register identifier for a target architecture
64pub trait Register: Copy + Clone + fmt::Debug + core::hash::Hash + Eq {
65    /// Get the register number/identifier
66    fn id(&self) -> u32;
67    
68    /// Get the ABI classification for this register.
69    /// 
70    /// This method should return the appropriate `AbiClass` based on the
71    /// target architecture's calling convention.
72    /// 
73    /// # Example
74    /// 
75    /// ```rust,ignore
76    /// // For RISC-V
77    /// match self {
78    ///     Register::T0 | Register::T1 => AbiClass::CallerSaved,
79    ///     Register::S0 | Register::S1 => AbiClass::CalleeSaved,
80    ///     Register::SP | Register::FP => AbiClass::Other,
81    ///     // ...
82    /// }
83    /// ```
84    fn abi_class(&self) -> AbiClass;
85    
86    /// Check if this register is caller-saved.
87    /// 
88    /// Convenience method equivalent to `self.abi_class() == AbiClass::CallerSaved`.
89    fn is_caller_saved(&self) -> bool {
90        self.abi_class() == AbiClass::CallerSaved
91    }
92    
93    /// Check if this register is callee-saved.
94    /// 
95    /// Convenience method equivalent to `self.abi_class() == AbiClass::CalleeSaved`.
96    fn is_callee_saved(&self) -> bool {
97        self.abi_class() == AbiClass::CalleeSaved
98    }
99    
100    /// Check if this register is special-purpose.
101    /// 
102    /// Convenience method equivalent to `self.abi_class() == AbiClass::Special`.
103    fn is_special(&self) -> bool {
104        self.abi_class() == AbiClass::Special
105    }
106}
107
108/// An instruction builder for a specific architecture
109pub trait InstructionBuilder<I: Instruction> {
110    /// The register type used by this architecture
111    type Register: Register;
112    
113    /// Create a new instruction builder
114    fn new() -> Self;
115    
116    /// Get the generated instructions
117    fn instructions(&self) -> InstructionCollection<I>;
118    
119    /// Add an instruction to the builder
120    fn push(&mut self, instr: I);
121    
122    /// Clear all instructions
123    fn clear(&mut self);
124    
125    /// Get register usage information (register-tracking feature only)
126    /// 
127    /// This method returns information about which registers have been used
128    /// by the instructions in this builder, enabling register allocation
129    /// analysis and ABI compliance checking.
130    #[cfg(feature = "register-tracking")]
131    fn register_usage(&self) -> &crate::common::register_usage::RegisterUsageInfo<Self::Register>;
132    
133    /// Get mutable register usage information (register-tracking feature only)
134    /// 
135    /// This allows direct manipulation of the usage tracking, which can be
136    /// useful for advanced use cases or manual register tracking.
137    #[cfg(feature = "register-tracking")]
138    fn register_usage_mut(&mut self) -> &mut crate::common::register_usage::RegisterUsageInfo<Self::Register>;
139    
140    /// Clear register usage information (register-tracking feature only)
141    /// 
142    /// This resets the usage tracking to an empty state, which can be
143    /// useful when reusing a builder for multiple functions.
144    #[cfg(feature = "register-tracking")]
145    fn clear_register_usage(&mut self) {
146        self.register_usage_mut().clear();
147    }
148    
149    /// Create a JIT-compiled function from the assembled instructions (std-only)
150    /// 
151    /// This method converts the assembled instructions into executable machine code
152    /// that can be called directly as a function. The generic type parameter `F`
153    /// specifies the function signature.
154    /// 
155    /// # ABI Compatibility
156    /// 
157    /// While you specify a Rust function type like `fn() -> u64`, the actual JIT
158    /// code uses C ABI internally for stability across Rust versions. This conversion
159    /// is handled transparently by the `call()` methods.
160    /// 
161    /// # Limitations
162    /// 
163    /// Currently supports function signatures with up to 7 arguments. For functions
164    /// with more arguments or complex calling conventions, use manual function pointer
165    /// conversion.
166    /// 
167    /// # Safety
168    /// 
169    /// This function is unsafe because:
170    /// - It allocates executable memory
171    /// - It assumes the assembled code follows the correct C ABI
172    /// - The caller must ensure the function signature matches the actual code
173    /// - The assembled code must be valid for the target architecture
174    /// 
175    #[cfg(feature = "std")]
176    unsafe fn function<F>(&self) -> Result<crate::common::jit::CallableJitFunction<F>, crate::common::jit::JitError>;
177
178    /// Create a raw JIT-compiled function for manual type conversion (std-only)
179    /// 
180    /// This method is similar to `function()` but returns a type-erased function
181    /// that allows manual conversion to any function signature. Use this for
182    /// function signatures with more than 7 arguments or custom calling conventions.
183    /// 
184    /// # Safety
185    /// 
186    /// This function is unsafe because:
187    /// - It allocates executable memory  
188    /// - The caller must manually ensure type safety when calling `as_fn()`
189    /// - The assembled code must be valid for the target architecture
190    /// 
191    #[cfg(feature = "std")]
192    unsafe fn raw_function(&self) -> Result<crate::common::jit::RawCallableJitFunction, crate::common::jit::JitError>;
193}
194
195/// Convenience functions for instruction collections
196/// These functions work with any collection of instructions
197pub fn instructions_to_bytes<I: Instruction>(instructions: &[I]) -> Vec<u8> {
198    let mut result = Vec::new();
199    for instr in instructions {
200        result.extend_from_slice(&instr.bytes());
201    }
202    result
203}
204
205/// Get the total size in bytes of a collection of instructions
206pub fn instructions_total_size<I: Instruction>(instructions: &[I]) -> usize {
207    instructions.iter().map(|i| i.size()).sum()
208}
209
210/// A collection of instructions with convenient methods for byte manipulation
211#[derive(Debug, Clone, PartialEq, Eq)]
212pub struct InstructionCollection<I: Instruction> {
213    instructions: Vec<I>,
214}
215
216impl<I: Instruction> InstructionCollection<I> {
217    /// Create a new empty instruction collection
218    pub fn new() -> Self {
219        Self {
220            instructions: Vec::new(),
221        }
222    }
223    
224    /// Create from a vector of instructions
225    pub fn from_vec(instructions: Vec<I>) -> Self {
226        Self { instructions }
227    }
228    
229    /// Create from a slice of instructions
230    pub fn from_slice(instructions: &[I]) -> Self {
231        Self {
232            instructions: instructions.to_vec(),
233        }
234    }
235    
236    /// Get the instructions as a slice
237    pub fn as_slice(&self) -> &[I] {
238        &self.instructions
239    }
240    
241    /// Get the instructions as a mutable slice
242    pub fn as_mut_slice(&mut self) -> &mut [I] {
243        &mut self.instructions
244    }
245    
246    /// Convert instructions to a single byte vector
247    pub fn to_bytes(&self) -> Vec<u8> {
248        instructions_to_bytes(&self.instructions)
249    }
250    
251    /// Get the total size in bytes of all instructions
252    pub fn total_size(&self) -> usize {
253        instructions_total_size(&self.instructions)
254    }
255    
256    /// Get the number of instructions
257    pub fn len(&self) -> usize {
258        self.instructions.len()
259    }
260    
261    /// Check if the collection is empty
262    pub fn is_empty(&self) -> bool {
263        self.instructions.is_empty()
264    }
265    
266    /// Add an instruction to the collection
267    pub fn push(&mut self, instruction: I) {
268        self.instructions.push(instruction);
269    }
270    
271    /// Remove all instructions
272    pub fn clear(&mut self) {
273        self.instructions.clear();
274    }
275    
276    /// Get an iterator over the instructions
277    pub fn iter(&self) -> core::slice::Iter<'_, I> {
278        self.instructions.iter()
279    }
280    
281    /// Get a mutable iterator over the instructions
282    pub fn iter_mut(&mut self) -> core::slice::IterMut<'_, I> {
283        self.instructions.iter_mut()
284    }
285    
286    /// Convert to a Vec (consumes self)
287    pub fn into_vec(self) -> Vec<I> {
288        self.instructions
289    }
290    
291    /// Create a Vec by cloning the instructions
292    pub fn to_vec(&self) -> Vec<I> {
293        self.instructions.clone()
294    }
295    
296    /// Get a reference to the instruction at the given index
297    pub fn get(&self, index: usize) -> Option<&I> {
298        self.instructions.get(index)
299    }
300    
301    /// Get a mutable reference to the instruction at the given index
302    pub fn get_mut(&mut self, index: usize) -> Option<&mut I> {
303        self.instructions.get_mut(index)
304    }
305}
306
307impl<I: Instruction> Default for InstructionCollection<I> {
308    fn default() -> Self {
309        Self::new()
310    }
311}
312
313impl<I: Instruction> From<Vec<I>> for InstructionCollection<I> {
314    fn from(instructions: Vec<I>) -> Self {
315        Self::from_vec(instructions)
316    }
317}
318
319impl<I: Instruction> From<&[I]> for InstructionCollection<I> {
320    fn from(instructions: &[I]) -> Self {
321        Self::from_slice(instructions)
322    }
323}
324
325impl<I: Instruction> AsRef<[I]> for InstructionCollection<I> {
326    fn as_ref(&self) -> &[I] {
327        &self.instructions
328    }
329}
330
331impl<I: Instruction> AsMut<[I]> for InstructionCollection<I> {
332    fn as_mut(&mut self) -> &mut [I] {
333        &mut self.instructions
334    }
335}
336
337impl<I: Instruction> core::ops::Index<usize> for InstructionCollection<I> {
338    type Output = I;
339    
340    fn index(&self, index: usize) -> &Self::Output {
341        &self.instructions[index]
342    }
343}
344
345impl<I: Instruction> core::ops::IndexMut<usize> for InstructionCollection<I> {
346    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
347        &mut self.instructions[index]
348    }
349}
350
351impl<I: Instruction> IntoIterator for InstructionCollection<I> {
352    type Item = I;
353    type IntoIter = <Vec<I> as IntoIterator>::IntoIter;
354    
355    fn into_iter(self) -> Self::IntoIter {
356        self.instructions.into_iter()
357    }
358}
359
360impl<'a, I: Instruction> IntoIterator for &'a InstructionCollection<I> {
361    type Item = &'a I;
362    type IntoIter = core::slice::Iter<'a, I>;
363    
364    fn into_iter(self) -> Self::IntoIter {
365        self.instructions.iter()
366    }
367}
368
369impl<'a, I: Instruction> IntoIterator for &'a mut InstructionCollection<I> {
370    type Item = &'a mut I;
371    type IntoIter = core::slice::IterMut<'a, I>;
372    
373    fn into_iter(self) -> Self::IntoIter {
374        self.instructions.iter_mut()
375    }
376}
377
378impl<I: Instruction> core::ops::Deref for InstructionCollection<I> {
379    type Target = [I];
380    
381    fn deref(&self) -> &Self::Target {
382        &self.instructions
383    }
384}
385
386impl<I: Instruction> core::ops::DerefMut for InstructionCollection<I> {
387    fn deref_mut(&mut self) -> &mut Self::Target {
388        &mut self.instructions
389    }
390}
391
392/// Trait extension for instruction collections
393/// This allows you to call `.to_bytes()` and `.total_size()` directly on slices and vectors
394pub trait InstructionCollectionExt<I: Instruction> {
395    /// Convert instructions to a single byte vector
396    fn to_bytes(&self) -> Vec<u8>;
397    
398    /// Get the total size in bytes of all instructions
399    fn total_size(&self) -> usize;
400}
401
402impl<I: Instruction> InstructionCollectionExt<I> for [I] {
403    fn to_bytes(&self) -> Vec<u8> {
404        instructions_to_bytes(self)
405    }
406    
407    fn total_size(&self) -> usize {
408        instructions_total_size(self)
409    }
410}
411
412impl<I: Instruction> InstructionCollectionExt<I> for Vec<I> {
413    fn to_bytes(&self) -> Vec<u8> {
414        instructions_to_bytes(self)
415    }
416    
417    fn total_size(&self) -> usize {
418        instructions_total_size(self)
419    }
420}
421
422/// Architecture-specific encoding functions
423pub trait ArchitectureEncoder<I: Instruction> {
424    /// Encode an instruction with specific opcode and operands
425    fn encode(&self, opcode: u32, operands: &[u32]) -> I;
426}
427
428/// Common result type for instruction building
429pub type BuildResult<I> = Result<I, BuildError>;
430
431/// Errors that can occur during instruction building
432#[derive(Debug, Clone, Copy, PartialEq, Eq)]
433pub enum BuildError {
434    /// Invalid register identifier
435    InvalidRegister(u32),
436    /// Invalid immediate value
437    InvalidImmediate(i64),
438    /// Unsupported instruction
439    UnsupportedInstruction,
440    /// Invalid operand combination
441    InvalidOperands,
442}
443
444impl fmt::Display for BuildError {
445    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
446        match self {
447            BuildError::InvalidRegister(id) => write!(f, "Invalid register ID: {}", id),
448            BuildError::InvalidImmediate(val) => write!(f, "Invalid immediate value: {}", val),
449            BuildError::UnsupportedInstruction => write!(f, "Unsupported instruction"),
450            BuildError::InvalidOperands => write!(f, "Invalid operand combination"),
451        }
452    }
453}
454
455#[cfg(feature = "std")]
456impl std::error::Error for BuildError {}
457
458/// JIT execution functionality (std-only)
459#[cfg(feature = "std")]
460pub mod jit {
461    use std::marker::PhantomData;
462    use jit_allocator2::JitAllocator;
463
464    /// A raw JIT-compiled function for manual type conversion
465    /// 
466    /// This is a type-erased version of `CallableJitFunction` that allows
467    /// manual function pointer conversion for cases not covered by the
468    /// predefined `call()` methods.
469    /// 
470    /// # Usage
471    /// 
472    /// ```rust,no_run
473    /// # use jit_assembler::common::jit::*;
474    /// # let code = &[0u8; 4];
475    /// let raw_func = RawCallableJitFunction::new(code)?;
476    /// 
477    /// // Manual conversion to any function type
478    /// let func: extern "C" fn(u64, u64, u64, u64, u64, u64, u64, u64) -> u64 = 
479    ///     unsafe { raw_func.as_fn() };
480    /// let result = func(1, 2, 3, 4, 5, 6, 7, 8);
481    /// # Ok::<(), JitError>(())
482    /// ```
483    pub struct RawCallableJitFunction {
484        _allocator: Box<JitAllocator>,
485        exec_ptr: *const u8,
486    }
487
488    impl RawCallableJitFunction {
489        /// Create a new raw callable JIT function from instruction bytes
490        pub fn new(code: &[u8]) -> Result<Self, JitError> {
491            let mut allocator = JitAllocator::new(Default::default());
492            let (exec_ptr, mut_ptr) = allocator.alloc(code.len()).map_err(JitError::AllocationFailed)?;
493            
494            unsafe {
495                std::ptr::copy_nonoverlapping(code.as_ptr(), mut_ptr, code.len());
496            }
497
498            Ok(RawCallableJitFunction {
499                _allocator: allocator,
500                exec_ptr,
501            })
502        }
503
504        /// Convert to a function pointer of the specified type
505        /// 
506        /// # Safety
507        /// 
508        /// The caller must ensure that:
509        /// - The function signature `F` matches the actual assembled code
510        /// - The assembled code follows the expected calling convention (usually C ABI)
511        /// - The function pointer is called with valid arguments
512        /// 
513        /// # Example
514        /// 
515        /// ```rust,no_run
516        /// # use jit_assembler::common::jit::*;
517        /// # let code = &[0u8; 4];
518        /// let raw_func = RawCallableJitFunction::new(code)?;
519        /// 
520        /// // For complex signatures not covered by CallableJitFunction
521        /// let func: extern "C" fn(u64, u64, u64, u64, u64, u64, u64, u64) -> u64 = 
522        ///     unsafe { raw_func.as_fn() };
523        /// let result = func(1, 2, 3, 4, 5, 6, 7, 8);
524        /// # Ok::<(), JitError>(())
525        /// ```
526        pub unsafe fn as_fn<F>(&self) -> F {
527            std::mem::transmute_copy(&self.exec_ptr)
528        }
529    }
530
531    /// A JIT-compiled function that can be called directly
532    /// 
533    /// This structure wraps executable machine code and provides type-safe
534    /// calling methods. While the type parameter `F` represents a Rust function
535    /// signature, the actual execution uses C ABI for stability.
536    /// 
537    /// # Supported Signatures
538    /// 
539    /// Currently supports function signatures with 0-7 arguments:
540    /// - `fn() -> R`
541    /// - `fn(A1) -> R`  
542    /// - `fn(A1, A2) -> R`
543    /// - ... up to `fn(A1, A2, A3, A4, A5, A6, A7) -> R`
544    /// 
545    /// For unsupported signatures, use `RawCallableJitFunction` instead.
546    pub struct CallableJitFunction<F> {
547        _allocator: Box<JitAllocator>,
548        exec_ptr: *const u8,
549        _phantom: PhantomData<F>,
550    }
551
552    impl<F> CallableJitFunction<F> {
553        /// Create a new callable JIT function from instruction bytes
554        pub fn new(code: &[u8]) -> Result<Self, JitError> {
555            let mut allocator = JitAllocator::new(Default::default());
556            let (exec_ptr, mut_ptr) = allocator.alloc(code.len()).map_err(JitError::AllocationFailed)?;
557            
558            unsafe {
559                std::ptr::copy_nonoverlapping(code.as_ptr(), mut_ptr, code.len());
560            }
561
562            Ok(CallableJitFunction {
563                _allocator: allocator,
564                exec_ptr,
565                _phantom: PhantomData,
566            })
567        }
568    }
569
570    /// Direct call methods based on function signature
571    /// 
572    /// These methods provide type-safe calling with automatic ABI conversion from
573    /// Rust function types to C ABI. Currently supports 0-7 arguments.
574    /// 
575    /// The conversion from `fn(...)` to `extern "C" fn(...)` is handled internally
576    /// for ABI stability across Rust versions.
577    
578    impl<R> CallableJitFunction<fn() -> R> {
579        /// Call with no arguments - natural syntax: func.call()
580        pub fn call(&self) -> R {
581            let func: extern "C" fn() -> R = unsafe { std::mem::transmute_copy(&self.exec_ptr) };
582            func()
583        }
584    }
585
586    impl<A1, R> CallableJitFunction<fn(A1) -> R> {
587        /// Call with one argument - natural syntax: func.call(arg)
588        pub fn call(&self, arg1: A1) -> R {
589            let func: extern "C" fn(A1) -> R = unsafe { std::mem::transmute_copy(&self.exec_ptr) };
590            func(arg1)
591        }
592    }
593
594    impl<A1, A2, R> CallableJitFunction<fn(A1, A2) -> R> {
595        /// Call with two arguments - natural syntax: func.call(arg1, arg2)
596        pub fn call(&self, arg1: A1, arg2: A2) -> R {
597            let func: extern "C" fn(A1, A2) -> R = unsafe { std::mem::transmute_copy(&self.exec_ptr) };
598            func(arg1, arg2)
599        }
600    }
601
602    impl<A1, A2, A3, R> CallableJitFunction<fn(A1, A2, A3) -> R> {
603        /// Call with three arguments - natural syntax: func.call(arg1, arg2, arg3)
604        pub fn call(&self, arg1: A1, arg2: A2, arg3: A3) -> R {
605            let func: extern "C" fn(A1, A2, A3) -> R = unsafe { std::mem::transmute_copy(&self.exec_ptr) };
606            func(arg1, arg2, arg3)
607        }
608    }
609
610    impl<A1, A2, A3, A4, R> CallableJitFunction<fn(A1, A2, A3, A4) -> R> {
611        /// Call with four arguments - natural syntax: func.call(arg1, arg2, arg3, arg4)
612        pub fn call(&self, arg1: A1, arg2: A2, arg3: A3, arg4: A4) -> R {
613            let func: extern "C" fn(A1, A2, A3, A4) -> R = unsafe { std::mem::transmute_copy(&self.exec_ptr) };
614            func(arg1, arg2, arg3, arg4)
615        }
616    }
617
618    impl<A1, A2, A3, A4, A5, R> CallableJitFunction<fn(A1, A2, A3, A4, A5) -> R> {
619        /// Call with five arguments
620        pub fn call(&self, arg1: A1, arg2: A2, arg3: A3, arg4: A4, arg5: A5) -> R {
621            let func: extern "C" fn(A1, A2, A3, A4, A5) -> R = unsafe { std::mem::transmute_copy(&self.exec_ptr) };
622            func(arg1, arg2, arg3, arg4, arg5)
623        }
624    }
625
626    impl<A1, A2, A3, A4, A5, A6, R> CallableJitFunction<fn(A1, A2, A3, A4, A5, A6) -> R> {
627        /// Call with six arguments
628        pub fn call(&self, arg1: A1, arg2: A2, arg3: A3, arg4: A4, arg5: A5, arg6: A6) -> R {
629            let func: extern "C" fn(A1, A2, A3, A4, A5, A6) -> R = unsafe { std::mem::transmute_copy(&self.exec_ptr) };
630            func(arg1, arg2, arg3, arg4, arg5, arg6)
631        }
632    }
633
634    impl<A1, A2, A3, A4, A5, A6, A7, R> CallableJitFunction<fn(A1, A2, A3, A4, A5, A6, A7) -> R> {
635        /// Call with seven arguments
636        pub fn call(&self, arg1: A1, arg2: A2, arg3: A3, arg4: A4, arg5: A5, arg6: A6, arg7: A7) -> R {
637            let func: extern "C" fn(A1, A2, A3, A4, A5, A6, A7) -> R = unsafe { std::mem::transmute_copy(&self.exec_ptr) };
638            func(arg1, arg2, arg3, arg4, arg5, arg6, arg7)
639        }
640    }
641
642    // Note: For void return functions, we don't generate them here 
643    // as they would need special handling with unit type ()
644
645    /// Errors that can occur during JIT execution
646    #[derive(Debug)]
647    pub enum JitError {
648        AllocationFailed(jit_allocator2::Error),
649    }
650
651    impl std::fmt::Display for JitError {
652        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
653            match self {
654                JitError::AllocationFailed(e) => write!(f, "Failed to allocate JIT memory: {:?}", e),
655            }
656        }
657    }
658
659    impl std::error::Error for JitError {}
660}