1use std::collections::HashMap;
48use std::sync::Arc;
49
50#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
52pub enum IrType {
53 Bool,
54 I64,
55 F64,
56 Ptr,
57}
58
59impl IrType {
60 pub fn size(&self) -> usize {
62 match self {
63 IrType::Bool => 1,
64 IrType::I64 | IrType::F64 | IrType::Ptr => 8,
65 }
66 }
67}
68
69#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
71pub struct Reg(pub u32);
72
73impl Reg {
74 pub fn new(id: u32) -> Self {
75 Reg(id)
76 }
77}
78
79#[derive(Debug, Clone)]
81pub enum IrInst {
82 ConstBool(Reg, bool),
84 ConstI64(Reg, i64),
85 ConstF64(Reg, f64),
86
87 LoadI64(Reg, u32),
89 LoadF64(Reg, u32),
90 LoadBool(Reg, u32),
91
92 EqI64(Reg, Reg, Reg),
94 NeI64(Reg, Reg, Reg),
95 LtI64(Reg, Reg, Reg),
96 LeI64(Reg, Reg, Reg),
97 GtI64(Reg, Reg, Reg),
98 GeI64(Reg, Reg, Reg),
99
100 EqF64(Reg, Reg, Reg),
102 NeF64(Reg, Reg, Reg),
103 LtF64(Reg, Reg, Reg),
104 LeF64(Reg, Reg, Reg),
105 GtF64(Reg, Reg, Reg),
106 GeF64(Reg, Reg, Reg),
107
108 And(Reg, Reg, Reg),
110 Or(Reg, Reg, Reg),
111 Not(Reg, Reg),
112
113 AddI64(Reg, Reg, Reg),
115 SubI64(Reg, Reg, Reg),
116 MulI64(Reg, Reg, Reg),
117 AddF64(Reg, Reg, Reg),
118 SubF64(Reg, Reg, Reg),
119 MulF64(Reg, Reg, Reg),
120 DivF64(Reg, Reg, Reg),
121
122 I64ToF64(Reg, Reg),
124 F64ToI64(Reg, Reg),
125
126 Ret(Reg),
128}
129
130impl IrInst {
131 pub fn dest(&self) -> Option<Reg> {
133 match self {
134 IrInst::ConstBool(r, _)
135 | IrInst::ConstI64(r, _)
136 | IrInst::ConstF64(r, _)
137 | IrInst::LoadI64(r, _)
138 | IrInst::LoadF64(r, _)
139 | IrInst::LoadBool(r, _)
140 | IrInst::EqI64(r, _, _)
141 | IrInst::NeI64(r, _, _)
142 | IrInst::LtI64(r, _, _)
143 | IrInst::LeI64(r, _, _)
144 | IrInst::GtI64(r, _, _)
145 | IrInst::GeI64(r, _, _)
146 | IrInst::EqF64(r, _, _)
147 | IrInst::NeF64(r, _, _)
148 | IrInst::LtF64(r, _, _)
149 | IrInst::LeF64(r, _, _)
150 | IrInst::GtF64(r, _, _)
151 | IrInst::GeF64(r, _, _)
152 | IrInst::And(r, _, _)
153 | IrInst::Or(r, _, _)
154 | IrInst::Not(r, _)
155 | IrInst::AddI64(r, _, _)
156 | IrInst::SubI64(r, _, _)
157 | IrInst::MulI64(r, _, _)
158 | IrInst::AddF64(r, _, _)
159 | IrInst::SubF64(r, _, _)
160 | IrInst::MulF64(r, _, _)
161 | IrInst::DivF64(r, _, _)
162 | IrInst::I64ToF64(r, _)
163 | IrInst::F64ToI64(r, _) => Some(*r),
164 IrInst::Ret(_) => None,
165 }
166 }
167
168 pub fn sources(&self) -> Vec<Reg> {
170 match self {
171 IrInst::ConstBool(_, _)
172 | IrInst::ConstI64(_, _)
173 | IrInst::ConstF64(_, _)
174 | IrInst::LoadI64(_, _)
175 | IrInst::LoadF64(_, _)
176 | IrInst::LoadBool(_, _) => vec![],
177
178 IrInst::Not(_, src)
179 | IrInst::I64ToF64(_, src)
180 | IrInst::F64ToI64(_, src)
181 | IrInst::Ret(src) => vec![*src],
182
183 IrInst::EqI64(_, a, b)
184 | IrInst::NeI64(_, a, b)
185 | IrInst::LtI64(_, a, b)
186 | IrInst::LeI64(_, a, b)
187 | IrInst::GtI64(_, a, b)
188 | IrInst::GeI64(_, a, b)
189 | IrInst::EqF64(_, a, b)
190 | IrInst::NeF64(_, a, b)
191 | IrInst::LtF64(_, a, b)
192 | IrInst::LeF64(_, a, b)
193 | IrInst::GtF64(_, a, b)
194 | IrInst::GeF64(_, a, b)
195 | IrInst::And(_, a, b)
196 | IrInst::Or(_, a, b)
197 | IrInst::AddI64(_, a, b)
198 | IrInst::SubI64(_, a, b)
199 | IrInst::MulI64(_, a, b)
200 | IrInst::AddF64(_, a, b)
201 | IrInst::SubF64(_, a, b)
202 | IrInst::MulF64(_, a, b)
203 | IrInst::DivF64(_, a, b) => vec![*a, *b],
204 }
205 }
206}
207
208#[derive(Debug, Clone)]
210pub struct IrFunction {
211 pub instructions: Vec<IrInst>,
213 pub reg_types: HashMap<Reg, IrType>,
215 pub field_types: Vec<IrType>,
217 pub num_regs: u32,
219}
220
221impl IrFunction {
222 pub fn new() -> Self {
224 Self {
225 instructions: Vec::new(),
226 reg_types: HashMap::new(),
227 field_types: Vec::new(),
228 num_regs: 0,
229 }
230 }
231
232 pub fn alloc_reg(&mut self, ty: IrType) -> Reg {
234 let reg = Reg(self.num_regs);
235 self.num_regs += 1;
236 self.reg_types.insert(reg, ty);
237 reg
238 }
239
240 pub fn add_field(&mut self, ty: IrType) -> u32 {
242 let idx = self.field_types.len() as u32;
243 self.field_types.push(ty);
244 idx
245 }
246
247 pub fn push(&mut self, inst: IrInst) {
249 self.instructions.push(inst);
250 }
251
252 pub fn len(&self) -> usize {
254 self.instructions.len()
255 }
256
257 pub fn is_empty(&self) -> bool {
259 self.instructions.is_empty()
260 }
261
262 pub fn validate(&self) -> Result<(), String> {
264 let mut defined: std::collections::HashSet<Reg> = std::collections::HashSet::new();
266
267 for inst in &self.instructions {
268 for src in inst.sources() {
270 if !defined.contains(&src) {
271 return Err(format!("register {:?} used before definition", src));
272 }
273 }
274
275 if let Some(dest) = inst.dest() {
277 defined.insert(dest);
278 }
279 }
280
281 if let Some(last) = self.instructions.last() {
283 if !matches!(last, IrInst::Ret(_)) {
284 return Err("function must end with Ret".to_string());
285 }
286 } else {
287 return Err("function is empty".to_string());
288 }
289
290 Ok(())
291 }
292}
293
294impl Default for IrFunction {
295 fn default() -> Self {
296 Self::new()
297 }
298}
299
300#[derive(Debug, Clone, Copy)]
302pub enum RtValue {
303 Bool(bool),
304 I64(i64),
305 F64(f64),
306}
307
308impl RtValue {
309 pub fn as_bool(&self) -> bool {
310 match self {
311 RtValue::Bool(b) => *b,
312 RtValue::I64(i) => *i != 0,
313 RtValue::F64(f) => *f != 0.0,
314 }
315 }
316
317 pub fn as_i64(&self) -> i64 {
318 match self {
319 RtValue::Bool(b) => {
320 if *b {
321 1
322 } else {
323 0
324 }
325 }
326 RtValue::I64(i) => *i,
327 RtValue::F64(f) => *f as i64,
328 }
329 }
330
331 pub fn as_f64(&self) -> f64 {
332 match self {
333 RtValue::Bool(b) => {
334 if *b {
335 1.0
336 } else {
337 0.0
338 }
339 }
340 RtValue::I64(i) => *i as f64,
341 RtValue::F64(f) => *f,
342 }
343 }
344}
345
346pub trait FieldAccess {
348 fn get_i64(&self, field: u32) -> i64;
349 fn get_f64(&self, field: u32) -> f64;
350 fn get_bool(&self, field: u32) -> bool;
351}
352
353pub struct ArrayFields {
355 i64_fields: Vec<i64>,
356 f64_fields: Vec<f64>,
357 bool_fields: Vec<bool>,
358}
359
360impl ArrayFields {
361 pub fn new() -> Self {
362 Self {
363 i64_fields: Vec::new(),
364 f64_fields: Vec::new(),
365 bool_fields: Vec::new(),
366 }
367 }
368
369 pub fn set_i64(&mut self, idx: usize, value: i64) {
370 if idx >= self.i64_fields.len() {
371 self.i64_fields.resize(idx + 1, 0);
372 }
373 self.i64_fields[idx] = value;
374 }
375
376 pub fn set_f64(&mut self, idx: usize, value: f64) {
377 if idx >= self.f64_fields.len() {
378 self.f64_fields.resize(idx + 1, 0.0);
379 }
380 self.f64_fields[idx] = value;
381 }
382
383 pub fn set_bool(&mut self, idx: usize, value: bool) {
384 if idx >= self.bool_fields.len() {
385 self.bool_fields.resize(idx + 1, false);
386 }
387 self.bool_fields[idx] = value;
388 }
389}
390
391impl Default for ArrayFields {
392 fn default() -> Self {
393 Self::new()
394 }
395}
396
397impl FieldAccess for ArrayFields {
398 fn get_i64(&self, field: u32) -> i64 {
399 self.i64_fields.get(field as usize).copied().unwrap_or(0)
400 }
401
402 fn get_f64(&self, field: u32) -> f64 {
403 self.f64_fields.get(field as usize).copied().unwrap_or(0.0)
404 }
405
406 fn get_bool(&self, field: u32) -> bool {
407 self.bool_fields
408 .get(field as usize)
409 .copied()
410 .unwrap_or(false)
411 }
412}
413
414pub struct IrInterpreter {
416 function: IrFunction,
418}
419
420impl IrInterpreter {
421 pub fn new(function: IrFunction) -> Result<Self, String> {
423 function.validate()?;
424 Ok(Self { function })
425 }
426
427 pub fn execute<F: FieldAccess>(&self, fields: &F) -> bool {
429 let mut regs: Vec<RtValue> = vec![RtValue::Bool(false); self.function.num_regs as usize];
430
431 for inst in &self.function.instructions {
432 match inst {
433 IrInst::ConstBool(dest, val) => {
434 regs[dest.0 as usize] = RtValue::Bool(*val);
435 }
436 IrInst::ConstI64(dest, val) => {
437 regs[dest.0 as usize] = RtValue::I64(*val);
438 }
439 IrInst::ConstF64(dest, val) => {
440 regs[dest.0 as usize] = RtValue::F64(*val);
441 }
442 IrInst::LoadI64(dest, field) => {
443 regs[dest.0 as usize] = RtValue::I64(fields.get_i64(*field));
444 }
445 IrInst::LoadF64(dest, field) => {
446 regs[dest.0 as usize] = RtValue::F64(fields.get_f64(*field));
447 }
448 IrInst::LoadBool(dest, field) => {
449 regs[dest.0 as usize] = RtValue::Bool(fields.get_bool(*field));
450 }
451 IrInst::EqI64(dest, a, b) => {
452 let result = regs[a.0 as usize].as_i64() == regs[b.0 as usize].as_i64();
453 regs[dest.0 as usize] = RtValue::Bool(result);
454 }
455 IrInst::NeI64(dest, a, b) => {
456 let result = regs[a.0 as usize].as_i64() != regs[b.0 as usize].as_i64();
457 regs[dest.0 as usize] = RtValue::Bool(result);
458 }
459 IrInst::LtI64(dest, a, b) => {
460 let result = regs[a.0 as usize].as_i64() < regs[b.0 as usize].as_i64();
461 regs[dest.0 as usize] = RtValue::Bool(result);
462 }
463 IrInst::LeI64(dest, a, b) => {
464 let result = regs[a.0 as usize].as_i64() <= regs[b.0 as usize].as_i64();
465 regs[dest.0 as usize] = RtValue::Bool(result);
466 }
467 IrInst::GtI64(dest, a, b) => {
468 let result = regs[a.0 as usize].as_i64() > regs[b.0 as usize].as_i64();
469 regs[dest.0 as usize] = RtValue::Bool(result);
470 }
471 IrInst::GeI64(dest, a, b) => {
472 let result = regs[a.0 as usize].as_i64() >= regs[b.0 as usize].as_i64();
473 regs[dest.0 as usize] = RtValue::Bool(result);
474 }
475 IrInst::EqF64(dest, a, b) => {
476 let result = regs[a.0 as usize].as_f64() == regs[b.0 as usize].as_f64();
477 regs[dest.0 as usize] = RtValue::Bool(result);
478 }
479 IrInst::NeF64(dest, a, b) => {
480 let result = regs[a.0 as usize].as_f64() != regs[b.0 as usize].as_f64();
481 regs[dest.0 as usize] = RtValue::Bool(result);
482 }
483 IrInst::LtF64(dest, a, b) => {
484 let result = regs[a.0 as usize].as_f64() < regs[b.0 as usize].as_f64();
485 regs[dest.0 as usize] = RtValue::Bool(result);
486 }
487 IrInst::LeF64(dest, a, b) => {
488 let result = regs[a.0 as usize].as_f64() <= regs[b.0 as usize].as_f64();
489 regs[dest.0 as usize] = RtValue::Bool(result);
490 }
491 IrInst::GtF64(dest, a, b) => {
492 let result = regs[a.0 as usize].as_f64() > regs[b.0 as usize].as_f64();
493 regs[dest.0 as usize] = RtValue::Bool(result);
494 }
495 IrInst::GeF64(dest, a, b) => {
496 let result = regs[a.0 as usize].as_f64() >= regs[b.0 as usize].as_f64();
497 regs[dest.0 as usize] = RtValue::Bool(result);
498 }
499 IrInst::And(dest, a, b) => {
500 let result = regs[a.0 as usize].as_bool() && regs[b.0 as usize].as_bool();
501 regs[dest.0 as usize] = RtValue::Bool(result);
502 }
503 IrInst::Or(dest, a, b) => {
504 let result = regs[a.0 as usize].as_bool() || regs[b.0 as usize].as_bool();
505 regs[dest.0 as usize] = RtValue::Bool(result);
506 }
507 IrInst::Not(dest, src) => {
508 let result = !regs[src.0 as usize].as_bool();
509 regs[dest.0 as usize] = RtValue::Bool(result);
510 }
511 IrInst::AddI64(dest, a, b) => {
512 let result = regs[a.0 as usize]
513 .as_i64()
514 .wrapping_add(regs[b.0 as usize].as_i64());
515 regs[dest.0 as usize] = RtValue::I64(result);
516 }
517 IrInst::SubI64(dest, a, b) => {
518 let result = regs[a.0 as usize]
519 .as_i64()
520 .wrapping_sub(regs[b.0 as usize].as_i64());
521 regs[dest.0 as usize] = RtValue::I64(result);
522 }
523 IrInst::MulI64(dest, a, b) => {
524 let result = regs[a.0 as usize]
525 .as_i64()
526 .wrapping_mul(regs[b.0 as usize].as_i64());
527 regs[dest.0 as usize] = RtValue::I64(result);
528 }
529 IrInst::AddF64(dest, a, b) => {
530 let result = regs[a.0 as usize].as_f64() + regs[b.0 as usize].as_f64();
531 regs[dest.0 as usize] = RtValue::F64(result);
532 }
533 IrInst::SubF64(dest, a, b) => {
534 let result = regs[a.0 as usize].as_f64() - regs[b.0 as usize].as_f64();
535 regs[dest.0 as usize] = RtValue::F64(result);
536 }
537 IrInst::MulF64(dest, a, b) => {
538 let result = regs[a.0 as usize].as_f64() * regs[b.0 as usize].as_f64();
539 regs[dest.0 as usize] = RtValue::F64(result);
540 }
541 IrInst::DivF64(dest, a, b) => {
542 let result = regs[a.0 as usize].as_f64() / regs[b.0 as usize].as_f64();
543 regs[dest.0 as usize] = RtValue::F64(result);
544 }
545 IrInst::I64ToF64(dest, src) => {
546 regs[dest.0 as usize] = RtValue::F64(regs[src.0 as usize].as_i64() as f64);
547 }
548 IrInst::F64ToI64(dest, src) => {
549 regs[dest.0 as usize] = RtValue::I64(regs[src.0 as usize].as_f64() as i64);
550 }
551 IrInst::Ret(src) => {
552 return regs[src.0 as usize].as_bool();
553 }
554 }
555 }
556
557 false
558 }
559}
560
561pub struct IrBuilder {
563 function: IrFunction,
564}
565
566impl IrBuilder {
567 pub fn new() -> Self {
569 Self {
570 function: IrFunction::new(),
571 }
572 }
573
574 pub fn define_field(&mut self, ty: IrType) -> u32 {
576 self.function.add_field(ty)
577 }
578
579 pub fn load_i64(&mut self, field: u32) -> Reg {
581 let reg = self.function.alloc_reg(IrType::I64);
582 self.function.push(IrInst::LoadI64(reg, field));
583 reg
584 }
585
586 pub fn load_f64(&mut self, field: u32) -> Reg {
588 let reg = self.function.alloc_reg(IrType::F64);
589 self.function.push(IrInst::LoadF64(reg, field));
590 reg
591 }
592
593 pub fn const_i64(&mut self, val: i64) -> Reg {
595 let reg = self.function.alloc_reg(IrType::I64);
596 self.function.push(IrInst::ConstI64(reg, val));
597 reg
598 }
599
600 pub fn const_f64(&mut self, val: f64) -> Reg {
602 let reg = self.function.alloc_reg(IrType::F64);
603 self.function.push(IrInst::ConstF64(reg, val));
604 reg
605 }
606
607 pub fn const_bool(&mut self, val: bool) -> Reg {
609 let reg = self.function.alloc_reg(IrType::Bool);
610 self.function.push(IrInst::ConstBool(reg, val));
611 reg
612 }
613
614 pub fn ge_i64(&mut self, a: Reg, b: Reg) -> Reg {
616 let reg = self.function.alloc_reg(IrType::Bool);
617 self.function.push(IrInst::GeI64(reg, a, b));
618 reg
619 }
620
621 pub fn gt_i64(&mut self, a: Reg, b: Reg) -> Reg {
623 let reg = self.function.alloc_reg(IrType::Bool);
624 self.function.push(IrInst::GtI64(reg, a, b));
625 reg
626 }
627
628 pub fn lt_i64(&mut self, a: Reg, b: Reg) -> Reg {
630 let reg = self.function.alloc_reg(IrType::Bool);
631 self.function.push(IrInst::LtI64(reg, a, b));
632 reg
633 }
634
635 pub fn gt_f64(&mut self, a: Reg, b: Reg) -> Reg {
637 let reg = self.function.alloc_reg(IrType::Bool);
638 self.function.push(IrInst::GtF64(reg, a, b));
639 reg
640 }
641
642 pub fn and(&mut self, a: Reg, b: Reg) -> Reg {
644 let reg = self.function.alloc_reg(IrType::Bool);
645 self.function.push(IrInst::And(reg, a, b));
646 reg
647 }
648
649 pub fn or(&mut self, a: Reg, b: Reg) -> Reg {
651 let reg = self.function.alloc_reg(IrType::Bool);
652 self.function.push(IrInst::Or(reg, a, b));
653 reg
654 }
655
656 pub fn not(&mut self, a: Reg) -> Reg {
658 let reg = self.function.alloc_reg(IrType::Bool);
659 self.function.push(IrInst::Not(reg, a));
660 reg
661 }
662
663 pub fn ret(&mut self, reg: Reg) {
665 self.function.push(IrInst::Ret(reg));
666 }
667
668 pub fn build(self) -> IrFunction {
670 self.function
671 }
672}
673
674impl Default for IrBuilder {
675 fn default() -> Self {
676 Self::new()
677 }
678}
679
680pub struct CompiledFilter {
682 interpreter: IrInterpreter,
684 exec_count: std::sync::atomic::AtomicU64,
686}
687
688impl CompiledFilter {
689 pub fn compile(function: IrFunction) -> Result<Self, String> {
691 let interpreter = IrInterpreter::new(function)?;
692 Ok(Self {
693 interpreter,
694 exec_count: std::sync::atomic::AtomicU64::new(0),
695 })
696 }
697
698 pub fn execute<F: FieldAccess>(&self, fields: &F) -> bool {
700 self.exec_count
701 .fetch_add(1, std::sync::atomic::Ordering::Relaxed);
702 self.interpreter.execute(fields)
703 }
704
705 pub fn exec_count(&self) -> u64 {
707 self.exec_count.load(std::sync::atomic::Ordering::Relaxed)
708 }
709
710 pub fn is_hot(&self) -> bool {
712 self.exec_count() > 1000
713 }
714}
715
716pub struct FilterCache {
718 cache: std::sync::RwLock<HashMap<String, Arc<CompiledFilter>>>,
719}
720
721impl FilterCache {
722 pub fn new() -> Self {
724 Self {
725 cache: std::sync::RwLock::new(HashMap::new()),
726 }
727 }
728
729 pub fn get_or_compile(
731 &self,
732 key: &str,
733 build_fn: impl FnOnce() -> IrFunction,
734 ) -> Result<Arc<CompiledFilter>, String> {
735 {
737 let cache = self.cache.read().unwrap();
738 if let Some(filter) = cache.get(key) {
739 return Ok(Arc::clone(filter));
740 }
741 }
742
743 let function = build_fn();
745 let filter = Arc::new(CompiledFilter::compile(function)?);
746
747 {
749 let mut cache = self.cache.write().unwrap();
750 cache.insert(key.to_string(), Arc::clone(&filter));
751 }
752
753 Ok(filter)
754 }
755
756 pub fn len(&self) -> usize {
758 self.cache.read().unwrap().len()
759 }
760
761 pub fn is_empty(&self) -> bool {
763 self.cache.read().unwrap().is_empty()
764 }
765
766 pub fn clear(&self) {
768 self.cache.write().unwrap().clear();
769 }
770}
771
772impl Default for FilterCache {
773 fn default() -> Self {
774 Self::new()
775 }
776}
777
778#[cfg(test)]
779mod tests {
780 use super::*;
781
782 #[test]
783 fn test_simple_filter() {
784 let mut builder = IrBuilder::new();
786 let age_field = builder.define_field(IrType::I64);
787
788 let age = builder.load_i64(age_field);
789 let threshold = builder.const_i64(18);
790 let result = builder.ge_i64(age, threshold);
791 builder.ret(result);
792
793 let func = builder.build();
794 let interp = IrInterpreter::new(func).unwrap();
795
796 let mut fields = ArrayFields::new();
797 fields.set_i64(0, 21);
798 assert!(interp.execute(&fields));
799
800 fields.set_i64(0, 16);
801 assert!(!interp.execute(&fields));
802 }
803
804 #[test]
805 fn test_compound_filter() {
806 let mut builder = IrBuilder::new();
808 let age_field = builder.define_field(IrType::I64);
809 let score_field = builder.define_field(IrType::F64);
810
811 let age = builder.load_i64(age_field);
812 let age_threshold = builder.const_i64(18);
813 let age_ok = builder.ge_i64(age, age_threshold);
814
815 let score = builder.load_f64(score_field);
816 let score_threshold = builder.const_f64(50.0);
817 let score_ok = builder.gt_f64(score, score_threshold);
818
819 let result = builder.and(age_ok, score_ok);
820 builder.ret(result);
821
822 let func = builder.build();
823 let interp = IrInterpreter::new(func).unwrap();
824
825 let mut fields = ArrayFields::new();
826
827 fields.set_i64(age_field as usize, 21);
829 fields.set_f64(score_field as usize, 75.0);
830 assert!(interp.execute(&fields));
831
832 fields.set_i64(age_field as usize, 16);
834 fields.set_f64(score_field as usize, 75.0);
835 assert!(!interp.execute(&fields));
836
837 fields.set_i64(age_field as usize, 21);
839 fields.set_f64(score_field as usize, 30.0);
840 assert!(!interp.execute(&fields));
841 }
842
843 #[test]
844 fn test_or_filter() {
845 let mut builder = IrBuilder::new();
847 let age_field = builder.define_field(IrType::I64);
848
849 let age = builder.load_i64(age_field);
850 let low = builder.const_i64(18);
851 let high = builder.const_i64(65);
852
853 let too_young = builder.lt_i64(age, low);
854 let too_old = builder.gt_i64(age, high);
855 let result = builder.or(too_young, too_old);
856 builder.ret(result);
857
858 let func = builder.build();
859 let interp = IrInterpreter::new(func).unwrap();
860
861 let mut fields = ArrayFields::new();
862
863 fields.set_i64(0, 10);
864 assert!(interp.execute(&fields)); fields.set_i64(0, 70);
867 assert!(interp.execute(&fields)); fields.set_i64(0, 30);
870 assert!(!interp.execute(&fields)); }
872
873 #[test]
874 fn test_not_filter() {
875 let mut builder = IrBuilder::new();
877 let age_field = builder.define_field(IrType::I64);
878
879 let age = builder.load_i64(age_field);
880 let threshold = builder.const_i64(18);
881 let too_young = builder.lt_i64(age, threshold);
882 let result = builder.not(too_young);
883 builder.ret(result);
884
885 let func = builder.build();
886 let interp = IrInterpreter::new(func).unwrap();
887
888 let mut fields = ArrayFields::new();
889
890 fields.set_i64(0, 10);
891 assert!(!interp.execute(&fields)); fields.set_i64(0, 21);
894 assert!(interp.execute(&fields)); }
896
897 #[test]
898 fn test_validation() {
899 let func = IrFunction::new();
901 assert!(func.validate().is_err());
902
903 let mut func = IrFunction::new();
905 func.push(IrInst::ConstBool(Reg(0), true));
906 assert!(func.validate().is_err());
907
908 let mut func = IrFunction::new();
910 func.push(IrInst::Ret(Reg(99)));
911 assert!(func.validate().is_err());
912
913 let mut func = IrFunction::new();
915 func.alloc_reg(IrType::Bool);
916 func.push(IrInst::ConstBool(Reg(0), true));
917 func.push(IrInst::Ret(Reg(0)));
918 assert!(func.validate().is_ok());
919 }
920
921 #[test]
922 fn test_compiled_filter() {
923 let mut builder = IrBuilder::new();
924 let field = builder.define_field(IrType::I64);
925 let val = builder.load_i64(field);
926 let threshold = builder.const_i64(10);
927 let result = builder.ge_i64(val, threshold);
928 builder.ret(result);
929
930 let filter = CompiledFilter::compile(builder.build()).unwrap();
931
932 let mut fields = ArrayFields::new();
933 fields.set_i64(0, 15);
934
935 assert!(filter.execute(&fields));
936 assert_eq!(filter.exec_count(), 1);
937
938 filter.execute(&fields);
939 filter.execute(&fields);
940 assert_eq!(filter.exec_count(), 3);
941 }
942
943 #[test]
944 fn test_filter_cache() {
945 let cache = FilterCache::new();
946
947 let filter1 = cache
948 .get_or_compile("age_filter", || {
949 let mut builder = IrBuilder::new();
950 let field = builder.define_field(IrType::I64);
951 let val = builder.load_i64(field);
952 let threshold = builder.const_i64(18);
953 let result = builder.ge_i64(val, threshold);
954 builder.ret(result);
955 builder.build()
956 })
957 .unwrap();
958
959 let filter2 = cache
960 .get_or_compile("age_filter", || {
961 panic!("should not be called - filter should be cached");
962 })
963 .unwrap();
964
965 assert!(Arc::ptr_eq(&filter1, &filter2));
966 assert_eq!(cache.len(), 1);
967 }
968
969 #[test]
970 fn test_ir_type_size() {
971 assert_eq!(IrType::Bool.size(), 1);
972 assert_eq!(IrType::I64.size(), 8);
973 assert_eq!(IrType::F64.size(), 8);
974 assert_eq!(IrType::Ptr.size(), 8);
975 }
976
977 #[test]
978 fn test_instruction_sources() {
979 let inst = IrInst::And(Reg(2), Reg(0), Reg(1));
980 assert_eq!(inst.sources(), vec![Reg(0), Reg(1)]);
981 assert_eq!(inst.dest(), Some(Reg(2)));
982
983 let inst = IrInst::ConstI64(Reg(0), 42);
984 assert!(inst.sources().is_empty());
985 assert_eq!(inst.dest(), Some(Reg(0)));
986
987 let inst = IrInst::Ret(Reg(0));
988 assert_eq!(inst.sources(), vec![Reg(0)]);
989 assert_eq!(inst.dest(), None);
990 }
991
992 #[test]
993 #[allow(clippy::approx_constant)] fn test_rt_value_conversions() {
995 assert!(RtValue::Bool(true).as_bool());
996 assert!(!RtValue::Bool(false).as_bool());
997 assert!(RtValue::I64(1).as_bool());
998 assert!(!RtValue::I64(0).as_bool());
999
1000 assert_eq!(RtValue::I64(42).as_i64(), 42);
1001 assert_eq!(RtValue::F64(3.14).as_f64(), 3.14);
1002 assert_eq!(RtValue::I64(10).as_f64(), 10.0);
1003 }
1004}