llvm_mapper/block/
module.rs

1//! Functionality for mapping the `MODULE_BLOCK` block.
2
3use llvm_support::bitcodes::{IrBlockId, ModuleCode};
4use llvm_support::TARGET_TRIPLE;
5use thiserror::Error;
6
7use crate::block::attributes::{AttributeError, AttributeGroups, Attributes};
8use crate::block::type_table::{TypeTable, TypeTableError};
9use crate::block::{BlockId, IrBlock};
10use crate::map::{CtxMappable, MapError, PartialCtxMappable, PartialMapCtx};
11use crate::record::{
12    Alias, AliasError, Comdat, ComdatError, DataLayout, DataLayoutError,
13    Function as FunctionRecord, FunctionError as FunctionRecordError,
14};
15use crate::unroll::UnrolledBlock;
16
17/// Errors that can occur while mapping a module.
18#[derive(Debug, Error)]
19pub enum ModuleError {
20    /// The `MODULE_CODE_VERSION` couldn't be found.
21    #[error("bitcode module has no version")]
22    MissingVersion,
23
24    /// An error occured while mapping the datalayout record.
25    #[error("invalid datalayout record")]
26    DataLayoutRecord(#[from] DataLayoutError),
27
28    /// An error occurred while mapping the type table block.
29    #[error("invalid type table block")]
30    TypeTableBlock(#[from] TypeTableError),
31
32    /// An error occurred while mapping one of the attribute blocks.
33    #[error("invalid attribute block")]
34    AttributeBlock(#[from] AttributeError),
35
36    /// An error occurred while mapping a COMDAT record.
37    #[error("invalid COMDAT record")]
38    ComdatRecord(#[from] ComdatError),
39
40    /// An error occurred while mapping a function record.
41    #[error("invalid function record")]
42    FunctionRecord(#[from] FunctionRecordError),
43
44    /// An error occurred while mapping an alias record.
45    #[error("invalid alias record")]
46    Alias(#[from] AliasError),
47
48    /// A generic mapping error occurred.
49    #[error("generic mapping error")]
50    Map(#[from] MapError),
51}
52
53/// Models the `MODULE_BLOCK` block.
54#[non_exhaustive]
55#[derive(Debug)]
56pub struct Module {
57    /// The target triple specification.
58    pub triple: String,
59    /// Any assembly block lines in the module.
60    pub asm: Vec<String>,
61    /// Any dependent libraries listed in the module.
62    pub deplibs: Vec<String>,
63}
64
65impl IrBlock for Module {
66    type Error = ModuleError;
67
68    const BLOCK_ID: IrBlockId = IrBlockId::Module;
69
70    fn try_map_inner(block: &UnrolledBlock, ctx: &mut PartialMapCtx) -> Result<Self, Self::Error> {
71        // Mapping the module requires us to fill in the `PartialMapCtx` first,
72        // so we can reify it into a `MapCtx` for subsequent steps.
73        ctx.version = Some({
74            let version = block
75                .records()
76                .exactly_one(ModuleCode::Version)
77                .map_err(MapError::Inconsistent)?;
78
79            *version.fields().get(0).ok_or(ModuleError::MissingVersion)?
80        });
81
82        // Each module *should* have a datalayout record, but doesn't necessarily.
83        if let Some(record) = block
84            .records()
85            .one_or_none(ModuleCode::DataLayout)
86            .map_err(MapError::Inconsistent)?
87        {
88            ctx.datalayout = DataLayout::try_map(record, ctx)?;
89        }
90
91        // Build the section table. We'll reference this later.
92        ctx.section_table = block
93            .records()
94            .by_code(ModuleCode::SectionName)
95            .map(|rec| rec.try_string(0))
96            .collect::<Result<Vec<_>, _>>()
97            .map_err(MapError::RecordString)?;
98
99        // Build the GC table. We'll reference this later.
100        ctx.gc_table = block
101            .records()
102            .by_code(ModuleCode::GcName)
103            .map(|rec| rec.try_string(0))
104            .collect::<Result<Vec<_>, _>>()
105            .map_err(MapError::RecordString)?;
106
107        // Build the type table.
108        ctx.type_table = Some(TypeTable::try_map(
109            block
110                .blocks()
111                .exactly_one(BlockId::Ir(IrBlockId::Type))
112                .map_err(MapError::Inconsistent)?,
113            ctx,
114        )?);
115
116        // Collect all attribute groups and individual attribute references.
117        // The order here is important: attribute groups must be mapped
118        // and stored in the `PartialMapCtx` before the attribute block itself can be mapped.
119        // Neither block is mandatory.
120        if let Some(attribute_groups) = block
121            .blocks()
122            .one_or_none(BlockId::Ir(IrBlockId::ParamAttrGroup))
123            .map_err(MapError::Inconsistent)?
124            .map(|b| AttributeGroups::try_map(b, ctx))
125            .transpose()?
126        {
127            ctx.attribute_groups = attribute_groups;
128        }
129
130        if let Some(attributes) = block
131            .blocks()
132            .one_or_none(BlockId::Ir(IrBlockId::ParamAttr))
133            .map_err(MapError::Inconsistent)?
134            .map(|b| Attributes::try_map(b, ctx))
135            .transpose()?
136        {
137            ctx.attributes = attributes;
138        }
139
140        // Build the list of COMDATs. We'll reference this later.
141        ctx.comdats = block
142            .records()
143            .by_code(ModuleCode::Comdat)
144            .map(|rec| Comdat::try_map(rec, ctx))
145            .collect::<Result<Vec<_>, _>>()?;
146
147        // After this point, `ctx` refers to a fully reified `MapCtx`.
148        let ctx = ctx.reify().map_err(MapError::Context)?;
149
150        // Each module *should* have a target triple, but doesn't necessarily.
151        let triple = match block
152            .records()
153            .one_or_none(ModuleCode::Triple)
154            .map_err(MapError::Inconsistent)?
155        {
156            Some(record) => record.try_string(0).map_err(MapError::RecordString)?,
157            None => TARGET_TRIPLE.into(),
158        };
159
160        // Each module has zero or exactly one MODULE_CODE_ASM records.
161        let asm = match block
162            .records()
163            .one_or_none(ModuleCode::Asm)
164            .map_err(MapError::Inconsistent)?
165        {
166            None => Vec::new(),
167            Some(record) => record
168                .try_string(0)
169                .map_err(MapError::RecordString)?
170                .split('\n')
171                .map(String::from)
172                .collect::<Vec<_>>(),
173        };
174
175        // Deplib records are deprecated, but we might be parsing an older bitstream.
176        let deplibs = block
177            .records()
178            .by_code(ModuleCode::DepLib)
179            .map(|rec| rec.try_string(0))
180            .collect::<Result<Vec<_>, _>>()
181            .map_err(MapError::RecordString)?;
182
183        // Collect the function records and blocks in this module.
184        let functions = block
185            .records()
186            .by_code(ModuleCode::Function)
187            .map(|rec| FunctionRecord::try_map(rec, &ctx))
188            .collect::<Result<Vec<_>, _>>()?;
189
190        // TODO: Handle function blocks as well.
191        log::debug!("functions: {:?}", functions);
192
193        let aliases = block
194            .records()
195            .by_code(ModuleCode::Alias)
196            .map(|rec| Alias::try_map(rec, &ctx))
197            .collect::<Result<Vec<_>, _>>()?;
198
199        log::debug!("aliases: {:?}", aliases);
200
201        Ok(Self {
202            triple,
203            asm,
204            deplibs,
205        })
206    }
207}