Skip to main content

pliron_llvm/llvm_sys/
debuginfo.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) The pliron contributors
3
4//! Safe(r) wrappers around llvm_sys::debuginfo.
5
6use std::ptr;
7
8use llvm_sys::{
9    debuginfo::{
10        LLVMChecksumKind, LLVMDIBuilderCreateCompileUnit, LLVMDIBuilderCreateDebugLocation,
11        LLVMDIBuilderCreateFile, LLVMDIBuilderCreateFileWithChecksum, LLVMDIBuilderCreateFunction,
12        LLVMDIBuilderCreateLexicalBlockFile, LLVMDIBuilderCreateSubroutineType,
13        LLVMDIFileGetDirectory, LLVMDIFileGetFilename, LLVMDIFlags, LLVMDILocationGetColumn,
14        LLVMDILocationGetInlinedAt, LLVMDILocationGetLine, LLVMDILocationGetScope,
15        LLVMDIScopeGetFile, LLVMDISubprogramGetLine, LLVMDWARFEmissionKind,
16        LLVMDWARFSourceLanguage, LLVMDebugMetadataVersion, LLVMGetSubprogram,
17        LLVMInstructionGetDebugLoc, LLVMMetadataKind, LLVMSetSubprogram,
18    },
19    prelude::LLVMMetadataRef,
20};
21
22use crate::{
23    llvm_sys::{
24        core::{LLVMContext, LLVMMetadata, LLVMValue, llvm_get_metadata_kind, llvm_is_a},
25        sized_cstr_to_string,
26    },
27    metadata_conversions::to_llvm_ir::{DwarfEmissionKind, DwarfSourceLanguage},
28};
29
30// We wrap LLVMDIBuilder in a module to limit its visibility for constructing
31mod llvm_di_builder {
32    use core::marker::PhantomData;
33
34    use llvm_sys::{
35        debuginfo::{LLVMCreateDIBuilder, LLVMDIBuilderFinalize, LLVMDisposeDIBuilder},
36        prelude::LLVMDIBuilderRef,
37    };
38
39    use crate::llvm_sys::core::LLVMModule;
40
41    /// Managed LLVMDIBuilder. Its module's metadata is finalized on [Drop::drop].
42    pub struct LLVMDIBuilder<'a>(LLVMDIBuilderRef, PhantomData<&'a LLVMModule>);
43
44    impl<'a> LLVMDIBuilder<'a> {
45        /// Create a new LLVMDIBuilder for the given module.
46        pub fn new(module: &'a LLVMModule) -> Self {
47            unsafe { LLVMDIBuilder(LLVMCreateDIBuilder(module.inner_ref()), PhantomData) }
48        }
49
50        /// Get the inner LLVMDIBuilderRef
51        pub(in crate::llvm_sys) fn inner_ref(&self) -> LLVMDIBuilderRef {
52            self.0
53        }
54    }
55
56    impl Drop for LLVMDIBuilder<'_> {
57        fn drop(&mut self) {
58            unsafe {
59                LLVMDIBuilderFinalize(self.0);
60                LLVMDisposeDIBuilder(self.0);
61            }
62        }
63    }
64}
65pub use llvm_di_builder::LLVMDIBuilder;
66
67impl From<LLVMDWARFEmissionKind> for DwarfEmissionKind {
68    fn from(kind: LLVMDWARFEmissionKind) -> Self {
69        match kind {
70            LLVMDWARFEmissionKind::LLVMDWARFEmissionKindNone => DwarfEmissionKind::None,
71            LLVMDWARFEmissionKind::LLVMDWARFEmissionKindFull => DwarfEmissionKind::Full,
72            LLVMDWARFEmissionKind::LLVMDWARFEmissionKindLineTablesOnly => {
73                DwarfEmissionKind::LineTablesOnly
74            }
75        }
76    }
77}
78
79impl From<DwarfEmissionKind> for LLVMDWARFEmissionKind {
80    fn from(kind: DwarfEmissionKind) -> Self {
81        match kind {
82            DwarfEmissionKind::None => LLVMDWARFEmissionKind::LLVMDWARFEmissionKindNone,
83            DwarfEmissionKind::Full => LLVMDWARFEmissionKind::LLVMDWARFEmissionKindFull,
84            DwarfEmissionKind::LineTablesOnly => {
85                LLVMDWARFEmissionKind::LLVMDWARFEmissionKindLineTablesOnly
86            }
87        }
88    }
89}
90
91impl From<LLVMDWARFSourceLanguage> for DwarfSourceLanguage {
92    fn from(language: LLVMDWARFSourceLanguage) -> Self {
93        match language {
94            LLVMDWARFSourceLanguage::LLVMDWARFSourceLanguageC => DwarfSourceLanguage::C,
95            LLVMDWARFSourceLanguage::LLVMDWARFSourceLanguageC_plus_plus => {
96                DwarfSourceLanguage::CPlusPlus
97            }
98            LLVMDWARFSourceLanguage::LLVMDWARFSourceLanguageRust => DwarfSourceLanguage::Rust,
99            language => todo!("DWARF source language {language:?}"),
100        }
101    }
102}
103
104impl From<DwarfSourceLanguage> for LLVMDWARFSourceLanguage {
105    fn from(language: DwarfSourceLanguage) -> Self {
106        match language {
107            DwarfSourceLanguage::C => LLVMDWARFSourceLanguage::LLVMDWARFSourceLanguageC,
108            DwarfSourceLanguage::CPlusPlus => {
109                LLVMDWARFSourceLanguage::LLVMDWARFSourceLanguageC_plus_plus
110            }
111            DwarfSourceLanguage::Rust => LLVMDWARFSourceLanguage::LLVMDWARFSourceLanguageRust,
112        }
113    }
114}
115
116/// Is `md` of `kind`?
117fn is_md_kind(md: LLVMMetadata, kind: LLVMMetadataKind) -> bool {
118    // `LLVMMetadataKind` has no `PartialEq`. Thus we compare the discriminants.
119    llvm_get_metadata_kind(md) as u32 == kind as u32
120}
121
122/// Is `md` a scope that a `DILocation` can refer to?
123fn is_local_scope(md: LLVMMetadata) -> bool {
124    matches!(
125        llvm_get_metadata_kind(md),
126        LLVMMetadataKind::LLVMDISubprogramMetadataKind
127            | LLVMMetadataKind::LLVMDILexicalBlockMetadataKind
128            | LLVMMetadataKind::LLVMDILexicalBlockFileMetadataKind
129    )
130}
131
132/// LLVMDebugMetadataVersion
133pub fn llvm_debug_metadata_version() -> u32 {
134    unsafe { LLVMDebugMetadataVersion() }
135}
136
137/// LLVMDIBuilderCreateFile
138pub fn llvm_di_builder_create_file(
139    builder: &LLVMDIBuilder,
140    filename: &str,
141    directory: &str,
142) -> LLVMMetadata {
143    unsafe {
144        LLVMDIBuilderCreateFile(
145            builder.inner_ref(),
146            filename.as_ptr() as *const core::ffi::c_char,
147            filename.len(),
148            directory.as_ptr() as *const core::ffi::c_char,
149            directory.len(),
150        )
151        .into()
152    }
153}
154
155/// LLVMDIBuilderCreateFileWithChecksum
156pub fn llvm_di_builder_create_file_with_checksum(
157    builder: &LLVMDIBuilder,
158    filename: &str,
159    directory: &str,
160    checksum_kind: LLVMChecksumKind,
161    checksum: &str,
162    source: &str,
163) -> LLVMMetadata {
164    let len = match checksum_kind {
165        LLVMChecksumKind::CSK_MD5 => 32,
166        LLVMChecksumKind::CSK_SHA1 => 40,
167        LLVMChecksumKind::CSK_SHA256 => 64,
168    };
169    assert!(checksum.len() == len && checksum.bytes().all(|b| b.is_ascii_hexdigit()));
170    unsafe {
171        LLVMDIBuilderCreateFileWithChecksum(
172            builder.inner_ref(),
173            filename.as_ptr() as *const core::ffi::c_char,
174            filename.len(),
175            directory.as_ptr() as *const core::ffi::c_char,
176            directory.len(),
177            checksum_kind,
178            checksum.as_ptr() as *const core::ffi::c_char,
179            checksum.len(),
180            source.as_ptr() as *const core::ffi::c_char,
181            source.len(),
182        )
183        .into()
184    }
185}
186
187/// LLVMDIBuilderCreateCompileUnit
188#[allow(clippy::too_many_arguments)]
189pub fn llvm_di_builder_create_compile_unit(
190    builder: &LLVMDIBuilder,
191    language: LLVMDWARFSourceLanguage,
192    file: LLVMMetadata,
193    producer: &str,
194    is_optimized: bool,
195    flags: &str,
196    runtime_version: u32,
197    split_name: &str,
198    kind: LLVMDWARFEmissionKind,
199    dwo_id: u32,
200    split_debug_inlining: bool,
201    debug_info_for_profiling: bool,
202    sys_root: &str,
203    sdk: &str,
204) -> LLVMMetadata {
205    assert!(is_md_kind(file, LLVMMetadataKind::LLVMDIFileMetadataKind));
206    unsafe {
207        LLVMDIBuilderCreateCompileUnit(
208            builder.inner_ref(),
209            language,
210            file.into(),
211            producer.as_ptr() as *const core::ffi::c_char,
212            producer.len(),
213            is_optimized.into(),
214            flags.as_ptr() as *const core::ffi::c_char,
215            flags.len(),
216            runtime_version,
217            split_name.as_ptr() as *const core::ffi::c_char,
218            split_name.len(),
219            kind,
220            dwo_id,
221            split_debug_inlining.into(),
222            debug_info_for_profiling.into(),
223            sys_root.as_ptr() as *const core::ffi::c_char,
224            sys_root.len(),
225            sdk.as_ptr() as *const core::ffi::c_char,
226            sdk.len(),
227        )
228        .into()
229    }
230}
231
232/// LLVMDIBuilderCreateSubroutineType
233pub fn llvm_di_builder_create_subroutine_type(
234    builder: &LLVMDIBuilder,
235    file: LLVMMetadata,
236    parameter_types: &[LLVMMetadata],
237    flags: LLVMDIFlags,
238) -> LLVMMetadata {
239    assert!(is_md_kind(file, LLVMMetadataKind::LLVMDIFileMetadataKind));
240    let mut parameter_types: Vec<LLVMMetadataRef> =
241        parameter_types.iter().map(|ty| (*ty).into()).collect();
242    unsafe {
243        LLVMDIBuilderCreateSubroutineType(
244            builder.inner_ref(),
245            file.into(),
246            parameter_types.as_mut_ptr(),
247            parameter_types.len().try_into().unwrap(),
248            flags,
249        )
250        .into()
251    }
252}
253
254/// LLVMDIBuilderCreateFunction
255#[allow(clippy::too_many_arguments)]
256pub fn llvm_di_builder_create_function(
257    builder: &LLVMDIBuilder,
258    scope: LLVMMetadata,
259    name: &str,
260    linkage_name: &str,
261    file: LLVMMetadata,
262    line: u32,
263    ty: LLVMMetadata,
264    is_local_to_unit: bool,
265    is_definition: bool,
266    scope_line: u32,
267    flags: LLVMDIFlags,
268    is_optimized: bool,
269) -> LLVMMetadata {
270    assert!(is_md_kind(file, LLVMMetadataKind::LLVMDIFileMetadataKind));
271    assert!(is_md_kind(
272        ty,
273        LLVMMetadataKind::LLVMDISubroutineTypeMetadataKind
274    ));
275    unsafe {
276        LLVMDIBuilderCreateFunction(
277            builder.inner_ref(),
278            scope.into(),
279            name.as_ptr() as *const core::ffi::c_char,
280            name.len(),
281            linkage_name.as_ptr() as *const core::ffi::c_char,
282            linkage_name.len(),
283            file.into(),
284            line,
285            ty.into(),
286            is_local_to_unit.into(),
287            is_definition.into(),
288            scope_line,
289            flags,
290            is_optimized.into(),
291        )
292        .into()
293    }
294}
295
296/// LLVMDIBuilderCreateLexicalBlockFile
297pub fn llvm_di_builder_create_lexical_block_file(
298    builder: &LLVMDIBuilder,
299    scope: LLVMMetadata,
300    file: LLVMMetadata,
301    discriminator: u32,
302) -> LLVMMetadata {
303    assert!(is_local_scope(scope));
304    assert!(is_md_kind(file, LLVMMetadataKind::LLVMDIFileMetadataKind));
305    unsafe {
306        LLVMDIBuilderCreateLexicalBlockFile(
307            builder.inner_ref(),
308            scope.into(),
309            file.into(),
310            discriminator,
311        )
312        .into()
313    }
314}
315
316/// LLVMDIBuilderCreateDebugLocation
317pub fn llvm_di_builder_create_debug_location(
318    ctx: &LLVMContext,
319    line: u32,
320    column: u32,
321    scope: LLVMMetadata,
322    inlined_at: Option<LLVMMetadata>,
323) -> LLVMMetadata {
324    assert!(is_local_scope(scope));
325    assert!(
326        inlined_at.is_none_or(|loc| is_md_kind(loc, LLVMMetadataKind::LLVMDILocationMetadataKind))
327    );
328    unsafe {
329        LLVMDIBuilderCreateDebugLocation(
330            ctx.inner_ref(),
331            line,
332            column,
333            scope.into(),
334            inlined_at.map_or(ptr::null_mut(), Into::into),
335        )
336        .into()
337    }
338}
339
340/// LLVMDIScopeGetFile
341pub fn llvm_di_scope_get_file(scope: LLVMMetadata) -> Option<LLVMMetadata> {
342    let file = unsafe { LLVMDIScopeGetFile(scope.into()) };
343    (!file.is_null()).then(|| file.into())
344}
345
346/// LLVMSetSubprogram
347pub fn llvm_set_subprogram(func: LLVMValue, subprogram: LLVMMetadata) {
348    assert!(llvm_is_a::function(func));
349    assert!(is_md_kind(
350        subprogram,
351        LLVMMetadataKind::LLVMDISubprogramMetadataKind
352    ));
353    unsafe { LLVMSetSubprogram(func.into(), subprogram.into()) }
354}
355
356/// LLVMGetSubprogram
357pub fn llvm_get_subprogram(func: LLVMValue) -> Option<LLVMMetadata> {
358    assert!(llvm_is_a::function(func));
359    let subprogram = unsafe { LLVMGetSubprogram(func.into()) };
360    (!subprogram.is_null()).then(|| subprogram.into())
361}
362
363/// LLVMDISubprogramGetLine
364pub fn llvm_di_subprogram_get_line(subprogram: LLVMMetadata) -> u32 {
365    assert!(is_md_kind(
366        subprogram,
367        LLVMMetadataKind::LLVMDISubprogramMetadataKind
368    ));
369    unsafe { LLVMDISubprogramGetLine(subprogram.into()) }
370}
371
372/// LLVMInstructionGetDebugLoc
373pub fn llvm_instruction_get_debug_loc(inst: LLVMValue) -> Option<LLVMMetadata> {
374    assert!(llvm_is_a::instruction(inst));
375    let loc = unsafe { LLVMInstructionGetDebugLoc(inst.into()) };
376    (!loc.is_null()).then(|| loc.into())
377}
378
379/// LLVMDILocationGetLine
380pub fn llvm_di_location_get_line(loc: LLVMMetadata) -> u32 {
381    assert!(is_md_kind(
382        loc,
383        LLVMMetadataKind::LLVMDILocationMetadataKind
384    ));
385    unsafe { LLVMDILocationGetLine(loc.into()) }
386}
387
388/// LLVMDILocationGetColumn
389pub fn llvm_di_location_get_column(loc: LLVMMetadata) -> u32 {
390    assert!(is_md_kind(
391        loc,
392        LLVMMetadataKind::LLVMDILocationMetadataKind
393    ));
394    unsafe { LLVMDILocationGetColumn(loc.into()) }
395}
396
397/// LLVMDILocationGetScope
398pub fn llvm_di_location_get_scope(loc: LLVMMetadata) -> LLVMMetadata {
399    assert!(is_md_kind(
400        loc,
401        LLVMMetadataKind::LLVMDILocationMetadataKind
402    ));
403    unsafe { LLVMDILocationGetScope(loc.into()).into() }
404}
405
406/// LLVMDILocationGetInlinedAt
407pub fn llvm_di_location_get_inlined_at(loc: LLVMMetadata) -> Option<LLVMMetadata> {
408    assert!(is_md_kind(
409        loc,
410        LLVMMetadataKind::LLVMDILocationMetadataKind
411    ));
412    let inlined_at = unsafe { LLVMDILocationGetInlinedAt(loc.into()) };
413    (!inlined_at.is_null()).then(|| inlined_at.into())
414}
415
416/// LLVMDIFileGetFilename
417pub fn llvm_di_file_get_filename(file: LLVMMetadata) -> String {
418    assert!(is_md_kind(file, LLVMMetadataKind::LLVMDIFileMetadataKind));
419    let mut len = 0;
420    let ptr = unsafe { LLVMDIFileGetFilename(file.into(), &mut len) };
421    sized_cstr_to_string(ptr, len as usize).unwrap_or_default()
422}
423
424/// LLVMDIFileGetDirectory
425pub fn llvm_di_file_get_directory(file: LLVMMetadata) -> String {
426    assert!(is_md_kind(file, LLVMMetadataKind::LLVMDIFileMetadataKind));
427    let mut len = 0;
428    let ptr = unsafe { LLVMDIFileGetDirectory(file.into(), &mut len) };
429    sized_cstr_to_string(ptr, len as usize).unwrap_or_default()
430}