Skip to main content

qcode_emulator/
concrete.rs

1use crate::{
2    CallContinuation, CallInterception, CallSite, DomainMemory, EmulatorError, EmulatorErrorKind,
3    Interpreter,
4};
5use qcode::{
6    address_index::{AddressIndex, AddressTarget},
7    context::Context,
8    space::{MemorySpaceId, Space, SpaceId, SpaceType},
9    value::{
10        BasicBlock, BlockId, BlockParamId, BlockRef, FunctionBody, FunctionId, Instruction,
11        LocalValueId, Value, ValueId, ValueRef, Varnode,
12        insn::{
13            Branch, BranchInd, CBranch, Call, CallInd, Callee, Carry, Extract, InstructionId,
14            InstructionRef, IntBinop, LzCount, Mnemonic, PopCount, Range, Return, SBorrow, SCarry,
15            Scan, Sext, Store, Tuple, Unop, Zext,
16        },
17        varnode::{VarnodeId, register::RegisterId},
18    },
19};
20use std::cmp;
21
22mod float80;
23
24use rustc_apfloat::{
25    Float, FloatConvert, Round, Status,
26    ieee::{Double, Single, X87DoubleExtended},
27};
28use rustc_hash::{FxHashMap, FxHashSet};
29
30use super::DomainValue;
31
32fn require_real_callee(callee: Callee) -> Result<FunctionId, EmulatorErrorKind> {
33    match callee {
34        Callee::Real(target) => Ok(target),
35        Callee::Minted(slot) => Err(EmulatorErrorKind::UnresolvedMintedCallee(slot)),
36    }
37}
38
39/// Whether the call instruction `call_id` carries the `regpure` binding
40/// convention (argpromote v2): its register interface is explicit at the site,
41/// so inputs are bound positionally from `Call.args` and outputs are replayed by
42/// the caller rather than by the emulator's implicit writeback.
43fn call_is_regpure(ctx: &Context<'_>, call_id: InstructionId) -> bool {
44    matches!(
45        ctx.get_insn(call_id).mnemonic(),
46        Mnemonic::Call(call) if call.tag.is_regpure()
47    )
48}
49
50#[derive(Debug, Default, Clone)]
51pub struct EmulatedSpace(FxHashMap<u64, u8>);
52
53impl EmulatedSpace {
54    pub fn read_byte(&self, addr: u64) -> Result<u8, EmulatorErrorKind> {
55        self.0
56            .get(&addr)
57            .copied()
58            .ok_or(EmulatorErrorKind::MemoryReadError(addr))
59    }
60
61    pub fn read(&self, addr: u64, size: usize) -> Result<Vec<u8>, EmulatorErrorKind> {
62        (0..size).map(|i| self.read_byte(addr + i as u64)).collect()
63    }
64
65    pub fn read_zero_filled(&self, addr: u64, size: usize) -> Vec<u8> {
66        (0..size)
67            .map(|i| self.0.get(&(addr + i as u64)).copied().unwrap_or(0))
68            .collect()
69    }
70
71    pub fn write_byte(&mut self, addr: u64, value: u8) {
72        self.0.insert(addr, value);
73    }
74
75    /// Reserves capacity for at least `additional` more bytes, so a bulk write
76    /// allocates once instead of rehashing the table on the way up.
77    pub fn reserve(&mut self, additional: usize) {
78        self.0.reserve(additional);
79    }
80
81    /// Returns an editable region of this space for the given address and size.
82    /// If `addr + size` would overflow, a zero-size region is returned (reads yield 0, writes are no-ops).
83    pub fn get_mut_region(
84        &mut self,
85        addr: u64,
86        size: usize,
87    ) -> Result<EmulatedSpaceRegion<'_>, EmulatorErrorKind> {
88        let end = addr
89            .checked_add(size as u64)
90            .ok_or(EmulatorErrorKind::AddressOverflow(addr, size))?;
91        Ok(EmulatedSpaceRegion::new(self, addr, end))
92    }
93
94    /// Reads a little-endian unsigned integer from the region
95    pub fn read_u128(&self, addr: u64, size: u64) -> Result<u128, EmulatorErrorKind> {
96        let mut res = 0u128;
97
98        for cur in addr..addr + cmp::min(size, 16) {
99            let byte = self.read_byte(cur)?;
100            res |= u128::from(byte) << ((cur - addr) * 8);
101        }
102
103        Ok(res)
104    }
105
106    /// Reads a little-endian unsigned integer, treating missing bytes as zero.
107    pub fn read_u128_zero_filled(&self, addr: u64, size: u64) -> u128 {
108        let mut res = 0u128;
109
110        for cur in addr..addr + cmp::min(size, 16) {
111            let byte = self.0.get(&cur).copied().unwrap_or(0);
112            res |= u128::from(byte) << ((cur - addr) * 8);
113        }
114
115        res
116    }
117}
118
119/// An exclusive region of an emulated space, used for reading/writing a contiguous range of addresses in a space.
120pub struct EmulatedSpaceRegion<'space> {
121    space: &'space mut EmulatedSpace,
122    start: u64,
123    end: u64,
124}
125
126impl<'space> EmulatedSpaceRegion<'space> {
127    pub fn new(space: &'space mut EmulatedSpace, start: u64, end: u64) -> Self {
128        Self { space, start, end }
129    }
130
131    /// The size of the region in bytes
132    pub fn size(&self) -> usize {
133        (self.end - self.start) as usize
134    }
135
136    /// Writes a little-endian unsigned integer to the region
137    pub fn write_u128(&mut self, value: u128) {
138        let end = self.start + cmp::min(16, self.size()) as u64;
139        for addr in self.start..end {
140            let byte = u8::try_from((value >> ((addr - self.start) * 8)) & 0xffu128).unwrap();
141            self.space.write_byte(addr, byte);
142        }
143    }
144}
145
146#[derive(Debug, Default, Clone)]
147pub struct EmulatedMemory {
148    spaces: FxHashMap<MemorySpaceId, EmulatedSpace>,
149    zero_filled_spaces: FxHashSet<MemorySpaceId>,
150    /// Space count the zero-fill set was last built for. Spaces are append-only
151    /// and their type is fixed at creation, so an unchanged count means the set
152    /// is still valid — this keeps the per-step call O(1) instead of rescanning.
153    configured_space_count: Option<usize>,
154}
155
156impl EmulatedMemory {
157    fn is_zero_filled(&self, space: MemorySpaceId) -> bool {
158        matches!(space, MemorySpaceId::Temp(_)) || self.zero_filled_spaces.contains(&space)
159    }
160
161    fn configure_spaces(&mut self, ctx: &Context<'_>) {
162        let space_count = ctx.space_count();
163        if self.configured_space_count == Some(space_count) {
164            return;
165        }
166        self.zero_filled_spaces.clear();
167        for index in 0..space_count {
168            let id = SpaceId::from(index);
169            let space = Space::from_id(ctx, id);
170            // x86's private x87 RAM file is architectural state like the
171            // register space, not process memory. A fresh CPU state has zero
172            // payload bytes there, so instructions such as FXSAVE can read it
173            // before a harness explicitly seeds an f80 slot.
174            if matches!(space.ty, SpaceType::Register) || space.name.as_deref() == Some("x87") {
175                self.zero_filled_spaces.insert(id.into());
176            }
177        }
178        self.configured_space_count = Some(space_count);
179    }
180
181    fn read_raw(
182        &self,
183        space: MemorySpaceId,
184        addr: u64,
185        size: usize,
186    ) -> Result<Vec<u8>, EmulatorErrorKind> {
187        match self.spaces.get(&space) {
188            Some(value) if self.is_zero_filled(space) => Ok(value.read_zero_filled(addr, size)),
189            Some(value) => value.read(addr, size),
190            None if self.is_zero_filled(space) => Ok(vec![0; size]),
191            None => Err(EmulatorErrorKind::UnknownSpace(space)),
192        }
193    }
194}
195
196fn bool_to_u64(value: bool) -> u64 {
197    if value { 1 } else { 0 }
198}
199
200fn mask_for_size(size: usize) -> u128 {
201    let bits = size.saturating_mul(8);
202    if bits >= u128::BITS as usize {
203        u128::MAX
204    } else if bits == 0 {
205        0u128
206    } else {
207        (1u128 << bits) - 1
208    }
209}
210
211fn u128_to_u64(value: u128) -> u64 {
212    u64::try_from(value & u128::from(u64::MAX)).unwrap()
213}
214
215#[derive(Debug, Clone, Copy)]
216pub struct SizedValue {
217    /// Raw bits for this value
218    value: u128,
219
220    /// Number of bytes that are actually used in this value.
221    size: u8,
222}
223
224impl SizedValue {
225    pub fn new(value: u64, size: usize) -> Self {
226        let size = cmp::min(size, 16) as u8;
227        let value = u128::from(value) & mask_for_size(size as usize);
228        Self { value, size }
229    }
230
231    pub fn from_bits(value: u128, size: usize) -> Self {
232        let size = cmp::min(size, 16) as u8;
233        let value = value & mask_for_size(size as usize);
234        Self { value, size }
235    }
236
237    fn as_u64(&self) -> u64 {
238        u128_to_u64(self.value & mask_for_size(self.size as usize))
239    }
240
241    pub fn as_bits(&self) -> u128 {
242        self.value & mask_for_size(self.size as usize)
243    }
244
245    fn signed_value(&self) -> i128 {
246        let bits = (self.size as usize).saturating_mul(8);
247        if bits == 0 {
248            return i128::from(0i8);
249        }
250        if bits >= u128::BITS as usize {
251            return self.as_bits() as i128;
252        }
253
254        let value = self.as_bits();
255        let sign_bit = u128::from(1u8) << (bits - 1);
256        let extended = if (value & sign_bit) != u128::from(0u8) {
257            value | !mask_for_size(self.size as usize)
258        } else {
259            value
260        };
261        extended as i128
262    }
263
264    /// Size, in bytes, at which a two-operand integer op (add/sub/and/.../carry/
265    /// scarry/sborrow) is evaluated.
266    ///
267    /// These p-code ops require both operands to share a size, so well-formed IR
268    /// always has `self.size == other.size`. When the lifter leaves them
269    /// mismatched it is the *left* operand that carries the operative width: it
270    /// is the destination/base that the right operand is being combined into
271    /// (e.g. address arithmetic `int_add(base:8, disp:4)` must stay 8 bytes, not
272    /// truncate to the displacement). The narrower side here is an immediate
273    /// whose value already fits, so taking the left width is correct.
274    ///
275    /// Cases where the *immediate* is on the left and wider than the real
276    /// operand — NEG's `OF = sborrow(0, AL)` — are instead fixed upstream, by
277    /// sizing the immediate to its sibling in the lifter, so this function never
278    /// sees that mismatch. See `emit_function_call` in `harbinger::emit`.
279    fn widen_size(&self, _other: &Self) -> usize {
280        self.size as usize
281    }
282
283    fn from_f80_bits(value: u128) -> Self {
284        Self::from_bits(value, 10)
285    }
286
287    fn f64_from_self(&self) -> f64 {
288        match self.size as usize {
289            0..=4 => f32::from_bits(self.as_u64() as u32) as f64,
290            8 => f64::from_bits(self.as_u64()),
291            10 => float80::to_f64(self.as_bits()),
292            _ => 0.0,
293        }
294    }
295
296    fn from_f64(value: f64, size: usize) -> Self {
297        match size {
298            0..=4 => Self::new((value as f32).to_bits() as u64, 4),
299            8 => Self::new(value.to_bits(), 8),
300            10 => Self::from_f80_bits(float80::from_f64(value)),
301            _ => Self::new(0, size),
302        }
303    }
304}
305
306impl DomainValue for SizedValue {
307    fn size(&self) -> Result<usize, EmulatorErrorKind> {
308        Ok(self.size as usize)
309    }
310
311    fn value(&self) -> Result<u64, EmulatorErrorKind> {
312        let value = self.as_bits();
313        if value > u128::from(u64::MAX) {
314            Err(EmulatorErrorKind::ValueError(value)) // Replace with appropriate error
315        } else {
316            Ok(u64::try_from(value).unwrap())
317        }
318    }
319
320    fn from_u64(value: u64) -> Self {
321        Self::new(value, 8)
322    }
323
324    fn zero(size: usize) -> Self {
325        Self::new(0, size)
326    }
327
328    fn is_float_nan(&self) -> Result<Self, EmulatorErrorKind> {
329        let is_nan = if self.size == 10 {
330            float80::is_nan(self.as_bits())
331        } else {
332            self.f64_from_self().is_nan()
333        };
334        Ok(Self::new(bool_to_u64(is_nan), 1))
335    }
336
337    fn int_to_float(&self, size: usize) -> Result<Self, EmulatorErrorKind> {
338        let signed = self.signed_value();
339        match size {
340            4 => Ok(Self::new((signed as f32).to_bits() as u64, 4)),
341            8 => Ok(Self::new((signed as f64).to_bits(), 8)),
342            10 => Ok(Self::from_f80_bits(float80::from_i128(signed))),
343            _ => Ok(Self::new(0, size)),
344        }
345    }
346
347    fn float_to_float(&self, size: usize) -> Result<Self, EmulatorErrorKind> {
348        if size == 10 {
349            let value = match self.size as usize {
350                0..=4 => float80::from_f32_bits(self.as_u64() as u32),
351                8 => float80::from_f64(f64::from_bits(self.as_u64())),
352                10 => self.as_bits(),
353                _ => 0,
354            };
355            return Ok(Self::from_f80_bits(value));
356        }
357        match size {
358            4 => Ok(Self::new((self.f64_from_self() as f32).to_bits() as u64, 4)),
359            8 => Ok(Self::new(self.f64_from_self().to_bits(), 8)),
360            _ => Ok(Self::new(0, size)),
361        }
362    }
363
364    fn float_to_int(&self, size: usize) -> Result<Self, EmulatorErrorKind> {
365        let value = if self.size == 10 {
366            float80::to_i128(self.as_bits(), size * 8) as u64
367        } else {
368            self.f64_from_self() as i64 as u64
369        };
370        Ok(Self::new(value, size))
371    }
372
373    fn zext(&self, size: usize) -> Result<Self, EmulatorErrorKind> {
374        Ok(Self::from_bits(Zext::eval(self.as_bits(), size), size))
375    }
376
377    fn sext(&self, size: usize) -> Result<Self, EmulatorErrorKind> {
378        Ok(Self::from_bits(
379            Sext::eval(self.as_bits(), self.size as usize, size),
380            size,
381        ))
382    }
383
384    fn range(&self, start: usize, size: usize) -> Result<Self, EmulatorErrorKind> {
385        Ok(Self::from_bits(
386            Range::eval(self.as_bits(), start, size),
387            size,
388        ))
389    }
390
391    fn byte_swap(&self) -> Result<Self, EmulatorErrorKind> {
392        let size = self.size as usize;
393        let mut value = 0u128;
394        for index in 0..size {
395            let byte = (self.as_bits() >> (index * 8)) & 0xff;
396            value |= byte << ((size - index - 1) * 8);
397        }
398        Ok(Self::from_bits(value, size))
399    }
400
401    fn intrinsic(
402        id: qcode::value::insn::IntrinsicId,
403        args: &[Self],
404        out_size: usize,
405    ) -> Result<Self, EmulatorErrorKind> {
406        let operands: Vec<(u128, usize)> = args
407            .iter()
408            .map(|a| (a.as_bits(), a.size as usize))
409            .collect();
410        let value = id
411            .desc()
412            .eval(&operands, out_size)
413            .ok_or_else(|| EmulatorErrorKind::UnsupportedIntrinsic(Box::from(id.name())))?;
414        Ok(Self::from_bits(value, out_size))
415    }
416
417    fn pop_count(&self) -> Result<Self, EmulatorErrorKind> {
418        let size = self.size as usize;
419        Ok(Self::from_bits(PopCount::eval(self.as_bits(), size), size))
420    }
421
422    fn lz_count(&self) -> Result<Self, EmulatorErrorKind> {
423        let size = self.size as usize;
424        Ok(Self::from_bits(LzCount::eval(self.as_bits(), size), size))
425    }
426
427    fn carry(&self, other: &Self) -> Result<Self, EmulatorErrorKind> {
428        let size = self.widen_size(other);
429        Ok(Self::from_bits(
430            Carry::eval(self.as_bits(), other.as_bits(), size),
431            1,
432        ))
433    }
434
435    fn scarry(&self, other: &Self) -> Result<Self, EmulatorErrorKind> {
436        let size = self.widen_size(other);
437        Ok(Self::from_bits(
438            SCarry::eval(self.as_bits(), other.as_bits(), size),
439            1,
440        ))
441    }
442
443    fn sborrow(&self, other: &Self) -> Result<Self, EmulatorErrorKind> {
444        let size = self.widen_size(other);
445        Ok(Self::from_bits(
446            SBorrow::eval(self.as_bits(), other.as_bits(), size),
447            1,
448        ))
449    }
450
451    fn int_not(&self) -> Result<Self, EmulatorErrorKind> {
452        let size = self.size as usize;
453        Ok(Self::from_bits(
454            Unop::IntNot.eval_int(self.as_bits(), size).unwrap(),
455            size,
456        ))
457    }
458
459    fn int_negate(&self) -> Result<Self, EmulatorErrorKind> {
460        let size = self.size as usize;
461        Ok(Self::from_bits(
462            Unop::IntNegate.eval_int(self.as_bits(), size).unwrap(),
463            size,
464        ))
465    }
466
467    fn float_negate(&self) -> Result<Self, EmulatorErrorKind> {
468        if self.size == 10 {
469            return Ok(Self::from_f80_bits(float80::negate(self.as_bits())));
470        }
471        Ok(Self::from_f64(-self.f64_from_self(), self.size as usize))
472    }
473
474    fn float_abs(&self) -> Result<Self, EmulatorErrorKind> {
475        if self.size == 10 {
476            return Ok(Self::from_f80_bits(float80::abs(self.as_bits())));
477        }
478        Ok(Self::from_f64(
479            self.f64_from_self().abs(),
480            self.size as usize,
481        ))
482    }
483
484    fn float_sqrt(&self) -> Result<Self, EmulatorErrorKind> {
485        Ok(Self::from_f64(
486            self.f64_from_self().sqrt(),
487            self.size as usize,
488        ))
489    }
490
491    fn float_ceil(&self) -> Result<Self, EmulatorErrorKind> {
492        Ok(Self::from_f64(
493            self.f64_from_self().ceil(),
494            self.size as usize,
495        ))
496    }
497
498    fn float_floor(&self) -> Result<Self, EmulatorErrorKind> {
499        Ok(Self::from_f64(
500            self.f64_from_self().floor(),
501            self.size as usize,
502        ))
503    }
504
505    fn float_round(&self) -> Result<Self, EmulatorErrorKind> {
506        Ok(Self::from_f64(
507            self.f64_from_self().round(),
508            self.size as usize,
509        ))
510    }
511
512    fn int_equal(&self, other: &Self) -> Result<Self, EmulatorErrorKind> {
513        Ok(Self::from_bits(
514            IntBinop::Equal.eval(self.as_bits(), other.as_bits(), self.size as usize),
515            1,
516        ))
517    }
518
519    fn int_not_equal(&self, other: &Self) -> Result<Self, EmulatorErrorKind> {
520        Ok(Self::from_bits(
521            IntBinop::NotEqual.eval(self.as_bits(), other.as_bits(), self.size as usize),
522            1,
523        ))
524    }
525
526    fn int_less(&self, other: &Self) -> Result<Self, EmulatorErrorKind> {
527        Ok(Self::from_bits(
528            IntBinop::Less.eval(self.as_bits(), other.as_bits(), self.size as usize),
529            1,
530        ))
531    }
532
533    fn int_sless(&self, other: &Self) -> Result<Self, EmulatorErrorKind> {
534        Ok(Self::from_bits(
535            IntBinop::SLess.eval(self.as_bits(), other.as_bits(), self.size as usize),
536            1,
537        ))
538    }
539
540    fn int_less_equal(&self, other: &Self) -> Result<Self, EmulatorErrorKind> {
541        Ok(Self::from_bits(
542            IntBinop::LessEqual.eval(self.as_bits(), other.as_bits(), self.size as usize),
543            1,
544        ))
545    }
546
547    fn int_sless_equal(&self, other: &Self) -> Result<Self, EmulatorErrorKind> {
548        Ok(Self::from_bits(
549            IntBinop::SLessEqual.eval(self.as_bits(), other.as_bits(), self.size as usize),
550            1,
551        ))
552    }
553
554    fn int_add(&self, other: &Self) -> Result<Self, EmulatorErrorKind> {
555        let size = self.widen_size(other);
556        Ok(Self::from_bits(
557            IntBinop::Add.eval(self.as_bits(), other.as_bits(), size),
558            size,
559        ))
560    }
561
562    fn int_sub(&self, other: &Self) -> Result<Self, EmulatorErrorKind> {
563        let size = self.widen_size(other);
564        Ok(Self::from_bits(
565            IntBinop::Sub.eval(self.as_bits(), other.as_bits(), size),
566            size,
567        ))
568    }
569
570    fn int_xor(&self, other: &Self) -> Result<Self, EmulatorErrorKind> {
571        let size = self.widen_size(other);
572        Ok(Self::from_bits(
573            IntBinop::Xor.eval(self.as_bits(), other.as_bits(), size),
574            size,
575        ))
576    }
577
578    fn int_and(&self, other: &Self) -> Result<Self, EmulatorErrorKind> {
579        let size = self.widen_size(other);
580        Ok(Self::from_bits(
581            IntBinop::And.eval(self.as_bits(), other.as_bits(), size),
582            size,
583        ))
584    }
585
586    fn int_or(&self, other: &Self) -> Result<Self, EmulatorErrorKind> {
587        let size = self.widen_size(other);
588        Ok(Self::from_bits(
589            IntBinop::Or.eval(self.as_bits(), other.as_bits(), size),
590            size,
591        ))
592    }
593
594    fn int_shift_left(&self, other: &Self) -> Result<Self, EmulatorErrorKind> {
595        let size = self.size as usize;
596        Ok(Self::from_bits(
597            IntBinop::ShiftLeft.eval(self.as_bits(), other.as_bits(), size),
598            size,
599        ))
600    }
601
602    fn int_shift_right(&self, other: &Self) -> Result<Self, EmulatorErrorKind> {
603        let size = self.size as usize;
604        Ok(Self::from_bits(
605            IntBinop::ShiftRight.eval(self.as_bits(), other.as_bits(), size),
606            size,
607        ))
608    }
609
610    fn int_sshift_right(&self, other: &Self) -> Result<Self, EmulatorErrorKind> {
611        let size = self.size as usize;
612        Ok(Self::from_bits(
613            IntBinop::SShiftRight.eval(self.as_bits(), other.as_bits(), size),
614            size,
615        ))
616    }
617
618    fn int_mul(&self, other: &Self) -> Result<Self, EmulatorErrorKind> {
619        let size = self.widen_size(other);
620        Ok(Self::from_bits(
621            IntBinop::Mul.eval(self.as_bits(), other.as_bits(), size),
622            size,
623        ))
624    }
625
626    fn int_div(&self, other: &Self) -> Result<Self, EmulatorErrorKind> {
627        let size = self.widen_size(other);
628        Ok(Self::from_bits(
629            IntBinop::Div.eval(self.as_bits(), other.as_bits(), size),
630            size,
631        ))
632    }
633
634    fn int_rem(&self, other: &Self) -> Result<Self, EmulatorErrorKind> {
635        let size = self.widen_size(other);
636        Ok(Self::from_bits(
637            IntBinop::Rem.eval(self.as_bits(), other.as_bits(), size),
638            size,
639        ))
640    }
641
642    fn int_sdiv(&self, other: &Self) -> Result<Self, EmulatorErrorKind> {
643        let size = self.widen_size(other);
644        Ok(Self::from_bits(
645            IntBinop::Sdiv.eval(self.as_bits(), other.as_bits(), size),
646            size,
647        ))
648    }
649
650    fn int_srem(&self, other: &Self) -> Result<Self, EmulatorErrorKind> {
651        let size = self.widen_size(other);
652        Ok(Self::from_bits(
653            IntBinop::Srem.eval(self.as_bits(), other.as_bits(), size),
654            size,
655        ))
656    }
657
658    fn float_add(&self, other: &Self) -> Result<Self, EmulatorErrorKind> {
659        let size = self.widen_size(other);
660        if size == 10 {
661            return Ok(Self::from_f80_bits(float80::add(
662                self.as_bits(),
663                other.as_bits(),
664            )));
665        }
666        Ok(Self::from_f64(
667            self.f64_from_self() + other.f64_from_self(),
668            size,
669        ))
670    }
671
672    fn float_sub(&self, other: &Self) -> Result<Self, EmulatorErrorKind> {
673        let size = self.widen_size(other);
674        if size == 10 {
675            return Ok(Self::from_f80_bits(float80::sub(
676                self.as_bits(),
677                other.as_bits(),
678            )));
679        }
680        Ok(Self::from_f64(
681            self.f64_from_self() - other.f64_from_self(),
682            size,
683        ))
684    }
685
686    fn float_mul(&self, other: &Self) -> Result<Self, EmulatorErrorKind> {
687        let size = self.widen_size(other);
688        if size == 10 {
689            return Ok(Self::from_f80_bits(float80::mul(
690                self.as_bits(),
691                other.as_bits(),
692            )));
693        }
694        Ok(Self::from_f64(
695            self.f64_from_self() * other.f64_from_self(),
696            size,
697        ))
698    }
699
700    fn float_div(&self, other: &Self) -> Result<Self, EmulatorErrorKind> {
701        let size = self.widen_size(other);
702        if size == 10 {
703            return Ok(Self::from_f80_bits(float80::div(
704                self.as_bits(),
705                other.as_bits(),
706            )));
707        }
708        Ok(Self::from_f64(
709            self.f64_from_self() / other.f64_from_self(),
710            size,
711        ))
712    }
713
714    fn float_equal(&self, other: &Self) -> Result<Self, EmulatorErrorKind> {
715        let equal = if self.size == 10 {
716            float80::equal(self.as_bits(), other.as_bits())
717        } else {
718            self.f64_from_self() == other.f64_from_self()
719        };
720        Ok(Self::new(bool_to_u64(equal), 1))
721    }
722
723    fn float_not_equal(&self, other: &Self) -> Result<Self, EmulatorErrorKind> {
724        let unequal = if self.size == 10 {
725            !float80::equal(self.as_bits(), other.as_bits())
726        } else {
727            self.f64_from_self() != other.f64_from_self()
728        };
729        Ok(Self::new(bool_to_u64(unequal), 1))
730    }
731
732    fn float_less(&self, other: &Self) -> Result<Self, EmulatorErrorKind> {
733        let less = if self.size == 10 {
734            float80::less(self.as_bits(), other.as_bits())
735        } else {
736            self.f64_from_self() < other.f64_from_self()
737        };
738        Ok(Self::new(bool_to_u64(less), 1))
739    }
740
741    fn float_less_equal(&self, other: &Self) -> Result<Self, EmulatorErrorKind> {
742        let less_equal = if self.size == 10 {
743            float80::less_equal(self.as_bits(), other.as_bits())
744        } else {
745            self.f64_from_self() <= other.f64_from_self()
746        };
747        Ok(Self::new(bool_to_u64(less_equal), 1))
748    }
749}
750
751/// The byte-addressable memory an emulator run needs, over and above the
752/// value-domain reads and writes of [`DomainMemory`].
753///
754/// [`DomainMemory`] speaks in whole values of a domain, which is all the
755/// interpreter itself needs. A *harness* needs more: to seed a fixture, dump a
756/// region, or poke a single register lane, it has to talk in bytes. Keeping
757/// those on a separate trait is what lets [`StandaloneEmulator`] be generic over
758/// its memory, so a richer backend — one with mapped pages and permissions —
759/// can be substituted without the interpreter knowing.
760pub trait EmulatorMemory: DomainMemory<V = SizedValue> {
761    /// Prepares per-space bookkeeping for `ctx`. Called before any access, and
762    /// cheap to call repeatedly: spaces are append-only, so an implementation
763    /// can skip the work when nothing has been added.
764    fn configure_spaces(&mut self, ctx: &Context<'_>);
765
766    /// Reads `size` raw bytes, without the value-domain's width handling.
767    fn read_bytes(
768        &self,
769        space: MemorySpaceId,
770        addr: u64,
771        size: usize,
772    ) -> Result<Vec<u8>, EmulatorErrorKind>;
773
774    /// Writes raw bytes, creating the space if it does not exist yet.
775    fn write_bytes(
776        &mut self,
777        space: MemorySpaceId,
778        addr: u64,
779        bytes: &[u8],
780    ) -> Result<(), EmulatorErrorKind>;
781}
782
783impl EmulatorMemory for EmulatedMemory {
784    fn configure_spaces(&mut self, ctx: &Context<'_>) {
785        EmulatedMemory::configure_spaces(self, ctx)
786    }
787
788    fn read_bytes(
789        &self,
790        space: MemorySpaceId,
791        addr: u64,
792        size: usize,
793    ) -> Result<Vec<u8>, EmulatorErrorKind> {
794        self.read_raw(space, addr, size)
795    }
796
797    fn write_bytes(
798        &mut self,
799        space: MemorySpaceId,
800        addr: u64,
801        bytes: &[u8],
802    ) -> Result<(), EmulatorErrorKind> {
803        let space = self.spaces.entry(space).or_default();
804        space.reserve(bytes.len());
805        for (index, byte) in bytes.iter().enumerate() {
806            space.write_byte(addr + index as u64, *byte);
807        }
808        Ok(())
809    }
810}
811
812impl DomainMemory for EmulatedMemory {
813    type V = SizedValue;
814
815    fn read(
816        &self,
817        space: MemorySpaceId,
818        addr: Self::V,
819        size: usize,
820    ) -> Result<Self::V, EmulatorErrorKind> {
821        let addr = addr.value()?;
822        let zero_filled = self.is_zero_filled(space);
823        let bits = match self.spaces.get(&space) {
824            Some(s) if zero_filled => s.read_u128_zero_filled(addr, size as u64),
825            Some(s) => s.read_u128(addr, size as u64)?,
826            None if zero_filled => 0,
827            None => return Err(EmulatorErrorKind::UnknownSpace(space)),
828        };
829        Ok(SizedValue::from_bits(bits, size))
830    }
831
832    fn write(
833        &mut self,
834        space: MemorySpaceId,
835        addr: Self::V,
836        size: usize,
837        value: Self::V,
838    ) -> Result<(), EmulatorErrorKind> {
839        let addr = addr.value()?;
840
841        self.spaces
842            .entry(space)
843            .or_default()
844            .get_mut_region(addr, size)?
845            .write_u128(value.as_bits());
846        Ok(())
847    }
848}
849
850/// Values of literals, resolved once and kept by id.
851///
852/// Reading a literal goes through the module's interner, which takes an
853/// `RwLock` read guard per access so that constants can be minted through a
854/// shared reference. That is two atomic operations for what is morally a
855/// constant, and it measured at 14% of run time in a profile of an interpreter
856/// loop. A literal is immutable once interned — attaching a symbolic reference
857/// later changes neither its value nor its width — so caching it by id is
858/// sound, and turns the access into an array index.
859#[derive(Debug, Default, Clone)]
860pub struct LiteralCache(Vec<Option<SizedValue>>);
861
862impl LiteralCache {
863    fn get(&mut self, ctx: &Context<'_>, id: qcode::value::LiteralId) -> SizedValue {
864        let index: usize = id.into();
865        if index >= self.0.len() {
866            self.0.resize(index + 1, None);
867        }
868        match self.0[index] {
869            Some(value) => value,
870            None => {
871                // The miss pays the interner's lock, once per distinct literal.
872                let ValueRef::Literal(literal) = ValueRef::new(ValueId::Literal(id), ctx) else {
873                    unreachable!("a literal id resolves to a literal")
874                };
875                let value = SizedValue::new(literal.value(), literal.size());
876                self.0[index] = Some(value);
877                value
878            }
879        }
880    }
881}
882
883/// Values produced by instructions, stored densely.
884///
885/// Every p-code operation that yields a value writes one here and its consumers
886/// read it back, so this is one of the hottest structures in the interpreter. A
887/// `FxHashMap<InstructionId, _>` hashes a `(function, local)` pair on every
888/// access; instruction ids are dense and small, so a slot array indexed by
889/// those two components removes the hashing entirely.
890///
891/// Sparse ids cost only an unused slot, which is what makes this safe to use
892/// after a pass has removed instructions from a block.
893#[derive(Debug, Default, Clone)]
894pub struct InsnValues(Vec<Vec<Option<SizedValue>>>);
895
896impl InsnValues {
897    pub fn get(&self, id: &InstructionId) -> Option<&SizedValue> {
898        let func: usize = id.func.into();
899        let local: usize = id.local.into();
900        self.0.get(func)?.get(local)?.as_ref()
901    }
902
903    pub fn insert(&mut self, id: InstructionId, value: SizedValue) {
904        let func: usize = id.func.into();
905        let local: usize = id.local.into();
906        if func >= self.0.len() {
907            self.0.resize_with(func + 1, Vec::new);
908        }
909        let slots = &mut self.0[func];
910        if local >= slots.len() {
911            slots.resize(local + 1, None);
912        }
913        slots[local] = Some(value);
914    }
915
916    pub fn clear(&mut self) {
917        self.0.clear();
918    }
919}
920
921/// Type alias for an instruction hook function, which is called with the current instruction and emulator state after each instruction is executed.
922type InstructionHook<M> =
923    Box<dyn Fn(&InstructionRef<'_, '_>, &StandaloneEmulator<M>) + Send + Sync>;
924type CallInterceptor<M> = Box<
925    dyn FnMut(
926            &Context<'_>,
927            &mut StandaloneEmulator<M>,
928            &CallSite,
929        ) -> Result<CallInterception, Box<str>>
930        + Send
931        + Sync,
932>;
933
934#[derive(Debug, Clone, Copy, PartialEq, Eq)]
935enum StepEvent {
936    Normal,
937    DirectCallEntered(FunctionId),
938    IndirectCallEntered,
939    Return,
940    ReturnValue,
941    InterceptedCall,
942}
943
944/// A lifetime-free emulator that takes `&Context<'_>` explicitly on each call.
945/// Use this when you need to store an emulator without a lifetime (e.g., across an FFI boundary).
946pub struct StandaloneEmulator<M = EmulatedMemory> {
947    pub memory: M,
948    /// Literal values resolved once instead of per access.
949    literal_cache: LiteralCache,
950    /// Whether the module contains any array or list type, and the published
951    /// type count that answer was valid for.
952    ///
953    /// Every `Load`, `Store` and `Range` is guarded by "is this operand
954    /// sequence-typed", which otherwise costs an arena resolution plus two
955    /// virtual calls *per operation* — about 9% of run time. Almost every module
956    /// has no sequence types at all, and that is answerable once.
957    sequence_types: bool,
958    sequence_types_checked_at: Option<usize>,
959    /// The current block's instruction list, and which block it belongs to.
960    ///
961    /// Resolving a block means two registry indexes (`bodies[func].blocks[local]`)
962    /// and the interpreter did it on every step, though a block is entered once
963    /// and then walked. Refreshed whenever the block changes *or* execution is
964    /// at a block's first instruction — a block's contents can only change while
965    /// nothing is part-way through it, which is exactly the case a VM that lifts
966    /// on demand creates when it fills a placeholder block and re-enters it.
967    cached_block: Option<BlockId>,
968    cached_insns: Vec<qcode::value::LocalInsnId>,
969    pub insn_values: InsnValues,
970    pub block_param_values: FxHashMap<BlockParamId, SizedValue>,
971    /// Block params bound to **poison** (argpromote v2): a symbolic pure-call
972    /// argument whose bits are undefined. Reading one during emulation is a hard
973    /// error (`PoisonRead`), so a pure-call fold whose result actually depends on
974    /// a symbolic argument bails instead of computing on a bogus concrete value.
975    pub poison_params: FxHashSet<BlockParamId>,
976    /// Field values of aggregate-typed instruction results (`Tuple` results and,
977    /// on return, the call instruction that produced them). `Extract` projects a
978    /// field back out. Keeps the scalar `SizedValue` domain unchanged — the
979    /// functional `argpromote` write-set is the only producer/consumer.
980    pub aggregate_values: FxHashMap<InstructionId, Vec<SizedValue>>,
981    /// Field values of aggregate-typed **block params** seeded by
982    /// [`run_map_body`](Self::run_map_body) (the `enumerate` `(index, elem)` lane
983    /// fed to a `map` body). `Extract` on such a param projects a field back out.
984    pub block_param_aggregates: FxHashMap<BlockParamId, Vec<SizedValue>>,
985    /// Little-endian byte buffers of array-typed instruction results — the value
986    /// domain for the sequence intrinsics (`iota`/`singleton`/`insert`/`concat`),
987    /// `Scan`, and array-typed `Store`. Kept out of the scalar `SizedValue`
988    /// domain the same way [`aggregate_values`](Self::aggregate_values) keeps
989    /// tuples out of it; a scalar `at(arr, i)` reads one lane back into
990    /// `insn_values`.
991    pub array_values: FxHashMap<InstructionId, Vec<u8>>,
992    pub block: BlockId,
993    pub idx: usize,
994    /// Call stack maintained by `run_function` (outermost function first).
995    pub call_stack: Vec<FunctionId>,
996    /// The call instruction id for each active nested call, so a `Return` can
997    /// deposit the callee's `Return.value` as that call's result.
998    call_site_stack: Vec<InstructionId>,
999
1000    /// Disposable address lookup for the immutable module snapshot supplied by
1001    /// the caller. The lifetime-free emulator initializes this lazily because
1002    /// [`StandaloneEmulator::new`] intentionally takes no `Context`.
1003    address_index: Option<AddressIndex>,
1004
1005    pub instruction_hook: Option<InstructionHook<M>>,
1006    call_interceptor: Option<CallInterceptor<M>>,
1007}
1008
1009impl StandaloneEmulator<EmulatedMemory> {
1010    /// Builds an emulator over the default flat memory.
1011    pub fn new(entry: BlockId) -> Self {
1012        Self::new_in(entry)
1013    }
1014
1015    /// Builds an emulator positioned at `addr`, over the default flat memory.
1016    pub fn from_address(ctx: &Context<'_>, addr: u64) -> Self {
1017        Self::from_address_in(ctx, addr)
1018    }
1019}
1020
1021impl<M: EmulatorMemory + Default> StandaloneEmulator<M> {
1022    /// Builds an emulator over an explicit memory backend.
1023    ///
1024    /// [`new`](StandaloneEmulator::new) is the one to reach for with the default
1025    /// flat memory: Rust's default type parameters do not participate in
1026    /// inference, so a generic `new` would force every call site to name its
1027    /// backend.
1028    pub fn new_in(entry: BlockId) -> Self {
1029        Self {
1030            memory: M::default(),
1031            literal_cache: LiteralCache::default(),
1032            sequence_types: false,
1033            sequence_types_checked_at: None,
1034            cached_block: None,
1035            cached_insns: Vec::new(),
1036            insn_values: InsnValues::default(),
1037            block_param_values: FxHashMap::default(),
1038            poison_params: FxHashSet::default(),
1039            aggregate_values: FxHashMap::default(),
1040            block_param_aggregates: FxHashMap::default(),
1041            array_values: FxHashMap::default(),
1042            block: entry,
1043            idx: 0,
1044            call_stack: Vec::new(),
1045            call_site_stack: Vec::new(),
1046            address_index: None,
1047            instruction_hook: None,
1048            call_interceptor: None,
1049        }
1050    }
1051
1052    /// Drops the cached instruction list for the current block.
1053    ///
1054    /// The cache assumes a block's contents only change while nothing is
1055    /// part-way through it. A caller that runs a block's body by some other
1056    /// means and then positions the emulator *inside* that block breaks the
1057    /// assumption, and must say so.
1058    pub fn invalidate_block_cache(&mut self) {
1059        self.cached_block = None;
1060    }
1061
1062    /// Takes the cached address lookup, leaving the emulator without one.
1063    ///
1064    /// The index is derived state, built once from what was assumed to be an
1065    /// immutable module. A VM that lifts code on demand makes the module
1066    /// *mutable*, so it has to keep the index current instead. Moving the index
1067    /// out, updating it in place as blocks are added, and moving it back with
1068    /// [`set_address_index`](Self::set_address_index) keeps discovery O(1) —
1069    /// rebuilding it per lift is quadratic in the size of the module.
1070    pub fn take_address_index(&mut self) -> Option<AddressIndex> {
1071        self.address_index.take()
1072    }
1073
1074    /// Installs an address lookup, replacing any cached one.
1075    /// Borrows the cached address index, if one has been built.
1076    pub fn address_index(&self) -> Option<&AddressIndex> {
1077        self.address_index.as_ref()
1078    }
1079
1080    pub fn set_address_index(&mut self, address_index: AddressIndex) {
1081        self.address_index = Some(address_index);
1082    }
1083
1084    /// Resolves a guest address to the block that covers it, building the
1085    /// cached index if there is not one yet.
1086    pub fn block_at_address(&mut self, ctx: &Context<'_>, address: u64) -> Option<BlockId> {
1087        self.block_at(ctx, address)
1088    }
1089
1090    fn with_address_index(entry: BlockId, address_index: AddressIndex) -> Self {
1091        let mut emulator = Self::new_in(entry);
1092        emulator.address_index = Some(address_index);
1093        emulator
1094    }
1095
1096    fn resolve_block_at(ctx: &Context<'_>, index: &AddressIndex, address: u64) -> Option<BlockId> {
1097        match index.get(address) {
1098            Some(AddressTarget::Block(block)) => Some(block),
1099            Some(AddressTarget::Function(function)) => FunctionBody::from_id(ctx, function)
1100                .root()
1101                .map(|root| root.id),
1102            None => None,
1103        }
1104    }
1105
1106    fn block_at(&mut self, ctx: &Context<'_>, address: u64) -> Option<BlockId> {
1107        let index = self
1108            .address_index
1109            .get_or_insert_with(|| AddressIndex::analyze(ctx));
1110        Self::resolve_block_at(ctx, index, address)
1111    }
1112
1113    fn make_error(&self, ctx: &Context<'_>, kind: EmulatorErrorKind) -> EmulatorError {
1114        let block = BasicBlock::from_id(ctx, self.block);
1115        let instruction = block
1116            .instruction_ids()
1117            .get(self.idx)
1118            .copied()
1119            .or_else(|| block.instruction_ids().last().copied())
1120            .expect("cannot construct EmulatorError for empty block");
1121
1122        EmulatorError::new(kind, &Instruction::from_id(ctx, instruction))
1123    }
1124
1125    /// Construct an [`EmulatorErrorKind::EmptyBlock`] error for the current block
1126    /// without indexing into it. [`make_error`](Self::make_error) can't serve this
1127    /// case — it panics when the block has no instructions to attach context to.
1128    fn make_empty_block_error(&self, ctx: &Context<'_>) -> EmulatorError {
1129        let block = BasicBlock::from_id(ctx, self.block);
1130        EmulatorError {
1131            kind: EmulatorErrorKind::EmptyBlock(self.block),
1132            ctx: format!(
1133                "Block: {:?}\nFunction: {:?}",
1134                block.name(),
1135                block.function().map(|f| f.name())
1136            ),
1137            address: block.address(),
1138        }
1139    }
1140
1141    fn make_error_at(
1142        &self,
1143        ctx: &Context<'_>,
1144        instruction: InstructionId,
1145        kind: EmulatorErrorKind,
1146    ) -> EmulatorError {
1147        EmulatorError::new(kind, &Instruction::from_id(ctx, instruction))
1148    }
1149
1150    /// Builds an emulator positioned at `addr`, over an explicit backend.
1151    pub fn from_address_in(ctx: &Context<'_>, addr: u64) -> Self {
1152        let address_index = AddressIndex::analyze(ctx);
1153        let entry = Self::resolve_block_at(ctx, &address_index, addr)
1154            .expect("Invalid block or function address");
1155        let mut emulator = Self::with_address_index(entry, address_index);
1156        emulator.memory.configure_spaces(ctx);
1157        emulator
1158    }
1159
1160    pub fn set_varnode(
1161        &mut self,
1162        ctx: &Context<'_>,
1163        id: VarnodeId,
1164        value: u64,
1165    ) -> Result<(), EmulatorErrorKind> {
1166        self.set_varnode_u128(ctx, id, u128::from(value))
1167    }
1168
1169    pub fn set_varnode_u128(
1170        &mut self,
1171        ctx: &Context<'_>,
1172        id: VarnodeId,
1173        value: u128,
1174    ) -> Result<(), EmulatorErrorKind> {
1175        self.memory.configure_spaces(ctx);
1176        let varnode = Varnode::from_id(ctx, id);
1177        let space = varnode.space().id;
1178        let addr = varnode.address() as u64;
1179        let size = varnode.size();
1180        self.memory.write(
1181            space.into(),
1182            SizedValue::from_u64(addr),
1183            size,
1184            SizedValue::from_bits(value, size),
1185        )
1186    }
1187
1188    pub fn read_varnode(&self, ctx: &Context<'_>, id: VarnodeId) -> Option<u64> {
1189        let value = self.read_varnode_u128(ctx, id)?;
1190        u64::try_from(value).ok()
1191    }
1192
1193    pub fn read_varnode_u128(&self, ctx: &Context<'_>, id: VarnodeId) -> Option<u128> {
1194        let varnode = Varnode::from_id(ctx, id);
1195        let space = varnode.space().id;
1196        let addr = varnode.address() as u64;
1197        let size = varnode.size();
1198        self.memory
1199            .read(space.into(), SizedValue::from_u64(addr), size)
1200            .ok()
1201            .map(|v| v.as_bits())
1202    }
1203
1204    pub fn get_value(&mut self, ctx: &Context<'_>, id: ValueId) -> Option<u64> {
1205        let mut tmp = TempInterpreter {
1206            memory: &mut self.memory,
1207            literals: &mut self.literal_cache,
1208            insn_values: &mut self.insn_values,
1209            block_param_values: &mut self.block_param_values,
1210            poison_params: &self.poison_params,
1211            ctx,
1212        };
1213        tmp.get_value(id).ok().and_then(|v| v.value().ok())
1214    }
1215
1216    pub fn set_varnode_bytes(
1217        &mut self,
1218        ctx: &Context<'_>,
1219        id: VarnodeId,
1220        bytes: &[u8],
1221    ) -> Result<(), EmulatorErrorKind> {
1222        let varnode = Varnode::from_id(ctx, id);
1223        let space = varnode.space().id;
1224        let base_addr = varnode.address() as u64;
1225        for (i, chunk) in bytes.chunks(8).enumerate() {
1226            let addr = base_addr + (i * 8) as u64;
1227            let mut buf = [0u8; 8];
1228            buf[..chunk.len()].copy_from_slice(chunk);
1229            let value = u64::from_le_bytes(buf);
1230            self.memory.write(
1231                space.into(),
1232                SizedValue::from_u64(addr),
1233                chunk.len(),
1234                SizedValue::new(value, chunk.len()),
1235            )?;
1236        }
1237        Ok(())
1238    }
1239
1240    pub fn set_varnode_by_name(
1241        &mut self,
1242        ctx: &Context<'_>,
1243        name: &str,
1244        value: u64,
1245    ) -> Result<bool, EmulatorErrorKind> {
1246        self.set_varnode_by_name_u128(ctx, name, u128::from(value))
1247    }
1248
1249    pub fn set_varnode_by_name_u128(
1250        &mut self,
1251        ctx: &Context<'_>,
1252        name: &str,
1253        value: u128,
1254    ) -> Result<bool, EmulatorErrorKind> {
1255        match ctx.get_named(name) {
1256            Some(ValueId::Varnode(id)) => {
1257                self.set_varnode_u128(ctx, id, value)?;
1258                Ok(true)
1259            }
1260            _ => Ok(false),
1261        }
1262    }
1263
1264    pub fn set_varnode_by_name_bytes(
1265        &mut self,
1266        ctx: &Context<'_>,
1267        name: &str,
1268        bytes: &[u8],
1269    ) -> Result<bool, EmulatorErrorKind> {
1270        match ctx.get_named(name) {
1271            Some(ValueId::Varnode(id)) => {
1272                self.set_varnode_bytes(ctx, id, bytes)?;
1273                Ok(true)
1274            }
1275            _ => Ok(false),
1276        }
1277    }
1278
1279    pub fn read_varnode_by_name(&mut self, ctx: &Context<'_>, name: &str) -> Option<u64> {
1280        let value = self.read_varnode_by_name_u128(ctx, name)?;
1281        u64::try_from(value).ok()
1282    }
1283
1284    pub fn read_varnode_by_name_u128(&mut self, ctx: &Context<'_>, name: &str) -> Option<u128> {
1285        match ctx.get_named(name)? {
1286            ValueId::Varnode(id) => self.read_varnode_u128(ctx, id),
1287            _ => None,
1288        }
1289    }
1290
1291    pub fn read_varnode_bytes(&mut self, ctx: &Context<'_>, id: VarnodeId) -> Vec<u8> {
1292        let varnode = Varnode::from_id(ctx, id);
1293        let space = varnode.space().id;
1294        let addr = varnode.address() as u64;
1295        let size = varnode.size();
1296        self.memory
1297            .read_bytes(space.into(), addr, size)
1298            .unwrap_or_default()
1299    }
1300
1301    pub fn read_varnode_by_name_bytes(&mut self, ctx: &Context<'_>, name: &str) -> Option<Vec<u8>> {
1302        match ctx.get_named(name)? {
1303            ValueId::Varnode(id) => Some(self.read_varnode_bytes(ctx, id)),
1304            _ => None,
1305        }
1306    }
1307
1308    pub fn get_value_bytes(&mut self, ctx: &Context<'_>, id: ValueId) -> Option<Vec<u8>> {
1309        let mut tmp = TempInterpreter {
1310            memory: &mut self.memory,
1311            literals: &mut self.literal_cache,
1312            insn_values: &mut self.insn_values,
1313            block_param_values: &mut self.block_param_values,
1314            poison_params: &self.poison_params,
1315            ctx,
1316        };
1317        let sv = tmp.get_value(id).ok()?;
1318        let size = sv.size().ok()?;
1319        let bits = sv.as_bits();
1320        let mut bytes = vec![0u8; size];
1321        for (i, byte) in bytes.iter_mut().enumerate() {
1322            *byte = u8::try_from((bits >> (i * 8)) & u128::from(0xffu8)).unwrap();
1323        }
1324        Some(bytes)
1325    }
1326
1327    pub fn current_block(&self) -> BlockId {
1328        self.block
1329    }
1330
1331    pub fn set_call_interceptor(
1332        &mut self,
1333        interceptor: impl FnMut(
1334            &Context<'_>,
1335            &mut StandaloneEmulator<M>,
1336            &CallSite,
1337        ) -> Result<CallInterception, Box<str>>
1338        + Send
1339        + Sync
1340        + 'static,
1341    ) {
1342        self.call_interceptor = Some(Box::new(interceptor));
1343    }
1344
1345    pub fn clear_call_interceptor(&mut self) {
1346        self.call_interceptor = None;
1347    }
1348
1349    pub fn read_memory(
1350        &mut self,
1351        ctx: &Context<'_>,
1352        space: impl Into<MemorySpaceId>,
1353        addr: u64,
1354        size: usize,
1355    ) -> Result<Vec<u8>, EmulatorErrorKind> {
1356        self.memory.configure_spaces(ctx);
1357        self.memory.read_bytes(space.into(), addr, size)
1358    }
1359
1360    pub fn write_memory(
1361        &mut self,
1362        ctx: &Context<'_>,
1363        space: impl Into<MemorySpaceId>,
1364        addr: u64,
1365        value: &[u8],
1366    ) -> Result<(), EmulatorErrorKind> {
1367        self.memory.configure_spaces(ctx);
1368        self.memory.write_bytes(space.into(), addr, value)
1369    }
1370
1371    /// Resolve a terminator's bare-local argument list; `func` is the owning
1372    /// function of the instruction the args came from (strict IR locality).
1373    fn collect_block_args(
1374        &mut self,
1375        ctx: &Context<'_>,
1376        func: FunctionId,
1377        args: &[LocalValueId],
1378    ) -> Result<Vec<SizedValue>, EmulatorErrorKind> {
1379        let mut tmp = TempInterpreter {
1380            memory: &mut self.memory,
1381            literals: &mut self.literal_cache,
1382            insn_values: &mut self.insn_values,
1383            block_param_values: &mut self.block_param_values,
1384            poison_params: &self.poison_params,
1385            ctx,
1386        };
1387        args.iter()
1388            .map(|&arg| tmp.get_value(arg.qualify(func)))
1389            .collect()
1390    }
1391
1392    fn bind_block_args(
1393        &mut self,
1394        ctx: &Context<'_>,
1395        func: FunctionId,
1396        target: BlockId,
1397        args: &[LocalValueId],
1398    ) -> Result<(), EmulatorErrorKind> {
1399        let values = self.collect_block_args(ctx, func, args)?;
1400        let params = BasicBlock::from_id(ctx, target)
1401            .params()
1402            .map(|param| param.id)
1403            .collect::<Vec<_>>();
1404        if values.len() != params.len() {
1405            return Err(EmulatorErrorKind::ValueError(values.len() as u128));
1406        }
1407        for (param, value) in params.into_iter().zip(values) {
1408            self.block_param_values.insert(param, value);
1409        }
1410        Ok(())
1411    }
1412
1413    /// Resolve a range of a register varnode used as a store destination.
1414    ///
1415    /// The flat SLEIGH emitter represents `ST1[8:10] = value` with a `range`
1416    /// value so it retains the destination byte offset. As a source, `range`
1417    /// means extracted bits; as a register-space store pointer, it is an
1418    /// lvalue and must instead mean the base register address plus that offset.
1419    /// Treating it as extracted bits writes to an address derived from the old
1420    /// high word, leaving the high 16 bits of an i80 register stale.
1421    fn register_range_store_address(
1422        &self,
1423        ctx: &Context<'_>,
1424        func: FunctionId,
1425        store: &Store,
1426    ) -> Option<u64> {
1427        let space = store.space.qualify(func);
1428        let space_id = space.shared()?;
1429        if !matches!(Space::from_id(ctx, space_id).ty, SpaceType::Register) {
1430            return None;
1431        }
1432        let ValueRef::Instruction(range) = ValueRef::new(store.ptr.qualify(func), ctx) else {
1433            return None;
1434        };
1435        let Mnemonic::Range(Range { src, start, .. }) = range.mnemonic() else {
1436            return None;
1437        };
1438        let ValueRef::Varnode(varnode) = ValueRef::new(src.qualify(func), ctx) else {
1439            return None;
1440        };
1441        let varnode = Varnode::from_id(ctx, varnode.id);
1442        (varnode.space().id == space_id).then_some(varnode.address() as u64 + *start as u64)
1443    }
1444
1445    fn apply_call_continuation(
1446        &mut self,
1447        ctx: &Context<'_>,
1448        continuation: CallContinuation,
1449    ) -> Result<(), EmulatorErrorKind> {
1450        let target = match continuation {
1451            CallContinuation::Block(block) => block,
1452            CallContinuation::Address(addr) => self
1453                .block_at(ctx, addr)
1454                .ok_or(EmulatorErrorKind::InvalidBlockAddress(addr))?,
1455        };
1456        self.block = target;
1457        self.idx = 0;
1458        Ok(())
1459    }
1460
1461    fn intercept_call(
1462        &mut self,
1463        ctx: &Context<'_>,
1464        block: BlockId,
1465        instruction: InstructionId,
1466        call: &Call,
1467    ) -> crate::Result<Option<StepEvent>> {
1468        let Some(mut interceptor) = self.call_interceptor.take() else {
1469            return Ok(None);
1470        };
1471        let target = require_real_callee(call.target)
1472            .map_err(|kind| self.make_error_at(ctx, instruction, kind))?;
1473
1474        let site = CallSite {
1475            instruction,
1476            block,
1477            target,
1478            // Operands are stored bare-local; the CallSite is a boundary object,
1479            // so qualify with the call instruction's own function.
1480            args: call
1481                .args
1482                .iter()
1483                .map(|a| a.qualify(instruction.func))
1484                .collect(),
1485        };
1486        let result = interceptor(ctx, self, &site);
1487        self.call_interceptor = Some(interceptor);
1488
1489        match result {
1490            Ok(CallInterception::PassThrough) => Ok(None),
1491            Ok(CallInterception::Handled(continuation)) => {
1492                self.apply_call_continuation(ctx, continuation)
1493                    .map_err(|kind| self.make_error_at(ctx, instruction, kind))?;
1494                Ok(Some(StepEvent::InterceptedCall))
1495            }
1496            Err(message) => Err(self.make_error_at(
1497                ctx,
1498                instruction,
1499                EmulatorErrorKind::InterceptError(message),
1500            )),
1501        }
1502    }
1503
1504    /// Evaluate an operand through the scalar interpreter while retaining its
1505    /// full f80 payload (the public convenience getter intentionally returns
1506    /// only u64 values).
1507    fn scalar_value(
1508        &mut self,
1509        ctx: &Context<'_>,
1510        id: ValueId,
1511    ) -> Result<SizedValue, EmulatorErrorKind> {
1512        let mut interpreter = TempInterpreter {
1513            memory: &mut self.memory,
1514            literals: &mut self.literal_cache,
1515            insn_values: &mut self.insn_values,
1516            block_param_values: &mut self.block_param_values,
1517            poison_params: &self.poison_params,
1518            ctx,
1519        };
1520        interpreter.get_value(id)
1521    }
1522
1523    /// Decode the IEEE rounding-direction argument used by the supplemental
1524    /// floating p-code operations.  Its encoding is deliberately independent
1525    /// of architectural control words: 0 = nearest-even, 1 = down, 2 = up,
1526    /// 3 = toward zero.
1527    fn ieee_rounding_mode(value: u128) -> Option<Round> {
1528        match value {
1529            0 => Some(Round::NearestTiesToEven),
1530            1 => Some(Round::TowardNegative),
1531            2 => Some(Round::TowardPositive),
1532            3 => Some(Round::TowardZero),
1533            _ => None,
1534        }
1535    }
1536
1537    /// Evaluate one of the explicit IEEE arithmetic p-code operations in the
1538    /// format carried by its operands.  It reports IEEE facts only: no
1539    /// architectural payload selection and no precision-control policy.
1540    fn ieee_arithmetic(
1541        lhs: SizedValue,
1542        rhs: SizedValue,
1543        round: Round,
1544        name: &str,
1545    ) -> Option<(SizedValue, Status)> {
1546        if lhs.size != rhs.size {
1547            return None;
1548        }
1549        macro_rules! operation {
1550            ($lhs:expr, $rhs:expr) => {
1551                match name {
1552                    "float_add" | "float_add_flags" => $lhs.add_r($rhs, round),
1553                    "float_sub" | "float_sub_flags" => $lhs.sub_r($rhs, round),
1554                    "float_mul" | "float_mul_flags" => $lhs.mul_r($rhs, round),
1555                    "float_div" | "float_div_flags" => $lhs.div_r($rhs, round),
1556                    _ => return None,
1557                }
1558            };
1559        }
1560        // Keeping the formats separate is important: routing f32 through f64
1561        // changes both the rounded result and its exception facts.
1562        match lhs.size {
1563            4 => {
1564                let lhs = Single::from_bits(lhs.as_bits());
1565                let rhs = Single::from_bits(rhs.as_bits());
1566                let value = operation!(lhs, rhs);
1567                Some((
1568                    SizedValue::from_bits(value.value.to_bits(), 4),
1569                    value.status,
1570                ))
1571            }
1572            8 => {
1573                let lhs = Double::from_bits(lhs.as_bits());
1574                let rhs = Double::from_bits(rhs.as_bits());
1575                let value = operation!(lhs, rhs);
1576                Some((
1577                    SizedValue::from_bits(value.value.to_bits(), 8),
1578                    value.status,
1579                ))
1580            }
1581            10 => {
1582                let lhs = X87DoubleExtended::from_bits(lhs.as_bits());
1583                let rhs = X87DoubleExtended::from_bits(rhs.as_bits());
1584                let value = operation!(lhs, rhs);
1585                Some((
1586                    SizedValue::from_bits(value.value.to_bits(), 10),
1587                    value.status,
1588                ))
1589            }
1590            _ => None,
1591        }
1592    }
1593
1594    /// Round a floating value to a narrower significand precision within its
1595    /// own exponent range.  This is an IEEE-level operation: the precision is
1596    /// an explicit argument, not read from any architectural control word.
1597    /// Only the 80-bit format is implemented; f32/f64 operands return `None`
1598    /// so the caller falls through to the generic interpreter.
1599    fn ieee_round_to_precision(
1600        value: SizedValue,
1601        precision: u128,
1602        round: Round,
1603    ) -> Option<(SizedValue, Status)> {
1604        if value.size != 10 {
1605            return None;
1606        }
1607        let precision = u32::try_from(precision).ok()?;
1608        let result = float80::round_to_precision(value.as_bits(), precision, round);
1609        Some((SizedValue::from_bits(result.bits, 10), result.status))
1610    }
1611
1612    /// Narrow an extended value to a smaller IEEE format under an explicit
1613    /// rounding mode.  This is the conversion alone: no architectural
1614    /// indefinite substitution, no status word, no store policy.
1615    fn ieee_narrow(value: SizedValue, size: u128, round: Round) -> Option<(SizedValue, Status)> {
1616        let mut loses_info = false;
1617        macro_rules! to {
1618            ($source:expr, $target:ty, $bytes:expr) => {{
1619                let converted: rustc_apfloat::StatusAnd<$target> =
1620                    $source.convert_r(round, &mut loses_info);
1621                Some((
1622                    SizedValue::from_bits(converted.value.to_bits(), $bytes),
1623                    converted.status,
1624                ))
1625            }};
1626        }
1627        match (value.size, size) {
1628            (8, 4) => to!(Double::from_bits(value.as_bits()), Single, 4),
1629            (10, 4) => to!(X87DoubleExtended::from_bits(value.as_bits()), Single, 4),
1630            (10, 8) => to!(X87DoubleExtended::from_bits(value.as_bits()), Double, 8),
1631            _ => None,
1632        }
1633    }
1634
1635    /// Widen a 32- or 64-bit IEEE value to the extended format.  Every such
1636    /// value is exactly representable there, so the widening is lossless and
1637    /// operations implemented for the extended format alone can serve the
1638    /// narrower ones through it.
1639    /// True when a 32-, 64- or 80-bit IEEE value is a signalling NaN.  The
1640    /// widening below quiets one, so the fact has to be read from the source.
1641    fn is_signaling_nan(value: SizedValue) -> bool {
1642        let bits = value.as_bits();
1643        match value.size {
1644            4 => Single::from_bits(bits).is_signaling(),
1645            8 => Double::from_bits(bits).is_signaling(),
1646            10 => X87DoubleExtended::from_bits(bits).is_signaling(),
1647            _ => false,
1648        }
1649    }
1650
1651    fn widen_to_f80(value: SizedValue) -> Option<X87DoubleExtended> {
1652        let mut loses_info = false;
1653        match value.size {
1654            4 => Some(
1655                Single::from_bits(value.as_bits())
1656                    .convert_r(Round::NearestTiesToEven, &mut loses_info)
1657                    .value,
1658            ),
1659            8 => Some(
1660                Double::from_bits(value.as_bits())
1661                    .convert_r(Round::NearestTiesToEven, &mut loses_info)
1662                    .value,
1663            ),
1664            10 => Some(X87DoubleExtended::from_bits(value.as_bits())),
1665            _ => None,
1666        }
1667    }
1668
1669    /// Narrow an extended intermediate that carries a sticky inexactness back
1670    /// to `size` bytes without double rounding.  Forcing the extended
1671    /// significand's low bit when the intermediate was inexact (round to odd)
1672    /// keeps the exact value on the correct side of every boundary of the
1673    /// narrower format, which has at least two fewer significand bits.
1674    fn narrow_with_sticky(
1675        bits: u128,
1676        inexact: bool,
1677        size: u8,
1678        round: Round,
1679    ) -> Option<(SizedValue, Status)> {
1680        if size == 10 {
1681            return Some((
1682                SizedValue::from_bits(bits, 10),
1683                if inexact { Status::INEXACT } else { Status::OK },
1684            ));
1685        }
1686        let sticky = if inexact { bits | 1 } else { bits };
1687        let (mut result, mut status) =
1688            Self::ieee_narrow(SizedValue::from_bits(sticky, 10), u128::from(size), round)?;
1689        if inexact {
1690            status |= Status::INEXACT;
1691        }
1692        result = SizedValue::from_bits(result.as_bits(), size as usize);
1693        Some((result, status))
1694    }
1695
1696    /// Convert a floating value to a two's-complement integer of `size` bytes
1697    /// under an explicit rounding mode.  A NaN, an infinity or a value outside
1698    /// the destination range is invalid; the architectural replacement value
1699    /// for that case is the caller's business, not this operation's.
1700    fn ieee_to_int(value: SizedValue, size: u128, round: Round) -> Option<(SizedValue, Status)> {
1701        // Every narrower format widens losslessly, so one extended conversion
1702        // serves f32, f64 and f80 alike.
1703        let signaling = Self::is_signaling_nan(value);
1704        let value = SizedValue::from_bits(Self::widen_to_f80(value)?.to_bits(), 10);
1705        let size = usize::try_from(size).ok()?;
1706        if !matches!(size, 2 | 4 | 8) {
1707            return None;
1708        }
1709        let mut exact = false;
1710        let converted =
1711            X87DoubleExtended::from_bits(value.as_bits()).to_i128_r(size * 8, round, &mut exact);
1712        let mask = (1u128 << (size * 8)) - 1;
1713        let mut status = converted.status;
1714        if signaling {
1715            status |= Status::INVALID_OP;
1716        }
1717        Some((
1718            SizedValue::from_bits(converted.value as u128 & mask, size),
1719            status,
1720        ))
1721    }
1722
1723    /// Convert a two's-complement integer of the operand's own width to an
1724    /// IEEE value of `size` bytes under an explicit rounding mode.  Only
1725    /// inexact is possible; what the conversion means architecturally is the
1726    /// caller's business.
1727    fn ieee_from_int(value: SizedValue, size: u128, round: Round) -> Option<(SizedValue, Status)> {
1728        let source = value.signed_value();
1729        let width = usize::from(value.size) * 8;
1730        match size {
1731            4 => {
1732                let converted = Single::from_i128_r(source, round);
1733                Some((
1734                    SizedValue::from_bits(converted.value.to_bits(), 4),
1735                    converted.status,
1736                ))
1737            }
1738            8 => {
1739                let converted = Double::from_i128_r(source, round);
1740                Some((
1741                    SizedValue::from_bits(converted.value.to_bits(), 8),
1742                    converted.status,
1743                ))
1744            }
1745            10 => {
1746                let converted = X87DoubleExtended::from_i128_r(source, round);
1747                Some((
1748                    SizedValue::from_bits(converted.value.to_bits(), 10),
1749                    converted.status,
1750                ))
1751            }
1752            _ => {
1753                let _ = width;
1754                None
1755            }
1756        }
1757    }
1758
1759    fn ieee_flags(status: Status) -> SizedValue {
1760        let mut flags = 0u128;
1761        if status.contains(Status::INVALID_OP) {
1762            flags |= 1;
1763        }
1764        if status.contains(Status::DIV_BY_ZERO) {
1765            flags |= 1 << 2;
1766        }
1767        if status.contains(Status::OVERFLOW) {
1768            flags |= 1 << 3;
1769        }
1770        if status.contains(Status::UNDERFLOW) {
1771            flags |= 1 << 4;
1772        }
1773        if status.contains(Status::INEXACT) {
1774            flags |= 1 << 5;
1775        }
1776        SizedValue::from_bits(flags, 1)
1777    }
1778
1779    /// Interpret the packed-integer SLEIGH user-ops that x86's MMX/SSE
1780    /// constructors leave as `pcodeop` applications. Returns `None` for every
1781    /// other user-op so the generic interpreter stays the implementation.
1782    ///
1783    /// `pavgb`/`pavgw` are applied by the spec to one lane at a time, so they
1784    /// are scalar here. `pmaddwd`/`pmulhuw` receive a whole vector and are
1785    /// width-generic, covering the 8-byte MMX and 16-byte XMM forms alike.
1786    fn interpret_packed_pcode_op(
1787        &mut self,
1788        ctx: &Context<'_>,
1789        insn: &InstructionRef<'_, '_>,
1790        mnemonic: &Mnemonic,
1791    ) -> Result<Option<SizedValue>, EmulatorErrorKind> {
1792        let Mnemonic::PCodeOp(op) = mnemonic else {
1793            return Ok(None);
1794        };
1795        let name = ctx.shared.pcode_ops[op.id].clone();
1796        let func = insn.id.func;
1797
1798        // The significand/exponent split is a pure decomposition of the
1799        // encoding and needs no context at all.
1800        if let [src] = op.args.as_slice() {
1801            let value = self.scalar_value(ctx, src.qualify(func))?;
1802            if value.size != 10 {
1803                return Ok(None);
1804            }
1805            return Ok(match name.as_ref() {
1806                "extract_significand" => Some(SizedValue::from_f80_bits(
1807                    float80::extract_significand(value.as_bits()),
1808                )),
1809                "extract_exponent" => Some(SizedValue::from_f80_bits(
1810                    float80::extract_exponent(value.as_bits()).bits,
1811                )),
1812                _ => None,
1813            });
1814        }
1815
1816        // The three-argument form is the explicit, architecture-neutral IEEE
1817        // interface.  Leave two-argument user-ops on the legacy dispatch below.
1818        if let [lhs, rhs, rounding_mode] = op.args.as_slice() {
1819            let lhs = self.scalar_value(ctx, lhs.qualify(func))?;
1820            let rhs = self.scalar_value(ctx, rhs.qualify(func))?;
1821            let rounding_mode = self.scalar_value(ctx, rounding_mode.qualify(func))?;
1822
1823            // The partial remainder's third operand selects the quotient's
1824            // rounding rule rather than the result's: zero truncates it toward
1825            // zero, non-zero rounds it to nearest even. The remainder itself is
1826            // exact under either rule, so no result rounding mode applies.
1827            if matches!(name.as_ref(), "float_rem_partial" | "float_rem_quotient")
1828                && lhs.size == 10
1829                && rhs.size == 10
1830            {
1831                let to_nearest = rounding_mode.as_bits() != 0;
1832                let result = float80::remainder(lhs.as_bits(), rhs.as_bits(), to_nearest);
1833                return Ok(Some(if name.as_ref() == "float_rem_quotient" {
1834                    // Bits 0-2 hold the low three bits of the quotient's
1835                    // magnitude; bit 3 reports that the reduction was partial,
1836                    // in which case no quotient bits are available and bits 0-2
1837                    // are zero.
1838                    let code = if result.incomplete {
1839                        8
1840                    } else {
1841                        u128::from(result.quotient & 7)
1842                    };
1843                    SizedValue::from_bits(code, 1)
1844                } else {
1845                    SizedValue::from_f80_bits(result.bits)
1846                }));
1847            }
1848
1849            let Some(round) = Self::ieee_rounding_mode(rounding_mode.as_bits()) else {
1850                return Ok(None);
1851            };
1852            let evaluated = match name.as_ref() {
1853                "float_round_to_precision" | "float_round_to_precision_flags" => {
1854                    Self::ieee_round_to_precision(lhs, rhs.as_bits(), round)
1855                }
1856                "float_narrow" | "float_narrow_flags" => {
1857                    Self::ieee_narrow(lhs, rhs.as_bits(), round)
1858                }
1859                "float_to_int" | "float_to_int_flags" => {
1860                    Self::ieee_to_int(lhs, rhs.as_bits(), round)
1861                }
1862                "float_scalb" | "float_scalb_flags" if lhs.size == 10 => {
1863                    let steps = i32::try_from(rhs.as_bits() as i64).unwrap_or(
1864                        if (rhs.as_bits() as i64) < 0 {
1865                            i32::MIN
1866                        } else {
1867                            i32::MAX
1868                        },
1869                    );
1870                    let result = float80::scalb_ieee(lhs.as_bits(), steps, round);
1871                    Some((SizedValue::from_f80_bits(result.bits), result.status))
1872                }
1873                "float_from_int" | "float_from_int_flags" => {
1874                    Self::ieee_from_int(lhs, rhs.as_bits(), round)
1875                }
1876                _ => Self::ieee_arithmetic(lhs, rhs, round, name.as_ref()),
1877            };
1878            if let Some((result, status)) = evaluated {
1879                return Ok(Some(if name.ends_with("_flags") {
1880                    Self::ieee_flags(status)
1881                } else {
1882                    result
1883                }));
1884            }
1885            return Ok(None);
1886        }
1887
1888        let [lhs, rhs] = op.args.as_slice() else {
1889            return Ok(None);
1890        };
1891        let lhs = self.scalar_value(ctx, lhs.qualify(func))?;
1892        let rhs = self.scalar_value(ctx, rhs.qualify(func))?;
1893
1894        // The explicit IEEE unary operations take their rounding mode as the
1895        // second operand and report architecture-neutral facts, exactly like
1896        // their binary counterparts.
1897        if let (true, Some(round)) = (
1898            matches!(lhs.size, 4 | 8 | 10),
1899            Self::ieee_rounding_mode(rhs.as_bits()),
1900        ) {
1901            // Each operation is evaluated in the extended format, which holds
1902            // every f32 and f64 exactly; the intermediate is then narrowed back
1903            // with its inexactness kept sticky, so a single rounding reaches
1904            // the operand's own format.
1905            let extended_only = matches!(
1906                name.as_ref(),
1907                "float_log2" | "float_log2_flags" | "to_bcd" | "to_bcd_flags"
1908            );
1909            let wide = (!extended_only || lhs.size == 10)
1910                .then(|| Self::widen_to_f80(lhs))
1911                .flatten();
1912            let unary = wide.and_then(|wide| {
1913                let wide = wide.to_bits();
1914                // A narrower format is served by an extended evaluation that
1915                // truncates, so that the sticky narrowing below performs the
1916                // operand format's single rounding. Rounding twice would move
1917                // an inexact result two steps under a directed mode.
1918                let inner = if lhs.size == 10 {
1919                    round
1920                } else {
1921                    Round::TowardZero
1922                };
1923                Some(match name.as_ref() {
1924                    "float_sqrt" | "float_sqrt_flags" => float80::sqrt_ieee(wide, inner),
1925                    "float_round_to_integral" | "float_round_to_integral_flags" => {
1926                        float80::round_to_integral_ieee(wide, round)
1927                    }
1928                    "float_log2" | "float_log2_flags" => float80::log2_ieee(wide),
1929                    "to_bcd" | "to_bcd_flags" => float80::to_bcd(wide, round),
1930                    _ => return None,
1931                })
1932            });
1933            if let Some(result) = unary {
1934                if name.starts_with("to_bcd") {
1935                    return Ok(Some(if name.ends_with("_flags") {
1936                        Self::ieee_flags(result.status)
1937                    } else {
1938                        // The packed decimal is ten bytes of digits, not a float.
1939                        SizedValue::from_bits(result.bits, 10)
1940                    }));
1941                }
1942                // Widening quiets a signalling NaN, so its invalid report is
1943                // read from the source operand instead.
1944                let mut result = result;
1945                if Self::is_signaling_nan(lhs) {
1946                    result.status |= Status::INVALID_OP;
1947                }
1948                let inexact = result.status.contains(Status::INEXACT);
1949                // Round-to-integral already delivers the operand format's own
1950                // result, exact in that format, so its inexactness is reported
1951                // as is and must not act as a sticky bit for the narrowing: a
1952                // directed mode would otherwise move the integer one more ulp.
1953                let integral = name.starts_with("float_round_to_integral");
1954                let sticky = inexact && !integral;
1955                let kept = if integral {
1956                    result.status
1957                } else {
1958                    result.status & !Status::INEXACT
1959                };
1960                let (value, status) =
1961                    match Self::narrow_with_sticky(result.bits, sticky, lhs.size, round) {
1962                        Some((value, status)) => (value, status | kept),
1963                        None => (
1964                            SizedValue::from_bits(result.bits, lhs.size as usize),
1965                            result.status,
1966                        ),
1967                    };
1968                return Ok(Some(if name.ends_with("_flags") {
1969                    Self::ieee_flags(status)
1970                } else {
1971                    value
1972                }));
1973            }
1974        }
1975
1976        // Unsigned rounded average of one lane: (a + b + 1) >> 1, computed
1977        // wide enough that the carry out of the lane is kept.
1978        let average = |width: usize| -> Option<SizedValue> {
1979            (lhs.size as usize == width && rhs.size as usize == width).then(|| {
1980                let sum = lhs.as_bits() + rhs.as_bits() + 1;
1981                SizedValue::from_bits(sum >> 1, width)
1982            })
1983        };
1984
1985        let value = match name.as_ref() {
1986            "pavgb" => average(1),
1987            "pavgw" => average(2),
1988            // Unsigned 16x16 multiply per word lane, keeping the high half.
1989            "pmulhuw" => Self::packed_lanes(&lhs, &rhs, 2, |a, b| ((a * b) >> 16) & 0xffff),
1990            // Saturating packed add/subtract. A signed lane clamps to its
1991            // width's bounds; an unsigned lane clamps to zero and its maximum.
1992            "paddsb" => Self::saturating(&lhs, &rhs, 1, true, false),
1993            "paddsw" => Self::saturating(&lhs, &rhs, 2, true, false),
1994            "psubsb" => Self::saturating(&lhs, &rhs, 1, true, true),
1995            "psubsw" => Self::saturating(&lhs, &rhs, 2, true, true),
1996            "paddusb" => Self::saturating(&lhs, &rhs, 1, false, false),
1997            "paddusw" => Self::saturating(&lhs, &rhs, 2, false, false),
1998            "psubusb" => Self::saturating(&lhs, &rhs, 1, false, true),
1999            "psubusw" => Self::saturating(&lhs, &rhs, 2, false, true),
2000            // Signed 16x16 multiplies summed in pairs into each dword lane.
2001            "pmaddwd" => Self::packed_lanes(&lhs, &rhs, 4, |a, b| {
2002                let word =
2003                    |v: u128, half: u32| i64::from(((v >> (half * 16)) & 0xffff) as u16 as i16);
2004                let product = word(a, 0) * word(b, 0) + word(a, 1) * word(b, 1);
2005                u128::from(product as u32)
2006            }),
2007            _ => None,
2008        };
2009        Ok(value)
2010    }
2011
2012    /// Saturating packed add (`subtract` false) or subtract, per `width`-byte
2013    /// lane. `signed` selects signed bounds over unsigned ones.
2014    fn saturating(
2015        lhs: &SizedValue,
2016        rhs: &SizedValue,
2017        width: usize,
2018        signed: bool,
2019        subtract: bool,
2020    ) -> Option<SizedValue> {
2021        let bits = width * 8;
2022        Self::packed_lanes(lhs, rhs, width, |a, b| {
2023            if signed {
2024                let sign =
2025                    |v: u128| (v as i128) - (((v >> (bits - 1)) & 1) as i128) * (1i128 << bits);
2026                let (a, b) = (sign(a), sign(b));
2027                let value = if subtract { a - b } else { a + b };
2028                let max = (1i128 << (bits - 1)) - 1;
2029                let min = -(1i128 << (bits - 1));
2030                (value.clamp(min, max) as u128) & ((1u128 << bits) - 1)
2031            } else if subtract {
2032                a.saturating_sub(b)
2033            } else {
2034                (a + b).min((1u128 << bits) - 1)
2035            }
2036        })
2037    }
2038
2039    /// Apply `lane` to each `width`-byte lane of two equally sized vectors.
2040    /// Returns `None` unless both operands share a width that divides evenly
2041    /// into lanes.
2042    fn packed_lanes(
2043        lhs: &SizedValue,
2044        rhs: &SizedValue,
2045        width: usize,
2046        lane: impl Fn(u128, u128) -> u128,
2047    ) -> Option<SizedValue> {
2048        let size = lhs.size as usize;
2049        if size != rhs.size as usize || size == 0 || !size.is_multiple_of(width) {
2050            return None;
2051        }
2052        let bits = width * 8;
2053        let mask = (1u128 << bits) - 1;
2054        let mut out = 0u128;
2055        for index in 0..size / width {
2056            let shift = index * bits;
2057            let a = (lhs.as_bits() >> shift) & mask;
2058            let b = (rhs.as_bits() >> shift) & mask;
2059            out |= (lane(a, b) & mask) << shift;
2060        }
2061        Some(SizedValue::from_bits(out, size))
2062    }
2063
2064    fn step_with_event(&mut self, ctx: &Context<'_>) -> crate::Result<StepEvent> {
2065        self.memory.configure_spaces(ctx);
2066        let block_id = self.block;
2067        if self.cached_block != Some(block_id) || self.idx == 0 {
2068            // The module only gains types while nothing is part-way through a
2069            // block — lifting happens on block entry — so this rides the same
2070            // refresh as the instruction list rather than paying per step.
2071            self.refresh_sequence_types(ctx);
2072            self.cached_insns.clear();
2073            self.cached_insns
2074                .extend_from_slice(ctx.block(block_id).instruction_ids());
2075            self.cached_block = Some(block_id);
2076        }
2077        // A degenerate block — empty, or exhausted without a terminator — is
2078        // malformed lifter output, not an emulator bug. Report it so a bounded
2079        // consumer (and a VM running lifted-on-demand code) can stop with a
2080        // reason instead of aborting the process.
2081        let Some(&local) = self.cached_insns.get(self.idx) else {
2082            return Err(self.make_empty_block_error(ctx));
2083        };
2084        let insn_id = InstructionId::new(block_id.func, local);
2085        let insn = InstructionRef::from_id(ctx, insn_id);
2086        let id = insn.id;
2087
2088        if let Some(hook) = self.instruction_hook.as_ref() {
2089            hook(&insn, self)
2090        }
2091
2092        // Resolved once and threaded through the step. Each `insn.mnemonic()`
2093        // re-walks `view.instruction(id)`, two registry lookups deep, and the
2094        // step path asked for the same instruction's mnemonic several times.
2095        let mnemonic = insn.mnemonic();
2096
2097        match mnemonic {
2098            Mnemonic::Branch(Branch { target, args }) => {
2099                // Terminator targets are bare body-local indices in the terminator's
2100                // own arena (`id.func`); qualify to the current block's function.
2101                let target = BlockId::new(id.func, *target);
2102                self.bind_block_args(ctx, id.func, target, args)
2103                    .map_err(|kind| self.make_error(ctx, kind))?;
2104                self.block = target;
2105                self.idx = 0;
2106            }
2107
2108            Mnemonic::Call(call) => {
2109                if let Some(event) = self.intercept_call(ctx, block_id, insn_id, call)? {
2110                    return Ok(event);
2111                }
2112                let target =
2113                    require_real_callee(call.target).map_err(|kind| self.make_error(ctx, kind))?;
2114                self.block = FunctionBody::from_id(ctx, target)
2115                    .root()
2116                    .ok_or_else(|| {
2117                        self.make_error(ctx, EmulatorErrorKind::EmptyFunctionRoot(target))
2118                    })?
2119                    .id;
2120                self.idx = 0;
2121                // Remember this call so the matching `Return` can deposit the
2122                // callee's `Return.value` as this call's (aggregate) result.
2123                self.call_site_stack.push(insn_id);
2124                return Ok(StepEvent::DirectCallEntered(target));
2125            }
2126
2127            Mnemonic::TailCall(tc) => {
2128                // A tail call pops our frame and transfers to the callee's entry;
2129                // the callee's `Return` returns directly to *our* caller. Mirror the
2130                // old tail-`Branch`-into-entry behavior: jump to the callee root
2131                // without pushing a call frame.
2132                let target =
2133                    require_real_callee(tc.target).map_err(|kind| self.make_error(ctx, kind))?;
2134                self.block = FunctionBody::from_id(ctx, target)
2135                    .root()
2136                    .ok_or_else(|| {
2137                        self.make_error(ctx, EmulatorErrorKind::EmptyFunctionRoot(target))
2138                    })?
2139                    .id;
2140                self.idx = 0;
2141            }
2142
2143            Mnemonic::Apply(apply) => {
2144                const APPLY_STEP_BUDGET: usize = 100_000;
2145                let target =
2146                    require_real_callee(apply.target).map_err(|kind| self.make_error(ctx, kind))?;
2147                let args = self
2148                    .collect_block_args(ctx, id.func, &apply.args)
2149                    .map_err(|kind| self.make_error(ctx, kind))?;
2150                let root = FunctionBody::from_id(ctx, target)
2151                    .root()
2152                    .ok_or_else(|| {
2153                        self.make_error(ctx, EmulatorErrorKind::EmptyFunctionRoot(target))
2154                    })?
2155                    .id;
2156                let mut nested = StandaloneEmulator::<M>::new_in(root);
2157                nested
2158                    .run_pure(ctx, target, &args, APPLY_STEP_BUDGET)
2159                    .map_err(|e| self.make_error(ctx, e.kind))?;
2160                let ret_value = lambda_return_value(ctx, nested.current_block())
2161                    .ok_or_else(|| self.make_error(ctx, EmulatorErrorKind::ValueError(0)))?;
2162                if let Some(value) = nested.get_value(ctx, ret_value) {
2163                    let size = ctx
2164                        .stored_type_of(ret_value)
2165                        .map(|ty| ctx.shared.types.size_of(ty))
2166                        .unwrap_or(8);
2167                    self.insn_values
2168                        .insert(insn_id, SizedValue::new(value, size));
2169                } else if let ValueId::Instruction(ret_id) = ret_value
2170                    && let Some(agg) = nested.aggregate_values.get(&ret_id).cloned()
2171                {
2172                    // The lambda returns an aggregate (e.g. the result tuple produced
2173                    // by accumulator elimination); propagate it field-wise so the
2174                    // caller's `extract(apply, i)` resolves — mirroring the scalar
2175                    // case above and the `Return` arm's call-result handling.
2176                    self.aggregate_values.insert(insn_id, agg);
2177                }
2178                self.idx += 1;
2179            }
2180
2181            Mnemonic::CBranch(CBranch {
2182                condition,
2183                success_block: target,
2184                success_args,
2185                failure_block: fallthrough,
2186                failure_args,
2187            }) => {
2188                let cond_val = self.get_value(ctx, condition.qualify(id.func)).unwrap();
2189                let target = BlockId::new(id.func, *target);
2190                let fallthrough = BlockId::new(id.func, *fallthrough);
2191                if cond_val != 0 {
2192                    self.bind_block_args(ctx, id.func, target, success_args)
2193                        .map_err(|kind| self.make_error(ctx, kind))?;
2194                    self.block = target;
2195                } else {
2196                    self.bind_block_args(ctx, id.func, fallthrough, failure_args)
2197                        .map_err(|kind| self.make_error(ctx, kind))?;
2198                    self.block = fallthrough;
2199                }
2200                self.idx = 0;
2201            }
2202
2203            Mnemonic::Switch(switch) => {
2204                let value = self
2205                    .get_value(ctx, switch.scrutinee.qualify(id.func))
2206                    .unwrap();
2207                let arm = switch
2208                    .cases
2209                    .iter()
2210                    .find(|case| case.value == value)
2211                    .map(|case| (case.target, &case.args));
2212                let (target, args) = match arm
2213                    .or_else(|| switch.default.map(|target| (target, &switch.default_args)))
2214                {
2215                    Some(arm) => arm,
2216                    // No arm matches and there is no default. A table behind a
2217                    // bounds check is total over its listed values, so arriving
2218                    // here means the guard that guaranteed that was wrong.
2219                    None => {
2220                        return Err(
2221                            self.make_error(ctx, EmulatorErrorKind::InvalidBlockAddress(value))
2222                        );
2223                    }
2224                };
2225                let target = BlockId::new(id.func, target);
2226                self.bind_block_args(ctx, id.func, target, args)
2227                    .map_err(|kind| self.make_error(ctx, kind))?;
2228                self.block = target;
2229                self.idx = 0;
2230            }
2231
2232            Mnemonic::BranchInd(BranchInd { ptr }) => {
2233                let addr = self.get_value(ctx, ptr.qualify(id.func)).unwrap();
2234                let target = self.block_at(ctx, addr).ok_or_else(|| {
2235                    self.make_error(ctx, EmulatorErrorKind::InvalidBlockAddress(addr))
2236                })?;
2237                self.block = target;
2238                self.idx = 0;
2239            }
2240
2241            Mnemonic::CallInd(CallInd { ptr, .. }) => {
2242                let addr = self.get_value(ctx, ptr.qualify(id.func)).unwrap();
2243                let target = self.block_at(ctx, addr).ok_or_else(|| {
2244                    self.make_error(ctx, EmulatorErrorKind::InvalidBlockAddress(addr))
2245                })?;
2246                self.block = target;
2247                self.idx = 0;
2248                self.call_site_stack.push(insn_id);
2249                return Ok(StepEvent::IndirectCallEntered);
2250            }
2251
2252            Mnemonic::Return(Return { ptr, value, .. }) => {
2253                // Deposit the callee's return value as the result of the call that
2254                // entered it: an aggregate (the functional write-set) is copied
2255                // field-wise; a scalar return is copied through. This is what makes
2256                // a caller's `extract(call, i)` see the callee's effects.
2257                if let Some(call_id) = self.call_site_stack.pop() {
2258                    if let Some(LocalValueId::Instruction(src_local)) = value {
2259                        let src = InstructionId::new(id.func, *src_local);
2260                        if let Some(agg) = self.aggregate_values.get(&src).cloned() {
2261                            self.aggregate_values.insert(call_id, agg);
2262                        } else if let Some(scalar) = self.insn_values.get(&src).copied() {
2263                            self.insn_values.insert(call_id, scalar);
2264                        }
2265                    }
2266                    // v2 implicit convention: an `Opaque` (non-regpure) call to a
2267                    // *materialized* callee binds outputs by storing each return-pack
2268                    // slot back to its mapped register (post-mem2reg the callee body
2269                    // may no longer write those registers directly). A regpure site
2270                    // replays the pack itself, so it is skipped.
2271                    self.writeback_materialized_outputs(ctx, call_id, id.func);
2272                }
2273
2274                let addr = self.get_value(ctx, ptr.qualify(id.func)).unwrap();
2275                let target = self.block_at(ctx, addr).ok_or_else(|| {
2276                    self.make_error(ctx, EmulatorErrorKind::InvalidBlockAddress(addr))
2277                })?;
2278                self.block = target;
2279                self.idx = 0;
2280                return Ok(StepEvent::Return);
2281            }
2282
2283            Mnemonic::ReturnValue(_) => {
2284                return Ok(StepEvent::ReturnValue);
2285            }
2286
2287            // Aggregate construction: evaluate each field and stash the field
2288            // vector. Fields are scalar for the register write-set (nested
2289            // aggregates, e.g. the RAM channel, are not modelled here yet).
2290            Mnemonic::Tuple(Tuple { fields }) => {
2291                let fields = fields.clone();
2292                let mut vals: Vec<SizedValue> = Vec::with_capacity(fields.len());
2293                for f in fields {
2294                    let val = {
2295                        let mut tmp = TempInterpreter {
2296                            memory: &mut self.memory,
2297                            literals: &mut self.literal_cache,
2298                            insn_values: &mut self.insn_values,
2299                            block_param_values: &mut self.block_param_values,
2300                            poison_params: &self.poison_params,
2301                            ctx,
2302                        };
2303                        tmp.get_value(f.qualify(id.func))
2304                    };
2305                    vals.push(val.map_err(|kind| self.make_error(ctx, kind))?);
2306                }
2307                self.aggregate_values.insert(insn_id, vals);
2308                self.idx += 1;
2309            }
2310
2311            // Aggregate projection: pull field `index` out of the stashed vector.
2312            Mnemonic::Extract(Extract { agg, index }) => {
2313                let field = match agg {
2314                    LocalValueId::Instruction(agg_local) => self
2315                        .aggregate_values
2316                        .get(&InstructionId::new(id.func, *agg_local))
2317                        .and_then(|v| v.get(*index))
2318                        .copied(),
2319                    // A `map` body's `enumerate` lane arrives as an aggregate
2320                    // block param seeded by `run_map_body`.
2321                    LocalValueId::BlockParam(pid_local) => self
2322                        .block_param_aggregates
2323                        .get(&BlockParamId::new(id.func, *pid_local))
2324                        .and_then(|v| v.get(*index))
2325                        .copied(),
2326                    _ => None,
2327                };
2328                if let Some(field) = field {
2329                    self.insn_values.insert(insn_id, field);
2330                }
2331                self.idx += 1;
2332            }
2333
2334            // Whole-array load: the region snapshot `%l0 = load(ram:{N*esz}, base)`
2335            // that `array_promote` reads once before an original-array scan/map.
2336            // Its result is array-typed, so it lives in `array_values`; a scalar
2337            // load falls through to the generic interpreter below.
2338            Mnemonic::Load(load) if self.is_array_operand(ctx, ValueId::Instruction(insn_id)) => {
2339                let (space, ptr, size) = (load.space, load.ptr.qualify(id.func), load.size);
2340                let addr = self
2341                    .get_value(ctx, ptr)
2342                    .ok_or_else(|| self.make_error(ctx, EmulatorErrorKind::ValueError(0)))?;
2343                let buf = self
2344                    .read_memory(ctx, space.qualify(id.func), addr, size)
2345                    .map_err(|kind| self.make_error(ctx, kind))?;
2346                self.array_values.insert(insn_id, buf);
2347                self.idx += 1;
2348            }
2349
2350            // Whole-array store: the promoted buffer written back to memory in one
2351            // shot (`store(ram, base <- arr)` at loop exit). A scalar store falls
2352            // through to the generic interpreter below.
2353            Mnemonic::Store(store)
2354                if self
2355                    .register_range_store_address(ctx, id.func, store)
2356                    .is_some() =>
2357            {
2358                let address = self
2359                    .register_range_store_address(ctx, id.func, store)
2360                    .expect("guard checked register range store address");
2361                let mut tmp = TempInterpreter {
2362                    memory: &mut self.memory,
2363                    literals: &mut self.literal_cache,
2364                    insn_values: &mut self.insn_values,
2365                    block_param_values: &mut self.block_param_values,
2366                    poison_params: &self.poison_params,
2367                    ctx,
2368                };
2369                let value = tmp.get_value(store.src.qualify(id.func));
2370                let value = value.map_err(|kind| self.make_error(ctx, kind))?;
2371                self.memory
2372                    .write(
2373                        store.space.qualify(id.func),
2374                        SizedValue::from_u64(address),
2375                        store.size,
2376                        value,
2377                    )
2378                    .map_err(|kind| self.make_error(ctx, kind))?;
2379                self.idx += 1;
2380            }
2381
2382            Mnemonic::Store(store) if self.is_array_operand(ctx, store.src.qualify(id.func)) => {
2383                let (space, ptr, src) = (
2384                    store.space,
2385                    store.ptr.qualify(id.func),
2386                    store.src.qualify(id.func),
2387                );
2388                let buf = self
2389                    .resolve_array(ctx, src)
2390                    .ok_or_else(|| self.make_error(ctx, EmulatorErrorKind::ValueError(0)))?;
2391                let addr = self
2392                    .get_value(ctx, ptr)
2393                    .ok_or_else(|| self.make_error(ctx, EmulatorErrorKind::ValueError(0)))?;
2394                self.write_memory(ctx, space.qualify(id.func), addr, &buf)
2395                    .map_err(|kind| self.make_error(ctx, kind))?;
2396                self.idx += 1;
2397            }
2398
2399            // Total left-scan: thread the accumulator through every lane, running
2400            // the (pure) binary body once per element, and materialize the result
2401            // as an array buffer.
2402            Mnemonic::Scan(scan) => {
2403                let scan = scan.clone();
2404                self.eval_scan(ctx, insn_id, &scan)
2405                    .map_err(|kind| self.make_error(ctx, kind))?;
2406                self.idx += 1;
2407            }
2408
2409            // Total map: apply the pure unary body to every source element.
2410            Mnemonic::Map(map) => {
2411                let map = map.clone();
2412                self.eval_map(ctx, insn_id, &map)
2413                    .map_err(|kind| self.make_error(ctx, kind))?;
2414                self.idx += 1;
2415            }
2416
2417            // Array slice: `arr[start:start+size]` on an array-typed source is a
2418            // sub-buffer (e.g. `l0[1..]`, the original-array scan source), kept in
2419            // the `array_values` domain. A scalar `Range` (bit-field extract) falls
2420            // through to the generic interpreter below.
2421            Mnemonic::Range(range) if self.is_array_operand(ctx, range.src.qualify(id.func)) => {
2422                let (src, start, size) = (range.src.qualify(id.func), range.start, range.size);
2423                let buf = self
2424                    .resolve_array(ctx, src)
2425                    .ok_or_else(|| self.make_error(ctx, EmulatorErrorKind::ValueError(0)))?;
2426                let end = (start + size).min(buf.len());
2427                let slice = buf.get(start..end).unwrap_or(&[]).to_vec();
2428                self.array_values.insert(insn_id, slice);
2429                self.idx += 1;
2430            }
2431
2432            // The sequence intrinsics whose value is an *array* (or a lane read out
2433            // of one) live in the `array_values` domain rather than scalar
2434            // `insn_values`; every other (scalar) intrinsic falls through to the
2435            // generic interpreter.
2436            Mnemonic::Intrinsic(app) if is_array_intrinsic(app.id.name()) => {
2437                let name = app.id.name();
2438                let args: Vec<ValueId> = app.args.iter().map(|a| a.qualify(id.func)).collect();
2439                self.eval_array_intrinsic(ctx, insn_id, name, &args)
2440                    .map_err(|kind| self.make_error(ctx, kind))?;
2441                self.idx += 1;
2442            }
2443
2444            _ => {
2445                if let Some(value) = self
2446                    .interpret_packed_pcode_op(ctx, &insn, mnemonic)
2447                    .map_err(|kind| self.make_error(ctx, kind))?
2448                {
2449                    self.insn_values.insert(id, value);
2450                    self.idx += 1;
2451                    return Ok(StepEvent::Normal);
2452                }
2453                let mut tmp = TempInterpreter {
2454                    memory: &mut self.memory,
2455                    literals: &mut self.literal_cache,
2456                    insn_values: &mut self.insn_values,
2457                    block_param_values: &mut self.block_param_values,
2458                    poison_params: &self.poison_params,
2459                    ctx,
2460                };
2461                if let Some(value) = tmp.interpret(insn, mnemonic)? {
2462                    self.insn_values.insert(id, value);
2463                }
2464                self.idx += 1;
2465            }
2466        }
2467
2468        Ok(StepEvent::Normal)
2469    }
2470
2471    pub fn step(&mut self, ctx: &Context<'_>) -> crate::Result<()> {
2472        self.step_with_event(ctx).map(|_| ())
2473    }
2474
2475    pub fn run_block(&mut self, ctx: &Context<'_>) -> crate::Result<()> {
2476        loop {
2477            self.step(ctx)?;
2478            if self.idx == 0 {
2479                break;
2480            }
2481        }
2482        Ok(())
2483    }
2484
2485    /// Runs blocks until the current block starts at `addr`.
2486    pub fn run_until(&mut self, ctx: &Context<'_>, addr: u64) -> crate::Result<()> {
2487        let target = self
2488            .block_at(ctx, addr)
2489            .ok_or_else(|| self.make_error(ctx, EmulatorErrorKind::UnknownAddress(addr)))?;
2490        while self.block != target {
2491            self.run_block(ctx)?;
2492        }
2493        Ok(())
2494    }
2495
2496    /// Runs the given function from its root block, stopping when the
2497    /// outermost `Return` is reached (without executing it).
2498    /// Nested calls are tracked via `call_depth` so inner returns are handled normally.
2499    /// The `call_stack` field is updated throughout execution.
2500    /// Seed a function's entry-block params with the values of the registers
2501    /// they were promoted from.
2502    ///
2503    /// `mem2reg` turns each register that is live-in to a function into a
2504    /// root-block parameter named after that register — these are the function's
2505    /// arguments. Entering the function (at the top level or via a call) binds
2506    /// those params from the current register file, so the callee receives the
2507    /// caller's register state through the calling convention. Params with no
2508    /// matching register (e.g. promoted stack slots) are left unbound.
2509    /// v2 implicit binding convention: store a materialized callee's return-pack
2510    /// slots back into their mapped registers on return from an `Opaque` call
2511    /// site. A `regpure` site is skipped (it replays the pack in its own body),
2512    /// as is any callee that is not materialized (no mapping to write back).
2513    fn writeback_materialized_outputs(
2514        &mut self,
2515        ctx: &Context<'_>,
2516        call_id: InstructionId,
2517        callee: FunctionId,
2518    ) {
2519        if call_is_regpure(ctx, call_id) {
2520            return;
2521        }
2522        let outputs = match &FunctionBody::from_id(ctx, callee).effects().register {
2523            qcode::value::RegisterChannelState::Materialized(map) => map.outputs.clone(),
2524            _ => return,
2525        };
2526        let Some(agg) = self.aggregate_values.get(&call_id).cloned() else {
2527            return;
2528        };
2529        for (field, &reg) in agg.iter().zip(&outputs) {
2530            let _ = self.set_varnode_u128(ctx, reg, field.as_bits());
2531        }
2532    }
2533
2534    fn seed_entry_params(&mut self, ctx: &Context<'_>, func: FunctionId) {
2535        let Some(root) = FunctionBody::from_id(ctx, func).root() else {
2536            return;
2537        };
2538        let root_id = root.id;
2539        enum Seed {
2540            Reg(VarnodeId),
2541            /// A materialized global value input: the param's origin is its
2542            /// address literal, and its value is the *contents* at that address
2543            /// (the RAM channel threads `mem[addr]` by value — see
2544            /// `argpromote::ram`'s global materialization). Implicit binding
2545            /// therefore reads memory at the literal, not the literal itself.
2546            Lit(u64),
2547        }
2548        let params: Vec<(BlockParamId, Option<Seed>, usize)> = BasicBlock::from_id(ctx, root_id)
2549            .params()
2550            .map(|param| {
2551                let src = param
2552                    .name()
2553                    .and_then(|name| ctx.get_named(name))
2554                    .and_then(|value| match value {
2555                        ValueId::Varnode(id) => Some(Seed::Reg(id)),
2556                        _ => None,
2557                    })
2558                    .or_else(|| match param.origin() {
2559                        Some(ValueId::Literal(_)) => {
2560                            let ValueRef::Literal(lit) = ValueRef::new(param.origin()?, ctx) else {
2561                                return None;
2562                            };
2563                            Some(Seed::Lit(lit.value()))
2564                        }
2565                        _ => None,
2566                    });
2567                (param.id, src, param.size())
2568            })
2569            .collect();
2570        for (param_id, src, size) in params {
2571            let value = match src {
2572                Some(Seed::Reg(varnode_id)) => self.read_varnode(ctx, varnode_id),
2573                Some(Seed::Lit(addr)) => {
2574                    // The literal is the global's *address*; the param carries the
2575                    // value stored there. Seed from memory (little-endian, param
2576                    // width) rather than the raw literal.
2577                    let space = ctx.shared.default_space;
2578                    self.read_memory(ctx, space, addr, size).ok().map(|bytes| {
2579                        let mut buf = [0u8; 8];
2580                        let n = bytes.len().min(8);
2581                        buf[..n].copy_from_slice(&bytes[..n]);
2582                        u64::from_le_bytes(buf)
2583                    })
2584                }
2585                None => None,
2586            };
2587            if let Some(value) = value {
2588                self.block_param_values
2589                    .insert(param_id, SizedValue::new(value, size));
2590            }
2591        }
2592    }
2593
2594    /// Bind a functionalized (`pure_reg`) callee's entry params positionally from
2595    /// the call's arguments, evaluated in the *caller's* frame.
2596    ///
2597    /// `argpromote_registers` makes such a callee a pure value function whose
2598    /// inputs flow through `Call.args` (not ambient register state), so the args
2599    /// are the source of truth — this replaces the register-file seeding
2600    /// [`seed_entry_params`](Self::seed_entry_params) does for conventional
2601    /// callees. The arg/param alignment invariant (`arg[i] ↔ param[i]`, built in
2602    /// lockstep by argpromote and preserved by mem2reg + `remove_entry_param`)
2603    /// makes the positional binding sound. Every arg is read before any param is
2604    /// written, so a self-recursive call still sees the caller's values.
2605    fn bind_entry_params_from_args(
2606        &mut self,
2607        ctx: &Context<'_>,
2608        call_id: InstructionId,
2609        target: FunctionId,
2610    ) {
2611        let args = match ctx.get_insn(call_id).mnemonic() {
2612            Mnemonic::Call(call) => call.args.clone(),
2613            _ => return,
2614        };
2615        let Some(root) = FunctionBody::from_id(ctx, target).root() else {
2616            return;
2617        };
2618        let params: Vec<(BlockParamId, usize)> = BasicBlock::from_id(ctx, root.id)
2619            .params()
2620            .map(|p| (p.id, p.size()))
2621            .collect();
2622        if args.len() != params.len() {
2623            // The alignment invariant is violated; fall back to register seeding
2624            // rather than mis-bind by position.
2625            self.seed_entry_params(ctx, target);
2626            return;
2627        }
2628        // Evaluate every arg in the caller frame first (recursion-safe), then bind.
2629        let values: Vec<SizedValue> = args
2630            .iter()
2631            .zip(&params)
2632            .map(|(&arg, &(_, size))| {
2633                let raw = self.get_value(ctx, arg.qualify(call_id.func)).unwrap_or(0);
2634                SizedValue::new(raw, size)
2635            })
2636            .collect();
2637        for ((param_id, _), value) in params.into_iter().zip(values) {
2638            self.block_param_values.insert(param_id, value);
2639        }
2640    }
2641
2642    pub fn run_function(&mut self, ctx: &Context<'_>, func: FunctionId) -> crate::Result<()> {
2643        let root = FunctionBody::from_id(ctx, func)
2644            .root()
2645            .ok_or_else(|| self.make_error(ctx, EmulatorErrorKind::EmptyFunctionRoot(func)))?
2646            .id;
2647        self.block = root;
2648        self.idx = 0;
2649        self.call_stack.push(func);
2650        self.seed_entry_params(ctx, func);
2651
2652        let mut call_depth: i32 = 0;
2653
2654        let result = loop {
2655            let insn_ids = BasicBlock::from_id(ctx, self.block)
2656                .instruction_ids()
2657                .to_vec();
2658            let insn = InstructionRef::from_id(ctx, insn_ids[self.idx]);
2659
2660            if matches!(insn.mnemonic(), Mnemonic::Return(_)) && call_depth == 0 {
2661                break Ok(());
2662            }
2663
2664            match self.step_with_event(ctx)? {
2665                StepEvent::DirectCallEntered(target) => {
2666                    call_depth += 1;
2667                    self.call_stack.push(target);
2668                    // Dual binding convention (argpromote v2): a `regpure`-tagged
2669                    // call site passes its inputs explicitly through `Call.args`
2670                    // (bound positionally); an `Opaque` (implicit) call — and a
2671                    // conventional callee — reads them from the register file the
2672                    // calling convention set up. The legacy `pure_reg` flag is
2673                    // still honored during the migration.
2674                    let regpure_site = self
2675                        .call_site_stack
2676                        .last()
2677                        .copied()
2678                        .is_some_and(|call_id| call_is_regpure(ctx, call_id));
2679                    if regpure_site || FunctionBody::from_id(ctx, target).is_reg_materialized() {
2680                        if let Some(&call_id) = self.call_site_stack.last() {
2681                            self.bind_entry_params_from_args(ctx, call_id, target);
2682                        }
2683                    } else {
2684                        self.seed_entry_params(ctx, target);
2685                    }
2686                }
2687                StepEvent::IndirectCallEntered => {
2688                    call_depth += 1;
2689                    // Infer the callee from the block we landed in.
2690                    if let Some(parent) = BasicBlock::from_id(ctx, self.block).parent() {
2691                        let callee = parent.id;
2692                        self.call_stack.push(callee);
2693                        self.seed_entry_params(ctx, callee);
2694                    }
2695                }
2696                StepEvent::Return | StepEvent::ReturnValue => {
2697                    self.call_stack.pop();
2698                    call_depth -= 1;
2699                }
2700                StepEvent::Normal | StepEvent::InterceptedCall => {}
2701            }
2702        };
2703
2704        self.call_stack.pop(); // pop the outermost function
2705        result
2706    }
2707
2708    /// Emulate a **pure** function in isolation: bind its root params positionally
2709    /// from `args` (so symbolic caller inputs can be passed an arbitrary poison
2710    /// value) and run to the first top-level `Return` *without executing it*,
2711    /// leaving the body's computed values readable via [`get_value`](Self::get_value)
2712    /// at the block returned by [`current_block`](Self::current_block).
2713    ///
2714    /// `args` must align with the root params index-for-index (the `pure_reg`
2715    /// call interface). The run is bounded by `max_steps`; exceeding it yields
2716    /// [`EmulatorErrorKind::StepBudgetExceeded`]. Intended for v1 **leaf** pure
2717    /// functions (no nested calls), so call bookkeeping is intentionally minimal.
2718    pub fn run_pure(
2719        &mut self,
2720        ctx: &Context<'_>,
2721        func: FunctionId,
2722        args: &[SizedValue],
2723        max_steps: usize,
2724    ) -> crate::Result<()> {
2725        let opts: Vec<Option<SizedValue>> = args.iter().map(|&v| Some(v)).collect();
2726        self.run_pure_partial(ctx, func, &opts, max_steps)
2727    }
2728
2729    /// Like [`run_pure`](Self::run_pure), but each positional argument may be
2730    /// [`None`] to bind that root param to **poison** (a symbolic value with
2731    /// undefined bits). Reading a poison param during emulation is a hard error
2732    /// (`PoisonRead`), so a consumer such as pure-call folding bails when the
2733    /// result actually depends on a symbolic argument, rather than computing on a
2734    /// bogus concrete value (argpromote v2, `ARGPROMOTE_REGISTERS_V2.md`).
2735    pub fn run_pure_partial(
2736        &mut self,
2737        ctx: &Context<'_>,
2738        func: FunctionId,
2739        args: &[Option<SizedValue>],
2740        max_steps: usize,
2741    ) -> crate::Result<()> {
2742        let root = FunctionBody::from_id(ctx, func)
2743            .root()
2744            .ok_or_else(|| self.make_error(ctx, EmulatorErrorKind::EmptyFunctionRoot(func)))?
2745            .id;
2746        self.block = root;
2747        self.idx = 0;
2748        self.call_stack.push(func);
2749
2750        // Bind root params positionally from `args`: a concrete `Some(v)` seeds
2751        // the param value, a `None` marks it poison (read ⇒ hard error).
2752        let param_ids: Vec<BlockParamId> = BasicBlock::from_id(ctx, root)
2753            .params()
2754            .map(|p| p.id)
2755            .collect();
2756        for (param_id, arg) in param_ids.into_iter().zip(args) {
2757            match arg {
2758                Some(value) => {
2759                    self.block_param_values.insert(param_id, *value);
2760                }
2761                None => {
2762                    self.poison_params.insert(param_id);
2763                }
2764            }
2765        }
2766
2767        self.drive_to_return(ctx, root, func, max_steps)
2768    }
2769
2770    /// Like [`run_pure`](Self::run_pure), but for a `map` body — its leading
2771    /// element param may be an **aggregate** (the `enumerate` `(index, elem)`
2772    /// lane), seeded so the body's `Extract`s on it resolve. `args` align with
2773    /// the root params index-for-index: a [`BodyArg::Scalar`] seeds a scalar
2774    /// param, a [`BodyArg::Aggregate`] seeds an `Extract`-able tuple param.
2775    /// Whether `id` is an array/list-typed value (routed through
2776    /// [`array_values`](Self::array_values) rather than scalar `insn_values`).
2777    /// Refreshes [`sequence_types`](Self::sequence_types) when the module has
2778    /// gained types since it was last answered. The probe is lock-free; only a
2779    /// genuine change pays for the locked question behind it.
2780    fn refresh_sequence_types(&mut self, ctx: &Context<'_>) {
2781        let published = ctx.shared.types.published_len();
2782        if self.sequence_types_checked_at == Some(published) {
2783            return;
2784        }
2785        self.sequence_types = ctx.shared.types.has_sequence_types();
2786        self.sequence_types_checked_at = Some(published);
2787    }
2788
2789    fn is_array_operand(&self, ctx: &Context<'_>, id: ValueId) -> bool {
2790        // Nothing in this module is sequence-typed, so no operand can be.
2791        if !self.sequence_types {
2792            return false;
2793        }
2794        match ctx.stored_type_of(id) {
2795            Some(ty) => {
2796                ctx.shared.types.array_of(ty).is_some() || ctx.shared.types.list_of(ty).is_some()
2797            }
2798            None => false,
2799        }
2800    }
2801
2802    /// Resolve an array-typed operand to its little-endian byte buffer: a `Bytes`
2803    /// blob's data, a previously-computed `array_values` entry, or a short array
2804    /// materialized as a scalar literal/result.
2805    fn resolve_array(&mut self, ctx: &Context<'_>, id: ValueId) -> Option<Vec<u8>> {
2806        match id {
2807            ValueId::Bytes(b) => Some(ctx.shared.values.bytes[b].data.clone()),
2808            ValueId::Instruction(i) => self
2809                .array_values
2810                .get(&i)
2811                .cloned()
2812                .or_else(|| self.get_value_bytes(ctx, id)),
2813            ValueId::Literal(_) => self.get_value_bytes(ctx, id),
2814            // A root/block param bound by `run_pure` (e.g. an argpromote-minted
2815            // `[i8;N]` array param): its little-endian bytes come from the bound
2816            // `SizedValue`. Defensive width check — a short buffer must fail
2817            // resolution rather than silently produce clamped `Range` slices for
2818            // callers that lack the projection guarantee.
2819            ValueId::BlockParam(_) => {
2820                let bytes = self.get_value_bytes(ctx, id)?;
2821                let ty_size = ctx
2822                    .stored_type_of(id)
2823                    .map(|ty| ctx.shared.types.size_of(ty))?;
2824                (bytes.len() == ty_size).then_some(bytes)
2825            }
2826            _ => None,
2827        }
2828    }
2829
2830    /// Evaluate one array-valued (or lane-reading) sequence intrinsic, depositing
2831    /// its result in `array_values` (arrays) or `insn_values` (`at`).
2832    fn eval_array_intrinsic(
2833        &mut self,
2834        ctx: &Context<'_>,
2835        insn_id: InstructionId,
2836        name: &str,
2837        args: &[ValueId],
2838    ) -> Result<(), EmulatorErrorKind> {
2839        match name {
2840            "iota" => {
2841                let n = self
2842                    .get_value(ctx, args[0])
2843                    .ok_or(EmulatorErrorKind::ValueError(0))?;
2844                let mut buf = Vec::with_capacity(n as usize * 8);
2845                for i in 0..n {
2846                    buf.extend_from_slice(&i.to_le_bytes());
2847                }
2848                self.array_values.insert(insn_id, buf);
2849            }
2850            "singleton" => {
2851                let buf = self
2852                    .get_value_bytes(ctx, args[0])
2853                    .ok_or(EmulatorErrorKind::ValueError(0))?;
2854                self.array_values.insert(insn_id, buf);
2855            }
2856            "concat" => {
2857                let mut a = self
2858                    .resolve_array(ctx, args[0])
2859                    .ok_or(EmulatorErrorKind::ValueError(0))?;
2860                let b = self
2861                    .resolve_array(ctx, args[1])
2862                    .ok_or(EmulatorErrorKind::ValueError(0))?;
2863                a.extend_from_slice(&b);
2864                self.array_values.insert(insn_id, a);
2865            }
2866            "insert" => {
2867                let mut buf = self
2868                    .resolve_array(ctx, args[0])
2869                    .ok_or(EmulatorErrorKind::ValueError(0))?;
2870                let i = self
2871                    .get_value(ctx, args[1])
2872                    .ok_or(EmulatorErrorKind::ValueError(0))? as usize;
2873                let vbytes = self
2874                    .get_value_bytes(ctx, args[2])
2875                    .ok_or(EmulatorErrorKind::ValueError(0))?;
2876                let esz = vbytes.len();
2877                let off = i * esz;
2878                if off + esz <= buf.len() {
2879                    buf[off..off + esz].copy_from_slice(&vbytes);
2880                }
2881                self.array_values.insert(insn_id, buf);
2882            }
2883            "enumerate" => {
2884                // `enumerate(arr) = [(index: i64, elem: T); N]`, materialized only
2885                // over a fixed array (or bounded list). A length-erased unbounded
2886                // list has no concrete count, so bail recoverably rather than
2887                // fabricate one — matching `enumerate`'s deferred `eval`.
2888                let src_ty = ctx
2889                    .stored_type_of(args[0])
2890                    .ok_or(EmulatorErrorKind::ValueError(0))?;
2891                if matches!(ctx.shared.types.list_of(src_ty), Some((_, None))) {
2892                    return Err(EmulatorErrorKind::UnsupportedIntrinsic(Box::from(
2893                        "enumerate",
2894                    )));
2895                }
2896                let in_elem = ctx
2897                    .shared
2898                    .types
2899                    .seq_elem_of(src_ty)
2900                    .ok_or(EmulatorErrorKind::ValueError(0))?;
2901                let isz = ctx.shared.types.size_of(in_elem).max(1);
2902                let buf = self
2903                    .resolve_array(ctx, args[0])
2904                    .ok_or(EmulatorErrorKind::ValueError(0))?;
2905                // The `(index, elem)` result tuple is a *structural* aggregate:
2906                // its fields are addressed by index, not byte offset, so lay them
2907                // out sequentially by field size (field 0 = i64 index, field 1 =
2908                // elem). This is the same layout `eval_scan` splits back out.
2909                let tuple_ty = ctx
2910                    .stored_type_of(ValueId::Instruction(insn_id))
2911                    .and_then(|ty| ctx.shared.types.seq_elem_of(ty))
2912                    .ok_or(EmulatorErrorKind::ValueError(0))?;
2913                let (idx_sz, elem_off) = {
2914                    let fields = ctx
2915                        .shared
2916                        .types
2917                        .aggregate_fields(tuple_ty)
2918                        .ok_or(EmulatorErrorKind::ValueError(0))?;
2919                    let [idx_f, _elem_f] = fields else {
2920                        return Err(EmulatorErrorKind::ValueError(0));
2921                    };
2922                    let idx_sz = ctx.shared.types.size_of(idx_f.type_id).min(8);
2923                    (idx_sz, idx_sz)
2924                };
2925                let tsz = idx_sz + isz;
2926                let count = buf.len() / isz;
2927                let mut out = vec![0u8; count * tsz];
2928                for i in 0..count {
2929                    let base = i * tsz;
2930                    let idx_bytes = (i as u64).to_le_bytes();
2931                    out[base..base + idx_sz].copy_from_slice(&idx_bytes[..idx_sz]);
2932                    out[base + elem_off..base + elem_off + isz]
2933                        .copy_from_slice(&buf[i * isz..i * isz + isz]);
2934                }
2935                self.array_values.insert(insn_id, out);
2936            }
2937            "at" => {
2938                let buf = self
2939                    .resolve_array(ctx, args[0])
2940                    .ok_or(EmulatorErrorKind::ValueError(0))?;
2941                let i = self
2942                    .get_value(ctx, args[1])
2943                    .ok_or(EmulatorErrorKind::ValueError(0))? as usize;
2944                let esz = ctx
2945                    .stored_type_of(ValueId::Instruction(insn_id))
2946                    .map(|ty| ctx.shared.types.size_of(ty))
2947                    .unwrap_or(8);
2948                let off = i * esz;
2949                let lane = buf
2950                    .get(off..off + esz)
2951                    .ok_or(EmulatorErrorKind::ValueError(0))?;
2952                self.insn_values
2953                    .insert(insn_id, SizedValue::from_bits(le_bits(lane), esz));
2954            }
2955            other => panic!("eval_array_intrinsic called on non-array intrinsic `{other}`"),
2956        }
2957        Ok(())
2958    }
2959
2960    /// Thread a scan's accumulator across every lane of its source array, running
2961    /// the pure binary body `(acc, elem) -> acc'` once per element in a fresh
2962    /// nested emulator, and store the concatenated per-step accumulators as the
2963    /// result array buffer.
2964    fn eval_scan(
2965        &mut self,
2966        ctx: &Context<'_>,
2967        insn_id: InstructionId,
2968        scan: &Scan,
2969    ) -> Result<(), EmulatorErrorKind> {
2970        const SCAN_STEP_BUDGET: usize = 100_000;
2971
2972        let src = self
2973            .resolve_array(ctx, scan.src.qualify(insn_id.func))
2974            .ok_or(EmulatorErrorKind::ValueError(0))?;
2975        // Element sizes come from the operand/result element *types* (which are
2976        // known even for a length-erased `[T;*]` result); the lane count is the
2977        // source buffer's length in input elements. This handles both a folded
2978        // fixed-array source and a symbolic-length `iota`.
2979        let in_elem = ctx
2980            .stored_type_of(scan.src.qualify(insn_id.func))
2981            .and_then(|ty| ctx.shared.types.seq_elem_of(ty))
2982            .ok_or(EmulatorErrorKind::ValueError(0))?;
2983        let isz = ctx.shared.types.size_of(in_elem).max(1);
2984        let out_elem = ctx
2985            .stored_type_of(ValueId::Instruction(insn_id))
2986            .and_then(|ty| ctx.shared.types.seq_elem_of(ty))
2987            .ok_or(EmulatorErrorKind::ValueError(0))?;
2988        let osz = ctx.shared.types.size_of(out_elem);
2989        let count = src.len() / isz;
2990        let body = require_real_callee(scan.body)?;
2991        if count == 0 {
2992            self.array_values.insert(insn_id, Vec::new());
2993            return Ok(());
2994        }
2995
2996        // Loop-invariant captures, resolved once as scalars.
2997        let capture_args: Vec<BodyArg> = scan
2998            .captures
2999            .iter()
3000            .map(|&c| {
3001                let v = self
3002                    .get_value(ctx, c.qualify(insn_id.func))
3003                    .ok_or(EmulatorErrorKind::ValueError(0))?;
3004                let sz = ctx
3005                    .stored_type_of(c.qualify(insn_id.func))
3006                    .map(|ty| ctx.shared.types.size_of(ty))
3007                    .unwrap_or(8);
3008                Ok(BodyArg::Scalar(SizedValue::new(v, sz)))
3009            })
3010            .collect::<Result<_, EmulatorErrorKind>>()?;
3011
3012        let init = self
3013            .get_value(ctx, scan.init.qualify(insn_id.func))
3014            .ok_or(EmulatorErrorKind::ValueError(0))?;
3015        let mut acc = SizedValue::new(init, osz);
3016
3017        let root = FunctionBody::from_id(ctx, body)
3018            .root()
3019            .ok_or(EmulatorErrorKind::EmptyFunctionRoot(body))?
3020            .id;
3021
3022        // When the source element is a tuple (the `enumerate` `(index, elem)`
3023        // lane), each lane is passed as an aggregate so the body's `Extract`s
3024        // resolve; a plain scalar element is passed as-is. The tuple is a
3025        // structural aggregate (fields addressed by index), so its bytes are laid
3026        // out sequentially by field size — the same layout `enumerate` writes.
3027        let elem_fields: Option<Vec<(usize, usize)>> =
3028            ctx.shared.types.aggregate_fields(in_elem).map(|fs| {
3029                let mut off = 0;
3030                fs.iter()
3031                    .map(|f| {
3032                        let sz = ctx.shared.types.size_of(f.type_id);
3033                        let field = (off, sz);
3034                        off += sz;
3035                        field
3036                    })
3037                    .collect()
3038            });
3039
3040        let mut out = Vec::with_capacity(count * osz);
3041        for k in 0..count {
3042            let elem = &src[k * isz..k * isz + isz];
3043            let elem_arg = match &elem_fields {
3044                Some(fields) => BodyArg::Aggregate(
3045                    fields
3046                        .iter()
3047                        .map(|&(off, sz)| SizedValue::from_bits(le_bits(&elem[off..off + sz]), sz))
3048                        .collect(),
3049                ),
3050                None => BodyArg::Scalar(SizedValue::from_bits(le_bits(elem), isz)),
3051            };
3052            let mut body_args = Vec::with_capacity(2 + capture_args.len());
3053            body_args.push(BodyArg::Scalar(acc));
3054            body_args.push(elem_arg);
3055            body_args.extend(capture_args.iter().cloned());
3056
3057            let mut emu = StandaloneEmulator::new(root);
3058            emu.run_map_body(ctx, body, &body_args, SCAN_STEP_BUDGET)
3059                .map_err(|e| e.kind)?;
3060            let ret = body_return_value(ctx, emu.current_block())
3061                .ok_or(EmulatorErrorKind::ValueError(0))?;
3062            let mut lane = emu
3063                .get_value_bytes(ctx, ret)
3064                .ok_or(EmulatorErrorKind::ValueError(0))?;
3065            lane.resize(osz, 0);
3066            acc = SizedValue::from_bits(le_bits(&lane), osz);
3067            out.extend_from_slice(&lane);
3068        }
3069        self.array_values.insert(insn_id, out);
3070        Ok(())
3071    }
3072
3073    /// Total map: run the (pure) unary body once per source element and
3074    /// materialize the results as an array buffer. Mirrors [`Self::eval_scan`]
3075    /// without the threaded accumulator.
3076    fn eval_map(
3077        &mut self,
3078        ctx: &Context<'_>,
3079        insn_id: InstructionId,
3080        map: &qcode::value::insn::Map,
3081    ) -> Result<(), EmulatorErrorKind> {
3082        const MAP_STEP_BUDGET: usize = 100_000;
3083
3084        let src = self
3085            .resolve_array(ctx, map.src.qualify(insn_id.func))
3086            .ok_or(EmulatorErrorKind::ValueError(0))?;
3087        let in_elem = ctx
3088            .stored_type_of(map.src.qualify(insn_id.func))
3089            .and_then(|ty| ctx.shared.types.seq_elem_of(ty))
3090            .ok_or(EmulatorErrorKind::ValueError(0))?;
3091        let isz = ctx.shared.types.size_of(in_elem).max(1);
3092        let out_elem = ctx
3093            .stored_type_of(ValueId::Instruction(insn_id))
3094            .and_then(|ty| ctx.shared.types.seq_elem_of(ty))
3095            .ok_or(EmulatorErrorKind::ValueError(0))?;
3096        let osz = ctx.shared.types.size_of(out_elem);
3097        let count = src.len() / isz;
3098        let body = require_real_callee(map.body)?;
3099
3100        let capture_args: Vec<BodyArg> = map
3101            .captures
3102            .iter()
3103            .map(|&c| {
3104                let v = self
3105                    .get_value(ctx, c.qualify(insn_id.func))
3106                    .ok_or(EmulatorErrorKind::ValueError(0))?;
3107                let sz = ctx
3108                    .stored_type_of(c.qualify(insn_id.func))
3109                    .map(|ty| ctx.shared.types.size_of(ty))
3110                    .unwrap_or(8);
3111                Ok(BodyArg::Scalar(SizedValue::new(v, sz)))
3112            })
3113            .collect::<Result<_, EmulatorErrorKind>>()?;
3114
3115        // The element may be an `enumerate` tuple `(index, elem)`; pass it as an
3116        // aggregate so the body's `Extract`s resolve (same layout as `eval_scan`).
3117        let elem_fields: Option<Vec<(usize, usize)>> =
3118            ctx.shared.types.aggregate_fields(in_elem).map(|fs| {
3119                let mut off = 0;
3120                fs.iter()
3121                    .map(|f| {
3122                        let sz = ctx.shared.types.size_of(f.type_id);
3123                        let field = (off, sz);
3124                        off += sz;
3125                        field
3126                    })
3127                    .collect()
3128            });
3129
3130        let mut out = Vec::with_capacity(count * osz);
3131        for k in 0..count {
3132            let elem = &src[k * isz..k * isz + isz];
3133            let elem_arg = match &elem_fields {
3134                Some(fields) => BodyArg::Aggregate(
3135                    fields
3136                        .iter()
3137                        .map(|&(off, sz)| SizedValue::from_bits(le_bits(&elem[off..off + sz]), sz))
3138                        .collect(),
3139                ),
3140                None => BodyArg::Scalar(SizedValue::from_bits(le_bits(elem), isz)),
3141            };
3142            let mut body_args = Vec::with_capacity(1 + capture_args.len());
3143            body_args.push(elem_arg);
3144            body_args.extend(capture_args.iter().cloned());
3145
3146            let mut emu = StandaloneEmulator::new(
3147                FunctionBody::from_id(ctx, body)
3148                    .root()
3149                    .ok_or(EmulatorErrorKind::EmptyFunctionRoot(body))?
3150                    .id,
3151            );
3152            emu.run_map_body(ctx, body, &body_args, MAP_STEP_BUDGET)
3153                .map_err(|e| e.kind)?;
3154            let ret = body_return_value(ctx, emu.current_block())
3155                .ok_or(EmulatorErrorKind::ValueError(0))?;
3156            let mut lane = emu
3157                .get_value_bytes(ctx, ret)
3158                .ok_or(EmulatorErrorKind::ValueError(0))?;
3159            lane.resize(osz, 0);
3160            out.extend_from_slice(&lane);
3161        }
3162        self.array_values.insert(insn_id, out);
3163        Ok(())
3164    }
3165
3166    pub fn run_map_body(
3167        &mut self,
3168        ctx: &Context<'_>,
3169        func: FunctionId,
3170        args: &[BodyArg],
3171        max_steps: usize,
3172    ) -> crate::Result<()> {
3173        let root = FunctionBody::from_id(ctx, func)
3174            .root()
3175            .ok_or_else(|| self.make_error(ctx, EmulatorErrorKind::EmptyFunctionRoot(func)))?
3176            .id;
3177        self.block = root;
3178        self.idx = 0;
3179        self.call_stack.push(func);
3180
3181        let param_ids: Vec<BlockParamId> = BasicBlock::from_id(ctx, root)
3182            .params()
3183            .map(|p| p.id)
3184            .collect();
3185        for (param_id, arg) in param_ids.into_iter().zip(args) {
3186            match arg {
3187                BodyArg::Scalar(v) => {
3188                    self.block_param_values.insert(param_id, *v);
3189                }
3190                BodyArg::Aggregate(fields) => {
3191                    self.block_param_aggregates.insert(param_id, fields.clone());
3192                }
3193            }
3194        }
3195
3196        self.drive_to_return(ctx, root, func, max_steps)
3197    }
3198
3199    /// Shared drive loop for the bounded `run_pure`/`run_map_body` entry points:
3200    /// run from the current position to the first top-level value or machine
3201    /// `Return` (without
3202    /// executing it), popping the call frame. Params must already be seeded and
3203    /// `func` pushed onto the call stack.
3204    fn drive_to_return(
3205        &mut self,
3206        ctx: &Context<'_>,
3207        _root: BlockId,
3208        _func: FunctionId,
3209        max_steps: usize,
3210    ) -> crate::Result<()> {
3211        let mut steps = 0usize;
3212        let result = loop {
3213            let insn_ids = BasicBlock::from_id(ctx, self.block)
3214                .instruction_ids()
3215                .to_vec();
3216            // A well-formed block ends in a terminator, so `self.idx` should always
3217            // point at a real instruction. Lifting can leave degenerate empty blocks
3218            // behind, though; bail with a recoverable error instead of indexing out
3219            // of bounds (which would crash the whole analysis via GVN pure-call
3220            // folding). `make_error` can't be used here — it also indexes the block.
3221            if self.idx >= insn_ids.len() {
3222                break Err(self.make_empty_block_error(ctx));
3223            }
3224            let insn = InstructionRef::from_id(ctx, insn_ids[self.idx]);
3225            if matches!(
3226                insn.mnemonic(),
3227                Mnemonic::Return(_) | Mnemonic::ReturnValue(_)
3228            ) {
3229                break Ok(());
3230            }
3231            steps += 1;
3232            if steps > max_steps {
3233                break Err(self.make_error(ctx, EmulatorErrorKind::StepBudgetExceeded(max_steps)));
3234            }
3235            if let Err(e) = self.step(ctx) {
3236                break Err(e);
3237            }
3238        };
3239
3240        self.call_stack.pop();
3241        result
3242    }
3243}
3244
3245fn lambda_return_value(ctx: &Context<'_>, block: BlockId) -> Option<ValueId> {
3246    let last = BasicBlock::from_id(ctx, block).iter().last()?;
3247    match last.mnemonic() {
3248        Mnemonic::ReturnValue(ret) => Some(ret.value.qualify(last.id.func)),
3249        _ => None,
3250    }
3251}
3252
3253/// The value a scan/map body block returns — via either a machine `Return` (the
3254/// outlined-body form) or a lambda `ReturnValue`. `None` if the block does not
3255/// end in a value-carrying return.
3256fn body_return_value(ctx: &Context<'_>, block: BlockId) -> Option<ValueId> {
3257    let last = BasicBlock::from_id(ctx, block).iter().last()?;
3258    match last.mnemonic() {
3259        Mnemonic::Return(ret) => ret.value.map(|v| v.qualify(last.id.func)),
3260        Mnemonic::ReturnValue(ret) => Some(ret.value.qualify(last.id.func)),
3261        _ => None,
3262    }
3263}
3264
3265/// The array-valued (or lane-reading) sequence intrinsics the emulator evaluates
3266/// over its [`array_values`](StandaloneEmulator::array_values) domain rather than
3267/// the scalar interpreter.
3268fn is_array_intrinsic(name: &str) -> bool {
3269    matches!(
3270        name,
3271        "iota" | "singleton" | "concat" | "insert" | "at" | "enumerate"
3272    )
3273}
3274
3275/// Fold a little-endian byte slice (≤ 16 bytes) into a `u128`.
3276fn le_bits(bytes: &[u8]) -> u128 {
3277    let mut buf = [0u8; 16];
3278    let n = bytes.len().min(16);
3279    buf[..n].copy_from_slice(&bytes[..n]);
3280    u128::from_le_bytes(buf)
3281}
3282
3283/// A positional argument to a `map` body for [`run_map_body`](StandaloneEmulator::run_map_body):
3284/// a scalar param value, or the field vector of an aggregate (tuple) param.
3285#[derive(Debug, Clone)]
3286pub enum BodyArg {
3287    Scalar(SizedValue),
3288    Aggregate(Vec<SizedValue>),
3289}
3290
3291/// Private helper that pairs `&mut StandaloneEmulator` fields with `&Context<'_>`
3292/// so the default `Interpreter::interpret()` impl can be reused.
3293struct TempInterpreter<'a, 'ctx, M> {
3294    memory: &'a mut M,
3295    literals: &'a mut LiteralCache,
3296    insn_values: &'a mut InsnValues,
3297    block_param_values: &'a mut FxHashMap<BlockParamId, SizedValue>,
3298    poison_params: &'a FxHashSet<BlockParamId>,
3299    ctx: &'ctx Context<'ctx>,
3300}
3301
3302impl<'ctx, M: EmulatorMemory> Interpreter for TempInterpreter<'_, 'ctx, M> {
3303    type V = SizedValue;
3304    type M = M;
3305
3306    fn memory(&mut self) -> &mut Self::M {
3307        self.memory
3308    }
3309
3310    fn ctx(&self) -> &Context<'_> {
3311        self.ctx
3312    }
3313
3314    fn get_value(&mut self, id: ValueId) -> Result<Self::V, EmulatorErrorKind> {
3315        // Taken before `ValueRef::new`, which would resolve the literal through
3316        // the interner's lock.
3317        if let ValueId::Literal(literal) = id {
3318            return Ok(self.literals.get(self.ctx, literal));
3319        }
3320        match ValueRef::new(id, self.ctx) {
3321            ValueRef::Literal(literal) => Ok(SizedValue::new(literal.value(), literal.size())),
3322            // Byte blobs are wider than the emulator's scalar SizedValue.
3323            ValueRef::Bytes(_) => Err(EmulatorErrorKind::ValueError(0)),
3324            ValueRef::Instruction(insn) => self
3325                .insn_values
3326                .get(&insn.id)
3327                .copied()
3328                .ok_or(EmulatorErrorKind::ValueError(0)),
3329            ValueRef::Varnode(varnode) => Ok(SizedValue::new(varnode.address() as u64, 8)),
3330            ValueRef::Temp(temp) => Ok(SizedValue::new(temp.address() as u64, 8)),
3331            ValueRef::BasicBlock(_) => panic!("Cannot get value of a block"),
3332            ValueRef::BlockParam(param) => {
3333                if self.poison_params.contains(&param.id) {
3334                    return Err(EmulatorErrorKind::PoisonRead);
3335                }
3336                self.block_param_values
3337                    .get(&param.id)
3338                    .copied()
3339                    .ok_or(EmulatorErrorKind::ValueError(0))
3340            }
3341            ValueRef::Function(f) => f
3342                .address()
3343                .map(SizedValue::from_u64)
3344                .ok_or(EmulatorErrorKind::EmptyFunctionRoot(f.id)),
3345            // Poison has undefined bits: demanding its concrete value is a hard
3346            // error (propagating it as an unread operand never reaches here).
3347            ValueRef::Poison(_) => Err(EmulatorErrorKind::PoisonRead),
3348        }
3349    }
3350}
3351
3352pub struct Emulator<'ctx, M = EmulatedMemory> {
3353    inner: StandaloneEmulator<M>,
3354    ctx: &'ctx Context<'ctx>,
3355}
3356
3357impl<'ctx> Emulator<'ctx, EmulatedMemory> {
3358    /// Builds an emulator over the default flat memory.
3359    pub fn new(ctx: &'ctx Context<'ctx>, entry: BlockId) -> Self {
3360        Self::new_in(ctx, entry)
3361    }
3362
3363    pub fn from_function(ctx: &'ctx Context<'ctx>, func: FunctionId) -> Self {
3364        Self::from_function_in(ctx, func)
3365    }
3366
3367    pub fn from_block(ctx: &'ctx Context<'ctx>, block: BlockId) -> Self {
3368        Self::new_in(ctx, block)
3369    }
3370
3371    pub fn from_address(ctx: &'ctx Context<'ctx>, addr: u64) -> Self {
3372        Self::from_address_in(ctx, addr)
3373    }
3374}
3375
3376impl<'ctx, M: EmulatorMemory + Default> Emulator<'ctx, M> {
3377    /// Builds an emulator over an explicit memory backend.
3378    pub fn new_in(ctx: &'ctx Context<'ctx>, entry: BlockId) -> Self {
3379        let mut inner =
3380            StandaloneEmulator::<M>::with_address_index(entry, AddressIndex::analyze(ctx));
3381        inner.memory.configure_spaces(ctx);
3382        Self { inner, ctx }
3383    }
3384
3385    pub fn set_instruction_hook(
3386        &mut self,
3387        hook: impl Fn(&InstructionRef<'_, '_>, &StandaloneEmulator<M>) + Send + Sync + 'static,
3388    ) {
3389        self.inner.instruction_hook = Some(Box::new(hook));
3390    }
3391
3392    pub fn set_call_interceptor(
3393        &mut self,
3394        interceptor: impl FnMut(
3395            &Context<'_>,
3396            &mut StandaloneEmulator<M>,
3397            &CallSite,
3398        ) -> Result<CallInterception, Box<str>>
3399        + Send
3400        + Sync
3401        + 'static,
3402    ) {
3403        self.inner.set_call_interceptor(interceptor);
3404    }
3405
3406    pub fn clear_call_interceptor(&mut self) {
3407        self.inner.clear_call_interceptor();
3408    }
3409
3410    /// Builds an emulator at a function's root block, over an explicit backend.
3411    pub fn from_function_in(ctx: &'ctx Context<'ctx>, func: FunctionId) -> Self {
3412        let entry = FunctionBody::from_id(ctx, func)
3413            .root()
3414            .expect("Cannot create emulator for function with empty root block")
3415            .id;
3416        Self::new_in(ctx, entry)
3417    }
3418
3419    /// Builds an emulator positioned at `addr`, over an explicit backend.
3420    pub fn from_address_in(ctx: &'ctx Context<'ctx>, addr: u64) -> Self {
3421        Self {
3422            inner: StandaloneEmulator::<M>::from_address_in(ctx, addr),
3423            ctx,
3424        }
3425    }
3426
3427    /// Debugging method to view a value at a given address
3428    pub fn inspect_memory(&mut self, space: SpaceId, addr: u64, size: usize) -> Option<Vec<u8>> {
3429        self.inner
3430            .memory
3431            .read_bytes(MemorySpaceId::Shared(space), addr, size)
3432            .ok()
3433    }
3434
3435    /// Sets the value of a varnode
3436    pub fn set_varnode(&mut self, id: VarnodeId, value: u64) -> Result<(), EmulatorErrorKind> {
3437        self.inner.set_varnode(self.ctx, id, value)
3438    }
3439
3440    /// Sets the value of a varnode using full 128-bit precision.
3441    pub fn set_varnode_u128(
3442        &mut self,
3443        id: VarnodeId,
3444        value: u128,
3445    ) -> Result<(), EmulatorErrorKind> {
3446        self.inner.set_varnode_u128(self.ctx, id, value)
3447    }
3448
3449    /// Sets the value of a register
3450    pub fn set_register(&mut self, id: RegisterId, value: u64) -> Result<(), EmulatorErrorKind> {
3451        let id = self.ctx.get_register(id).id;
3452        self.set_varnode(id, value)
3453    }
3454
3455    /// Writes a value to memory
3456    pub fn write_memory(
3457        &mut self,
3458        space: SpaceId,
3459        addr: u64,
3460        value: &[u8],
3461    ) -> Result<(), EmulatorErrorKind> {
3462        self.inner.write_memory(self.ctx, space, addr, value)
3463    }
3464
3465    pub fn read_memory(
3466        &mut self,
3467        space: SpaceId,
3468        addr: u64,
3469        size: usize,
3470    ) -> Result<Vec<u8>, EmulatorErrorKind> {
3471        self.inner.read_memory(self.ctx, space, addr, size)
3472    }
3473
3474    /// Sets the value of a register using full 128-bit precision.
3475    pub fn set_register_u128(
3476        &mut self,
3477        id: RegisterId,
3478        value: u128,
3479    ) -> Result<(), EmulatorErrorKind> {
3480        let id = self.ctx.get_register(id).id;
3481        self.set_varnode_u128(id, value)
3482    }
3483
3484    pub fn read_varnode(&mut self, id: VarnodeId) -> Option<u64> {
3485        self.inner.read_varnode(self.ctx, id)
3486    }
3487
3488    pub fn read_varnode_u128(&mut self, id: VarnodeId) -> Option<u128> {
3489        self.inner.read_varnode_u128(self.ctx, id)
3490    }
3491
3492    pub fn read_register(&mut self, id: RegisterId) -> Option<u64> {
3493        let id = self.ctx.get_register(id).id;
3494        self.read_varnode(id)
3495    }
3496
3497    pub fn read_register_u128(&mut self, id: RegisterId) -> Option<u128> {
3498        let id = self.ctx.get_register(id).id;
3499        self.read_varnode_u128(id)
3500    }
3501
3502    /// Writes a single 64-bit lane of a wide register.
3503    /// Lane `n` covers bytes `[n*8 .. n*8+8]` relative to the register's base address.
3504    pub fn set_register_lane(&mut self, id: RegisterId, lane: usize, value: u64) {
3505        let (space_id, base_addr) = {
3506            let vn = self.ctx.get_register(id);
3507            (vn.space().id, vn.address() as u64)
3508        };
3509        let base = base_addr + (lane as u64) * 8;
3510        let _ = self
3511            .inner
3512            .memory
3513            .write_bytes(space_id.into(), base, &value.to_le_bytes());
3514    }
3515
3516    /// Reads a single 64-bit lane of a wide register (little-endian).
3517    /// Lane `n` covers bytes `[n*8 .. n*8+8]` relative to the register's base address.
3518    pub fn read_register_lane(&mut self, id: RegisterId, lane: usize) -> u64 {
3519        let (space_id, base_addr) = {
3520            let vn = self.ctx.get_register(id);
3521            (vn.space().id, vn.address() as u64)
3522        };
3523        let base = base_addr + (lane as u64) * 8;
3524        // An unwritten lane reads as zero: register space is architectural
3525        // state that exists whether or not a harness has seeded it.
3526        let bytes = self
3527            .inner
3528            .memory
3529            .read_bytes(space_id.into(), base, 8)
3530            .unwrap_or_else(|_| vec![0; 8]);
3531        u64::from_le_bytes(bytes.try_into().expect("read_bytes returns 8 bytes"))
3532    }
3533
3534    /// Gets the current block
3535    pub fn block(&self) -> BlockRef<'ctx, 'ctx> {
3536        BasicBlock::from_id(self.ctx, self.inner.block)
3537    }
3538
3539    /// Gets the current instruction
3540    pub fn insn(&self) -> Option<InstructionRef<'ctx, 'ctx>> {
3541        let block = self.block();
3542        if self.inner.idx >= block.instruction_count() {
3543            None
3544        } else {
3545            let id = block.instruction_ids()[self.inner.idx];
3546            Some(InstructionRef::from_id(self.ctx, id))
3547        }
3548    }
3549
3550    /// Executes a single pcode instruction
3551    pub fn step(&mut self) -> crate::Result<()> {
3552        self.inner.step(self.ctx)
3553    }
3554
3555    /// Executes instructions until the end of the current block
3556    pub fn run_block(&mut self) -> crate::Result<()> {
3557        self.inner.run_block(self.ctx)
3558    }
3559
3560    /// Runs blocks until the current block starts at `addr`.
3561    pub fn run_until(&mut self, addr: u64) -> crate::Result<()> {
3562        self.inner.run_until(self.ctx, addr)
3563    }
3564
3565    /// Runs the given function from its root block, stopping before the outermost `Return`.
3566    pub fn run_function(&mut self, func: FunctionId) -> crate::Result<()> {
3567        self.inner.run_function(self.ctx, func)
3568    }
3569
3570    /// Returns the current emulator call stack (outermost function first).
3571    /// Only populated during `run_function` execution.
3572    pub fn call_stack(&self) -> &[FunctionId] {
3573        &self.inner.call_stack
3574    }
3575}
3576
3577impl<'ctx, M: EmulatorMemory> Interpreter for Emulator<'ctx, M> {
3578    type V = SizedValue;
3579    type M = M;
3580
3581    fn memory(&mut self) -> &mut Self::M {
3582        &mut self.inner.memory
3583    }
3584
3585    fn ctx(&self) -> &Context<'ctx> {
3586        self.ctx
3587    }
3588
3589    fn get_value(&mut self, id: ValueId) -> Result<Self::V, EmulatorErrorKind> {
3590        if let ValueId::Literal(literal) = id {
3591            let value = self.inner.literal_cache.get(self.ctx, literal);
3592            return Ok(value);
3593        }
3594        match ValueRef::new(id, self.ctx) {
3595            ValueRef::Literal(literal) => Ok(SizedValue::new(literal.value(), literal.size())),
3596            // Byte blobs are wider than the emulator's scalar SizedValue.
3597            ValueRef::Bytes(_) => Err(EmulatorErrorKind::ValueError(0)),
3598            ValueRef::Instruction(insn) => self
3599                .inner
3600                .insn_values
3601                .get(&insn.id)
3602                .copied()
3603                .ok_or(EmulatorErrorKind::ValueError(0)),
3604            ValueRef::Varnode(varnode) => Ok(SizedValue::new(varnode.address() as u64, 8)),
3605            ValueRef::Temp(temp) => Ok(SizedValue::new(temp.address() as u64, 8)),
3606            ValueRef::BasicBlock(_) => panic!("Cannot get value of a block"),
3607            ValueRef::BlockParam(param) => self
3608                .inner
3609                .block_param_values
3610                .get(&param.id)
3611                .copied()
3612                .ok_or(EmulatorErrorKind::ValueError(0)),
3613            ValueRef::Function(f) => f
3614                .address()
3615                .map(SizedValue::from_u64)
3616                .ok_or(EmulatorErrorKind::EmptyFunctionRoot(f.id)),
3617            // Poison has undefined bits: demanding its concrete value is a hard
3618            // error (propagating it as an unread operand never reaches here).
3619            ValueRef::Poison(_) => Err(EmulatorErrorKind::PoisonRead),
3620        }
3621    }
3622}
3623
3624#[cfg(test)]
3625mod tests {
3626    use super::*;
3627    use qcode::context::Context;
3628    use qcode::space::{Space, SpaceType};
3629    use qcode::value::QCodeMut;
3630    use qcode::value::TempSpace;
3631    use std::sync::{Arc, Mutex};
3632    use wazabin_qcode_macro::qcode;
3633
3634    /// Reading a poison value is a hard error (`PoisonRead`); propagating it as
3635    /// an unread operand never reaches `get_value`.
3636    #[test]
3637    fn reading_poison_is_a_hard_error() {
3638        let mut ctx = Context::new();
3639        let func = ctx.anon_function();
3640        let block = BasicBlock::make(&mut ctx, func).with_address(0x1000).id;
3641        let i32_ty = ctx.shared.types.get_or_make_int(4);
3642        let poison = ctx.get_poison(i32_ty);
3643        let mut emu = StandaloneEmulator::new(block);
3644        let mut tmp = TempInterpreter {
3645            memory: &mut emu.memory,
3646            literals: &mut emu.literal_cache,
3647            insn_values: &mut emu.insn_values,
3648            block_param_values: &mut emu.block_param_values,
3649            poison_params: &emu.poison_params,
3650            ctx: &ctx,
3651        };
3652        assert!(matches!(
3653            tmp.get_value(poison),
3654            Err(EmulatorErrorKind::PoisonRead)
3655        ));
3656    }
3657
3658    #[test]
3659    fn minted_callee_is_not_executable() {
3660        assert!(matches!(
3661            require_real_callee(Callee::Minted(7)),
3662            Err(EmulatorErrorKind::UnresolvedMintedCallee(7))
3663        ));
3664    }
3665
3666    #[test]
3667    fn sized_value_masks_to_declared_width() {
3668        let value = SizedValue::new(0x1234, 1);
3669        assert_eq!(value.size().unwrap(), 1);
3670        assert_eq!(value.value().unwrap(), 0x34);
3671    }
3672
3673    #[test]
3674    fn from_address_resolves_function_entry_to_root() {
3675        let mut ctx = Context::new();
3676        let function = FunctionBody::make_at_addr(&mut ctx, 0x1000, None).id;
3677        let root = BasicBlock::make(&mut ctx, function).with_address(0x1000).id;
3678
3679        let emulator = StandaloneEmulator::from_address(&ctx, 0x1000);
3680
3681        assert_eq!(emulator.current_block(), root);
3682        assert!(emulator.address_index.is_some());
3683    }
3684
3685    #[test]
3686    fn standalone_address_lookup_builds_one_lazy_snapshot() {
3687        let mut ctx = Context::new();
3688        let function = ctx.anon_function();
3689        let block = BasicBlock::make(&mut ctx, function).with_address(0x2000).id;
3690        ctx.block_mut(block).extra_addresses.push(0x2001);
3691        let mut emulator = StandaloneEmulator::new(block);
3692
3693        assert!(emulator.address_index.is_none());
3694        assert_eq!(emulator.block_at(&ctx, 0x2001), Some(block));
3695        assert!(emulator.address_index.is_some());
3696        assert_eq!(emulator.block_at(&ctx, 0x2000), Some(block));
3697    }
3698
3699    #[test]
3700    fn int_add_wraps_by_width_and_sets_carry() {
3701        let lhs = SizedValue::new(0xff, 1);
3702        let rhs = SizedValue::new(0x01, 1);
3703
3704        let sum = lhs.int_add(&rhs).unwrap();
3705        let carry = lhs.carry(&rhs).unwrap();
3706
3707        assert_eq!(sum.value().unwrap(), 0x00);
3708        assert_eq!(sum.size().unwrap(), 1);
3709        assert_eq!(carry.value().unwrap(), 1);
3710        assert_eq!(carry.size().unwrap(), 1);
3711    }
3712
3713    #[test]
3714    fn branch_args_bind_block_params() {
3715        let mut ctx = Context::new();
3716        qcode!(
3717            ctx,
3718            "
3719            <src>
3720                goto <dst @x=0x2>;
3721            <dst @x>
3722                %sum = i64 @x + 0x3;
3723                goto <0x1001>;
3724            "
3725        );
3726
3727        let mut emu = StandaloneEmulator::new(src);
3728        emu.step(&ctx).expect("branch binds block params");
3729        emu.step(&ctx).expect("destination uses block param");
3730
3731        assert_eq!(emu.get_value(&ctx, sum.into()), Some(5));
3732    }
3733
3734    #[test]
3735    fn apply_evaluates_recursive_lambda_value_return() {
3736        let mut ctx = Context::new();
3737        qcode!(
3738            ctx,
3739            "
3740            lambda dec:
3741            <entry @n:i64>
3742                %is_zero = @n == 0;
3743                if %is_zero goto <done @r=@n> else goto <step @m=@n>;
3744
3745            <step @m:i64>
3746                %next = @m - 1;
3747                %out = apply dec(%next);
3748                return %out;
3749
3750            <done @r:i64>
3751                return @r;
3752            "
3753        );
3754
3755        let dec = qcode::value::FunctionBody::from_name(&ctx, "dec")
3756            .expect("lambda exists")
3757            .id;
3758        let root = qcode::value::FunctionBody::from_id(&ctx, dec)
3759            .root()
3760            .expect("lambda has root")
3761            .id;
3762        let mut emu = StandaloneEmulator::new(root);
3763        emu.run_pure(&ctx, dec, &[SizedValue::new(3, 8)], 1000)
3764            .expect("recursive lambda evaluates");
3765        let ret = lambda_return_value(&ctx, emu.current_block()).expect("lambda returned a value");
3766        assert_eq!(emu.get_value(&ctx, ret), Some(0));
3767    }
3768
3769    /// Test setup is the publication barrier: `result_type` only reads, so the
3770    /// types an intrinsic resolves to must exist before it is pushed.
3771    fn publish_iota_result(ctx: &mut Context) {
3772        use qcode::types::TypeRequest;
3773        let i64_ty = ctx.shared.types.get_or_make_int(8);
3774        ctx.shared
3775            .types
3776            .create_requested_types(&[TypeRequest::list(i64_ty, None)]);
3777    }
3778
3779    /// `map @f arr` runs the pure unary body over every element and materializes
3780    /// the result buffer. `f(x) = x * 3`, `arr = [1, 2, 3, 4]` ⇒ `[3, 6, 9, 12]`.
3781    #[test]
3782    fn map_over_array_is_emulated() {
3783        let mut ctx = Context::new();
3784        publish_iota_result(&mut ctx);
3785        qcode!(
3786            ctx,
3787            "
3788            lambda triple:
3789            <tb @x:i64>
3790                %r = @x * 3;
3791                return %r;
3792            fn main:
3793            <me>
3794                %src = $iota(i64 0x4);
3795                %m = triple <$> %src;
3796                goto <0x1001>;
3797            "
3798        );
3799
3800        let mut emu = StandaloneEmulator::new(me);
3801        emu.step(&ctx).expect("iota");
3802        emu.step(&ctx).expect("map");
3803
3804        let buf = emu.array_values.get(&m).expect("map produced an array");
3805        let words: Vec<u64> = buf
3806            .as_chunks::<8>()
3807            .0
3808            .iter()
3809            .map(|&c| u64::from_le_bytes(c))
3810            .collect();
3811        // iota(4) = [0,1,2,3]; triple ⇒ [0, 3, 6, 9].
3812        assert_eq!(words, vec![0, 3, 6, 9]);
3813    }
3814
3815    /// End-to-end array emulation: `scanl @step init (iota n)` threads the
3816    /// accumulator through the driver array and materializes the result buffer.
3817    /// `step(acc, x) = acc + x`, `init = 10`, `iota(3) = [0, 1, 2]` ⇒
3818    /// `[10, 11, 13]` (out[i] = acc after adding x_i, prefix-fold style).
3819    #[test]
3820    fn scan_over_iota_is_emulated() {
3821        let mut ctx = Context::new();
3822        publish_iota_result(&mut ctx);
3823        qcode!(
3824            ctx,
3825            "
3826            lambda step:
3827            <sb @acc:i64 @x:i64>
3828                %r = @acc + @x;
3829                return %r;
3830            fn main:
3831            <me>
3832                %src = $iota(i64 0x3);
3833                %s = scanl @step i64 0xa %src;
3834                goto <0x1001>;
3835            "
3836        );
3837
3838        let mut emu = StandaloneEmulator::new(me);
3839        // Step: iota, then scan (do not execute the terminating goto).
3840        emu.step(&ctx).expect("iota");
3841        emu.step(&ctx).expect("scan");
3842
3843        let buf = emu.array_values.get(&s).expect("scan produced an array");
3844        let words: Vec<u64> = buf
3845            .as_chunks::<8>()
3846            .0
3847            .iter()
3848            .map(|&c| u64::from_le_bytes(c))
3849            .collect();
3850        assert_eq!(words, vec![10, 11, 13]);
3851    }
3852
3853    #[test]
3854    fn enumerate_over_array_is_emulated() {
3855        use qcode::value::{FunctionBody, ValueId, insn::IntrinsicId};
3856
3857        let mut ctx = Context::new();
3858        let f = FunctionBody::make(&mut ctx, "f".into()).unwrap().id;
3859        let entry = ctx.get_or_make_block(0x1000, f);
3860        {
3861            let mut fm = FunctionBody::from_id_mut(&mut ctx, f);
3862            fm.set_root(entry).unwrap();
3863            fm.add_block(entry);
3864        }
3865        // A fixed `[i64; 4]` source `[10, 20, 30, 40]`.
3866        let i64_ty = ctx.shared.types.get_or_make_int(8);
3867        let arr_ty = ctx.shared.types.get_or_make_array(i64_ty, 4);
3868        let data: Vec<u8> = [10u64, 20, 30, 40]
3869            .iter()
3870            .flat_map(|w| w.to_le_bytes())
3871            .collect();
3872        let src = ctx.get_bytes(data).id();
3873        if let ValueId::Bytes(bid) = src {
3874            ctx.shared.values.bytes[bid].type_id = arr_ty;
3875        }
3876        // Publish the `(index, elem)` tuple and its array before pushing the
3877        // intrinsic: `result_type` only reads.
3878        {
3879            use qcode::types::{AggregateField, TypeRequest};
3880            let fields = vec![
3881                AggregateField::new("index", i64_ty),
3882                AggregateField::new("elem", i64_ty),
3883            ];
3884            let tuple = ctx
3885                .shared
3886                .types
3887                .create_requested_types(&[TypeRequest::aggregate(fields)])[0];
3888            ctx.shared
3889                .types
3890                .create_requested_types(&[TypeRequest::array(tuple, 4)]);
3891        }
3892        let enum_id = IntrinsicId::from_name("enumerate").unwrap();
3893        let e = {
3894            let mut b = ctx.builder(entry);
3895            let e = b.push_intrinsic(enum_id, vec![src]).id();
3896            let ptr = b.shr().get_const(0, 8);
3897            b.push_return(ptr);
3898            e
3899        };
3900        let ValueId::Instruction(eid) = e else {
3901            unreachable!()
3902        };
3903
3904        let mut emu = StandaloneEmulator::new(entry);
3905        emu.step(&ctx).expect("enumerate");
3906
3907        // `enumerate([10,20,30,40]) = [(0,10),(1,20),(2,30),(3,40)]`: each lane is
3908        // an `(index: i64, elem: i64)` tuple.
3909        let buf = emu
3910            .array_values
3911            .get(&eid)
3912            .expect("enumerate produced an array");
3913        let words: Vec<u64> = buf
3914            .as_chunks::<8>()
3915            .0
3916            .iter()
3917            .map(|&c| u64::from_le_bytes(c))
3918            .collect();
3919        assert_eq!(words, vec![0, 10, 1, 20, 2, 30, 3, 40]);
3920    }
3921
3922    /// Emulating a function that returns `enumerate` over an *unbounded* list
3923    /// bails with a recoverable `UnsupportedIntrinsic` rather than fabricating a
3924    /// length — a length-erased list has no concrete count to materialize. (A
3925    /// fixed array *is* materialized; see the `mt_scan` differential test.)
3926    #[test]
3927    fn enumerate_of_unbounded_list_bails_recoverably() {
3928        use qcode::value::{
3929            BasicBlock, FunctionBody, InstructionRef, ValueId,
3930            insn::{IntrinsicApp, IntrinsicId, Return},
3931        };
3932
3933        let mut ctx = Context::new();
3934        let f = FunctionBody::make(&mut ctx, "f".into()).unwrap().id;
3935        let entry = ctx.get_or_make_block(0x1000, f);
3936        {
3937            let mut fm = FunctionBody::from_id_mut(&mut ctx, f);
3938            fm.set_root(entry).unwrap();
3939            fm.add_block(entry);
3940        }
3941        let i8 = ctx.shared.types.get_or_make_int(1);
3942        let list_ty = ctx.shared.types.get_or_make_unbounded_list(i8);
3943        let src = {
3944            let mut b = ctx.builder(entry);
3945            b.push_param(8).id()
3946        };
3947        if let ValueId::BlockParam(pid) = src {
3948            ctx.block_param_mut(pid).type_id = list_ty;
3949        }
3950        // Build `enumerate` over the unbounded list with an explicit result type:
3951        // its `result_type` declines an unbounded operand (no static length), so
3952        // the intrinsic is only ever constructed this way, never via inference.
3953        let enum_id = IntrinsicId::from_name("enumerate").unwrap();
3954        // Create the intrinsic in the block's own storage arena so the pushed
3955        // instruction stays strict-local (its parent block lives in the same
3956        // function) — a foreign instruction placement is a locality violation the
3957        // in-body-id localization (ruling 2) forbids.
3958        let env = {
3959            let insn = InstructionRef::from_mnemonic_with_type(
3960                &mut ctx,
3961                entry.func,
3962                Mnemonic::Intrinsic(IntrinsicApp {
3963                    id: enum_id,
3964                    args: vec![src.localize(entry.func)],
3965                }),
3966                list_ty,
3967            )
3968            .id;
3969            BasicBlock::from_id_mut(&mut ctx, entry).push_insn(insn);
3970            ValueId::Instruction(insn)
3971        };
3972        let ptr = ctx.get_const(0, 8).id();
3973        {
3974            let mut b = ctx.builder(entry);
3975            b.push_return(ptr);
3976        }
3977        let rid = BasicBlock::from_id(&ctx, entry).iter().last().unwrap().id;
3978        ctx.replace_instruction_mnemonic(
3979            rid,
3980            Mnemonic::Return(Return {
3981                ptr: ptr.localize(rid.func),
3982                value: Some(env.localize(rid.func)),
3983            }),
3984        );
3985
3986        let mut emu = StandaloneEmulator::new(entry);
3987        let err = emu
3988            .run_pure(&ctx, f, &[SizedValue::new(0, 4)], 1000)
3989            .expect_err("enumerate must not be emulated");
3990        assert!(
3991            matches!(err.kind, EmulatorErrorKind::UnsupportedIntrinsic(ref n) if &**n == "enumerate"),
3992            "expected recoverable UnsupportedIntrinsic, got {:?}",
3993            err.kind
3994        );
3995    }
3996
3997    /// Build pure `f(arr: [i8; n])` returning `at(arr, idx)` and run it with the
3998    /// array param bound to `bound`. Returns the emulated scalar lane, or `None`
3999    /// if `resolve_array` refuses the binding (e.g. an oversize param).
4000    fn run_at_over_array_param(n: usize, idx: u64, bound: SizedValue) -> Option<u64> {
4001        use qcode::value::{
4002            BasicBlock, FunctionBody, ValueId,
4003            insn::{IntrinsicId, Return},
4004        };
4005
4006        let mut ctx = Context::new();
4007        let arr_ty = {
4008            let i8 = ctx.shared.types.get_or_make_int(1);
4009            ctx.shared.types.get_or_make_array(i8, n)
4010        };
4011        let f = FunctionBody::make(&mut ctx, "f".into()).unwrap().id;
4012        let entry = ctx.get_or_make_block(0x1000, f);
4013        {
4014            let mut fm = FunctionBody::from_id_mut(&mut ctx, f);
4015            fm.set_root(entry).unwrap();
4016            fm.add_block(entry);
4017        }
4018        let arr_pid = BasicBlock::from_id_mut(&mut ctx, entry).push_param(n).id;
4019        ctx.block_param_mut(arr_pid).type_id = arr_ty;
4020
4021        let at_id = IntrinsicId::from_name("at").unwrap();
4022        let (ret, ptr, lane);
4023        {
4024            let mut b = ctx.builder(entry);
4025            let arr = ValueId::BlockParam(arr_pid);
4026            let i = b.shr().get_const(idx, 8);
4027            lane = b.push_intrinsic(at_id, vec![arr, i]).id();
4028            ptr = b.shr().get_const(0, 8);
4029            ret = b.push_return(ptr).id();
4030        }
4031        let ValueId::Instruction(rid) = ret else {
4032            unreachable!()
4033        };
4034        ctx.replace_instruction_mnemonic(
4035            rid,
4036            Mnemonic::Return(Return {
4037                ptr: ptr.localize(rid.func),
4038                value: Some(lane.localize(rid.func)),
4039            }),
4040        );
4041
4042        let mut emu = StandaloneEmulator::new(entry);
4043        emu.run_pure(&ctx, f, &[bound], 1000).ok()?;
4044        emu.get_value(&ctx, lane)
4045    }
4046
4047    /// Dispatch through a `switch`: the arm whose case matches the scrutinee is
4048    /// taken, an unmatched value falls to the default, and an arm's block
4049    /// arguments are bound on the way through.
4050    fn run_switch(scrutinee: u64) -> Option<u64> {
4051        let mut ctx = Context::new();
4052        qcode!(
4053            ctx,
4054            "
4055            lambda sw:
4056            <entry @i:i64>
4057                switch @i { 0x0 => <a>, 0x3 => <b @v=0x63>, default => <d> };
4058            <a>
4059                return 0x11;
4060            <b @v:i64>
4061                return @v;
4062            <d>
4063                return 0x99;
4064            "
4065        );
4066        let root = FunctionBody::from_id(&ctx, sw).root().expect("root").id;
4067        let mut emu = StandaloneEmulator::new(root);
4068        emu.run_pure(&ctx, sw, &[SizedValue::new(scrutinee, 8)], 1000)
4069            .ok()?;
4070        let term = BasicBlock::from_id(&ctx, emu.block)
4071            .instruction_ids()
4072            .last()
4073            .copied()?;
4074        let Mnemonic::ReturnValue(r) = Instruction::from_id(&ctx, term).mnemonic() else {
4075            return None;
4076        };
4077        emu.get_value(&ctx, r.value.qualify(term.func))
4078    }
4079
4080    #[test]
4081    fn switch_selects_the_matching_arm() {
4082        assert_eq!(run_switch(0), Some(0x11));
4083        // The matching arm binds its target's block parameter.
4084        assert_eq!(run_switch(3), Some(0x63));
4085        // No case matches 7, so control reaches the default.
4086        assert_eq!(run_switch(7), Some(0x99));
4087    }
4088
4089    /// A literal-bound `[i8;4]` array param resolves through the `BlockParam` arm
4090    /// of `resolve_array`: each little-endian byte of `0x2f76bfc2` is readable via
4091    /// `at`, exactly the read the pure-call folder needs.
4092    #[test]
4093    fn run_pure_reads_array_param_bytes_little_endian() {
4094        let arg = SizedValue::new(0x2f76bfc2, 4);
4095        // LE layout of 0x2f76bfc2 = [0xc2, 0xbf, 0x76, 0x2f].
4096        assert_eq!(run_at_over_array_param(4, 0, arg), Some(0xc2));
4097        assert_eq!(run_at_over_array_param(4, 1, arg), Some(0xbf));
4098        assert_eq!(run_at_over_array_param(4, 2, arg), Some(0x76));
4099        assert_eq!(run_at_over_array_param(4, 3, arg), Some(0x2f));
4100    }
4101
4102    /// An oversize array param (declared width beyond `SizedValue`'s 16-byte cap)
4103    /// can't be materialized from a scalar binding, so the defensive width check
4104    /// fails resolution rather than hand back a clamped, misaligned buffer.
4105    #[test]
4106    fn run_pure_rejects_oversize_array_param() {
4107        // A 20-byte `[i8;20]` param: the bound `SizedValue` clamps to 16 bytes, so
4108        // `bytes.len() (16) != ty_size (20)` and resolution must fail.
4109        assert_eq!(
4110            run_at_over_array_param(20, 0, SizedValue::new(0xff, 20)),
4111            None
4112        );
4113    }
4114
4115    #[test]
4116    fn int_mul_wraps_for_64_bit_values() {
4117        let lhs = SizedValue::new(u64::MAX, 8);
4118        let rhs = SizedValue::new(2, 8);
4119
4120        let product = lhs.int_mul(&rhs).unwrap();
4121
4122        assert_eq!(product.value().unwrap(), u64::MAX.wrapping_mul(2));
4123        assert_eq!(product.size().unwrap(), 8);
4124    }
4125
4126    #[test]
4127    fn int_mul_wraps_for_128_bit_values() {
4128        let lhs = SizedValue::from_bits(u128::MAX, 16);
4129        let rhs = SizedValue::from_bits(u128::from(2u8), 16);
4130
4131        let product = lhs.int_mul(&rhs).unwrap();
4132
4133        assert_eq!(product.as_bits(), u128::MAX.wrapping_mul(u128::from(2u8)));
4134        assert_eq!(product.size().unwrap(), 16);
4135        assert!(matches!(
4136            product.value(),
4137            Err(EmulatorErrorKind::ValueError(_))
4138        ));
4139    }
4140
4141    #[test]
4142    fn int_div_and_rem_work_for_128_bit_values() {
4143        let lhs = SizedValue::from_bits(u128::MAX, 16);
4144        let rhs = SizedValue::from_bits(u128::from(3u8), 16);
4145
4146        let q = lhs.int_div(&rhs).unwrap();
4147        let r = lhs.int_rem(&rhs).unwrap();
4148
4149        assert_eq!(q.as_bits(), u128::MAX / u128::from(3u8));
4150        assert_eq!(r.as_bits(), u128::MAX % u128::from(3u8));
4151        assert_eq!(q.size().unwrap(), 16);
4152        assert_eq!(r.size().unwrap(), 16);
4153    }
4154
4155    #[test]
4156    fn int_sdiv_and_srem_work_for_128_bit_values() {
4157        let lhs = SizedValue::from_bits(u128::from(0xffff_ffff_ffff_ffffu64), 16);
4158        let rhs = SizedValue::from_bits(u128::from(2u8), 16);
4159
4160        let q = lhs.int_sdiv(&rhs).unwrap();
4161        let r = lhs.int_srem(&rhs).unwrap();
4162
4163        assert_eq!(q.as_bits(), u128::from(0x7fff_ffff_ffff_ffffu64));
4164        assert_eq!(r.as_bits(), u128::from(1u8));
4165        assert_eq!(q.size().unwrap(), 16);
4166        assert_eq!(r.size().unwrap(), 16);
4167    }
4168
4169    #[test]
4170    fn signed_extension_and_shift_behave_as_expected() {
4171        let negative_byte = SizedValue::new(0x80, 1);
4172        let extended = negative_byte.sext(8).unwrap();
4173        let shifted = negative_byte
4174            .int_sshift_right(&SizedValue::new(1, 1))
4175            .unwrap();
4176
4177        assert_eq!(extended.value().unwrap(), 0xffff_ffff_ffff_ff80);
4178        assert_eq!(extended.size().unwrap(), 8);
4179        assert_eq!(shifted.value().unwrap(), 0xc0);
4180        assert_eq!(shifted.size().unwrap(), 1);
4181    }
4182
4183    #[test]
4184    fn signed_comparisons_use_value_width() {
4185        let lhs = SizedValue::new(0xff, 1);
4186        let rhs = SizedValue::new(0x01, 1);
4187
4188        assert_eq!(lhs.int_sless(&rhs).and_then(|v| v.value()).unwrap(), 1);
4189        assert_eq!(rhs.int_sless(&lhs).and_then(|v| v.value()).unwrap(), 0);
4190    }
4191
4192    #[test]
4193    fn int_sub_uses_lhs_width_with_default_u64_immediate() {
4194        let lhs = SizedValue::new(0, 4);
4195        let rhs = SizedValue::from_u64(1);
4196
4197        let diff = lhs.int_sub(&rhs).unwrap();
4198
4199        assert_eq!(diff.size().unwrap(), 4);
4200        assert_eq!(diff.value().unwrap(), 0xffff_ffff);
4201    }
4202
4203    #[test]
4204    fn sborrow_uses_lhs_width_with_default_u64_immediate() {
4205        let lhs = SizedValue::new(0x80, 1);
4206        let rhs = SizedValue::new(1, 1);
4207
4208        // 0x80 - 1 overflows in signed 8-bit arithmetic.
4209        assert_eq!(lhs.sborrow(&rhs).and_then(|v| v.value()).unwrap(), 1);
4210    }
4211
4212    #[test]
4213    fn scarry_uses_lhs_width_with_default_u64_immediate() {
4214        let lhs = SizedValue::new(0x7f, 1);
4215        let rhs = SizedValue::new(1, 1);
4216
4217        // 0x7f + 1 overflows in signed 8-bit arithmetic.
4218        assert_eq!(lhs.scarry(&rhs).and_then(|v| v.value()).unwrap(), 1);
4219    }
4220
4221    #[test]
4222    fn sborrow_neg() {
4223        let lhs = SizedValue::new(0x0, 1);
4224        let rhs = SizedValue::new(0x80, 1);
4225
4226        // 0 - 0x80  overflows in signed 8-bit arithmetic.
4227        assert_eq!(lhs.sborrow(&rhs).and_then(|v| v.value()).unwrap(), 1);
4228    }
4229
4230    #[test]
4231    fn lz_count_respects_width() {
4232        let value = SizedValue::new(0x01, 1);
4233        let lz = value.lz_count().unwrap();
4234
4235        assert_eq!(lz.value().unwrap(), 7);
4236        assert_eq!(lz.size().unwrap(), 1);
4237    }
4238
4239    #[test]
4240    fn float_conversion_handles_f32_and_f64() {
4241        let minus_one = SizedValue::new(0xff, 1);
4242        let as_f32 = minus_one.int_to_float(4).unwrap();
4243        assert_eq!(as_f32.value().unwrap(), (-1.0f32).to_bits() as u64);
4244        assert_eq!(as_f32.size().unwrap(), 4);
4245
4246        let f32_value = SizedValue::new((1.5f32).to_bits() as u64, 4);
4247        let promoted = f32_value.float_to_float(8).unwrap();
4248        let promoted_bits = promoted.value().unwrap();
4249        assert_eq!(f64::from_bits(promoted_bits), 1.5f64);
4250        assert_eq!(promoted.size().unwrap(), 8);
4251
4252        let demoted = promoted.float_to_float(4).unwrap();
4253        assert_eq!(demoted.value().unwrap(), (1.5f32).to_bits() as u64);
4254        assert_eq!(demoted.size().unwrap(), 4);
4255    }
4256
4257    #[test]
4258    fn x87_precision_and_store_rounding_are_separate_from_generic_arithmetic() {
4259        let one = 0x3fff_8000_0000_0000_0000u128;
4260        // Precision rounding narrows the f80 significand without narrowing
4261        // the exponent, and follows the IEEE rounding mode it is given.
4262        let one_plus_half_single_ulp = one + (1u128 << 39);
4263        assert_eq!(
4264            float80::round_to_precision(one_plus_half_single_ulp, 24, Round::NearestTiesToEven)
4265                .bits,
4266            one
4267        );
4268        assert_eq!(
4269            float80::round_to_precision(one_plus_half_single_ulp, 24, Round::TowardPositive).bits,
4270            one + (1u128 << 40)
4271        );
4272        // 64 significand bits is the identity.
4273        assert_eq!(
4274            float80::round_to_precision(one_plus_half_single_ulp, 64, Round::TowardPositive).bits,
4275            one_plus_half_single_ulp
4276        );
4277
4278        // Narrowing takes an explicit rounding mode and no control word: the
4279        // exact halfway value narrows to 1.0 under nearest-even and to the
4280        // next f32 under round-up.
4281        let extended = SizedValue::from_bits(one_plus_half_single_ulp, 10);
4282        assert_eq!(
4283            StandaloneEmulator::<EmulatedMemory>::ieee_narrow(
4284                extended,
4285                4,
4286                Round::NearestTiesToEven
4287            )
4288            .unwrap()
4289            .0
4290            .as_bits(),
4291            u128::from(1.0f32.to_bits())
4292        );
4293        assert_eq!(
4294            StandaloneEmulator::<EmulatedMemory>::ieee_narrow(extended, 4, Round::TowardPositive)
4295                .unwrap()
4296                .0
4297                .as_bits(),
4298            u128::from((1.0f32).to_bits() + 1)
4299        );
4300
4301        // Generic f80 arithmetic is architecture-neutral: it computes a value
4302        // and records no x87 status.  x87 constructors use the explicit IEEE
4303        // p-code operations instead and apply their own exception policy.
4304        let mut ctx = Context::new();
4305        qcode!(
4306            ctx,
4307            "
4308            varnode i16 FPUControlWord;
4309            varnode i16 FPUStatusWord;
4310            varnode f80 A;
4311            varnode f80 B;
4312
4313        <block>
4314            %a = load(A:10, &A);
4315            %b = load(B:10, &B);
4316            %result = %a f/ %b;
4317            goto <0x1001>;
4318        "
4319        );
4320        let mut emu = Emulator::from_block(&ctx, block);
4321        emu.set_varnode(FPUControlWord, 0x037b).unwrap(); // ZE unmasked
4322        emu.set_varnode_u128(A, one).unwrap();
4323        emu.set_varnode_u128(B, 0).unwrap();
4324        emu.run_block().unwrap();
4325        assert_eq!(emu.get_value(result.into()).unwrap().size().unwrap(), 10);
4326        // Never written: the status word stays untouched by the operation.
4327        assert_eq!(emu.read_varnode(FPUStatusWord), None);
4328    }
4329
4330    /// An f80 comparison is a pure predicate. A signalling NaN operand is
4331    /// invalid under x87's rules, but raising it is the specification's job:
4332    /// the emulator writes no status word for a comparison either.
4333    #[test]
4334    fn f80_comparison_records_no_status() {
4335        let mut ctx = Context::new();
4336        qcode!(
4337            ctx,
4338            "
4339            varnode i16 FPUControlWord;
4340            varnode i16 FPUStatusWord;
4341            varnode f80 A;
4342            varnode f80 B;
4343
4344        <block>
4345            %a = load(A:10, &A);
4346            %b = load(B:10, &B);
4347            %equal = %a f== %b;
4348            goto <0x1001>;
4349        "
4350        );
4351        let mut emu = Emulator::from_block(&ctx, block);
4352        emu.set_varnode(FPUControlWord, 0x037f).unwrap();
4353        // A signalling NaN: exponent all ones, integer bit set, quiet bit
4354        // clear, non-zero fraction.
4355        emu.set_varnode_u128(A, 0x7fff_8000_0000_0000_0001).unwrap();
4356        emu.set_varnode_u128(B, 0x3fff_8000_0000_0000_0000).unwrap();
4357        emu.run_block().unwrap();
4358        assert_eq!(emu.get_value(equal.into()).unwrap().value().unwrap(), 0);
4359        assert_eq!(emu.read_varnode(FPUStatusWord), None);
4360    }
4361
4362    /// A partial reduction consumes 63 quotient bits per step, leaving the
4363    /// remainder exact. Hardware reduces (2 - 2^-63) * 2^16383 modulo 1.0 to
4364    /// exactly zero, which only happens when the whole reducible span is
4365    /// consumed: a 32-bit partial quotient leaves a large non-zero remainder.
4366    #[test]
4367    fn float80_partial_remainder_is_exact() {
4368        let dividend = 0x7ffe_ffff_ffff_ffff_ffffu128;
4369        let one = 0x3fff_8000_0000_0000_0000u128;
4370        for ieee in [false, true] {
4371            let result = float80::remainder(dividend, one, ieee);
4372            assert!(result.incomplete);
4373            assert_eq!(result.bits, 0);
4374        }
4375
4376        // One exponent more of span drops a whole 32-bit group of quotient
4377        // bits, leaving 32 behind: the remainder is the dividend's low 32
4378        // exponents, not zero. Hardware reduces in 32-bit groups.
4379        let half = 0x3ffe_8000_0000_0000_0000u128;
4380        let result = float80::remainder(dividend, half, false);
4381        assert!(result.incomplete);
4382        assert_eq!(result.bits, 0x7fdd_ffff_fffe_0000_0000);
4383
4384        // A span under 64 exponents still completes in one step and reports
4385        // the quotient's low bits.
4386        let three = 0x4000_c000_0000_0000_0000u128;
4387        let result = float80::remainder(three, one, false);
4388        assert!(!result.incomplete);
4389        assert_eq!(result.bits, 0);
4390        assert_eq!(result.quotient & 7, 3);
4391    }
4392
4393    /// The 80-bit square root is computed on the integer significand, so it
4394    /// keeps all 64 bits. Routing it through f64 would lose eleven of them.
4395    #[test]
4396    fn float80_sqrt_is_correctly_rounded_at_extended_precision() {
4397        let two = 0x4000_8000_0000_0000_0000;
4398        let four = 0x4001_8000_0000_0000_0000;
4399        let one = 0x3fff_8000_0000_0000_0000;
4400
4401        // sqrt(2) rounds up into the last significand bit.
4402        let root_two = float80::sqrt_ieee(two, Round::NearestTiesToEven);
4403        assert_eq!(root_two.bits, 0x3fff_b504_f333_f9de_6484);
4404        assert!(root_two.status.contains(Status::INEXACT));
4405
4406        // Exact roots stay exact and report nothing.
4407        for (input, expect) in [(four, two), (one, one), (0, 0)] {
4408            let result = float80::sqrt_ieee(input, Round::NearestTiesToEven);
4409            assert_eq!(result.bits, expect);
4410            assert_eq!(result.status, Status::OK);
4411        }
4412
4413        // sqrt(3) is a case where the integer root does round up, so nearest
4414        // and truncation land on different significands.
4415        let three = 0x4000_c000_0000_0000_0000;
4416        assert_eq!(
4417            float80::sqrt_ieee(three, Round::NearestTiesToEven).bits,
4418            0x3fff_ddb3_d742_c265_539e
4419        );
4420        assert_eq!(
4421            float80::sqrt_ieee(three, Round::TowardZero).bits,
4422            0x3fff_ddb3_d742_c265_539d
4423        );
4424
4425        // A negative operand is invalid. What is delivered in its place is
4426        // the specification's choice, so the operation returns the operand.
4427        let negative = float80::sqrt_ieee(0xbfff_8000_0000_0000_0000, Round::NearestTiesToEven);
4428        assert_eq!(negative.bits, 0xbfff_8000_0000_0000_0000);
4429        assert!(negative.status.contains(Status::INVALID_OP));
4430    }
4431
4432    #[test]
4433    fn float80_arithmetic_preserves_extended_precision_bits() {
4434        // x87 80-bit encodings: significand in bits 0..64, exponent/sign in
4435        // bits 64..80. 3.0 is not representable by merely treating f80 as an
4436        // f64 bit-pattern, which was the former behavior.
4437        let one = SizedValue::from_bits(0x3fff_8000_0000_0000_0000, 10);
4438        let two = SizedValue::from_bits(0x4000_8000_0000_0000_0000, 10);
4439        let three = one.float_add(&two).unwrap();
4440
4441        assert_eq!(three.as_bits(), 0x4000_c000_0000_0000_0000);
4442        assert_eq!(three.size().unwrap(), 10);
4443        assert_eq!(two.float_to_float(10).unwrap().as_bits(), two.as_bits());
4444        assert_eq!(
4445            SizedValue::new(3, 1).int_to_float(10).unwrap().as_bits(),
4446            three.as_bits()
4447        );
4448    }
4449
4450    #[test]
4451    fn explicit_ieee_arithmetic_pairs_results_and_flags_in_every_rounding_mode() {
4452        // Each tuple has an inexact operand pair for its respective operation.
4453        // Exercise all supported formats as well as every rounding direction;
4454        // the result and flags calls must be two views of exactly one IEEE
4455        // evaluation, not host arithmetic with independently inferred flags.
4456        let cases = [
4457            (
4458                4,
4459                0x3f80_0000,
4460                0x3380_0000,
4461                0x3f80_0001,
4462                0x3fc0_0000,
4463                0x3f80_0000,
4464                0x4040_0000,
4465            ),
4466            (
4467                8,
4468                0x3ff0_0000_0000_0000,
4469                0x3ca0_0000_0000_0000,
4470                0x3ff0_0000_0000_0001,
4471                0x3ff8_0000_0000_0000,
4472                0x3ff0_0000_0000_0000,
4473                0x4008_0000_0000_0000,
4474            ),
4475            (
4476                10,
4477                0x3fff_8000_0000_0000_0000,
4478                0x3fbf_8000_0000_0000_0000,
4479                0x3fff_8000_0000_0000_0001,
4480                0x3fff_c000_0000_0000_0000,
4481                0x3fff_8000_0000_0000_0000,
4482                0x4000_c000_0000_0000_0000,
4483            ),
4484        ];
4485        let operations = [
4486            ("float_add", "float_add_flags", 0usize),
4487            ("float_sub", "float_sub_flags", 1),
4488            ("float_mul", "float_mul_flags", 2),
4489            ("float_div", "float_div_flags", 3),
4490        ];
4491
4492        for (size, add_lhs, add_rhs, mul_lhs, mul_rhs, div_lhs, div_rhs) in cases {
4493            // At one, the spacing below is half the spacing above. Use a
4494            // quarter of the upward ULP for subtraction so it is inexact too.
4495            let sub_rhs = match size {
4496                4 => 0x3300_0000,
4497                8 => 0x3c90_0000_0000_0000,
4498                10 => 0x3fbe_8000_0000_0000_0000,
4499                _ => unreachable!(),
4500            };
4501            let operands = [
4502                (add_lhs, add_rhs),
4503                (add_lhs, sub_rhs),
4504                (mul_lhs, mul_rhs),
4505                (div_lhs, div_rhs),
4506            ];
4507            for (result_name, flags_name, pair) in operations {
4508                let (lhs, rhs) = operands[pair];
4509                for mode in 0..4 {
4510                    let round =
4511                        StandaloneEmulator::<EmulatedMemory>::ieee_rounding_mode(mode).unwrap();
4512                    let (result, status) = StandaloneEmulator::<EmulatedMemory>::ieee_arithmetic(
4513                        SizedValue::from_bits(lhs, size),
4514                        SizedValue::from_bits(rhs, size),
4515                        round,
4516                        result_name,
4517                    )
4518                    .unwrap();
4519                    let (_, flag_status) = StandaloneEmulator::<EmulatedMemory>::ieee_arithmetic(
4520                        SizedValue::from_bits(lhs, size),
4521                        SizedValue::from_bits(rhs, size),
4522                        round,
4523                        flags_name,
4524                    )
4525                    .unwrap();
4526                    assert_eq!(
4527                        status,
4528                        flag_status,
4529                        "{result_name}, f{}, mode {mode}",
4530                        size * 8
4531                    );
4532                    let flags = StandaloneEmulator::<EmulatedMemory>::ieee_flags(flag_status);
4533                    assert_ne!(
4534                        flags.as_bits() & (1 << 5),
4535                        0,
4536                        "{result_name}, f{}, mode {mode}",
4537                        size * 8
4538                    );
4539                    assert_eq!(result.size as usize, size);
4540                }
4541            }
4542        }
4543    }
4544
4545    #[test]
4546    fn simple_addition() {
4547        let mut ctx = Context::new();
4548
4549        qcode!(
4550            ctx,
4551            "
4552            varnode i64 V0;
4553            varnode i64 V1;
4554
4555        <block>
4556            %v0 = load(V0:8, &V0);
4557            %v1 = load(V1:8, &V1);
4558            %res = %v0 + %v1;
4559            goto <0x1001>;
4560        "
4561        );
4562
4563        let mut emu = Emulator::from_block(&ctx, block);
4564        emu.set_varnode(V0, 2).unwrap();
4565        emu.set_varnode(V1, 3).unwrap();
4566        emu.run_block().unwrap();
4567
4568        assert_eq!(
4569            emu.get_value(res.into()).and_then(|v| v.value()).unwrap(),
4570            5
4571        );
4572    }
4573
4574    #[test]
4575    fn gep_emulates_as_base_plus_offset() {
4576        let mut ctx = Context::new();
4577
4578        // `Inner { val: i32 @ 0x08 }` (0x08 via leading padding), `%p : Inner*`.
4579        qcode!(
4580            ctx,
4581            "
4582            type Inner { _: 8, val: 4 };
4583            varnode i64 V0;
4584
4585        <block>
4586            Inner* %p = load(V0:8, &V0);
4587            %fld = gep(%p.val);
4588            goto <0x1001>;
4589        "
4590        );
4591
4592        let mut emu = Emulator::from_block(&ctx, block);
4593        emu.set_varnode(V0, 0x1000).unwrap();
4594        emu.run_block().unwrap();
4595
4596        let fld = emu.get_value(fld.into()).unwrap();
4597        assert_eq!(fld.value().unwrap(), 0x1008);
4598        // Width follows the pointer base, not the immediate's default u64.
4599        assert_eq!(fld.size().unwrap(), 8);
4600    }
4601
4602    #[test]
4603    fn emulator_int_div_works_with_128_bit_operands() {
4604        let mut ctx = Context::new();
4605        qcode!(
4606            ctx,
4607            "
4608            varnode i128 V0;
4609            varnode i128 V1;
4610
4611        <block>
4612            %v0 = load(V0:16, &V0);
4613            %v1 = load(V1:16, &V1);
4614
4615            %res = %v0 / %v1;
4616            goto <0x1001>;
4617        "
4618        );
4619
4620        let mut emu = Emulator::from_block(&ctx, block);
4621        let v0_bits = u128::from(1u8) << 100;
4622        let v1_bits = u128::from(1u8) << 99;
4623
4624        emu.set_varnode_u128(V0, v0_bits).unwrap();
4625        emu.set_varnode_u128(V1, v1_bits).unwrap();
4626        emu.run_block().unwrap();
4627
4628        // Quotient is small enough to also be visible through legacy u64 extraction.
4629        assert_eq!(
4630            emu.get_value(res.into()).and_then(|v| v.value()).unwrap(),
4631            2
4632        );
4633    }
4634
4635    // -----------------------------------------------------------------------
4636    // Memory error-handling tests
4637    // -----------------------------------------------------------------------
4638
4639    #[test]
4640    fn uninitialized_memory_reads_error() {
4641        let space = EmulatedSpace::default();
4642        assert!(matches!(
4643            space.read_byte(0xdead_beef),
4644            Err(EmulatorErrorKind::MemoryReadError(0xdead_beef))
4645        ));
4646        assert!(matches!(
4647            space.read(0x1000, 4),
4648            Err(EmulatorErrorKind::MemoryReadError(0x1000))
4649        ));
4650    }
4651
4652    #[test]
4653    fn configured_register_and_body_temporary_spaces_zero_fill_missing_bytes() {
4654        let mut ctx = Context::new();
4655        let mut register = Space::new(Some("register"), 1, 8);
4656        register.ty = SpaceType::Register;
4657        let register = ctx.add_space(register);
4658        let function = ctx.anon_function();
4659        let temporary = MemorySpaceId::Temp(ctx.bodies[function].push_temp_space(TempSpace::new(
4660            Some("scratch"),
4661            1,
4662            8,
4663        )));
4664        let mut memory = EmulatedMemory::default();
4665        memory.configure_spaces(&ctx);
4666
4667        for space in [register.into(), temporary] {
4668            assert_eq!(
4669                memory
4670                    .read(space, SizedValue::from_u64(0x1000), 4)
4671                    .unwrap()
4672                    .value()
4673                    .unwrap(),
4674                0
4675            );
4676        }
4677
4678        memory
4679            .write(
4680                ctx.shared.default_space.into(),
4681                SizedValue::from_u64(0x1000),
4682                1,
4683                SizedValue::new(0xaa, 1),
4684            )
4685            .unwrap();
4686        assert!(matches!(
4687            memory.read(
4688                ctx.shared.default_space.into(),
4689                SizedValue::from_u64(0x1001),
4690                1
4691            ),
4692            Err(EmulatorErrorKind::MemoryReadError(0x1001))
4693        ));
4694    }
4695
4696    #[test]
4697    fn temporary_spaces_with_the_same_address_are_isolated() {
4698        use qcode::value::TempSpace;
4699
4700        let mut ctx = Context::new();
4701        let first_fn = FunctionBody::make(&mut ctx, "first".into()).unwrap().id;
4702        let second_fn = FunctionBody::make(&mut ctx, "second".into()).unwrap().id;
4703        let first = ctx.bodies[first_fn].push_temp_space(TempSpace::new(None, 1, 8));
4704        let second = ctx.bodies[second_fn].push_temp_space(TempSpace::new(None, 1, 8));
4705        assert_eq!(first.local, second.local, "fixture must collide local IDs");
4706        let first = MemorySpaceId::Temp(first);
4707        let second = MemorySpaceId::Temp(second);
4708        let mut memory = EmulatedMemory::default();
4709        memory.configure_spaces(&ctx);
4710
4711        let address = SizedValue::from_u64(0x20);
4712        memory
4713            .write(first, address, 1, SizedValue::new(0xaa, 1))
4714            .unwrap();
4715        memory
4716            .write(second, address, 1, SizedValue::new(0x55, 1))
4717            .unwrap();
4718
4719        assert_eq!(
4720            memory.read(first, address, 1).unwrap().value().unwrap(),
4721            0xaa
4722        );
4723        assert_eq!(
4724            memory.read(second, address, 1).unwrap().value().unwrap(),
4725            0x55
4726        );
4727    }
4728
4729    #[test]
4730    fn interpreter_qualifies_colliding_local_spaces_by_function() {
4731        use qcode::value::TempSpace;
4732
4733        fn make_writer(
4734            ctx: &mut Context<'static>,
4735            name: &'static str,
4736            byte: u64,
4737        ) -> (FunctionId, qcode::value::TempSpaceId) {
4738            let fid = FunctionBody::make(ctx, name.into()).unwrap().id;
4739            let root = BasicBlock::make(ctx, fid).id;
4740            FunctionBody::from_id_mut(ctx, fid).set_root(root).unwrap();
4741            let space = ctx.bodies[fid].push_temp_space(TempSpace::new(None, 1, 8));
4742            let mut b = (ctx).builder(root);
4743            let ptr = b.shr().get_const(0x20, 8);
4744            let value = b.shr().get_const(byte, 1);
4745            b.push_store(
4746                value,
4747                ptr,
4748                qcode::space::LocalMemorySpaceId::Temp(space.local),
4749            );
4750            b.push_return(ptr);
4751            (fid, space)
4752        }
4753
4754        let mut ctx = Context::new();
4755        let (first, first_space) = make_writer(&mut ctx, "first", 0xaa);
4756        let (second, second_space) = make_writer(&mut ctx, "second", 0x55);
4757        assert_eq!(first_space.local, second_space.local);
4758
4759        let root = FunctionBody::from_id(&ctx, first).root().unwrap().id;
4760        let mut emulator = StandaloneEmulator::new(root);
4761        emulator.run_function(&ctx, first).unwrap();
4762        emulator.run_function(&ctx, second).unwrap();
4763
4764        let address = SizedValue::from_u64(0x20);
4765        assert_eq!(
4766            emulator
4767                .memory
4768                .read(MemorySpaceId::Temp(first_space), address, 1)
4769                .unwrap()
4770                .value()
4771                .unwrap(),
4772            0xaa
4773        );
4774        assert_eq!(
4775            emulator
4776                .memory
4777                .read(MemorySpaceId::Temp(second_space), address, 1)
4778                .unwrap()
4779                .value()
4780                .unwrap(),
4781            0x55
4782        );
4783    }
4784
4785    #[test]
4786    fn sized_value_byte_swap_preserves_width() {
4787        let value = SizedValue::new(0x1234, 2).byte_swap().unwrap();
4788        assert_eq!(value.value().unwrap(), 0x3412);
4789        assert_eq!(value.size().unwrap(), 2);
4790    }
4791
4792    #[test]
4793    fn swap_bytes_pcode_op_is_emulated() {
4794        let mut ctx = Context::new();
4795        let op = ctx.shared.pcode_ops.push(Box::from("swap_bytes"));
4796        let block_id = {
4797            let __f = ctx.anon_function();
4798            ctx.get_or_make_block(0x1000, __f)
4799        };
4800        let target = ctx.get_or_make_block(0x1001, block_id.func);
4801        let result = {
4802            let src = ctx.get_const(0x1234, 2).id();
4803            let mut builder = ctx.builder(block_id);
4804            let result = builder.push_pcode_op(op, vec![src], None, 2).id;
4805            builder.finalize(target);
4806            result
4807        };
4808        let mut emulator = Emulator::from_block(&ctx, block_id);
4809
4810        emulator.step().unwrap();
4811
4812        assert_eq!(
4813            emulator
4814                .get_value(result.into())
4815                .and_then(|value| value.value())
4816                .unwrap(),
4817            0x3412
4818        );
4819    }
4820
4821    #[test]
4822    fn undef_pcode_op_is_zero_at_its_declared_width() {
4823        let mut ctx = Context::new();
4824        let op = ctx.shared.pcode_ops.push(Box::from("undef"));
4825        let block_id = {
4826            let function = ctx.anon_function();
4827            ctx.get_or_make_block(0x1000, function)
4828        };
4829        let target = ctx.get_or_make_block(0x1001, block_id.func);
4830        let result = {
4831            let mut builder = ctx.builder(block_id);
4832            let result = builder.push_pcode_op(op, vec![], None, 1).id;
4833            builder.finalize(target);
4834            result
4835        };
4836        let mut emulator = Emulator::from_block(&ctx, block_id);
4837
4838        emulator.step().unwrap();
4839
4840        let value = emulator.get_value(result.into()).unwrap();
4841        assert_eq!(value.value().unwrap(), 0);
4842        assert_eq!(value.size().unwrap(), 1);
4843    }
4844
4845    #[test]
4846    fn rol_intrinsic_is_emulated() {
4847        use qcode::value::insn::IntrinsicId;
4848        let mut ctx = Context::new();
4849        let rol = IntrinsicId::from_name("rol").unwrap();
4850        let block_id = {
4851            let __f = ctx.anon_function();
4852            ctx.get_or_make_block(0x1000, __f)
4853        };
4854        let target = ctx.get_or_make_block(0x1001, block_id.func);
4855        let result = {
4856            let x = ctx.get_const(0x1234_5678, 4).id();
4857            let k = ctx.get_const(8, 4).id();
4858            let mut builder = ctx.builder(block_id);
4859            let result = builder.push_intrinsic(rol, vec![x, k]).id;
4860            builder.finalize(target);
4861            result
4862        };
4863        let mut emulator = Emulator::from_block(&ctx, block_id);
4864
4865        emulator.step().unwrap();
4866
4867        assert_eq!(
4868            emulator
4869                .get_value(result.into())
4870                .and_then(|value| value.value())
4871                .unwrap(),
4872            0x1234_5678u32.rotate_left(8) as u64,
4873        );
4874    }
4875
4876    #[test]
4877    fn unknown_pcode_op_returns_typed_error() {
4878        let mut ctx = Context::new();
4879        let op = ctx.shared.pcode_ops.push(Box::from("rdpmc"));
4880        let block_id = {
4881            let __f = ctx.anon_function();
4882            ctx.get_or_make_block(0x1000, __f)
4883        };
4884        let target = ctx.get_or_make_block(0x1001, block_id.func);
4885        {
4886            let mut builder = ctx.builder(block_id);
4887            builder.push_pcode_op(op, vec![], None, 0);
4888            builder.finalize(target);
4889        }
4890        let mut emulator = Emulator::from_block(&ctx, block_id);
4891
4892        let error = emulator.step().unwrap_err();
4893
4894        assert!(matches!(
4895            error.kind,
4896            EmulatorErrorKind::UnsupportedPCodeOp(operation) if operation.as_ref() == "rdpmc"
4897        ));
4898    }
4899
4900    #[test]
4901    fn get_region_overflow_does_not_panic() {
4902        let mut space = EmulatedSpace::default();
4903        // addr + size would overflow u64 without checked_add
4904        assert!(matches!(
4905            space.get_mut_region(u64::MAX - 2, 8),
4906            Err(EmulatorErrorKind::AddressOverflow(_, _))
4907        ));
4908    }
4909
4910    // -----------------------------------------------------------------------
4911    // run_function happy-path tests
4912    // -----------------------------------------------------------------------
4913
4914    #[test]
4915    fn run_function_returns_ok_for_trivial_function() {
4916        let mut ctx = Context::new();
4917        qcode!(
4918            ctx,
4919            "
4920            fn function:
4921            <entry>
4922                return at i64 0;
4923            "
4924        );
4925
4926        let mut emu = Emulator::from_function(&ctx, function);
4927        assert!(emu.run_function(function).is_ok());
4928    }
4929
4930    #[test]
4931    fn run_function_executes_instructions_before_return() {
4932        let mut ctx = Context::new();
4933        qcode!(
4934            ctx,
4935            "
4936            varnode i64 A;
4937            varnode i64 B;
4938
4939            fn function:
4940            <entry>
4941                %a = load(A:8, &A);
4942                %b = load(B:8, &B);
4943                %sum = %a + %b;
4944                return at i64 0;
4945            "
4946        );
4947
4948        let mut emu = Emulator::from_function(&ctx, function);
4949        emu.set_varnode(A, 7).unwrap();
4950        emu.set_varnode(B, 5).unwrap();
4951        emu.run_function(function).unwrap();
4952
4953        assert_eq!(
4954            emu.get_value(sum.into()).and_then(|v| v.value()).unwrap(),
4955            12
4956        );
4957    }
4958
4959    #[test]
4960    fn run_function_call_stack_empty_after_successful_return() {
4961        let mut ctx = Context::new();
4962
4963        qcode!(
4964            ctx,
4965            "
4966            varnode i64 A;
4967            varnode i64 B;
4968
4969            fn function:
4970            <entry>
4971                %a = load(A:8, &A);
4972                %b = load(B:8, &B);
4973                %sum = %a + %b;
4974                return at i64 0;
4975            "
4976        );
4977
4978        let mut emu = Emulator::from_function(&ctx, function);
4979        emu.set_varnode(A, 0).unwrap();
4980        emu.set_varnode(B, 0).unwrap();
4981        emu.run_function(function).unwrap();
4982
4983        assert!(emu.call_stack().is_empty());
4984    }
4985
4986    #[test]
4987    fn unhandled_direct_call_still_enters_callee() {
4988        let mut ctx = Context::new();
4989        qcode!(
4990            ctx,
4991            "
4992            fn callee:
4993            <callee_entry>
4994                return at i64 0;
4995
4996            <caller>
4997                call <callee>;
4998            "
4999        );
5000
5001        let mut emu = Emulator::from_block(&ctx, caller);
5002        emu.step().unwrap();
5003
5004        assert_eq!(emu.block().id, callee_entry);
5005    }
5006
5007    #[test]
5008    fn handled_direct_call_resumes_at_selected_block() {
5009        let mut ctx = Context::new();
5010        qcode!(
5011            ctx,
5012            "
5013            varnode i64 RET;
5014
5015            fn library:
5016            <library_entry>
5017                return at i64 0;
5018
5019            fn function:
5020            <entry>
5021                call <library>;
5022            <after_call>
5023                %ret = load(RET:8, &RET);
5024                return at i64 0;
5025            "
5026        );
5027
5028        let mut emu = Emulator::from_function(&ctx, function);
5029        emu.set_call_interceptor(move |ctx, emu, site| {
5030            if site.target == library {
5031                emu.set_varnode(ctx, RET, 42)
5032                    .map_err(|err| err.to_string().into_boxed_str())?;
5033                Ok(CallInterception::Handled(CallContinuation::Block(
5034                    after_call,
5035                )))
5036            } else {
5037                Ok(CallInterception::PassThrough)
5038            }
5039        });
5040
5041        emu.run_function(function).unwrap();
5042
5043        assert_eq!(
5044            emu.get_value(ret.into()).and_then(|v| v.value()).unwrap(),
5045            42
5046        );
5047        assert!(emu.call_stack().is_empty());
5048    }
5049
5050    #[test]
5051    fn handled_direct_call_can_resume_by_address() {
5052        let mut ctx = Context::new();
5053        qcode!(
5054            ctx,
5055            "
5056            fn library:
5057            <library_entry>
5058                return at i64 0;
5059
5060            <entry>
5061                call <library>;
5062            <0x2000>
5063                return at i64 0;
5064            "
5065        );
5066
5067        let mut emu = Emulator::from_block(&ctx, entry);
5068        emu.set_call_interceptor(move |_, _, site| {
5069            if site.target == library {
5070                Ok(CallInterception::Handled(CallContinuation::Address(0x2000)))
5071            } else {
5072                Ok(CallInterception::PassThrough)
5073            }
5074        });
5075
5076        emu.step().unwrap();
5077
5078        assert_eq!(emu.block().address(), Some(0x2000));
5079    }
5080
5081    #[test]
5082    fn handled_direct_call_reports_unknown_continuation_address() {
5083        let mut ctx = Context::new();
5084        qcode!(
5085            ctx,
5086            "
5087            fn library:
5088            <library_entry>
5089                return at i64 0;
5090
5091            <entry>
5092                call <library>;
5093            "
5094        );
5095
5096        let mut emu = Emulator::from_block(&ctx, entry);
5097        emu.set_call_interceptor(move |_, _, site| {
5098            if site.target == library {
5099                Ok(CallInterception::Handled(CallContinuation::Address(0xdead)))
5100            } else {
5101                Ok(CallInterception::PassThrough)
5102            }
5103        });
5104
5105        let err = emu.step().unwrap_err();
5106
5107        assert!(matches!(
5108            err.kind,
5109            EmulatorErrorKind::InvalidBlockAddress(0xdead)
5110        ));
5111    }
5112
5113    #[test]
5114    fn call_interceptor_errors_are_reported_at_call_site() {
5115        let mut ctx = Context::new();
5116        qcode!(
5117            ctx,
5118            "
5119            fn library:
5120            <library_entry>
5121                return at i64 0;
5122
5123            <entry>
5124                call <library>;
5125            "
5126        );
5127
5128        let mut emu = Emulator::from_block(&ctx, entry);
5129        emu.set_call_interceptor(|_, _, _| Err("model failed".into()));
5130
5131        let err = emu.step().unwrap_err();
5132
5133        assert!(matches!(
5134            err.kind,
5135            EmulatorErrorKind::InterceptError(message) if message.as_ref() == "model failed"
5136        ));
5137        assert!(err.ctx.contains("call fn library();"));
5138    }
5139
5140    #[test]
5141    fn interceptor_can_model_state_across_calls() {
5142        let mut ctx = Context::new();
5143        qcode!(
5144            ctx,
5145            "
5146            fn make_object:
5147            <make_object_entry>
5148                return at i64 0;
5149
5150            fn append_byte:
5151            <append_byte_entry>
5152                return at i64 0;
5153
5154            fn function:
5155            <entry>
5156                call <make_object>;
5157            <append>
5158                call <append_byte>;
5159            <done>
5160                return at i64 0;
5161            "
5162        );
5163
5164        let modeled = Arc::new(Mutex::new(Vec::<u8>::new()));
5165        let modeled_for_hook = Arc::clone(&modeled);
5166        let mut emu = Emulator::from_function(&ctx, function);
5167        emu.set_call_interceptor(move |_, _, site| {
5168            let mut model = modeled_for_hook.lock().unwrap();
5169            if site.target == make_object {
5170                model.clear();
5171                Ok(CallInterception::Handled(CallContinuation::Block(append)))
5172            } else if site.target == append_byte {
5173                model.push(0x41);
5174                Ok(CallInterception::Handled(CallContinuation::Block(done)))
5175            } else {
5176                Ok(CallInterception::PassThrough)
5177            }
5178        });
5179
5180        emu.run_function(function).unwrap();
5181
5182        assert_eq!(*modeled.lock().unwrap(), vec![0x41]);
5183    }
5184
5185    // -----------------------------------------------------------------------
5186    // run_function error tests
5187    // -----------------------------------------------------------------------
5188
5189    #[test]
5190    fn branchind_to_unknown_address_returns_error() {
5191        let mut ctx = Context::new();
5192        qcode!(
5193            ctx,
5194            "
5195            fn function:
5196            <entry>
5197                # Branching to literal 0 — no block lives at address 0
5198                goto [i64 0];
5199            "
5200        );
5201
5202        let mut emu = Emulator::from_function(&ctx, function);
5203        let err = emu.run_function(function).unwrap_err();
5204        assert!(matches!(
5205            err.kind,
5206            EmulatorErrorKind::InvalidBlockAddress(0)
5207        ));
5208    }
5209
5210    #[test]
5211    fn error_includes_faulting_instruction_id() {
5212        let mut ctx = Context::new();
5213        qcode!(
5214            ctx,
5215            "
5216            fn function:
5217            <entry>
5218                # Null pointer dereference
5219                %bad_load = load(ram:8, i64 0);
5220                return at i64 0;
5221            "
5222        );
5223
5224        let mut emu = Emulator::from_function(&ctx, function);
5225        let err = emu.run_function(function).unwrap_err();
5226
5227        assert!(
5228            err.ctx.contains(
5229                &Instruction::from_id(&ctx, bad_load)
5230                    .as_statement()
5231                    .to_string()
5232            )
5233        );
5234    }
5235
5236    #[test]
5237    fn error_call_stack_reflects_active_frames_at_fault() {
5238        // Build callee: immediately does a BranchInd to address 0 (always fails)
5239        let mut ctx = Context::new();
5240        qcode!(
5241            ctx,
5242            "
5243            fn callee:
5244            <entry1>
5245                # Branching to literal 0 — no block lives at address 0
5246                goto [i64 0];
5247
5248            fn caller:
5249            <entry2>
5250                call <callee>;
5251            "
5252        );
5253
5254        // Build caller: calls callee
5255        let mut emu = Emulator::from_function(&ctx, caller);
5256        let err = emu.run_function(caller).unwrap_err();
5257
5258        assert!(matches!(
5259            err.kind,
5260            EmulatorErrorKind::InvalidBlockAddress(0)
5261        ));
5262        assert_eq!(emu.call_stack(), &[caller, callee]);
5263    }
5264}