1pub mod from_llvm_ir {
8 use alloc::{
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 context::{Context, Ptr},
18 input_error_noloc,
19 operation::Operation,
20 result::Result,
21 utils::table::{HMap, HSet},
22 };
23 use thiserror::Error;
24
25 use crate::{
26 from_llvm_ir::{ConversionContext, const_llvm_value_to_attr},
27 llvm_sys::core::{
28 LLVMMetadata, LLVMModule, LLVMValue, llvm_get_md_kind_id_in_module,
29 llvm_get_md_node_operands, llvm_get_md_string, llvm_get_metadata_kind,
30 llvm_get_named_metadata_operands, llvm_global_copy_all_metadata,
31 llvm_instruction_get_all_metadata_other_than_debug_loc, llvm_md_node_in_module,
32 llvm_metadata_as_value_in_module, llvm_named_metadata_names,
33 llvm_print_module_to_string, llvm_print_value_to_string, llvm_value_as_metadata,
34 },
35 metadata::{
36 MdAttachmentsAttr, MdNodeAttr, MdNodeId, MdOperandAttr, MdTableAttr, NamedMdAttr,
37 set_attachments, set_metadata_table, set_named_metadata,
38 },
39 };
40
41 #[derive(Default)]
43 pub(crate) struct MdConversionContext {
44 node_map: HMap<LLVMMetadata, Option<MdNodeId>>,
47 table: MdTableAttr,
49 kind_names: HMap<u32, String>,
51 kind_names_scraped: bool,
53 }
54
55 #[derive(Error, Debug)]
57 pub enum MdConversionErr {
58 #[error("Cannot determine the name of metadata kind id {0}")]
59 UnknownKind(u32),
60 }
61
62 pub(crate) const MD_KIND_DBG: &str = "dbg";
64
65 const FIXED_MD_KIND_NAMES: &[&str] = &[
68 "dbg",
69 "tbaa",
70 "prof",
71 "fpmath",
72 "range",
73 "tbaa.struct",
74 "invariant.load",
75 "alias.scope",
76 "noalias",
77 "nontemporal",
78 "llvm.mem.parallel_loop_access",
79 "nonnull",
80 "dereferenceable",
81 "dereferenceable_or_null",
82 "make.implicit",
83 "unpredictable",
84 "invariant.group",
85 "align",
86 "llvm.loop",
87 "type",
88 "section_prefix",
89 "absolute_symbol",
90 "associated",
91 "callees",
92 "irr_loop",
93 "llvm.access.group",
94 "callback",
95 "llvm.preserve.access.index",
96 "vcall_visibility",
97 "noundef",
98 "annotation",
99 "nosanitize",
100 "func_sanitize",
101 "exclude",
102 "memprof",
103 "callsite",
104 "kcfi_type",
105 "pcsections",
106 "DIAssignID",
107 "coro.outside.frame",
108 "mmra",
109 "noalias.addrspace",
110 "callee_type",
111 "nofree",
112 "captures",
113 "alloc_token",
114 "implicit.ref",
115 ];
116
117 fn scrape_md_kind_names(module_text: &str) -> Vec<String> {
127 fn is_name_start(c: u8) -> bool {
128 c.is_ascii_alphabetic() || matches!(c, b'-' | b'$' | b'.' | b'_')
129 }
130 fn is_name_char(c: u8) -> bool {
131 c.is_ascii_alphanumeric() || matches!(c, b'-' | b'$' | b'.' | b'_')
132 }
133 fn hex_digit(c: Option<&u8>) -> Option<u8> {
134 c.and_then(|c| (*c as char).to_digit(16)).map(|d| d as u8)
135 }
136
137 let text = module_text.as_bytes();
138 let mut seen = HSet::default();
139 let mut names = vec![];
140 let mut idx = 0;
141 while idx < text.len() {
142 if text[idx] != b'!' {
143 idx += 1;
144 continue;
145 }
146 idx += 1;
147 let mut name = vec![];
150 while idx < text.len() {
151 let c = text[idx];
152 if is_name_char(c) && (!name.is_empty() || is_name_start(c)) {
153 name.push(c);
154 idx += 1;
155 } else if c == b'\\'
156 && let (Some(hi), Some(lo)) =
157 (hex_digit(text.get(idx + 1)), hex_digit(text.get(idx + 2)))
158 {
159 name.push((hi << 4) | lo);
160 idx += 3;
161 } else {
162 break;
163 }
164 }
165 if let Ok(name) = String::from_utf8(name)
167 && !name.is_empty()
168 && seen.insert(name.clone())
169 {
170 names.push(name);
171 }
172 }
173 names
174 }
175
176 pub(crate) fn md_kind_name(
178 cctx: &mut ConversionContext,
179 module: &LLVMModule,
180 kind_id: u32,
181 ) -> Result<String> {
182 if cctx.md.kind_names.is_empty() {
183 for name in FIXED_MD_KIND_NAMES {
184 let id = llvm_get_md_kind_id_in_module(module, name);
185 cctx.md
186 .kind_names
187 .entry(id)
188 .or_insert_with(|| name.to_string());
189 }
190 }
191
192 if let Some(name) = cctx.md.kind_names.get(&kind_id) {
193 return Ok(name.clone());
194 }
195
196 if !cctx.md.kind_names_scraped {
198 cctx.md.kind_names_scraped = true;
199 let module_text = llvm_print_module_to_string(module)
200 .ok_or_else(|| input_error_noloc!(MdConversionErr::UnknownKind(kind_id)))?;
201 for name in scrape_md_kind_names(&module_text) {
202 let id = llvm_get_md_kind_id_in_module(module, &name);
203 cctx.md.kind_names.entry(id).or_insert(name);
204 }
205 }
206
207 cctx.md
208 .kind_names
209 .get(&kind_id)
210 .cloned()
211 .ok_or_else(|| input_error_noloc!(MdConversionErr::UnknownKind(kind_id)))
212 }
213
214 fn convert_md_node(
217 ctx: &Context,
218 cctx: &mut ConversionContext,
219 module: &LLVMModule,
220 md: LLVMMetadata,
221 ) -> Result<Option<MdNodeId>> {
222 if let Some(id) = cctx.md.node_map.get(&md) {
223 return Ok(*id);
224 }
225
226 let kind = llvm_get_metadata_kind(md);
228 if !matches!(kind, LLVMMetadataKind::LLVMMDTupleMetadataKind) {
229 log::warn!("Dropping unsupported metadata of kind {kind:?}");
230 cctx.md.node_map.insert(md, None);
231 return Ok(None);
232 }
233
234 let id = cctx.md.table.reserve();
237 cctx.md.node_map.insert(md, Some(id));
238
239 let md_val = llvm_metadata_as_value_in_module(module, md);
240 let llvm_operands = llvm_get_md_node_operands(md_val);
241 let mut operands = Vec::with_capacity(llvm_operands.len());
242 for operand in &llvm_operands {
243 let Some(operand) = convert_md_operand(ctx, cctx, module, *operand)? else {
246 let printed = llvm_print_value_to_string(md_val).unwrap_or_default();
249 let printed = printed.split_once(" = ").map_or(&*printed, |(_, def)| def);
250 log::warn!("Dropping metadata node {printed} with an operand we cannot represent");
251 cctx.md.node_map.insert(md, None);
252 return Ok(None);
254 };
255 operands.push(operand);
256 }
257
258 let llvm_md_operands: Vec<_> = llvm_operands
262 .iter()
263 .map(|operand| operand.map(llvm_value_as_metadata))
264 .collect();
265 let distinct = llvm_md_node_in_module(module, &llvm_md_operands) != md;
266
267 cctx.md.table.set(
268 id,
269 if distinct {
270 MdNodeAttr::new_distinct_tuple(operands)
271 } else {
272 MdNodeAttr::new_tuple(operands)
273 },
274 );
275
276 Ok(Some(id))
277 }
278
279 fn convert_md_operand(
282 ctx: &Context,
283 cctx: &mut ConversionContext,
284 module: &LLVMModule,
285 operand: Option<LLVMValue>,
286 ) -> Result<Option<MdOperandAttr>> {
287 let Some(val) = operand else {
288 return Ok(Some(MdOperandAttr::Null));
289 };
290
291 let md = llvm_value_as_metadata(val);
294 match llvm_get_metadata_kind(md) {
295 LLVMMetadataKind::LLVMMDStringMetadataKind => match llvm_get_md_string(val) {
296 Some(s) => Ok(Some(MdOperandAttr::String(s))),
297 None => {
298 log::warn!("Dropping metadata string operand whose contents aren't UTF-8");
299 Ok(None)
300 }
301 },
302 LLVMMetadataKind::LLVMMDTupleMetadataKind => {
303 Ok(convert_md_node(ctx, cctx, module, md)?.map(MdOperandAttr::Node))
304 }
305 LLVMMetadataKind::LLVMConstantAsMetadataMetadataKind => {
306 match const_llvm_value_to_attr(ctx, cctx, val)? {
307 Some(attr) => Ok(Some(MdOperandAttr::Constant(attr))),
308 None => {
309 log::warn!(
310 "Dropping unsupported constant metadata operand {}",
311 llvm_print_value_to_string(val).unwrap_or_default()
312 );
313 Ok(None)
314 }
315 }
316 }
317 kind => {
318 log::warn!("Dropping unsupported metadata operand of kind {kind:?}");
319 Ok(None)
320 }
321 }
322 }
323
324 fn convert_md_attachments(
326 ctx: &Context,
327 cctx: &mut ConversionContext,
328 module: &LLVMModule,
329 entries: Vec<(u32, LLVMMetadata)>,
330 m_op: Ptr<Operation>,
331 ) -> Result<()> {
332 if entries.is_empty() {
333 return Ok(());
334 }
335 let mut attachments = MdAttachmentsAttr::new();
336 for (kind_id, md) in entries {
337 let kind = md_kind_name(cctx, module, kind_id)?;
338 if kind == MD_KIND_DBG {
339 continue;
341 }
342 let Some(node) = convert_md_node(ctx, cctx, module, md)? else {
343 log::warn!("Dropping metadata attached under kind \"{kind}\"");
344 continue;
345 };
346 attachments.set(kind, node);
347 }
348 if !attachments.is_empty() {
349 set_attachments(ctx, m_op, attachments);
350 }
351 Ok(())
352 }
353
354 pub(crate) fn convert_instruction_metadata(
356 ctx: &Context,
357 cctx: &mut ConversionContext,
358 module: &LLVMModule,
359 inst: LLVMValue,
360 m_inst: Ptr<Operation>,
361 ) -> Result<()> {
362 let entries = llvm_instruction_get_all_metadata_other_than_debug_loc(inst);
363 convert_md_attachments(ctx, cctx, module, entries, m_inst)
364 }
365
366 pub(crate) fn convert_global_object_metadata(
368 ctx: &Context,
369 cctx: &mut ConversionContext,
370 module: &LLVMModule,
371 global: LLVMValue,
372 m_op: Ptr<Operation>,
373 ) -> Result<()> {
374 let entries = llvm_global_copy_all_metadata(global);
375 convert_md_attachments(ctx, cctx, module, entries, m_op)
376 }
377
378 fn convert_named_metadata(
380 ctx: &Context,
381 cctx: &mut ConversionContext,
382 module: &LLVMModule,
383 ) -> Result<NamedMdAttr> {
384 let mut named = NamedMdAttr::new();
385 for name in llvm_named_metadata_names(module) {
386 for operand in llvm_get_named_metadata_operands(module, &name) {
387 let md = llvm_value_as_metadata(operand);
388 let Some(node) = convert_md_node(ctx, cctx, module, md)? else {
389 log::warn!("Dropping an operand of named metadata \"{name}\"");
390 continue;
391 };
392 named.push(name.clone(), node);
393 }
394 }
395 Ok(named)
396 }
397
398 pub(crate) fn convert_module_metadata(
403 ctx: &Context,
404 cctx: &mut ConversionContext,
405 module: &LLVMModule,
406 module_op: ModuleOp,
407 ) -> Result<()> {
408 let named_md = convert_named_metadata(ctx, cctx, module)?;
409 if !named_md.is_empty() {
410 set_named_metadata(ctx, module_op, named_md);
411 }
412 if !cctx.md.table.is_empty() {
413 set_metadata_table(ctx, module_op, cctx.md.table.clone());
414 }
415 Ok(())
416 }
417}
418
419pub mod to_llvm_ir {
421 use alloc::{string::ToString, vec::Vec};
422
423 use pliron::{
424 attribute::attr_cast,
425 builtin::ops::ModuleOp,
426 context::{Context, Ptr},
427 input_err_noloc, input_error_noloc,
428 operation::Operation,
429 printable::Printable,
430 result::Result,
431 utils::table::{HMap, HSet},
432 };
433 use thiserror::Error;
434
435 use crate::{
436 llvm_sys::core::{
437 LLVMContext, LLVMMetadata, LLVMValue, llvm_add_named_metadata_operand,
438 llvm_get_md_kind_id_in_context, llvm_global_set_metadata, llvm_is_a,
439 llvm_md_node_in_context2, llvm_md_string_in_context2, llvm_metadata_as_value,
440 llvm_metadata_replace_all_uses_with, llvm_set_metadata, llvm_temporary_md_node,
441 llvm_value_as_metadata,
442 },
443 metadata::{
444 MdNodeId, MdOperandAttr, MdTableAttr, get_attachments, get_metadata_table,
445 get_named_metadata,
446 },
447 to_llvm_ir::{AttrToLLVMConst, ConversionContext},
448 };
449
450 #[derive(Default)]
452 pub(crate) struct MdConversionContext {
453 table: MdTableAttr,
455 node_map: HMap<MdNodeId, LLVMMetadata>,
457 temporaries: HMap<MdNodeId, LLVMMetadata>,
459 in_progress: HSet<MdNodeId>,
461 }
462
463 #[derive(Error, Debug)]
465 pub enum MdToLLVMErr {
466 #[error("Metadata node #{0} is not in the module's metadata table")]
467 DanglingNodeRef(MdNodeId),
468 #[error("Metadata operand {0} is not convertible to an LLVM constant")]
469 OperandNotConst(String),
470 #[error(
471 "Metadata node #{0} is `distinct`, but the LLVM C-API can only create a distinct \
472 node that refers to itself"
473 )]
474 UnrepresentableDistinct(MdNodeId),
475 }
476
477 fn convert_md_node(
480 ctx: &Context,
481 llvm_ctx: &LLVMContext,
482 cctx: &mut ConversionContext,
483 id: MdNodeId,
484 ) -> Result<LLVMMetadata> {
485 if let Some(md) = cctx.md.node_map.get(&id) {
486 return Ok(*md);
487 }
488 if cctx.md.in_progress.contains(&id) {
492 let temp = *cctx
495 .md
496 .temporaries
497 .entry(id)
498 .or_insert_with(|| llvm_temporary_md_node(llvm_ctx, &[]));
499 return Ok(temp);
500 }
501
502 let node = cctx
503 .md
504 .table
505 .get(id)
506 .cloned()
507 .ok_or_else(|| input_error_noloc!(MdToLLVMErr::DanglingNodeRef(id)))?;
508
509 cctx.md.in_progress.insert(id);
510 let mut operands = Vec::with_capacity(node.operands().len());
511 for operand in node.operands() {
512 operands.push(convert_md_operand(ctx, llvm_ctx, cctx, operand)?);
513 }
514 cctx.md.in_progress.remove(&id);
515
516 let md = llvm_md_node_in_context2(llvm_ctx, &operands);
517 if let Some(temp) = cctx.md.temporaries.remove(&id) {
518 llvm_metadata_replace_all_uses_with(temp, md);
519 }
520 cctx.md.node_map.insert(id, md);
521
522 Ok(md)
523 }
524
525 fn convert_md_operand(
527 ctx: &Context,
528 llvm_ctx: &LLVMContext,
529 cctx: &mut ConversionContext,
530 operand: &MdOperandAttr,
531 ) -> Result<Option<LLVMMetadata>> {
532 let md = match operand {
533 MdOperandAttr::Null => None,
534 MdOperandAttr::String(s) => Some(llvm_md_string_in_context2(llvm_ctx, s)),
535 MdOperandAttr::Node(id) => Some(convert_md_node(ctx, llvm_ctx, cctx, *id)?),
536 MdOperandAttr::Constant(attr) => {
537 let const_val = attr_cast::<dyn AttrToLLVMConst>(&**attr).ok_or_else(|| {
538 input_error_noloc!(MdToLLVMErr::OperandNotConst(attr.disp(ctx).to_string()))
539 })?;
540 Some(llvm_value_as_metadata(
541 const_val.convert(ctx, llvm_ctx, cctx)?,
542 ))
543 }
544 };
545 Ok(md)
546 }
547
548 pub(crate) fn convert_module_metadata(
551 ctx: &Context,
552 llvm_ctx: &LLVMContext,
553 cctx: &mut ConversionContext,
554 module: ModuleOp,
555 ) -> Result<()> {
556 let Some(table) = get_metadata_table(ctx, module) else {
557 return Ok(());
558 };
559 cctx.md.table = table;
560
561 for id in 0..cctx.md.table.len() as MdNodeId {
562 convert_md_node(ctx, llvm_ctx, cctx, id)?;
563 }
564
565 for (id, node) in cctx.md.table.clone().iter() {
568 if !node.is_distinct() {
569 continue;
570 }
571 let md = cctx.md.node_map[&id];
572 let mut operands = Vec::with_capacity(node.operands().len());
573 for operand in node.operands() {
574 operands.push(convert_md_operand(ctx, llvm_ctx, cctx, operand)?);
575 }
576 if llvm_md_node_in_context2(llvm_ctx, &operands) == md {
577 return input_err_noloc!(MdToLLVMErr::UnrepresentableDistinct(id));
578 }
579 }
580
581 if let Some(named) = get_named_metadata(ctx, module) {
582 for (name, nodes) in named.iter() {
583 for node in nodes {
584 let md = convert_md_node(ctx, llvm_ctx, cctx, *node)?;
585 llvm_add_named_metadata_operand(
586 cctx.cur_llvm_module,
587 name,
588 llvm_metadata_as_value(llvm_ctx, md),
589 );
590 }
591 }
592 }
593
594 Ok(())
595 }
596
597 pub(crate) fn convert_md_attachments(
599 ctx: &Context,
600 llvm_ctx: &LLVMContext,
601 cctx: &mut ConversionContext,
602 op: Ptr<Operation>,
603 op_llvm: LLVMValue,
604 ) -> Result<()> {
605 let Some(attachments) = get_attachments(ctx, op) else {
606 return Ok(());
607 };
608 let is_instruction = llvm_is_a::instruction(op_llvm);
609 for (kind, node) in attachments
610 .iter()
611 .map(|(kind, node)| (kind.to_string(), node))
612 .collect::<Vec<_>>()
613 {
614 let md = convert_md_node(ctx, llvm_ctx, cctx, node)?;
615 let kind_id = llvm_get_md_kind_id_in_context(llvm_ctx, &kind);
616 if is_instruction {
617 llvm_set_metadata(op_llvm, kind_id, llvm_metadata_as_value(llvm_ctx, md));
618 } else if llvm_is_a::global_object(op_llvm) {
619 llvm_global_set_metadata(op_llvm, kind_id, md);
620 } else {
621 log::warn!(
625 "Dropping metadata \"{kind}\" of {}, which did not convert to an instruction",
626 Operation::get_opid(op, ctx)
627 );
628 }
629 }
630 Ok(())
631 }
632}