llvm_mapper/block/
type_table.rs

1//! Functionality for mapping the `TYPE_BLOCK_ID_NEW` block.
2
3use std::convert::TryFrom;
4
5use llvm_support::bitcodes::{IrBlockId, TypeCode};
6use llvm_support::{
7    AddressSpace, ArrayTypeError, FunctionTypeError, IntegerTypeError, PointerTypeError,
8    StructTypeError, Type, VectorTypeError,
9};
10use num_enum::TryFromPrimitiveError;
11use thiserror::Error;
12
13use crate::block::IrBlock;
14use crate::map::{MapError, PartialMapCtx};
15use crate::unroll::UnrolledBlock;
16
17/// Errors that can occur when mapping the type table.
18#[derive(Debug, Error)]
19pub enum TypeTableError {
20    /// The size of the type table is invalid.
21    #[error("invalid type table size (expected {0} elements, got {1})")]
22    BadSize(usize, usize),
23
24    /// An invalid type index was requested.
25    #[error("invalid type table index: {0}")]
26    BadIndex(usize),
27
28    /// An unknown record code was seen.
29    #[error("unknown type code")]
30    UnknownTypeCode(#[from] TryFromPrimitiveError<TypeCode>),
31
32    /// The layout of the table itself (i.e., the record structures) is invalid.
33    #[error("invalid type table structure (broken records)")]
34    BadTable,
35
36    /// An invalid integer type was seen.
37    #[error("invalid integer type")]
38    InvalidIntegerType(#[from] IntegerTypeError),
39
40    /// An invalid pointer type was seen.
41    #[error("invalid pointer type")]
42    InvalidPointerType(#[from] PointerTypeError),
43
44    /// An invalid array type was seen.
45    #[error("invalid array type")]
46    InvalidArrayType(#[from] ArrayTypeError),
47
48    /// An invalid vector type was seen.
49    #[error("invalid vector type")]
50    InvalidVectorType(#[from] VectorTypeError),
51
52    /// An invalid structure type was seen.
53    #[error("invalid structure type")]
54    InvalidStructType(#[from] StructTypeError),
55
56    /// An invalid function type was seen.
57    #[error("invalid function type")]
58    InvalidFunctionType(#[from] FunctionTypeError),
59
60    /// A generic mapping error occured.
61    #[error("mapping error in string table")]
62    Map(#[from] MapError),
63}
64
65/// A symbolic type reference, which is really just an index into some
66/// unspecified type table.
67#[derive(Debug)]
68pub(crate) struct TypeRef(pub(crate) usize);
69
70impl From<usize> for TypeRef {
71    fn from(value: usize) -> TypeRef {
72        TypeRef(value)
73    }
74}
75
76impl From<u64> for TypeRef {
77    fn from(value: u64) -> TypeRef {
78        TypeRef::from(value as usize)
79    }
80}
81
82/// Represents a "partial type," i.e. a type whose subtypes may be symbolic
83/// and not fully resolved against a type table.
84#[derive(Debug)]
85enum PartialType {
86    Half,
87    BFloat,
88    Float,
89    Double,
90    Metadata,
91    X86Fp80,
92    Fp128,
93    PpcFp128,
94    Void,
95    Label,
96    X86Mmx,
97    X86Amx,
98    Token,
99    Integer(PartialIntegerType),
100    Function(PartialFunctionType),
101    Pointer(PartialPointerType),
102    OpaquePointer(AddressSpace),
103    Struct(PartialStructType),
104    Array(PartialArrayType),
105    FixedVector(PartialVectorType),
106    ScalableVector(PartialVectorType),
107}
108
109impl PartialType {
110    /// Fallibly convert this `PartialType` into a `Type`, using the given
111    /// `PartialTypeTable` as a reference.
112    fn resolve(&self, partials: &PartialTypeTable) -> Result<Type, TypeTableError> {
113        match self {
114            PartialType::Half => Ok(Type::Half),
115            PartialType::BFloat => Ok(Type::BFloat),
116            PartialType::Float => Ok(Type::Float),
117            PartialType::Double => Ok(Type::Double),
118            PartialType::Metadata => Ok(Type::Metadata),
119            PartialType::X86Fp80 => Ok(Type::X86Fp80),
120            PartialType::Fp128 => Ok(Type::Fp128),
121            PartialType::PpcFp128 => Ok(Type::PpcFp128),
122            PartialType::Void => Ok(Type::Void),
123            PartialType::Label => Ok(Type::Label),
124            PartialType::X86Mmx => Ok(Type::X86Mmx),
125            PartialType::X86Amx => Ok(Type::X86Amx),
126            PartialType::Token => Ok(Type::Token),
127            PartialType::Integer(ity) => Ok(Type::new_integer(ity.bit_width)?),
128            PartialType::Function(fty) => {
129                let return_type = partials.resolve(&fty.return_type)?;
130                let param_types = fty
131                    .param_types
132                    .iter()
133                    .map(|ty_ref| partials.resolve(ty_ref))
134                    .collect::<Result<Vec<_>, _>>()?;
135
136                Ok(Type::new_function(return_type, param_types, fty.is_vararg)?)
137            }
138            PartialType::Pointer(pty) => {
139                let pointee = partials.resolve(&pty.pointee)?;
140
141                Ok(Type::new_pointer(pointee, pty.address_space)?)
142            }
143            PartialType::OpaquePointer(oty) => Ok(Type::OpaquePointer(*oty)),
144            PartialType::Struct(sty) => {
145                let field_types = sty
146                    .field_types
147                    .iter()
148                    .map(|fty| partials.resolve(fty))
149                    .collect::<Result<Vec<_>, _>>()?;
150
151                Ok(Type::new_struct(
152                    sty.name.clone(),
153                    field_types,
154                    sty.is_packed,
155                )?)
156            }
157            PartialType::Array(aty) => {
158                let element_type = partials.resolve(&aty.element_type)?;
159
160                Ok(Type::new_array(aty.num_elements, element_type)?)
161            }
162            PartialType::FixedVector(vty) => {
163                log::debug!("vty: {:?}", vty);
164
165                let element_type = partials.resolve(&vty.element_type)?;
166                log::debug!("element_type: {:?}", partials.get(&vty.element_type));
167
168                Ok(Type::new_vector(vty.num_elements, element_type)?)
169            }
170            PartialType::ScalableVector(vty) => {
171                let element_type = partials.resolve(&vty.element_type)?;
172
173                Ok(Type::new_scalable_vector(vty.num_elements, element_type)?)
174            }
175        }
176    }
177}
178
179#[derive(Debug)]
180struct PartialIntegerType {
181    bit_width: u32,
182}
183
184/// Represents an (unresolved) function type.
185#[derive(Debug)]
186struct PartialFunctionType {
187    return_type: TypeRef,
188    param_types: Vec<TypeRef>,
189    is_vararg: bool,
190}
191
192/// Represents an (unresolved) pointer type.
193#[derive(Debug)]
194struct PartialPointerType {
195    pointee: TypeRef,
196    address_space: AddressSpace,
197}
198
199#[derive(Debug)]
200struct PartialStructType {
201    name: Option<String>,
202    field_types: Vec<TypeRef>,
203    is_packed: bool,
204}
205
206#[derive(Debug)]
207struct PartialArrayType {
208    num_elements: u64,
209    element_type: TypeRef,
210}
211
212#[derive(Debug)]
213struct PartialVectorType {
214    num_elements: u64,
215    element_type: TypeRef,
216}
217
218/// Represents a partial type table.
219///
220/// Every partial type table starts out empty (but with an expected ultimate size),
221/// and is incrementally updated as records within the type block are visited.
222#[derive(Debug)]
223struct PartialTypeTable {
224    numentries: usize,
225    inner: Vec<PartialType>,
226}
227
228impl PartialTypeTable {
229    fn new(numentries: usize) -> Self {
230        Self {
231            numentries: numentries,
232            inner: Vec::with_capacity(numentries),
233        }
234    }
235
236    fn add(&mut self, ty: PartialType) {
237        self.inner.push(ty)
238    }
239
240    fn last_mut(&mut self) -> Option<&mut PartialType> {
241        self.inner.last_mut()
242    }
243
244    /// Fallibly convert a `TypeRef` into its `PartialType` in this partial type table.
245    fn get(&self, ty_ref: &TypeRef) -> Result<&PartialType, TypeTableError> {
246        self.inner
247            .get(ty_ref.0)
248            .ok_or(TypeTableError::BadIndex(ty_ref.0))
249    }
250
251    /// Fallibly converts the given `TypeRef` into a fully owned `Type`.
252    fn resolve(&self, ty_ref: &TypeRef) -> Result<Type, TypeTableError> {
253        // `TypeRef` resolution happens in two steps: we grab the corresponding
254        // `PartialType`, and then resolve its subtypes.
255        let pty = self.get(ty_ref)?;
256
257        log::debug!("type ref {} resolves to {:?}", ty_ref.0, pty);
258
259        pty.resolve(self)
260    }
261
262    /// Fallibly converts this `PartialTypeTable` into a `TypeTable`.
263    fn reify(self) -> Result<TypeTable, TypeTableError> {
264        if self.inner.len() != self.numentries {
265            return Err(TypeTableError::BadSize(self.numentries, self.inner.len()));
266        }
267
268        // Walk the partial type table, resolving each partial type
269        // into a fully owned `Type`.
270        let types = self
271            .inner
272            .iter()
273            .map(|pty| pty.resolve(&self))
274            .collect::<Result<Vec<_>, _>>()?;
275
276        Ok(TypeTable(types))
277    }
278}
279
280/// Models the `TYPE_BLOCK_ID_NEW` block.
281#[derive(Clone, Debug)]
282pub struct TypeTable(Vec<Type>);
283
284impl TypeTable {
285    pub(crate) fn get(&self, ty_ref: impl Into<TypeRef>) -> Option<&Type> {
286        let ty_ref = ty_ref.into();
287        self.0.get(ty_ref.0)
288    }
289}
290
291impl IrBlock for TypeTable {
292    type Error = TypeTableError;
293
294    const BLOCK_ID: IrBlockId = IrBlockId::Type;
295
296    fn try_map_inner(block: &UnrolledBlock, _ctx: &mut PartialMapCtx) -> Result<Self, Self::Error> {
297        // Figure out how many type entries we have, and reserve the space for them up-front.
298        let numentries = *block
299            .records()
300            .one(TypeCode::NumEntry)
301            .ok_or(TypeTableError::BadTable)
302            .and_then(|r| r.fields().get(0).ok_or(TypeTableError::BadTable))?
303            as usize;
304
305        // To map the type table, we perform two passes:
306        // 1. We iterate over all type records, building an initial table of "partial"
307        //    types that contain only symbolic references to other types.
308        //    This pass allows us to fully resolve e.g. forward-declared types
309        //    without having to perform a more expensive visiting pass later.
310        // 2. We iterate over all of the partial types, resolving them into
311        //    fully owned and expanded `Type`s.
312        let mut partial_types = PartialTypeTable::new(numentries);
313        let mut last_type_name = String::new();
314        for record in block.records() {
315            // A convenience macro for turning a type record field access into an error on failure.
316            macro_rules! type_field {
317                ($n:literal) => {
318                    record
319                        .fields()
320                        .get($n)
321                        .copied()
322                        .ok_or(TypeTableError::BadTable)?
323                };
324            }
325
326            let code = TypeCode::try_from(record.code()).map_err(TypeTableError::from)?;
327
328            match code {
329                // Already visited; nothing to do.
330                TypeCode::NumEntry => continue,
331                TypeCode::Void => partial_types.add(PartialType::Void),
332                TypeCode::Half => partial_types.add(PartialType::Half),
333                TypeCode::BFloat => partial_types.add(PartialType::BFloat),
334                TypeCode::Float => partial_types.add(PartialType::Float),
335                TypeCode::Double => partial_types.add(PartialType::Double),
336                TypeCode::Label => partial_types.add(PartialType::Label),
337                TypeCode::Opaque => {
338                    // NOTE(ww): LLVM's BitcodeReader checks that the
339                    // TYPE_CODE_OPAQUE record has exactly one field, but
340                    // doesn't seem to use that field for anything.
341                    // Not sure what's up with that.
342
343                    if last_type_name.is_empty() {
344                        return Err(MapError::Invalid(
345                            "opaque type but no preceding type name".into(),
346                        )
347                        .into());
348                    }
349
350                    // Our opaque type might be forward-referenced. If so, we
351                    // fill in the previous type's name. Otherwise, we create
352                    // a new structure type with no body.
353                    if let Some(PartialType::Struct(s)) = partial_types.last_mut() {
354                        if s.name.is_some() {
355                            return Err(MapError::Invalid(
356                                "forward-declared opaque type already has name".into(),
357                            )
358                            .into());
359                        }
360
361                        s.name = Some(last_type_name.clone());
362                    } else {
363                        partial_types.add(PartialType::Struct(PartialStructType {
364                            name: Some(last_type_name.clone()),
365                            field_types: vec![],
366                            is_packed: false,
367                        }));
368                    }
369
370                    last_type_name.clear();
371                }
372                TypeCode::Integer => {
373                    let bit_width = type_field!(0) as u32;
374                    partial_types.add(PartialType::Integer(PartialIntegerType { bit_width }));
375                }
376                TypeCode::Pointer => {
377                    let pointee = TypeRef(type_field!(0) as usize);
378
379                    let address_space = AddressSpace::try_from(type_field!(1)).map_err(|e| {
380                        MapError::Invalid(format!("bad address space for pointer type: {:?}", e))
381                    })?;
382
383                    partial_types.add(PartialType::Pointer(PartialPointerType {
384                        pointee,
385                        address_space,
386                    }));
387                }
388                TypeCode::FunctionOld => {
389                    // TODO(ww): These only show up in older bitcode, so don't bother with them for now.
390                    return Err(MapError::Unsupported(
391                        "unsupported: old function type codes; please implement!".into(),
392                    )
393                    .into());
394                }
395                TypeCode::Array => {
396                    let num_elements = type_field!(0);
397
398                    let element_type = TypeRef(type_field!(1) as usize);
399
400                    partial_types.add(PartialType::Array(PartialArrayType {
401                        num_elements,
402                        element_type,
403                    }));
404                }
405                TypeCode::Vector => {
406                    let num_elements = type_field!(0);
407
408                    let element_type = TypeRef(type_field!(1) as usize);
409
410                    // A vector type is either fixed or scalable, depending on the
411                    // third field (which can also be absent, indicating fixed).
412                    let scalable = record.fields().get(2).map_or_else(|| false, |f| *f > 0);
413                    let new_type = match scalable {
414                        true => PartialType::ScalableVector(PartialVectorType {
415                            num_elements,
416                            element_type,
417                        }),
418                        false => PartialType::FixedVector(PartialVectorType {
419                            num_elements,
420                            element_type,
421                        }),
422                    };
423
424                    partial_types.add(new_type);
425                }
426                TypeCode::X86Fp80 => partial_types.add(PartialType::X86Fp80),
427                TypeCode::Fp128 => partial_types.add(PartialType::Fp128),
428                TypeCode::PpcFp128 => partial_types.add(PartialType::PpcFp128),
429                TypeCode::Metadata => partial_types.add(PartialType::Metadata),
430                TypeCode::X86Mmx => partial_types.add(PartialType::X86Mmx),
431                TypeCode::StructAnon => {
432                    let is_packed = type_field!(0) > 0;
433
434                    let field_types = record.fields()[1..]
435                        .iter()
436                        .map(|f| TypeRef(*f as usize))
437                        .collect::<Vec<_>>();
438
439                    partial_types.add(PartialType::Struct(PartialStructType {
440                        name: None,
441                        field_types,
442                        is_packed,
443                    }));
444                }
445                TypeCode::StructName => {
446                    // A `TYPE_CODE_STRUCT_NAME` is not a type in its own right; it merely
447                    // supplies the name for a future type record.
448                    last_type_name.push_str(&record.try_string(0).map_err(MapError::RecordString)?);
449                    continue;
450                }
451                TypeCode::StructNamed => {
452                    // TODO(ww): Should probably be deduped with StructAnon above,
453                    // since they're 90% identical.
454
455                    let is_packed = type_field!(0) > 0;
456
457                    let field_types = record.fields()[1..]
458                        .iter()
459                        .map(|f| TypeRef(*f as usize))
460                        .collect::<Vec<_>>();
461
462                    // Like with opaque types, we might be forward-referenced here.
463                    // If so, we update our pre-existing structure type with its
464                    // correct name and fields.
465                    if let Some(PartialType::Struct(s)) = partial_types.last_mut() {
466                        if s.name.is_some() || !s.field_types.is_empty() {
467                            return Err(MapError::Invalid(
468                                "forward-declared struct type already has name and/or type fields"
469                                    .into(),
470                            )
471                            .into());
472                        }
473
474                        s.name = Some(last_type_name.clone());
475                        s.field_types = field_types;
476                    } else {
477                        partial_types.add(PartialType::Struct(PartialStructType {
478                            name: Some(last_type_name.clone()),
479                            field_types,
480                            is_packed,
481                        }));
482                    }
483
484                    last_type_name.clear();
485                }
486                TypeCode::Function => {
487                    let is_vararg = type_field!(0) > 0;
488                    let return_type = TypeRef(type_field!(1) as usize);
489
490                    let param_types = record.fields()[2..]
491                        .iter()
492                        .map(|f| TypeRef(*f as usize))
493                        .collect::<Vec<_>>();
494
495                    partial_types.add(PartialType::Function(PartialFunctionType {
496                        return_type,
497                        param_types,
498                        is_vararg,
499                    }));
500                }
501                TypeCode::Token => partial_types.add(PartialType::Token),
502                TypeCode::X86Amx => partial_types.add(PartialType::X86Amx),
503                TypeCode::OpaquePointer => {
504                    let address_space = AddressSpace::try_from(type_field!(0)).map_err(|e| {
505                        MapError::Invalid(format!("bad address space in type: {:?}", e))
506                    })?;
507
508                    partial_types.add(PartialType::OpaquePointer(address_space))
509                }
510            }
511        }
512
513        partial_types.reify()
514    }
515}