Skip to main content

blvm_consensus/
optimizations.rs

1//! BLVM Runtime Optimization Passes
2//!
3//! Additional optimization passes for 10-30% performance gains
4//!
5//! This module provides runtime optimization passes:
6//! - Constant folding (pre-computed constants)
7//! - Bounds check optimization (proven bounds)
8//! - Inlining hints (hot function markers)
9//! - Memory layout optimization (cache-friendly structures)
10//!
11//! Reference: Orange Paper Section 13.1 - Performance Considerations
12
13use crate::constants::*;
14
15/// Pre-computed constants for constant folding optimization
16///
17/// These constants are computed at compile time to avoid runtime computation
18/// in hot paths. Reference: BLVM Optimization Pass 2 - Constant Folding
19#[cfg(feature = "production")]
20pub mod precomputed_constants {
21    use super::*;
22
23    /// Pre-computed: 2^64 - 1 (used for wrapping arithmetic checks)
24    pub const U64_MAX: u64 = u64::MAX;
25
26    /// Pre-computed: MAX_MONEY as u64 (for comparisons)
27    pub const MAX_MONEY_U64: u64 = MAX_MONEY as u64;
28
29    /// Pre-computed: Inverse of SATOSHIS_PER_BTC (for BTC conversion)
30    pub const BTC_PER_SATOSHI: f64 = 1.0 / (SATOSHIS_PER_BTC as f64);
31
32    /// Pre-computed: 2^32 - 1 (for 32-bit wrapping checks)
33    pub const U32_MAX: u32 = u32::MAX;
34
35    /// Pre-computed: Number of satoshis in 1 BTC (for readability)
36    pub const ONE_BTC_SATOSHIS: i64 = SATOSHIS_PER_BTC;
37}
38
39/// Memory layout optimization: Cache-friendly hash array
40///
41/// Optimizes hash array access for cache locality.
42/// Uses 32-byte aligned structures for better cache performance.
43///
44/// This structure ensures each hash is aligned to a 32-byte boundary, which:
45/// - Reduces cache line splits
46/// - Improves prefetching behavior
47/// - Better fits modern CPU cache architectures (64-byte cache lines)
48///
49/// Reference: BLVM Optimization Pass 3 - Memory Layout Optimization
50/// Cache-aligned hash for optimized batch operations
51#[repr(align(32))]
52#[derive(Clone)]
53pub struct CacheAlignedHash([u8; 32]);
54
55impl CacheAlignedHash {
56    #[inline]
57    pub fn new(hash: [u8; 32]) -> Self {
58        Self(hash)
59    }
60
61    #[inline]
62    pub fn as_bytes(&self) -> &[u8; 32] {
63        &self.0
64    }
65}
66
67/// Memory prefetching optimization
68///
69/// Provides platform-specific prefetch hints to improve cache performance
70/// for sequential memory accesses. Used before batch UTXO lookups and
71/// other sequential data structure traversals.
72///
73/// Reference: BLVM Optimization Pass 1.3 - Memory Prefetching
74#[cfg(feature = "production")]
75pub mod prefetch {
76    /// Prefetch data for read access
77    ///
78    /// Hints the CPU to prefetch data into cache before it's needed.
79    /// This improves performance for sequential memory access patterns.
80    ///
81    /// # Safety
82    /// The pointer must be valid, but it doesn't need to be dereferenceable
83    /// at the time of the call. The prefetch is a hint and may be ignored.
84    #[cfg(target_arch = "x86_64")]
85    #[inline(always)]
86    pub unsafe fn prefetch_read(ptr: *const i8) {
87        use std::arch::x86_64::{_MM_HINT_T0, _mm_prefetch};
88        unsafe {
89            _mm_prefetch(ptr, _MM_HINT_T0);
90        }
91    }
92
93    #[cfg(target_arch = "aarch64")]
94    #[inline(always)]
95    pub unsafe fn prefetch_read(ptr: *const i8) {
96        // std::arch::aarch64::_prefetch requires the unstable
97        // `stdarch_aarch64_prefetch` feature (issue #117217) and is not yet
98        // available on stable Rust.  Use inline asm instead: `core::arch::asm!`
99        // is stable since 1.59 and emits the identical PRFM instruction.
100        // PRFM PLDL1KEEP = Prefetch for Load, L1, temporal (≡ _prefetch hint T0).
101        core::arch::asm!(
102            "prfm pldl1keep, [{addr}]",
103            addr = in(reg) ptr,
104            options(nostack, readonly, preserves_flags)
105        );
106    }
107
108    #[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
109    #[inline(always)]
110    pub unsafe fn prefetch_read(_ptr: *const i8) {
111        // No-op for unsupported architectures
112    }
113
114    /// Prefetch a slice of data for sequential access
115    ///
116    /// Prefetches the next cache line(s) of data to improve sequential access.
117    /// Safe wrapper around prefetch_read that works with slices.
118    #[inline(always)]
119    pub fn prefetch_slice<T>(slice: &[T], index: usize) {
120        if index < slice.len() {
121            unsafe {
122                let ptr = slice.as_ptr().add(index) as *const i8;
123                prefetch_read(ptr);
124            }
125        }
126    }
127
128    /// Prefetch multiple elements ahead in a slice
129    ///
130    /// Prefetches elements at `index + offset` to prepare for future access.
131    /// Useful for sequential loops where you know you'll access elements ahead.
132    #[inline(always)]
133    pub fn prefetch_ahead<T>(slice: &[T], index: usize, offset: usize) {
134        let prefetch_index = index.saturating_add(offset);
135        prefetch_slice(slice, prefetch_index);
136    }
137}
138
139/// Memory layout optimization: Compact stack frame
140///
141/// Compact stack frame for script execution optimization
142/// Optimized stack frame structure for cache locality.
143#[repr(C, packed)]
144pub struct CompactStackFrame {
145    pub opcode: u8,
146    pub flags: u32,
147    pub script_offset: u16,
148    pub stack_height: u16,
149}
150
151impl CompactStackFrame {
152    #[inline]
153    pub fn new(opcode: u8, flags: u32, script_offset: u16, stack_height: u16) -> Self {
154        Self {
155            opcode,
156            flags,
157            script_offset,
158            stack_height,
159        }
160    }
161}
162
163/// Inlining hints for hot functions
164///
165/// Functions marked with HOT_INLINE should be aggressively inlined.
166/// These are called in tight loops and benefit from inlining.
167#[macro_export]
168#[cfg(feature = "production")]
169macro_rules! hot_inline {
170    () => {
171        #[inline(always)]
172    };
173}
174
175/// Constant folding: Pre-compute common hash results
176///
177/// Caches common hash pre-images for constant folding.
178#[cfg(feature = "production")]
179pub mod constant_folding {
180    /// Pre-computed: SHA256 of empty string
181    pub const EMPTY_STRING_HASH: [u8; 32] = [
182        0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9,
183        0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52,
184        0xb8, 0x55,
185    ];
186
187    /// Pre-computed: Double SHA256 of empty string
188    pub const EMPTY_STRING_DOUBLE_HASH: [u8; 32] = [
189        0x5d, 0xf6, 0xe0, 0xe2, 0x76, 0x13, 0x59, 0xf3, 0x73, 0x9a, 0x1c, 0x6f, 0x87, 0x40, 0x64,
190        0x0a, 0xf1, 0x2e, 0xc7, 0xc3, 0x72, 0x4a, 0x5c, 0x2c, 0xa5, 0xf3, 0x0f, 0x26, 0x60, 0x87,
191        0x7e, 0x6b,
192    ];
193
194    /// Check if input matches empty string hash (constant folding)
195    #[inline(always)]
196    pub fn is_empty_hash(hash: &[u8; 32]) -> bool {
197        *hash == EMPTY_STRING_HASH
198    }
199
200    /// Check if input matches empty string double hash (constant folding)
201    #[inline(always)]
202    pub fn is_empty_double_hash(hash: &[u8; 32]) -> bool {
203        *hash == EMPTY_STRING_DOUBLE_HASH
204    }
205
206    /// Constant-fold: Check if hash is zero (all zeros)
207    #[inline(always)]
208    pub fn is_zero_hash(hash: &[u8; 32]) -> bool {
209        hash.iter().all(|&b| b == 0)
210    }
211}
212
213/// SIMD Vectorization: Batch hash operations
214///
215/// Provides batch hash processing for parallel hash operations.
216/// Leverages existing SIMD in sha2 crate (asm feature) + Rayon for CPU-core parallelization.
217///
218/// Provides batch functions for:
219/// - SHA256 and double SHA256 (Bitcoin standard)
220/// - RIPEMD160 and HASH160 (OP_HASH160)
221///
222/// Uses chunked processing for better cache locality and parallelizes across CPU cores
223/// when batch size is large enough (≥8 items).
224///
225/// Reference: BLVM Optimization Pass 5 - SIMD Vectorization
226#[cfg(feature = "production")]
227pub mod simd_vectorization {
228    use crate::crypto::OptimizedSha256;
229    use digest::Digest;
230    use ripemd::Ripemd160;
231
232    /// Minimum batch size for parallelization (overhead not worth it for smaller batches).
233    /// batch_sha256 uses OptimizedSha256 (SHA-NI when available) for consistency with batch_double_sha256_aligned.
234    const PARALLEL_THRESHOLD: usize = 8;
235
236    /// Chunk size for cache-friendly processing. Hardware-derived via ibd_tuning.
237    #[inline]
238    fn chunk_size() -> usize {
239        blvm_primitives::ibd_tuning::hash_batch_chunk_size()
240    }
241
242    /// Batch SHA256: Compute SHA256 for multiple independent inputs
243    ///
244    /// # Arguments
245    /// * `inputs` - Slice of byte slices to hash
246    ///
247    /// # Returns
248    /// Vector of 32-byte hashes, one per input (in same order)
249    ///
250    /// # Performance
251    /// - Small batches (< 4 items): Sequential (overhead not worth parallelization)
252    /// - Medium batches (4-7 items): Chunked sequential
253    /// - Large batches (≥8 items): Multi-core parallelization with Rayon
254    ///
255    /// # Optimizations
256    /// - Uses sha2 crate with "asm" feature for optimized assembly
257    /// - For large batches, leverages Rayon for multi-core parallelization
258    /// - AVX2 batch optimization available via `crypto::avx2_batch` module
259    pub fn batch_sha256(inputs: &[&[u8]]) -> Vec<[u8; 32]> {
260        if inputs.is_empty() {
261            return Vec::new();
262        }
263
264        // Small batches: sequential processing. Use OptimizedSha256 (SHA-NI when available).
265        if inputs.len() < 4 {
266            let hasher = OptimizedSha256::new();
267            return inputs.iter().map(|input| hasher.hash(input)).collect();
268        }
269
270        // Medium batches: chunked sequential processing
271        if inputs.len() < PARALLEL_THRESHOLD {
272            let hasher = OptimizedSha256::new();
273            let mut results = Vec::with_capacity(inputs.len());
274            for chunk in inputs.chunks(chunk_size()) {
275                for input in chunk {
276                    results.push(hasher.hash(input));
277                }
278            }
279            return results;
280        }
281
282        // Large batches: Try AVX2 first, then fallback to multi-core parallelization
283        #[cfg(target_arch = "x86_64")]
284        {
285            use crate::crypto::sha256_avx2;
286            if sha256_avx2::is_avx2_available() {
287                // Use AVX2 batch processing for chunks of 8
288                use crate::crypto::avx2_batch;
289                return avx2_batch::batch_sha256_avx2(inputs);
290            }
291        }
292
293        // Fallback: serial chunked processing. The previous `par_chunks` rayon path was
294        // disastrous in IBD: N validation workers × per-block calls × shared rayon pool =
295        // catastrophic oversubscription. SHA-NI single-thread is fast enough; cross-block
296        // parallelism (worker pool) is the only level we want.
297        let hasher = OptimizedSha256::new();
298        let mut results = Vec::with_capacity(inputs.len());
299        for chunk in inputs.chunks(chunk_size()) {
300            for input in chunk {
301                results.push(hasher.hash(input));
302            }
303        }
304        results
305    }
306
307    /// Batch double SHA256: Compute SHA256(SHA256(x)) for multiple inputs
308    ///
309    /// This is Bitcoin's standard hash function used for transaction IDs, block hashes, etc.
310    ///
311    /// # Arguments
312    /// * `inputs` - Slice of byte slices to hash
313    ///
314    /// # Returns
315    /// Vector of 32-byte hashes, one per input (in same order)
316    pub fn batch_double_sha256(inputs: &[&[u8]]) -> Vec<[u8; 32]> {
317        // Use aligned version for better cache performance
318        batch_double_sha256_aligned(inputs)
319            .into_iter()
320            .map(|h| *h.as_bytes())
321            .collect()
322    }
323
324    /// Batch double SHA256 with cache-aligned output
325    ///
326    /// Returns cache-aligned hash structures for better memory performance.
327    /// Uses 32-byte alignment for optimal cache line utilization.
328    ///
329    /// # Arguments
330    /// * `inputs` - Slice of byte slices to hash
331    ///
332    /// # Returns
333    /// Vector of cache-aligned 32-byte hashes, one per input (in same order)
334    pub fn batch_double_sha256_aligned(inputs: &[&[u8]]) -> Vec<super::CacheAlignedHash> {
335        if inputs.is_empty() {
336            return Vec::new();
337        }
338
339        // Small batches: sequential processing (overhead not worth it)
340        // Use OptimizedSha256 (SHA-NI when available) instead of sha2
341        let hasher = OptimizedSha256::new();
342        if inputs.len() < 4 {
343            return inputs
344                .iter()
345                .map(|input| super::CacheAlignedHash::new(hasher.hash256(input)))
346                .collect();
347        }
348
349        // Medium batches: chunked sequential processing
350        if inputs.len() < PARALLEL_THRESHOLD {
351            let mut results = Vec::with_capacity(inputs.len());
352            for chunk in inputs.chunks(chunk_size()) {
353                for input in chunk {
354                    results.push(super::CacheAlignedHash::new(hasher.hash256(input)));
355                }
356            }
357            return results;
358        }
359
360        // Serial chunked processing — see `batch_sha256` for rationale (rayon oversubscribes
361        // the pool when N IBD workers each push hashing batches; SHA-NI keeps the per-worker
362        // path fast on its own thread).
363        let hasher = OptimizedSha256::new();
364        let mut results = Vec::with_capacity(inputs.len());
365        for chunk in inputs.chunks(chunk_size()) {
366            for input in chunk {
367                results.push(super::CacheAlignedHash::new(hasher.hash256(input)));
368            }
369        }
370        results
371    }
372
373    /// Batch RIPEMD160: Compute RIPEMD160 for multiple inputs
374    ///
375    /// # Arguments
376    /// * `inputs` - Slice of byte slices to hash
377    ///
378    /// # Returns
379    /// Vector of 20-byte hashes, one per input (in same order)
380    pub fn batch_ripemd160(inputs: &[&[u8]]) -> Vec<[u8; 20]> {
381        if inputs.is_empty() {
382            return Vec::new();
383        }
384
385        // Small batches: sequential processing
386        if inputs.len() < 4 {
387            return inputs
388                .iter()
389                .map(|input| {
390                    let hash = Ripemd160::digest(input);
391                    let mut result = [0u8; 20];
392                    result.copy_from_slice(&hash);
393                    result
394                })
395                .collect();
396        }
397
398        // Medium batches: chunked sequential processing
399        if inputs.len() < PARALLEL_THRESHOLD {
400            let mut results = Vec::with_capacity(inputs.len());
401            for chunk in inputs.chunks(chunk_size()) {
402                for input in chunk {
403                    let hash = Ripemd160::digest(input);
404                    let mut result = [0u8; 20];
405                    result.copy_from_slice(&hash);
406                    results.push(result);
407                }
408            }
409            return results;
410        }
411
412        // Serial chunked processing — same rationale as `batch_sha256`: cross-block
413        // parallelism is provided by the IBD worker pool; rayon par_chunks here
414        // oversubscribes the global pool when N workers each call this per-block.
415        let mut results = Vec::with_capacity(inputs.len());
416        for chunk in inputs.chunks(chunk_size()) {
417            for input in chunk {
418                let hash = Ripemd160::digest(input);
419                let mut result = [0u8; 20];
420                result.copy_from_slice(&hash);
421                results.push(result);
422            }
423        }
424        results
425    }
426
427    /// Batch HASH160: Compute RIPEMD160(SHA256(x)) for multiple inputs
428    ///
429    /// This is Bitcoin's HASH160 operation (OP_HASH160 in script).
430    ///
431    /// # Arguments
432    /// * `inputs` - Slice of byte slices to hash
433    ///
434    /// # Returns
435    /// Vector of 20-byte hashes, one per input (in same order)
436    pub fn batch_hash160(inputs: &[&[u8]]) -> Vec<[u8; 20]> {
437        if inputs.is_empty() {
438            return Vec::new();
439        }
440
441        // Small batches: sequential processing. Use OptimizedSha256 (SHA-NI) for SHA256 part.
442        if inputs.len() < 4 {
443            let hasher = OptimizedSha256::new();
444            return inputs
445                .iter()
446                .map(|input| {
447                    let sha256_hash: [u8; 32] = hasher.hash(input);
448                    let ripemd160_hash = Ripemd160::digest(sha256_hash);
449                    let mut result = [0u8; 20];
450                    result.copy_from_slice(&ripemd160_hash);
451                    result
452                })
453                .collect();
454        }
455
456        // Medium batches: chunked sequential processing
457        if inputs.len() < PARALLEL_THRESHOLD {
458            let hasher = OptimizedSha256::new();
459            let mut results = Vec::with_capacity(inputs.len());
460            for chunk in inputs.chunks(chunk_size()) {
461                for input in chunk {
462                    let sha256_hash: [u8; 32] = hasher.hash(input);
463                    let ripemd160_hash = Ripemd160::digest(sha256_hash);
464                    let mut result = [0u8; 20];
465                    result.copy_from_slice(&ripemd160_hash);
466                    results.push(result);
467                }
468            }
469            return results;
470        }
471
472        // Serial chunked processing — see `batch_sha256` for rationale.
473        let hasher = OptimizedSha256::new();
474        let mut results = Vec::with_capacity(inputs.len());
475        for chunk in inputs.chunks(chunk_size()) {
476            for input in chunk {
477                let sha256_hash: [u8; 32] = hasher.hash(input);
478                let ripemd160_hash = Ripemd160::digest(sha256_hash);
479                let mut result = [0u8; 20];
480                result.copy_from_slice(&ripemd160_hash);
481                results.push(result);
482            }
483        }
484        results
485    }
486}
487
488#[cfg(feature = "production")]
489pub use constant_folding::*;
490#[cfg(feature = "production")]
491pub use precomputed_constants::*;
492
493/// Proven bounds for runtime optimization
494///
495/// These bounds are proven by formal verification and can be used
496/// for runtime optimizations without additional safety checks.
497///
498/// Proven runtime bounds for BLVM optimizations
499///
500/// These bounds have been formally proven and are used for runtime optimizations.
501/// Unlike proof-time limits (in `_helpers::proof_limits`), these represent actual
502/// Bitcoin limits that have been proven to hold in all cases.
503///
504/// Reference: BLVM Optimization Pass
505#[cfg(feature = "production")]
506pub mod proven_bounds {
507    use crate::constants::{MAX_INPUTS, MAX_OUTPUTS};
508
509    /// Maximum transaction size (proven by formal verification in transaction.rs)
510    pub const MAX_TX_SIZE_PROVEN: usize = 100000; // Bytes
511
512    /// Maximum block size (proven by formal verification in block.rs)
513    pub const MAX_BLOCK_SIZE_PROVEN: usize = 4000000; // Bytes (4MB)
514
515    /// Maximum inputs per transaction (proven by formal verification)
516    /// References actual Bitcoin limit from constants.rs
517    pub const MAX_INPUTS_PROVEN: usize = MAX_INPUTS;
518
519    /// Maximum outputs per transaction (proven by formal verification)
520    /// References actual Bitcoin limit from constants.rs
521    pub const MAX_OUTPUTS_PROVEN: usize = MAX_OUTPUTS;
522
523    /// Maximum transactions per block (proven by formal verification)
524    /// Note: Bitcoin limit is effectively unbounded by consensus rules, but practical limit
525    /// is around 10,000 transactions per block based on block size limits.
526    pub const MAX_TRANSACTIONS_PROVEN: usize = 10000;
527
528    /// Maximum previous headers for difficulty adjustment (proven by formal verification)
529    pub const MAX_PREV_HEADERS_PROVEN: usize = 5;
530}
531
532/// Optimized access using proven bounds
533///
534/// Uses bounds proven by formal verification to optimize runtime access.
535/// This is safe because formal proofs guarantee these bounds hold.
536///
537/// Reference: Formal proofs in transaction.rs, block.rs, mining.rs, pow.rs, etc.
538/// These proofs formally verify that certain bounds always hold, allowing us to
539/// use optimized access patterns without runtime bounds checks.
540#[cfg(feature = "production")]
541pub mod optimized_access {
542    use super::proven_bounds;
543
544    /// Get element with proven bounds check
545    ///
546    /// Uses proven maximum sizes to optimize bounds checking.
547    /// For transactions proven to have <= MAX_INPUTS_PROVEN inputs,
548    /// we can use optimized access patterns.
549    ///
550    /// # Safety
551    /// This function is safe because formal proofs guarantee bounds.
552    /// However, it still returns `Option` to handle cases where:
553    /// - Runtime bounds differ from proof bounds (should not happen in practice)
554    /// - Defensive programming (fail-safe)
555    ///
556    /// # Panics
557    /// Never panics - always returns `None` if out of bounds.
558    ///
559    /// # Examples
560    /// ```rust
561    /// use blvm_consensus::optimizations::optimized_access::get_proven;
562    /// use blvm_consensus::types::Transaction;
563    ///
564    /// # let tx = Transaction { version: 1, inputs: vec![].into(), outputs: vec![].into(), lock_time: 0 };
565    /// # let index = 0;
566    /// if let Some(input) = get_proven(&tx.inputs, index) {
567    ///     // Safe to use
568    /// }
569    /// ```
570    #[inline(always)]
571    pub fn get_proven<T>(slice: &[T], index: usize) -> Option<&T> {
572        // Formal proofs have proven index < MAX_SIZE in various proofs
573        // We can use unsafe access for proven-safe indices
574        // This is safe because formal proofs guarantee bounds
575        if index < slice.len() {
576            unsafe { Some(slice.get_unchecked(index)) }
577        } else {
578            None
579        }
580    }
581
582    /// Pre-allocate buffer using proven maximum size
583    ///
584    /// Uses proven maximum sizes to avoid reallocation.
585    /// For example, transaction buffers can be pre-sized to MAX_TX_SIZE_PROVEN.
586    #[inline(always)]
587    pub fn prealloc_proven<T>(max_size: usize) -> Vec<T> {
588        // Pre-allocate to proven maximum to avoid reallocation
589        Vec::with_capacity(max_size)
590    }
591
592    /// Pre-allocate transaction buffer using proven maximum
593    #[inline(always)]
594    pub fn prealloc_tx_buffer() -> Vec<u8> {
595        prealloc_proven::<u8>(proven_bounds::MAX_TX_SIZE_PROVEN)
596    }
597
598    /// Pre-allocate block buffer using proven maximum
599    #[inline(always)]
600    pub fn prealloc_block_buffer() -> Vec<u8> {
601        prealloc_proven::<u8>(proven_bounds::MAX_BLOCK_SIZE_PROVEN)
602    }
603
604    /// Get element with proven bounds (alias for get_proven for compatibility)
605    #[inline(always)]
606    pub fn get_proven_by_<T>(slice: &[T], index: usize) -> Option<&T> {
607        get_proven(slice, index)
608    }
609}
610
611/// Alias module for _optimized_access (for backward compatibility)
612#[cfg(feature = "production")]
613pub mod _optimized_access {
614    use super::optimized_access;
615
616    /// Get element with proven bounds
617    #[inline(always)]
618    pub fn get_proven_by_<T>(slice: &[T], index: usize) -> Option<&T> {
619        optimized_access::get_proven(slice, index)
620    }
621}
622
623/// Re-export prealloc helpers for convenience
624#[cfg(feature = "production")]
625pub use optimized_access::{prealloc_block_buffer, prealloc_tx_buffer};
626
627/// Reference implementations for equivalence proofs
628///
629/// These are safe versions of optimized functions, used to prove
630/// that optimizations are correct via formal verification.
631#[cfg(feature = "production")]
632pub mod reference_implementations {
633    /// Reference (safe) implementation of get_proven
634    /// This is the version we prove equivalence against
635    #[inline(always)]
636    pub fn get_proven_reference<T>(slice: &[T], index: usize) -> Option<&T> {
637        slice.get(index) // Safe version
638    }
639}
640
641/// Runtime assertions for optimization correctness
642///
643/// These functions provide runtime checks in debug builds to verify
644/// that optimizations match their reference implementations.
645#[cfg(all(
646    feature = "production",
647    any(debug_assertions, feature = "runtime-invariants")
648))]
649pub mod runtime_assertions {
650    use super::optimized_access::get_proven;
651    use super::reference_implementations::get_proven_reference;
652
653    /// Checked version of get_proven with runtime assertions
654    ///
655    /// This function performs runtime checks in debug builds to ensure
656    /// the optimized implementation matches the reference implementation.
657    #[inline(always)]
658    pub fn get_proven_checked<T>(slice: &[T], index: usize) -> Option<&T> {
659        let result_optimized = get_proven(slice, index);
660        let result_reference = get_proven_reference(slice, index);
661
662        // Runtime check: both must agree
663        debug_assert_eq!(
664            result_optimized.is_some(),
665            result_reference.is_some(),
666            "Optimization correctness check failed: optimized and reference disagree on Some/None"
667        );
668
669        if let (Some(opt_val), Some(ref_val)) = (result_optimized, result_reference) {
670            debug_assert_eq!(
671                opt_val as *const T, ref_val as *const T,
672                "Optimization correctness check failed: optimized and reference return different pointers"
673            );
674        }
675
676        result_optimized
677    }
678}