1use core::fmt;
7
8use crate::alloc::fmt::TryWrite;
9use crate::alloc::prelude::*;
10use crate::alloc::{self, try_format, Box, HashMap, String, Vec};
11use crate::ast::{Span, Spanned};
12use crate::compile::meta;
13use crate::compile::{self, Assembly, AssemblyInst, ErrorKind, Location, Pool, WithSpan};
14use crate::hash;
15use crate::query::QueryInner;
16use crate::runtime::debug::{DebugArgs, DebugGlobal, DebugSignature};
17use crate::runtime::inst;
18use crate::runtime::unit::UnitEncoder;
19use crate::runtime::{
20 Address, Call, ConstValueBuf, DebugInfo, DebugInst, Inst, Label, Protocol, Rtti, RttiKind,
21 StaticString, Unit, UnitFn,
22};
23use crate::sync::Arc;
24use crate::{Context, Diagnostics, Hash, Item, ItemBuf, SourceId};
25
26#[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#[derive(Debug, Default)]
51pub(crate) struct UnitBuilder {
52 reexports: HashMap<Hash, Hash>,
54 functions: hash::Map<UnitFn>,
56 functions_rev: HashMap<usize, Hash>,
58 static_strings: Vec<Arc<StaticString>>,
60 static_string_rev: HashMap<Hash, usize>,
62 static_bytes: Vec<Vec<u8>>,
64 static_bytes_rev: HashMap<Hash, usize>,
66 static_object_keys: Vec<Box<[String]>>,
73 static_object_keys_rev: HashMap<Hash, usize>,
75 drop_sets: Vec<Arc<[Address]>>,
77 drop_sets_rev: HashMap<Vec<Address>, usize>,
79 rtti: hash::Map<Arc<Rtti>>,
81 label_count: usize,
83 required_functions: HashMap<Hash, Vec<(Span, SourceId)>>,
85 debug: Option<Box<DebugInfo>>,
87 constants: hash::Map<ConstValueBuf>,
89 hash_to_ident: HashMap<Hash, Box<str>>,
91 globals: Vec<Option<ConstValueBuf>>,
94 globals_rev: hash::Map<usize>,
96}
97
98impl UnitBuilder {
99 pub(crate) fn drop_set(&mut self) -> DropSet<'_> {
101 DropSet {
102 builder: self,
103 addresses: Vec::new(),
104 }
105 }
106
107 pub(crate) fn insert_debug_ident(&mut self, ident: &str) -> alloc::Result<()> {
109 self.hash_to_ident
110 .try_insert(Hash::ident(ident), ident.try_into()?)?;
111 Ok(())
112 }
113
114 pub(crate) fn build<S>(mut self, span: Span, storage: S) -> compile::Result<Unit<S>> {
118 if let Some(debug) = &mut self.debug {
119 debug.functions_rev = self.functions_rev;
120 debug.hash_to_ident = self.hash_to_ident;
121 }
122
123 for (from, to) in self.reexports {
124 if let Some(info) = self.functions.get(&to) {
125 let info = *info;
126 if self
127 .functions
128 .try_insert(from, info)
129 .with_span(span)?
130 .is_some()
131 {
132 return Err(compile::Error::new(
133 span,
134 ErrorKind::FunctionConflictHash { hash: from },
135 ));
136 }
137 continue;
138 }
139
140 if let Some(value) = self.constants.get(&to) {
141 let const_value = value.try_clone()?;
142
143 if self
144 .constants
145 .try_insert(from, const_value)
146 .with_span(span)?
147 .is_some()
148 {
149 return Err(compile::Error::new(
150 span,
151 ErrorKind::ConstantConflict { hash: from },
152 ));
153 }
154
155 continue;
156 }
157
158 return Err(compile::Error::new(
159 span,
160 ErrorKind::MissingFunctionHash { hash: to },
161 ));
162 }
163
164 Ok(Unit::new(
165 storage,
166 self.functions,
167 self.static_strings,
168 self.static_bytes,
169 self.static_object_keys,
170 self.drop_sets,
171 self.rtti,
172 self.debug,
173 self.constants,
174 self.globals,
175 self.globals_rev,
176 ))
177 }
178
179 pub(crate) fn snapshot<S>(&self, storage: S) -> alloc::Result<Unit<S>> {
191 Ok(Unit::new(
192 storage,
193 self.functions.try_clone()?,
194 self.static_strings.try_clone()?,
195 self.static_bytes.try_clone()?,
196 self.static_object_keys.try_clone()?,
197 self.drop_sets.try_clone()?,
198 self.rtti.try_clone()?,
199 None,
200 self.constants.try_clone()?,
201 self.globals.try_clone()?,
202 self.globals_rev.try_clone()?,
203 ))
204 }
205
206 pub(crate) fn global_slot(&mut self, hash: Hash) -> alloc::Result<usize> {
212 if let Some(slot) = self.globals_rev.get(&hash).copied() {
213 return Ok(slot);
214 }
215
216 let slot = self.globals.len();
217 self.globals.try_push(None)?;
218 self.globals_rev.try_insert(hash, slot)?;
219 Ok(slot)
220 }
221
222 pub(crate) fn insert_global(
224 &mut self,
225 hash: Hash,
226 path: &Item,
227 init: Option<ConstValueBuf>,
228 debug_info: bool,
229 ) -> alloc::Result<usize> {
230 let slot = self.global_slot(hash)?;
231
232 if let Some(init) = init {
233 self.globals[slot] = Some(init);
234 }
235
236 if debug_info {
237 let debug = self.debug_mut()?;
238
239 while debug.globals.len() <= slot {
240 debug.globals.try_push(DebugGlobal::new(ItemBuf::new()))?;
241 }
242
243 debug.globals[slot] = DebugGlobal::new(path.try_to_owned()?);
244 }
245
246 Ok(slot)
247 }
248
249 pub(crate) fn new_static_string(
254 &mut self,
255 span: &dyn Spanned,
256 current: &str,
257 ) -> compile::Result<usize> {
258 let current = StaticString::new(current)?;
259 let hash = current.hash();
260
261 if let Some(existing_slot) = self.static_string_rev.get(&hash).copied() {
262 let Some(existing) = self.static_strings.get(existing_slot) else {
263 return Err(compile::Error::new(
264 span,
265 ErrorKind::StaticStringMissing {
266 hash,
267 slot: existing_slot,
268 },
269 ));
270 };
271
272 if ***existing != *current {
273 return Err(compile::Error::new(
274 span,
275 ErrorKind::StaticStringHashConflict {
276 hash,
277 current: (*current).try_clone()?,
278 existing: (***existing).try_clone()?,
279 },
280 ));
281 }
282
283 return Ok(existing_slot);
284 }
285
286 let new_slot = self.static_strings.len();
287 self.static_strings.try_push(Arc::try_new(current)?)?;
288 self.static_string_rev.try_insert(hash, new_slot)?;
289 Ok(new_slot)
290 }
291
292 pub(crate) fn new_static_bytes(
297 &mut self,
298 span: &dyn Spanned,
299 current: &[u8],
300 ) -> compile::Result<usize> {
301 let hash = Hash::static_bytes(current);
302
303 if let Some(existing_slot) = self.static_bytes_rev.get(&hash).copied() {
304 let existing = self.static_bytes.get(existing_slot).ok_or_else(|| {
305 compile::Error::new(
306 span,
307 ErrorKind::StaticBytesMissing {
308 hash,
309 slot: existing_slot,
310 },
311 )
312 })?;
313
314 if &**existing != current {
315 return Err(compile::Error::new(
316 span,
317 ErrorKind::StaticBytesHashConflict {
318 hash,
319 current: current.try_to_owned()?,
320 existing: existing.try_clone()?,
321 },
322 ));
323 }
324
325 return Ok(existing_slot);
326 }
327
328 let new_slot = self.static_bytes.len();
329 self.static_bytes.try_push(current.try_to_owned()?)?;
330 self.static_bytes_rev.try_insert(hash, new_slot)?;
331 Ok(new_slot)
332 }
333
334 pub(crate) fn new_static_object_keys_iter(
337 &mut self,
338 span: &dyn Spanned,
339 current: impl IntoIterator<Item: AsRef<str>>,
340 ) -> compile::Result<usize> {
341 let current = current
342 .into_iter()
343 .map(|s| s.as_ref().try_to_owned())
344 .try_collect::<alloc::Result<Box<_>>>()??;
345
346 self.new_static_object_keys(span, current)
347 }
348
349 pub(crate) fn new_static_object_keys(
352 &mut self,
353 span: &dyn Spanned,
354 current: Box<[String]>,
355 ) -> compile::Result<usize> {
356 let hash = Hash::object_keys(¤t[..]);
357
358 if let Some(existing_slot) = self.static_object_keys_rev.get(&hash).copied() {
359 let existing = self.static_object_keys.get(existing_slot).ok_or_else(|| {
360 compile::Error::new(
361 span,
362 ErrorKind::StaticObjectKeysMissing {
363 hash,
364 slot: existing_slot,
365 },
366 )
367 })?;
368
369 if *existing != current {
370 return Err(compile::Error::new(
371 span,
372 ErrorKind::StaticObjectKeysHashConflict {
373 hash,
374 current,
375 existing: existing.try_clone()?,
376 },
377 ));
378 }
379
380 return Ok(existing_slot);
381 }
382
383 let new_slot = self.static_object_keys.len();
384 self.static_object_keys.try_push(current)?;
385 self.static_object_keys_rev.try_insert(hash, new_slot)?;
386 Ok(new_slot)
387 }
388
389 pub(crate) fn insert_meta(
391 &mut self,
392 span: &dyn Spanned,
393 meta: &meta::Meta,
394 pool: &Pool,
395 query: &mut QueryInner,
396 debug_info: bool,
397 ) -> compile::Result<()> {
398 debug_assert_eq! {
399 pool.item_type_hash(meta.item_meta.item),
400 meta.hash,
401 };
402
403 match meta.kind {
404 meta::Kind::Type { .. } => {
405 let rtti = Arc::try_new(Rtti {
406 kind: RttiKind::Empty,
407 hash: meta.hash,
408 variant_hash: Hash::EMPTY,
409 item: pool.item(meta.item_meta.item).try_to_owned()?,
410 fields: HashMap::default(),
411 })?;
412
413 self.constants
414 .try_insert(
415 Hash::associated_function(meta.hash, &Protocol::INTO_TYPE_NAME),
416 ConstValueBuf::try_from(rtti.item.try_to_string()?)?,
417 )
418 .with_span(span)?;
419
420 if self
421 .rtti
422 .try_insert(meta.hash, rtti)
423 .with_span(span)?
424 .is_some()
425 {
426 return Err(compile::Error::new(
427 span,
428 ErrorKind::TypeRttiConflict { hash: meta.hash },
429 ));
430 }
431 }
432 meta::Kind::Struct {
433 fields: meta::Fields::Empty,
434 enum_hash: Hash::EMPTY,
435 ..
436 } => {
437 let info = UnitFn::EmptyStruct { hash: meta.hash };
438
439 let signature = DebugSignature::new(
440 pool.item(meta.item_meta.item).try_to_owned()?,
441 DebugArgs::EmptyArgs,
442 );
443
444 let rtti = Arc::try_new(Rtti {
445 kind: RttiKind::Empty,
446 hash: meta.hash,
447 variant_hash: Hash::EMPTY,
448 item: pool.item(meta.item_meta.item).try_to_owned()?,
449 fields: HashMap::default(),
450 })?;
451
452 if self
453 .rtti
454 .try_insert(meta.hash, rtti)
455 .with_span(span)?
456 .is_some()
457 {
458 return Err(compile::Error::new(
459 span,
460 ErrorKind::TypeRttiConflict { hash: meta.hash },
461 ));
462 }
463
464 if self
465 .functions
466 .try_insert(meta.hash, info)
467 .with_span(span)?
468 .is_some()
469 {
470 return Err(compile::Error::new(
471 span,
472 ErrorKind::FunctionConflict {
473 existing: signature,
474 },
475 ));
476 }
477
478 self.constants
479 .try_insert(
480 Hash::associated_function(meta.hash, &Protocol::INTO_TYPE_NAME),
481 ConstValueBuf::try_from(signature.path.try_to_string()?)?,
482 )
483 .with_span(span)?;
484
485 self.debug_mut()?
486 .functions
487 .try_insert(meta.hash, signature)?;
488 }
489 meta::Kind::Struct {
490 fields: meta::Fields::Empty,
491 enum_hash,
492 ..
493 } => {
494 let rtti = Arc::try_new(Rtti {
495 kind: RttiKind::Empty,
496 hash: enum_hash,
497 variant_hash: meta.hash,
498 item: pool.item(meta.item_meta.item).try_to_owned()?,
499 fields: HashMap::default(),
500 })?;
501
502 if self
503 .rtti
504 .try_insert(meta.hash, rtti)
505 .with_span(span)?
506 .is_some()
507 {
508 return Err(compile::Error::new(
509 span,
510 ErrorKind::RttiConflict { hash: meta.hash },
511 ));
512 }
513
514 let info = UnitFn::EmptyStruct { hash: meta.hash };
515
516 let signature = DebugSignature::new(
517 pool.item(meta.item_meta.item).try_to_owned()?,
518 DebugArgs::EmptyArgs,
519 );
520
521 if self
522 .functions
523 .try_insert(meta.hash, info)
524 .with_span(span)?
525 .is_some()
526 {
527 return Err(compile::Error::new(
528 span,
529 ErrorKind::FunctionConflict {
530 existing: signature,
531 },
532 ));
533 }
534
535 self.debug_mut()?
536 .functions
537 .try_insert(meta.hash, signature)?;
538 }
539 meta::Kind::Struct {
540 fields: meta::Fields::Unnamed(args),
541 enum_hash: Hash::EMPTY,
542 ..
543 } => {
544 let info = UnitFn::TupleStruct {
545 hash: meta.hash,
546 args,
547 };
548
549 let signature = DebugSignature::new(
550 pool.item(meta.item_meta.item).try_to_owned()?,
551 DebugArgs::TupleArgs(args),
552 );
553
554 let rtti = Arc::try_new(Rtti {
555 kind: RttiKind::Tuple,
556 hash: meta.hash,
557 variant_hash: Hash::EMPTY,
558 item: pool.item(meta.item_meta.item).try_to_owned()?,
559 fields: HashMap::default(),
560 })?;
561
562 if self
563 .rtti
564 .try_insert(meta.hash, rtti)
565 .with_span(span)?
566 .is_some()
567 {
568 return Err(compile::Error::new(
569 span,
570 ErrorKind::TypeRttiConflict { hash: meta.hash },
571 ));
572 }
573
574 if self
575 .functions
576 .try_insert(meta.hash, info)
577 .with_span(span)?
578 .is_some()
579 {
580 return Err(compile::Error::new(
581 span,
582 ErrorKind::FunctionConflict {
583 existing: signature,
584 },
585 ));
586 }
587
588 self.constants
589 .try_insert(
590 Hash::associated_function(meta.hash, &Protocol::INTO_TYPE_NAME),
591 ConstValueBuf::try_from(signature.path.try_to_string()?)?,
592 )
593 .with_span(span)?;
594
595 self.debug_mut()?
596 .functions
597 .try_insert(meta.hash, signature)?;
598 }
599 meta::Kind::Struct {
600 fields: meta::Fields::Unnamed(args),
601 enum_hash,
602 ..
603 } => {
604 let rtti = Arc::try_new(Rtti {
605 kind: RttiKind::Tuple,
606 hash: enum_hash,
607 variant_hash: meta.hash,
608 item: pool.item(meta.item_meta.item).try_to_owned()?,
609 fields: HashMap::default(),
610 })?;
611
612 if self
613 .rtti
614 .try_insert(meta.hash, rtti)
615 .with_span(span)?
616 .is_some()
617 {
618 return Err(compile::Error::new(
619 span,
620 ErrorKind::RttiConflict { hash: meta.hash },
621 ));
622 }
623
624 let info = UnitFn::TupleStruct {
625 hash: meta.hash,
626 args,
627 };
628
629 let signature = DebugSignature::new(
630 pool.item(meta.item_meta.item).try_to_owned()?,
631 DebugArgs::TupleArgs(args),
632 );
633
634 if self
635 .functions
636 .try_insert(meta.hash, info)
637 .with_span(span)?
638 .is_some()
639 {
640 return Err(compile::Error::new(
641 span,
642 ErrorKind::FunctionConflict {
643 existing: signature,
644 },
645 ));
646 }
647
648 self.debug_mut()?
649 .functions
650 .try_insert(meta.hash, signature)?;
651 }
652 meta::Kind::Struct {
653 fields: meta::Fields::Named(ref named),
654 enum_hash: Hash::EMPTY,
655 ..
656 } => {
657 let rtti = Arc::try_new(Rtti {
658 kind: RttiKind::Struct,
659 hash: meta.hash,
660 variant_hash: Hash::EMPTY,
661 item: pool.item(meta.item_meta.item).try_to_owned()?,
662 fields: named.to_fields()?,
663 })?;
664
665 self.constants
666 .try_insert(
667 Hash::associated_function(meta.hash, &Protocol::INTO_TYPE_NAME),
668 ConstValueBuf::try_from(rtti.item.try_to_string()?)?,
669 )
670 .with_span(span)?;
671
672 if self
673 .rtti
674 .try_insert(meta.hash, rtti)
675 .with_span(span)?
676 .is_some()
677 {
678 return Err(compile::Error::new(
679 span,
680 ErrorKind::TypeRttiConflict { hash: meta.hash },
681 ));
682 }
683 }
684 meta::Kind::Struct {
685 fields: meta::Fields::Named(ref named),
686 enum_hash,
687 ..
688 } => {
689 let rtti = Arc::try_new(Rtti {
690 kind: RttiKind::Struct,
691 hash: enum_hash,
692 variant_hash: meta.hash,
693 item: pool.item(meta.item_meta.item).try_to_owned()?,
694 fields: named.to_fields()?,
695 })?;
696
697 if self
698 .rtti
699 .try_insert(meta.hash, rtti)
700 .with_span(span)?
701 .is_some()
702 {
703 return Err(compile::Error::new(
704 span,
705 ErrorKind::RttiConflict { hash: meta.hash },
706 ));
707 }
708 }
709 meta::Kind::Enum { .. } => {
710 let name = pool
711 .item(meta.item_meta.item)
712 .try_to_string()
713 .with_span(span)?;
714
715 self.constants
716 .try_insert(
717 Hash::associated_function(meta.hash, &Protocol::INTO_TYPE_NAME),
718 ConstValueBuf::try_from(name)?,
719 )
720 .with_span(span)?;
721 }
722 meta::Kind::Const => {
723 let Some(const_value) = query.get_const_value(meta.hash) else {
724 return Err(compile::Error::msg(
725 span,
726 try_format!("Missing constant for hash {}", meta.hash),
727 ));
728 };
729
730 let value = const_value.try_to_owned().with_span(span)?;
731
732 self.constants
733 .try_insert(meta.hash, value)
734 .with_span(span)?;
735 }
736 meta::Kind::Static => {
737 let init = match query.get_static_init(meta.hash) {
738 Some(init) => Some(init.try_to_owned().with_span(span)?),
739 None => None,
740 };
741
742 self.insert_global(meta.hash, pool.item(meta.item_meta.item), init, debug_info)
743 .with_span(span)?;
744 }
745 meta::Kind::Macro => (),
746 meta::Kind::AttributeMacro => (),
747 meta::Kind::Function { .. } => (),
748 meta::Kind::Closure { .. } => (),
749 meta::Kind::AsyncBlock { .. } => (),
750 meta::Kind::ConstFn => (),
751 meta::Kind::Import { .. } => (),
752 meta::Kind::Alias { .. } => (),
753 meta::Kind::Module => (),
754 meta::Kind::Trait => (),
755 }
756
757 Ok(())
758 }
759
760 pub(crate) fn new_assembly(&self, location: Location) -> Assembly {
762 Assembly::new(location, self.label_count)
763 }
764
765 pub(crate) fn new_function_reexport(
767 &mut self,
768 location: Location,
769 item: &Item,
770 target: &Item,
771 ) -> compile::Result<()> {
772 let hash = Hash::type_hash(item);
773 let target = Hash::type_hash(target);
774
775 if self.reexports.try_insert(hash, target)?.is_some() {
776 return Err(compile::Error::new(
777 location.span,
778 ErrorKind::FunctionReExportConflict { hash },
779 ));
780 }
781
782 Ok(())
783 }
784
785 pub(crate) fn new_function(
787 &mut self,
788 location: Location,
789 item: &Item,
790 instance: Option<(Hash, &str)>,
791 args: usize,
792 captures: Option<usize>,
793 assembly: Assembly,
794 call: Call,
795 debug_args: Box<[Box<str>]>,
796 unit_storage: &mut dyn UnitEncoder,
797 size: usize,
798 ) -> compile::Result<()> {
799 tracing::trace!("instance fn: {}", item);
800
801 let offset = unit_storage.offset();
802
803 let info = UnitFn::Offset {
804 offset,
805 call,
806 args,
807 captures,
808 };
809 let signature = DebugSignature::new(item.try_to_owned()?, DebugArgs::Named(debug_args));
810
811 if let Some((type_hash, name)) = instance {
812 let instance_fn = Hash::associated_function(type_hash, name);
813
814 if self
815 .functions
816 .try_insert(instance_fn, info)
817 .with_span(location.span)?
818 .is_some()
819 {
820 return Err(compile::Error::new(
821 location.span,
822 ErrorKind::FunctionConflict {
823 existing: signature,
824 },
825 ));
826 }
827
828 self.debug_mut()?
829 .functions
830 .try_insert(instance_fn, signature.try_clone()?)?;
831 }
832
833 let hash = Hash::type_hash(item);
834
835 if self
836 .functions
837 .try_insert(hash, info)
838 .with_span(location.span)?
839 .is_some()
840 {
841 return Err(compile::Error::new(
842 location.span,
843 ErrorKind::FunctionConflict {
844 existing: signature,
845 },
846 ));
847 }
848
849 self.constants
850 .try_insert(
851 Hash::associated_function(hash, &Protocol::INTO_TYPE_NAME),
852 ConstValueBuf::try_from(signature.path.try_to_string().with_span(location.span)?)?,
853 )
854 .with_span(location.span)?;
855
856 self.debug_mut()?.functions.try_insert(hash, signature)?;
857 self.functions_rev.try_insert(offset, hash)?;
858 self.add_assembly(location, assembly, unit_storage, size)?;
859 Ok(())
860 }
861
862 pub(crate) fn new_const_function(
870 &mut self,
871 location: Location,
872 hash: Hash,
873 args: usize,
874 assembly: Assembly,
875 unit_storage: &mut dyn UnitEncoder,
876 size: usize,
877 ) -> compile::Result<()> {
878 let offset = unit_storage.offset();
879
880 let info = UnitFn::Offset {
881 offset,
882 call: Call::Immediate,
883 args,
884 captures: None,
885 };
886
887 if self
888 .functions
889 .try_insert(hash, info)
890 .with_span(location.span)?
891 .is_some()
892 {
893 return Err(compile::Error::new(
894 location.span,
895 ErrorKind::FunctionConflictHash { hash },
896 ));
897 }
898
899 self.add_assembly(location, assembly, unit_storage, size)?;
900 Ok(())
901 }
902
903 pub(crate) fn link(
908 &mut self,
909 context: &Context,
910 diagnostics: &mut Diagnostics,
911 ) -> alloc::Result<()> {
912 for (hash, spans) in &self.required_functions {
913 if self.functions.get(hash).is_none() && context.lookup_function(*hash).is_none() {
914 diagnostics.error(
915 SourceId::empty(),
916 LinkerError::MissingFunction {
917 hash: *hash,
918 spans: spans.try_clone()?,
919 },
920 )?;
921 }
922 }
923
924 Ok(())
925 }
926
927 fn debug_mut(&mut self) -> alloc::Result<&mut DebugInfo> {
929 if self.debug.is_none() {
930 self.debug = Some(Box::try_new(DebugInfo::default())?);
931 }
932
933 Ok(self.debug.as_mut().unwrap())
934 }
935
936 fn add_assembly(
938 &mut self,
939 location: Location,
940 assembly: Assembly,
941 storage: &mut dyn UnitEncoder,
942 size: usize,
943 ) -> compile::Result<()> {
944 self.label_count = assembly.label_count;
945
946 storage
947 .encode(Inst::new(inst::Kind::Allocate { size }))
948 .with_span(location.span)?;
949
950 let base = storage.extend_offsets(assembly.labels.len())?;
951
952 self.required_functions
953 .try_extend(assembly.required_functions)?;
954
955 for (offset, (_, labels)) in &assembly.labels {
956 for label in labels {
957 if let Some(jump) = label.jump() {
958 label.set_jump(storage.label_jump(base, *offset, jump));
959 }
960 }
961 }
962
963 for (pos, (inst, span)) in assembly.instructions.into_iter().enumerate() {
964 let mut comment = String::new();
965
966 let at = storage.offset();
967
968 let mut labels = Vec::new();
969
970 for label in assembly
971 .labels
972 .get(&pos)
973 .map(|e| e.1.as_slice())
974 .unwrap_or_default()
975 {
976 if let Some(index) = label.jump() {
977 storage.mark_offset(index);
978 }
979
980 labels.try_push(label.to_debug_label())?;
981 }
982
983 let build_label = |label: Label| {
984 label
985 .jump()
986 .ok_or(ErrorKind::MissingLabelLocation {
987 name: label.name,
988 index: label.index,
989 })
990 .with_span(span)
991 };
992
993 match inst {
994 AssemblyInst::Jump { label } => {
995 write!(comment, "label:{label}")?;
996 let jump = build_label(label)?;
997 storage
998 .encode(Inst::new(inst::Kind::Jump { jump }))
999 .with_span(span)?;
1000 }
1001 AssemblyInst::JumpIf { addr, label } => {
1002 write!(comment, "label:{label}")?;
1003 let jump = build_label(label)?;
1004 storage
1005 .encode(Inst::new(inst::Kind::JumpIf { cond: addr, jump }))
1006 .with_span(span)?;
1007 }
1008 AssemblyInst::JumpIfNot { addr, label } => {
1009 write!(comment, "label:{label}")?;
1010 let jump = build_label(label)?;
1011 storage
1012 .encode(Inst::new(inst::Kind::JumpIfNot { cond: addr, jump }))
1013 .with_span(span)?;
1014 }
1015 AssemblyInst::IterNext { addr, label, out } => {
1016 write!(comment, "label:{label}")?;
1017 let jump = build_label(label)?;
1018 storage
1019 .encode(Inst::new(inst::Kind::IterNext { addr, jump, out }))
1020 .with_span(span)?;
1021 }
1022 AssemblyInst::Raw { raw } => {
1023 let kind = match raw {
1026 inst @ inst::Kind::Call {
1027 hash,
1028 addr,
1029 args,
1030 out,
1031 } => {
1032 if let Some(UnitFn::Offset { offset, call, .. }) =
1033 self.functions.get(&hash)
1034 {
1035 inst::Kind::CallOffset {
1036 offset: *offset,
1037 call: *call,
1038 addr,
1039 args,
1040 out,
1041 }
1042 } else {
1043 inst
1044 }
1045 }
1046 kind => kind,
1047 };
1048
1049 storage.encode(Inst::new(kind)).with_span(span)?;
1050 }
1051 }
1052
1053 if let Some(c) = assembly.comments.get(&pos) {
1054 if !comment.is_empty() {
1055 comment.try_push_str("; ")?;
1056 }
1057
1058 comment.try_push_str(c)?;
1059 }
1060
1061 let comment = if comment.is_empty() {
1062 None
1063 } else {
1064 Some(comment.try_into()?)
1065 };
1066
1067 self.debug_mut()?.instructions.try_insert(
1068 at,
1069 DebugInst::new(location.source_id, span, comment, labels),
1070 )?;
1071 }
1072
1073 Ok(())
1074 }
1075}
1076
1077pub(crate) struct DropSet<'a> {
1079 builder: &'a mut UnitBuilder,
1080 addresses: Vec<Address>,
1081}
1082
1083impl DropSet<'_> {
1084 pub(crate) fn push(&mut self, addr: Address) -> alloc::Result<()> {
1086 self.addresses.try_push(addr)
1087 }
1088
1089 pub(crate) fn finish(self) -> alloc::Result<Option<usize>> {
1090 if self.addresses.is_empty() {
1091 return Ok(None);
1092 }
1093
1094 if let Some(set) = self.builder.drop_sets_rev.get(&self.addresses) {
1095 return Ok(Some(*set));
1096 }
1097
1098 let set = self.builder.drop_sets.len();
1099
1100 self.builder
1101 .drop_sets_rev
1102 .try_insert(self.addresses.try_clone()?, set)?;
1103 self.builder
1104 .drop_sets
1105 .try_push(Arc::copy_from_slice(&self.addresses[..])?)?;
1106 Ok(Some(set))
1107 }
1108}