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, ConstValue, 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<ConstValue>,
89 hash_to_ident: HashMap<Hash, Box<str>>,
91 globals: Vec<Option<ConstValue>>,
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 global_slot(&mut self, hash: Hash) -> alloc::Result<usize> {
185 if let Some(slot) = self.globals_rev.get(&hash).copied() {
186 return Ok(slot);
187 }
188
189 let slot = self.globals.len();
190 self.globals.try_push(None)?;
191 self.globals_rev.try_insert(hash, slot)?;
192 Ok(slot)
193 }
194
195 pub(crate) fn insert_global(
197 &mut self,
198 hash: Hash,
199 path: &Item,
200 init: Option<ConstValue>,
201 debug_info: bool,
202 ) -> alloc::Result<usize> {
203 let slot = self.global_slot(hash)?;
204
205 if let Some(init) = init {
206 self.globals[slot] = Some(init);
207 }
208
209 if debug_info {
210 let debug = self.debug_mut()?;
211
212 while debug.globals.len() <= slot {
213 debug.globals.try_push(DebugGlobal::new(ItemBuf::new()))?;
214 }
215
216 debug.globals[slot] = DebugGlobal::new(path.try_to_owned()?);
217 }
218
219 Ok(slot)
220 }
221
222 pub(crate) fn new_static_string(
227 &mut self,
228 span: &dyn Spanned,
229 current: &str,
230 ) -> compile::Result<usize> {
231 let current = StaticString::new(current)?;
232 let hash = current.hash();
233
234 if let Some(existing_slot) = self.static_string_rev.get(&hash).copied() {
235 let Some(existing) = self.static_strings.get(existing_slot) else {
236 return Err(compile::Error::new(
237 span,
238 ErrorKind::StaticStringMissing {
239 hash,
240 slot: existing_slot,
241 },
242 ));
243 };
244
245 if ***existing != *current {
246 return Err(compile::Error::new(
247 span,
248 ErrorKind::StaticStringHashConflict {
249 hash,
250 current: (*current).try_clone()?,
251 existing: (***existing).try_clone()?,
252 },
253 ));
254 }
255
256 return Ok(existing_slot);
257 }
258
259 let new_slot = self.static_strings.len();
260 self.static_strings.try_push(Arc::try_new(current)?)?;
261 self.static_string_rev.try_insert(hash, new_slot)?;
262 Ok(new_slot)
263 }
264
265 pub(crate) fn new_static_bytes(
270 &mut self,
271 span: &dyn Spanned,
272 current: &[u8],
273 ) -> compile::Result<usize> {
274 let hash = Hash::static_bytes(current);
275
276 if let Some(existing_slot) = self.static_bytes_rev.get(&hash).copied() {
277 let existing = self.static_bytes.get(existing_slot).ok_or_else(|| {
278 compile::Error::new(
279 span,
280 ErrorKind::StaticBytesMissing {
281 hash,
282 slot: existing_slot,
283 },
284 )
285 })?;
286
287 if &**existing != current {
288 return Err(compile::Error::new(
289 span,
290 ErrorKind::StaticBytesHashConflict {
291 hash,
292 current: current.try_to_owned()?,
293 existing: existing.try_clone()?,
294 },
295 ));
296 }
297
298 return Ok(existing_slot);
299 }
300
301 let new_slot = self.static_bytes.len();
302 self.static_bytes.try_push(current.try_to_owned()?)?;
303 self.static_bytes_rev.try_insert(hash, new_slot)?;
304 Ok(new_slot)
305 }
306
307 pub(crate) fn new_static_object_keys_iter(
310 &mut self,
311 span: &dyn Spanned,
312 current: impl IntoIterator<Item: AsRef<str>>,
313 ) -> compile::Result<usize> {
314 let current = current
315 .into_iter()
316 .map(|s| s.as_ref().try_to_owned())
317 .try_collect::<alloc::Result<Box<_>>>()??;
318
319 self.new_static_object_keys(span, current)
320 }
321
322 pub(crate) fn new_static_object_keys(
325 &mut self,
326 span: &dyn Spanned,
327 current: Box<[String]>,
328 ) -> compile::Result<usize> {
329 let hash = Hash::object_keys(¤t[..]);
330
331 if let Some(existing_slot) = self.static_object_keys_rev.get(&hash).copied() {
332 let existing = self.static_object_keys.get(existing_slot).ok_or_else(|| {
333 compile::Error::new(
334 span,
335 ErrorKind::StaticObjectKeysMissing {
336 hash,
337 slot: existing_slot,
338 },
339 )
340 })?;
341
342 if *existing != current {
343 return Err(compile::Error::new(
344 span,
345 ErrorKind::StaticObjectKeysHashConflict {
346 hash,
347 current,
348 existing: existing.try_clone()?,
349 },
350 ));
351 }
352
353 return Ok(existing_slot);
354 }
355
356 let new_slot = self.static_object_keys.len();
357 self.static_object_keys.try_push(current)?;
358 self.static_object_keys_rev.try_insert(hash, new_slot)?;
359 Ok(new_slot)
360 }
361
362 pub(crate) fn insert_meta(
364 &mut self,
365 span: &dyn Spanned,
366 meta: &meta::Meta,
367 pool: &Pool,
368 query: &mut QueryInner,
369 debug_info: bool,
370 ) -> compile::Result<()> {
371 debug_assert_eq! {
372 pool.item_type_hash(meta.item_meta.item),
373 meta.hash,
374 };
375
376 match meta.kind {
377 meta::Kind::Type { .. } => {
378 let rtti = Arc::try_new(Rtti {
379 kind: RttiKind::Empty,
380 hash: meta.hash,
381 variant_hash: Hash::EMPTY,
382 item: pool.item(meta.item_meta.item).try_to_owned()?,
383 fields: HashMap::default(),
384 })?;
385
386 self.constants
387 .try_insert(
388 Hash::associated_function(meta.hash, &Protocol::INTO_TYPE_NAME),
389 ConstValue::try_from(rtti.item.try_to_string()?)?,
390 )
391 .with_span(span)?;
392
393 if self
394 .rtti
395 .try_insert(meta.hash, rtti)
396 .with_span(span)?
397 .is_some()
398 {
399 return Err(compile::Error::new(
400 span,
401 ErrorKind::TypeRttiConflict { hash: meta.hash },
402 ));
403 }
404 }
405 meta::Kind::Struct {
406 fields: meta::Fields::Empty,
407 enum_hash: Hash::EMPTY,
408 ..
409 } => {
410 let info = UnitFn::EmptyStruct { hash: meta.hash };
411
412 let signature = DebugSignature::new(
413 pool.item(meta.item_meta.item).try_to_owned()?,
414 DebugArgs::EmptyArgs,
415 );
416
417 let rtti = Arc::try_new(Rtti {
418 kind: RttiKind::Empty,
419 hash: meta.hash,
420 variant_hash: Hash::EMPTY,
421 item: pool.item(meta.item_meta.item).try_to_owned()?,
422 fields: HashMap::default(),
423 })?;
424
425 if self
426 .rtti
427 .try_insert(meta.hash, rtti)
428 .with_span(span)?
429 .is_some()
430 {
431 return Err(compile::Error::new(
432 span,
433 ErrorKind::TypeRttiConflict { hash: meta.hash },
434 ));
435 }
436
437 if self
438 .functions
439 .try_insert(meta.hash, info)
440 .with_span(span)?
441 .is_some()
442 {
443 return Err(compile::Error::new(
444 span,
445 ErrorKind::FunctionConflict {
446 existing: signature,
447 },
448 ));
449 }
450
451 self.constants
452 .try_insert(
453 Hash::associated_function(meta.hash, &Protocol::INTO_TYPE_NAME),
454 ConstValue::try_from(signature.path.try_to_string()?)?,
455 )
456 .with_span(span)?;
457
458 self.debug_mut()?
459 .functions
460 .try_insert(meta.hash, signature)?;
461 }
462 meta::Kind::Struct {
463 fields: meta::Fields::Empty,
464 enum_hash,
465 ..
466 } => {
467 let rtti = Arc::try_new(Rtti {
468 kind: RttiKind::Empty,
469 hash: enum_hash,
470 variant_hash: meta.hash,
471 item: pool.item(meta.item_meta.item).try_to_owned()?,
472 fields: HashMap::default(),
473 })?;
474
475 if self
476 .rtti
477 .try_insert(meta.hash, rtti)
478 .with_span(span)?
479 .is_some()
480 {
481 return Err(compile::Error::new(
482 span,
483 ErrorKind::RttiConflict { hash: meta.hash },
484 ));
485 }
486
487 let info = UnitFn::EmptyStruct { hash: meta.hash };
488
489 let signature = DebugSignature::new(
490 pool.item(meta.item_meta.item).try_to_owned()?,
491 DebugArgs::EmptyArgs,
492 );
493
494 if self
495 .functions
496 .try_insert(meta.hash, info)
497 .with_span(span)?
498 .is_some()
499 {
500 return Err(compile::Error::new(
501 span,
502 ErrorKind::FunctionConflict {
503 existing: signature,
504 },
505 ));
506 }
507
508 self.debug_mut()?
509 .functions
510 .try_insert(meta.hash, signature)?;
511 }
512 meta::Kind::Struct {
513 fields: meta::Fields::Unnamed(args),
514 enum_hash: Hash::EMPTY,
515 ..
516 } => {
517 let info = UnitFn::TupleStruct {
518 hash: meta.hash,
519 args,
520 };
521
522 let signature = DebugSignature::new(
523 pool.item(meta.item_meta.item).try_to_owned()?,
524 DebugArgs::TupleArgs(args),
525 );
526
527 let rtti = Arc::try_new(Rtti {
528 kind: RttiKind::Tuple,
529 hash: meta.hash,
530 variant_hash: Hash::EMPTY,
531 item: pool.item(meta.item_meta.item).try_to_owned()?,
532 fields: HashMap::default(),
533 })?;
534
535 if self
536 .rtti
537 .try_insert(meta.hash, rtti)
538 .with_span(span)?
539 .is_some()
540 {
541 return Err(compile::Error::new(
542 span,
543 ErrorKind::TypeRttiConflict { hash: meta.hash },
544 ));
545 }
546
547 if self
548 .functions
549 .try_insert(meta.hash, info)
550 .with_span(span)?
551 .is_some()
552 {
553 return Err(compile::Error::new(
554 span,
555 ErrorKind::FunctionConflict {
556 existing: signature,
557 },
558 ));
559 }
560
561 self.constants
562 .try_insert(
563 Hash::associated_function(meta.hash, &Protocol::INTO_TYPE_NAME),
564 ConstValue::try_from(signature.path.try_to_string()?)?,
565 )
566 .with_span(span)?;
567
568 self.debug_mut()?
569 .functions
570 .try_insert(meta.hash, signature)?;
571 }
572 meta::Kind::Struct {
573 fields: meta::Fields::Unnamed(args),
574 enum_hash,
575 ..
576 } => {
577 let rtti = Arc::try_new(Rtti {
578 kind: RttiKind::Tuple,
579 hash: enum_hash,
580 variant_hash: meta.hash,
581 item: pool.item(meta.item_meta.item).try_to_owned()?,
582 fields: HashMap::default(),
583 })?;
584
585 if self
586 .rtti
587 .try_insert(meta.hash, rtti)
588 .with_span(span)?
589 .is_some()
590 {
591 return Err(compile::Error::new(
592 span,
593 ErrorKind::RttiConflict { hash: meta.hash },
594 ));
595 }
596
597 let info = UnitFn::TupleStruct {
598 hash: meta.hash,
599 args,
600 };
601
602 let signature = DebugSignature::new(
603 pool.item(meta.item_meta.item).try_to_owned()?,
604 DebugArgs::TupleArgs(args),
605 );
606
607 if self
608 .functions
609 .try_insert(meta.hash, info)
610 .with_span(span)?
611 .is_some()
612 {
613 return Err(compile::Error::new(
614 span,
615 ErrorKind::FunctionConflict {
616 existing: signature,
617 },
618 ));
619 }
620
621 self.debug_mut()?
622 .functions
623 .try_insert(meta.hash, signature)?;
624 }
625 meta::Kind::Struct {
626 fields: meta::Fields::Named(ref named),
627 enum_hash: Hash::EMPTY,
628 ..
629 } => {
630 let rtti = Arc::try_new(Rtti {
631 kind: RttiKind::Struct,
632 hash: meta.hash,
633 variant_hash: Hash::EMPTY,
634 item: pool.item(meta.item_meta.item).try_to_owned()?,
635 fields: named.to_fields()?,
636 })?;
637
638 self.constants
639 .try_insert(
640 Hash::associated_function(meta.hash, &Protocol::INTO_TYPE_NAME),
641 ConstValue::try_from(rtti.item.try_to_string()?)?,
642 )
643 .with_span(span)?;
644
645 if self
646 .rtti
647 .try_insert(meta.hash, rtti)
648 .with_span(span)?
649 .is_some()
650 {
651 return Err(compile::Error::new(
652 span,
653 ErrorKind::TypeRttiConflict { hash: meta.hash },
654 ));
655 }
656 }
657 meta::Kind::Struct {
658 fields: meta::Fields::Named(ref named),
659 enum_hash,
660 ..
661 } => {
662 let rtti = Arc::try_new(Rtti {
663 kind: RttiKind::Struct,
664 hash: enum_hash,
665 variant_hash: meta.hash,
666 item: pool.item(meta.item_meta.item).try_to_owned()?,
667 fields: named.to_fields()?,
668 })?;
669
670 if self
671 .rtti
672 .try_insert(meta.hash, rtti)
673 .with_span(span)?
674 .is_some()
675 {
676 return Err(compile::Error::new(
677 span,
678 ErrorKind::RttiConflict { hash: meta.hash },
679 ));
680 }
681 }
682 meta::Kind::Enum { .. } => {
683 let name = pool
684 .item(meta.item_meta.item)
685 .try_to_string()
686 .with_span(span)?;
687
688 self.constants
689 .try_insert(
690 Hash::associated_function(meta.hash, &Protocol::INTO_TYPE_NAME),
691 ConstValue::try_from(name)?,
692 )
693 .with_span(span)?;
694 }
695 meta::Kind::Const => {
696 let Some(const_value) = query.get_const_value(meta.hash) else {
697 return Err(compile::Error::msg(
698 span,
699 try_format!("Missing constant for hash {}", meta.hash),
700 ));
701 };
702
703 let value = const_value.try_clone().with_span(span)?;
704
705 self.constants
706 .try_insert(meta.hash, value)
707 .with_span(span)?;
708 }
709 meta::Kind::Static => {
710 let init = match query.get_static_init(meta.hash) {
711 Some(init) => Some(init.try_clone().with_span(span)?),
712 None => None,
713 };
714
715 self.insert_global(meta.hash, pool.item(meta.item_meta.item), init, debug_info)
716 .with_span(span)?;
717 }
718 meta::Kind::Macro => (),
719 meta::Kind::AttributeMacro => (),
720 meta::Kind::Function { .. } => (),
721 meta::Kind::Closure { .. } => (),
722 meta::Kind::AsyncBlock { .. } => (),
723 meta::Kind::ConstFn => (),
724 meta::Kind::Import { .. } => (),
725 meta::Kind::Alias { .. } => (),
726 meta::Kind::Module => (),
727 meta::Kind::Trait => (),
728 }
729
730 Ok(())
731 }
732
733 pub(crate) fn new_assembly(&self, location: Location) -> Assembly {
735 Assembly::new(location, self.label_count)
736 }
737
738 pub(crate) fn new_function_reexport(
740 &mut self,
741 location: Location,
742 item: &Item,
743 target: &Item,
744 ) -> compile::Result<()> {
745 let hash = Hash::type_hash(item);
746 let target = Hash::type_hash(target);
747
748 if self.reexports.try_insert(hash, target)?.is_some() {
749 return Err(compile::Error::new(
750 location.span,
751 ErrorKind::FunctionReExportConflict { hash },
752 ));
753 }
754
755 Ok(())
756 }
757
758 pub(crate) fn new_function(
760 &mut self,
761 location: Location,
762 item: &Item,
763 instance: Option<(Hash, &str)>,
764 args: usize,
765 captures: Option<usize>,
766 assembly: Assembly,
767 call: Call,
768 debug_args: Box<[Box<str>]>,
769 unit_storage: &mut dyn UnitEncoder,
770 size: usize,
771 ) -> compile::Result<()> {
772 tracing::trace!("instance fn: {}", item);
773
774 let offset = unit_storage.offset();
775
776 let info = UnitFn::Offset {
777 offset,
778 call,
779 args,
780 captures,
781 };
782 let signature = DebugSignature::new(item.try_to_owned()?, DebugArgs::Named(debug_args));
783
784 if let Some((type_hash, name)) = instance {
785 let instance_fn = Hash::associated_function(type_hash, name);
786
787 if self
788 .functions
789 .try_insert(instance_fn, info)
790 .with_span(location.span)?
791 .is_some()
792 {
793 return Err(compile::Error::new(
794 location.span,
795 ErrorKind::FunctionConflict {
796 existing: signature,
797 },
798 ));
799 }
800
801 self.debug_mut()?
802 .functions
803 .try_insert(instance_fn, signature.try_clone()?)?;
804 }
805
806 let hash = Hash::type_hash(item);
807
808 if self
809 .functions
810 .try_insert(hash, info)
811 .with_span(location.span)?
812 .is_some()
813 {
814 return Err(compile::Error::new(
815 location.span,
816 ErrorKind::FunctionConflict {
817 existing: signature,
818 },
819 ));
820 }
821
822 self.constants
823 .try_insert(
824 Hash::associated_function(hash, &Protocol::INTO_TYPE_NAME),
825 ConstValue::try_from(signature.path.try_to_string().with_span(location.span)?)?,
826 )
827 .with_span(location.span)?;
828
829 self.debug_mut()?.functions.try_insert(hash, signature)?;
830 self.functions_rev.try_insert(offset, hash)?;
831 self.add_assembly(location, assembly, unit_storage, size)?;
832 Ok(())
833 }
834
835 pub(crate) fn link(
840 &mut self,
841 context: &Context,
842 diagnostics: &mut Diagnostics,
843 ) -> alloc::Result<()> {
844 for (hash, spans) in &self.required_functions {
845 if self.functions.get(hash).is_none() && context.lookup_function(*hash).is_none() {
846 diagnostics.error(
847 SourceId::empty(),
848 LinkerError::MissingFunction {
849 hash: *hash,
850 spans: spans.try_clone()?,
851 },
852 )?;
853 }
854 }
855
856 Ok(())
857 }
858
859 fn debug_mut(&mut self) -> alloc::Result<&mut DebugInfo> {
861 if self.debug.is_none() {
862 self.debug = Some(Box::try_new(DebugInfo::default())?);
863 }
864
865 Ok(self.debug.as_mut().unwrap())
866 }
867
868 fn add_assembly(
870 &mut self,
871 location: Location,
872 assembly: Assembly,
873 storage: &mut dyn UnitEncoder,
874 size: usize,
875 ) -> compile::Result<()> {
876 self.label_count = assembly.label_count;
877
878 storage
879 .encode(Inst::new(inst::Kind::Allocate { size }))
880 .with_span(location.span)?;
881
882 let base = storage.extend_offsets(assembly.labels.len())?;
883
884 self.required_functions
885 .try_extend(assembly.required_functions)?;
886
887 for (offset, (_, labels)) in &assembly.labels {
888 for label in labels {
889 if let Some(jump) = label.jump() {
890 label.set_jump(storage.label_jump(base, *offset, jump));
891 }
892 }
893 }
894
895 for (pos, (inst, span)) in assembly.instructions.into_iter().enumerate() {
896 let mut comment = String::new();
897
898 let at = storage.offset();
899
900 let mut labels = Vec::new();
901
902 for label in assembly
903 .labels
904 .get(&pos)
905 .map(|e| e.1.as_slice())
906 .unwrap_or_default()
907 {
908 if let Some(index) = label.jump() {
909 storage.mark_offset(index);
910 }
911
912 labels.try_push(label.to_debug_label())?;
913 }
914
915 let build_label = |label: Label| {
916 label
917 .jump()
918 .ok_or(ErrorKind::MissingLabelLocation {
919 name: label.name,
920 index: label.index,
921 })
922 .with_span(span)
923 };
924
925 match inst {
926 AssemblyInst::Jump { label } => {
927 write!(comment, "label:{label}")?;
928 let jump = build_label(label)?;
929 storage
930 .encode(Inst::new(inst::Kind::Jump { jump }))
931 .with_span(span)?;
932 }
933 AssemblyInst::JumpIf { addr, label } => {
934 write!(comment, "label:{label}")?;
935 let jump = build_label(label)?;
936 storage
937 .encode(Inst::new(inst::Kind::JumpIf { cond: addr, jump }))
938 .with_span(span)?;
939 }
940 AssemblyInst::JumpIfNot { addr, label } => {
941 write!(comment, "label:{label}")?;
942 let jump = build_label(label)?;
943 storage
944 .encode(Inst::new(inst::Kind::JumpIfNot { cond: addr, jump }))
945 .with_span(span)?;
946 }
947 AssemblyInst::IterNext { addr, label, out } => {
948 write!(comment, "label:{label}")?;
949 let jump = build_label(label)?;
950 storage
951 .encode(Inst::new(inst::Kind::IterNext { addr, jump, out }))
952 .with_span(span)?;
953 }
954 AssemblyInst::Raw { raw } => {
955 let kind = match raw {
958 inst @ inst::Kind::Call {
959 hash,
960 addr,
961 args,
962 out,
963 } => {
964 if let Some(UnitFn::Offset { offset, call, .. }) =
965 self.functions.get(&hash)
966 {
967 inst::Kind::CallOffset {
968 offset: *offset,
969 call: *call,
970 addr,
971 args,
972 out,
973 }
974 } else {
975 inst
976 }
977 }
978 kind => kind,
979 };
980
981 storage.encode(Inst::new(kind)).with_span(span)?;
982 }
983 }
984
985 if let Some(c) = assembly.comments.get(&pos) {
986 if !comment.is_empty() {
987 comment.try_push_str("; ")?;
988 }
989
990 comment.try_push_str(c)?;
991 }
992
993 let comment = if comment.is_empty() {
994 None
995 } else {
996 Some(comment.try_into()?)
997 };
998
999 self.debug_mut()?.instructions.try_insert(
1000 at,
1001 DebugInst::new(location.source_id, span, comment, labels),
1002 )?;
1003 }
1004
1005 Ok(())
1006 }
1007}
1008
1009pub(crate) struct DropSet<'a> {
1011 builder: &'a mut UnitBuilder,
1012 addresses: Vec<Address>,
1013}
1014
1015impl DropSet<'_> {
1016 pub(crate) fn push(&mut self, addr: Address) -> alloc::Result<()> {
1018 self.addresses.try_push(addr)
1019 }
1020
1021 pub(crate) fn finish(self) -> alloc::Result<Option<usize>> {
1022 if self.addresses.is_empty() {
1023 return Ok(None);
1024 }
1025
1026 if let Some(set) = self.builder.drop_sets_rev.get(&self.addresses) {
1027 return Ok(Some(*set));
1028 }
1029
1030 let set = self.builder.drop_sets.len();
1031
1032 self.builder
1033 .drop_sets_rev
1034 .try_insert(self.addresses.try_clone()?, set)?;
1035 self.builder
1036 .drop_sets
1037 .try_push(Arc::copy_from_slice(&self.addresses[..])?)?;
1038 Ok(Some(set))
1039 }
1040}