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//! Debug locations (`DILocation`, and the file and line of a function's `DISubprogram`)
38//! are represented as op [Location](pliron::location::Location)s.
39//!
40//! TODO: Only generic nodes (LLVM's `MDTuple`) are supported. LLVM's other specialized
41//! debug info nodes (`DICompileUnit`, `DIType`, ...) need extensions in [MdNodeAttr];
42//! until then, conversion from LLVM-IR drops them, with a warning.
43
44use 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
80/// Index of a metadata node in the module's metadata table ([MdTableAttr]).
81pub type MdNodeId = u32;
82
83/// Write `s` as a quoted string, escaping what pliron's quoted string parser un-escapes.
84///
85/// [quoted](pliron::irfmt::printers::quoted) escapes via [Debug](core::fmt::Debug), which
86/// the parser won't accept back.
87fn 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    /// The module level [MdTableAttr] holding every metadata node's definition.
100    ATTR_KEY_MD_TABLE, "llvm_metadata_defs"
101);
102
103dict_key!(
104    /// The module level [NamedMdAttr], LLVM's named metadata.
105    ATTR_KEY_NAMED_MD, "llvm_named_metadata"
106);
107
108dict_key!(
109    /// The [MdAttachmentsAttr] on an [Operation].
110    ATTR_KEY_MD_ATTACHMENTS, "llvm_metadata"
111);
112
113/// An operand of a metadata node ([MdNodeAttr]).
114///
115/// Contrary to its name, this isn't an [Attribute].
116#[derive(PartialEq, Eq, Clone, Debug, Hash)]
117pub enum MdOperandAttr {
118    /// A null operand. Printed as `null`.
119    Null,
120    /// LLVM's `MDString`. Printed as `!"contents"`.
121    String(String),
122    /// A reference to another node in the module's metadata table. Printed as `#42`.
123    Node(MdNodeId),
124    /// LLVM's `ConstantAsMetadata` wrapping a constant value.
125    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/// A metadata node: an entry in the module's metadata table ([MdTableAttr]).
175///
176/// TODO: Only LLVM's generic `MDTuple` is modelled. Typed support for LLVM's
177/// specialized debug info nodes would be added as further variants of this enum,
178/// holding the scalar fields that those nodes keep outside their operand list.
179#[derive(PartialEq, Eq, Clone, Debug, Hash)]
180pub enum MdNodeAttr {
181    /// LLVM's `MDTuple`: `!{ op, op, ... }`, or `distinct !{ op, op, ... }` for a node
182    /// that LLVM must not unique with a structurally identical one.
183    Tuple {
184        distinct: bool,
185        operands: Vec<MdOperandAttr>,
186    },
187}
188
189impl MdNodeAttr {
190    /// A uniqued `MDTuple` with the given operands.
191    pub fn new_tuple(operands: Vec<MdOperandAttr>) -> Self {
192        MdNodeAttr::Tuple {
193            distinct: false,
194            operands,
195        }
196    }
197
198    /// A `distinct` `MDTuple` with the given operands.
199    pub fn new_distinct_tuple(operands: Vec<MdOperandAttr>) -> Self {
200        MdNodeAttr::Tuple {
201            distinct: true,
202            operands,
203        }
204    }
205
206    /// Is this a `distinct` node?
207    pub fn is_distinct(&self) -> bool {
208        let MdNodeAttr::Tuple { distinct, .. } = self;
209        *distinct
210    }
211
212    /// This node's operands.
213    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/// The module level table of metadata node definitions. A node is referred to,
262/// from anywhere in the module, by its [index](MdNodeId) in this table.
263///
264/// Printed as `[#0 = !{...}, #1 = distinct !{...}]`.
265#[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    /// An empty table.
274    pub fn new() -> Self {
275        Self::default()
276    }
277
278    /// Add `node` to the table and get the [MdNodeId] it can be referred to by.
279    pub fn push(&mut self, node: MdNodeAttr) -> MdNodeId {
280        self.0.push_back(node) as MdNodeId
281    }
282
283    /// Add `node` to the table, unless it is a non-`distinct` node that is structurally
284    /// equal to one already in the table, and get the [MdNodeId] to refer to it by.
285    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    /// Reserve an id for a node whose operands aren't known yet, so that the node
295    /// (or a node it refers to) can refer back to it. Must be followed by
296    /// [Self::set](Self::set) for the same id.
297    pub fn reserve(&mut self) -> MdNodeId {
298        self.push(MdNodeAttr::new_tuple(Vec::new()))
299    }
300
301    /// Set the node at `id`, which must already be in the table.
302    pub fn set(&mut self, id: MdNodeId, node: MdNodeAttr) {
303        self.0[id as usize] = node;
304    }
305
306    /// The node at `id`, if there is one.
307    pub fn get(&self, id: MdNodeId) -> Option<&MdNodeAttr> {
308        self.0.get(id as usize)
309    }
310
311    /// Number of nodes in this table.
312    pub fn len(&self) -> usize {
313        self.0.len()
314    }
315
316    /// Is this table empty?
317    pub fn is_empty(&self) -> bool {
318        self.0.is_empty()
319    }
320
321    /// Iterate over `(id, node)` pairs.
322    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        // A module's metadata table can be long, so print an entry per line.
338        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/// Metadata attached to an [Operation], keyed by LLVM's metadata *kind* name
392/// (`"tbaa"`, `"llvm.loop"`, ...), the way `%v = load ..., !tbaa !5` attaches node
393/// `!5` under kind `tbaa`.
394///
395/// Printed as `["tbaa" = #5, "llvm.loop" = #14]`.
396#[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    /// No attachments.
405    pub fn new() -> Self {
406        Self::default()
407    }
408
409    /// The node attached under metadata kind `kind`, if any.
410    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    /// Attach `node` under metadata kind `kind`, replacing any existing attachment
418    /// for that kind.
419    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    /// Remove the attachment for metadata kind `kind`, if any.
428    pub fn remove(&mut self, kind: &str) {
429        self.0.retain(|(k, _)| k != kind);
430    }
431
432    /// Is there no attachment at all?
433    pub fn is_empty(&self) -> bool {
434        self.0.is_empty()
435    }
436
437    /// Iterate over `(kind name, node)` pairs.
438    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/// LLVM's [named metadata](https://llvm.org/docs/LangRef.html#named-metadata-nodes):
484/// a module level list of nodes under a name, such as `!llvm.module.flags = !{!0, !1}`.
485///
486/// Printed as `["llvm.module.flags" = [#0, #1], "llvm.ident" = [#4]]`.
487#[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    /// No named metadata.
496    pub fn new() -> Self {
497        Self::default()
498    }
499
500    /// The nodes listed under `name`, if `name` is present.
501    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    /// Append `node` to the list under `name`, creating the list if needed.
509    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    /// Is there no named metadata at all?
518    pub fn is_empty(&self) -> bool {
519        self.0.is_empty()
520    }
521
522    /// Iterate over `(name, nodes)` pairs.
523    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
578/// Get the metadata table of `module_op`.
579pub 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
588/// Set the metadata table on `module_op`.
589pub 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
597/// Get the named metadata of `module_op`.
598pub 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
607/// Set the named metadata on `module_op`.
608pub 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
616/// Get the metadata attached to `op`.
617pub 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
624/// Attach `attachments` to `op`, replacing whatever was attached to it.
625pub 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
631/// Attach the node `node` to `op` under the LLVM metadata kind `kind`.
632pub 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
638/// Starting at `op` and walking up its ancestors, find the enclosing [ModuleOp].
639pub 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
650/// Starting at `op` and walking up its ancestors, find the [metadata table](MdTableAttr)
651/// of the enclosing [ModuleOp].
652pub 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/// Error enum for metadata addition.
657#[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
663/// Add `node` to the metadata table of the module enclosing `op`, creating the table
664/// if the module doesn't have one yet, and get the [MdNodeId] to refer to it by.
665///
666/// A non-`distinct` node already in the table isn't added again.
667pub 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
677/// Add `node` to the metadata table of the module enclosing `op`,
678/// and attach it to `op` under the LLVM metadata kind `kind`,
679/// replacing any existing attachment for that kind.
680pub 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
701/// Ensure that symbols referred to by constant `attr` resolve in `module_op`.
702fn 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
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
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}