llvm_mapper/map.rs
1//! Traits for mapping bitstream types to models.
2
3use thiserror::Error;
4
5use crate::block::Strtab;
6use crate::block::{AttributeGroups, Attributes, TypeTable};
7use crate::record::{Comdat, DataLayout, RecordStringError};
8use crate::unroll::ConsistencyError;
9
10/// Generic errors that can occur when mapping.
11#[derive(Debug, Error)]
12pub enum MapError {
13 /// We couldn't map a block, for any number of reasons.
14 #[error("error while mapping block: {0}")]
15 BadBlockMap(String),
16
17 /// We encountered an inconsistent block or record state.
18 #[error("inconsistent block or record state")]
19 Inconsistent(#[from] ConsistencyError),
20
21 /// We encountered an unsupported feature or layout.
22 #[error("unsupported: {0}")]
23 Unsupported(String),
24
25 /// We encountered an invalid state or combination of states.
26 ///
27 /// This variant should be used extremely sparingly.
28 #[error("invalid: {0}")]
29 Invalid(String),
30
31 /// We couldn't extract a string from a record.
32 #[error("error while extracting string: {0}")]
33 RecordString(#[from] RecordStringError),
34
35 /// We don't have the appropriate context for a mapping operation.
36 #[error("missing context for mapping")]
37 Context(#[from] MapCtxError),
38}
39
40/// Errors that can occur when accessing a [`MapCtx`](MapCtx).
41#[derive(Debug, Error)]
42pub enum MapCtxError {
43 /// The version field is needed, but unavailable.
44 #[error("mapping context requires a version for disambiguation, but none is available")]
45 NoVersion,
46
47 /// The type table is needed, but unavailable.
48 #[error("mapping context requires types, but none are available")]
49 NoTypeTable,
50}
51
52/// A mushy container for various bits of state that are necessary for
53/// correct block and record mapping in the context of a particular IR module.
54///
55/// This is the "partial" counterpart to the [`MapCtx`](MapCtx) structure,
56/// which is produced from this structure with a call to [`reify`](PartialMapCtx::reify).
57#[non_exhaustive]
58#[derive(Debug, Default)]
59pub(crate) struct PartialMapCtx {
60 pub(crate) version: Option<u64>,
61 pub(crate) datalayout: DataLayout,
62 pub(crate) section_table: Vec<String>,
63 pub(crate) gc_table: Vec<String>,
64 pub(crate) strtab: Strtab,
65 pub(crate) attribute_groups: AttributeGroups,
66 pub(crate) attributes: Attributes,
67 pub(crate) type_table: Option<TypeTable>,
68 pub(crate) comdats: Vec<Comdat>,
69}
70
71impl PartialMapCtx {
72 pub(crate) fn reify(&self) -> Result<MapCtx, MapCtxError> {
73 log::debug!("reifying {self:?}");
74 Ok(MapCtx {
75 version: self.version.ok_or(MapCtxError::NoVersion)?,
76 datalayout: &self.datalayout,
77 section_table: &self.section_table,
78 gc_table: &self.gc_table,
79 strtab: &self.strtab,
80 attribute_groups: &self.attribute_groups,
81 attributes: &self.attributes,
82 type_table: self.type_table.as_ref().ok_or(MapCtxError::NoTypeTable)?,
83 comdats: &self.comdats,
84 })
85 }
86
87 /// A helper function for whether or not to use an associated string table for string lookups.
88 ///
89 /// This corresponds to `MODULE_CODE_VERSION`s of 2 and higher.
90 pub fn use_strtab(&self) -> Result<bool, MapCtxError> {
91 self.version.map(|v| v >= 2).ok_or(MapCtxError::NoVersion)
92 }
93
94 /// Returns the attribute groups stored in this context, or an error if not available.
95 pub fn attribute_groups(&self) -> &AttributeGroups {
96 &self.attribute_groups
97 }
98}
99
100/// A handle for various bits of state that are necessary for correct block
101/// and record mapping in the context of a particular IR module.
102///
103/// Block and record mapping operations are expected to update the supplied context,
104/// as appropriate.
105#[non_exhaustive]
106#[derive(Debug)]
107pub struct MapCtx<'ctx> {
108 /// The `MODULE_CODE_VERSION` for the IR module being mapped.
109 pub version: u64,
110
111 /// The datalayout specification.
112 pub datalayout: &'ctx DataLayout,
113
114 /// The section table.
115 pub section_table: &'ctx [String],
116
117 /// The GC table.
118 pub gc_table: &'ctx [String],
119
120 /// The string table.
121 pub strtab: &'ctx Strtab,
122
123 /// Any attribute groups.
124 pub attribute_groups: &'ctx AttributeGroups,
125
126 /// Any raw attributes.
127 pub attributes: &'ctx Attributes,
128
129 /// The type table.
130 pub type_table: &'ctx TypeTable,
131
132 /// The COMDAT list.
133 pub comdats: &'ctx [Comdat],
134 // TODO(ww): Maybe symtab and identification in here?
135}
136
137impl MapCtx<'_> {
138 /// A helper function for whether or not to use an associated string table for string lookups.
139 ///
140 /// This corresponds to `MODULE_CODE_VERSION`s of 2 and higher.
141 pub fn use_strtab(&self) -> bool {
142 self.version >= 2
143 }
144
145 /// A helper function for determining how operands are encoded.
146 ///
147 /// This corresponds to `MODULE_CODE_VERSION`s of 1 and higher.
148 pub fn use_relative_ids(&self) -> bool {
149 self.version >= 1
150 }
151}
152
153/// A trait for mapping some raw `T` into a model type.
154///
155/// This trait allows an implementer to modify the given [`PartialMapCtx`](PartialMapCtx),
156/// filling it in with state before it's reified into a "real" [`MapCtx`](MapCtx).
157///
158/// This two-stage process is designed to limit the number of invalid
159/// states that a `MapCtx` can be in, and to enable better lifetimes
160/// later in the IR module mapping process.
161pub(crate) trait PartialCtxMappable<T>: Sized {
162 type Error;
163
164 /// Attempt to map `T` into `Self` using the given [`PartialMapCtx`](PartialMapCtx).
165 fn try_map(raw: &T, ctx: &mut PartialMapCtx) -> Result<Self, Self::Error>;
166}
167
168/// A trait for mapping some raw `T` into a model type.
169///
170/// Implementing this trait is *almost* always preferable over
171/// [`PartialCtxMappable`](PartialCtxMappable) -- the former should really only
172/// be used when a mapping implementation **absolutely** must modify its
173/// [`MapCtx`](MapCtx), which should only happen early in IR module parsing.
174pub(crate) trait CtxMappable<'ctx, T>: Sized {
175 type Error;
176
177 /// Attempt to map `T` into `Self` using the given [`MapCtx`](MapCtx).
178 fn try_map(raw: &T, ctx: &'ctx MapCtx) -> Result<Self, Self::Error>;
179}