Skip to main content

pliron_llvm/
metadata.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) The pliron contributors
3
4//! `pliron` representation of [LLVM metadata](https://llvm.org/docs/LangRef.html#metadata).
5//!
6//! LLVM's metadata graph is made of `MDNode`s. The graph may be cyclic, including
7//! self referential nodes (like `!0 = distinct !{!0, !"llvm.loop.unroll.disable"}`).
8//! Nodes have an identity: structural equivalence does not imply identity equivalence.
9//!
10//! In `pliron`, every `MDNode` lives in a module level [metadata table](MdTableAttr),
11//! and the index in that table [identifies](MdNodeId) the node. This is similar to LLVM's
12//! textual format, where every node is printed at module level and referred to by number:
13//!
14//! ```llvm
15//! %10 = load i32, ptr %9, !tbaa !5, !alias.scope !12
16//! !5  = !{!6, !6, i64 0}
17//! !12 = distinct !{!12, !"copy: argument 1"}
18//! ```
19//!
20//! becomes, in pliron:
21//!
22//! ```text
23//! v10 = llvm.load v9 : builtin.integer si32 !0
24//!
25//! outlined_attributes:
26//! !0 = [llvm_metadata = llvm.md_attachments ["tbaa" = #5, "alias.scope" = #12]]
27//! ```
28//!
29//! with the definitions of `#5` and `#12` in the module's [MdTableAttr].
30//!
31//! Metadata *attachments* on an [Operation] ([MdAttachmentsAttr]), and the module's
32//! named metadata ([NamedMdAttr]), both refer to nodes by [MdNodeId] too.
33//! All of [MdTableAttr], [MdAttachmentsAttr] and [NamedMdAttr] are [OutlinedAttr]s,
34//! ensuring better readability and simpler formatters (custom printers and parsers
35//! don't need to be responsible for metadata, it's automatically taken care of).
36//!
37//! TODO: Only generic nodes (LLVM's `MDTuple`) are supported. LLVM's specialized
38//! debug info nodes (`DILocation`, `DISubprogram`, ...) need extensions in [MdNodeAttr];
39//! until then, conversion from LLVM-IR drops them, with a warning.
40
41use 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
79/// Index of a metadata node in the module's metadata table ([MdTableAttr]).
80pub type MdNodeId = u32;
81
82/// Write `s` as a quoted string, escaping what pliron's quoted string parser un-escapes.
83///
84/// [quoted](pliron::irfmt::printers::quoted) escapes via [Debug](core::fmt::Debug), which
85/// the parser won't accept back.
86fn 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    /// The module level [MdTableAttr] holding every metadata node's definition.
99    ATTR_KEY_MD_TABLE, "llvm_metadata_defs"
100);
101
102dict_key!(
103    /// The module level [NamedMdAttr], LLVM's named metadata.
104    ATTR_KEY_NAMED_MD, "llvm_named_metadata"
105);
106
107dict_key!(
108    /// The [MdAttachmentsAttr] on an [Operation].
109    ATTR_KEY_MD_ATTACHMENTS, "llvm_metadata"
110);
111
112/// An operand of a metadata node ([MdNodeAttr]).
113///
114/// Contrary to its name, this isn't an [Attribute].
115#[derive(PartialEq, Eq, Clone, Debug, Hash)]
116pub enum MdOperandAttr {
117    /// A null operand. Printed as `null`.
118    Null,
119    /// LLVM's `MDString`. Printed as `!"contents"`.
120    String(String),
121    /// A reference to another node in the module's metadata table. Printed as `#42`.
122    Node(MdNodeId),
123    /// LLVM's `ConstantAsMetadata` wrapping a constant value.
124    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/// A metadata node: an entry in the module's metadata table ([MdTableAttr]).
174///
175/// TODO: Only LLVM's generic `MDTuple` is modelled. Typed support for LLVM's
176/// specialized debug info nodes would be added as further variants of this enum,
177/// holding the scalar fields that those nodes keep outside their operand list.
178#[derive(PartialEq, Eq, Clone, Debug, Hash)]
179pub enum MdNodeAttr {
180    /// LLVM's `MDTuple`: `!{ op, op, ... }`, or `distinct !{ op, op, ... }` for a node
181    /// that LLVM must not unique with a structurally identical one.
182    Tuple {
183        distinct: bool,
184        operands: Vec<MdOperandAttr>,
185    },
186}
187
188impl MdNodeAttr {
189    /// A uniqued `MDTuple` with the given operands.
190    pub fn new_tuple(operands: Vec<MdOperandAttr>) -> Self {
191        MdNodeAttr::Tuple {
192            distinct: false,
193            operands,
194        }
195    }
196
197    /// A `distinct` `MDTuple` with the given operands.
198    pub fn new_distinct_tuple(operands: Vec<MdOperandAttr>) -> Self {
199        MdNodeAttr::Tuple {
200            distinct: true,
201            operands,
202        }
203    }
204
205    /// Is this a `distinct` node?
206    pub fn is_distinct(&self) -> bool {
207        let MdNodeAttr::Tuple { distinct, .. } = self;
208        *distinct
209    }
210
211    /// This node's operands.
212    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/// The module level table of metadata node definitions. A node is referred to,
261/// from anywhere in the module, by its [index](MdNodeId) in this table.
262///
263/// Printed as `[#0 = !{...}, #1 = distinct !{...}]`.
264#[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    /// An empty table.
273    pub fn new() -> Self {
274        Self::default()
275    }
276
277    /// Add `node` to the table and get the [MdNodeId] it can be referred to by.
278    pub fn push(&mut self, node: MdNodeAttr) -> MdNodeId {
279        self.0.push_back(node) as MdNodeId
280    }
281
282    /// Add `node` to the table, unless it is a non-`distinct` node that is structurally
283    /// equal to one already in the table, and get the [MdNodeId] to refer to it by.
284    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    /// Reserve an id for a node whose operands aren't known yet, so that the node
294    /// (or a node it refers to) can refer back to it. Must be followed by
295    /// [Self::set](Self::set) for the same id.
296    pub fn reserve(&mut self) -> MdNodeId {
297        self.push(MdNodeAttr::new_tuple(Vec::new()))
298    }
299
300    /// Set the node at `id`, which must already be in the table.
301    pub fn set(&mut self, id: MdNodeId, node: MdNodeAttr) {
302        self.0[id as usize] = node;
303    }
304
305    /// The node at `id`, if there is one.
306    pub fn get(&self, id: MdNodeId) -> Option<&MdNodeAttr> {
307        self.0.get(id as usize)
308    }
309
310    /// Number of nodes in this table.
311    pub fn len(&self) -> usize {
312        self.0.len()
313    }
314
315    /// Is this table empty?
316    pub fn is_empty(&self) -> bool {
317        self.0.is_empty()
318    }
319
320    /// Iterate over `(id, node)` pairs.
321    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        // A module's metadata table can be long, so print an entry per line.
337        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/// Metadata attached to an [Operation], keyed by LLVM's metadata *kind* name
390/// (`"tbaa"`, `"llvm.loop"`, ...), the way `%v = load ..., !tbaa !5` attaches node
391/// `!5` under kind `tbaa`.
392///
393/// Printed as `["tbaa" = #5, "llvm.loop" = #14]`.
394#[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    /// No attachments.
403    pub fn new() -> Self {
404        Self::default()
405    }
406
407    /// The node attached under metadata kind `kind`, if any.
408    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    /// Attach `node` under metadata kind `kind`, replacing any existing attachment
416    /// for that kind.
417    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    /// Remove the attachment for metadata kind `kind`, if any.
426    pub fn remove(&mut self, kind: &str) {
427        self.0.retain(|(k, _)| k != kind);
428    }
429
430    /// Is there no attachment at all?
431    pub fn is_empty(&self) -> bool {
432        self.0.is_empty()
433    }
434
435    /// Iterate over `(kind name, node)` pairs.
436    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/// LLVM's [named metadata](https://llvm.org/docs/LangRef.html#named-metadata-nodes):
482/// a module level list of nodes under a name, such as `!llvm.module.flags = !{!0, !1}`.
483///
484/// Printed as `["llvm.module.flags" = [#0, #1], "llvm.ident" = [#4]]`.
485#[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    /// No named metadata.
494    pub fn new() -> Self {
495        Self::default()
496    }
497
498    /// The nodes listed under `name`, if `name` is present.
499    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    /// Append `node` to the list under `name`, creating the list if needed.
507    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    /// Is there no named metadata at all?
516    pub fn is_empty(&self) -> bool {
517        self.0.is_empty()
518    }
519
520    /// Iterate over `(name, nodes)` pairs.
521    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
576/// Get the metadata table of `module_op`.
577pub 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
586/// Set the metadata table on `module_op`.
587pub 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
595/// Get the named metadata of `module_op`.
596pub 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
605/// Set the named metadata on `module_op`.
606pub 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
614/// Get the metadata attached to `op`.
615pub 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
622/// Attach `attachments` to `op`, replacing whatever was attached to it.
623pub 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
629/// Attach the node `node` to `op` under the LLVM metadata kind `kind`.
630pub 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
636/// Starting at `op` and walking up its ancestors, find the enclosing [ModuleOp].
637pub 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
648/// Starting at `op` and walking up its ancestors, find the [metadata table](MdTableAttr)
649/// of the enclosing [ModuleOp].
650pub 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/// Error enum for metadata addition.
655#[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
661/// Add `node` to the metadata table of the module enclosing `op`, creating the table
662/// if the module doesn't have one yet, and get the [MdNodeId] to refer to it by.
663///
664/// A non-`distinct` node already in the table isn't added again.
665pub 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
676/// Add `node` to the metadata table of the module enclosing `op`,
677/// and attach it to `op` under the LLVM metadata kind `kind`,
678/// replacing any existing attachment for that kind.
679pub 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
700/// Ensure that symbols referred to by constant `attr` resolve in `module_op`.
701fn 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
729/// Verify that
730/// - Every metadata reference in the module rooted at `module_op`
731///   resolves to a node in the module's metadata table
732/// - Every constant that a metadata node holds verifies and refers
733///   only to symbols of the module.
734pub 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}