Skip to main content

torsh_jit/
debug_symbols.rs

1//! Debug symbol generation for JIT compilation
2//!
3//! This module provides comprehensive debugging symbol infrastructure for JIT-compiled
4//! code, including DWARF debug information, symbol tables, and source mapping.
5
6use crate::ir::{IrModule, IrValue, TypeKind};
7use crate::JitResult;
8use indexmap::IndexMap;
9use std::collections::HashMap;
10
11/// Debug symbol manager for JIT compilation
12#[derive(Debug, Clone)]
13pub struct DebugSymbolManager {
14    /// Symbol tables indexed by module name
15    symbol_tables: IndexMap<String, SymbolTable>,
16
17    /// Source mappings for compiled modules
18    source_mappings: IndexMap<String, SourceMapping>,
19
20    /// DWARF debug information
21    dwarf_info: DwarfDebugInfo,
22
23    /// Configuration for debug symbol generation
24    config: DebugSymbolConfig,
25
26    /// Statistics about debug symbols
27    stats: DebugSymbolStats,
28}
29
30/// Symbol table containing debug symbols for a module
31#[derive(Debug, Clone)]
32pub struct SymbolTable {
33    /// Module name
34    pub module_name: String,
35
36    /// Function symbols
37    pub functions: IndexMap<String, FunctionSymbol>,
38
39    /// Variable symbols
40    pub variables: IndexMap<String, VariableSymbol>,
41
42    /// Type symbols
43    pub types: IndexMap<String, TypeSymbol>,
44
45    /// Address ranges for symbols
46    pub address_ranges: Vec<AddressRange>,
47
48    /// Line number information
49    pub line_info: LineNumberTable,
50}
51
52/// Function symbol information
53#[derive(Debug, Clone)]
54pub struct FunctionSymbol {
55    /// Function name
56    pub name: String,
57
58    /// Mangled name (if different from name)
59    pub mangled_name: Option<String>,
60
61    /// Function start address
62    pub start_address: u64,
63
64    /// Function end address
65    pub end_address: u64,
66
67    /// Function size in bytes
68    pub size: usize,
69
70    /// Return type
71    pub return_type: TypeSymbol,
72
73    /// Parameter symbols
74    pub parameters: Vec<ParameterSymbol>,
75
76    /// Local variable symbols
77    pub locals: Vec<LocalVariableSymbol>,
78
79    /// Source location
80    pub source_location: SourceLocation,
81
82    /// Inlined functions (for optimization tracking)
83    pub inlined_functions: Vec<InlinedFunction>,
84}
85
86/// Variable symbol information
87#[derive(Debug, Clone)]
88pub struct VariableSymbol {
89    /// Variable name
90    pub name: String,
91
92    /// Variable type
93    pub var_type: TypeSymbol,
94
95    /// Storage location
96    pub location: VariableLocation,
97
98    /// Source location where declared
99    pub declaration_location: SourceLocation,
100
101    /// Scope information
102    pub scope: Scope,
103
104    /// Live ranges (where variable is valid)
105    pub live_ranges: Vec<LiveRange>,
106}
107
108/// Type symbol information
109#[derive(Debug, Clone)]
110pub struct TypeSymbol {
111    /// Type name
112    pub name: String,
113
114    /// Type kind
115    pub kind: TypeKind,
116
117    /// Size in bytes
118    pub size: usize,
119
120    /// Alignment requirements
121    pub alignment: usize,
122
123    /// For composite types, member information
124    pub members: Vec<TypeMember>,
125
126    /// Source location where type is defined
127    pub definition_location: Option<SourceLocation>,
128}
129
130/// Parameter symbol information
131#[derive(Debug, Clone)]
132pub struct ParameterSymbol {
133    /// Parameter name
134    pub name: String,
135
136    /// Parameter type
137    pub param_type: TypeSymbol,
138
139    /// Parameter index
140    pub index: usize,
141
142    /// Storage location
143    pub location: VariableLocation,
144}
145
146/// Local variable symbol information
147#[derive(Debug, Clone)]
148pub struct LocalVariableSymbol {
149    /// Variable name
150    pub name: String,
151
152    /// Variable type
153    pub var_type: TypeSymbol,
154
155    /// Storage location
156    pub location: VariableLocation,
157
158    /// Scope information
159    pub scope: Scope,
160
161    /// Live ranges
162    pub live_ranges: Vec<LiveRange>,
163}
164
165/// Variable storage location
166#[derive(Debug, Clone)]
167pub enum VariableLocation {
168    /// Stored in a register
169    Register { register: RegisterId },
170
171    /// Stored on the stack
172    Stack { offset: i32 },
173
174    /// Stored in memory
175    Memory { address: u64 },
176
177    /// Constant value
178    Constant { value: ConstantValue },
179
180    /// Composite location (e.g., split across registers)
181    Composite { locations: Vec<VariableLocation> },
182
183    /// Location unknown or optimized away
184    Unknown,
185}
186
187/// Register identifier
188#[derive(Debug, Clone, PartialEq, Eq, Hash)]
189pub enum RegisterId {
190    /// X86-64 registers
191    X64(X64Register),
192
193    /// ARM64 registers
194    Arm64(Arm64Register),
195
196    /// Generic register by number
197    Generic(u32),
198}
199
200/// X86-64 register names
201#[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/// ARM64 register names
238#[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/// Constant value for debugging
307#[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/// Type member information
318#[derive(Debug, Clone)]
319pub struct TypeMember {
320    /// Member name
321    pub name: String,
322
323    /// Member type
324    pub member_type: TypeSymbol,
325
326    /// Offset within the type
327    pub offset: usize,
328
329    /// Size of the member
330    pub size: usize,
331}
332
333/// Variable scope information
334#[derive(Debug, Clone)]
335pub struct Scope {
336    /// Scope start address
337    pub start_address: u64,
338
339    /// Scope end address
340    pub end_address: u64,
341
342    /// Parent scope (if any)
343    pub parent: Option<Box<Scope>>,
344
345    /// Scope kind
346    pub kind: ScopeKind,
347}
348
349/// Kind of scope
350#[derive(Debug, Clone)]
351pub enum ScopeKind {
352    /// Function scope
353    Function,
354
355    /// Block scope
356    Block,
357
358    /// Try/catch scope
359    Exception,
360
361    /// Loop scope
362    Loop,
363
364    /// Conditional scope
365    Conditional,
366}
367
368/// Variable live range
369#[derive(Debug, Clone)]
370pub struct LiveRange {
371    /// Start address
372    pub start: u64,
373
374    /// End address
375    pub end: u64,
376
377    /// Location during this range
378    pub location: VariableLocation,
379}
380
381/// Address range with metadata
382#[derive(Debug, Clone)]
383pub struct AddressRange {
384    /// Start address
385    pub start: u64,
386
387    /// End address
388    pub end: u64,
389
390    /// Symbol associated with this range
391    pub symbol: String,
392
393    /// Additional attributes
394    pub attributes: HashMap<String, String>,
395}
396
397/// Line number table for source mapping
398#[derive(Debug, Clone)]
399pub struct LineNumberTable {
400    /// Entries mapping addresses to line numbers
401    pub entries: Vec<LineNumberEntry>,
402
403    /// Source file information
404    pub source_files: Vec<SourceFile>,
405}
406
407/// Line number table entry
408#[derive(Debug, Clone)]
409pub struct LineNumberEntry {
410    /// Address
411    pub address: u64,
412
413    /// Source file index
414    pub file_index: usize,
415
416    /// Line number
417    pub line: u32,
418
419    /// Column number
420    pub column: u32,
421
422    /// Whether this is a statement boundary
423    pub is_statement: bool,
424
425    /// Whether this is a basic block boundary
426    pub is_basic_block: bool,
427}
428
429/// Source file information
430#[derive(Debug, Clone)]
431pub struct SourceFile {
432    /// File path
433    pub path: String,
434
435    /// File size
436    pub size: usize,
437
438    /// Modification time
439    pub mtime: Option<u64>,
440
441    /// MD5 hash of file contents
442    pub md5_hash: Option<[u8; 16]>,
443}
444
445/// Source location information
446#[derive(Debug, Clone)]
447pub struct SourceLocation {
448    /// File path
449    pub file: String,
450
451    /// Line number (1-based)
452    pub line: u32,
453
454    /// Column number (1-based)
455    pub column: u32,
456
457    /// Length of the construct
458    pub length: Option<u32>,
459}
460
461/// Inlined function information
462#[derive(Debug, Clone)]
463pub struct InlinedFunction {
464    /// Original function name
465    pub original_name: String,
466
467    /// Inlined at location
468    pub inlined_at: SourceLocation,
469
470    /// Address ranges where inlined
471    pub address_ranges: Vec<AddressRange>,
472
473    /// Call site information
474    pub call_site: SourceLocation,
475}
476
477/// Source mapping for a compiled module
478#[derive(Debug, Clone)]
479pub struct SourceMapping {
480    /// Module name
481    pub module_name: String,
482
483    /// Address to source location mapping
484    pub address_to_source: HashMap<u64, SourceLocation>,
485
486    /// Source location to address mapping
487    pub source_to_address: HashMap<(String, u32, u32), Vec<u64>>,
488
489    /// Inline stack information
490    pub inline_stacks: HashMap<u64, Vec<InlinedFunction>>,
491}
492
493/// DWARF debug information
494#[derive(Debug, Clone, Default)]
495pub struct DwarfDebugInfo {
496    /// Compilation units
497    pub compilation_units: Vec<CompilationUnit>,
498
499    /// Debug sections
500    pub debug_sections: HashMap<String, Vec<u8>>,
501
502    /// String table
503    pub string_table: Vec<String>,
504
505    /// Abbreviation tables
506    pub abbreviation_tables: Vec<AbbreviationTable>,
507}
508
509/// DWARF compilation unit
510#[derive(Debug, Clone)]
511pub struct CompilationUnit {
512    /// Unit offset
513    pub offset: u64,
514
515    /// Unit length
516    pub length: u64,
517
518    /// DWARF version
519    pub version: u16,
520
521    /// Producer (compiler) information
522    pub producer: String,
523
524    /// Language code
525    pub language: u32,
526
527    /// Low PC (start address)
528    pub low_pc: u64,
529
530    /// High PC (end address)
531    pub high_pc: u64,
532
533    /// Debug information entries
534    pub entries: Vec<DebugInfoEntry>,
535}
536
537/// DWARF debug information entry
538#[derive(Debug, Clone)]
539pub struct DebugInfoEntry {
540    /// Entry offset
541    pub offset: u64,
542
543    /// Tag (what kind of entity this describes)
544    pub tag: DwarfTag,
545
546    /// Attributes
547    pub attributes: HashMap<DwarfAttribute, DwarfValue>,
548
549    /// Child entries
550    pub children: Vec<DebugInfoEntry>,
551}
552
553/// DWARF tags
554#[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/// DWARF attributes
571#[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/// DWARF attribute values
589#[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/// DWARF abbreviation table
600#[derive(Debug, Clone)]
601pub struct AbbreviationTable {
602    /// Table offset
603    pub offset: u64,
604
605    /// Abbreviation entries
606    pub entries: HashMap<u64, AbbreviationEntry>,
607}
608
609/// DWARF abbreviation entry
610#[derive(Debug, Clone)]
611pub struct AbbreviationEntry {
612    /// Abbreviation code
613    pub code: u64,
614
615    /// Tag
616    pub tag: DwarfTag,
617
618    /// Whether entry has children
619    pub has_children: bool,
620
621    /// Attribute specifications
622    pub attributes: Vec<(DwarfAttribute, DwarfForm)>,
623}
624
625/// DWARF attribute forms
626#[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/// Configuration for debug symbol generation
648#[derive(Debug, Clone)]
649pub struct DebugSymbolConfig {
650    /// Enable DWARF debug information generation
651    pub enable_dwarf: bool,
652
653    /// Enable source mapping
654    pub enable_source_mapping: bool,
655
656    /// Include variable location information
657    pub include_variable_locations: bool,
658
659    /// Include inlined function information
660    pub include_inline_info: bool,
661
662    /// Debug information level (0-3)
663    pub debug_level: u8,
664
665    /// Compress debug sections
666    pub compress_debug_sections: bool,
667
668    /// Include optimization remarks
669    pub include_optimization_remarks: bool,
670}
671
672/// Statistics about debug symbol generation
673#[derive(Debug, Clone, Default)]
674pub struct DebugSymbolStats {
675    /// Total number of symbols generated
676    pub total_symbols: usize,
677
678    /// Total debug information size in bytes
679    pub debug_info_size: usize,
680
681    /// Number of source mappings
682    pub source_mappings: usize,
683
684    /// Generation time in nanoseconds
685    pub generation_time_ns: u64,
686
687    /// Compression ratio (if enabled)
688    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    /// Create a new debug symbol manager
707    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    /// Create a new debug symbol manager with default configuration
718    pub fn with_defaults() -> Self {
719        Self::new(DebugSymbolConfig::default())
720    }
721
722    /// Generate debug symbols for a compiled module
723    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        // Create symbol table for the module
732        let symbol_table = self.create_symbol_table(module, code_address, code_size)?;
733
734        // Generate source mapping
735        let source_mapping = self.create_source_mapping(module, code_address)?;
736
737        // Generate DWARF debug information
738        if self.config.enable_dwarf {
739            self.generate_dwarf_info(module, &symbol_table)?;
740        }
741
742        // Store the generated information
743        self.symbol_tables.insert(module.name.clone(), symbol_table);
744        self.source_mappings
745            .insert(module.name.clone(), source_mapping);
746
747        // Update statistics
748        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    /// Create symbol table for a module
756    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        // Add module-level function symbol
775        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        // Add address range for the entire module
798        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        // Extract type information from IR
806        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        // Extract variable information from IR values
814        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    /// Create source mapping for a module
826    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        // Map the entry point
839        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    /// Generate DWARF debug information
865    fn generate_dwarf_info(
866        &mut self,
867        _module: &IrModule,
868        symbol_table: &SymbolTable,
869    ) -> JitResult<()> {
870        // Create compilation unit
871        let compilation_unit = CompilationUnit {
872            offset: 0,
873            length: 0, // Will be filled in later
874            version: 4,
875            producer: "ToRSh JIT Compiler".to_string(),
876            language: 0x8001, // DW_LANG_Rust (unofficial)
877            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    /// Create debug information entries from symbol table
896    fn create_debug_entries(&self, symbol_table: &SymbolTable) -> JitResult<Vec<DebugInfoEntry>> {
897        let mut entries = Vec::new();
898
899        // Create entries for functions
900        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        // Create entries for types
923        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    /// Create type symbol from IR type definition
945    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    /// Create variable symbol from IR value definition
961    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        // Create symbol based on value kind
969        match &value_def.kind {
970            ValueKind::Parameter { index } => {
971                // Create symbol for function parameter
972                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                // Create symbol for instruction result (temporary variable)
996                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                // Create symbol for constant value
1020                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, // Use Block instead of Global
1050                    },
1051                    live_ranges: Vec::new(),
1052                }))
1053            }
1054            ValueKind::Undef => {
1055                // Don't create symbols for undefined values
1056                Ok(None)
1057            }
1058        }
1059    }
1060
1061    /// Helper to convert IrType to TypeSymbol
1062    fn ir_type_to_type_symbol(&self, ir_type: &crate::ir::IrType) -> TypeSymbol {
1063        use crate::ir::TypeKind;
1064
1065        // IrType is just a u32 ID. Without access to the IrModule's type registry,
1066        // we create a generic type symbol with the ID
1067        let type_id = ir_type.0;
1068
1069        TypeSymbol {
1070            name: format!("type_{}", type_id),
1071            kind: TypeKind::I32, // Default kind
1072            size: 4,             // Default size
1073            alignment: 4,        // Default alignment
1074            members: Vec::new(),
1075            definition_location: None,
1076        }
1077    }
1078
1079    /// Get symbol table for a module
1080    pub fn get_symbol_table(&self, module_name: &str) -> Option<&SymbolTable> {
1081        self.symbol_tables.get(module_name)
1082    }
1083
1084    /// Get source mapping for a module
1085    pub fn get_source_mapping(&self, module_name: &str) -> Option<&SourceMapping> {
1086        self.source_mappings.get(module_name)
1087    }
1088
1089    /// Lookup source location for an address
1090    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    /// Lookup addresses for a source location
1100    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    /// Get DWARF debug information
1114    pub fn get_dwarf_info(&self) -> &DwarfDebugInfo {
1115        &self.dwarf_info
1116    }
1117
1118    /// Get debug symbol statistics
1119    pub fn stats(&self) -> &DebugSymbolStats {
1120        &self.stats
1121    }
1122
1123    /// Clear all debug symbols (for memory management)
1124    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    /// Create a void type symbol
1134    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    /// Create a basic type symbol
1146    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}