From db40efbcdc7275df4ba67dd455a53f5342ae1ce4 Mon Sep 17 00:00:00 2001 From: Andrew Lenharth Date: Thu, 9 Feb 2023 06:37:04 -0700 Subject: [PATCH] [FIRRTL] Simple points-to like analysis (#4637) This analysis maps values back to their definitions (operations which produce a new value) and provides an indexing path in the original definition to produce the current value. Subaccesses produce a -1 (wildcard) in the path. The analysis provides the user with a stable summary node for each value which is suitable to use in other data-structures. wire a : firrtl.vector, 4> => definition, is a root (empty path) v = subindex(a, 2) => derived from a with path {2} w = subfield(v, f2) => derived from a with path {2, 1} --- .../circt/Dialect/FIRRTL/FIRRTLFieldSource.h | 58 +++++++++++ include/circt/Dialect/FIRRTL/Passes.h | 2 + include/circt/Dialect/FIRRTL/Passes.td | 6 ++ lib/Dialect/FIRRTL/CMakeLists.txt | 1 + lib/Dialect/FIRRTL/FIRRTLFieldSource.cpp | 96 +++++++++++++++++++ lib/Dialect/FIRRTL/Transforms/CMakeLists.txt | 1 + .../Transforms/PrintFIRRTLFieldSource.cpp | 65 +++++++++++++ 7 files changed, 229 insertions(+) create mode 100644 include/circt/Dialect/FIRRTL/FIRRTLFieldSource.h create mode 100644 lib/Dialect/FIRRTL/FIRRTLFieldSource.cpp create mode 100644 lib/Dialect/FIRRTL/Transforms/PrintFIRRTLFieldSource.cpp diff --git a/include/circt/Dialect/FIRRTL/FIRRTLFieldSource.h b/include/circt/Dialect/FIRRTL/FIRRTLFieldSource.h new file mode 100644 index 000000000000..5ef2a501fdc7 --- /dev/null +++ b/include/circt/Dialect/FIRRTL/FIRRTLFieldSource.h @@ -0,0 +1,58 @@ +//===- FIRRTLFieldSource.h - Field Source (points-to) -----------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines the FIRRTL Field Source Analysis, which is a points-to +// analysis which identifies the source field and storage of any value derived +// from a storage location. Values derived from reads of storage locations do +// not appear in this analysis. +// +//===----------------------------------------------------------------------===// + +#ifndef CIRCT_DIALECT_FIRRTL_FIRRTLFIELDSOURCE_H +#define CIRCT_DIALECT_FIRRTL_FIRRTLFIELDSOURCE_H + +#include "circt/Dialect/FIRRTL/FIRRTLOps.h" +#include "circt/Support/LLVM.h" + +namespace circt { +namespace firrtl { + +/// To use this class, retrieve a cached copy from the analysis manager: +/// auto &fieldsource = getAnalysis(getOperation()); +class FieldSource { + +public: + explicit FieldSource(Operation *operation); + + struct PathNode { + PathNode(Value src, ArrayRef ar) : src(src), path(ar) {} + Value src; + SmallVector path; + + bool isRoot() const { return path.empty(); } + }; + + const PathNode *nodeForValue(Value v) const; + +private: + void visitOp(Operation *op); + void visitSubfield(SubfieldOp sf); + void visitSubindex(SubindexOp si); + void visitSubaccess(SubaccessOp sa); + void visitMem(MemOp mem); + void visitInst(InstanceOp inst); + + void makeNodeForValue(Value dst, Value src, ArrayRef path); + + DenseMap paths; +}; + +} // namespace firrtl +} // namespace circt + +#endif // CIRCT_DIALECT_FIRRTL_FIRRTLFIELDSOURCE_H diff --git a/include/circt/Dialect/FIRRTL/Passes.h b/include/circt/Dialect/FIRRTL/Passes.h index f061099aa220..39c37af37625 100644 --- a/include/circt/Dialect/FIRRTL/Passes.h +++ b/include/circt/Dialect/FIRRTL/Passes.h @@ -96,6 +96,8 @@ createMemToRegOfVecPass(bool replSeqMem = false, bool ignoreReadEnable = false); std::unique_ptr createPrefixModulesPass(); +std::unique_ptr createFIRRTLFieldSourcePass(); + std::unique_ptr createPrintInstanceGraphPass(); std::unique_ptr createPrintNLATablePass(); diff --git a/include/circt/Dialect/FIRRTL/Passes.td b/include/circt/Dialect/FIRRTL/Passes.td index 495599dcc321..a164496f407f 100644 --- a/include/circt/Dialect/FIRRTL/Passes.td +++ b/include/circt/Dialect/FIRRTL/Passes.td @@ -402,6 +402,12 @@ def PrefixModules : Pass<"firrtl-prefix-modules", "firrtl::CircuitOp"> { let constructor = "circt::firrtl::createPrefixModulesPass()"; } +def PrintFIRRTLFieldSourcePass + : Pass<"firrtl-print-field-source", "firrtl::FModuleOp"> { + let summary = "Print field source information."; + let constructor = "circt::firrtl::createFIRRTLFieldSourcePass()"; +} + def PrintInstanceGraph : Pass<"firrtl-print-instance-graph", "firrtl::CircuitOp"> { let summary = "Print a DOT graph of the module hierarchy."; diff --git a/lib/Dialect/FIRRTL/CMakeLists.txt b/lib/Dialect/FIRRTL/CMakeLists.txt index 443e97b47e61..296e0dbc5d93 100644 --- a/lib/Dialect/FIRRTL/CMakeLists.txt +++ b/lib/Dialect/FIRRTL/CMakeLists.txt @@ -5,6 +5,7 @@ add_circt_dialect_library(CIRCTFIRRTL FIRRTLAnnotations.cpp FIRRTLAttributes.cpp FIRRTLDialect.cpp + FIRRTLFieldSource.cpp FIRRTLFolds.cpp FIRRTLInstanceGraph.cpp FIRRTLOpInterfaces.cpp diff --git a/lib/Dialect/FIRRTL/FIRRTLFieldSource.cpp b/lib/Dialect/FIRRTL/FIRRTLFieldSource.cpp new file mode 100644 index 000000000000..7fc0893aba27 --- /dev/null +++ b/lib/Dialect/FIRRTL/FIRRTLFieldSource.cpp @@ -0,0 +1,96 @@ +//===- FIRRTLFieldSource.cpp - Field Source Analysis ------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines a basic points-to like analysis. +// +//===----------------------------------------------------------------------===// + +#include "circt/Dialect/FIRRTL/FIRRTLFieldSource.h" + +using namespace circt; +using namespace firrtl; + +FieldSource::FieldSource(Operation *operation) { + FModuleOp mod = cast(operation); + for (auto port : mod.getBodyBlock()->getArguments()) + if (auto ft = dyn_cast(port.getType())) + if (!ft.isGround()) + makeNodeForValue(port, port, {}); + for (auto &op : *mod.getBodyBlock()) + visitOp(&op); +} + +void FieldSource::visitOp(Operation *op) { + if (auto sf = dyn_cast(op)) + return visitSubfield(sf); + if (auto si = dyn_cast(op)) + return visitSubindex(si); + if (auto sa = dyn_cast(op)) + return visitSubaccess(sa); + if (isa(op)) + return makeNodeForValue(op->getResult(0), op->getResult(0), {}); + if (auto mem = dyn_cast(op)) + return visitMem(mem); + if (auto inst = dyn_cast(op)) + return visitInst(inst); + // recurse in to regions + for (auto &r : op->getRegions()) + for (auto &b : r.getBlocks()) + for (auto &op : b) + visitOp(&op); +} + +void FieldSource::visitSubfield(SubfieldOp sf) { + auto value = sf.getInput(); + const auto *node = nodeForValue(value); + assert(node && "node should be in the map"); + auto sv = node->path; + sv.push_back(sf.getFieldIndex()); + makeNodeForValue(sf.getResult(), node->src, sv); +} + +void FieldSource::visitSubindex(SubindexOp si) { + auto value = si.getInput(); + const auto *node = nodeForValue(value); + assert(node && "node should be in the map"); + auto sv = node->path; + sv.push_back(si.getIndex()); + makeNodeForValue(si.getResult(), node->src, sv); +} + +void FieldSource::visitSubaccess(SubaccessOp sa) { + auto value = sa.getInput(); + const auto *node = nodeForValue(value); + assert(node && "node should be in the map"); + auto sv = node->path; + sv.push_back(-1); + makeNodeForValue(sa.getResult(), node->src, sv); +} + +void FieldSource::visitMem(MemOp mem) { + for (auto r : mem.getResults()) + makeNodeForValue(r, r, {}); +} + +void FieldSource::visitInst(InstanceOp inst) { + for (auto r : inst.getResults()) + makeNodeForValue(r, r, {}); +} + +const FieldSource::PathNode *FieldSource::nodeForValue(Value v) const { + auto ii = paths.find(v); + if (ii == paths.end()) + return nullptr; + return &ii->second; +} + +void FieldSource::makeNodeForValue(Value dst, Value src, + ArrayRef path) { + auto ii = paths.try_emplace(dst, src, path); + assert(ii.second && "Double insert into the map"); +} diff --git a/lib/Dialect/FIRRTL/Transforms/CMakeLists.txt b/lib/Dialect/FIRRTL/Transforms/CMakeLists.txt index 1404a0ab9c2a..2762e3f2e2d9 100755 --- a/lib/Dialect/FIRRTL/Transforms/CMakeLists.txt +++ b/lib/Dialect/FIRRTL/Transforms/CMakeLists.txt @@ -29,6 +29,7 @@ add_circt_dialect_library(CIRCTFIRRTLTransforms MemToRegOfVec.cpp ModuleInliner.cpp PrefixModules.cpp + PrintFIRRTLFieldSource.cpp PrintInstanceGraph.cpp PrintNLATable.cpp RandomizeRegisterInit.cpp diff --git a/lib/Dialect/FIRRTL/Transforms/PrintFIRRTLFieldSource.cpp b/lib/Dialect/FIRRTL/Transforms/PrintFIRRTLFieldSource.cpp new file mode 100644 index 000000000000..31fae52bb15c --- /dev/null +++ b/lib/Dialect/FIRRTL/Transforms/PrintFIRRTLFieldSource.cpp @@ -0,0 +1,65 @@ +//===- PrintFIRRTLFieldSource.cpp - Print the Field Source ------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +//===----------------------------------------------------------------------===// +// +// Print the FieldSource analysis. +// +//===----------------------------------------------------------------------===// + +#include "PassDetails.h" +#include "circt/Dialect/FIRRTL/FIRRTLFieldSource.h" +#include "circt/Dialect/FIRRTL/Passes.h" +#include "llvm/Support/raw_ostream.h" + +using namespace circt; +using namespace firrtl; + +namespace { +struct PrintFIRRTLFieldSourcePass + : public PrintFIRRTLFieldSourcePassBase { + PrintFIRRTLFieldSourcePass(raw_ostream &os) : os(os) {} + + void visitValue(const FieldSource &fieldRefs, Value v) { + auto *p = fieldRefs.nodeForValue(v); + if (p) { + if (p->isRoot()) + os << "ROOT: "; + else + os << "SUB: " << v; + os << " : " << p->src << " : {"; + llvm::interleaveComma(p->path, os); + os << "}\n"; + } + } + + void visitOp(const FieldSource &fieldRefs, Operation *op) { + for (auto r : op->getResults()) + visitValue(fieldRefs, r); + // recurse in to regions + for (auto &r : op->getRegions()) + for (auto &b : r.getBlocks()) + for (auto &op : b) + visitOp(fieldRefs, &op); + } + + void runOnOperation() override { + auto modOp = getOperation(); + os << "** " << modOp.getName() << "\n"; + auto &fieldRefs = getAnalysis(); + for (auto port : modOp.getBodyBlock()->getArguments()) + visitValue(fieldRefs, port); + for (auto &op : *modOp.getBodyBlock()) + visitOp(fieldRefs, &op); + + markAllAnalysesPreserved(); + } + raw_ostream &os; +}; +} // end anonymous namespace + +std::unique_ptr circt::firrtl::createFIRRTLFieldSourcePass() { + return std::make_unique(llvm::errs()); +}