11#include "mlir/IR/ImplicitLocOpBuilder.h"
12#include "mlir/Pass/Pass.h"
13#include "llvm/Support/Debug.h"
15#define DEBUG_TYPE "arc-allocate-state"
19#define GEN_PASS_DEF_ALLOCATESTATE
20#include "circt/Dialect/Arc/ArcPasses.h.inc"
28using llvm::SmallMapVector;
36struct AllocateStatePass
37 :
public arc::impl::AllocateStateBase<AllocateStatePass> {
39 using AllocateStateBase::AllocateStateBase;
41 void runOnOperation()
override;
42 void allocateBlock(
Block *block);
43 void allocateOps(Value storage,
Block *block, ArrayRef<Operation *> ops);
44 void tapNamedState(AllocStateOp allocOp, IntegerAttr offset);
46 SmallVector<Attribute> traceTaps;
50void AllocateStatePass::runOnOperation() {
53 ModelOp modelOp = getOperation();
54 LLVM_DEBUG(llvm::dbgs() <<
"Allocating state in `" << modelOp.getName()
59 modelOp.walk([&](Block *block) { allocateBlock(block); });
61 if (!traceTaps.empty())
62 modelOp.setTraceTapsAttr(ArrayAttr::get(modelOp.getContext(), traceTaps));
68void AllocateStatePass::tapNamedState(AllocStateOp allocOp,
71 auto valueType = cast<StateType>(allocOp.getType()).getType();
72 auto typeAttr = TypeAttr::get(valueType);
74 if (
auto namesAttr = allocOp->getAttrOfType<ArrayAttr>(
"names")) {
75 if (!namesAttr.empty()) {
76 tapAttr = TraceTapAttr::get(allocOp.getContext(), typeAttr,
77 offset.getValue().getZExtValue(), namesAttr);
79 }
else if (
auto nameAttr = allocOp->getAttrOfType<StringAttr>(
"name")) {
80 auto arrayAttr = ArrayAttr::get(nameAttr.getContext(), {nameAttr});
81 tapAttr = TraceTapAttr::get(allocOp.getContext(), typeAttr,
82 offset.getValue().getZExtValue(), arrayAttr);
86 traceTaps.push_back(tapAttr);
89 auto indexAttr = IntegerAttr::get(
90 IntegerType::get(allocOp.getContext(), 64,
91 IntegerType::SignednessSemantics::Unsigned),
92 traceTaps.size() - 1);
93 auto modelSymAttr = FlatSymbolRefAttr::get(getOperation().getSymNameAttr());
95 for (
auto *user : allocOp.getResult().getUsers()) {
96 if (
auto writeUser = dyn_cast<StateWriteOp>(user)) {
98 assert(!(writeUser.getTraceTapIndex() || writeUser.getTraceTapModel()));
99 writeUser.setTraceTapIndexAttr(indexAttr);
100 writeUser.setTraceTapModelAttr(modelSymAttr);
105void AllocateStatePass::allocateBlock(Block *block) {
106 SmallMapVector<Value, std::vector<Operation *>, 1> opsByStorage;
110 for (
auto &op : *block) {
111 if (isa<AllocStateOp, RootInputOp, RootOutputOp, AllocMemoryOp,
112 AllocStorageOp>(&op))
113 opsByStorage[op.getOperand(0)].push_back(&op);
115 LLVM_DEBUG(llvm::dbgs() <<
"- Visiting block in "
116 << block->getParentOp()->getName() <<
"\n");
119 for (
auto &[storage, ops] : opsByStorage)
120 allocateOps(storage, block, ops);
123void AllocateStatePass::allocateOps(Value storage, Block *block,
124 ArrayRef<Operation *> ops) {
125 SmallVector<std::tuple<Value, Value, IntegerAttr>> gettersToCreate;
129 unsigned currentByte = 0;
130 auto allocBytes = [&](
unsigned numBytes) {
131 currentByte = llvm::alignToPowerOf2(
132 currentByte, llvm::bit_ceil(std::min(numBytes, 16U)));
133 unsigned offset = currentByte;
134 currentByte += numBytes;
139 OpBuilder builder(block->getParentOp());
140 for (
auto *op : ops) {
141 if (isa<AllocStateOp, RootInputOp, RootOutputOp>(op)) {
142 auto result = op->getResult(0);
143 auto storage = op->getOperand(0);
144 unsigned numBytes = cast<StateType>(result.getType()).getByteWidth();
145 auto offset = builder.getI32IntegerAttr(allocBytes(numBytes));
146 op->setAttr(
"offset", offset);
147 gettersToCreate.emplace_back(result, storage, offset);
149 if (
auto allocStateOp = dyn_cast<AllocStateOp>(op))
150 tapNamedState(allocStateOp, offset);
154 if (
auto memOp = dyn_cast<AllocMemoryOp>(op)) {
155 auto memType = memOp.getType();
156 unsigned stride = memType.getStride();
157 unsigned numBytes = memType.getNumWords() * stride;
158 auto offset = builder.getI32IntegerAttr(allocBytes(numBytes));
159 op->setAttr(
"offset", offset);
160 op->setAttr(
"stride", builder.getI32IntegerAttr(stride));
161 gettersToCreate.emplace_back(memOp, memOp.getStorage(), offset);
165 if (
auto allocStorageOp = dyn_cast<AllocStorageOp>(op)) {
166 auto offset = builder.getI32IntegerAttr(
167 allocBytes(allocStorageOp.getType().getSize()));
168 allocStorageOp.setOffsetAttr(offset);
169 gettersToCreate.emplace_back(allocStorageOp, allocStorageOp.getInput(),
174 assert(
"unsupported op for allocation" &&
false);
180 DenseMap<Operation *, unsigned> opOrder;
181 block->walk([&](Operation *op) { opOrder.insert({op, opOrder.size()}); });
182 SmallVector<StorageGetOp> getters;
183 for (
auto [result, storage, offset] : gettersToCreate) {
185 for (
auto *user :
llvm::make_early_inc_range(result.getUsers())) {
186 auto &getter = getterForBlock[user->getBlock()];
189 auto userOrder = opOrder.lookup(user);
190 if (!getter || !result.getDefiningOp<AllocStorageOp>()) {
191 ImplicitLocOpBuilder builder(result.getLoc(), user);
193 StorageGetOp::create(builder, result.getType(), storage, offset);
194 getters.push_back(getter);
195 opOrder[getter] = userOrder;
196 }
else if (userOrder < opOrder.lookup(getter)) {
197 getter->moveBefore(user);
198 opOrder[getter] = userOrder;
200 user->replaceUsesOfWith(result, getter);
205 Operation *storageOwner = storage.getDefiningOp();
207 storageOwner = cast<BlockArgument>(storage).getOwner()->getParentOp();
209 if (storageOwner->isProperAncestor(block->getParentOp())) {
210 auto substorage = AllocStorageOp::create(
211 builder, block->getParentOp()->getLoc(),
212 StorageType::get(&getContext(), currentByte), storage);
214 op->replaceUsesOfWith(storage, substorage);
215 for (
auto op : getters)
216 op->replaceUsesOfWith(storage, substorage);
218 storage.setType(StorageType::get(&getContext(), currentByte));
assert(baseType &&"element must be base type")
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.