1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
|
// SPDX-License-Identifier: Apache-2.0
package firrtl.analyses
import firrtl.ir._
import firrtl.passes.MemPortUtils
import firrtl.{InstanceKind, Kind, WDefInstance}
import scala.collection.mutable
/** This trait represents a data structure that stores information
* on all the symbols available in a single firrtl module.
* The module can either be scanned all at once using the
* scanModule helper function from the companion object or
* the SymbolTable can be updated while traversing the module by
* calling the declare method every time a declaration is encountered.
* Different implementations of SymbolTable might want to store different
* information (e.g., only the names without the types) or build
* different indices depending on what information the transform needs.
*/
trait SymbolTable {
// methods that need to be implemented by any Symbol table
def declare(name: String, tpe: Type, kind: Kind): Unit
def declareInstance(name: String, module: String): Unit
// convenience methods
def declare(d: DefInstance): Unit = declareInstance(d.name, d.module)
def declare(d: DefMemory): Unit = declare(d.name, MemPortUtils.memType(d), firrtl.MemKind)
def declare(d: DefNode): Unit = declare(d.name, d.value.tpe, firrtl.NodeKind)
def declare(d: DefWire): Unit = declare(d.name, d.tpe, firrtl.WireKind)
def declare(d: DefRegister): Unit = declare(d.name, d.tpe, firrtl.RegKind)
def declare(d: Port): Unit = declare(d.name, d.tpe, firrtl.PortKind)
}
/** Trusts the type annotation on DefInstance nodes instead of re-deriving the type from
* the module ports which would require global (cross-module) information.
*/
private[firrtl] abstract class LocalSymbolTable extends SymbolTable {
def declareInstance(name: String, module: String): Unit = declare(name, UnknownType, InstanceKind)
override def declare(d: WDefInstance): Unit = declare(d.name, d.tpe, InstanceKind)
}
/** Uses a function to derive instance types from module names */
private[firrtl] abstract class ModuleTypesSymbolTable(moduleTypes: String => Type) extends SymbolTable {
def declareInstance(name: String, module: String): Unit = declare(name, moduleTypes(module), InstanceKind)
}
/** Uses a single buffer. No O(1) access, but deterministic Symbol order. */
private[firrtl] trait WithSeq extends SymbolTable {
private val symbols = mutable.ArrayBuffer[Symbol]()
override def declare(name: String, tpe: Type, kind: Kind): Unit = symbols.append(Sym(name, tpe, kind))
def getSymbols: Iterable[Symbol] = symbols
}
/** Uses a mutable map to provide O(1) access to symbols by name. */
private[firrtl] trait WithMap extends SymbolTable {
private val symbols = mutable.HashMap[String, Symbol]()
override def declare(name: String, tpe: Type, kind: Kind): Unit = {
assert(!symbols.contains(name), s"Symbol $name already declared: ${symbols(name)}")
symbols(name) = Sym(name, tpe, kind)
}
def apply(name: String): Symbol = symbols(name)
def size: Int = symbols.size
}
private case class Sym(name: String, tpe: Type, kind: Kind) extends Symbol
private[firrtl] trait Symbol { def name: String; def tpe: Type; def kind: Kind }
/** only remembers the names of symbols */
private[firrtl] class NamespaceTable extends LocalSymbolTable {
private var names = List[String]()
override def declare(name: String, tpe: Type, kind: Kind): Unit = names = name :: names
def getNames: Seq[String] = names
}
/** Provides convenience methods to populate SymbolTables. */
object SymbolTable {
def scanModule[T <: SymbolTable](m: DefModule, t: T): T = {
implicit val table: T = t
m.foreachPort(table.declare)
m.foreachStmt(scanStatement)
table
}
private def scanStatement(s: Statement)(implicit table: SymbolTable): Unit = s match {
case d: DefInstance => table.declare(d)
case d: DefMemory => table.declare(d)
case d: DefNode => table.declare(d)
case d: DefWire => table.declare(d)
case d: DefRegister => table.declare(d)
// Matches named statements like printf, stop, assert, assume, cover if the name is not empty.
// Empty names are allowed for backwards compatibility reasons and
// indicate that the entity has essentially no name.
case s: IsDeclaration if s.name.nonEmpty => table.declare(s.name, UnknownType, firrtl.UnknownKind)
case other => other.foreachStmt(scanStatement)
}
}
|