device-driver-mir 2.0.0

Internal compiler crate for the device-driver toolkit
Documentation
use std::collections::HashSet;

use device_driver_common::specifiers::{AddressRange, RepeatSource};

use crate::{
    model::{Field, FieldSet, Manifest, Unique, UniqueId},
    passes::{Assumption, Pass},
    search_object,
};
use device_driver_diagnostics::{
    Diagnostics, DynError,
    errors::{FieldAddressExceedsFieldsetSize, FieldAddressNegative, OverlappingFields},
};

/// Validate that the bit ranges of fields fall within the max size and don't have overlap if they're not allowed
pub struct BitRangesValidated;

impl Pass for BitRangesValidated {
    const ASSUMPTIONS_MADE: &[Assumption] = &[
        Assumption::RepeatStrideNonZero,
        Assumption::RepeatEnumRefValid,
        Assumption::NamesUnique,
    ];
    const ASSUMPTIONS_RELEASED: &[Assumption] = &[];

    fn run_pass(
        manifest: &mut Manifest,
        diagnostics: &mut Diagnostics,
    ) -> Result<HashSet<UniqueId>, DynError> {
        let mut removals = HashSet::new();

        for object in manifest.iter_objects() {
            if let Some(field_set) = object.as_field_set() {
                validate_len(field_set, manifest, diagnostics, &mut removals);
                if !field_set.allow_bit_overlap {
                    validate_overlap(field_set, manifest, diagnostics);
                }
            }
        }

        Ok(removals)
    }
}

fn validate_len(
    field_set: &FieldSet,
    manifest: &Manifest,
    diagnostics: &mut Diagnostics,
    removals: &mut HashSet<UniqueId>,
) {
    for field in &field_set.fields {
        let field_len = field.field_address.len();

        if field_len == 0 {
            panic!("A zero-sized field can't be specified");
        }

        let (offset_iter, repeated) = get_repeat_iter(manifest, field);

        let max_repeat_offset = offset_iter.iter().max().unwrap();
        let min_repeat_offset = offset_iter.iter().min().unwrap();

        let max_field_end = i128::from(field.field_address.end) + max_repeat_offset;
        let min_field_start = i128::from(field.field_address.start) + min_repeat_offset;

        if max_field_end >= i128::from(field_set.size_bits()) {
            diagnostics.add(FieldAddressExceedsFieldsetSize {
                address: field.field_address.span,
                max_field_end,
                repeat_offset: repeated.then_some(*max_repeat_offset),
                fieldset_size_bits: field_set.size_bits(),
                fieldset_size_span: field_set.size_bytes.span,
            });
            removals.insert(field.id_with(field_set.id()));
        }

        if min_field_start < 0 {
            diagnostics.add(FieldAddressNegative {
                address: field.field_address.span,
                min_field_start,
                repeat_offset: repeated.then_some(*min_repeat_offset),
                field_set_context: field_set.name.span,
            });
            removals.insert(field.id_with(field_set.id()));
        }
    }
}

fn validate_overlap(field_set: &FieldSet, manifest: &Manifest, diagnostics: &mut Diagnostics) {
    for (i, field) in field_set.fields.iter().enumerate() {
        let (offsets, repeated) = get_repeat_iter(manifest, field);

        'second_field: for second_field in field_set.fields.iter().skip(i + 1) {
            let (second_offsets, second_repeated) = get_repeat_iter(manifest, second_field);

            for offset in &offsets {
                for second_offset in &second_offsets {
                    if ranges_overlap(
                        &field.field_address,
                        *offset,
                        &second_field.field_address,
                        *second_offset,
                    ) {
                        diagnostics.add(OverlappingFields {
                            field_address_1: field.field_address.span,
                            repeat_offset_1: repeated.then_some(*offset),
                            field_address_start_1: i128::from(field.field_address.start) + offset,
                            field_address_end_1: i128::from(field.field_address.end) + offset,
                            field_address_2: second_field.field_address.span,
                            repeat_offset_2: second_repeated.then_some(*second_offset),
                            field_address_start_2: i128::from(second_field.field_address.start)
                                + second_offset,
                            field_address_end_2: i128::from(second_field.field_address.end)
                                + second_offset,

                            field_set_context: field_set.name.span,
                        });

                        continue 'second_field;
                    }
                }
            }
        }
    }
}

fn ranges_overlap(l: &AddressRange, offset: i128, r: &AddressRange, second_offset: i128) -> bool {
    (i128::from(l.start) + offset) <= (i128::from(r.end) + second_offset)
        && (i128::from(r.start) + second_offset) <= (i128::from(l.end) + offset)
}

fn get_repeat_iter(manifest: &Manifest, field: &Field) -> (Vec<i128>, bool) {
    if let Some(repeat) = &field.repeat {
        let stride = repeat.stride;
        match &repeat.source.value {
            RepeatSource::Count(count) => (
                (0..i128::from(count.get()))
                    .map(move |count| count * stride.value)
                    .collect(),
                true,
            ),
            RepeatSource::Enum(enum_name) => (
                search_object(manifest, enum_name)
                    .expect("Checked in earlier pass")
                    .as_enum()
                    .expect("Checked in earlier pass")
                    .iter_variants_with_discriminant()
                    .map(move |(discriminant, _)| discriminant * stride.value)
                    .collect(),
                true,
            ),
        }
    } else {
        (vec![0], false)
    }
}

#[cfg(test)]
mod tests {
    use std::num::NonZero;

    use device_driver_common::{
        identifier::Identifier,
        span::{Span, SpanExt},
        specifiers::Repeat,
    };

    use crate::model::{Device, Field, Object};

    use super::*;

    #[test]
    fn max_len_exceeded() {
        let mut start_mir = Device {
            description: String::new(),
            name: Identifier::try_parse("Device").unwrap().with_dummy_span(),
            objects: vec![Object::FieldSet(FieldSet {
                name: Identifier::try_parse("MyReg").unwrap().with_dummy_span(),
                size_bytes: 1.with_dummy_span(),
                fields: vec![Field {
                    name: Identifier::try_parse("my_field").unwrap().with_dummy_span(),
                    field_address: AddressRange { start: 0, end: 7 }.with_dummy_span(),
                    ..Default::default()
                }],
                ..Default::default()
            })],
            ..Default::default()
        }
        .into();

        let mut diagnostics = Diagnostics::new();
        BitRangesValidated::run_pass(&mut start_mir, &mut diagnostics).unwrap();
        assert!(!diagnostics.has_error());

        let mut start_mir = Device {
            description: String::new(),
            name: Identifier::try_parse("Device").unwrap().with_dummy_span(),
            objects: vec![Object::FieldSet(FieldSet {
                name: Identifier::try_parse("MyReg").unwrap().with_dummy_span(),
                size_bytes: 1.with_dummy_span(),
                fields: vec![Field {
                    name: Identifier::try_parse("my_field").unwrap().with_dummy_span(),
                    field_address: AddressRange { start: 0, end: 8 }.with_dummy_span(),
                    ..Default::default()
                }],
                ..Default::default()
            })],
            ..Default::default()
        }
        .into();

        let mut diagnostics = Diagnostics::new();
        BitRangesValidated::run_pass(&mut start_mir, &mut diagnostics).unwrap();
        assert!(diagnostics.has_error());

        let mut start_mir = Device {
            description: String::new(),
            name: Identifier::try_parse("Device").unwrap().with_dummy_span(),
            objects: vec![Object::FieldSet(FieldSet {
                name: Identifier::try_parse("MyReg").unwrap().with_dummy_span(),
                size_bytes: 1.with_dummy_span(),
                fields: vec![Field {
                    name: Identifier::try_parse("my_field").unwrap().with_dummy_span(),
                    field_address: AddressRange { start: 0, end: 4 }.with_dummy_span(),
                    repeat: Some(Repeat {
                        source: RepeatSource::Count(NonZero::new(3).unwrap()).with_dummy_span(),
                        stride: 5.with_dummy_span(),
                        span: Span::empty(),
                    }),
                    ..Default::default()
                }],
                ..Default::default()
            })],
            ..Default::default()
        }
        .into();

        let mut diagnostics = Diagnostics::new();
        BitRangesValidated::run_pass(&mut start_mir, &mut diagnostics).unwrap();
        assert!(diagnostics.has_error());
    }

    #[test]
    fn overlap() {
        let mut start_mir = Device {
            description: String::new(),
            name: Identifier::try_parse("Device").unwrap().with_dummy_span(),
            objects: vec![Object::FieldSet(FieldSet {
                name: Identifier::try_parse("MyReg").unwrap().with_dummy_span(),
                size_bytes: 2.with_dummy_span(),
                fields: vec![
                    Field {
                        name: Identifier::try_parse("my_field").unwrap().with_dummy_span(),
                        field_address: AddressRange { start: 0, end: 4 }.with_dummy_span(),
                        ..Default::default()
                    },
                    Field {
                        name: Identifier::try_parse("my_field2")
                            .unwrap()
                            .with_dummy_span(),
                        field_address: AddressRange { start: 5, end: 9 }.with_dummy_span(),
                        ..Default::default()
                    },
                ],
                ..Default::default()
            })],
            ..Default::default()
        }
        .into();

        let mut diagnostics = Diagnostics::new();
        BitRangesValidated::run_pass(&mut start_mir, &mut diagnostics).unwrap();
        assert!(!diagnostics.has_error());

        let mut start_mir = Device {
            description: String::new(),
            name: Identifier::try_parse("Device").unwrap().with_dummy_span(),
            objects: vec![Object::FieldSet(FieldSet {
                name: Identifier::try_parse("MyReg").unwrap().with_dummy_span(),
                size_bytes: 2.with_dummy_span(),
                allow_bit_overlap: true,
                fields: vec![
                    Field {
                        name: Identifier::try_parse("my_field").unwrap().with_dummy_span(),
                        field_address: AddressRange { start: 0, end: 5 }.with_dummy_span(),
                        ..Default::default()
                    },
                    Field {
                        name: Identifier::try_parse("my_field2")
                            .unwrap()
                            .with_dummy_span(),
                        field_address: AddressRange { start: 5, end: 9 }.with_dummy_span(),
                        ..Default::default()
                    },
                ],
                ..Default::default()
            })],
            ..Default::default()
        }
        .into();

        let mut diagnostics = Diagnostics::new();
        BitRangesValidated::run_pass(&mut start_mir, &mut diagnostics).unwrap();
        assert!(!diagnostics.has_error());

        let mut start_mir = Device {
            description: String::new(),
            name: Identifier::try_parse("Device").unwrap().with_dummy_span(),
            objects: vec![Object::FieldSet(FieldSet {
                name: Identifier::try_parse("MyReg").unwrap().with_dummy_span(),
                size_bytes: 2.with_dummy_span(),
                fields: vec![
                    Field {
                        name: Identifier::try_parse("my_field").unwrap().with_dummy_span(),
                        field_address: AddressRange { start: 0, end: 5 }.with_dummy_span(),
                        ..Default::default()
                    },
                    Field {
                        name: Identifier::try_parse("my_field2")
                            .unwrap()
                            .with_dummy_span(),
                        field_address: AddressRange { start: 5, end: 9 }.with_dummy_span(),
                        ..Default::default()
                    },
                ],
                ..Default::default()
            })],
            ..Default::default()
        }
        .into();

        let mut diagnostics = Diagnostics::new();
        BitRangesValidated::run_pass(&mut start_mir, &mut diagnostics).unwrap();
        assert!(!diagnostics.has_error());
        assert!(!diagnostics.is_empty());

        let mut start_mir = Device {
            description: String::new(),
            name: Identifier::try_parse("Device").unwrap().with_dummy_span(),
            objects: vec![Object::FieldSet(FieldSet {
                name: Identifier::try_parse("MyReg").unwrap().with_dummy_span(),
                size_bytes: 2.with_dummy_span(),
                fields: vec![
                    Field {
                        name: Identifier::try_parse("my_field").unwrap().with_dummy_span(),
                        field_address: AddressRange { start: 0, end: 0 }.with_dummy_span(),
                        repeat: Some(Repeat {
                            source: RepeatSource::Count(NonZero::new(6).unwrap()).with_dummy_span(),
                            stride: 1.with_dummy_span(),
                            span: Span::empty(),
                        }),
                        ..Default::default()
                    },
                    Field {
                        name: Identifier::try_parse("my_field2")
                            .unwrap()
                            .with_dummy_span(),
                        field_address: AddressRange { start: 5, end: 9 }.with_dummy_span(),
                        ..Default::default()
                    },
                ],
                ..Default::default()
            })],
            ..Default::default()
        }
        .into();

        let mut diagnostics = Diagnostics::new();
        BitRangesValidated::run_pass(&mut start_mir, &mut diagnostics).unwrap();
        assert!(!diagnostics.has_error());
        assert!(!diagnostics.is_empty());
    }
}