1#[cfg(feature = "arbitrary")]
9mod arbitrary;
10mod builder;
11mod serialization;
12mod types;
13
14use alloc::{sync::Arc, vec::Vec};
15
16pub use builder::*;
17use miden_core::mast::{MastForestRootMap, MastNodeId};
18#[cfg(all(feature = "arbitrary", test))]
19use miden_core::serde::{Deserializable, Serializable};
20use miden_debug_types::{Location, Uri};
21use miden_utils_indexing::{Idx, IndexVec};
22pub use types::*;
23
24type FxHashMap<K, V> = hashbrown::HashMap<K, V, rustc_hash::FxBuildHasher>;
25type FxHashSet<K> = hashbrown::HashSet<K, rustc_hash::FxBuildHasher>;
26
27pub const DEBUG_INFO_VERSION: u8 = 3;
28
29pub const MAX_DEBUG_INFO_PAYLOAD_SIZE: usize = 16 * 1024 * 1024;
34
35pub const MAX_DEBUG_INFO_STRING_ROWS: usize = 100_000;
37
38pub const MAX_DEBUG_INFO_STRING_SIZE: usize = 4 * 1024;
40
41pub const MAX_DEBUG_INFO_TYPE_ROWS: usize = 1_000_000;
43
44#[cfg_attr(
55 all(feature = "arbitrary", test),
56 miden_test_serialization_macros::serialization_test
57)]
58pub type PackageDebugInfo = DebugInfo<MastNodeId, DebugSourceNodeId>;
59
60#[derive(Eq, PartialEq)]
66pub struct DebugInfo<Exec: Idx, Src: Idx> {
67 version: u8,
69 strings: IndexVec<DebugStringIdx, Arc<str>>,
71 files: IndexVec<DebugFileIdx, DebugFileInfo>,
77 locations: IndexVec<DebugLocIdx, DebugLoc>,
81 types: IndexVec<DebugTypeIdx, DebugTypeInfo>,
83 functions: IndexVec<DebugFunctionIdx, FunctionInfo<Src>>,
92 nodes: IndexVec<Src, SourceNode<Exec, Src>>,
98 roots: Vec<Src>,
102 error_messages: Vec<DebugErrorMessage>,
104}
105
106#[derive(Clone, Debug, Default)]
111pub struct DebugInfoTableRemapping {
112 strings: IndexVec<DebugStringIdx, DebugStringIdx>,
113 files: IndexVec<DebugFileIdx, DebugFileIdx>,
114 locations: IndexVec<DebugLocIdx, DebugLocIdx>,
115 types: IndexVec<DebugTypeIdx, DebugTypeIdx>,
116 functions: IndexVec<DebugFunctionIdx, DebugFunctionIdx>,
119}
120
121impl DebugInfoTableRemapping {
122 pub fn string(&self, index: DebugStringIdx) -> Option<DebugStringIdx> {
124 self.strings.get(index).copied()
125 }
126
127 pub fn file(&self, index: DebugFileIdx) -> Option<DebugFileIdx> {
129 self.files.get(index).copied()
130 }
131
132 pub fn location(&self, index: DebugLocIdx) -> Option<DebugLocIdx> {
134 self.locations.get(index).copied()
135 }
136
137 pub fn ty(&self, index: DebugTypeIdx) -> Option<DebugTypeIdx> {
139 self.types.get(index).copied()
140 }
141
142 pub fn function(&self, index: DebugFunctionIdx) -> Option<DebugFunctionIdx> {
144 self.functions.get(index).copied()
145 }
146}
147
148impl<Exec: Idx, Src: Idx> Default for DebugInfo<Exec, Src> {
152 fn default() -> Self {
153 Self {
154 version: DEBUG_INFO_VERSION,
155 strings: Default::default(),
156 files: Default::default(),
157 locations: Default::default(),
158 types: Default::default(),
159 functions: Default::default(),
160 nodes: Default::default(),
161 roots: Default::default(),
162 error_messages: Default::default(),
163 }
164 }
165}
166
167impl<Exec, Src> Clone for DebugInfo<Exec, Src>
168where
169 Exec: Idx + Clone,
170 Src: Idx + Clone,
171{
172 fn clone(&self) -> Self {
173 Self {
174 version: self.version,
175 strings: self.strings.clone(),
176 files: self.files.clone(),
177 locations: self.locations.clone(),
178 types: self.types.clone(),
179 functions: self.functions.clone(),
180 nodes: self.nodes.clone(),
181 roots: self.roots.clone(),
182 error_messages: self.error_messages.clone(),
183 }
184 }
185}
186
187impl<Exec, Src> core::fmt::Debug for DebugInfo<Exec, Src>
188where
189 Exec: Idx + core::fmt::Debug,
190 Src: Idx + core::fmt::Debug,
191{
192 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
193 f.debug_struct("DebugInfo")
194 .field("version", &self.version)
195 .field("strings", &self.strings)
196 .field("files", &self.files)
197 .field("locations", &self.locations)
198 .field("types", &self.types)
199 .field("functions", &self.functions)
200 .field("nodes", &self.nodes)
201 .field("roots", &self.roots)
202 .field("error_messages", &self.error_messages)
203 .finish()
204 }
205}
206
207impl<Exec: Idx, Src: Idx> core::ops::Index<DebugStringIdx> for DebugInfo<Exec, Src> {
211 type Output = Arc<str>;
212
213 fn index(&self, index: DebugStringIdx) -> &Self::Output {
214 &self.strings[index]
215 }
216}
217
218impl<Exec: Idx, Src: Idx> core::ops::Index<DebugFileIdx> for DebugInfo<Exec, Src> {
219 type Output = DebugFileInfo;
220
221 fn index(&self, index: DebugFileIdx) -> &Self::Output {
222 &self.files[index]
223 }
224}
225
226impl<Exec: Idx, Src: Idx> core::ops::Index<DebugFunctionIdx> for DebugInfo<Exec, Src> {
227 type Output = FunctionInfo<Src>;
228
229 fn index(&self, index: DebugFunctionIdx) -> &Self::Output {
230 &self.functions[index]
231 }
232}
233
234impl<Exec: Idx, Src: Idx> core::ops::Index<DebugTypeIdx> for DebugInfo<Exec, Src> {
235 type Output = DebugTypeInfo;
236
237 fn index(&self, index: DebugTypeIdx) -> &Self::Output {
238 &self.types[index]
239 }
240}
241
242impl<Exec: Idx, Src: Idx> core::ops::Index<DebugLocIdx> for DebugInfo<Exec, Src> {
243 type Output = DebugLoc;
244
245 fn index(&self, index: DebugLocIdx) -> &Self::Output {
246 &self.locations[index]
247 }
248}
249
250pub trait SourceNodeIdMarker: Idx + core::hash::Hash {}
254
255impl<Exec: Idx, Src: SourceNodeIdMarker> core::ops::Index<Src> for DebugInfo<Exec, Src> {
256 type Output = SourceNode<Exec, Src>;
257
258 fn index(&self, index: Src) -> &Self::Output {
259 &self.nodes[index]
260 }
261}
262
263impl<Exec: Idx, Src: Idx> DebugInfo<Exec, Src> {
267 pub fn version(&self) -> u8 {
269 self.version
270 }
271
272 pub fn strings(&self) -> &IndexVec<DebugStringIdx, Arc<str>> {
274 &self.strings
275 }
276
277 pub fn get_string(&self, idx: DebugStringIdx) -> Option<Arc<str>> {
279 self.strings.get(idx).cloned()
280 }
281
282 pub fn files(&self) -> &IndexVec<DebugFileIdx, DebugFileInfo> {
284 &self.files
285 }
286
287 pub fn get_file(&self, idx: DebugFileIdx) -> Option<&DebugFileInfo> {
289 self.files.get(idx)
290 }
291
292 pub fn get_file_index_by_uri(&self, uri: &Uri) -> Option<DebugFileIdx> {
295 self.files
296 .iter()
297 .position(|file| {
298 self.strings
299 .get(file.path_idx)
300 .map(|path| path.as_ref() == uri.as_str())
301 .unwrap_or(false)
302 })
303 .map(|pos| DebugFileIdx::from(pos as u32))
304 }
305
306 pub fn trim_file_paths(&mut self, mut trimmer: impl FnMut(&str) -> Option<Arc<str>>) {
312 use hashbrown::hash_map::Entry;
313
314 let mut string_indices = FxHashMap::<Arc<str>, DebugStringIdx>::default();
315 for (index, string) in self.strings.iter().enumerate() {
316 string_indices
317 .entry(string.clone())
318 .or_insert_with(|| DebugStringIdx::from(index as u32));
319 }
320
321 let mut remapped_paths = FxHashMap::<DebugStringIdx, DebugStringIdx>::default();
326 for file in self.files.iter_mut() {
327 let old_path_idx = file.path_idx;
328 let new_path_idx = if let Some(new_path_idx) = remapped_paths.get(&old_path_idx) {
329 *new_path_idx
330 } else {
331 let Some(path) = self.strings.get(old_path_idx).cloned() else {
332 continue;
333 };
334 let new_path_idx = match trimmer(path.as_ref()) {
335 None => old_path_idx,
336 Some(new_path) => match string_indices.entry(new_path.clone()) {
337 Entry::Occupied(entry) => *entry.get(),
338 Entry::Vacant(entry) => {
339 let index =
340 self.strings.push(new_path).expect("too many debug info strings");
341 entry.insert(index);
342 index
343 },
344 },
345 };
346 remapped_paths.insert(old_path_idx, new_path_idx);
347 new_path_idx
348 };
349 file.path_idx = new_path_idx;
350 }
351 }
352
353 pub fn types(&self) -> &IndexVec<DebugTypeIdx, DebugTypeInfo> {
355 &self.types
356 }
357
358 pub fn get_type(&self, idx: DebugTypeIdx) -> Option<&DebugTypeInfo> {
360 self.types.get(idx)
361 }
362
363 pub fn locations(&self) -> &IndexVec<DebugLocIdx, DebugLoc> {
365 &self.locations
366 }
367
368 pub fn get_location(&self, idx: DebugLocIdx) -> Option<Location> {
370 let DebugLoc { file_idx, start, end } = self.locations.get(idx)?;
371 let file = self.files.get(*file_idx)?;
372 let uri = self.strings.get(file.path_idx).cloned()?;
373 Some(Location {
374 uri: Uri::from(uri),
375 start: *start,
376 end: *end,
377 })
378 }
379
380 pub fn error_messages(&self) -> &[DebugErrorMessage] {
382 &self.error_messages
383 }
384
385 pub fn error_message(&self, err_code: u64) -> Option<Arc<str>> {
387 self.error_messages
388 .iter()
389 .find(|row| row.err_code == err_code)
390 .and_then(|row| self.strings.get(row.message).cloned())
391 }
392
393 pub fn nodes(&self) -> &IndexVec<Src, SourceNode<Exec, Src>> {
395 &self.nodes
396 }
397
398 pub fn roots(&self) -> &[Src] {
400 &self.roots
401 }
402
403 pub fn source_node(&self, source_node: Src) -> Option<&SourceNode<Exec, Src>> {
405 self.nodes.get(source_node)
406 }
407
408 pub fn functions(&self) -> &[FunctionInfo<Src>] {
410 self.functions.as_slice()
411 }
412
413 pub fn get_function(&self, idx: DebugFunctionIdx) -> Option<&FunctionInfo<Src>> {
415 self.functions.get(idx)
416 }
417
418 pub fn source_roots_for_exec_node(
420 &self,
421 exec_node: Exec,
422 ) -> impl Iterator<Item = (Src, &SourceNode<Exec, Src>)> {
423 self.roots.iter().copied().filter_map(move |source_node_id| {
424 let source_node = self.nodes.get(source_node_id)?;
425 if source_node.exec_node == exec_node {
426 Some((source_node_id, source_node))
427 } else {
428 None
429 }
430 })
431 }
432
433 pub fn unique_source_root_for_exec_node(
435 &self,
436 exec_node: Exec,
437 ) -> Result<Option<Src>, SourceGraphLookupError<Exec, Src>> {
438 let mut roots = self
439 .source_roots_for_exec_node(exec_node)
440 .map(|(source_node_id, _)| source_node_id);
441 let first = roots.next();
442 if roots.next().is_some() {
443 return Err(SourceGraphLookupError::AmbiguousRoot { exec_node });
444 }
445 Ok(first)
446 }
447
448 pub fn child_source_node(
450 &self,
451 parent: Src,
452 child_index: usize,
453 ) -> Result<Option<(Src, &SourceNode<Exec, Src>)>, SourceGraphLookupError<Exec, Src>> {
454 let parent_node = self
455 .source_node(parent)
456 .ok_or(SourceGraphLookupError::MissingSourceNode { source_node: parent })?;
457 let Some(child) = parent_node.children.get(child_index).copied() else {
458 return Ok(None);
459 };
460 let child_node = self
461 .source_node(child)
462 .ok_or(SourceGraphLookupError::MissingSourceNode { source_node: child })?;
463
464 Ok(Some((child, child_node)))
465 }
466
467 pub fn asm_ops_for_source_node(
469 &self,
470 source_node: Src,
471 ) -> impl Iterator<Item = &DebugSourceAsmOp> {
472 self.source_node(source_node).into_iter().flat_map(|node| node.asm_ops.iter())
473 }
474
475 pub fn first_asm_op_for_source_node(&self, source_node: Src) -> Option<&DebugSourceAsmOp> {
477 self.source_node(source_node).and_then(|node| node.asm_ops.first())
478 }
479
480 pub fn asm_op_for_operation(&self, source_node: Src, op_idx: u32) -> Option<&DebugSourceAsmOp> {
482 self.source_node(source_node).and_then(|node| node.asm_op_for_operation(op_idx))
483 }
484
485 pub fn debug_vars_for_source_node(
487 &self,
488 source_node: Src,
489 ) -> impl Iterator<Item = &DebugSourceVar> {
490 self.source_node(source_node)
491 .into_iter()
492 .flat_map(|node| node.debug_vars.iter())
493 }
494
495 pub fn debug_vars_for_operation(
497 &self,
498 source_node: Src,
499 op_idx: u32,
500 ) -> impl Iterator<Item = &DebugSourceVar> {
501 self.debug_vars_for_source_node(source_node)
502 .filter(move |row| row.op_idx == op_idx)
503 }
504
505 pub fn inline_calls_for_source_node(
507 &self,
508 source_node: Src,
509 ) -> impl Iterator<Item = &DebugSourceInlineCall> {
510 self.source_node(source_node)
511 .into_iter()
512 .flat_map(|node| node.inline_calls.iter())
513 }
514
515 pub fn inline_calls_for_operation(
517 &self,
518 source_node: Src,
519 op_idx: u32,
520 ) -> impl Iterator<Item = &DebugSourceInlineCall> {
521 self.inline_calls_for_source_node(source_node)
522 .filter(move |row| row.op_idx == op_idx)
523 }
524}
525
526impl<Exec: Idx, Src: Idx> DebugInfo<Exec, Src> {
527 pub fn merge_tables_into<TargetExec: Idx, TargetSrc: Idx>(
535 &self,
536 target: &mut DebugInfoBuilder<TargetExec, TargetSrc>,
537 ) -> Result<DebugInfoTableRemapping, DebugInfoTableRemapError> {
538 self.validate_shared_table_references()?;
541
542 let mut remapping = DebugInfoTableRemapping::default();
543
544 for (index, string) in self.strings.iter().enumerate() {
545 let source = DebugStringIdx::from(
546 u32::try_from(index).expect("invalid source string table index"),
547 );
548 let inserted = remapping
549 .strings
550 .push(target.add_string(string.clone()))
551 .expect("too many remapped strings");
552 debug_assert_eq!(inserted, source);
553 }
554
555 let type_offset = target.debug_info().types.len();
558 for index in 0..self.types.len() {
559 let source =
560 DebugTypeIdx::from(u32::try_from(index).expect("invalid source type table index"));
561 let destination = DebugTypeIdx::from(
562 u32::try_from(type_offset + index).expect("too many types after merging"),
563 );
564 let inserted = remapping.types.push(destination).expect("too many remapped types");
565 debug_assert_eq!(inserted, source);
566 }
567 for (index, ty) in self.types.iter().enumerate() {
568 let source =
569 DebugTypeIdx::from(u32::try_from(index).expect("invalid source type table index"));
570 let ty = remap_debug_type_info(ty, &remapping.strings, &remapping.types)?;
571 let destination = target.push_type(ty);
572 debug_assert_eq!(destination, remapping.types[source]);
573 }
574
575 for (index, file) in self.files.iter().enumerate() {
576 let source =
577 DebugFileIdx::from(u32::try_from(index).expect("invalid source file table index"));
578 let path_idx = remapping.string(file.path_idx).ok_or(
579 DebugInfoTableRemapError::MissingSourceString { string_idx: file.path_idx },
580 )?;
581 let file = DebugFileInfo::new(path_idx)
582 .with_checksum(*file.checksum().unwrap_or(&DebugFileInfo::EMPTY_CHECKSUM));
583 let inserted = remapping
584 .files
585 .push(target.add_file_info(file))
586 .expect("too many remapped files");
587 debug_assert_eq!(inserted, source);
588 }
589
590 for (index, location) in self.locations.iter().enumerate() {
591 let source = DebugLocIdx::from(
592 u32::try_from(index).expect("invalid source location table index"),
593 );
594 let file_idx = remapping.file(location.file_idx).ok_or(
595 DebugInfoTableRemapError::MissingSourceFile { file_idx: location.file_idx },
596 )?;
597 let location = DebugLoc {
598 file_idx,
599 start: location.start,
600 end: location.end,
601 };
602 let inserted = remapping
603 .locations
604 .push(target.add_location_info(location))
605 .expect("too many remapped locations");
606 debug_assert_eq!(inserted, source);
607 }
608
609 for error_message in self.error_messages() {
610 let message = remapping.string(error_message.message).ok_or(
611 DebugInfoTableRemapError::MissingSourceString { string_idx: error_message.message },
612 )?;
613 target.add_error_message_with_index(error_message.err_code, message);
614 }
615
616 for (index, function) in self.functions().iter().enumerate() {
617 let source = DebugFunctionIdx::from(
618 u32::try_from(index).expect("invalid source function table index"),
619 );
620 let name_idx = remapping.string(function.name_idx).ok_or(
621 DebugInfoTableRemapError::MissingSourceString { string_idx: function.name_idx },
622 )?;
623 let linkage_name_idx = function
624 .linkage_name_idx
625 .try_into_option()
626 .map_err(|err| DebugInfoTableRemapError::InvalidOptionField {
627 context: "debug function linkage name",
628 err,
629 })?
630 .map(|index| {
631 remapping
632 .string(index)
633 .ok_or(DebugInfoTableRemapError::MissingSourceString { string_idx: index })
634 })
635 .transpose()?;
636 let type_idx = function
637 .type_idx
638 .try_into_option()
639 .map_err(|err| DebugInfoTableRemapError::InvalidOptionField {
640 context: "debug function type",
641 err,
642 })?
643 .map(|tid| {
644 remapping.ty(tid).ok_or(DebugInfoTableRemapError::MissingType { type_idx: tid })
645 })
646 .transpose()?;
647 let file_idx = remapping.file(function.file_idx).ok_or(
648 DebugInfoTableRemapError::MissingSourceFile { file_idx: function.file_idx },
649 )?;
650 let function_idx = target.add_function(FunctionInfo {
651 mast_root: function.mast_root,
652 source_node: None.into(),
653 type_idx: type_idx.into(),
654 linkage_name_idx: linkage_name_idx.into(),
655 name_idx,
656 file_idx,
657 line: function.line,
658 column: function.column,
659 });
660 let inserted =
661 remapping.functions.push(function_idx).expect("too many remapped functions");
662 debug_assert_eq!(inserted, source);
663 }
664
665 Ok(remapping)
666 }
667
668 fn validate_shared_table_references(&self) -> Result<(), DebugInfoTableRemapError> {
669 for ty in self.types.iter() {
670 validate_debug_type_info(ty, &self.strings, &self.types)?;
671 }
672 for file in self.files.iter() {
673 if self.strings.get(file.path_idx).is_none() {
674 return Err(DebugInfoTableRemapError::MissingSourceString {
675 string_idx: file.path_idx,
676 });
677 }
678 }
679 for location in self.locations.iter() {
680 if self.files.get(location.file_idx).is_none() {
681 return Err(DebugInfoTableRemapError::MissingSourceFile {
682 file_idx: location.file_idx,
683 });
684 }
685 }
686 for error_message in self.error_messages.iter() {
687 if self.strings.get(error_message.message).is_none() {
688 return Err(DebugInfoTableRemapError::MissingSourceString {
689 string_idx: error_message.message,
690 });
691 }
692 }
693 for function in self.functions.iter() {
694 if self.files.get(function.file_idx).is_none() {
695 return Err(DebugInfoTableRemapError::MissingSourceFile {
696 file_idx: function.file_idx,
697 });
698 }
699 if let Some(tid) = function.type_idx.try_into_option().map_err(|err| {
700 DebugInfoTableRemapError::InvalidOptionField { context: "debug function type", err }
701 })? && self.types.get(tid).is_none()
702 {
703 return Err(DebugInfoTableRemapError::MissingType { type_idx: tid });
704 }
705 if self.strings.get(function.name_idx).is_none() {
706 return Err(DebugInfoTableRemapError::MissingSourceString {
707 string_idx: function.name_idx,
708 });
709 }
710 if let Some(linkage_name_idx) =
711 function.linkage_name_idx.try_into_option().map_err(|err| {
712 DebugInfoTableRemapError::InvalidOptionField {
713 context: "debug function linkage name",
714 err,
715 }
716 })?
717 && self.strings.get(linkage_name_idx).is_none()
718 {
719 return Err(DebugInfoTableRemapError::MissingSourceString {
720 string_idx: linkage_name_idx,
721 });
722 }
723 }
724 Ok(())
725 }
726}
727
728impl<Src: SourceNodeIdMarker> DebugInfo<MastNodeId, Src> {
729 pub fn merge_source_debug<'a>(
738 inputs: impl IntoIterator<Item = (usize, &'a Self)>,
739 root_map: &MastForestRootMap,
740 ) -> Result<Self, DebugInfoMergeError<MastNodeId, Src>>
741 where
742 Src: 'a,
743 {
744 let mut builder = DebugInfoBuilder::default();
745 for (forest_index, debug_info) in inputs {
746 let tables = debug_info
747 .merge_tables_into(&mut builder)
748 .map_err(|error| table_remap_error(forest_index, error))?;
749
750 let mut remapped_nodes = FxHashMap::<Src, Src>::default();
751 let start_node_index = builder.debug_info().nodes().len();
752 for i in 0..debug_info.nodes.len() {
753 let prev_index = Src::from(u32::try_from(i).expect("too many nodes"));
754 let new_index = Src::from(
755 u32::try_from(start_node_index + i).expect("too many nodes after merging"),
756 );
757 remapped_nodes.insert(prev_index, new_index);
758 }
759
760 for (i, source_node) in debug_info.nodes.iter().enumerate() {
761 let prev_index = Src::from(u32::try_from(i).expect("too many nodes"));
762
763 let exec_node = root_map.map_node(forest_index, &source_node.exec_node).ok_or(
764 DebugInfoMergeError::MissingExecNodeMapping {
765 forest_index,
766 exec_node: source_node.exec_node,
767 },
768 )?;
769 let children = source_node
770 .children
771 .iter()
772 .map(|child| {
773 remapped_nodes.get(child).copied().ok_or(
774 DebugInfoMergeError::MissingSourceNodeMapping {
775 forest_index,
776 source_node: *child,
777 },
778 )
779 })
780 .collect::<Result<Vec<_>, _>>()?;
781
782 let mut asm_ops = Vec::with_capacity(source_node.asm_ops.len());
783 for row in source_node.asm_ops.iter() {
784 let location_idx = row
785 .location_idx
786 .try_into_option()
787 .map_err(|err| DebugInfoMergeError::InvalidOptionField {
788 forest_index,
789 context: "debug source assembly op location",
790 err,
791 })?
792 .map(|location_idx| {
793 tables.location(location_idx).ok_or(
794 DebugInfoMergeError::MissingSourceLocationMapping {
795 forest_index,
796 location_idx,
797 },
798 )
799 })
800 .transpose()?;
801 let context_name_idx = tables.string(row.context_name_idx).ok_or(
802 DebugInfoMergeError::MissingSourceStringMapping {
803 forest_index,
804 string_idx: row.context_name_idx,
805 },
806 )?;
807 let op_name_idx = tables.string(row.op_name_idx).ok_or(
808 DebugInfoMergeError::MissingSourceStringMapping {
809 forest_index,
810 string_idx: row.op_name_idx,
811 },
812 )?;
813 asm_ops.push(DebugSourceAsmOp::new(
814 row.op_idx,
815 location_idx,
816 context_name_idx,
817 op_name_idx,
818 row.num_cycles,
819 ));
820 }
821
822 let mut debug_vars = Vec::with_capacity(source_node.debug_vars.len());
823 for row in source_node.debug_vars.iter() {
824 let name_idx = tables.string(row.name_idx).ok_or(
825 DebugInfoMergeError::MissingSourceStringMapping {
826 forest_index,
827 string_idx: row.name_idx,
828 },
829 )?;
830
831 let location_idx = row
832 .location_idx
833 .map(|idx| {
834 tables.location(idx).ok_or(
835 DebugInfoMergeError::MissingSourceLocationMapping {
836 forest_index,
837 location_idx: idx,
838 },
839 )
840 })
841 .transpose()?;
842 let type_id = row
843 .type_id
844 .map(|idx| {
845 tables.ty(idx).ok_or(DebugInfoMergeError::MissingTypeMapping {
846 forest_index,
847 type_idx: idx,
848 })
849 })
850 .transpose()?;
851 debug_vars.push(DebugSourceVar {
852 op_idx: row.op_idx,
853 name_idx,
854 type_id,
855 arg_idx: row.arg_idx,
856 location_idx,
857 value_location: row.value_location.clone(),
858 });
859 }
860 let new_index = builder
861 .debug_info_mut()
862 .nodes
863 .push(SourceNode {
864 exec_node,
865 children,
866 op_start: source_node.op_start,
867 op_end: source_node.op_end,
868 asm_ops,
869 debug_vars,
870 inline_calls: Vec::with_capacity(source_node.inline_calls.len()),
871 })
872 .expect("too many nodes");
873 debug_assert_eq!(new_index, remapped_nodes[&prev_index],);
874 }
875
876 for (index, function) in debug_info.functions().iter().enumerate() {
877 let Some(previous_source_node) =
878 function.source_node.try_into_option().map_err(|err| {
879 DebugInfoMergeError::InvalidOptionField {
880 forest_index,
881 context: "debug function source node",
882 err,
883 }
884 })?
885 else {
886 continue;
887 };
888 let source_node = remapped_nodes.get(&previous_source_node).copied().ok_or(
889 DebugInfoMergeError::MissingSourceNodeMapping {
890 forest_index,
891 source_node: previous_source_node,
892 },
893 )?;
894 let previous_function = DebugFunctionIdx::from(
895 u32::try_from(index).expect("invalid source function table index"),
896 );
897 let function = tables.function(previous_function).ok_or(
898 DebugInfoMergeError::MissingFunctionMapping {
899 forest_index,
900 function_idx: previous_function,
901 },
902 )?;
903 builder.set_function_source_node(function, source_node);
904 }
905
906 for root in debug_info.roots().iter().copied() {
907 builder.debug_info_mut().roots.push(remapped_nodes.get(&root).copied().ok_or(
908 DebugInfoMergeError::MissingSourceNodeMapping {
909 forest_index,
910 source_node: root,
911 },
912 )?);
913 }
914
915 for (prev, new) in remapped_nodes.iter() {
916 let source_node = debug_info.source_node(*prev).unwrap();
917 if source_node.inline_calls.is_empty() {
918 continue;
919 }
920 let target_node = &mut builder[*new];
921 for row in source_node.inline_calls.iter() {
922 let callee_idx = tables.function(row.callee_idx).ok_or(
923 DebugInfoMergeError::MissingFunctionMapping {
924 forest_index,
925 function_idx: row.callee_idx,
926 },
927 )?;
928 let loc_idx = tables.location(row.loc_idx).ok_or(
929 DebugInfoMergeError::MissingSourceLocationMapping {
930 forest_index,
931 location_idx: row.loc_idx,
932 },
933 )?;
934 target_node.inline_calls.push(DebugSourceInlineCall {
935 op_idx: row.op_idx,
936 callee_idx,
937 loc_idx,
938 });
939 }
940 }
941 }
942
943 Ok(*builder.build())
944 }
945}
946
947fn validate_debug_type_info(
948 ty: &DebugTypeInfo,
949 strings: &IndexVec<DebugStringIdx, Arc<str>>,
950 types: &IndexVec<DebugTypeIdx, DebugTypeInfo>,
951) -> Result<(), DebugInfoTableRemapError> {
952 match ty {
953 DebugTypeInfo::Primitive(_) | DebugTypeInfo::Unknown | DebugTypeInfo::Variadic => {},
954 DebugTypeInfo::Pointer { pointee_type_idx } => {
955 validate_type_idx(*pointee_type_idx, types)?;
956 },
957 DebugTypeInfo::Array { element_type_idx, .. } => {
958 validate_type_idx(*element_type_idx, types)?;
959 },
960 DebugTypeInfo::Struct { name_idx, fields, .. } => {
961 validate_type_string(*name_idx, strings)?;
962 for field in fields {
963 validate_type_string(field.name_idx, strings)?;
964 validate_type_idx(field.type_idx, types)?;
965 }
966 },
967 DebugTypeInfo::Function { return_type_idx, param_type_indices } => {
968 if let Some(return_type_idx) = return_type_idx {
969 validate_type_idx(*return_type_idx, types)?;
970 }
971 for ¶m_type_idx in param_type_indices {
972 validate_type_idx(param_type_idx, types)?;
973 }
974 },
975 DebugTypeInfo::Enum {
976 name_idx,
977 discriminant_type_idx,
978 variants,
979 ..
980 } => {
981 validate_type_string(*name_idx, strings)?;
982 validate_type_idx(*discriminant_type_idx, types)?;
983 for variant in variants {
984 validate_type_string(variant.name_idx, strings)?;
985 if let Some(type_idx) = variant.type_idx {
986 validate_type_idx(type_idx, types)?;
987 }
988 }
989 },
990 }
991 Ok(())
992}
993
994fn validate_type_string(
995 string_idx: DebugStringIdx,
996 strings: &IndexVec<DebugStringIdx, Arc<str>>,
997) -> Result<(), DebugInfoTableRemapError> {
998 if strings.get(string_idx).is_none() {
999 Err(DebugInfoTableRemapError::MissingTypeString { string_idx })
1000 } else {
1001 Ok(())
1002 }
1003}
1004
1005fn validate_type_idx(
1006 type_idx: DebugTypeIdx,
1007 types: &IndexVec<DebugTypeIdx, DebugTypeInfo>,
1008) -> Result<(), DebugInfoTableRemapError> {
1009 if types.get(type_idx).is_none() {
1010 Err(DebugInfoTableRemapError::MissingType { type_idx })
1011 } else {
1012 Ok(())
1013 }
1014}
1015
1016fn remap_debug_type_info(
1017 ty: &DebugTypeInfo,
1018 string_map: &IndexVec<DebugStringIdx, DebugStringIdx>,
1019 type_map: &IndexVec<DebugTypeIdx, DebugTypeIdx>,
1020) -> Result<DebugTypeInfo, DebugInfoTableRemapError> {
1021 Ok(match ty {
1022 DebugTypeInfo::Primitive(primitive) => DebugTypeInfo::Primitive(*primitive),
1023 DebugTypeInfo::Pointer { pointee_type_idx } => DebugTypeInfo::Pointer {
1024 pointee_type_idx: remap_type_idx(*pointee_type_idx, type_map)?,
1025 },
1026 DebugTypeInfo::Array { element_type_idx, count } => DebugTypeInfo::Array {
1027 element_type_idx: remap_type_idx(*element_type_idx, type_map)?,
1028 count: *count,
1029 },
1030 DebugTypeInfo::Struct { name_idx, size, fields } => DebugTypeInfo::Struct {
1031 name_idx: remap_type_string(*name_idx, string_map)?,
1032 size: *size,
1033 fields: fields
1034 .iter()
1035 .map(|field| {
1036 Ok(DebugFieldInfo {
1037 name_idx: remap_type_string(field.name_idx, string_map)?,
1038 type_idx: remap_type_idx(field.type_idx, type_map)?,
1039 offset: field.offset,
1040 })
1041 })
1042 .collect::<Result<_, DebugInfoTableRemapError>>()?,
1043 },
1044 DebugTypeInfo::Function { return_type_idx, param_type_indices } => {
1045 DebugTypeInfo::Function {
1046 return_type_idx: return_type_idx
1047 .map(|idx| remap_type_idx(idx, type_map))
1048 .transpose()?,
1049 param_type_indices: param_type_indices
1050 .iter()
1051 .map(|idx| remap_type_idx(*idx, type_map))
1052 .collect::<Result<_, _>>()?,
1053 }
1054 },
1055 DebugTypeInfo::Enum {
1056 name_idx,
1057 size,
1058 discriminant_type_idx,
1059 variants,
1060 } => DebugTypeInfo::Enum {
1061 name_idx: remap_type_string(*name_idx, string_map)?,
1062 size: *size,
1063 discriminant_type_idx: remap_type_idx(*discriminant_type_idx, type_map)?,
1064 variants: variants
1065 .iter()
1066 .map(|variant| {
1067 Ok(DebugVariantInfo {
1068 name_idx: remap_type_string(variant.name_idx, string_map)?,
1069 type_idx: variant
1070 .type_idx
1071 .map(|idx| remap_type_idx(idx, type_map))
1072 .transpose()?,
1073 payload_offset: variant.payload_offset,
1074 discriminant: variant.discriminant,
1075 })
1076 })
1077 .collect::<Result<_, DebugInfoTableRemapError>>()?,
1078 },
1079 DebugTypeInfo::Unknown => DebugTypeInfo::Unknown,
1080 DebugTypeInfo::Variadic => DebugTypeInfo::Variadic,
1081 })
1082}
1083
1084fn remap_type_string(
1085 string_idx: DebugStringIdx,
1086 string_map: &IndexVec<DebugStringIdx, DebugStringIdx>,
1087) -> Result<DebugStringIdx, DebugInfoTableRemapError> {
1088 string_map
1089 .get(string_idx)
1090 .copied()
1091 .ok_or(DebugInfoTableRemapError::MissingTypeString { string_idx })
1092}
1093
1094fn remap_type_idx(
1095 type_idx: DebugTypeIdx,
1096 type_map: &IndexVec<DebugTypeIdx, DebugTypeIdx>,
1097) -> Result<DebugTypeIdx, DebugInfoTableRemapError> {
1098 type_map
1099 .get(type_idx)
1100 .copied()
1101 .ok_or(DebugInfoTableRemapError::MissingType { type_idx })
1102}
1103
1104fn table_remap_error<Exec: Idx, Src: Idx>(
1105 forest_index: usize,
1106 error: DebugInfoTableRemapError,
1107) -> DebugInfoMergeError<Exec, Src> {
1108 match error {
1109 DebugInfoTableRemapError::InvalidOptionField { context, err } => {
1110 DebugInfoMergeError::InvalidOptionField { forest_index, context, err }
1111 },
1112 DebugInfoTableRemapError::MissingTypeString { string_idx } => {
1113 DebugInfoMergeError::MissingTypeStringMapping { forest_index, string_idx }
1114 },
1115 DebugInfoTableRemapError::MissingType { type_idx } => {
1116 DebugInfoMergeError::MissingTypeMapping { forest_index, type_idx }
1117 },
1118 DebugInfoTableRemapError::MissingSourceString { string_idx } => {
1119 DebugInfoMergeError::MissingSourceStringMapping { forest_index, string_idx }
1120 },
1121 DebugInfoTableRemapError::MissingSourceFile { file_idx } => {
1122 DebugInfoMergeError::MissingSourceFileMapping { forest_index, file_idx }
1123 },
1124 }
1125}