1use crate::ir::{IrModule, IrValue, TypeKind};
7use crate::JitResult;
8use indexmap::IndexMap;
9use std::collections::HashMap;
10
11#[derive(Debug, Clone)]
13pub struct DebugSymbolManager {
14 symbol_tables: IndexMap<String, SymbolTable>,
16
17 source_mappings: IndexMap<String, SourceMapping>,
19
20 dwarf_info: DwarfDebugInfo,
22
23 config: DebugSymbolConfig,
25
26 stats: DebugSymbolStats,
28}
29
30#[derive(Debug, Clone)]
32pub struct SymbolTable {
33 pub module_name: String,
35
36 pub functions: IndexMap<String, FunctionSymbol>,
38
39 pub variables: IndexMap<String, VariableSymbol>,
41
42 pub types: IndexMap<String, TypeSymbol>,
44
45 pub address_ranges: Vec<AddressRange>,
47
48 pub line_info: LineNumberTable,
50}
51
52#[derive(Debug, Clone)]
54pub struct FunctionSymbol {
55 pub name: String,
57
58 pub mangled_name: Option<String>,
60
61 pub start_address: u64,
63
64 pub end_address: u64,
66
67 pub size: usize,
69
70 pub return_type: TypeSymbol,
72
73 pub parameters: Vec<ParameterSymbol>,
75
76 pub locals: Vec<LocalVariableSymbol>,
78
79 pub source_location: SourceLocation,
81
82 pub inlined_functions: Vec<InlinedFunction>,
84}
85
86#[derive(Debug, Clone)]
88pub struct VariableSymbol {
89 pub name: String,
91
92 pub var_type: TypeSymbol,
94
95 pub location: VariableLocation,
97
98 pub declaration_location: SourceLocation,
100
101 pub scope: Scope,
103
104 pub live_ranges: Vec<LiveRange>,
106}
107
108#[derive(Debug, Clone)]
110pub struct TypeSymbol {
111 pub name: String,
113
114 pub kind: TypeKind,
116
117 pub size: usize,
119
120 pub alignment: usize,
122
123 pub members: Vec<TypeMember>,
125
126 pub definition_location: Option<SourceLocation>,
128}
129
130#[derive(Debug, Clone)]
132pub struct ParameterSymbol {
133 pub name: String,
135
136 pub param_type: TypeSymbol,
138
139 pub index: usize,
141
142 pub location: VariableLocation,
144}
145
146#[derive(Debug, Clone)]
148pub struct LocalVariableSymbol {
149 pub name: String,
151
152 pub var_type: TypeSymbol,
154
155 pub location: VariableLocation,
157
158 pub scope: Scope,
160
161 pub live_ranges: Vec<LiveRange>,
163}
164
165#[derive(Debug, Clone)]
167pub enum VariableLocation {
168 Register { register: RegisterId },
170
171 Stack { offset: i32 },
173
174 Memory { address: u64 },
176
177 Constant { value: ConstantValue },
179
180 Composite { locations: Vec<VariableLocation> },
182
183 Unknown,
185}
186
187#[derive(Debug, Clone, PartialEq, Eq, Hash)]
189pub enum RegisterId {
190 X64(X64Register),
192
193 Arm64(Arm64Register),
195
196 Generic(u32),
198}
199
200#[derive(Debug, Clone, PartialEq, Eq, Hash)]
202pub enum X64Register {
203 RAX,
204 RBX,
205 RCX,
206 RDX,
207 RSI,
208 RDI,
209 RBP,
210 RSP,
211 R8,
212 R9,
213 R10,
214 R11,
215 R12,
216 R13,
217 R14,
218 R15,
219 XMM0,
220 XMM1,
221 XMM2,
222 XMM3,
223 XMM4,
224 XMM5,
225 XMM6,
226 XMM7,
227 XMM8,
228 XMM9,
229 XMM10,
230 XMM11,
231 XMM12,
232 XMM13,
233 XMM14,
234 XMM15,
235}
236
237#[derive(Debug, Clone, PartialEq, Eq, Hash)]
239pub enum Arm64Register {
240 X0,
241 X1,
242 X2,
243 X3,
244 X4,
245 X5,
246 X6,
247 X7,
248 X8,
249 X9,
250 X10,
251 X11,
252 X12,
253 X13,
254 X14,
255 X15,
256 X16,
257 X17,
258 X18,
259 X19,
260 X20,
261 X21,
262 X22,
263 X23,
264 X24,
265 X25,
266 X26,
267 X27,
268 X28,
269 X29,
270 X30,
271 SP,
272 V0,
273 V1,
274 V2,
275 V3,
276 V4,
277 V5,
278 V6,
279 V7,
280 V8,
281 V9,
282 V10,
283 V11,
284 V12,
285 V13,
286 V14,
287 V15,
288 V16,
289 V17,
290 V18,
291 V19,
292 V20,
293 V21,
294 V22,
295 V23,
296 V24,
297 V25,
298 V26,
299 V27,
300 V28,
301 V29,
302 V30,
303 V31,
304}
305
306#[derive(Debug, Clone)]
308pub enum ConstantValue {
309 Int(i64),
310 UInt(u64),
311 Float(f64),
312 Bool(bool),
313 String(String),
314 Null,
315}
316
317#[derive(Debug, Clone)]
319pub struct TypeMember {
320 pub name: String,
322
323 pub member_type: TypeSymbol,
325
326 pub offset: usize,
328
329 pub size: usize,
331}
332
333#[derive(Debug, Clone)]
335pub struct Scope {
336 pub start_address: u64,
338
339 pub end_address: u64,
341
342 pub parent: Option<Box<Scope>>,
344
345 pub kind: ScopeKind,
347}
348
349#[derive(Debug, Clone)]
351pub enum ScopeKind {
352 Function,
354
355 Block,
357
358 Exception,
360
361 Loop,
363
364 Conditional,
366}
367
368#[derive(Debug, Clone)]
370pub struct LiveRange {
371 pub start: u64,
373
374 pub end: u64,
376
377 pub location: VariableLocation,
379}
380
381#[derive(Debug, Clone)]
383pub struct AddressRange {
384 pub start: u64,
386
387 pub end: u64,
389
390 pub symbol: String,
392
393 pub attributes: HashMap<String, String>,
395}
396
397#[derive(Debug, Clone)]
399pub struct LineNumberTable {
400 pub entries: Vec<LineNumberEntry>,
402
403 pub source_files: Vec<SourceFile>,
405}
406
407#[derive(Debug, Clone)]
409pub struct LineNumberEntry {
410 pub address: u64,
412
413 pub file_index: usize,
415
416 pub line: u32,
418
419 pub column: u32,
421
422 pub is_statement: bool,
424
425 pub is_basic_block: bool,
427}
428
429#[derive(Debug, Clone)]
431pub struct SourceFile {
432 pub path: String,
434
435 pub size: usize,
437
438 pub mtime: Option<u64>,
440
441 pub md5_hash: Option<[u8; 16]>,
443}
444
445#[derive(Debug, Clone)]
447pub struct SourceLocation {
448 pub file: String,
450
451 pub line: u32,
453
454 pub column: u32,
456
457 pub length: Option<u32>,
459}
460
461#[derive(Debug, Clone)]
463pub struct InlinedFunction {
464 pub original_name: String,
466
467 pub inlined_at: SourceLocation,
469
470 pub address_ranges: Vec<AddressRange>,
472
473 pub call_site: SourceLocation,
475}
476
477#[derive(Debug, Clone)]
479pub struct SourceMapping {
480 pub module_name: String,
482
483 pub address_to_source: HashMap<u64, SourceLocation>,
485
486 pub source_to_address: HashMap<(String, u32, u32), Vec<u64>>,
488
489 pub inline_stacks: HashMap<u64, Vec<InlinedFunction>>,
491}
492
493#[derive(Debug, Clone, Default)]
495pub struct DwarfDebugInfo {
496 pub compilation_units: Vec<CompilationUnit>,
498
499 pub debug_sections: HashMap<String, Vec<u8>>,
501
502 pub string_table: Vec<String>,
504
505 pub abbreviation_tables: Vec<AbbreviationTable>,
507}
508
509#[derive(Debug, Clone)]
511pub struct CompilationUnit {
512 pub offset: u64,
514
515 pub length: u64,
517
518 pub version: u16,
520
521 pub producer: String,
523
524 pub language: u32,
526
527 pub low_pc: u64,
529
530 pub high_pc: u64,
532
533 pub entries: Vec<DebugInfoEntry>,
535}
536
537#[derive(Debug, Clone)]
539pub struct DebugInfoEntry {
540 pub offset: u64,
542
543 pub tag: DwarfTag,
545
546 pub attributes: HashMap<DwarfAttribute, DwarfValue>,
548
549 pub children: Vec<DebugInfoEntry>,
551}
552
553#[derive(Debug, Clone, PartialEq, Eq, Hash)]
555pub enum DwarfTag {
556 CompileUnit,
557 Subprogram,
558 Variable,
559 Parameter,
560 BaseType,
561 PointerType,
562 ArrayType,
563 StructureType,
564 UnionType,
565 EnumerationType,
566 LexicalBlock,
567 InlinedSubroutine,
568}
569
570#[derive(Debug, Clone, PartialEq, Eq, Hash)]
572pub enum DwarfAttribute {
573 Name,
574 Type,
575 Location,
576 LowPc,
577 HighPc,
578 FrameBase,
579 ByteSize,
580 Encoding,
581 DeclarationFile,
582 DeclarationLine,
583 CallFile,
584 CallLine,
585 InlineStatus,
586}
587
588#[derive(Debug, Clone)]
590pub enum DwarfValue {
591 String(String),
592 Address(u64),
593 Constant(u64),
594 Block(Vec<u8>),
595 Reference(u64),
596 Flag(bool),
597}
598
599#[derive(Debug, Clone)]
601pub struct AbbreviationTable {
602 pub offset: u64,
604
605 pub entries: HashMap<u64, AbbreviationEntry>,
607}
608
609#[derive(Debug, Clone)]
611pub struct AbbreviationEntry {
612 pub code: u64,
614
615 pub tag: DwarfTag,
617
618 pub has_children: bool,
620
621 pub attributes: Vec<(DwarfAttribute, DwarfForm)>,
623}
624
625#[derive(Debug, Clone)]
627pub enum DwarfForm {
628 Addr,
629 Block1,
630 Block2,
631 Block4,
632 Data1,
633 Data2,
634 Data4,
635 Data8,
636 String,
637 Strp,
638 Ref1,
639 Ref2,
640 Ref4,
641 Ref8,
642 RefAddr,
643 Flag,
644 FlagPresent,
645}
646
647#[derive(Debug, Clone)]
649pub struct DebugSymbolConfig {
650 pub enable_dwarf: bool,
652
653 pub enable_source_mapping: bool,
655
656 pub include_variable_locations: bool,
658
659 pub include_inline_info: bool,
661
662 pub debug_level: u8,
664
665 pub compress_debug_sections: bool,
667
668 pub include_optimization_remarks: bool,
670}
671
672#[derive(Debug, Clone, Default)]
674pub struct DebugSymbolStats {
675 pub total_symbols: usize,
677
678 pub debug_info_size: usize,
680
681 pub source_mappings: usize,
683
684 pub generation_time_ns: u64,
686
687 pub compression_ratio: f32,
689}
690
691impl Default for DebugSymbolConfig {
692 fn default() -> Self {
693 Self {
694 enable_dwarf: true,
695 enable_source_mapping: true,
696 include_variable_locations: true,
697 include_inline_info: true,
698 debug_level: 2,
699 compress_debug_sections: false,
700 include_optimization_remarks: false,
701 }
702 }
703}
704
705impl DebugSymbolManager {
706 pub fn new(config: DebugSymbolConfig) -> Self {
708 Self {
709 symbol_tables: IndexMap::new(),
710 source_mappings: IndexMap::new(),
711 dwarf_info: DwarfDebugInfo::default(),
712 config,
713 stats: DebugSymbolStats::default(),
714 }
715 }
716
717 pub fn with_defaults() -> Self {
719 Self::new(DebugSymbolConfig::default())
720 }
721
722 pub fn generate_symbols(
724 &mut self,
725 module: &IrModule,
726 code_address: u64,
727 code_size: usize,
728 ) -> JitResult<()> {
729 let start_time = std::time::Instant::now();
730
731 let symbol_table = self.create_symbol_table(module, code_address, code_size)?;
733
734 let source_mapping = self.create_source_mapping(module, code_address)?;
736
737 if self.config.enable_dwarf {
739 self.generate_dwarf_info(module, &symbol_table)?;
740 }
741
742 self.symbol_tables.insert(module.name.clone(), symbol_table);
744 self.source_mappings
745 .insert(module.name.clone(), source_mapping);
746
747 let generation_time = start_time.elapsed().as_nanos() as u64;
749 self.stats.generation_time_ns += generation_time;
750 self.stats.total_symbols += 1;
751
752 Ok(())
753 }
754
755 fn create_symbol_table(
757 &self,
758 module: &IrModule,
759 code_address: u64,
760 code_size: usize,
761 ) -> JitResult<SymbolTable> {
762 let mut symbol_table = SymbolTable {
763 module_name: module.name.clone(),
764 functions: IndexMap::new(),
765 variables: IndexMap::new(),
766 types: IndexMap::new(),
767 address_ranges: Vec::new(),
768 line_info: LineNumberTable {
769 entries: Vec::new(),
770 source_files: Vec::new(),
771 },
772 };
773
774 let function_symbol = FunctionSymbol {
776 name: module.name.clone(),
777 mangled_name: None,
778 start_address: code_address,
779 end_address: code_address + code_size as u64,
780 size: code_size,
781 return_type: TypeSymbol::void_type(),
782 parameters: Vec::new(),
783 locals: Vec::new(),
784 source_location: SourceLocation {
785 file: "<generated>".to_string(),
786 line: 1,
787 column: 1,
788 length: None,
789 },
790 inlined_functions: Vec::new(),
791 };
792
793 symbol_table
794 .functions
795 .insert(module.name.clone(), function_symbol);
796
797 symbol_table.address_ranges.push(AddressRange {
799 start: code_address,
800 end: code_address + code_size as u64,
801 symbol: module.name.clone(),
802 attributes: HashMap::new(),
803 });
804
805 for (type_id, type_def) in &module.types {
807 let type_symbol = self.create_type_symbol(type_id, type_def)?;
808 symbol_table
809 .types
810 .insert(format!("type_{}", type_id.0), type_symbol);
811 }
812
813 for (value_id, value_def) in &module.values {
815 if let Some(variable_symbol) = self.create_variable_symbol(value_id, value_def)? {
816 symbol_table
817 .variables
818 .insert(format!("var_{}", value_id.0), variable_symbol);
819 }
820 }
821
822 Ok(symbol_table)
823 }
824
825 fn create_source_mapping(
827 &self,
828 module: &IrModule,
829 code_address: u64,
830 ) -> JitResult<SourceMapping> {
831 let mut source_mapping = SourceMapping {
832 module_name: module.name.clone(),
833 address_to_source: HashMap::new(),
834 source_to_address: HashMap::new(),
835 inline_stacks: HashMap::new(),
836 };
837
838 let entry_location = SourceLocation {
840 file: "<generated>".to_string(),
841 line: 1,
842 column: 1,
843 length: None,
844 };
845
846 source_mapping
847 .address_to_source
848 .insert(code_address, entry_location.clone());
849
850 let key = (
851 entry_location.file.clone(),
852 entry_location.line,
853 entry_location.column,
854 );
855 source_mapping
856 .source_to_address
857 .entry(key)
858 .or_default()
859 .push(code_address);
860
861 Ok(source_mapping)
862 }
863
864 fn generate_dwarf_info(
866 &mut self,
867 _module: &IrModule,
868 symbol_table: &SymbolTable,
869 ) -> JitResult<()> {
870 let compilation_unit = CompilationUnit {
872 offset: 0,
873 length: 0, version: 4,
875 producer: "ToRSh JIT Compiler".to_string(),
876 language: 0x8001, low_pc: symbol_table
878 .address_ranges
879 .first()
880 .map(|r| r.start)
881 .unwrap_or(0),
882 high_pc: symbol_table
883 .address_ranges
884 .last()
885 .map(|r| r.end)
886 .unwrap_or(0),
887 entries: self.create_debug_entries(symbol_table)?,
888 };
889
890 self.dwarf_info.compilation_units.push(compilation_unit);
891
892 Ok(())
893 }
894
895 fn create_debug_entries(&self, symbol_table: &SymbolTable) -> JitResult<Vec<DebugInfoEntry>> {
897 let mut entries = Vec::new();
898
899 for (name, function) in &symbol_table.functions {
901 let mut attributes = HashMap::new();
902 attributes.insert(DwarfAttribute::Name, DwarfValue::String(name.clone()));
903 attributes.insert(
904 DwarfAttribute::LowPc,
905 DwarfValue::Address(function.start_address),
906 );
907 attributes.insert(
908 DwarfAttribute::HighPc,
909 DwarfValue::Address(function.end_address),
910 );
911
912 let entry = DebugInfoEntry {
913 offset: 0,
914 tag: DwarfTag::Subprogram,
915 attributes,
916 children: Vec::new(),
917 };
918
919 entries.push(entry);
920 }
921
922 for (name, type_symbol) in &symbol_table.types {
924 let mut attributes = HashMap::new();
925 attributes.insert(DwarfAttribute::Name, DwarfValue::String(name.clone()));
926 attributes.insert(
927 DwarfAttribute::ByteSize,
928 DwarfValue::Constant(type_symbol.size as u64),
929 );
930
931 let entry = DebugInfoEntry {
932 offset: 0,
933 tag: DwarfTag::BaseType,
934 attributes,
935 children: Vec::new(),
936 };
937
938 entries.push(entry);
939 }
940
941 Ok(entries)
942 }
943
944 fn create_type_symbol(
946 &self,
947 _type_id: &crate::ir::IrType,
948 type_def: &crate::ir::TypeDef,
949 ) -> JitResult<TypeSymbol> {
950 Ok(TypeSymbol {
951 name: format!("{:?}", type_def.kind),
952 kind: type_def.kind.clone(),
953 size: type_def.size.unwrap_or(0),
954 alignment: type_def.align.unwrap_or(1),
955 members: Vec::new(),
956 definition_location: None,
957 })
958 }
959
960 fn create_variable_symbol(
962 &self,
963 value_id: &IrValue,
964 value_def: &crate::ir::ValueDef,
965 ) -> JitResult<Option<VariableSymbol>> {
966 use crate::ir::ValueKind;
967
968 match &value_def.kind {
970 ValueKind::Parameter { index } => {
971 let var_type = self.ir_type_to_type_symbol(&value_def.ty);
973 let name = format!("param_{}", index);
974
975 Ok(Some(VariableSymbol {
976 name,
977 var_type,
978 location: VariableLocation::Unknown,
979 declaration_location: SourceLocation {
980 file: "".to_string(),
981 line: 0,
982 column: 0,
983 length: None,
984 },
985 scope: Scope {
986 start_address: 0,
987 end_address: u64::MAX,
988 parent: None,
989 kind: ScopeKind::Function,
990 },
991 live_ranges: Vec::new(),
992 }))
993 }
994 ValueKind::Instruction { block, index } => {
995 let var_type = self.ir_type_to_type_symbol(&value_def.ty);
997 let name = format!("tmp_{}_{}", block, index);
998
999 Ok(Some(VariableSymbol {
1000 name,
1001 var_type,
1002 location: VariableLocation::Unknown,
1003 declaration_location: SourceLocation {
1004 file: "".to_string(),
1005 line: 0,
1006 column: 0,
1007 length: None,
1008 },
1009 scope: Scope {
1010 start_address: 0,
1011 end_address: u64::MAX,
1012 parent: None,
1013 kind: ScopeKind::Block,
1014 },
1015 live_ranges: Vec::new(),
1016 }))
1017 }
1018 ValueKind::Constant { data } => {
1019 let var_type = self.ir_type_to_type_symbol(&value_def.ty);
1021 let name = format!("const_{:?}", value_id);
1022 let const_location = match data {
1023 crate::ir::ConstantData::Int(v) => VariableLocation::Constant {
1024 value: ConstantValue::Int(*v),
1025 },
1026 crate::ir::ConstantData::Float(v) => VariableLocation::Constant {
1027 value: ConstantValue::Float(*v),
1028 },
1029 crate::ir::ConstantData::Bool(v) => VariableLocation::Constant {
1030 value: ConstantValue::Bool(*v),
1031 },
1032 _ => VariableLocation::Unknown,
1033 };
1034
1035 Ok(Some(VariableSymbol {
1036 name,
1037 var_type,
1038 location: const_location,
1039 declaration_location: SourceLocation {
1040 file: "".to_string(),
1041 line: 0,
1042 column: 0,
1043 length: None,
1044 },
1045 scope: Scope {
1046 start_address: 0,
1047 end_address: u64::MAX,
1048 parent: None,
1049 kind: ScopeKind::Block, },
1051 live_ranges: Vec::new(),
1052 }))
1053 }
1054 ValueKind::Undef => {
1055 Ok(None)
1057 }
1058 }
1059 }
1060
1061 fn ir_type_to_type_symbol(&self, ir_type: &crate::ir::IrType) -> TypeSymbol {
1063 use crate::ir::TypeKind;
1064
1065 let type_id = ir_type.0;
1068
1069 TypeSymbol {
1070 name: format!("type_{}", type_id),
1071 kind: TypeKind::I32, size: 4, alignment: 4, members: Vec::new(),
1075 definition_location: None,
1076 }
1077 }
1078
1079 pub fn get_symbol_table(&self, module_name: &str) -> Option<&SymbolTable> {
1081 self.symbol_tables.get(module_name)
1082 }
1083
1084 pub fn get_source_mapping(&self, module_name: &str) -> Option<&SourceMapping> {
1086 self.source_mappings.get(module_name)
1087 }
1088
1089 pub fn lookup_source_location(&self, address: u64) -> Option<SourceLocation> {
1091 for source_mapping in self.source_mappings.values() {
1092 if let Some(location) = source_mapping.address_to_source.get(&address) {
1093 return Some(location.clone());
1094 }
1095 }
1096 None
1097 }
1098
1099 pub fn lookup_addresses(&self, file: &str, line: u32, column: u32) -> Vec<u64> {
1101 let mut addresses = Vec::new();
1102 let key = (file.to_string(), line, column);
1103
1104 for source_mapping in self.source_mappings.values() {
1105 if let Some(addrs) = source_mapping.source_to_address.get(&key) {
1106 addresses.extend(addrs);
1107 }
1108 }
1109
1110 addresses
1111 }
1112
1113 pub fn get_dwarf_info(&self) -> &DwarfDebugInfo {
1115 &self.dwarf_info
1116 }
1117
1118 pub fn stats(&self) -> &DebugSymbolStats {
1120 &self.stats
1121 }
1122
1123 pub fn clear(&mut self) {
1125 self.symbol_tables.clear();
1126 self.source_mappings.clear();
1127 self.dwarf_info = DwarfDebugInfo::default();
1128 self.stats = DebugSymbolStats::default();
1129 }
1130}
1131
1132impl TypeSymbol {
1133 pub fn void_type() -> Self {
1135 Self {
1136 name: "void".to_string(),
1137 kind: TypeKind::Void,
1138 size: 0,
1139 alignment: 1,
1140 members: Vec::new(),
1141 definition_location: None,
1142 }
1143 }
1144
1145 pub fn basic_type(kind: TypeKind) -> Self {
1147 let (name, size, alignment) = match kind {
1148 TypeKind::Bool => ("bool".to_string(), 1, 1),
1149 TypeKind::I8 => ("i8".to_string(), 1, 1),
1150 TypeKind::I16 => ("i16".to_string(), 2, 2),
1151 TypeKind::I32 => ("i32".to_string(), 4, 4),
1152 TypeKind::I64 => ("i64".to_string(), 8, 8),
1153 TypeKind::U8 => ("u8".to_string(), 1, 1),
1154 TypeKind::U16 => ("u16".to_string(), 2, 2),
1155 TypeKind::U32 => ("u32".to_string(), 4, 4),
1156 TypeKind::U64 => ("u64".to_string(), 8, 8),
1157 TypeKind::F16 => ("f16".to_string(), 2, 2),
1158 TypeKind::F32 => ("f32".to_string(), 4, 4),
1159 TypeKind::F64 => ("f64".to_string(), 8, 8),
1160 TypeKind::C64 => ("c64".to_string(), 8, 4),
1161 TypeKind::C128 => ("c128".to_string(), 16, 8),
1162 TypeKind::Void => ("void".to_string(), 0, 1),
1163 _ => ("unknown".to_string(), 0, 1),
1164 };
1165
1166 Self {
1167 name,
1168 kind,
1169 size,
1170 alignment,
1171 members: Vec::new(),
1172 definition_location: None,
1173 }
1174 }
1175}
1176
1177#[cfg(test)]
1178mod tests {
1179 use super::*;
1180
1181 #[test]
1182 fn test_debug_symbol_manager_creation() {
1183 let manager = DebugSymbolManager::with_defaults();
1184 assert_eq!(manager.symbol_tables.len(), 0);
1185 assert_eq!(manager.source_mappings.len(), 0);
1186 }
1187
1188 #[test]
1189 fn test_type_symbol_creation() {
1190 let void_type = TypeSymbol::void_type();
1191 assert_eq!(void_type.name, "void");
1192 assert_eq!(void_type.size, 0);
1193
1194 let i32_type = TypeSymbol::basic_type(TypeKind::I32);
1195 assert_eq!(i32_type.name, "i32");
1196 assert_eq!(i32_type.size, 4);
1197 assert_eq!(i32_type.alignment, 4);
1198 }
1199
1200 #[test]
1201 fn test_register_identification() {
1202 let x64_reg = RegisterId::X64(X64Register::RAX);
1203 let arm64_reg = RegisterId::Arm64(Arm64Register::X0);
1204 let generic_reg = RegisterId::Generic(0);
1205
1206 assert_ne!(x64_reg, arm64_reg);
1207 assert_ne!(arm64_reg, generic_reg);
1208 }
1209
1210 #[test]
1211 fn test_source_location() {
1212 let location = SourceLocation {
1213 file: "test.rs".to_string(),
1214 line: 42,
1215 column: 10,
1216 length: Some(15),
1217 };
1218
1219 assert_eq!(location.file, "test.rs");
1220 assert_eq!(location.line, 42);
1221 assert_eq!(location.column, 10);
1222 }
1223}