Skip to content

Commit

Permalink
[FIRRTL] Simple points-to like analysis (#4637)
Browse files Browse the repository at this point in the history
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<bundle<f1: uint, f2: uint>, 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}
  • Loading branch information
darthscsi authored Feb 9, 2023
1 parent 0285a98 commit db40efb
Show file tree
Hide file tree
Showing 7 changed files with 229 additions and 0 deletions.
58 changes: 58 additions & 0 deletions include/circt/Dialect/FIRRTL/FIRRTLFieldSource.h
Original file line number Diff line number Diff line change
@@ -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<FieldSource>(getOperation());
class FieldSource {

public:
explicit FieldSource(Operation *operation);

struct PathNode {
PathNode(Value src, ArrayRef<int64_t> ar) : src(src), path(ar) {}
Value src;
SmallVector<int64_t, 4> 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<int64_t> path);

DenseMap<Value, PathNode> paths;
};

} // namespace firrtl
} // namespace circt

#endif // CIRCT_DIALECT_FIRRTL_FIRRTLFIELDSOURCE_H
2 changes: 2 additions & 0 deletions include/circt/Dialect/FIRRTL/Passes.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ createMemToRegOfVecPass(bool replSeqMem = false, bool ignoreReadEnable = false);

std::unique_ptr<mlir::Pass> createPrefixModulesPass();

std::unique_ptr<mlir::Pass> createFIRRTLFieldSourcePass();

std::unique_ptr<mlir::Pass> createPrintInstanceGraphPass();

std::unique_ptr<mlir::Pass> createPrintNLATablePass();
Expand Down
6 changes: 6 additions & 0 deletions include/circt/Dialect/FIRRTL/Passes.td
Original file line number Diff line number Diff line change
Expand Up @@ -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.";
Expand Down
1 change: 1 addition & 0 deletions lib/Dialect/FIRRTL/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ add_circt_dialect_library(CIRCTFIRRTL
FIRRTLAnnotations.cpp
FIRRTLAttributes.cpp
FIRRTLDialect.cpp
FIRRTLFieldSource.cpp
FIRRTLFolds.cpp
FIRRTLInstanceGraph.cpp
FIRRTLOpInterfaces.cpp
Expand Down
96 changes: 96 additions & 0 deletions lib/Dialect/FIRRTL/FIRRTLFieldSource.cpp
Original file line number Diff line number Diff line change
@@ -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<FModuleOp>(operation);
for (auto port : mod.getBodyBlock()->getArguments())
if (auto ft = dyn_cast<FIRRTLBaseType>(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<SubfieldOp>(op))
return visitSubfield(sf);
if (auto si = dyn_cast<SubindexOp>(op))
return visitSubindex(si);
if (auto sa = dyn_cast<SubaccessOp>(op))
return visitSubaccess(sa);
if (isa<WireOp, NodeOp, RegOp, RegResetOp, BitCastOp>(op))
return makeNodeForValue(op->getResult(0), op->getResult(0), {});
if (auto mem = dyn_cast<MemOp>(op))
return visitMem(mem);
if (auto inst = dyn_cast<InstanceOp>(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<int64_t> path) {
auto ii = paths.try_emplace(dst, src, path);
assert(ii.second && "Double insert into the map");
}
1 change: 1 addition & 0 deletions lib/Dialect/FIRRTL/Transforms/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ add_circt_dialect_library(CIRCTFIRRTLTransforms
MemToRegOfVec.cpp
ModuleInliner.cpp
PrefixModules.cpp
PrintFIRRTLFieldSource.cpp
PrintInstanceGraph.cpp
PrintNLATable.cpp
RandomizeRegisterInit.cpp
Expand Down
65 changes: 65 additions & 0 deletions lib/Dialect/FIRRTL/Transforms/PrintFIRRTLFieldSource.cpp
Original file line number Diff line number Diff line change
@@ -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> {
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<FieldSource>();
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<mlir::Pass> circt::firrtl::createFIRRTLFieldSourcePass() {
return std::make_unique<PrintFIRRTLFieldSourcePass>(llvm::errs());
}

0 comments on commit db40efb

Please sign in to comment.