moore-svlog 0.14.0

The SystemVerilog implementation of the moore compiler framework.
// Copyright (c) 2016-2021 Fabian Schuiki

//! A collection if instantiation details.

#[warn(missing_docs)]
use crate::{
    crate_prelude::*,
    hir::{self, HirNode},
    port_list::AsPortedNode,
    resolver::InstTarget,
    Context, ParamEnv, ParamEnvData, ParamEnvSource, PortMapping,
};
use std::sync::Arc;

/// Instantiation details
///
/// This struct bundles all the information associated with an instantiation,
/// most importantly the parameter bindings and port connections.
///
/// This corresponds to the `bar(y)` in `foo #(x) bar(y);`.
#[derive(Debug)]
pub struct InstDetails<'a> {
    /// The HIR instantiation.
    pub hir: &'a hir::Inst<'a>,
    /// The target details.
    pub target: Arc<InstTargetDetails<'a>>,
    /// The port connections.
    pub ports: Arc<PortMapping<'a>>,
    /// The parameter environment generated by the instantiation, including any
    /// implicit parameters due to interface ports.
    pub inner_env: ParamEnv,
}

/// Instantiation target details
///
/// This struct bundles all the information associated with an instantiation
/// target, most importantly the parameter bindings.
///
/// This corresponds to the `foo #(x)` in `foo #(x) bar(y);`.
#[derive(Debug)]
pub struct InstTargetDetails<'a> {
    /// The HIR instantiation target.
    pub hir: &'a hir::InstTarget<'a>,
    /// The instantiated node.
    pub kind: InstTarget<'a>,
    /// The parameter environment around the instantiation.
    pub outer_env: ParamEnv,
    /// The parameter environment generated by the instantiation.
    pub inner_env: ParamEnv,
    /// The parameter bindings.
    pub params: &'a ParamEnvData<'a>,
}

/// Compute the details of an instantiation.
#[moore_derive::query]
pub(crate) fn inst_details<'a>(
    cx: &impl Context<'a>,
    Ref(inst): Ref<'a, hir::Inst<'a>>,
    env: ParamEnv,
) -> Result<Arc<InstDetails<'a>>> {
    // Look up the HIR of the instantiation.
    let inst_target = match cx.hir_of(inst.target)? {
        HirNode::InstTarget(x) => x,
        _ => unreachable!(),
    };

    // Determine the details of the instantiation target.
    let target = cx.inst_target_details(Ref(inst_target), env)?;

    // Determine the port connections of the instantiations. Connections
    // are made to the module's external ports, and must later be mapped
    // to the actual internal ports in a second step.
    let port_mapping = cx.port_mapping(
        target.kind.as_any().as_all().get_ported().unwrap(),
        target.outer_env,
        target.inner_env,
        Ref(inst),
        &inst.pos_ports,
        &inst.named_ports,
        inst.has_wildcard_port,
    )?;

    // Derive additional parametrization for the interface ports of the
    // instantiated module, if any.
    trace!("Deriving additional parametrization due to interfaces");
    let mut intf_params = vec![];
    for &(ext_port, assigned) in port_mapping.0.iter() {
        // Check if this is a simple 1:1 mapping from external to internal port,
        // which is the only possibility for interfaces.
        let port_list = cx.canonicalize_ports(ext_port.node);
        let int_port = match ext_port.exprs.as_slice() {
            &[ref expr] if expr.selects.is_empty() => &port_list.int[expr.port],
            _ => continue,
        };
        let data = match int_port.data {
            Some(ref x) => x,
            None => continue,
        };

        // Check if the port is actually an interface.
        let ty = cx.packed_type_from_ast(Ref(data.ty), target.inner_env, None);
        if ty.get_interface().is_none() {
            continue;
        }

        // Add this parametrization.
        trace!(
            " - Adding indirect {:?} for interface `{}` port `{}`",
            assigned,
            ty,
            int_port.name
        );
        intf_params.push((int_port.id, assigned));
    }

    // If we have found any additional parametrization, create an extended
    // parameter environment for this instance.
    let inner_env = if !intf_params.is_empty() {
        let mut params = target.params.clone();
        params.add_interfaces(intf_params);
        trace!(
            "Extended parametrization with implicit interface parameters: {:?}",
            params
        );
        cx.intern_param_env(params)
    } else {
        target.inner_env
    };

    // Wrap everything up.
    Ok(Arc::new(InstDetails {
        hir: inst,
        target: target,
        ports: port_mapping,
        inner_env,
    }))
}

/// Compute the details of an instantiated module or interface.
#[moore_derive::query]
pub(crate) fn inst_target_details<'a>(
    cx: &impl Context<'a>,
    Ref(inst_target): Ref<'a, hir::InstTarget<'a>>,
    env: ParamEnv,
) -> Result<Arc<InstTargetDetails<'a>>> {
    // Resolve the instantiation target.
    let target = cx.resolve_inst_target(inst_target.ast)?;

    // Create a new parameter environment that is generated by the
    // parametrization of this instance.
    let inst_env = cx.param_env(match target {
        resolver::InstTarget::Module(node) => ParamEnvSource::ModuleInst {
            module: Ref(cx.hir_of_module(node)?),
            env,
            pos: &inst_target.pos_params,
            named: &inst_target.named_params,
        },
        resolver::InstTarget::Interface(node) => ParamEnvSource::InterfaceInst {
            interface: Ref(cx.hir_of_interface(node)?),
            env,
            pos: &inst_target.pos_params,
            named: &inst_target.named_params,
        },
    })?;
    let inst_env_data = cx.param_env_data(inst_env);

    // Wrap everything up.
    Ok(Arc::new(InstTargetDetails {
        hir: inst_target,
        kind: target,
        outer_env: env,
        inner_env: inst_env,
        params: inst_env_data,
    }))
}

/// A visitor that emits instantiation details diagnostics.
pub struct InstVerbosityVisitor<'a, 'gcx> {
    cx: &'a GlobalContext<'gcx>,
    env: ParamEnv,
}

impl<'a, 'gcx> InstVerbosityVisitor<'a, 'gcx> {
    /// Create a new visitor that emits instantiation details.
    pub fn new(cx: &'a GlobalContext<'gcx>) -> Self {
        Self {
            cx,
            env: cx.default_param_env(),
        }
    }
}

impl<'a, 'gcx> hir::Visitor<'gcx> for InstVerbosityVisitor<'a, 'gcx> {
    type Context = GlobalContext<'gcx>;

    fn context(&self) -> &Self::Context {
        self.cx
    }

    fn visit_inst(&mut self, hir: &'gcx hir::Inst<'gcx>) {
        let details = match self.cx.inst_details(Ref(hir), self.env) {
            Ok(x) => x,
            Err(()) => return,
        };
        self.cx.emit(
            DiagBuilder2::note("instantiation details")
                .span(hir.name.span)
                .add_note(format!("{:#?}", details)),
        );
        Self {
            cx: self.cx,
            env: details.inner_env,
        }
        .visit_node_with_id(details.target.kind.as_any().id(), false);
    }
}