// SPDX-License-Identifier: Apache-2.0 package firrtl.passes import firrtl._ import firrtl.ir._ import firrtl.Mappers._ import firrtl.PrimOps._ import firrtl.options.Dependency /** Handles dynamic accesses to zero-length vectors. * * @note Removes assignments that use a zero-length vector as a sink * @note Removes signals resulting from accesses to a zero-length vector from attach groups * @note Removes attaches that become degenerate after zero-length-accessor removal * @note Replaces "source" references to elements of zero-length vectors with always-invalid validif */ object ZeroLengthVecs extends Pass { override def prerequisites = Seq(Dependency(PullMuxes), Dependency(ResolveKinds), Dependency(InferTypes), Dependency(ExpandConnects)) override def invalidates(a: Transform) = false // Pass in an expression, not just a type, since it's not possible to generate an expression of // interval type with the type alone unless you declare a component private def replaceWithDontCare(toReplace: Expression): Expression = { val default = toReplace.tpe match { case UIntType(w) => UIntLiteral(0, w) case SIntType(w) => SIntLiteral(0, w) case FixedType(w, p) => FixedLiteral(0, w, p) case it: IntervalType => val zeroType = IntervalType(Closed(0), Closed(0), IntWidth(0)) val zeroLit = DoPrim(AsInterval, Seq(SIntLiteral(0)), Seq(0, 0, 0), zeroType) DoPrim(Clip, Seq(zeroLit, toReplace), Nil, it) } ValidIf(UIntLiteral(0), default, toReplace.tpe) } private def zeroLenDerivedRefLike(expr: Expression): Boolean = (expr, expr.tpe) match { case (_, VectorType(_, 0)) => true case (WSubIndex(e, _, _, _), _) => zeroLenDerivedRefLike(e) case (WSubAccess(e, _, _, _), _) => zeroLenDerivedRefLike(e) case (WSubField(e, _, _, _), _) => zeroLenDerivedRefLike(e) case _ => false } // The connects have all been lowered, so all aggregate-typed expressions are "grounded" by WSubField/WSubAccess/WSubIndex // Map before matching because we want don't-cares to propagate UP expression trees private def dropZeroLenSubAccesses(expr: Expression): Expression = expr match { case _: WSubIndex | _: WSubAccess | _: WSubField => if (zeroLenDerivedRefLike(expr)) replaceWithDontCare(expr) else expr case e => e.map(dropZeroLenSubAccesses) } // Attach semantics: drop all zero-length-derived members of attach group, drop stmt if trivial private def onStmt(stmt: Statement): Statement = stmt match { case Connect(_, sink, _) if zeroLenDerivedRefLike(sink) => EmptyStmt case IsInvalid(_, sink) if zeroLenDerivedRefLike(sink) => EmptyStmt case Attach(info, sinks) => val filtered = Attach(info, sinks.filterNot(zeroLenDerivedRefLike)) if (filtered.exprs.length < 2) EmptyStmt else filtered case s => s.map(onStmt).map(dropZeroLenSubAccesses) } override def run(c: Circuit): Circuit = { c.copy(modules = c.modules.map(m => m.map(onStmt))) } }