1use crate::attributes::{AggregateAttr, SplatAttr, SymbolAddrAttr};
42use alloc::{
43 boxed::Box,
44 string::{String, ToString},
45 vec::Vec,
46};
47use pliron::{
48 arg_err,
49 attribute::{Attribute, verify_attr},
50 builtin::{
51 attr_interfaces::{OutlinedAttr, TypedAttrInterface},
52 ops::ModuleOp,
53 },
54 combine::{Parser, attempt, between, choice, not_followed_by, parser::char::spaces, token},
55 context::{Context, Ptr},
56 derive::{attr_interface_impl, pliron_attr},
57 dict_key,
58 graph::walkers::{
59 IRNode, WALKCONFIG_PREORDER_FORWARD,
60 interruptible::{WalkResult, immutable::walk_op, walk_advance, walk_break},
61 },
62 indented_block,
63 irfmt::{
64 parsers::{delimited_list_parser, list_parser},
65 printers::iter_with_sep,
66 },
67 location::{Located, Location},
68 op::Op,
69 operation::Operation,
70 parsable::{IntoParseResult, Parsable, ParseResult, StateStream},
71 printable::{self, ListSeparator, Printable, indented_nl},
72 result::{Error, Result},
73 symbol_table::SymbolTableCollection,
74 utils::vec_exns::VecExtns,
75 verify_err, verify_error,
76};
77use thiserror::Error;
78
79pub type MdNodeId = u32;
81
82fn write_md_quoted(f: &mut core::fmt::Formatter<'_>, s: &str) -> core::fmt::Result {
87 write!(f, "\"")?;
88 for c in s.chars() {
89 match c {
90 '\\' | '"' => write!(f, "\\{c}")?,
91 _ => write!(f, "{c}")?,
92 }
93 }
94 write!(f, "\"")
95}
96
97dict_key!(
98 ATTR_KEY_MD_TABLE, "llvm_metadata_defs"
100);
101
102dict_key!(
103 ATTR_KEY_NAMED_MD, "llvm_named_metadata"
105);
106
107dict_key!(
108 ATTR_KEY_MD_ATTACHMENTS, "llvm_metadata"
110);
111
112#[derive(PartialEq, Eq, Clone, Debug, Hash)]
116pub enum MdOperandAttr {
117 Null,
119 String(String),
121 Node(MdNodeId),
123 Constant(Box<dyn TypedAttrInterface>),
125}
126
127impl Printable for MdOperandAttr {
128 fn fmt(
129 &self,
130 ctx: &Context,
131 state: &printable::State,
132 f: &mut core::fmt::Formatter<'_>,
133 ) -> core::fmt::Result {
134 match self {
135 MdOperandAttr::Null => write!(f, "null"),
136 MdOperandAttr::String(s) => {
137 write!(f, "!")?;
138 write_md_quoted(f, s)
139 }
140 MdOperandAttr::Node(id) => write!(f, "#{id}"),
141 MdOperandAttr::Constant(attr) => attr.fmt(ctx, state, f),
142 }
143 }
144}
145
146impl Parsable for MdOperandAttr {
147 type Arg = ();
148 type Parsed = Self;
149
150 fn parse<'a>(
151 state_stream: &mut StateStream<'a>,
152 _arg: Self::Arg,
153 ) -> ParseResult<'a, Self::Parsed> {
154 choice((
155 attempt(
156 pliron::combine::parser::char::string("null")
157 .skip(not_followed_by(pliron::combine::parser::char::alpha_num())),
158 )
159 .map(|_| MdOperandAttr::Null),
160 token('!')
161 .with(String::parser(()))
162 .map(MdOperandAttr::String),
163 token('#')
164 .with(MdNodeId::parser(()))
165 .map(MdOperandAttr::Node),
166 <Box<dyn TypedAttrInterface>>::parser(()).map(MdOperandAttr::Constant),
167 ))
168 .parse_stream(state_stream)
169 .into()
170 }
171}
172
173#[derive(PartialEq, Eq, Clone, Debug, Hash)]
179pub enum MdNodeAttr {
180 Tuple {
183 distinct: bool,
184 operands: Vec<MdOperandAttr>,
185 },
186}
187
188impl MdNodeAttr {
189 pub fn new_tuple(operands: Vec<MdOperandAttr>) -> Self {
191 MdNodeAttr::Tuple {
192 distinct: false,
193 operands,
194 }
195 }
196
197 pub fn new_distinct_tuple(operands: Vec<MdOperandAttr>) -> Self {
199 MdNodeAttr::Tuple {
200 distinct: true,
201 operands,
202 }
203 }
204
205 pub fn is_distinct(&self) -> bool {
207 let MdNodeAttr::Tuple { distinct, .. } = self;
208 *distinct
209 }
210
211 pub fn operands(&self) -> &[MdOperandAttr] {
213 let MdNodeAttr::Tuple { operands, .. } = self;
214 operands
215 }
216}
217
218impl Printable for MdNodeAttr {
219 fn fmt(
220 &self,
221 ctx: &Context,
222 state: &printable::State,
223 f: &mut core::fmt::Formatter<'_>,
224 ) -> core::fmt::Result {
225 let MdNodeAttr::Tuple { distinct, operands } = self;
226 if *distinct {
227 write!(f, "distinct ")?;
228 }
229 write!(f, "!{{")?;
230 iter_with_sep(operands.iter(), ListSeparator::CharSpace(',')).fmt(ctx, state, f)?;
231 write!(f, "}}")
232 }
233}
234
235impl Parsable for MdNodeAttr {
236 type Arg = ();
237 type Parsed = Self;
238
239 fn parse<'a>(
240 state_stream: &mut StateStream<'a>,
241 _arg: Self::Arg,
242 ) -> ParseResult<'a, Self::Parsed> {
243 pliron::combine::optional(attempt(
244 pliron::combine::parser::char::string("distinct").skip(spaces()),
245 ))
246 .and(token('!').with(between(
247 token('{').skip(spaces()),
248 spaces().with(token('}')),
249 list_parser(',', MdOperandAttr::parser(())),
250 )))
251 .map(|(distinct, operands)| MdNodeAttr::Tuple {
252 distinct: distinct.is_some(),
253 operands,
254 })
255 .parse_stream(state_stream)
256 .into()
257 }
258}
259
260#[pliron_attr(name = "llvm.md_table", verifier = "succ")]
265#[derive(PartialEq, Eq, Clone, Debug, Hash, Default)]
266pub struct MdTableAttr(Vec<MdNodeAttr>);
267
268#[attr_interface_impl]
269impl OutlinedAttr for MdTableAttr {}
270
271impl MdTableAttr {
272 pub fn new() -> Self {
274 Self::default()
275 }
276
277 pub fn push(&mut self, node: MdNodeAttr) -> MdNodeId {
279 self.0.push_back(node) as MdNodeId
280 }
281
282 pub fn push_uniqued(&mut self, node: MdNodeAttr) -> MdNodeId {
285 if !node.is_distinct()
286 && let Some((id, _)) = self.iter().find(|(_, existing)| **existing == node)
287 {
288 return id;
289 }
290 self.push(node)
291 }
292
293 pub fn reserve(&mut self) -> MdNodeId {
297 self.push(MdNodeAttr::new_tuple(Vec::new()))
298 }
299
300 pub fn set(&mut self, id: MdNodeId, node: MdNodeAttr) {
302 self.0[id as usize] = node;
303 }
304
305 pub fn get(&self, id: MdNodeId) -> Option<&MdNodeAttr> {
307 self.0.get(id as usize)
308 }
309
310 pub fn len(&self) -> usize {
312 self.0.len()
313 }
314
315 pub fn is_empty(&self) -> bool {
317 self.0.is_empty()
318 }
319
320 pub fn iter(&self) -> impl Iterator<Item = (MdNodeId, &MdNodeAttr)> {
322 self.0
323 .iter()
324 .enumerate()
325 .map(|(idx, node)| (idx as MdNodeId, node))
326 }
327}
328
329impl Printable for MdTableAttr {
330 fn fmt(
331 &self,
332 ctx: &Context,
333 state: &printable::State,
334 f: &mut core::fmt::Formatter<'_>,
335 ) -> core::fmt::Result {
336 write!(f, "[")?;
338 indented_block!(state, {
339 for (id, node) in self.iter() {
340 if id != 0 {
341 write!(f, ",")?;
342 }
343 write!(f, "{}#{id} = ", indented_nl(state))?;
344 node.fmt(ctx, state, f)?;
345 }
346 });
347 if !self.is_empty() {
348 write!(f, "{}", indented_nl(state))?;
349 }
350 write!(f, "]")
351 }
352}
353
354#[derive(Debug, Error)]
355#[error("Metadata table entry {0} is out of order; entries must be #0, #1, ... in order")]
356pub struct MdTableParseErr(MdNodeId);
357
358impl Parsable for MdTableAttr {
359 type Arg = ();
360 type Parsed = Self;
361
362 fn parse<'a>(
363 state_stream: &mut StateStream<'a>,
364 _arg: Self::Arg,
365 ) -> ParseResult<'a, Self::Parsed> {
366 let loc = state_stream.loc();
367 let entry = token('#')
368 .with(MdNodeId::parser(()))
369 .skip(spaces())
370 .skip(token('='))
371 .skip(spaces())
372 .and(MdNodeAttr::parser(()));
373
374 let (entries, _) = delimited_list_parser('[', ']', ',', entry)
375 .parse_stream(state_stream)
376 .into_result()?;
377
378 let mut table = MdTableAttr::new();
379 for (id, node) in entries {
380 if id as usize != table.len() {
381 return Err(pliron::input_error!(loc, MdTableParseErr(id))).into_parse_result();
382 }
383 table.push(node);
384 }
385 Ok(table).into_parse_result()
386 }
387}
388
389#[pliron_attr(name = "llvm.md_attachments", verifier = "succ")]
395#[derive(PartialEq, Eq, Clone, Debug, Hash, Default)]
396pub struct MdAttachmentsAttr(Vec<(String, MdNodeId)>);
397
398#[attr_interface_impl]
399impl OutlinedAttr for MdAttachmentsAttr {}
400
401impl MdAttachmentsAttr {
402 pub fn new() -> Self {
404 Self::default()
405 }
406
407 pub fn get(&self, kind: &str) -> Option<MdNodeId> {
409 self.0
410 .iter()
411 .find(|(k, _)| k == kind)
412 .map(|(_, node)| *node)
413 }
414
415 pub fn set(&mut self, kind: impl Into<String>, node: MdNodeId) {
418 let kind = kind.into();
419 match self.0.iter_mut().find(|(k, _)| *k == kind) {
420 Some(entry) => entry.1 = node,
421 None => self.0.push((kind, node)),
422 }
423 }
424
425 pub fn remove(&mut self, kind: &str) {
427 self.0.retain(|(k, _)| k != kind);
428 }
429
430 pub fn is_empty(&self) -> bool {
432 self.0.is_empty()
433 }
434
435 pub fn iter(&self) -> impl Iterator<Item = (&str, MdNodeId)> {
437 self.0.iter().map(|(kind, node)| (kind.as_str(), *node))
438 }
439}
440
441impl Printable for MdAttachmentsAttr {
442 fn fmt(
443 &self,
444 _ctx: &Context,
445 _state: &printable::State,
446 f: &mut core::fmt::Formatter<'_>,
447 ) -> core::fmt::Result {
448 write!(f, "[")?;
449 for (idx, (kind, node)) in self.0.iter().enumerate() {
450 if idx != 0 {
451 write!(f, ", ")?;
452 }
453 write_md_quoted(f, kind)?;
454 write!(f, " = #{node}")?;
455 }
456 write!(f, "]")
457 }
458}
459
460impl Parsable for MdAttachmentsAttr {
461 type Arg = ();
462 type Parsed = Self;
463
464 fn parse<'a>(
465 state_stream: &mut StateStream<'a>,
466 _arg: Self::Arg,
467 ) -> ParseResult<'a, Self::Parsed> {
468 let entry = String::parser(())
469 .skip(spaces())
470 .skip(token('='))
471 .skip(spaces())
472 .and(token('#').with(MdNodeId::parser(())));
473
474 delimited_list_parser('[', ']', ',', entry)
475 .map(MdAttachmentsAttr)
476 .parse_stream(state_stream)
477 .into()
478 }
479}
480
481#[pliron_attr(name = "llvm.named_md", verifier = "succ")]
486#[derive(PartialEq, Eq, Clone, Debug, Hash, Default)]
487pub struct NamedMdAttr(Vec<(String, Vec<MdNodeId>)>);
488
489#[attr_interface_impl]
490impl OutlinedAttr for NamedMdAttr {}
491
492impl NamedMdAttr {
493 pub fn new() -> Self {
495 Self::default()
496 }
497
498 pub fn get(&self, name: &str) -> Option<&[MdNodeId]> {
500 self.0
501 .iter()
502 .find(|(n, _)| n == name)
503 .map(|(_, nodes)| nodes.as_slice())
504 }
505
506 pub fn push(&mut self, name: impl Into<String>, node: MdNodeId) {
508 let name = name.into();
509 match self.0.iter_mut().find(|(n, _)| *n == name) {
510 Some(entry) => entry.1.push(node),
511 None => self.0.push((name, alloc::vec![node])),
512 }
513 }
514
515 pub fn is_empty(&self) -> bool {
517 self.0.is_empty()
518 }
519
520 pub fn iter(&self) -> impl Iterator<Item = (&str, &[MdNodeId])> {
522 self.0
523 .iter()
524 .map(|(name, nodes)| (name.as_str(), nodes.as_slice()))
525 }
526}
527
528impl Printable for NamedMdAttr {
529 fn fmt(
530 &self,
531 _ctx: &Context,
532 _state: &printable::State,
533 f: &mut core::fmt::Formatter<'_>,
534 ) -> core::fmt::Result {
535 write!(f, "[")?;
536 for (idx, (name, nodes)) in self.0.iter().enumerate() {
537 if idx != 0 {
538 write!(f, ", ")?;
539 }
540 write_md_quoted(f, name)?;
541 write!(f, " = [")?;
542 for (idx, node) in nodes.iter().enumerate() {
543 if idx != 0 {
544 write!(f, ", ")?;
545 }
546 write!(f, "#{node}")?;
547 }
548 write!(f, "]")?;
549 }
550 write!(f, "]")
551 }
552}
553
554impl Parsable for NamedMdAttr {
555 type Arg = ();
556 type Parsed = Self;
557
558 fn parse<'a>(
559 state_stream: &mut StateStream<'a>,
560 _arg: Self::Arg,
561 ) -> ParseResult<'a, Self::Parsed> {
562 let nodes = delimited_list_parser('[', ']', ',', token('#').with(MdNodeId::parser(())));
563 let entry = String::parser(())
564 .skip(spaces())
565 .skip(token('='))
566 .skip(spaces())
567 .and(nodes);
568
569 delimited_list_parser('[', ']', ',', entry)
570 .map(NamedMdAttr)
571 .parse_stream(state_stream)
572 .into()
573 }
574}
575
576pub fn get_metadata_table(ctx: &Context, module_op: ModuleOp) -> Option<MdTableAttr> {
578 module_op
579 .get_operation()
580 .deref(ctx)
581 .attributes
582 .get::<MdTableAttr>(&ATTR_KEY_MD_TABLE)
583 .cloned()
584}
585
586pub fn set_metadata_table(ctx: &Context, module_op: ModuleOp, table: MdTableAttr) {
588 module_op
589 .get_operation()
590 .deref_mut(ctx)
591 .attributes
592 .set(ATTR_KEY_MD_TABLE.clone(), table);
593}
594
595pub fn get_named_metadata(ctx: &Context, module_op: ModuleOp) -> Option<NamedMdAttr> {
597 module_op
598 .get_operation()
599 .deref(ctx)
600 .attributes
601 .get::<NamedMdAttr>(&ATTR_KEY_NAMED_MD)
602 .cloned()
603}
604
605pub fn set_named_metadata(ctx: &Context, module_op: ModuleOp, named: NamedMdAttr) {
607 module_op
608 .get_operation()
609 .deref_mut(ctx)
610 .attributes
611 .set(ATTR_KEY_NAMED_MD.clone(), named);
612}
613
614pub fn get_attachments(ctx: &Context, op: Ptr<Operation>) -> Option<MdAttachmentsAttr> {
616 op.deref(ctx)
617 .attributes
618 .get::<MdAttachmentsAttr>(&ATTR_KEY_MD_ATTACHMENTS)
619 .cloned()
620}
621
622pub fn set_attachments(ctx: &Context, op: Ptr<Operation>, attachments: MdAttachmentsAttr) {
624 op.deref_mut(ctx)
625 .attributes
626 .set(ATTR_KEY_MD_ATTACHMENTS.clone(), attachments);
627}
628
629pub fn attach_metadata(ctx: &Context, op: Ptr<Operation>, kind: impl Into<String>, node: MdNodeId) {
631 let mut attachments = get_attachments(ctx, op).unwrap_or_default();
632 attachments.set(kind, node);
633 set_attachments(ctx, op, attachments);
634}
635
636pub fn find_enclosing_module(ctx: &Context, op: Ptr<Operation>) -> Option<ModuleOp> {
638 let mut cur = Some(op);
639 while let Some(op) = cur {
640 if let Some(module_op) = Operation::get_op::<ModuleOp>(op, ctx) {
641 return Some(module_op);
642 }
643 cur = op.deref(ctx).get_parent_op(ctx);
644 }
645 None
646}
647
648pub fn find_metadata_table(ctx: &Context, op: Ptr<Operation>) -> Option<MdTableAttr> {
651 find_enclosing_module(ctx, op).and_then(|module_op| get_metadata_table(ctx, module_op))
652}
653
654#[derive(Debug, Error)]
656pub enum MdAddErr {
657 #[error("Cannot add a metadata node for an operation that is not inside a module")]
658 NoEnclosingModule,
659}
660
661pub fn add_metadata_node(ctx: &Context, op: Ptr<Operation>, node: MdNodeAttr) -> Result<MdNodeId> {
666 let Some(module_op) = find_enclosing_module(ctx, op) else {
667 let loc = op.deref(ctx).loc();
668 return arg_err!(loc, MdAddErr::NoEnclosingModule);
669 };
670 let mut table = get_metadata_table(ctx, module_op).unwrap_or_default();
671 let node_id = table.push_uniqued(node);
672 set_metadata_table(ctx, module_op, table);
673 Ok(node_id)
674}
675
676pub fn attach_new_metadata(
680 ctx: &Context,
681 op: Ptr<Operation>,
682 kind: impl Into<String>,
683 node: MdNodeAttr,
684) -> Result<MdNodeId> {
685 let node_id = add_metadata_node(ctx, op, node)?;
686 attach_metadata(ctx, op, kind, node_id);
687 Ok(node_id)
688}
689
690#[derive(Debug, Error)]
691pub enum MetadataVerifyErr {
692 #[error("Metadata node #{0} is not in the module's metadata table")]
693 DanglingNodeRef(MdNodeId),
694 #[error("Metadata is attached here, but the module has no metadata table")]
695 NoTable,
696 #[error("Metadata refers to \"{0}\", which is not a symbol of this module")]
697 UndefinedSymbol(String),
698}
699
700fn check_symbols_resolve(
702 ctx: &Context,
703 module_op: ModuleOp,
704 symbol_tables: &mut SymbolTableCollection,
705 attr: &dyn Attribute,
706 loc: &Location,
707) -> Result<()> {
708 if let Some(symbol_addr) = attr.downcast_ref::<SymbolAddrAttr>() {
709 let symbol = symbol_addr.symbol();
710 if symbol_tables
711 .lookup_symbol_in_table(ctx, Box::new(module_op), symbol)
712 .is_none()
713 {
714 verify_err!(
715 loc.clone(),
716 MetadataVerifyErr::UndefinedSymbol(symbol.to_string())
717 )?;
718 }
719 } else if let Some(aggregate) = attr.downcast_ref::<AggregateAttr>() {
720 for element in aggregate.elements() {
721 check_symbols_resolve(ctx, module_op, symbol_tables, &**element, loc)?;
722 }
723 } else if let Some(splat) = attr.downcast_ref::<SplatAttr>() {
724 check_symbols_resolve(ctx, module_op, symbol_tables, splat.element(), loc)?;
725 }
726 Ok(())
727}
728
729pub fn verify_metadata(ctx: &Context, module_op: ModuleOp) -> Result<()> {
735 let module_op_ptr = module_op.get_operation();
736 let table = get_metadata_table(ctx, module_op).unwrap_or_default();
737 let num_nodes = table.len() as MdNodeId;
738 let loc = module_op_ptr.deref(ctx).loc();
739
740 let check = |node: MdNodeId, loc: Location| -> Result<()> {
741 if node >= num_nodes {
742 verify_err!(loc, MetadataVerifyErr::DanglingNodeRef(node))?;
743 }
744 Ok(())
745 };
746
747 let mut symbol_tables = SymbolTableCollection::new();
748 for (_, node) in table.iter() {
749 for operand in node.operands() {
750 match operand {
751 MdOperandAttr::Node(id) => check(*id, loc.clone())?,
752 MdOperandAttr::Constant(attr) => {
753 verify_attr(&**attr, ctx).map_err(|mut err| {
754 if err.loc.is_unknown() {
755 err.set_loc(loc.clone());
756 }
757 err
758 })?;
759 check_symbols_resolve(ctx, module_op, &mut symbol_tables, &**attr, &loc)?
760 }
761 MdOperandAttr::Null | MdOperandAttr::String(_) => (),
762 }
763 }
764 }
765
766 if let Some(named) = get_named_metadata(ctx, module_op) {
767 for (_, nodes) in named.iter() {
768 for node in nodes {
769 check(*node, loc.clone())?;
770 }
771 }
772 }
773
774 let mut state = (num_nodes, get_metadata_table(ctx, module_op).is_some());
775 let walk_result: WalkResult<Error> = walk_op(
776 ctx,
777 &mut state,
778 &WALKCONFIG_PREORDER_FORWARD,
779 module_op_ptr,
780 |ctx: &Context,
781 (num_nodes, has_table): &mut (MdNodeId, bool),
782 node: IRNode|
783 -> WalkResult<Error> {
784 let IRNode::Operation(op) = node else {
785 return walk_advance();
786 };
787 let Some(attachments) = get_attachments(ctx, op) else {
788 return walk_advance();
789 };
790 let loc = op.deref(ctx).loc();
791 if !*has_table && !attachments.is_empty() {
792 return walk_break(verify_error!(loc, MetadataVerifyErr::NoTable));
793 }
794 for (_, node) in attachments.iter() {
795 if node >= *num_nodes {
796 return walk_break(verify_error!(
797 loc.clone(),
798 MetadataVerifyErr::DanglingNodeRef(node)
799 ));
800 }
801 }
802 walk_advance()
803 },
804 );
805
806 match walk_result {
807 WalkResult::Break(err) => Err(err),
808 WalkResult::Continue(_) => Ok(()),
809 }
810}