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