12#include "mlir/IR/ImplicitLocOpBuilder.h"
13#include "mlir/Pass/Pass.h"
14#include "llvm/Support/Debug.h"
16#define DEBUG_TYPE "arc-allocate-state"
20#define GEN_PASS_DEF_ALLOCATESTATE
21#include "circt/Dialect/Arc/ArcPasses.h.inc"
37struct AllocateStatePass
38 :
public arc::impl::AllocateStateBase<AllocateStatePass> {
40 using AllocateStateBase::AllocateStateBase;
42 void runOnOperation()
override;
43 void allocateBlock(
Block *block, Value rootStorage);
44 void allocateOps(Value storage,
Block *block, ArrayRef<Operation *> ops,
46 void tapNamedState(AllocStateOp allocOp, IntegerAttr offset);
48 SmallVector<Attribute> traceTaps;
52void AllocateStatePass::runOnOperation() {
55 ModelOp modelOp = getOperation();
56 LLVM_DEBUG(llvm::dbgs() <<
"Allocating state in `" << modelOp.getName()
61 Value rootStorage = modelOp.getBodyBlock().getArgument(0);
62 modelOp.walk([&](Block *block) { allocateBlock(block, rootStorage); });
64 if (!traceTaps.empty())
65 modelOp.setTraceTapsAttr(ArrayAttr::get(modelOp.getContext(), traceTaps));
71void AllocateStatePass::tapNamedState(AllocStateOp allocOp,
74 auto valueType = cast<StateType>(allocOp.getType()).getType();
75 auto typeAttr = TypeAttr::get(valueType);
77 if (
auto namesAttr = allocOp->getAttrOfType<ArrayAttr>(
"names")) {
78 if (!namesAttr.empty()) {
79 tapAttr = TraceTapAttr::get(allocOp.getContext(), typeAttr,
80 offset.getValue().getZExtValue(), namesAttr);
82 }
else if (
auto nameAttr = allocOp->getAttrOfType<StringAttr>(
"name")) {
83 auto arrayAttr = ArrayAttr::get(nameAttr.getContext(), {nameAttr});
84 tapAttr = TraceTapAttr::get(allocOp.getContext(), typeAttr,
85 offset.getValue().getZExtValue(), arrayAttr);
89 traceTaps.push_back(tapAttr);
92 auto indexAttr = IntegerAttr::get(
93 IntegerType::get(allocOp.getContext(), 64,
94 IntegerType::SignednessSemantics::Unsigned),
95 traceTaps.size() - 1);
96 auto modelSymAttr = FlatSymbolRefAttr::get(getOperation().getSymNameAttr());
98 for (
auto *user : allocOp.getResult().getUsers()) {
99 if (
auto writeUser = dyn_cast<StateWriteOp>(user)) {
101 assert(!(writeUser.getTraceTapIndex() || writeUser.getTraceTapModel()));
102 writeUser.setTraceTapIndexAttr(indexAttr);
103 writeUser.setTraceTapModelAttr(modelSymAttr);
108void AllocateStatePass::allocateBlock(Block *block, Value rootStorage) {
113 for (
auto &op : *block) {
114 if (isa<AllocStateOp, RootInputOp, RootOutputOp, AllocMemoryOp,
115 AllocStorageOp>(&op))
116 opsByStorage[op.getOperand(0)].push_back(&op);
118 LLVM_DEBUG(llvm::dbgs() <<
"- Visiting block in "
119 << block->getParentOp()->getName() <<
"\n");
123 bool isRootBlock = block == rootStorage.getParentBlock();
124 for (
auto &[storage, ops] : opsByStorage) {
125 unsigned padding = isRootBlock && storage == rootStorage ?
kStateOffset : 0;
126 allocateOps(storage, block, ops, padding);
130void AllocateStatePass::allocateOps(Value storage, Block *block,
131 ArrayRef<Operation *> ops,
133 SmallVector<std::tuple<Value, Value, IntegerAttr>> gettersToCreate;
137 unsigned currentByte = padding;
138 auto allocBytes = [&](
unsigned numBytes) {
139 currentByte = llvm::alignToPowerOf2(
140 currentByte, llvm::bit_ceil(std::min(numBytes, 16U)));
141 unsigned offset = currentByte;
142 currentByte += numBytes;
147 OpBuilder builder(block->getParentOp());
148 for (
auto *op : ops) {
149 if (isa<AllocStateOp, RootInputOp, RootOutputOp>(op)) {
150 auto result = op->getResult(0);
151 auto storage = op->getOperand(0);
152 unsigned numBytes = cast<StateType>(result.getType()).getByteWidth();
153 auto offset = builder.getI32IntegerAttr(allocBytes(numBytes));
154 op->setAttr(
"offset", offset);
155 gettersToCreate.emplace_back(result, storage, offset);
157 if (
auto allocStateOp = dyn_cast<AllocStateOp>(op))
158 tapNamedState(allocStateOp, offset);
162 if (
auto memOp = dyn_cast<AllocMemoryOp>(op)) {
163 auto memType = memOp.getType();
164 unsigned stride = memType.getStride();
165 unsigned numBytes = memType.getNumWords() * stride;
166 auto offset = builder.getI32IntegerAttr(allocBytes(numBytes));
167 op->setAttr(
"offset", offset);
168 op->setAttr(
"stride", builder.getI32IntegerAttr(stride));
169 gettersToCreate.emplace_back(memOp, memOp.getStorage(), offset);
173 if (
auto allocStorageOp = dyn_cast<AllocStorageOp>(op)) {
174 auto offset = builder.getI32IntegerAttr(
175 allocBytes(allocStorageOp.getType().getSize()));
176 allocStorageOp.setOffsetAttr(offset);
177 gettersToCreate.emplace_back(allocStorageOp, allocStorageOp.getInput(),
182 assert(
"unsupported op for allocation" &&
false);
188 DenseMap<Operation *, unsigned> opOrder;
189 block->walk([&](Operation *op) { opOrder.insert({op, opOrder.size()}); });
190 SmallVector<StorageGetOp> getters;
191 for (
auto [result, storage, offset] : gettersToCreate) {
193 for (
auto *user :
llvm::make_early_inc_range(result.getUsers())) {
194 auto &getter = getterForBlock[user->getBlock()];
197 auto userOrder = opOrder.lookup(user);
198 if (!getter || !result.getDefiningOp<AllocStorageOp>()) {
199 ImplicitLocOpBuilder builder(result.getLoc(), user);
201 StorageGetOp::create(builder, result.getType(), storage, offset);
202 getters.push_back(getter);
203 opOrder[getter] = userOrder;
204 }
else if (userOrder < opOrder.lookup(getter)) {
205 getter->moveBefore(user);
206 opOrder[getter] = userOrder;
208 user->replaceUsesOfWith(result, getter);
216 storage.setType(StorageType::get(&getContext(), currentByte));
218 auto substorage = AllocStorageOp::create(
219 builder, block->getParentOp()->getLoc(),
220 StorageType::get(&getContext(), currentByte), storage);
222 op->replaceUsesOfWith(storage, substorage);
223 for (
auto op : getters)
224 op->replaceUsesOfWith(storage, substorage);
assert(baseType &&"element must be base type")
constexpr unsigned kStateOffset
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.