vj-codegen 0.1.0

LLVM code generator of the vj SystemVerilog simulator.
//! The syntax trees an assertion is built from (16.8, 16.12): a named sequence
//! or property is declared wherever the designer put it, in a package, a module
//! or an interface of ANOTHER source file than the assertion instantiating it,
//! so its syntax lives in another [`SyntaxTree`]. [`Trees`] is the set of trees
//! of one compilation: [`Trees::syntax`] finds the syntax node of a symbol,
//! expression or assertion expression in whichever tree holds it, and
//! [`key`] is the byte range of a node made unique across the set.
//!
//! The builder keys its per-declaration tables (the formal environment, the
//! dependency chain of 16.8, the local variable homes) by a node's byte range.
//! Two files give two nodes the same range, so a bare range would let a name
//! at offset 120 of the assertion's file resolve against a declaration that
//! happens to span offset 120 of the package's file: [`key`] shifts a node's
//! range by the identity of the tree it is in, so equal keys mean the same node
//! of the same tree.
use std::cell::RefCell;
use std::collections::HashMap;
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
use std::ops::Range;

use sv_lang::{AssertionExpr, Expression, Node, Symbol, SyntaxTree, TimingControl};

/// The syntax trees of one compilation (a single tree for a design in one
/// file). Copyable: it is a borrow.
#[derive(Clone, Copy, Debug)]
pub struct Trees<'t>(&'t [SyntaxTree]);

impl<'t> Trees<'t> {
    /// The trees of `trees`.
    pub fn new(trees: &'t [SyntaxTree]) -> Self {
        Trees(trees)
    }

    /// The syntax node of `x`, from whichever tree holds it; `None` when no tree
    /// does (a node slang made up, such as a default argument or an implicit
    /// declaration).
    pub fn syntax<S: HasSyntax>(self, x: &S) -> Option<Node<'t>> {
        self.0.iter().find_map(|t| x.syntax_in(t))
    }
}

impl<'t> From<&'t SyntaxTree> for Trees<'t> {
    fn from(tree: &'t SyntaxTree) -> Self {
        Trees(std::slice::from_ref(tree))
    }
}

impl<'t> From<&'t [SyntaxTree]> for Trees<'t> {
    fn from(trees: &'t [SyntaxTree]) -> Self {
        Trees(trees)
    }
}

/// The semantic objects that know their syntax node in a given tree: the
/// `syntax(&tree)` each of them has, as one trait for [`Trees::syntax`].
pub trait HasSyntax {
    /// The node in `tree`, or `None` when `tree` is not the one holding it.
    fn syntax_in<'t>(&self, tree: &'t SyntaxTree) -> Option<Node<'t>>;
}

macro_rules! has_syntax {
    ($($ty:ident),*) => {$(
        impl HasSyntax for $ty<'_> {
            fn syntax_in<'t>(&self, tree: &'t SyntaxTree) -> Option<Node<'t>> {
                self.syntax(tree)
            }
        }
    )*};
}
has_syntax!(AssertionExpr, Expression, Symbol, TimingControl);

thread_local! {
    /// The trees seen so far on this thread, by the hash of their root node's
    /// identity, numbered in order of first sight: the first tree is 0, so a
    /// design in one file keeps its plain byte ranges.
    static TREE_IDS: RefCell<HashMap<u64, usize>> = RefCell::new(HashMap::new());
}

/// Bits below which a byte offset lives; the tree number sits above them. A
/// source file of a terabyte is not a design.
const TREE_SHIFT: u32 = 40;

/// `node`'s byte range, unique across every tree of the compilation: the range
/// within its own tree, shifted by the number of that tree.
pub fn key(node: Node<'_>) -> Range<usize> {
    let mut root = node;
    while let Some(parent) = root.parent() {
        root = parent;
    }
    let mut hasher = DefaultHasher::new();
    root.hash(&mut hasher);
    let id = TREE_IDS.with(|ids| {
        let mut ids = ids.borrow_mut();
        let next = ids.len();
        *ids.entry(hasher.finish()).or_insert(next)
    });
    let range = node.byte_range();
    let base = id << TREE_SHIFT;
    base + range.start..base + range.end
}

#[cfg(test)]
mod tests {
    use super::*;
    use sv_lang::kinds::SyntaxKind;
    use sv_lang::{Compilation, Session};

    /// The declarator node of `x` in `tree`.
    fn declarator(tree: &SyntaxTree) -> Node<'_> {
        tree.root()
            .descendants()
            .find(|n| n.kind() == SyntaxKind::Declarator)
            .expect("the fixture declares `x`")
    }

    /// Two files that lay `x` out identically give its declarator the same byte
    /// range in both trees; [`Trees::syntax`] finds each symbol in ITS tree, and
    /// [`key`] tells the two nodes apart while agreeing with itself.
    #[test]
    fn equal_byte_ranges_in_two_files_have_distinct_keys() {
        let session = Session::new();
        let mut comp = Compilation::new(&session).unwrap();
        comp.add_source("module m1; logic x; endmodule\n").unwrap();
        comp.add_source("module m2; logic x; endmodule\n").unwrap();
        let design = comp.compile().unwrap();
        let trees = design.syntax_trees();
        assert_eq!(trees.len(), 2, "one tree per source");
        let set = Trees::new(&trees);
        let decl: Vec<Node<'_>> = trees.iter().map(declarator).collect();
        assert_eq!(
            decl[0].byte_range(),
            decl[1].byte_range(),
            "the layout is the same in both files"
        );
        assert_ne!(key(decl[0]), key(decl[1]), "the keys are not");
        assert_eq!(key(decl[0]), key(decl[0]), "a node's key is stable");

        let found: Vec<Node<'_>> = design
            .top_instances()
            .map(|inst| {
                let x = inst.instance_body().unwrap().find("x").unwrap();
                set.syntax(&x).expect("a symbol of either file has syntax")
            })
            .collect();
        assert_eq!(found.len(), 2);
        for node in &found {
            assert!(
                decl.contains(node),
                "found the declarator of its own tree: {node:?}"
            );
        }
        assert_ne!(found[0], found[1], "each in its own tree");
        assert_ne!(key(found[0]), key(found[1]));
    }

    /// Within one tree, keys differ exactly as the byte ranges do.
    #[test]
    fn keys_within_one_tree_preserve_range_arithmetic() {
        let session = Session::new();
        let tree = session
            .parse("module m; logic x; logic y; endmodule\n")
            .unwrap();
        let decls: Vec<Node<'_>> = tree
            .root()
            .descendants()
            .filter(|n| n.kind() == SyntaxKind::Declarator)
            .collect();
        assert_eq!(decls.len(), 2);
        let (a, b) = (key(decls[0]), key(decls[1]));
        assert_eq!(b.start - a.start, 9, "`logic y` is 9 bytes after `logic x`");
        assert_eq!(a.end - a.start, decls[0].byte_range().len());
    }
}