1pub mod from_llvm_ir {
7 use alloc::{
8 boxed::Box,
9 string::{String, ToString},
10 vec,
11 vec::Vec,
12 };
13
14 use llvm_sys::debuginfo::LLVMMetadataKind;
15 use pliron::{
16 builtin::ops::ModuleOp,
17 combine::stream::position::SourcePosition,
18 context::{Context, Ptr},
19 input_error_noloc,
20 location::{Location, Source},
21 operation::Operation,
22 result::Result,
23 std_deps::path::PathBuf,
24 utils::table::{HMap, HSet},
25 };
26 use thiserror::Error;
27
28 use crate::{
29 from_llvm_ir::{ConversionContext, const_llvm_value_to_attr},
30 llvm_sys::{
31 core::{
32 LLVMMetadata, LLVMModule, LLVMValue, llvm_get_md_kind_id_in_module,
33 llvm_get_md_node_operands, llvm_get_md_string, llvm_get_metadata_kind,
34 llvm_get_named_metadata_operands, llvm_global_copy_all_metadata,
35 llvm_instruction_get_all_metadata_other_than_debug_loc, llvm_is_a,
36 llvm_md_node_in_module, llvm_metadata_as_value_in_module,
37 llvm_named_metadata_names, llvm_print_module_to_string, llvm_print_value_to_string,
38 llvm_value_as_metadata,
39 },
40 debuginfo::{
41 llvm_di_file_get_directory, llvm_di_file_get_filename, llvm_di_location_get_column,
42 llvm_di_location_get_inlined_at, llvm_di_location_get_line,
43 llvm_di_location_get_scope, llvm_di_scope_get_file, llvm_di_subprogram_get_line,
44 llvm_get_subprogram, llvm_instruction_get_debug_loc,
45 },
46 },
47 metadata::{
48 MdAttachmentsAttr, MdNodeAttr, MdNodeId, MdOperandAttr, MdTableAttr, NamedMdAttr,
49 set_attachments, set_metadata_table, set_named_metadata,
50 },
51 };
52
53 #[derive(Default)]
55 pub(crate) struct MdConversionContext {
56 node_map: HMap<LLVMMetadata, Option<MdNodeId>>,
59 table: MdTableAttr,
61 kind_names: HMap<u32, String>,
63 kind_names_scraped: bool,
65 }
66
67 #[derive(Error, Debug)]
69 pub enum MdConversionErr {
70 #[error("Cannot determine the name of metadata kind id {0}")]
71 UnknownKind(u32),
72 }
73
74 pub(crate) const MD_KIND_DBG: &str = "dbg";
76
77 const FIXED_MD_KIND_NAMES: &[&str] = &[
80 "dbg",
81 "tbaa",
82 "prof",
83 "fpmath",
84 "range",
85 "tbaa.struct",
86 "invariant.load",
87 "alias.scope",
88 "noalias",
89 "nontemporal",
90 "llvm.mem.parallel_loop_access",
91 "nonnull",
92 "dereferenceable",
93 "dereferenceable_or_null",
94 "make.implicit",
95 "unpredictable",
96 "invariant.group",
97 "align",
98 "llvm.loop",
99 "type",
100 "section_prefix",
101 "absolute_symbol",
102 "associated",
103 "callees",
104 "irr_loop",
105 "llvm.access.group",
106 "callback",
107 "llvm.preserve.access.index",
108 "vcall_visibility",
109 "noundef",
110 "annotation",
111 "nosanitize",
112 "func_sanitize",
113 "exclude",
114 "memprof",
115 "callsite",
116 "kcfi_type",
117 "pcsections",
118 "DIAssignID",
119 "coro.outside.frame",
120 "mmra",
121 "noalias.addrspace",
122 "callee_type",
123 "nofree",
124 "captures",
125 "alloc_token",
126 "implicit.ref",
127 ];
128
129 fn scrape_md_kind_names(module_text: &str) -> Vec<String> {
139 fn is_name_start(c: u8) -> bool {
140 c.is_ascii_alphabetic() || matches!(c, b'-' | b'$' | b'.' | b'_')
141 }
142 fn is_name_char(c: u8) -> bool {
143 c.is_ascii_alphanumeric() || matches!(c, b'-' | b'$' | b'.' | b'_')
144 }
145 fn hex_digit(c: Option<&u8>) -> Option<u8> {
146 c.and_then(|c| (*c as char).to_digit(16)).map(|d| d as u8)
147 }
148
149 let text = module_text.as_bytes();
150 let mut seen = HSet::default();
151 let mut names = vec![];
152 let mut idx = 0;
153 while idx < text.len() {
154 if text[idx] != b'!' {
155 idx += 1;
156 continue;
157 }
158 idx += 1;
159 let mut name = vec![];
162 while idx < text.len() {
163 let c = text[idx];
164 if is_name_char(c) && (!name.is_empty() || is_name_start(c)) {
165 name.push(c);
166 idx += 1;
167 } else if c == b'\\'
168 && let (Some(hi), Some(lo)) =
169 (hex_digit(text.get(idx + 1)), hex_digit(text.get(idx + 2)))
170 {
171 name.push((hi << 4) | lo);
172 idx += 3;
173 } else {
174 break;
175 }
176 }
177 if let Ok(name) = String::from_utf8(name)
179 && !name.is_empty()
180 && seen.insert(name.clone())
181 {
182 names.push(name);
183 }
184 }
185 names
186 }
187
188 pub(crate) fn md_kind_name(
190 cctx: &mut ConversionContext,
191 module: &LLVMModule,
192 kind_id: u32,
193 ) -> Result<String> {
194 if cctx.md.kind_names.is_empty() {
195 for name in FIXED_MD_KIND_NAMES {
196 let id = llvm_get_md_kind_id_in_module(module, name);
197 cctx.md
198 .kind_names
199 .entry(id)
200 .or_insert_with(|| name.to_string());
201 }
202 }
203
204 if let Some(name) = cctx.md.kind_names.get(&kind_id) {
205 return Ok(name.clone());
206 }
207
208 if !cctx.md.kind_names_scraped {
210 cctx.md.kind_names_scraped = true;
211 let module_text = llvm_print_module_to_string(module)
212 .ok_or_else(|| input_error_noloc!(MdConversionErr::UnknownKind(kind_id)))?;
213 for name in scrape_md_kind_names(&module_text) {
214 let id = llvm_get_md_kind_id_in_module(module, &name);
215 cctx.md.kind_names.entry(id).or_insert(name);
216 }
217 }
218
219 cctx.md
220 .kind_names
221 .get(&kind_id)
222 .cloned()
223 .ok_or_else(|| input_error_noloc!(MdConversionErr::UnknownKind(kind_id)))
224 }
225
226 fn convert_md_node(
229 ctx: &Context,
230 cctx: &mut ConversionContext,
231 module: &LLVMModule,
232 md: LLVMMetadata,
233 ) -> Result<Option<MdNodeId>> {
234 if let Some(id) = cctx.md.node_map.get(&md) {
235 return Ok(*id);
236 }
237
238 let kind = llvm_get_metadata_kind(md);
240 if !matches!(kind, LLVMMetadataKind::LLVMMDTupleMetadataKind) {
241 log::warn!("Dropping unsupported metadata of kind {kind:?}");
242 cctx.md.node_map.insert(md, None);
243 return Ok(None);
244 }
245
246 let id = cctx.md.table.reserve();
249 cctx.md.node_map.insert(md, Some(id));
250
251 let md_val = llvm_metadata_as_value_in_module(module, md);
252 let llvm_operands = llvm_get_md_node_operands(md_val);
253 let mut operands = Vec::with_capacity(llvm_operands.len());
254 for operand in &llvm_operands {
255 let Some(operand) = convert_md_operand(ctx, cctx, module, *operand)? else {
258 let printed = llvm_print_value_to_string(md_val).unwrap_or_default();
261 let printed = printed.split_once(" = ").map_or(&*printed, |(_, def)| def);
262 log::warn!("Dropping metadata node {printed} with an operand we cannot represent");
263 cctx.md.node_map.insert(md, None);
264 return Ok(None);
266 };
267 operands.push(operand);
268 }
269
270 let llvm_md_operands: Vec<_> = llvm_operands
274 .iter()
275 .map(|operand| operand.map(llvm_value_as_metadata))
276 .collect();
277 let distinct = llvm_md_node_in_module(module, &llvm_md_operands) != md;
278
279 cctx.md.table.set(
280 id,
281 if distinct {
282 MdNodeAttr::new_distinct_tuple(operands)
283 } else {
284 MdNodeAttr::new_tuple(operands)
285 },
286 );
287
288 Ok(Some(id))
289 }
290
291 fn convert_md_operand(
294 ctx: &Context,
295 cctx: &mut ConversionContext,
296 module: &LLVMModule,
297 operand: Option<LLVMValue>,
298 ) -> Result<Option<MdOperandAttr>> {
299 let Some(val) = operand else {
300 return Ok(Some(MdOperandAttr::Null));
301 };
302
303 let md = llvm_value_as_metadata(val);
306 match llvm_get_metadata_kind(md) {
307 LLVMMetadataKind::LLVMMDStringMetadataKind => match llvm_get_md_string(val) {
308 Some(s) => Ok(Some(MdOperandAttr::String(s))),
309 None => {
310 log::warn!("Dropping metadata string operand whose contents aren't UTF-8");
311 Ok(None)
312 }
313 },
314 LLVMMetadataKind::LLVMMDTupleMetadataKind => {
315 Ok(convert_md_node(ctx, cctx, module, md)?.map(MdOperandAttr::Node))
316 }
317 LLVMMetadataKind::LLVMConstantAsMetadataMetadataKind => {
318 match const_llvm_value_to_attr(ctx, cctx, val)? {
319 Some(attr) => Ok(Some(MdOperandAttr::Constant(attr))),
320 None => {
321 log::warn!(
322 "Dropping unsupported constant metadata operand {}",
323 llvm_print_value_to_string(val).unwrap_or_default()
324 );
325 Ok(None)
326 }
327 }
328 }
329 kind => {
330 log::warn!("Dropping unsupported metadata operand of kind {kind:?}");
331 Ok(None)
332 }
333 }
334 }
335
336 fn convert_md_attachments(
338 ctx: &Context,
339 cctx: &mut ConversionContext,
340 module: &LLVMModule,
341 entries: Vec<(u32, LLVMMetadata)>,
342 m_op: Ptr<Operation>,
343 ) -> Result<()> {
344 if entries.is_empty() {
345 return Ok(());
346 }
347 let mut attachments = MdAttachmentsAttr::new();
348 for (kind_id, md) in entries {
349 let kind = md_kind_name(cctx, module, kind_id)?;
350 if kind == MD_KIND_DBG {
351 continue;
353 }
354 let Some(node) = convert_md_node(ctx, cctx, module, md)? else {
355 log::warn!("Dropping metadata attached under kind \"{kind}\"");
356 continue;
357 };
358 attachments.set(kind, node);
359 }
360 if !attachments.is_empty() {
361 set_attachments(ctx, m_op, attachments);
362 }
363 Ok(())
364 }
365
366 pub(crate) fn convert_instruction_metadata(
368 ctx: &Context,
369 cctx: &mut ConversionContext,
370 module: &LLVMModule,
371 inst: LLVMValue,
372 m_inst: Ptr<Operation>,
373 ) -> Result<()> {
374 convert_instruction_debug_loc(ctx, inst, m_inst);
375 let entries = llvm_instruction_get_all_metadata_other_than_debug_loc(inst);
376 convert_md_attachments(ctx, cctx, module, entries, m_inst)
377 }
378
379 pub(crate) fn convert_global_object_metadata(
381 ctx: &Context,
382 cctx: &mut ConversionContext,
383 module: &LLVMModule,
384 global: LLVMValue,
385 m_op: Ptr<Operation>,
386 ) -> Result<()> {
387 if llvm_is_a::function(global) {
388 convert_function_debug_loc(ctx, global, m_op);
389 }
390 let entries = llvm_global_copy_all_metadata(global);
391 convert_md_attachments(ctx, cctx, module, entries, m_op)
392 }
393
394 fn src_pos(ctx: &Context, scope: LLVMMetadata, line: u32, column: u32) -> Location {
396 if line == 0 {
397 return Location::Unknown;
399 }
400 let src = llvm_di_scope_get_file(scope).map_or(Source::InMemory, |file| {
401 let path = PathBuf::from(llvm_di_file_get_directory(file))
403 .join(llvm_di_file_get_filename(file));
404 Source::new_from_file(ctx, path)
405 });
406 let pos = SourcePosition {
407 line: line.try_into().unwrap_or(i32::MAX),
408 column: column.try_into().unwrap_or(i32::MAX),
409 };
410 Location::SrcPos { src, pos }
411 }
412
413 fn convert_di_location(ctx: &Context, di_loc: LLVMMetadata) -> Location {
416 let loc = src_pos(
417 ctx,
418 llvm_di_location_get_scope(di_loc),
419 llvm_di_location_get_line(di_loc),
420 llvm_di_location_get_column(di_loc),
421 );
422 match llvm_di_location_get_inlined_at(di_loc) {
423 Some(caller) => Location::CallSite {
426 callee: Box::new(loc),
427 caller: Box::new(convert_di_location(ctx, caller)),
428 },
429 None => loc,
430 }
431 }
432
433 fn convert_instruction_debug_loc(ctx: &Context, inst: LLVMValue, m_inst: Ptr<Operation>) {
435 if let Some(di_loc) = llvm_instruction_get_debug_loc(inst) {
436 let loc = convert_di_location(ctx, di_loc);
437 m_inst.deref_mut(ctx).set_loc(loc);
438 }
439 }
440
441 fn convert_function_debug_loc(ctx: &Context, function: LLVMValue, m_func: Ptr<Operation>) {
443 if let Some(subprogram) = llvm_get_subprogram(function) {
444 let line = llvm_di_subprogram_get_line(subprogram);
445 let loc = src_pos(ctx, subprogram, line, 0);
448 m_func.deref_mut(ctx).set_loc(loc);
449 }
450 }
451
452 fn convert_named_metadata(
454 ctx: &Context,
455 cctx: &mut ConversionContext,
456 module: &LLVMModule,
457 ) -> Result<NamedMdAttr> {
458 let mut named = NamedMdAttr::new();
459 for name in llvm_named_metadata_names(module) {
460 for operand in llvm_get_named_metadata_operands(module, &name) {
461 let md = llvm_value_as_metadata(operand);
462 let Some(node) = convert_md_node(ctx, cctx, module, md)? else {
463 log::warn!("Dropping an operand of named metadata \"{name}\"");
464 continue;
465 };
466 named.push(name.clone(), node);
467 }
468 }
469 Ok(named)
470 }
471
472 pub(crate) fn convert_module_metadata(
477 ctx: &Context,
478 cctx: &mut ConversionContext,
479 module: &LLVMModule,
480 module_op: ModuleOp,
481 ) -> Result<()> {
482 let named_md = convert_named_metadata(ctx, cctx, module)?;
483 if !named_md.is_empty() {
484 set_named_metadata(ctx, module_op, named_md);
485 }
486 if !cctx.md.table.is_empty() {
487 set_metadata_table(ctx, module_op, cctx.md.table.clone());
488 }
489 Ok(())
490 }
491}
492
493pub mod to_llvm_ir {
496 use alloc::{
497 string::{String, ToString},
498 vec::Vec,
499 };
500
501 use llvm_sys::{
502 LLVMModuleFlagBehavior,
503 debuginfo::{LLVMChecksumKind, LLVMDIFlagZero},
504 };
505 use pliron::{
506 attribute::attr_cast,
507 builtin::{op_interfaces::SymbolOpInterface, ops::ModuleOp},
508 context::{Context, Ptr},
509 graph::walkers::{
510 IRNode, WALKCONFIG_PREORDER_FORWARD,
511 interruptible::{WalkResult, immutable::walk_op, walk_advance, walk_break},
512 },
513 input_err_noloc, input_error_noloc,
514 location::{Location, Source},
515 op::Op,
516 operation::Operation,
517 printable::Printable,
518 result::Result,
519 uniqued_any,
520 utils::table::{HMap, HSet},
521 };
522 use thiserror::Error;
523
524 use crate::{
525 llvm_sys::{
526 core::{
527 LLVMContext, LLVMMetadata, LLVMModule, LLVMValue, llvm_add_module_flag,
528 llvm_add_named_metadata_operand, llvm_const_int, llvm_get_md_kind_id_in_context,
529 llvm_get_module_flag, llvm_global_set_metadata, llvm_int_type_in_context,
530 llvm_is_a, llvm_md_node_in_context2, llvm_md_string_in_context2,
531 llvm_metadata_as_value, llvm_metadata_replace_all_uses_with,
532 llvm_set_current_debug_location2, llvm_set_metadata, llvm_temporary_md_node,
533 llvm_value_as_metadata,
534 },
535 debuginfo::{
536 LLVMDIBuilder, llvm_debug_metadata_version, llvm_di_builder_create_compile_unit,
537 llvm_di_builder_create_debug_location, llvm_di_builder_create_file,
538 llvm_di_builder_create_file_with_checksum, llvm_di_builder_create_function,
539 llvm_di_builder_create_lexical_block_file, llvm_di_builder_create_subroutine_type,
540 llvm_di_scope_get_file, llvm_set_subprogram,
541 },
542 },
543 metadata::{
544 MdNodeId, MdOperandAttr, MdTableAttr, get_attachments, get_metadata_table,
545 get_named_metadata,
546 },
547 op_interfaces::LlvmSymbolName,
548 ops::FuncOp,
549 to_llvm_ir::{AttrToLLVMConst, ConversionContext},
550 };
551
552 pub(crate) struct MdConversionContext<'a> {
554 table: MdTableAttr,
556 node_map: HMap<MdNodeId, LLVMMetadata>,
558 temporaries: HMap<MdNodeId, LLVMMetadata>,
560 in_progress: HSet<MdNodeId>,
562 di: Option<DiConversionContext<'a>>,
564 }
565
566 impl<'a> MdConversionContext<'a> {
567 pub(crate) fn new(
568 module: &'a LLVMModule,
569 debug_info: Option<DebugInfoOptions<'a>>,
570 ) -> Self {
571 Self {
572 table: MdTableAttr::default(),
573 node_map: HMap::default(),
574 temporaries: HMap::default(),
575 in_progress: HSet::default(),
576 di: debug_info.map(|options| DiConversionContext::new(module, options)),
577 }
578 }
579 }
580
581 #[derive(Error, Debug)]
583 pub enum MdToLLVMErr {
584 #[error("Metadata node #{0} is not in the module's metadata table")]
585 DanglingNodeRef(MdNodeId),
586 #[error("Metadata operand {0} is not convertible to an LLVM constant")]
587 OperandNotConst(String),
588 #[error(
589 "Metadata node #{0} is `distinct`, but the LLVM C-API can only create a distinct \
590 node that refers to itself"
591 )]
592 UnrepresentableDistinct(MdNodeId),
593 }
594
595 fn convert_md_node(
598 ctx: &Context,
599 llvm_ctx: &LLVMContext,
600 cctx: &mut ConversionContext,
601 id: MdNodeId,
602 ) -> Result<LLVMMetadata> {
603 if let Some(md) = cctx.md.node_map.get(&id) {
604 return Ok(*md);
605 }
606 if cctx.md.in_progress.contains(&id) {
610 let temp = *cctx
613 .md
614 .temporaries
615 .entry(id)
616 .or_insert_with(|| llvm_temporary_md_node(llvm_ctx, &[]));
617 return Ok(temp);
618 }
619
620 let node = cctx
621 .md
622 .table
623 .get(id)
624 .cloned()
625 .ok_or_else(|| input_error_noloc!(MdToLLVMErr::DanglingNodeRef(id)))?;
626
627 cctx.md.in_progress.insert(id);
628 let mut operands = Vec::with_capacity(node.operands().len());
629 for operand in node.operands() {
630 operands.push(convert_md_operand(ctx, llvm_ctx, cctx, operand)?);
631 }
632 cctx.md.in_progress.remove(&id);
633
634 let md = llvm_md_node_in_context2(llvm_ctx, &operands);
635 if let Some(temp) = cctx.md.temporaries.remove(&id) {
636 llvm_metadata_replace_all_uses_with(temp, md);
637 }
638 cctx.md.node_map.insert(id, md);
639
640 Ok(md)
641 }
642
643 fn convert_md_operand(
645 ctx: &Context,
646 llvm_ctx: &LLVMContext,
647 cctx: &mut ConversionContext,
648 operand: &MdOperandAttr,
649 ) -> Result<Option<LLVMMetadata>> {
650 let md = match operand {
651 MdOperandAttr::Null => None,
652 MdOperandAttr::String(s) => Some(llvm_md_string_in_context2(llvm_ctx, s)),
653 MdOperandAttr::Node(id) => Some(convert_md_node(ctx, llvm_ctx, cctx, *id)?),
654 MdOperandAttr::Constant(attr) => {
655 let const_val = attr_cast::<dyn AttrToLLVMConst>(&**attr).ok_or_else(|| {
656 input_error_noloc!(MdToLLVMErr::OperandNotConst(attr.disp(ctx).to_string()))
657 })?;
658 Some(llvm_value_as_metadata(
659 const_val.convert(ctx, llvm_ctx, cctx)?,
660 ))
661 }
662 };
663 Ok(md)
664 }
665
666 pub(crate) fn convert_module_metadata(
669 ctx: &Context,
670 llvm_ctx: &LLVMContext,
671 cctx: &mut ConversionContext,
672 module: ModuleOp,
673 ) -> Result<()> {
674 let Some(table) = get_metadata_table(ctx, module) else {
675 return Ok(());
676 };
677 cctx.md.table = table;
678
679 for id in 0..cctx.md.table.len() as MdNodeId {
680 convert_md_node(ctx, llvm_ctx, cctx, id)?;
681 }
682
683 for (id, node) in cctx.md.table.clone().iter() {
686 if !node.is_distinct() {
687 continue;
688 }
689 let md = cctx.md.node_map[&id];
690 let mut operands = Vec::with_capacity(node.operands().len());
691 for operand in node.operands() {
692 operands.push(convert_md_operand(ctx, llvm_ctx, cctx, operand)?);
693 }
694 if llvm_md_node_in_context2(llvm_ctx, &operands) == md {
695 return input_err_noloc!(MdToLLVMErr::UnrepresentableDistinct(id));
696 }
697 }
698
699 if let Some(named) = get_named_metadata(ctx, module) {
700 for (name, nodes) in named.iter() {
701 for node in nodes {
702 let md = convert_md_node(ctx, llvm_ctx, cctx, *node)?;
703 llvm_add_named_metadata_operand(
704 cctx.cur_llvm_module,
705 name,
706 llvm_metadata_as_value(llvm_ctx, md),
707 );
708 }
709 }
710 }
711
712 Ok(())
713 }
714
715 pub(crate) fn convert_md_attachments(
717 ctx: &Context,
718 llvm_ctx: &LLVMContext,
719 cctx: &mut ConversionContext,
720 op: Ptr<Operation>,
721 op_llvm: LLVMValue,
722 ) -> Result<()> {
723 let Some(attachments) = get_attachments(ctx, op) else {
724 return Ok(());
725 };
726 let is_instruction = llvm_is_a::instruction(op_llvm);
727 for (kind, node) in attachments
728 .iter()
729 .map(|(kind, node)| (kind.to_string(), node))
730 .collect::<Vec<_>>()
731 {
732 let md = convert_md_node(ctx, llvm_ctx, cctx, node)?;
733 let kind_id = llvm_get_md_kind_id_in_context(llvm_ctx, &kind);
734 if is_instruction {
735 llvm_set_metadata(op_llvm, kind_id, llvm_metadata_as_value(llvm_ctx, md));
736 } else if llvm_is_a::global_object(op_llvm) {
737 llvm_global_set_metadata(op_llvm, kind_id, md);
738 } else {
739 log::warn!(
743 "Dropping metadata \"{kind}\" of {}, which did not convert to an instruction",
744 Operation::get_opid(op, ctx)
745 );
746 }
747 }
748 Ok(())
749 }
750
751 #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
753 pub enum DwarfEmissionKind {
754 None,
756 Full,
758 #[default]
760 LineTablesOnly,
761 }
762
763 #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
765 pub enum DwarfSourceLanguage {
766 #[default]
768 C,
769 CPlusPlus,
771 Rust,
773 }
774
775 #[derive(Clone, Debug)]
777 pub struct SourceText<'a> {
778 pub text: &'a str,
780 pub md5: String,
782 }
783
784 #[derive(Debug)]
786 pub struct DebugInfoOptions<'a> {
787 pub emission_kind: DwarfEmissionKind,
789 pub language: DwarfSourceLanguage,
791 pub producer: String,
793 pub optimized: bool,
795 pub directory: String,
797 pub dwarf_version: u32,
799 pub source_text: HMap<String, SourceText<'a>>,
801 }
802
803 impl Default for DebugInfoOptions<'_> {
804 fn default() -> Self {
805 Self {
806 emission_kind: DwarfEmissionKind::default(),
807 language: DwarfSourceLanguage::default(),
808 producer: "pliron".to_string(),
809 optimized: false,
810 directory: String::new(),
811 dwarf_version: 4,
812 source_text: HMap::default(),
813 }
814 }
815 }
816
817 struct DiConversionContext<'a> {
819 options: DebugInfoOptions<'a>,
820 builder: LLVMDIBuilder<'a>,
821 unit: Option<LLVMMetadata>,
823 files: HMap<Source, LLVMMetadata>,
825 block_files: HMap<(LLVMMetadata, LLVMMetadata), LLVMMetadata>,
827 inlined: HMap<LLVMMetadata, HMap<String, LLVMMetadata>>,
829 subprogram: Option<LLVMMetadata>,
831 }
832
833 impl<'a> DiConversionContext<'a> {
834 fn new(module: &'a LLVMModule, options: DebugInfoOptions<'a>) -> Self {
835 Self {
836 options,
837 builder: LLVMDIBuilder::new(module),
838 unit: None,
839 files: HMap::default(),
840 block_files: HMap::default(),
841 inlined: HMap::default(),
842 subprogram: None,
843 }
844 }
845
846 fn file(&mut self, ctx: &Context, src: &Source) -> LLVMMetadata {
847 *self.files.entry(*src).or_insert_with(|| {
848 let name = match src {
849 Source::File(key) => uniqued_any::get(ctx, *key).to_string_lossy().into_owned(),
850 Source::InMemory => "<in-memory>".to_string(),
851 };
852 let directory = &self.options.directory;
853 let Some(source) = self.options.source_text.get(&name) else {
854 return llvm_di_builder_create_file(&self.builder, &name, directory);
855 };
856 llvm_di_builder_create_file_with_checksum(
857 &self.builder,
858 &name,
859 directory,
860 LLVMChecksumKind::CSK_MD5,
861 &source.md5,
862 source.text,
863 )
864 })
865 }
866
867 fn ensure_unit(&mut self, file: LLVMMetadata) {
868 if self.unit.is_some() {
869 return;
870 }
871 self.unit = Some(llvm_di_builder_create_compile_unit(
872 &self.builder,
873 self.options.language.into(),
874 file,
875 &self.options.producer,
876 self.options.optimized,
877 "",
878 0,
879 "",
880 self.options.emission_kind.into(),
881 0,
882 false,
883 false,
884 "",
885 "",
886 ));
887 }
888
889 fn create_subprogram(
890 &mut self,
891 name: &str,
892 linkage_name: &str,
893 file: LLVMMetadata,
894 line: u32,
895 is_local_to_unit: bool,
896 ) -> LLVMMetadata {
897 self.ensure_unit(file);
898 let ty =
899 llvm_di_builder_create_subroutine_type(&self.builder, file, &[], LLVMDIFlagZero);
900 llvm_di_builder_create_function(
901 &self.builder,
902 file,
903 name,
904 linkage_name,
905 file,
906 line,
907 ty,
908 is_local_to_unit,
909 true,
910 line,
911 LLVMDIFlagZero,
912 self.options.optimized,
913 )
914 }
915
916 fn scope_in_file(
918 &mut self,
919 ctx: &Context,
920 scope: LLVMMetadata,
921 src: &Source,
922 ) -> LLVMMetadata {
923 let file = self.file(ctx, src);
924 if llvm_di_scope_get_file(scope) == Some(file) {
925 return scope;
926 }
927 *self.block_files.entry((scope, file)).or_insert_with(|| {
928 llvm_di_builder_create_lexical_block_file(&self.builder, scope, file, 0)
929 })
930 }
931
932 fn inlined_subprogram(&mut self, ctx: &Context, callee: &Location) -> Option<LLVMMetadata> {
934 let name = frame_name(callee)?;
935 let (src, _) = frame_src_pos(callee).unwrap_or((Source::InMemory, 0));
936 let file = self.file(ctx, &src);
937 if let Some(subprogram) = self.inlined.get(&file).and_then(|names| names.get(name)) {
938 return Some(*subprogram);
939 }
940 let subprogram = self.create_subprogram(name, "", file, 0, true);
941 self.inlined
942 .entry(file)
943 .or_default()
944 .insert(name.to_string(), subprogram);
945 Some(subprogram)
946 }
947
948 fn translate(
949 &mut self,
950 ctx: &Context,
951 llvm_ctx: &LLVMContext,
952 loc: &Location,
953 scope: LLVMMetadata,
954 inlined_at: Option<LLVMMetadata>,
955 ) -> Option<LLVMMetadata> {
956 match loc {
957 Location::SrcPos { src, pos } => {
958 let scope = self.scope_in_file(ctx, scope, src);
959 Some(llvm_di_builder_create_debug_location(
960 llvm_ctx,
961 non_negative(pos.line),
962 non_negative(pos.column),
963 scope,
964 inlined_at,
965 ))
966 }
967 Location::Named { child_loc, .. } => {
968 self.translate(ctx, llvm_ctx, child_loc, scope, inlined_at)
969 }
970 Location::CallSite { callee, caller } => {
971 let Some(caller) = self.translate(ctx, llvm_ctx, caller, scope, inlined_at)
972 else {
973 return self.translate(ctx, llvm_ctx, callee, scope, inlined_at);
974 };
975 let Some(callee_scope) = self.inlined_subprogram(ctx, callee) else {
976 return Some(caller);
977 };
978 self.translate(ctx, llvm_ctx, callee, callee_scope, Some(caller))
979 .or(Some(caller))
980 }
981 Location::Fused { locations, .. } => locations
982 .iter()
983 .find_map(|loc| self.translate(ctx, llvm_ctx, loc, scope, inlined_at)),
984 Location::Unknown => None,
985 }
986 }
987 }
988
989 fn non_negative(value: i32) -> u32 {
990 u32::try_from(value).unwrap_or(0)
991 }
992
993 fn frame_name(loc: &Location) -> Option<&str> {
995 match loc {
996 Location::Named { name, .. } => Some(name),
997 Location::Fused { locations, .. } => locations.iter().find_map(frame_name),
998 Location::CallSite { caller, .. } => frame_name(caller),
999 Location::SrcPos { .. } | Location::Unknown => None,
1000 }
1001 }
1002
1003 fn frame_src_pos(loc: &Location) -> Option<(Source, u32)> {
1005 match loc {
1006 Location::SrcPos { src, pos } => Some((*src, non_negative(pos.line))),
1007 Location::Named { child_loc, .. } => frame_src_pos(child_loc),
1008 Location::Fused { locations, .. } => locations.iter().find_map(frame_src_pos),
1009 Location::CallSite { caller, .. } => frame_src_pos(caller),
1010 Location::Unknown => None,
1011 }
1012 }
1013
1014 fn function_location(ctx: &Context, func_op: FuncOp) -> Option<Location> {
1016 let loc = func_op.loc(ctx);
1017 if !loc.is_unknown() {
1018 return Some(loc);
1019 }
1020 let result = walk_op(
1021 ctx,
1022 &mut (),
1023 &WALKCONFIG_PREORDER_FORWARD,
1024 func_op.get_operation(),
1025 |ctx: &Context, _state: &mut (), node: IRNode| -> WalkResult<Location> {
1026 let IRNode::Operation(op) = node else {
1027 return walk_advance();
1028 };
1029 if op.deref(ctx).loc().is_unknown() {
1030 walk_advance()
1031 } else {
1032 walk_break(op.deref(ctx).loc().clone())
1033 }
1034 },
1035 );
1036 match result {
1037 WalkResult::Break(loc) => Some(loc),
1038 WalkResult::Continue(_) => None,
1039 }
1040 }
1041
1042 pub(crate) fn begin_function_debug_info(
1044 ctx: &Context,
1045 cctx: &mut ConversionContext,
1046 func_op: FuncOp,
1047 func_llvm: LLVMValue,
1048 ) {
1049 let Some(di) = cctx.md.di.as_mut() else {
1050 return;
1051 };
1052 di.subprogram = None;
1053 let Some(loc) = function_location(ctx, func_op) else {
1054 return;
1055 };
1056 let llvm_name = func_op
1057 .llvm_symbol_name(ctx)
1058 .unwrap_or_else(|| func_op.get_symbol_name(ctx).to_string());
1059 let name = frame_name(&loc).unwrap_or(&llvm_name);
1060 let linkage_name = if name == llvm_name { "" } else { &llvm_name };
1061 let (src, line) = frame_src_pos(&loc).unwrap_or((Source::InMemory, 0));
1062 let file = di.file(ctx, &src);
1063 let subprogram = di.create_subprogram(name, linkage_name, file, line, false);
1064 llvm_set_subprogram(func_llvm, subprogram);
1065 di.subprogram = Some(subprogram);
1066 }
1067
1068 pub(crate) fn set_debug_location(
1071 ctx: &Context,
1072 llvm_ctx: &LLVMContext,
1073 cctx: &mut ConversionContext,
1074 op: Ptr<Operation>,
1075 ) {
1076 let Some(di) = cctx.md.di.as_mut() else {
1077 return;
1078 };
1079 let op_ref = op.deref(ctx);
1080 let di_loc = di.subprogram.map(|subprogram| {
1081 di.translate(ctx, llvm_ctx, op_ref.loc(), subprogram, None)
1082 .unwrap_or_else(|| {
1083 llvm_di_builder_create_debug_location(llvm_ctx, 0, 0, subprogram, None)
1084 })
1085 });
1086 llvm_set_current_debug_location2(&cctx.builder, di_loc);
1087 }
1088
1089 pub(crate) fn finish_debug_info(llvm_ctx: &LLVMContext, cctx: &mut ConversionContext) {
1091 let Some(di) = cctx.md.di.take() else {
1092 return;
1093 };
1094 if di.unit.is_none() {
1095 return;
1096 }
1097 let int32 = llvm_int_type_in_context(llvm_ctx, 32);
1098 for (key, value) in [
1099 ("Debug Info Version", llvm_debug_metadata_version()),
1100 ("Dwarf Version", di.options.dwarf_version),
1101 ] {
1102 if llvm_get_module_flag(cctx.cur_llvm_module, key).is_none() {
1103 let value = llvm_value_as_metadata(llvm_const_int(int32, value.into(), false));
1104 llvm_add_module_flag(
1105 cctx.cur_llvm_module,
1106 LLVMModuleFlagBehavior::LLVMModuleFlagBehaviorWarning,
1107 key,
1108 value,
1109 );
1110 }
1111 }
1112 }
1113}