use llvm_support::bitcodes::{IrBlockId, ModuleCode};
use llvm_support::TARGET_TRIPLE;
use thiserror::Error;
use crate::block::attributes::{AttributeError, AttributeGroups, Attributes};
use crate::block::type_table::{TypeTable, TypeTableError};
use crate::block::{BlockId, IrBlock};
use crate::map::{CtxMappable, MapError, PartialCtxMappable, PartialMapCtx};
use crate::record::{
Alias, AliasError, Comdat, ComdatError, DataLayout, DataLayoutError,
Function as FunctionRecord, FunctionError as FunctionRecordError,
};
use crate::unroll::UnrolledBlock;
#[derive(Debug, Error)]
pub enum ModuleError {
#[error("bitcode module has no version")]
MissingVersion,
#[error("invalid datalayout record")]
DataLayoutRecord(#[from] DataLayoutError),
#[error("invalid type table block")]
TypeTableBlock(#[from] TypeTableError),
#[error("invalid attribute block")]
AttributeBlock(#[from] AttributeError),
#[error("invalid COMDAT record")]
ComdatRecord(#[from] ComdatError),
#[error("invalid function record")]
FunctionRecord(#[from] FunctionRecordError),
#[error("invalid alias record")]
Alias(#[from] AliasError),
#[error("generic mapping error")]
Map(#[from] MapError),
}
#[non_exhaustive]
#[derive(Debug)]
pub struct Module {
pub triple: String,
pub asm: Vec<String>,
pub deplibs: Vec<String>,
}
impl IrBlock for Module {
type Error = ModuleError;
const BLOCK_ID: IrBlockId = IrBlockId::Module;
fn try_map_inner(block: &UnrolledBlock, ctx: &mut PartialMapCtx) -> Result<Self, Self::Error> {
ctx.version = Some({
let version = block
.records()
.exactly_one(ModuleCode::Version)
.map_err(MapError::Inconsistent)?;
*version.fields().get(0).ok_or(ModuleError::MissingVersion)?
});
if let Some(record) = block
.records()
.one_or_none(ModuleCode::DataLayout)
.map_err(MapError::Inconsistent)?
{
ctx.datalayout = DataLayout::try_map(record, ctx)?;
}
ctx.section_table = block
.records()
.by_code(ModuleCode::SectionName)
.map(|rec| rec.try_string(0))
.collect::<Result<Vec<_>, _>>()
.map_err(MapError::RecordString)?;
ctx.gc_table = block
.records()
.by_code(ModuleCode::GcName)
.map(|rec| rec.try_string(0))
.collect::<Result<Vec<_>, _>>()
.map_err(MapError::RecordString)?;
ctx.type_table = Some(TypeTable::try_map(
block
.blocks()
.exactly_one(BlockId::Ir(IrBlockId::Type))
.map_err(MapError::Inconsistent)?,
ctx,
)?);
if let Some(attribute_groups) = block
.blocks()
.one_or_none(BlockId::Ir(IrBlockId::ParamAttrGroup))
.map_err(MapError::Inconsistent)?
.map(|b| AttributeGroups::try_map(b, ctx))
.transpose()?
{
ctx.attribute_groups = attribute_groups;
}
if let Some(attributes) = block
.blocks()
.one_or_none(BlockId::Ir(IrBlockId::ParamAttr))
.map_err(MapError::Inconsistent)?
.map(|b| Attributes::try_map(b, ctx))
.transpose()?
{
ctx.attributes = attributes;
}
ctx.comdats = block
.records()
.by_code(ModuleCode::Comdat)
.map(|rec| Comdat::try_map(rec, ctx))
.collect::<Result<Vec<_>, _>>()?;
let ctx = ctx.reify().map_err(MapError::Context)?;
let triple = match block
.records()
.one_or_none(ModuleCode::Triple)
.map_err(MapError::Inconsistent)?
{
Some(record) => record.try_string(0).map_err(MapError::RecordString)?,
None => TARGET_TRIPLE.into(),
};
let asm = match block
.records()
.one_or_none(ModuleCode::Asm)
.map_err(MapError::Inconsistent)?
{
None => Vec::new(),
Some(record) => record
.try_string(0)
.map_err(MapError::RecordString)?
.split('\n')
.map(String::from)
.collect::<Vec<_>>(),
};
let deplibs = block
.records()
.by_code(ModuleCode::DepLib)
.map(|rec| rec.try_string(0))
.collect::<Result<Vec<_>, _>>()
.map_err(MapError::RecordString)?;
let functions = block
.records()
.by_code(ModuleCode::Function)
.map(|rec| FunctionRecord::try_map(rec, &ctx))
.collect::<Result<Vec<_>, _>>()?;
log::debug!("functions: {:?}", functions);
let aliases = block
.records()
.by_code(ModuleCode::Alias)
.map(|rec| Alias::try_map(rec, &ctx))
.collect::<Result<Vec<_>, _>>()?;
log::debug!("aliases: {:?}", aliases);
Ok(Self {
triple,
asm,
deplibs,
})
}
}