11use super :: { Reset , ReusableAllocations } ;
22use 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) ]
8897pub 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
102111impl Reset for EncodedOps {
@@ -108,13 +117,13 @@ impl Reset for EncodedOps {
108117impl 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 {
210228pub 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
215235impl 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}
494570impl < T > Clone for UpdateEncodedError < T > {
495571 fn clone ( & self ) -> Self {
496- Self {
497- kind : self . kind ,
498- marker : PhantomData ,
499- }
572+ * self
500573 }
501574}
502575impl < 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