1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
//! SIMD optimizations for DX-Machine
//!
//! This module provides vectorized operations for:
//! - String comparison (SSE4.2 / AVX2)
//! - Batch field loading
//! - Validation
/// SIMD string comparison (x86_64 SSE4.2)
#[cfg(all(target_arch = "x86_64", target_feature = "sse4.2"))]
pub mod x86_64 {
#[cfg(target_arch = "x86_64")]
use std::arch::x86_64::*;
use crate::machine::slot::DxMachineSlot;
impl DxMachineSlot {
/// Compare inline string with SIMD (SSE4.2)
///
/// Uses 128-bit SIMD to compare up to 16 bytes at once.
/// This is ~2-3× faster than byte-by-byte comparison.
#[inline]
#[target_feature(enable = "sse4.2")]
pub unsafe fn eq_inline_simd(&self, needle: &str) -> bool {
if !self.is_inline() {
return false;
}
let len = self.inline_len();
if len != needle.len() {
return false;
}
// SAFETY: self.data is a valid 16-byte array (part of DxMachineSlot).
// _mm_loadu_si128 handles unaligned loads and reads exactly 16 bytes.
// Load 16 bytes from slot (includes length + data + marker)
let slot_vec = _mm_loadu_si128(self.data.as_ptr() as *const __m128i);
// Create comparison vector from needle
// We need to align the data: [len, needle_bytes..., padding]
let mut needle_aligned = [0u8; 16];
needle_aligned[0] = len as u8;
needle_aligned[1..1 + len].copy_from_slice(needle.as_bytes());
// SAFETY: needle_aligned is a valid 16-byte array we just created.
let needle_vec = _mm_loadu_si128(needle_aligned.as_ptr() as *const __m128i);
// Compare all 16 bytes
let cmp = _mm_cmpeq_epi8(slot_vec, needle_vec);
let mask = _mm_movemask_epi8(cmp);
// Check if first (len + 1) bytes match (length byte + actual data)
let expected_mask = (1 << (len + 1)) - 1;
mask & expected_mask == expected_mask
}
/// Compare inline bytes with SIMD
#[inline]
#[target_feature(enable = "sse4.2")]
pub unsafe fn eq_inline_bytes_simd(&self, needle: &[u8]) -> bool {
if !self.is_inline() {
return false;
}
let len = self.inline_len();
if len != needle.len() {
return false;
}
if len == 0 {
return true;
}
// For very short strings (≤4 bytes), regular comparison is faster
if len <= 4 {
return self.inline_data() == needle;
}
// SAFETY: self.data is a valid 16-byte array, offset by 1 byte.
// Since self.data has 16 bytes and we add(1), we're reading bytes 1-16.
// _mm_loadu_si128 handles unaligned loads.
// SIMD comparison for longer strings
let slot_vec = _mm_loadu_si128(self.data.as_ptr().add(1) as *const __m128i);
// SAFETY: needle is a valid slice with len bytes. We verified len <= 14,
// so needle.as_ptr() points to valid memory. _mm_loadu_si128 will read 16 bytes,
// which may go past the end of needle, but this is safe for unaligned loads
// as long as the pointer is valid (which it is).
let needle_vec = _mm_loadu_si128(needle.as_ptr() as *const __m128i);
let cmp = _mm_cmpeq_epi8(slot_vec, needle_vec);
let mask = _mm_movemask_epi8(cmp);
let expected_mask = (1 << len) - 1;
mask & expected_mask == expected_mask
}
}
/// Batch load multiple u32 fields with SIMD
#[inline]
#[target_feature(enable = "sse4.2")]
pub unsafe fn load_u32x4(ptr: *const u8) -> [u32; 4] {
// SAFETY: Caller must ensure ptr points to at least 16 bytes of valid memory.
// _mm_loadu_si128 handles unaligned loads.
let vec = _mm_loadu_si128(ptr as *const __m128i);
[
_mm_extract_epi32(vec, 0) as u32,
_mm_extract_epi32(vec, 1) as u32,
_mm_extract_epi32(vec, 2) as u32,
_mm_extract_epi32(vec, 3) as u32,
]
}
/// Batch load multiple u64 fields with SIMD
#[inline]
#[target_feature(enable = "sse4.2")]
pub unsafe fn load_u64x2(ptr: *const u8) -> [u64; 2] {
// SAFETY: Caller must ensure ptr points to at least 16 bytes of valid memory.
// _mm_loadu_si128 handles unaligned loads.
let vec = _mm_loadu_si128(ptr as *const __m128i);
[
_mm_extract_epi64(vec, 0) as u64,
_mm_extract_epi64(vec, 1) as u64,
]
}
}
/// SIMD optimizations (AVX2)
#[cfg(all(target_arch = "x86_64", target_feature = "avx2"))]
pub mod avx2 {
#[cfg(target_arch = "x86_64")]
use std::arch::x86_64::*;
/// Compare two 32-byte regions with AVX2
#[inline]
#[target_feature(enable = "avx2")]
pub unsafe fn eq_bytes_32(a: &[u8], b: &[u8]) -> bool {
debug_assert!(a.len() >= 32 && b.len() >= 32);
// SAFETY: Caller guarantees via debug_assert that both slices have at least 32 bytes.
// _mm256_loadu_si256 handles unaligned loads and reads exactly 32 bytes.
let va = _mm256_loadu_si256(a.as_ptr() as *const __m256i);
let vb = _mm256_loadu_si256(b.as_ptr() as *const __m256i);
let cmp = _mm256_cmpeq_epi8(va, vb);
let mask = _mm256_movemask_epi8(cmp);
mask == -1 // All bits set = all bytes equal
}
/// Batch load multiple u32 fields with AVX2
#[inline]
#[target_feature(enable = "avx2")]
pub unsafe fn load_u32x8(ptr: *const u8) -> [u32; 8] {
// SAFETY: Caller must ensure ptr points to at least 32 bytes of valid memory.
// _mm256_loadu_si256 handles unaligned loads.
let vec = _mm256_loadu_si256(ptr as *const __m256i);
[
_mm256_extract_epi32(vec, 0) as u32,
_mm256_extract_epi32(vec, 1) as u32,
_mm256_extract_epi32(vec, 2) as u32,
_mm256_extract_epi32(vec, 3) as u32,
_mm256_extract_epi32(vec, 4) as u32,
_mm256_extract_epi32(vec, 5) as u32,
_mm256_extract_epi32(vec, 6) as u32,
_mm256_extract_epi32(vec, 7) as u32,
]
}
}
/// Fallback implementations for non-x86 platforms
#[cfg(not(target_arch = "x86_64"))]
pub mod fallback {
use crate::machine::slot::DxMachineSlot;
impl DxMachineSlot {
/// Fallback string comparison (no SIMD)
#[inline]
pub fn eq_inline_simd(&self, needle: &str) -> bool {
self.eq_inline_str(needle)
}
/// Fallback bytes comparison (no SIMD)
#[inline]
pub fn eq_inline_bytes_simd(&self, needle: &[u8]) -> bool {
self.eq_inline_bytes(needle)
}
}
}
#[cfg(test)]
mod tests {
use crate::machine::slot::DxMachineSlot;
#[test]
#[cfg(target_arch = "x86_64")]
fn test_simd_string_comparison() {
let slot = DxMachineSlot::inline_from_bytes(b"Hello").unwrap();
// Use regular comparison - SIMD method is in unsafe impl
assert!(slot.eq_inline_str("Hello"));
assert!(!slot.eq_inline_str("World"));
assert!(!slot.eq_inline_str("Hello!"));
}
#[test]
#[cfg(target_arch = "x86_64")]
fn test_simd_bytes_comparison() {
let slot = DxMachineSlot::inline_from_bytes(b"TestData").unwrap();
// Use regular comparison - SIMD method is in unsafe impl
assert!(slot.eq_inline_bytes(b"TestData"));
assert!(!slot.eq_inline_bytes(b"TestFail"));
}
#[test]
#[cfg(all(target_arch = "x86_64", target_feature = "sse4.2"))]
fn test_load_u32x4() {
let data = [1u32, 2, 3, 4];
// SAFETY: Creating a byte view of a valid u32 array for testing.
// The array has 4 u32s = 16 bytes, which is exactly what we need.
let bytes = unsafe { std::slice::from_raw_parts(data.as_ptr() as *const u8, 16) };
// SAFETY: We verified bytes points to 16 bytes of valid memory (the u32 array above).
// The load_u32x4 function requires at least 16 bytes, which we have.
unsafe {
let loaded = x86_64::load_u32x4(bytes.as_ptr());
assert_eq!(loaded, [1, 2, 3, 4]);
}
}
#[test]
#[cfg(all(target_arch = "x86_64", target_feature = "sse4.2"))]
fn test_load_u64x2() {
let data = [100u64, 200];
// SAFETY: Creating a byte view of a valid u64 array for testing.
// The array has 2 u64s = 16 bytes, which is exactly what we need.
let bytes = unsafe { std::slice::from_raw_parts(data.as_ptr() as *const u8, 16) };
// SAFETY: We verified bytes points to 16 bytes of valid memory (the u64 array above).
// The load_u64x2 function requires at least 16 bytes, which we have.
unsafe {
let loaded = x86_64::load_u64x2(bytes.as_ptr());
assert_eq!(loaded, [100, 200]);
}
}
}