Skip to content

Commit a56279d

Browse files
committed
integrate LabelRegistry into OpEncoder
1 parent d835fc2 commit a56279d

File tree

2 files changed

+183
-117
lines changed

2 files changed

+183
-117
lines changed

crates/wasmi/src/engine/translator/func/encoder.rs

Lines changed: 152 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,17 @@
11
use super::{Reset, ReusableAllocations};
22
use crate::{
33
core::FuelCostsProvider,
4-
engine::TranslationError,
4+
engine::{
5+
translator::{
6+
comparator::UpdateBranchOffset,
7+
func::{
8+
labels::{Label, ResolvedLabelUser},
9+
LabelRef,
10+
LabelRegistry,
11+
},
12+
},
13+
TranslationError,
14+
},
515
ir::{self, BlockFuel, BranchOffset, Encode as _, Op},
616
Engine,
717
Error,
@@ -84,19 +94,18 @@ impl<T> fmt::Debug for Pos<T> {
8494
}
8595

8696
#[derive(Debug, Default)]
87-
#[expect(unused)]
8897
pub struct EncodedOps {
8998
buffer: Vec<u8>,
9099
temp: Option<(BytePos, TempBuffer)>,
91100
}
92101

93102
/// The kind of temporary/scratch stored object.
94103
#[derive(Debug)]
95-
pub enum TempBuffer {
104+
enum TempBuffer {
96105
/// The temporary object is a [`BranchOffset`].
97-
BranchOffset(ir::BranchOffset),
106+
BranchOffset,
98107
/// The temporary object is a [`BlockFuel`].
99-
BlockFuel(ir::BlockFuel),
108+
BlockFuel,
100109
}
101110

102111
impl Reset for EncodedOps {
@@ -108,13 +117,13 @@ impl Reset for EncodedOps {
108117
impl EncodedOps {
109118
/// Returns the next [`BytePos`].
110119
#[must_use]
111-
pub fn next_pos(&self) -> BytePos {
120+
fn next_pos(&self) -> BytePos {
112121
BytePos::from(self.buffer.len())
113122
}
114123

115124
/// Takes the temporay buffer if any exists.
116125
#[must_use]
117-
pub fn take_temp(&mut self) -> Option<(BytePos, TempBuffer)> {
126+
fn take_temp(&mut self) -> Option<(BytePos, TempBuffer)> {
118127
self.temp.take()
119128
}
120129
}
@@ -139,16 +148,20 @@ impl ir::Encoder for EncodedOps {
139148
fn branch_offset(
140149
&mut self,
141150
pos: Self::Pos,
142-
branch_offset: BranchOffset,
151+
_branch_offset: BranchOffset,
143152
) -> Result<(), Self::Error> {
144153
debug_assert!(self.temp.is_none());
145-
self.temp = Some((pos, TempBuffer::BranchOffset(branch_offset)));
154+
self.temp = Some((pos, TempBuffer::BranchOffset));
146155
Ok(())
147156
}
148157

149-
fn block_fuel(&mut self, pos: Self::Pos, block_fuel: ir::BlockFuel) -> Result<(), Self::Error> {
158+
fn block_fuel(
159+
&mut self,
160+
pos: Self::Pos,
161+
_block_fuel: ir::BlockFuel,
162+
) -> Result<(), Self::Error> {
150163
debug_assert!(self.temp.is_none());
151-
self.temp = Some((pos, TempBuffer::BlockFuel(block_fuel)));
164+
self.temp = Some((pos, TempBuffer::BlockFuel));
152165
Ok(())
153166
}
154167
}
@@ -169,6 +182,8 @@ pub struct OpEncoder {
169182
fuel_costs: Option<FuelCostsProvider>,
170183
/// The list of constructed instructions and their parameters.
171184
ops: EncodedOps,
185+
/// Labels and label users for control flow and encoded branch operators.
186+
labels: LabelRegistry,
172187
}
173188

174189
/// The staged [`Op`] and information about its fuel consumption.
@@ -201,7 +216,10 @@ impl ReusableAllocations for OpEncoder {
201216
type Allocations = OpEncoderAllocations;
202217

203218
fn into_allocations(self) -> Self::Allocations {
204-
Self::Allocations { ops: self.ops }
219+
Self::Allocations {
220+
ops: self.ops,
221+
labels: self.labels,
222+
}
205223
}
206224
}
207225

@@ -210,11 +228,14 @@ impl ReusableAllocations for OpEncoder {
210228
pub struct OpEncoderAllocations {
211229
/// The list of constructed instructions and their parameters.
212230
ops: EncodedOps,
231+
/// Labels and label users for control flow and encoded branch operators.
232+
labels: LabelRegistry,
213233
}
214234

215235
impl Reset for OpEncoderAllocations {
216236
fn reset(&mut self) {
217237
self.ops.reset();
238+
self.labels.reset();
218239
}
219240
}
220241

@@ -230,13 +251,59 @@ impl OpEncoder {
230251
staged: None,
231252
fuel_costs,
232253
ops: alloc.ops,
254+
labels: alloc.labels,
233255
}
234256
}
235257

236-
/// Returns the [`Pos<Op>`] of the currently staged or next encoded item.
237-
pub fn next_pos(&self) -> Pos<Op> {
238-
// TODO: we should probably remove this API again from `OpEncoder`
239-
Pos::from(self.ops.next_pos())
258+
/// Allocates a new unpinned [`Label`].
259+
pub fn new_label(&mut self) -> LabelRef {
260+
self.labels.new_label()
261+
}
262+
263+
/// Pins the [`Label`] at `lref` to the current encoded bytestream position.
264+
///
265+
/// # Panics
266+
///
267+
/// If there is a staged [`Op`].
268+
pub fn pin_label(&mut self, lref: LabelRef) {
269+
assert!(self.staged.is_none());
270+
let next_pos = Pos::from(self.ops.next_pos());
271+
self.labels.pin_label(lref, next_pos);
272+
}
273+
274+
/// Pins the [`Label`] at `lref` to the current encoded bytestream position if unpinned.
275+
///
276+
/// # Note
277+
///
278+
/// Does nothing if the label is already pinned.
279+
///
280+
/// # Panics
281+
///
282+
/// If there is a staged [`Op`].
283+
pub fn pin_label_if_unpinned(&mut self, lref: LabelRef) {
284+
assert!(self.staged.is_none());
285+
let next_pos = Pos::from(self.ops.next_pos());
286+
self.labels.pin_label_if_unpinned(lref, next_pos);
287+
}
288+
289+
/// Resolves the [`BranchOffset`] to `lref` from the current encoded bytestream position if `lref` is pinned.
290+
///
291+
///
292+
/// # Note
293+
///
294+
/// Returns an uninitialized [`BranchOffset`] if `lref` refers to an unpinned [`Label`].
295+
///
296+
/// # Panics
297+
///
298+
/// If there is a staged [`Op`].
299+
fn try_resolve_label(&mut self, lref: LabelRef) -> Result<BranchOffset, Error> {
300+
assert!(self.staged.is_none());
301+
let src = self.ops.next_pos();
302+
let offset = match self.labels.get_label(lref) {
303+
Label::Pinned(dst) => trace_branch_offset(src, dst)?,
304+
Label::Unpinned => BranchOffset::uninit(),
305+
};
306+
Ok(offset)
240307
}
241308

242309
/// Returns the staged [`Op`] if any.
@@ -361,7 +428,7 @@ impl OpEncoder {
361428
let consumed_fuel = BlockFuel::from(fuel_costs.base());
362429
self.try_encode_staged()?;
363430
Op::consume_fuel(consumed_fuel).encode(&mut self.ops)?;
364-
let Some((pos, TempBuffer::BlockFuel(_))) = self.ops.take_temp() else {
431+
let Some((pos, TempBuffer::BlockFuel)) = self.ops.take_temp() else {
365432
unreachable!("expected encoded `BlockFuel` entry but found none")
366433
};
367434
debug_assert!(self.staged.is_none());
@@ -373,19 +440,28 @@ impl OpEncoder {
373440
/// # Note
374441
///
375442
/// Bumps the `fuel` of the [`Op::ConsumeFuel`] accordingly.
376-
pub fn encode_branch<T: ir::Encode>(
443+
pub fn encode_branch<T>(
377444
&mut self,
378-
item: T,
445+
dst: LabelRef,
446+
make_branch: impl FnOnce(BranchOffset) -> T,
379447
fuel_pos: Option<Pos<BlockFuel>>,
380448
fuel_selector: impl FuelCostsSelector,
381-
) -> Result<(Pos<T>, Pos<BranchOffset>), Error> {
449+
) -> Result<(Pos<T>, Pos<BranchOffset>), Error>
450+
where
451+
T: ir::Encode + UpdateBranchOffset,
452+
{
382453
self.try_encode_staged()?;
383454
self.bump_fuel_consumption(fuel_pos, fuel_selector)?;
455+
let offset = self.try_resolve_label(dst)?;
456+
let item = make_branch(offset);
384457
let pos_item = self.encode_impl(item)?;
385458
let pos_offset = match self.ops.take_temp() {
386-
Some((pos, TempBuffer::BranchOffset(_))) => Pos::from(pos),
459+
Some((pos, TempBuffer::BranchOffset)) => Pos::from(pos),
387460
_ => panic!("missing encoded position for `BranchOffset`"),
388461
};
462+
if !self.labels.is_pinned(dst) {
463+
self.labels.new_user(dst, pos_item.value, pos_offset);
464+
}
389465
debug_assert!(self.staged.is_none());
390466
Ok((pos_item, pos_offset))
391467
}
@@ -405,21 +481,6 @@ impl OpEncoder {
405481
Ok(Pos::from(pos))
406482
}
407483

408-
/// Updates the encoded [`BranchOffset`] at `pos` to `offset`.
409-
///
410-
/// # Panics
411-
///
412-
/// - If `pos` was out of bounds for `self`.
413-
/// - If the [`BranchOffset`] at `pos` failed to be decoded, updated or re-encoded.
414-
pub fn update_branch_offset(
415-
&mut self,
416-
pos: Pos<BranchOffset>,
417-
offset: BranchOffset,
418-
) -> Result<(), Error> {
419-
self.update_encoded(pos, |_| Some(offset));
420-
Ok(())
421-
}
422-
423484
/// Bumps consumed fuel for [`Op::ConsumeFuel`] at `fuel_pos` by `fuel_selector(fuel_costs)`.
424485
///
425486
/// Does nothing if fuel metering is disabled.
@@ -461,10 +522,11 @@ impl OpEncoder {
461522
None => return Ok(()),
462523
Some(fuel_pos) => fuel_pos,
463524
};
464-
self.update_encoded(fuel_pos, |mut fuel| -> Option<BlockFuel> {
465-
fuel.bump_by(delta).ok()?;
466-
Some(fuel)
467-
});
525+
self.ops
526+
.update_encoded(fuel_pos, |mut fuel| -> Option<BlockFuel> {
527+
fuel.bump_by(delta).ok()?;
528+
Some(fuel)
529+
});
468530
Ok(())
469531
}
470532

@@ -473,6 +535,20 @@ impl OpEncoder {
473535
debug_assert!(self.staged.is_none());
474536
&self.ops.buffer[..]
475537
}
538+
539+
/// Updates the branch offsets of all branch instructions inplace.
540+
///
541+
/// # Panics
542+
///
543+
/// If this is used before all branching labels have been pinned.
544+
pub fn update_branch_offsets(&mut self) -> Result<(), Error> {
545+
for user in self.labels.resolved_users() {
546+
let ResolvedLabelUser { src, dst, pos } = user;
547+
let offset = trace_branch_offset(src, dst)?;
548+
self.ops.update_branch_offset(pos, offset)?;
549+
}
550+
Ok(())
551+
}
476552
}
477553

478554
/// Error indicating that in-place updating of encoded items failed.
@@ -493,10 +569,7 @@ impl<T> From<UpdateEncodedErrorKind> for UpdateEncodedError<T> {
493569
}
494570
impl<T> Clone for UpdateEncodedError<T> {
495571
fn clone(&self) -> Self {
496-
Self {
497-
kind: self.kind,
498-
marker: PhantomData,
499-
}
572+
*self
500573
}
501574
}
502575
impl<T> Copy for UpdateEncodedError<T> {}
@@ -534,7 +607,22 @@ enum UpdateEncodedErrorKind {
534607
FailedToUpdateEncoded,
535608
}
536609

537-
impl OpEncoder {
610+
impl EncodedOps {
611+
/// Updates the encoded [`BranchOffset`] at `pos` to `offset`.
612+
///
613+
/// # Panics
614+
///
615+
/// - If `pos` was out of bounds for `self`.
616+
/// - If the [`BranchOffset`] at `pos` failed to be decoded, updated or re-encoded.
617+
pub fn update_branch_offset(
618+
&mut self,
619+
pos: Pos<BranchOffset>,
620+
offset: BranchOffset,
621+
) -> Result<(), Error> {
622+
self.update_encoded(pos, |_| Some(offset));
623+
Ok(())
624+
}
625+
538626
/// Updates an encoded value `v` of type `T` at `pos` in-place using the result of `f(v)`.
539627
///
540628
/// # Panics
@@ -571,7 +659,7 @@ impl OpEncoder {
571659
T: ir::Decode + ir::Encode,
572660
{
573661
let at = usize::from(BytePos::from(pos));
574-
let Some(buffer) = self.ops.buffer.get_mut(at..) else {
662+
let Some(buffer) = self.buffer.get_mut(at..) else {
575663
return Err(UpdateEncodedErrorKind::BufferOutOfBounds);
576664
};
577665
let Ok(decoded) = T::decode(&mut &buffer[..]) else {
@@ -668,3 +756,21 @@ fn encode_op_code<E: ir::Encoder>(encoder: &mut E, code: ir::OpCode) -> Result<E
668756
// are defined in the Wasmi executor and available to the translator.
669757
u16::from(code).encode(encoder)
670758
}
759+
760+
/// Creates an initialized [`BranchOffset`] from `src` to `dst`.
761+
///
762+
/// # Errors
763+
///
764+
/// If the resulting [`BranchOffset`] is out of bounds.
765+
fn trace_branch_offset(src: BytePos, dst: Pos<Op>) -> Result<BranchOffset, Error> {
766+
fn trace_offset_or_none(src: BytePos, dst: BytePos) -> Option<BranchOffset> {
767+
let src = isize::try_from(usize::from(src)).ok()?;
768+
let dst = isize::try_from(usize::from(dst)).ok()?;
769+
let offset = dst.checked_sub(src)?;
770+
i32::try_from(offset).map(BranchOffset::from).ok()
771+
}
772+
let Some(offset) = trace_offset_or_none(src, BytePos::from(dst)) else {
773+
return Err(Error::from(TranslationError::BranchOffsetOutOfBounds));
774+
};
775+
Ok(offset)
776+
}

0 commit comments

Comments
 (0)