1use 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
30mod 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 pub struct LLVMDIBuilder<'a>(LLVMDIBuilderRef, PhantomData<&'a LLVMModule>);
43
44 impl<'a> LLVMDIBuilder<'a> {
45 pub fn new(module: &'a LLVMModule) -> Self {
47 unsafe { LLVMDIBuilder(LLVMCreateDIBuilder(module.inner_ref()), PhantomData) }
48 }
49
50 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
116fn is_md_kind(md: LLVMMetadata, kind: LLVMMetadataKind) -> bool {
118 llvm_get_metadata_kind(md) as u32 == kind as u32
120}
121
122fn 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
132pub fn llvm_debug_metadata_version() -> u32 {
134 unsafe { LLVMDebugMetadataVersion() }
135}
136
137pub 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
155pub 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#[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
232pub 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#[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
296pub 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
316pub 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
340pub 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
346pub 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
356pub 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
363pub 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
372pub 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
379pub 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
388pub 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
397pub 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
406pub 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
416pub 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
424pub 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}