diff options
Diffstat (limited to 'macros/src/syntax/analyze.rs')
-rw-r--r-- | macros/src/syntax/analyze.rs | 417 |
1 files changed, 0 insertions, 417 deletions
diff --git a/macros/src/syntax/analyze.rs b/macros/src/syntax/analyze.rs deleted file mode 100644 index 3ed14877..00000000 --- a/macros/src/syntax/analyze.rs +++ /dev/null @@ -1,417 +0,0 @@ -//! RTIC application analysis - -use core::cmp; -use std::collections::{BTreeMap, BTreeSet, HashMap}; - -use indexmap::{IndexMap, IndexSet}; -use syn::{Ident, Type}; - -use crate::syntax::{ - ast::{App, LocalResources, TaskLocal}, - Set, -}; - -pub(crate) fn app(app: &App) -> Result<Analysis, syn::Error> { - // Collect all tasks into a vector - type TaskName = Ident; - type Priority = u8; - - // The task list is a Tuple (Name, Shared Resources, Local Resources, Priority) - let task_resources_list: Vec<(TaskName, Vec<&Ident>, &LocalResources, Priority)> = - Some(&app.init) - .iter() - .map(|ht| (ht.name.clone(), Vec::new(), &ht.args.local_resources, 0)) - .chain(app.idle.iter().map(|ht| { - ( - ht.name.clone(), - ht.args - .shared_resources - .iter() - .map(|(v, _)| v) - .collect::<Vec<_>>(), - &ht.args.local_resources, - 0, - ) - })) - .chain(app.software_tasks.iter().map(|(name, ht)| { - ( - name.clone(), - ht.args - .shared_resources - .iter() - .map(|(v, _)| v) - .collect::<Vec<_>>(), - &ht.args.local_resources, - ht.args.priority, - ) - })) - .chain(app.hardware_tasks.iter().map(|(name, ht)| { - ( - name.clone(), - ht.args - .shared_resources - .iter() - .map(|(v, _)| v) - .collect::<Vec<_>>(), - &ht.args.local_resources, - ht.args.priority, - ) - })) - .collect(); - - let mut error = vec![]; - let mut lf_res_with_error = vec![]; - let mut lf_hash = HashMap::new(); - - // Collect lock free resources - let lock_free: Vec<&Ident> = app - .shared_resources - .iter() - .filter(|(_, r)| r.properties.lock_free) - .map(|(i, _)| i) - .collect(); - - // Check that lock_free resources are correct - for lf_res in lock_free.iter() { - for (task, tr, _, priority) in task_resources_list.iter() { - for r in tr { - // Get all uses of resources annotated lock_free - if lf_res == r { - // Check so async tasks do not use lock free resources - if app.software_tasks.get(task).is_some() { - error.push(syn::Error::new( - r.span(), - format!( - "Lock free shared resource {:?} is used by an async tasks, which is forbidden", - r.to_string(), - ), - )); - } - - // HashMap returns the previous existing object if old.key == new.key - if let Some(lf_res) = lf_hash.insert(r.to_string(), (task, r, priority)) { - // Check if priority differ, if it does, append to - // list of resources which will be annotated with errors - if priority != lf_res.2 { - lf_res_with_error.push(lf_res.1); - lf_res_with_error.push(r); - } - - // If the resource already violates lock free properties - if lf_res_with_error.contains(&r) { - lf_res_with_error.push(lf_res.1); - lf_res_with_error.push(r); - } - } - } - } - } - } - - // Add error message in the resource struct - for r in lock_free { - if lf_res_with_error.contains(&&r) { - error.push(syn::Error::new( - r.span(), - format!( - "Lock free shared resource {:?} is used by tasks at different priorities", - r.to_string(), - ), - )); - } - } - - // Add error message for each use of the shared resource - for resource in lf_res_with_error.clone() { - error.push(syn::Error::new( - resource.span(), - format!( - "Shared resource {:?} is declared lock free but used by tasks at different priorities", - resource.to_string(), - ), - )); - } - - // Collect local resources - let local: Vec<&Ident> = app.local_resources.iter().map(|(i, _)| i).collect(); - - let mut lr_with_error = vec![]; - let mut lr_hash = HashMap::new(); - - // Check that local resources are not shared - for lr in local { - for (task, _, local_resources, _) in task_resources_list.iter() { - for (name, res) in local_resources.iter() { - // Get all uses of resources annotated lock_free - if lr == name { - match res { - TaskLocal::External => { - // HashMap returns the previous existing object if old.key == new.key - if let Some(lr) = lr_hash.insert(name.to_string(), (task, name)) { - lr_with_error.push(lr.1); - lr_with_error.push(name); - } - } - // If a declared local has the same name as the `#[local]` struct, it's an - // direct error - TaskLocal::Declared(_) => { - lr_with_error.push(lr); - lr_with_error.push(name); - } - } - } - } - } - } - - // Add error message for each use of the local resource - for resource in lr_with_error.clone() { - error.push(syn::Error::new( - resource.span(), - format!( - "Local resource {:?} is used by multiple tasks or collides with multiple definitions", - resource.to_string(), - ), - )); - } - - // Check 0-priority async software tasks and idle dependency - for (name, task) in &app.software_tasks { - if task.args.priority == 0 { - // If there is a 0-priority task, there must be no idle - if app.idle.is_some() { - error.push(syn::Error::new( - name.span(), - format!( - "Async task {:?} has priority 0, but `#[idle]` is defined. 0-priority async tasks are only allowed if there is no `#[idle]`.", - name.to_string(), - ) - )); - } - } - } - - // Collect errors if any and return/halt - if !error.is_empty() { - let mut err = error.get(0).unwrap().clone(); - error.iter().for_each(|e| err.combine(e.clone())); - return Err(err); - } - - // e. Location of resources - let mut used_shared_resource = IndexSet::new(); - let mut ownerships = Ownerships::new(); - let mut sync_types = SyncTypes::new(); - for (prio, name, access) in app.shared_resource_accesses() { - let res = app.shared_resources.get(name).expect("UNREACHABLE"); - - // (e) - // This shared resource is used - used_shared_resource.insert(name.clone()); - - // (c) - if let Some(priority) = prio { - if let Some(ownership) = ownerships.get_mut(name) { - match *ownership { - Ownership::Owned { priority: ceiling } - | Ownership::CoOwned { priority: ceiling } - | Ownership::Contended { ceiling } - if priority != ceiling => - { - *ownership = Ownership::Contended { - ceiling: cmp::max(ceiling, priority), - }; - - if access.is_shared() { - sync_types.insert(res.ty.clone()); - } - } - - Ownership::Owned { priority: ceil } if ceil == priority => { - *ownership = Ownership::CoOwned { priority }; - } - - _ => {} - } - } else { - ownerships.insert(name.clone(), Ownership::Owned { priority }); - } - } - } - - // Create the list of used local resource Idents - let mut used_local_resource = IndexSet::new(); - - for (_, _, locals, _) in task_resources_list { - for (local, _) in locals { - used_local_resource.insert(local.clone()); - } - } - - // Most shared resources need to be `Send`, only 0 prio does not need it - let mut send_types = SendTypes::new(); - - for (name, res) in app.shared_resources.iter() { - if ownerships - .get(name) - .map(|ownership| match *ownership { - Ownership::Owned { priority: ceiling } - | Ownership::CoOwned { priority: ceiling } - | Ownership::Contended { ceiling } => ceiling != 0, - }) - .unwrap_or(false) - { - send_types.insert(res.ty.clone()); - } - } - - // Most local resources need to be `Send` as well, only 0 prio does not need it - for (name, res) in app.local_resources.iter() { - if ownerships - .get(name) - .map(|ownership| match *ownership { - Ownership::Owned { priority: ceiling } - | Ownership::CoOwned { priority: ceiling } - | Ownership::Contended { ceiling } => ceiling != 0, - }) - .unwrap_or(false) - { - send_types.insert(res.ty.clone()); - } - } - - let mut channels = Channels::new(); - - for (name, spawnee) in &app.software_tasks { - let spawnee_prio = spawnee.args.priority; - - let channel = channels.entry(spawnee_prio).or_default(); - channel.tasks.insert(name.clone()); - - // All inputs are send as we do not know from where they may be spawned. - spawnee.inputs.iter().for_each(|input| { - send_types.insert(input.ty.clone()); - }); - } - - // No channel should ever be empty - debug_assert!(channels.values().all(|channel| !channel.tasks.is_empty())); - - Ok(Analysis { - channels, - shared_resources: used_shared_resource, - local_resources: used_local_resource, - ownerships, - send_types, - sync_types, - }) -} - -// /// Priority ceiling -// pub type Ceiling = Option<u8>; - -/// Task priority -pub type Priority = u8; - -/// Resource name -pub type Resource = Ident; - -/// Task name -pub type Task = Ident; - -/// The result of analyzing an RTIC application -pub struct Analysis { - /// SPSC message channels - pub channels: Channels, - - /// Shared resources - /// - /// If a resource is not listed here it means that's a "dead" (never - /// accessed) resource and the backend should not generate code for it - pub shared_resources: UsedSharedResource, - - /// Local resources - /// - /// If a resource is not listed here it means that's a "dead" (never - /// accessed) resource and the backend should not generate code for it - pub local_resources: UsedLocalResource, - - /// Resource ownership - pub ownerships: Ownerships, - - /// These types must implement the `Send` trait - pub send_types: SendTypes, - - /// These types must implement the `Sync` trait - pub sync_types: SyncTypes, -} - -/// All channels, keyed by dispatch priority -pub type Channels = BTreeMap<Priority, Channel>; - -/// Location of all *used* shared resources -pub type UsedSharedResource = IndexSet<Resource>; - -/// Location of all *used* local resources -pub type UsedLocalResource = IndexSet<Resource>; - -/// Resource ownership -pub type Ownerships = IndexMap<Resource, Ownership>; - -/// These types must implement the `Send` trait -pub type SendTypes = Set<Box<Type>>; - -/// These types must implement the `Sync` trait -pub type SyncTypes = Set<Box<Type>>; - -/// A channel used to send messages -#[derive(Debug, Default)] -pub struct Channel { - /// The channel capacity - pub capacity: u8, - - /// Tasks that can be spawned on this channel - pub tasks: BTreeSet<Task>, -} - -/// Resource ownership -#[derive(Clone, Copy, Debug, PartialEq)] -pub enum Ownership { - /// Owned by a single task - Owned { - /// Priority of the task that owns this resource - priority: u8, - }, - - /// "Co-owned" by more than one task; all of them have the same priority - CoOwned { - /// Priority of the tasks that co-own this resource - priority: u8, - }, - - /// Contended by more than one task; the tasks have different priorities - Contended { - /// Priority ceiling - ceiling: u8, - }, -} - -// impl Ownership { -// /// Whether this resource needs to a lock at this priority level -// pub fn needs_lock(&self, priority: u8) -> bool { -// match self { -// Ownership::Owned { .. } | Ownership::CoOwned { .. } => false, -// -// Ownership::Contended { ceiling } => { -// debug_assert!(*ceiling >= priority); -// -// priority < *ceiling -// } -// } -// } -// -// /// Whether this resource is exclusively owned -// pub fn is_owned(&self) -> bool { -// matches!(self, Ownership::Owned { .. }) -// } -// } |