rune/compile/
unit_builder.rs

1//! A single execution unit in the runestick virtual machine.
2//!
3//! A unit consists of a sequence of instructions, and lookaside tables for
4//! metadata like function locations.
5
6use core::fmt;
7
8use ::rust_alloc::sync::Arc;
9
10use crate::alloc::fmt::TryWrite;
11use crate::alloc::prelude::*;
12use crate::alloc::{self, try_format, Box, HashMap, String, Vec};
13use crate::ast::{Span, Spanned};
14use crate::compile::meta;
15use crate::compile::{self, Assembly, AssemblyInst, ErrorKind, Location, Pool, WithSpan};
16use crate::hash;
17use crate::query::QueryInner;
18use crate::runtime::debug::{DebugArgs, DebugSignature};
19use crate::runtime::unit::UnitEncoder;
20use crate::runtime::{
21    Call, ConstValue, DebugInfo, DebugInst, Inst, InstAddress, Label, Protocol, Rtti, RttiKind,
22    StaticString, Unit, UnitFn,
23};
24use crate::{Context, Diagnostics, Hash, Item, SourceId};
25
26/// Errors that can be raised when linking units.
27#[derive(Debug)]
28#[allow(missing_docs)]
29#[non_exhaustive]
30pub enum LinkerError {
31    MissingFunction {
32        hash: Hash,
33        spans: Vec<(Span, SourceId)>,
34    },
35}
36
37impl fmt::Display for LinkerError {
38    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
39        match self {
40            LinkerError::MissingFunction { hash, .. } => {
41                write!(f, "Missing function with hash {hash}")
42            }
43        }
44    }
45}
46
47impl core::error::Error for LinkerError {}
48
49/// Instructions from a single source file.
50#[derive(Debug, Default)]
51pub(crate) struct UnitBuilder {
52    /// Registered re-exports.
53    reexports: HashMap<Hash, Hash>,
54    /// Where functions are located in the collection of instructions.
55    functions: hash::Map<UnitFn>,
56    /// Function by address.
57    functions_rev: HashMap<usize, Hash>,
58    /// A static string.
59    static_strings: Vec<Arc<StaticString>>,
60    /// Reverse lookup for static strings.
61    static_string_rev: HashMap<Hash, usize>,
62    /// A static byte string.
63    static_bytes: Vec<Vec<u8>>,
64    /// Reverse lookup for static byte strings.
65    static_bytes_rev: HashMap<Hash, usize>,
66    /// Slots used for object keys.
67    ///
68    /// This is used when an object is used in a pattern match, to avoid having
69    /// to send the collection of keys to the virtual machine.
70    ///
71    /// All keys are sorted with the default string sort.
72    static_object_keys: Vec<Box<[String]>>,
73    /// Used to detect duplicates in the collection of static object keys.
74    static_object_keys_rev: HashMap<Hash, usize>,
75    /// A static string.
76    drop_sets: Vec<Arc<[InstAddress]>>,
77    /// Reverse lookup for drop sets.
78    drop_sets_rev: HashMap<Vec<InstAddress>, usize>,
79    /// Runtime type information for types.
80    rtti: hash::Map<Arc<Rtti>>,
81    /// The current label count.
82    label_count: usize,
83    /// A collection of required function hashes.
84    required_functions: HashMap<Hash, Vec<(Span, SourceId)>>,
85    /// Debug info if available for unit.
86    debug: Option<Box<DebugInfo>>,
87    /// Constant values
88    constants: hash::Map<ConstValue>,
89    /// Hash to identifiers.
90    hash_to_ident: HashMap<Hash, Box<str>>,
91}
92
93impl UnitBuilder {
94    /// Construct a new drop set.
95    pub(crate) fn drop_set(&mut self) -> DropSet<'_> {
96        DropSet {
97            builder: self,
98            addresses: Vec::new(),
99        }
100    }
101
102    /// Insert an identifier for debug purposes.
103    pub(crate) fn insert_debug_ident(&mut self, ident: &str) -> alloc::Result<()> {
104        self.hash_to_ident
105            .try_insert(Hash::ident(ident), ident.try_into()?)?;
106        Ok(())
107    }
108
109    /// Convert into a runtime unit, shedding our build metadata in the process.
110    ///
111    /// Returns `None` if the builder is still in use.
112    pub(crate) fn build<S>(mut self, span: Span, storage: S) -> compile::Result<Unit<S>> {
113        if let Some(debug) = &mut self.debug {
114            debug.functions_rev = self.functions_rev;
115            debug.hash_to_ident = self.hash_to_ident;
116        }
117
118        for (from, to) in self.reexports {
119            if let Some(info) = self.functions.get(&to) {
120                let info = *info;
121                if self
122                    .functions
123                    .try_insert(from, info)
124                    .with_span(span)?
125                    .is_some()
126                {
127                    return Err(compile::Error::new(
128                        span,
129                        ErrorKind::FunctionConflictHash { hash: from },
130                    ));
131                }
132                continue;
133            }
134
135            if let Some(value) = self.constants.get(&to) {
136                let const_value = value.try_clone()?;
137
138                if self
139                    .constants
140                    .try_insert(from, const_value)
141                    .with_span(span)?
142                    .is_some()
143                {
144                    return Err(compile::Error::new(
145                        span,
146                        ErrorKind::ConstantConflict { hash: from },
147                    ));
148                }
149
150                continue;
151            }
152
153            return Err(compile::Error::new(
154                span,
155                ErrorKind::MissingFunctionHash { hash: to },
156            ));
157        }
158
159        Ok(Unit::new(
160            storage,
161            self.functions,
162            self.static_strings,
163            self.static_bytes,
164            self.static_object_keys,
165            self.drop_sets,
166            self.rtti,
167            self.debug,
168            self.constants,
169        ))
170    }
171
172    /// Insert a static string and return its associated slot that can later be
173    /// looked up through [lookup_string][Unit::lookup_string].
174    ///
175    /// Only uses up space if the static string is unique.
176    pub(crate) fn new_static_string(
177        &mut self,
178        span: &dyn Spanned,
179        current: &str,
180    ) -> compile::Result<usize> {
181        let current = StaticString::new(current)?;
182        let hash = current.hash();
183
184        if let Some(existing_slot) = self.static_string_rev.get(&hash).copied() {
185            let Some(existing) = self.static_strings.get(existing_slot) else {
186                return Err(compile::Error::new(
187                    span,
188                    ErrorKind::StaticStringMissing {
189                        hash,
190                        slot: existing_slot,
191                    },
192                ));
193            };
194
195            if ***existing != *current {
196                return Err(compile::Error::new(
197                    span,
198                    ErrorKind::StaticStringHashConflict {
199                        hash,
200                        current: (*current).try_clone()?,
201                        existing: (***existing).try_clone()?,
202                    },
203                ));
204            }
205
206            return Ok(existing_slot);
207        }
208
209        let new_slot = self.static_strings.len();
210        self.static_strings.try_push(Arc::new(current))?;
211        self.static_string_rev.try_insert(hash, new_slot)?;
212        Ok(new_slot)
213    }
214
215    /// Insert a static byte string and return its associated slot that can
216    /// later be looked up through [lookup_bytes][Unit::lookup_bytes].
217    ///
218    /// Only uses up space if the static byte string is unique.
219    pub(crate) fn new_static_bytes(
220        &mut self,
221        span: &dyn Spanned,
222        current: &[u8],
223    ) -> compile::Result<usize> {
224        let hash = Hash::static_bytes(current);
225
226        if let Some(existing_slot) = self.static_bytes_rev.get(&hash).copied() {
227            let existing = self.static_bytes.get(existing_slot).ok_or_else(|| {
228                compile::Error::new(
229                    span,
230                    ErrorKind::StaticBytesMissing {
231                        hash,
232                        slot: existing_slot,
233                    },
234                )
235            })?;
236
237            if &**existing != current {
238                return Err(compile::Error::new(
239                    span,
240                    ErrorKind::StaticBytesHashConflict {
241                        hash,
242                        current: current.try_to_owned()?,
243                        existing: existing.try_clone()?,
244                    },
245                ));
246            }
247
248            return Ok(existing_slot);
249        }
250
251        let new_slot = self.static_bytes.len();
252        self.static_bytes.try_push(current.try_to_owned()?)?;
253        self.static_bytes_rev.try_insert(hash, new_slot)?;
254        Ok(new_slot)
255    }
256
257    /// Insert a new collection of static object keys, or return one already
258    /// existing.
259    pub(crate) fn new_static_object_keys_iter<I>(
260        &mut self,
261        span: &dyn Spanned,
262        current: I,
263    ) -> compile::Result<usize>
264    where
265        I: IntoIterator,
266        I::Item: AsRef<str>,
267    {
268        let current = current
269            .into_iter()
270            .map(|s| s.as_ref().try_to_owned())
271            .try_collect::<alloc::Result<Box<_>>>()??;
272
273        self.new_static_object_keys(span, current)
274    }
275
276    /// Insert a new collection of static object keys, or return one already
277    /// existing.
278    pub(crate) fn new_static_object_keys(
279        &mut self,
280        span: &dyn Spanned,
281        current: Box<[String]>,
282    ) -> compile::Result<usize> {
283        let hash = Hash::object_keys(&current[..]);
284
285        if let Some(existing_slot) = self.static_object_keys_rev.get(&hash).copied() {
286            let existing = self.static_object_keys.get(existing_slot).ok_or_else(|| {
287                compile::Error::new(
288                    span,
289                    ErrorKind::StaticObjectKeysMissing {
290                        hash,
291                        slot: existing_slot,
292                    },
293                )
294            })?;
295
296            if *existing != current {
297                return Err(compile::Error::new(
298                    span,
299                    ErrorKind::StaticObjectKeysHashConflict {
300                        hash,
301                        current,
302                        existing: existing.try_clone()?,
303                    },
304                ));
305            }
306
307            return Ok(existing_slot);
308        }
309
310        let new_slot = self.static_object_keys.len();
311        self.static_object_keys.try_push(current)?;
312        self.static_object_keys_rev.try_insert(hash, new_slot)?;
313        Ok(new_slot)
314    }
315
316    /// Declare a new struct.
317    pub(crate) fn insert_meta(
318        &mut self,
319        span: &dyn Spanned,
320        meta: &meta::Meta,
321        pool: &Pool,
322        query: &mut QueryInner,
323    ) -> compile::Result<()> {
324        debug_assert_eq! {
325            pool.item_type_hash(meta.item_meta.item),
326            meta.hash,
327        };
328
329        match meta.kind {
330            meta::Kind::Type { .. } => {
331                let rtti = Arc::new(Rtti {
332                    kind: RttiKind::Empty,
333                    hash: meta.hash,
334                    variant_hash: Hash::EMPTY,
335                    item: pool.item(meta.item_meta.item).try_to_owned()?,
336                    fields: HashMap::default(),
337                });
338
339                self.constants
340                    .try_insert(
341                        Hash::associated_function(meta.hash, &Protocol::INTO_TYPE_NAME),
342                        ConstValue::from(rtti.item.try_to_string()?),
343                    )
344                    .with_span(span)?;
345
346                if self
347                    .rtti
348                    .try_insert(meta.hash, rtti)
349                    .with_span(span)?
350                    .is_some()
351                {
352                    return Err(compile::Error::new(
353                        span,
354                        ErrorKind::TypeRttiConflict { hash: meta.hash },
355                    ));
356                }
357            }
358            meta::Kind::Struct {
359                fields: meta::Fields::Empty,
360                enum_hash: Hash::EMPTY,
361                ..
362            } => {
363                let info = UnitFn::EmptyStruct { hash: meta.hash };
364
365                let signature = DebugSignature::new(
366                    pool.item(meta.item_meta.item).try_to_owned()?,
367                    DebugArgs::EmptyArgs,
368                );
369
370                let rtti = Arc::new(Rtti {
371                    kind: RttiKind::Empty,
372                    hash: meta.hash,
373                    variant_hash: Hash::EMPTY,
374                    item: pool.item(meta.item_meta.item).try_to_owned()?,
375                    fields: HashMap::default(),
376                });
377
378                if self
379                    .rtti
380                    .try_insert(meta.hash, rtti)
381                    .with_span(span)?
382                    .is_some()
383                {
384                    return Err(compile::Error::new(
385                        span,
386                        ErrorKind::TypeRttiConflict { hash: meta.hash },
387                    ));
388                }
389
390                if self
391                    .functions
392                    .try_insert(meta.hash, info)
393                    .with_span(span)?
394                    .is_some()
395                {
396                    return Err(compile::Error::new(
397                        span,
398                        ErrorKind::FunctionConflict {
399                            existing: signature,
400                        },
401                    ));
402                }
403
404                self.constants
405                    .try_insert(
406                        Hash::associated_function(meta.hash, &Protocol::INTO_TYPE_NAME),
407                        ConstValue::from(signature.path.try_to_string()?),
408                    )
409                    .with_span(span)?;
410
411                self.debug_mut()?
412                    .functions
413                    .try_insert(meta.hash, signature)?;
414            }
415            meta::Kind::Struct {
416                fields: meta::Fields::Empty,
417                enum_hash,
418                ..
419            } => {
420                let rtti = Arc::new(Rtti {
421                    kind: RttiKind::Empty,
422                    hash: enum_hash,
423                    variant_hash: meta.hash,
424                    item: pool.item(meta.item_meta.item).try_to_owned()?,
425                    fields: HashMap::default(),
426                });
427
428                if self
429                    .rtti
430                    .try_insert(meta.hash, rtti)
431                    .with_span(span)?
432                    .is_some()
433                {
434                    return Err(compile::Error::new(
435                        span,
436                        ErrorKind::RttiConflict { hash: meta.hash },
437                    ));
438                }
439
440                let info = UnitFn::EmptyStruct { hash: meta.hash };
441
442                let signature = DebugSignature::new(
443                    pool.item(meta.item_meta.item).try_to_owned()?,
444                    DebugArgs::EmptyArgs,
445                );
446
447                if self
448                    .functions
449                    .try_insert(meta.hash, info)
450                    .with_span(span)?
451                    .is_some()
452                {
453                    return Err(compile::Error::new(
454                        span,
455                        ErrorKind::FunctionConflict {
456                            existing: signature,
457                        },
458                    ));
459                }
460
461                self.debug_mut()?
462                    .functions
463                    .try_insert(meta.hash, signature)?;
464            }
465            meta::Kind::Struct {
466                fields: meta::Fields::Unnamed(args),
467                enum_hash: Hash::EMPTY,
468                ..
469            } => {
470                let info = UnitFn::TupleStruct {
471                    hash: meta.hash,
472                    args,
473                };
474
475                let signature = DebugSignature::new(
476                    pool.item(meta.item_meta.item).try_to_owned()?,
477                    DebugArgs::TupleArgs(args),
478                );
479
480                let rtti = Arc::new(Rtti {
481                    kind: RttiKind::Tuple,
482                    hash: meta.hash,
483                    variant_hash: Hash::EMPTY,
484                    item: pool.item(meta.item_meta.item).try_to_owned()?,
485                    fields: HashMap::default(),
486                });
487
488                if self
489                    .rtti
490                    .try_insert(meta.hash, rtti)
491                    .with_span(span)?
492                    .is_some()
493                {
494                    return Err(compile::Error::new(
495                        span,
496                        ErrorKind::TypeRttiConflict { hash: meta.hash },
497                    ));
498                }
499
500                if self
501                    .functions
502                    .try_insert(meta.hash, info)
503                    .with_span(span)?
504                    .is_some()
505                {
506                    return Err(compile::Error::new(
507                        span,
508                        ErrorKind::FunctionConflict {
509                            existing: signature,
510                        },
511                    ));
512                }
513
514                self.constants
515                    .try_insert(
516                        Hash::associated_function(meta.hash, &Protocol::INTO_TYPE_NAME),
517                        ConstValue::from(signature.path.try_to_string()?),
518                    )
519                    .with_span(span)?;
520
521                self.debug_mut()?
522                    .functions
523                    .try_insert(meta.hash, signature)?;
524            }
525            meta::Kind::Struct {
526                fields: meta::Fields::Unnamed(args),
527                enum_hash,
528                ..
529            } => {
530                let rtti = Arc::new(Rtti {
531                    kind: RttiKind::Tuple,
532                    hash: enum_hash,
533                    variant_hash: meta.hash,
534                    item: pool.item(meta.item_meta.item).try_to_owned()?,
535                    fields: HashMap::default(),
536                });
537
538                if self
539                    .rtti
540                    .try_insert(meta.hash, rtti)
541                    .with_span(span)?
542                    .is_some()
543                {
544                    return Err(compile::Error::new(
545                        span,
546                        ErrorKind::RttiConflict { hash: meta.hash },
547                    ));
548                }
549
550                let info = UnitFn::TupleStruct {
551                    hash: meta.hash,
552                    args,
553                };
554
555                let signature = DebugSignature::new(
556                    pool.item(meta.item_meta.item).try_to_owned()?,
557                    DebugArgs::TupleArgs(args),
558                );
559
560                if self
561                    .functions
562                    .try_insert(meta.hash, info)
563                    .with_span(span)?
564                    .is_some()
565                {
566                    return Err(compile::Error::new(
567                        span,
568                        ErrorKind::FunctionConflict {
569                            existing: signature,
570                        },
571                    ));
572                }
573
574                self.debug_mut()?
575                    .functions
576                    .try_insert(meta.hash, signature)?;
577            }
578            meta::Kind::Struct {
579                fields: meta::Fields::Named(ref named),
580                enum_hash: Hash::EMPTY,
581                ..
582            } => {
583                let rtti = Arc::new(Rtti {
584                    kind: RttiKind::Struct,
585                    hash: meta.hash,
586                    variant_hash: Hash::EMPTY,
587                    item: pool.item(meta.item_meta.item).try_to_owned()?,
588                    fields: named.to_fields()?,
589                });
590
591                self.constants
592                    .try_insert(
593                        Hash::associated_function(meta.hash, &Protocol::INTO_TYPE_NAME),
594                        ConstValue::from(rtti.item.try_to_string()?),
595                    )
596                    .with_span(span)?;
597
598                if self
599                    .rtti
600                    .try_insert(meta.hash, rtti)
601                    .with_span(span)?
602                    .is_some()
603                {
604                    return Err(compile::Error::new(
605                        span,
606                        ErrorKind::TypeRttiConflict { hash: meta.hash },
607                    ));
608                }
609            }
610            meta::Kind::Struct {
611                fields: meta::Fields::Named(ref named),
612                enum_hash,
613                ..
614            } => {
615                let rtti = Arc::new(Rtti {
616                    kind: RttiKind::Struct,
617                    hash: enum_hash,
618                    variant_hash: meta.hash,
619                    item: pool.item(meta.item_meta.item).try_to_owned()?,
620                    fields: named.to_fields()?,
621                });
622
623                if self
624                    .rtti
625                    .try_insert(meta.hash, rtti)
626                    .with_span(span)?
627                    .is_some()
628                {
629                    return Err(compile::Error::new(
630                        span,
631                        ErrorKind::RttiConflict { hash: meta.hash },
632                    ));
633                }
634            }
635            meta::Kind::Enum { .. } => {
636                let name = pool
637                    .item(meta.item_meta.item)
638                    .try_to_string()
639                    .with_span(span)?;
640
641                self.constants
642                    .try_insert(
643                        Hash::associated_function(meta.hash, &Protocol::INTO_TYPE_NAME),
644                        ConstValue::from(name),
645                    )
646                    .with_span(span)?;
647            }
648            meta::Kind::Const => {
649                let Some(const_value) = query.get_const_value(meta.hash) else {
650                    return Err(compile::Error::msg(
651                        span,
652                        try_format!("Missing constant for hash {}", meta.hash),
653                    ));
654                };
655
656                let value = const_value.try_clone().with_span(span)?;
657
658                self.constants
659                    .try_insert(meta.hash, value)
660                    .with_span(span)?;
661            }
662            meta::Kind::Macro => (),
663            meta::Kind::AttributeMacro => (),
664            meta::Kind::Function { .. } => (),
665            meta::Kind::Closure { .. } => (),
666            meta::Kind::AsyncBlock { .. } => (),
667            meta::Kind::ConstFn => (),
668            meta::Kind::Import { .. } => (),
669            meta::Kind::Alias { .. } => (),
670            meta::Kind::Module => (),
671            meta::Kind::Trait => (),
672        }
673
674        Ok(())
675    }
676
677    /// Construct a new empty assembly associated with the current unit.
678    pub(crate) fn new_assembly(&self, location: Location) -> Assembly {
679        Assembly::new(location, self.label_count)
680    }
681
682    /// Register a new function re-export.
683    pub(crate) fn new_function_reexport(
684        &mut self,
685        location: Location,
686        item: &Item,
687        target: &Item,
688    ) -> compile::Result<()> {
689        let hash = Hash::type_hash(item);
690        let target = Hash::type_hash(target);
691
692        if self.reexports.try_insert(hash, target)?.is_some() {
693            return Err(compile::Error::new(
694                location.span,
695                ErrorKind::FunctionReExportConflict { hash },
696            ));
697        }
698
699        Ok(())
700    }
701
702    /// Declare a new instance function at the current instruction pointer.
703    pub(crate) fn new_function(
704        &mut self,
705        location: Location,
706        item: &Item,
707        instance: Option<(Hash, &str)>,
708        args: usize,
709        captures: Option<usize>,
710        assembly: Assembly,
711        call: Call,
712        debug_args: Box<[Box<str>]>,
713        unit_storage: &mut dyn UnitEncoder,
714        size: usize,
715    ) -> compile::Result<()> {
716        tracing::trace!("instance fn: {}", item);
717
718        let offset = unit_storage.offset();
719
720        let info = UnitFn::Offset {
721            offset,
722            call,
723            args,
724            captures,
725        };
726        let signature = DebugSignature::new(item.try_to_owned()?, DebugArgs::Named(debug_args));
727
728        if let Some((type_hash, name)) = instance {
729            let instance_fn = Hash::associated_function(type_hash, name);
730
731            if self
732                .functions
733                .try_insert(instance_fn, info)
734                .with_span(location.span)?
735                .is_some()
736            {
737                return Err(compile::Error::new(
738                    location.span,
739                    ErrorKind::FunctionConflict {
740                        existing: signature,
741                    },
742                ));
743            }
744
745            self.debug_mut()?
746                .functions
747                .try_insert(instance_fn, signature.try_clone()?)?;
748        }
749
750        let hash = Hash::type_hash(item);
751
752        if self
753            .functions
754            .try_insert(hash, info)
755            .with_span(location.span)?
756            .is_some()
757        {
758            return Err(compile::Error::new(
759                location.span,
760                ErrorKind::FunctionConflict {
761                    existing: signature,
762                },
763            ));
764        }
765
766        self.constants
767            .try_insert(
768                Hash::associated_function(hash, &Protocol::INTO_TYPE_NAME),
769                ConstValue::from(signature.path.try_to_string().with_span(location.span)?),
770            )
771            .with_span(location.span)?;
772
773        self.debug_mut()?.functions.try_insert(hash, signature)?;
774        self.functions_rev.try_insert(offset, hash)?;
775        self.add_assembly(location, assembly, unit_storage, size)?;
776        Ok(())
777    }
778
779    /// Try to link the unit with the context, checking that all necessary
780    /// functions are provided.
781    ///
782    /// This can prevent a number of runtime errors, like missing functions.
783    pub(crate) fn link(
784        &mut self,
785        context: &Context,
786        diagnostics: &mut Diagnostics,
787    ) -> alloc::Result<()> {
788        for (hash, spans) in &self.required_functions {
789            if self.functions.get(hash).is_none() && context.lookup_function(*hash).is_none() {
790                diagnostics.error(
791                    SourceId::empty(),
792                    LinkerError::MissingFunction {
793                        hash: *hash,
794                        spans: spans.try_clone()?,
795                    },
796                )?;
797            }
798        }
799
800        Ok(())
801    }
802
803    /// Insert and access debug information.
804    fn debug_mut(&mut self) -> alloc::Result<&mut DebugInfo> {
805        if self.debug.is_none() {
806            self.debug = Some(Box::try_new(DebugInfo::default())?);
807        }
808
809        Ok(self.debug.as_mut().unwrap())
810    }
811
812    /// Translate the given assembly into instructions.
813    fn add_assembly(
814        &mut self,
815        location: Location,
816        assembly: Assembly,
817        storage: &mut dyn UnitEncoder,
818        size: usize,
819    ) -> compile::Result<()> {
820        self.label_count = assembly.label_count;
821
822        storage
823            .encode(Inst::Allocate { size })
824            .with_span(location.span)?;
825
826        let base = storage.extend_offsets(assembly.labels.len())?;
827
828        self.required_functions
829            .try_extend(assembly.required_functions)?;
830
831        for (offset, (_, labels)) in &assembly.labels {
832            for label in labels {
833                if let Some(jump) = label.jump() {
834                    label.set_jump(storage.label_jump(base, *offset, jump));
835                }
836            }
837        }
838
839        for (pos, (inst, span)) in assembly.instructions.into_iter().enumerate() {
840            let mut comment = String::new();
841
842            let at = storage.offset();
843
844            let mut labels = Vec::new();
845
846            for label in assembly
847                .labels
848                .get(&pos)
849                .map(|e| e.1.as_slice())
850                .unwrap_or_default()
851            {
852                if let Some(index) = label.jump() {
853                    storage.mark_offset(index);
854                }
855
856                labels.try_push(label.to_debug_label())?;
857            }
858
859            let build_label = |label: Label| {
860                label
861                    .jump()
862                    .ok_or(ErrorKind::MissingLabelLocation {
863                        name: label.name,
864                        index: label.index,
865                    })
866                    .with_span(span)
867            };
868
869            match inst {
870                AssemblyInst::Jump { label } => {
871                    write!(comment, "label:{}", label)?;
872                    let jump = build_label(label)?;
873                    storage.encode(Inst::Jump { jump }).with_span(span)?;
874                }
875                AssemblyInst::JumpIf { addr, label } => {
876                    write!(comment, "label:{}", label)?;
877                    let jump = build_label(label)?;
878                    storage
879                        .encode(Inst::JumpIf { cond: addr, jump })
880                        .with_span(span)?;
881                }
882                AssemblyInst::JumpIfNot { addr, label } => {
883                    write!(comment, "label:{}", label)?;
884                    let jump = build_label(label)?;
885                    storage
886                        .encode(Inst::JumpIfNot { cond: addr, jump })
887                        .with_span(span)?;
888                }
889                AssemblyInst::IterNext { addr, label, out } => {
890                    write!(comment, "label:{}", label)?;
891                    let jump = build_label(label)?;
892                    storage
893                        .encode(Inst::IterNext { addr, jump, out })
894                        .with_span(span)?;
895                }
896                AssemblyInst::Raw { raw } => {
897                    // Optimization to avoid performing lookups for recursive
898                    // function calls.
899                    let inst = match raw {
900                        inst @ Inst::Call {
901                            hash,
902                            addr,
903                            args,
904                            out,
905                        } => {
906                            if let Some(UnitFn::Offset { offset, call, .. }) =
907                                self.functions.get(&hash)
908                            {
909                                Inst::CallOffset {
910                                    offset: *offset,
911                                    call: *call,
912                                    addr,
913                                    args,
914                                    out,
915                                }
916                            } else {
917                                inst
918                            }
919                        }
920                        inst => inst,
921                    };
922
923                    storage.encode(inst).with_span(span)?;
924                }
925            }
926
927            if let Some(c) = assembly.comments.get(&pos) {
928                if !comment.is_empty() {
929                    comment.try_push_str("; ")?;
930                }
931
932                comment.try_push_str(c)?;
933            }
934
935            let comment = if comment.is_empty() {
936                None
937            } else {
938                Some(comment.try_into()?)
939            };
940
941            self.debug_mut()?.instructions.try_insert(
942                at,
943                DebugInst::new(location.source_id, span, comment, labels),
944            )?;
945        }
946
947        Ok(())
948    }
949}
950
951/// A set of addresses that should be dropped.
952pub(crate) struct DropSet<'a> {
953    builder: &'a mut UnitBuilder,
954    addresses: Vec<InstAddress>,
955}
956
957impl DropSet<'_> {
958    /// Construct a new drop set.
959    pub(crate) fn push(&mut self, addr: InstAddress) -> alloc::Result<()> {
960        self.addresses.try_push(addr)
961    }
962
963    pub(crate) fn finish(self) -> alloc::Result<Option<usize>> {
964        if self.addresses.is_empty() {
965            return Ok(None);
966        }
967
968        if let Some(set) = self.builder.drop_sets_rev.get(&self.addresses) {
969            return Ok(Some(*set));
970        }
971
972        let set = self.builder.drop_sets.len();
973
974        self.builder
975            .drop_sets_rev
976            .try_insert(self.addresses.try_clone()?, set)?;
977        self.builder
978            .drop_sets
979            .try_push(Arc::from(&self.addresses[..]))?;
980        Ok(Some(set))
981    }
982}