wasmi 2.0.0-beta.2

WebAssembly interpreter
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
use super::{Reset, ReusableAllocations};
use crate::{
    Engine,
    Error,
    core::FuelCostsProvider,
    engine::{
        TranslationError,
        executor::op_code_to_handler,
        translator::{
            comparator::UpdateBranchOffset,
            func::{
                LabelRef,
                LabelRegistry,
                labels::{Label, ResolvedLabelUser},
            },
        },
    },
    ir::{self, BlockFuel, BranchOffset, Encode as _, Op, OpCode},
};
use alloc::vec::Vec;
use core::{cmp, fmt, marker::PhantomData, mem};

/// Fuel amount required by certain operators.
type FuelUsed = u64;

/// A byte position within the encoded byte buffer.
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct BytePos(usize);

impl From<usize> for BytePos {
    fn from(index: usize) -> Self {
        Self(index)
    }
}

impl From<BytePos> for usize {
    fn from(pos: BytePos) -> Self {
        pos.0
    }
}

/// A position within the encoded byte buffer and its known encoded type.
pub struct Pos<T> {
    /// The underlying byte position.
    value: BytePos,
    /// The type marker denoting what value type has been encoded.
    marker: PhantomData<fn() -> T>,
}

impl<T> From<BytePos> for Pos<T> {
    fn from(value: BytePos) -> Self {
        Self {
            value,
            marker: PhantomData,
        }
    }
}
impl<T> From<Pos<T>> for BytePos {
    fn from(pos: Pos<T>) -> Self {
        pos.value
    }
}
impl<T> Copy for Pos<T> {}
impl<T> Clone for Pos<T> {
    fn clone(&self) -> Self {
        *self
    }
}
impl<T> PartialEq for Pos<T> {
    fn eq(&self, other: &Self) -> bool {
        self.value == other.value
    }
}
impl<T> Eq for Pos<T> {}
impl<T> PartialOrd for Pos<T> {
    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
        Some(self.cmp(other))
    }
}
impl<T> Ord for Pos<T> {
    fn cmp(&self, other: &Self) -> cmp::Ordering {
        self.value.cmp(&other.value)
    }
}
impl<T> fmt::Debug for Pos<T> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("Pos")
            .field("value", &self.value)
            .field("marker", &self.marker)
            .finish()
    }
}

#[derive(Debug, Default)]
pub struct EncodedOps {
    buffer: Vec<u8>,
    temp: Option<ReportingPos>,
}

/// A [`Pos`] of an encoded item that needs to be reported back.
#[derive(Debug)]
enum ReportingPos {
    /// The temporary object is a [`BranchOffset`].
    BranchOffset(Pos<BranchOffset>),
    /// The temporary object is a [`BlockFuel`].
    BlockFuel(Pos<BlockFuel>),
}

impl Reset for EncodedOps {
    fn reset(&mut self) {
        self.buffer.clear();
    }
}

impl EncodedOps {
    /// Returns the next [`BytePos`].
    #[must_use]
    fn next_pos(&self) -> BytePos {
        BytePos::from(self.buffer.len())
    }

    /// Takes the reporting [`Pos`] if any exists.
    #[must_use]
    fn take_reporting_pos(&mut self) -> Option<ReportingPos> {
        self.temp.take()
    }
}

impl ir::Encoder for EncodedOps {
    type Pos = BytePos;
    type Error = TranslationError;

    fn write_bytes(&mut self, bytes: &[u8]) -> Result<Self::Pos, Self::Error> {
        let pos = self.buffer.len();
        if self.buffer.try_reserve(bytes.len()).is_err() {
            return Err(TranslationError::OutOfSystemMemory);
        }
        self.buffer.extend(bytes);
        Ok(BytePos::from(pos))
    }

    fn encode_op_code(&mut self, code: OpCode) -> Result<Self::Pos, Self::Error> {
        encode_op_code(self, code)
    }

    fn branch_offset(
        &mut self,
        pos: Self::Pos,
        _branch_offset: BranchOffset,
    ) -> Result<(), Self::Error> {
        debug_assert!(self.temp.is_none());
        self.temp = Some(ReportingPos::BranchOffset(pos.into()));
        Ok(())
    }

    fn block_fuel(
        &mut self,
        pos: Self::Pos,
        _block_fuel: ir::BlockFuel,
    ) -> Result<(), Self::Error> {
        debug_assert!(self.temp.is_none());
        self.temp = Some(ReportingPos::BlockFuel(pos.into()));
        Ok(())
    }
}

/// Creates and encodes the buffer of encoded [`Op`]s for a function.
#[derive(Debug, Default)]
pub struct OpEncoder {
    /// The currently staged [`Op`].
    ///
    /// # Note
    ///
    /// - This allows the last [`Op`] to be peeked, inspected and manipulated.
    /// - For example, this is useful to perform op-code fusion or adjusting the result slot.
    staged: Option<StagedOp>,
    /// The fuel costs of instructions.
    ///
    /// This is `Some` if fuel metering is enabled, otherwise `None`.
    fuel_costs: Option<FuelCostsProvider>,
    /// The list of constructed instructions and their parameters.
    ops: EncodedOps,
    /// Labels and label users for control flow and encoded branch operators.
    labels: LabelRegistry,
}

/// The staged [`Op`] and information about its fuel consumption.
#[derive(Debug, Copy, Clone)]
pub struct StagedOp {
    /// The staged [`Op`].
    op: Op,
    /// Fuel information for the staged [`Op`].
    ///
    /// - The [`Op::ConsumeFuel`] operator associated to the staged [`Op`] if any.
    /// - The fuel required by the staged [`Op`].
    fuel: Option<(Pos<BlockFuel>, FuelUsed)>,
}

impl StagedOp {
    /// Creates a new [`StagedOp`] from `op` and `fuel`.
    pub fn new(op: Op, fuel: Option<(Pos<BlockFuel>, FuelUsed)>) -> Self {
        Self { op, fuel }
    }

    /// Replaces the current [`Op`] with `op`.
    ///
    /// Returns the [`Op`] being replaced.
    pub fn replace(&mut self, op: Op) -> Op {
        mem::replace(&mut self.op, op)
    }
}

impl ReusableAllocations for OpEncoder {
    type Allocations = OpEncoderAllocations;

    fn into_allocations(self) -> Self::Allocations {
        Self::Allocations {
            ops: self.ops,
            labels: self.labels,
        }
    }
}

/// The reusable heap allocations of the [`OpEncoder`].
#[derive(Debug, Default)]
pub struct OpEncoderAllocations {
    /// The list of constructed instructions and their parameters.
    ops: EncodedOps,
    /// Labels and label users for control flow and encoded branch operators.
    labels: LabelRegistry,
}

impl Reset for OpEncoderAllocations {
    fn reset(&mut self) {
        self.ops.reset();
        self.labels.reset();
    }
}

impl OpEncoder {
    /// Creates a new [`OpEncoder`].
    pub fn new(engine: &Engine, alloc: OpEncoderAllocations) -> Self {
        let config = engine.config();
        let fuel_costs = config
            .get_consume_fuel()
            .then(|| config.fuel_costs())
            .cloned();
        Self {
            staged: None,
            fuel_costs,
            ops: alloc.ops,
            labels: alloc.labels,
        }
    }

    /// Allocates a new unpinned [`Label`].
    pub fn new_label(&mut self) -> LabelRef {
        self.labels.new_label()
    }

    /// Pins the [`Label`] at `lref` to the current encoded bytestream position.
    ///
    /// # Panics
    ///
    /// If there is a staged [`Op`].
    pub fn pin_label(&mut self, lref: LabelRef) -> Result<(), Error> {
        self.try_encode_staged()?;
        let next_pos = Pos::from(self.ops.next_pos());
        self.labels.pin_label(lref, next_pos);
        Ok(())
    }

    /// Pins the [`Label`] at `lref` to the current encoded bytestream position if unpinned.
    ///
    /// # Note
    ///
    /// Does nothing if the label is already pinned.
    ///
    /// # Panics
    ///
    /// If there is a staged [`Op`].
    pub fn pin_label_if_unpinned(&mut self, lref: LabelRef) -> Result<(), Error> {
        self.try_encode_staged()?;
        let next_pos = Pos::from(self.ops.next_pos());
        self.labels.pin_label_if_unpinned(lref, next_pos);
        Ok(())
    }

    /// Resolves the [`BranchOffset`] to `lref` from the current encoded bytestream position if `lref` is pinned.
    ///
    ///
    /// # Note
    ///
    /// Returns an uninitialized [`BranchOffset`] if `lref` refers to an unpinned [`Label`].
    ///
    /// # Panics
    ///
    /// If there is a staged [`Op`].
    fn try_resolve_label(&mut self, lref: LabelRef) -> Result<BranchOffset, Error> {
        assert!(self.staged.is_none());
        let src = self.ops.next_pos();
        let offset = match self.labels.get_label(lref) {
            Label::Pinned(dst) => trace_branch_offset(src, dst)?,
            Label::Unpinned => BranchOffset::uninit(),
        };
        Ok(offset)
    }

    /// Returns the staged [`Op`] if any.
    pub fn peek_staged(&self) -> Option<Op> {
        self.staged.map(|staged| staged.op)
    }

    /// Sets the staged [`Op`] to `new_staged` and encodes the previously staged [`Op`] if any.
    ///
    /// Returns the [`Pos<Op>`] of the staged [`Op`] if it was encoded.
    pub fn stage(
        &mut self,
        new_staged: Op,
        fuel_op: Option<Pos<BlockFuel>>,
        fuel_selector: impl FuelCostsSelector,
    ) -> Result<Pos<Op>, Error> {
        let fuel = match (fuel_op, &self.fuel_costs) {
            (None, None) => None,
            (Some(fuel_op), Some(fuel_costs)) => Some((fuel_op, fuel_selector.select(fuel_costs))),
            _ => unreachable!(),
        };
        let new_staged = StagedOp::new(new_staged, fuel);
        if let Some(old_staged) = self.staged.replace(new_staged) {
            self.encode_staged(old_staged)?;
        }
        Ok(Pos::from(self.ops.next_pos()))
    }

    /// Encodes the staged [`Op`] if there is any.
    ///
    /// # Note
    ///
    /// - After this operation there will be no more staged [`Op`].
    /// - Does nothing if there is no staged [`Op`].
    pub fn try_encode_staged(&mut self) -> Result<(), Error> {
        if let Some(staged) = self.staged.take() {
            self.encode_staged(staged)?;
        }
        debug_assert!(self.staged.is_none());
        Ok(())
    }

    /// Encodes the `staged_op`.
    ///
    /// - Bumps fuel consumption of the associated [`Op::ConsumeFuel`] operator.
    /// - Returns the [`Pos<Op>`] of the encoded [`StagedOp`].
    ///
    /// # Panics (Debug)
    ///
    /// If the staged operator unexpectedly issued [`BranchOffset`] or [`BlockFuel`] fields.
    /// Those operators may never be staged and must be taken care of directly.
    fn encode_staged(&mut self, staged: StagedOp) -> Result<Pos<Op>, Error> {
        if let Some((fuel_pos, fuel_used)) = staged.fuel {
            self.bump_fuel_consumption_by(Some(fuel_pos), fuel_used)?;
        }
        let pos = self.encode_impl(staged.op)?;
        debug_assert!(self.ops.temp.is_none());
        Ok(pos)
    }

    /// Drops the staged [`Op`] without encoding it.
    ///
    /// Returns the staged [`Op`]'s fuel information or `None` if fuel metering is disabled.
    ///
    /// # Panics
    ///
    /// If there was no staged [`Op`].
    pub fn drop_staged(&mut self) -> (Option<Pos<BlockFuel>>, FuelUsed) {
        let Some(staged) = self.staged.take() else {
            panic!("could not drop staged `Op` since there was none")
        };
        debug_assert!(self.staged.is_none());
        let fuel_pos = staged.fuel.map(|(pos, _)| pos);
        let fuel_used = staged.fuel.map(|(_, used)| used).unwrap_or(0);
        (fuel_pos, fuel_used)
    }

    /// Replaces the staged [`Op`] with `new_staged`.
    ///
    /// - This does __not__ encode the currently staged [`Op`] but merely replaces it.
    /// - Returns the [`Pos<Op>`] of the newly staged [`Op`].
    ///
    /// # Panics (Debug)
    ///
    /// If there currently is no staged [`Op`] that can be replaced.
    pub fn replace_staged(&mut self, new_staged: Op) -> Result<Pos<Op>, Error> {
        let Some(staged) = self.staged.as_mut() else {
            panic!("expected a staged `Op` but found `None`")
        };
        staged.replace(new_staged);
        Ok(Pos::from(self.ops.next_pos()))
    }

    /// Encodes an item of type `T` to the [`OpEncoder`] and returns its [`Pos`].
    ///
    /// # Note
    ///
    /// Bumps the `fuel` of the [`Op::ConsumeFuel`] accordingly.
    pub fn encode<T: ir::Encode>(
        &mut self,
        op: T,
        fuel_pos: Option<Pos<BlockFuel>>,
        fuel_selector: impl FuelCostsSelector,
    ) -> Result<Pos<T>, Error> {
        self.try_encode_staged()?;
        self.bump_fuel_consumption(fuel_pos, fuel_selector)?;
        let pos = self.encode_impl(op)?;
        debug_assert!(self.ops.take_reporting_pos().is_none());
        debug_assert!(self.staged.is_none());
        Ok(pos)
    }

    /// Encodes an [`Op::ConsumeFuel`] operator to `self`.
    ///
    /// # Note
    ///
    /// The pushed [`Op::ConsumeFuel`] is initialized with base fuel costs.
    pub fn encode_consume_fuel(&mut self) -> Result<Option<Pos<BlockFuel>>, Error> {
        let Some(fuel_costs) = &self.fuel_costs else {
            return Ok(None);
        };
        let consumed_fuel = BlockFuel::from(fuel_costs.base());
        self.try_encode_staged()?;
        Op::consume_fuel(consumed_fuel).encode(&mut self.ops)?;
        let Some(ReportingPos::BlockFuel(pos)) = self.ops.take_reporting_pos() else {
            unreachable!("expected encoded `BlockFuel` entry but found none")
        };
        debug_assert!(self.staged.is_none());
        Ok(Some(pos))
    }

    /// Encodes a type with [`BranchOffset`] to the [`OpEncoder`] and returns its [`Pos<Op>`] and [`Pos<BranchOffset>`].
    ///
    /// # Note
    ///
    /// Bumps the `fuel` of the [`Op::ConsumeFuel`] accordingly.
    pub fn encode_branch<T>(
        &mut self,
        dst: LabelRef,
        make_branch: impl FnOnce(BranchOffset) -> T,
        fuel_pos: Option<Pos<BlockFuel>>,
        fuel_selector: impl FuelCostsSelector,
    ) -> Result<(Pos<T>, Pos<BranchOffset>), Error>
    where
        T: ir::Encode + UpdateBranchOffset,
    {
        self.try_encode_staged()?;
        self.bump_fuel_consumption(fuel_pos, fuel_selector)?;
        let offset = self.try_resolve_label(dst)?;
        let item = make_branch(offset);
        let pos_item = self.encode_impl(item)?;
        let pos_offset = match self.ops.take_reporting_pos() {
            Some(ReportingPos::BranchOffset(pos)) => pos,
            _ => panic!("missing encoded position for `BranchOffset`"),
        };
        if !self.labels.is_pinned(dst) {
            self.labels
                .new_user(dst, BytePos::from(pos_item), pos_offset);
        }
        debug_assert!(self.staged.is_none());
        Ok((pos_item, pos_offset))
    }

    /// Encodes an [`Op`] to the [`OpEncoder`] and returns its [`Pos<Op>`].
    ///
    /// # Note
    ///
    /// - Encodes `last` [`Op`] prior to `op` if `last` is `Some`.
    /// - After this call `last` will yield `None`.
    fn encode_impl<T>(&mut self, op: T) -> Result<Pos<T>, Error>
    where
        T: ir::Encode,
    {
        let pos = self.ops.next_pos();
        op.encode(&mut self.ops)?;
        Ok(Pos::from(pos))
    }

    /// Bumps consumed fuel for [`Op::ConsumeFuel`] at `fuel_pos` by `fuel_selector(fuel_costs)`.
    ///
    /// Does nothing if fuel metering is disabled.
    ///
    /// # Errors
    ///
    /// If consumed fuel is out of bounds after this operation.
    fn bump_fuel_consumption(
        &mut self,
        fuel_pos: Option<Pos<BlockFuel>>,
        fuel_selector: impl FuelCostsSelector,
    ) -> Result<(), Error> {
        debug_assert_eq!(fuel_pos.is_some(), self.fuel_costs.is_some());
        let fuel_used = self
            .fuel_costs
            .as_ref()
            .map(|costs| fuel_selector.select(costs))
            .unwrap_or(0);
        if fuel_used == 0 {
            return Ok(());
        }
        self.bump_fuel_consumption_by(fuel_pos, fuel_used)
    }

    /// Bumps consumed fuel for [`Op::ConsumeFuel`] at `fuel_pos` by `delta`.
    ///
    /// Does nothing if fuel metering is disabled.
    ///
    /// # Errors
    ///
    /// If consumed fuel is out of bounds after this operation.
    fn bump_fuel_consumption_by(
        &mut self,
        fuel_pos: Option<Pos<BlockFuel>>,
        delta: FuelUsed,
    ) -> Result<(), Error> {
        debug_assert_eq!(fuel_pos.is_some(), self.fuel_costs.is_some());
        let fuel_pos = match fuel_pos {
            None => return Ok(()),
            Some(fuel_pos) => fuel_pos,
        };
        self.ops
            .update_encoded(fuel_pos, |mut fuel| -> Option<BlockFuel> {
                fuel.bump_by(delta).ok()?;
                Some(fuel)
            });
        Ok(())
    }

    /// Returns an iterator yielding all encoded [`Op`]s of the [`OpEncoder`] as bytes.
    pub fn encoded_ops(&self) -> &[u8] {
        debug_assert!(self.staged.is_none());
        &self.ops.buffer[..]
    }

    /// Updates the branch offsets of all branch instructions inplace.
    ///
    /// # Panics
    ///
    /// If this is used before all branching labels have been pinned.
    pub fn update_branch_offsets(&mut self) -> Result<(), Error> {
        for user in self.labels.resolved_users() {
            let ResolvedLabelUser { src, dst, pos } = user;
            let offset = trace_branch_offset(src, dst)?;
            self.ops.update_branch_offset(pos, offset)?;
        }
        Ok(())
    }
}

/// Error indicating that in-place updating of encoded items failed.
struct UpdateEncodedError<T> {
    /// The underlying kind of error.
    kind: UpdateEncodedErrorKind,
    /// The type that is decoded, updated and re-encoded.
    marker: PhantomData<fn() -> T>,
}

impl<T> From<UpdateEncodedErrorKind> for UpdateEncodedError<T> {
    fn from(kind: UpdateEncodedErrorKind) -> Self {
        Self {
            kind,
            marker: PhantomData,
        }
    }
}
impl<T> Clone for UpdateEncodedError<T> {
    fn clone(&self) -> Self {
        *self
    }
}
impl<T> Copy for UpdateEncodedError<T> {}
impl<T> fmt::Debug for UpdateEncodedError<T> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("UpdateEncodedError")
            .field("kind", &self.kind)
            .field("marker", &self.marker)
            .finish()
    }
}
impl<T> fmt::Display for UpdateEncodedError<T> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let message = match self.kind {
            UpdateEncodedErrorKind::BufferOutOfBounds => "buffer out of bounds",
            UpdateEncodedErrorKind::FailedToDecode => "failed to decode",
            UpdateEncodedErrorKind::FailedToEncode => "failed to encode",
            UpdateEncodedErrorKind::FailedToUpdateEncoded => "failed to update encoded",
        };
        let type_name = core::any::type_name::<T>();
        write!(f, "{message}: {type_name}")
    }
}

/// Kinds of errors indicating that in-place updating of encoded items failed.
#[derive(Debug, Copy, Clone)]
enum UpdateEncodedErrorKind {
    /// Buffer is out of bounds for the position of the update.
    BufferOutOfBounds,
    /// Failed to decode the encoded item.
    FailedToDecode,
    /// Failed to encode the updated item.
    FailedToEncode,
    /// Failed to update the encoded item.
    FailedToUpdateEncoded,
}

impl EncodedOps {
    /// Updates the encoded [`BranchOffset`] at `pos` to `offset`.
    ///
    /// # Panics
    ///
    /// - If `pos` was out of bounds for `self`.
    /// - If the [`BranchOffset`] at `pos` failed to be decoded, updated or re-encoded.
    pub fn update_branch_offset(
        &mut self,
        pos: Pos<BranchOffset>,
        offset: BranchOffset,
    ) -> Result<(), Error> {
        self.update_encoded(pos, |_| Some(offset));
        Ok(())
    }

    /// Updates an encoded value `v` of type `T` at `pos` in-place using the result of `f(v)`.
    ///
    /// # Panics
    ///
    /// - If the underlying bytes buffer is out of bounds for `pos`.
    /// - If decodiing of `T` at `pos` fails.
    /// - If encodiing of `T` at `pos` fails.
    fn update_encoded<T>(&mut self, pos: Pos<T>, f: impl FnOnce(T) -> Option<T>)
    where
        T: ir::Encode + ir::Decode,
    {
        if let Err(error) = self
            .update_encoded_or_err(pos, f)
            .map_err(<UpdateEncodedError<T>>::from)
        {
            panic!("`OpEncoder::update_encoded` unexpectedly failed: {error}")
        }
    }

    /// Updates a value of type `T` at `pos` using `f` in the encoded buffer.
    ///
    /// # Errors
    ///
    /// - If the underlying bytes buffer is out of bounds for `pos`.
    /// - If decodiing of `T` at `pos` fails.
    /// - If encodiing of `T` at `pos` fails.
    /// - If `f(value)` returns `None` and thus updating failed.
    fn update_encoded_or_err<T>(
        &mut self,
        pos: Pos<T>,
        f: impl FnOnce(T) -> Option<T>,
    ) -> Result<(), UpdateEncodedErrorKind>
    where
        T: ir::Decode + ir::Encode,
    {
        let at = usize::from(BytePos::from(pos));
        let Some(buffer) = self.buffer.get_mut(at..) else {
            return Err(UpdateEncodedErrorKind::BufferOutOfBounds);
        };
        let Ok(decoded) = T::decode(&mut &buffer[..]) else {
            return Err(UpdateEncodedErrorKind::FailedToDecode);
        };
        let Some(updated) = f(decoded) else {
            return Err(UpdateEncodedErrorKind::FailedToUpdateEncoded);
        };
        if updated.encode(&mut SliceEncoder::from(buffer)).is_err() {
            return Err(UpdateEncodedErrorKind::FailedToEncode);
        }
        Ok(())
    }
}

/// Utility type to encode items to a slice of bytes.
pub struct SliceEncoder<'a> {
    /// The underlying bytes that will store the encoded items.
    bytes: &'a mut [u8],
}

/// An error that may occur upon encoding items to a byte slice.
#[derive(Debug, Copy, Clone)]
pub struct SliceEncoderError;

impl<'a> From<&'a mut [u8]> for SliceEncoder<'a> {
    fn from(bytes: &'a mut [u8]) -> Self {
        Self { bytes }
    }
}

impl<'a> ir::Encoder for SliceEncoder<'a> {
    type Pos = ();
    type Error = SliceEncoderError;

    fn write_bytes(&mut self, bytes: &[u8]) -> Result<Self::Pos, Self::Error> {
        let Some(buffer) = self.bytes.get_mut(..bytes.len()) else {
            return Err(SliceEncoderError);
        };
        buffer.copy_from_slice(bytes);
        Ok(())
    }

    fn encode_op_code(&mut self, code: OpCode) -> Result<Self::Pos, Self::Error> {
        encode_op_code(self, code)
    }

    fn branch_offset(
        &mut self,
        _pos: Self::Pos,
        _branch_offset: BranchOffset,
    ) -> Result<(), Self::Error> {
        Ok(())
    }

    fn block_fuel(&mut self, _pos: Self::Pos, _block_fuel: BlockFuel) -> Result<(), Self::Error> {
        Ok(())
    }
}

/// Convenience trait to wrap type usable as fuel costs selectors.
pub trait FuelCostsSelector {
    /// Selects the fuel usage from the [`FuelCostsProvider`].
    fn select(self, costs: &FuelCostsProvider) -> FuelUsed;
}

impl<T> FuelCostsSelector for T
where
    T: FnOnce(&FuelCostsProvider) -> FuelUsed,
{
    fn select(self, costs: &FuelCostsProvider) -> FuelUsed {
        self(costs)
    }
}

impl FuelCostsSelector for BlockFuel {
    fn select(self, _costs: &FuelCostsProvider) -> FuelUsed {
        FuelUsed::from(self)
    }
}

impl FuelCostsSelector for FuelUsed {
    fn select(self, _costs: &FuelCostsProvider) -> FuelUsed {
        self
    }
}

/// Encodes an [`OpCode`] to a generic [`ir::Encoder`].
fn encode_op_code<E: ir::Encoder>(encoder: &mut E, code: OpCode) -> Result<E::Pos, E::Error> {
    match cfg!(feature = "indirect-dispatch") {
        true => {
            // Note: encoding for indirect-threading
            //
            // The op-codes are not resolved during translation time and must
            // be resolved during execution time. This decreases memory footprint
            // of the encoded IR at the cost of execution performance.
            u16::from(code).encode(encoder)
        }
        false => {
            // Note: encoding for direct-threading
            //
            // The op-codes are resolved during translation time (now) to their
            // underlying function pointers. This increases memory footprint
            // of the encoded IR but improves execution performance.
            (op_code_to_handler(code) as usize).encode(encoder)
        }
    }
}

/// Creates an initialized [`BranchOffset`] from `src` to `dst`.
///
/// # Errors
///
/// If the resulting [`BranchOffset`] is out of bounds.
fn trace_branch_offset(src: BytePos, dst: Pos<Op>) -> Result<BranchOffset, Error> {
    fn trace_offset_or_none(src: BytePos, dst: BytePos) -> Option<BranchOffset> {
        let src = isize::try_from(usize::from(src)).ok()?;
        let dst = isize::try_from(usize::from(dst)).ok()?;
        let offset = dst.checked_sub(src)?;
        i32::try_from(offset).map(BranchOffset::from).ok()
    }
    let Some(offset) = trace_offset_or_none(src, BytePos::from(dst)) else {
        return Err(Error::from(TranslationError::BranchOffsetOutOfBounds));
    };
    Ok(offset)
}