use super::CodeGen;
use crate::ast::Span;
use std::fmt::Write as _;
impl CodeGen {
pub(super) fn dbg_enabled(&self) -> bool {
self.dbg_source.is_some()
}
fn dbg_alloc_id(&mut self) -> usize {
self.dbg_md_counter += 1;
self.dbg_md_counter
}
pub(super) fn dbg_init_program(&mut self) {
if !self.dbg_enabled() || self.dbg_cu_id.is_some() {
return;
}
let source = self.dbg_source.as_ref().expect("dbg_enabled checked");
let abs = std::fs::canonicalize(source).unwrap_or_else(|_| source.clone());
let (filename, directory) = match (abs.file_name(), abs.parent()) {
(Some(name), Some(dir)) => (
name.to_string_lossy().into_owned(),
dir.to_string_lossy().into_owned(),
),
_ => (abs.to_string_lossy().into_owned(), String::new()),
};
let file_id = self.dbg_alloc_id();
let cu_id = self.dbg_alloc_id();
let sub_ty_id = self.dbg_alloc_id();
let dwarf_ver_id = self.dbg_alloc_id();
let debug_info_ver_id = self.dbg_alloc_id();
let _ = writeln!(
&mut self.dbg_metadata,
"!{} = !DIFile(filename: \"{}\", directory: \"{}\")",
file_id,
escape_md(&filename),
escape_md(&directory),
);
let _ = writeln!(
&mut self.dbg_metadata,
"!{} = distinct !DICompileUnit(language: DW_LANG_C, file: !{}, \
producer: \"seqc\", isOptimized: false, runtimeVersion: 0, \
emissionKind: FullDebug)",
cu_id, file_id,
);
let _ = writeln!(
&mut self.dbg_metadata,
"!{} = !DISubroutineType(types: !{{null}})",
sub_ty_id,
);
self.dbg_file_id = Some(file_id);
self.dbg_cu_id = Some(cu_id);
self.dbg_subroutine_type_id = Some(sub_ty_id);
self.dbg_module_flag_ids = Some((dwarf_ver_id, debug_info_ver_id));
}
pub(super) fn dbg_open_subprogram(&mut self, name: &str, line: usize) -> String {
if !self.dbg_enabled() {
return String::new();
}
self.dbg_init_program();
let (Some(file_id), Some(cu_id), Some(sub_ty_id)) = (
self.dbg_file_id,
self.dbg_cu_id,
self.dbg_subroutine_type_id,
) else {
return String::new();
};
let sp_id = self.dbg_alloc_id();
let dwarf_line = line.saturating_add(1);
let _ = writeln!(
&mut self.dbg_metadata,
"!{} = distinct !DISubprogram(name: \"{}\", scope: !{}, \
file: !{}, line: {}, type: !{}, scopeLine: {}, \
spFlags: DISPFlagDefinition, unit: !{})",
sp_id,
escape_md(name),
file_id,
file_id,
dwarf_line,
sub_ty_id,
dwarf_line,
cu_id,
);
self.current_dbg_subprogram_id = Some(sp_id);
format!(" !dbg !{}", sp_id)
}
pub(super) fn dbg_close_subprogram(&mut self) {
self.current_dbg_subprogram_id = None;
}
pub(super) fn dbg_call_suffix(&mut self, span: Option<&Span>) -> String {
if !self.dbg_enabled() {
return String::new();
}
let (Some(scope), Some(sp)) = (self.current_dbg_subprogram_id, span) else {
return String::new();
};
let loc_id = self.dbg_alloc_id();
let _ = writeln!(
&mut self.dbg_metadata,
"!{} = !DILocation(line: {}, column: {}, scope: !{})",
loc_id,
sp.line.saturating_add(1),
sp.column.saturating_add(1),
scope,
);
format!(", !dbg !{}", loc_id)
}
pub(super) fn dbg_emit_module_metadata(&self, ir: &mut String) {
let (Some(cu_id), Some((dwarf_ver_id, debug_info_ver_id))) =
(self.dbg_cu_id, self.dbg_module_flag_ids)
else {
return;
};
if !self.dbg_enabled() {
return;
}
let _ = writeln!(ir);
let _ = writeln!(ir, "!llvm.dbg.cu = !{{!{}}}", cu_id);
let _ = writeln!(
ir,
"!llvm.module.flags = !{{!{}, !{}}}",
dwarf_ver_id, debug_info_ver_id,
);
let _ = writeln!(
ir,
"!{} = !{{i32 7, !\"Dwarf Version\", i32 4}}",
dwarf_ver_id,
);
let _ = writeln!(
ir,
"!{} = !{{i32 2, !\"Debug Info Version\", i32 3}}",
debug_info_ver_id,
);
ir.push_str(&self.dbg_metadata);
}
}
fn escape_md(s: &str) -> String {
s.replace('\\', "\\\\").replace('"', "\\\"")
}