28#include "mlir/IR/OpDefinition.h"
29#include "mlir/IR/Operation.h"
30#include "mlir/IR/SymbolTable.h"
31#include "mlir/IR/Threading.h"
32#include "mlir/Interfaces/CallInterfaces.h"
33#include "mlir/Interfaces/FunctionInterfaces.h"
34#include "mlir/Support/LLVM.h"
35#include "mlir/Support/WalkResult.h"
36#include "llvm/ADT/DenseMap.h"
37#include "llvm/ADT/DenseSet.h"
38#include "llvm/ADT/STLExtras.h"
39#include "llvm/ADT/SmallVector.h"
40#include "llvm/Support/Casting.h"
41#include "llvm/Support/Debug.h"
45#define DEBUG_TYPE "arc-infer-context"
49#define GEN_PASS_DEF_INFERCONTEXT
50#include "circt/Dialect/Arc/ArcPasses.h.inc"
60struct CallerGraphNode {
61 CallerGraphNode(FunctionOpInterface &fnOp) : fnOp(fnOp) {}
62 FunctionOpInterface fnOp;
66struct InferContextPass :
public arc::impl::InferContextBase<InferContextPass> {
67 void runOnOperation()
override;
71 void buildCallerGraph(ArrayRef<FunctionOpInterface> functions,
72 SymbolTableCollection &symbolTable);
75 void backPropagateNeedsContext();
77 void updateRegion(Region *region, SymbolTableCollection &symbolTable);
80 llvm::SmallDenseSet<FunctionOpInterface> hasContextSet;
82 llvm::SmallDenseSet<FunctionOpInterface> needsContextSet;
85 DenseMap<FunctionOpInterface, CallerGraphNode> callerGraph;
88void InferContextPass::updateRegion(Region *region,
89 SymbolTableCollection &symbolTable) {
91 IRRewriter rewriter(region->getContext());
92 rewriter.setInsertionPointToStart(®ion->front());
93 FunctionOpInterface containingFn =
94 dyn_cast<FunctionOpInterface>(region->getParentOp());
97 Value inferredContextVal = {};
98 if (
auto modelOp = llvm::dyn_cast<ModelOp>(region->getParentOp())) {
100 auto storageArg = region->getArgument(0);
102 AsContextOp::create(rewriter, modelOp->getLoc(), storageArg);
103 LLVM_DEBUG(
auto fnName = modelOp.getSymName();
105 <<
"Updating body of model \"" << fnName <<
"\"\n";);
106 }
else if (
auto instantiateOp =
107 llvm::dyn_cast<SimInstantiateOp>(region->getParentOp())) {
109 auto instanceArg = region->getArgument(0);
111 AsContextOp::create(rewriter, instantiateOp->getLoc(), instanceArg);
112 LLVM_DEBUG(llvm::dbgs() <<
"Updating body of instance\n";);
113 }
else if (containingFn && (needsContextSet.contains(containingFn) ||
114 hasContextSet.contains(containingFn))) {
116 auto *ctxtArg = llvm::find_if(region->getArguments(), [](Value arg) {
117 return isa<ContextType>(arg.getType());
119 assert(ctxtArg &&
"Expected function to have a context argument");
120 inferredContextVal = *ctxtArg;
121 LLVM_DEBUG(
auto fnName = cast<FunctionOpInterface>(region->getParentOp())
125 <<
"Updating body of function \"" << fnName <<
"\"\n";);
129 LLVM_DEBUG(
auto fnName = cast<FunctionOpInterface>(region->getParentOp())
133 <<
"Traversing body of function \"" << fnName <<
"\"\n";);
137 region->walk<WalkOrder::PreOrder>([&](Operation *op) -> WalkResult {
138 if (op->getNumRegions() > 0) {
140 if (
auto instOp = dyn_cast<SimInstantiateOp>(op)) {
141 updateRegion(&instOp.getBody(), symbolTable);
142 return WalkResult::skip();
144 if (op->hasTrait<OpTrait::IsIsolatedFromAbove>())
145 return WalkResult::skip();
148 if (
auto ctxtOp = dyn_cast<arc::InferredContextOp>(op)) {
149 assert(inferredContextVal &&
"No context to propagate");
150 rewriter.replaceOp(ctxtOp, inferredContextVal);
151 return WalkResult::skip();
155 if (needsContextSet.empty())
156 return WalkResult::advance();
157 auto callOp = dyn_cast<CallOpInterface>(op);
159 return WalkResult::advance();
160 auto callee = dyn_cast_or_null<FunctionOpInterface>(
161 callOp.resolveCallableInTable(&symbolTable));
162 if (!callee || !needsContextSet.contains(callee))
163 return WalkResult::advance();
164 assert(inferredContextVal &&
"No context to propagate");
165 callOp.getArgOperandsMutable().append({inferredContextVal});
166 return WalkResult::advance();
172void InferContextPass::buildCallerGraph(ArrayRef<FunctionOpInterface> functions,
173 SymbolTableCollection &symbolTable) {
176 callerGraph.reserve(functions.size());
177 for (
auto fn : functions)
178 if (!fn.getFunctionBody().
empty())
179 callerGraph.emplace_or_assign(fn, CallerGraphNode(fn));
183 for (
auto fn : functions) {
184 if (fn.getFunctionBody().empty())
186 auto *caller = &callerGraph.at(fn);
187 fn.getFunctionBody().walk<WalkOrder::PreOrder>(
188 [&](Operation *op) -> WalkResult {
189 if (op->getNumRegions() > 0) {
192 if (
auto instOp = dyn_cast<SimInstantiateOp>(op))
193 return WalkResult::skip();
194 if (op->hasTrait<OpTrait::IsIsolatedFromAbove>())
195 return WalkResult::skip();
197 auto callOp = dyn_cast<CallOpInterface>(op);
199 return WalkResult::advance();
200 auto callee = llvm::dyn_cast_or_null<FunctionOpInterface>(
201 callOp.resolveCallableInTable(&symbolTable));
203 auto calleeIt = callerGraph.find(callee);
204 if (calleeIt != callerGraph.end())
205 calleeIt->second.callers.insert(caller);
207 return WalkResult::advance();
212void InferContextPass::backPropagateNeedsContext() {
216 DFSFrame(CallerGraphNode *node) : node(node) {}
217 CallerGraphNode *
const node;
219 bool isFinished()
const {
return index >= node->callers.size(); }
222 return DFSFrame(node->callers[index++]);
227 SmallVector<DFSFrame> dfsStack;
228 for (
auto seed : needsContextSet) {
229 LLVM_DEBUG(
auto fnName = seed.getNameAttr().getValue();
231 <<
"Seeding needsContext with function \"" << fnName <<
"\"\n";);
232 auto fnNode = callerGraph.find(seed);
233 assert(fnNode != callerGraph.end() &&
"Function not in caller graph");
234 dfsStack.emplace_back(&fnNode->second);
238 while (!dfsStack.empty()) {
239 if (dfsStack.back().isFinished()) {
243 auto next = dfsStack.back().getNext();
245 if (hasContextSet.contains(next.node->fnOp))
247 if (needsContextSet.insert(next.node->fnOp).second) {
248 LLVM_DEBUG(
auto fnName = next.node->fnOp.getNameAttr().getValue();
249 llvm::dbgs() <<
"Propagating needsContext to function \""
250 << fnName <<
"\"\n";);
251 dfsStack.push_back(next);
256void InferContextPass::runOnOperation() {
257 SymbolTableCollection symbolTable;
258 ModuleOp moduleOp = getOperation();
261 llvm::SmallDenseSet<FunctionOpInterface> hasInstancesSet;
263 hasContextSet.clear();
264 needsContextSet.clear();
272 SmallVector<FunctionOpInterface> funcOps =
273 llvm::to_vector(moduleOp.getOps<FunctionOpInterface>());
276 moduleOp.getContext(), funcOps, [&](FunctionOpInterface funcOp) {
277 bool needsContext = false;
278 bool hasInstances = false;
280 bool hasContext = llvm::any_of(funcOp.getArgumentTypes(), [](Type ty) {
281 return isa<ContextType>(ty);
284 funcOp.getFunctionBody().walk<WalkOrder::PreOrder>(
285 [&](Operation *op) -> WalkResult {
286 if (op->getNumRegions() > 0) {
287 if (isa<SimInstantiateOp>(op)) {
289 return WalkResult::skip();
292 if (op->hasTrait<OpTrait::IsIsolatedFromAbove>())
293 return WalkResult::skip();
294 }
else if (!hasContext) {
295 needsContext |= isa<InferredContextOp>(op);
298 if ((hasContext || needsContext) && hasInstances)
299 return WalkResult::interrupt();
300 return WalkResult::advance();
302 assert(!(hasContext && needsContext));
303 if (hasContext || needsContext || hasInstances) {
304 std::lock_guard<std::mutex> lock(setMutex);
306 hasContextSet.insert(funcOp);
308 needsContextSet.insert(funcOp);
310 hasInstancesSet.insert(funcOp);
314 SmallPtrSet<Region *, 4> regionsToUpdate;
316 if (!needsContextSet.empty()) {
318 buildCallerGraph(funcOps, symbolTable);
319 backPropagateNeedsContext();
323 auto ctxtType = arc::ContextType::get(getOperation()->getContext());
324 bool anyFailed =
false;
325 for (
auto fn : needsContextSet) {
327 fn.emitError(
"Cannot infer an Arc context through a public function. A "
328 "context argument must be provided explicitly.");
332 if (failed(fn.insertArgument(fn.getNumArguments(), ctxtType,
334 fn.emitError(
"Failed to add context argument to function.");
337 regionsToUpdate.insert(&fn.getFunctionBody());
347 LLVM_DEBUG(llvm::dbgs() <<
"No function needs a context argument.\n");
352 for (
auto instFn : hasInstancesSet)
353 regionsToUpdate.insert(&instFn.getFunctionBody());
354 for (
auto fnOp : hasContextSet)
355 regionsToUpdate.insert(&fnOp.getFunctionBody());
356 for (
auto modelOp : moduleOp.getOps<ModelOp>())
357 regionsToUpdate.insert(&modelOp.getBody());
359 for (Region *region : regionsToUpdate)
360 updateRegion(region, symbolTable);
362 markAnalysesPreserved<ModelInfoAnalysis>();
assert(baseType &&"element must be base type")
static InstancePath empty
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.