CIRCT 22.0.0git
Loading...
Searching...
No Matches
InsertRuntime.cpp
Go to the documentation of this file.
1//===- InsertRuntime.cpp --------------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
14#include "mlir/Dialect/LLVMIR/LLVMDialect.h"
15#include "mlir/Dialect/LLVMIR/LLVMTypes.h"
16
17#include "mlir/Pass/Pass.h"
18
19#define DEBUG_TYPE "arc-insert-runtime"
20
21namespace circt {
22namespace arc {
23#define GEN_PASS_DEF_INSERTRUNTIME
24#include "circt/Dialect/Arc/ArcPasses.h.inc"
25} // namespace arc
26} // namespace circt
27
28using namespace mlir;
29using namespace circt;
30using namespace arc;
31
32namespace {
33
34// API Helpers
35struct RuntimeFunction {
36 LLVM::LLVMFuncOp llvmFuncOp = {};
37 bool used = false;
38};
39
40struct AllocInstanceFunction : public RuntimeFunction {
41 explicit AllocInstanceFunction(ImplicitLocOpBuilder &builder) {
42 /*
43 uint8_t *
44 arcRuntimeIR_allocInstance(const ArcRuntimeModelInfo *model, const char
45 *args);
46 */
47 auto ptrTy = LLVM::LLVMPointerType::get(builder.getContext());
48 llvmFuncOp = LLVM::LLVMFuncOp::create(
49 builder, runtime::APICallbacks::symNameAllocInstance,
50 LLVM::LLVMFunctionType::get(ptrTy, {ptrTy, ptrTy}));
51 }
52};
53
54struct DeleteInstanceFunction : public RuntimeFunction {
55 explicit DeleteInstanceFunction(ImplicitLocOpBuilder &builder) {
56 /*
57 void arcRuntimeIR_deleteInstance(uint8_t *modelState);
58 */
59 auto ptrTy = LLVM::LLVMPointerType::get(builder.getContext());
60 auto voidTy = LLVM::LLVMVoidType::get(builder.getContext());
61 llvmFuncOp = LLVM::LLVMFuncOp::create(
62 builder, runtime::APICallbacks::symNameDeleteInstance,
63 LLVM::LLVMFunctionType::get(voidTy, {ptrTy}));
64 }
65};
66
67struct OnEvalFunction : public RuntimeFunction {
68 explicit OnEvalFunction(ImplicitLocOpBuilder &builder) {
69 /*
70 void arcRuntimeIR_onEval(uint8_t *modelState);
71 */
72 auto ptrTy = LLVM::LLVMPointerType::get(builder.getContext());
73 auto voidTy = LLVM::LLVMVoidType::get(builder.getContext());
74 llvmFuncOp =
75 LLVM::LLVMFuncOp::create(builder, runtime::APICallbacks::symNameOnEval,
76 LLVM::LLVMFunctionType::get(voidTy, {ptrTy}));
77 }
78};
79
80// Lowering Helpers
81
82struct RuntimeModelContext; // Forward declaration
83
84struct GlobalRuntimeContext {
85 GlobalRuntimeContext() = delete;
86
87 /// Constructs a global context and adds the available runtime API function
88 /// declarations to the MLIR module
89 explicit GlobalRuntimeContext(ModuleOp moduleOp)
90 : mlirModuleOp(moduleOp), globalBuilder(createBuilder(moduleOp)),
91 allocInstanceFn(globalBuilder), deleteInstanceFn(globalBuilder),
92 onEvalFn(globalBuilder) {}
93
94 /// Delete all API functions that are never called
95 void deleteUnusedFunctions() {
96 for (auto *fn : apiFunctions)
97 if (!fn->used)
98 fn->llvmFuncOp->erase();
99 }
100
101 /// Add an Arc model to the global runtime context
102 void addModel(ModelOp &modelOp, const ModelInfo &modelInfo);
103 /// Build a RuntimeModelOp for each registered model
104 LogicalResult buildRuntimeModelOps();
105 /// Find and assign instances of the registered models within the root module
106 LogicalResult collectInstances();
107
108 /// The root module
109 ModuleOp mlirModuleOp;
110 /// Builder for global operations
111 ImplicitLocOpBuilder globalBuilder;
112
113 // API Functions
114 AllocInstanceFunction allocInstanceFn;
115 DeleteInstanceFunction deleteInstanceFn;
116 OnEvalFunction onEvalFn;
117 const std::array<RuntimeFunction *, 3> apiFunctions = {
118 &allocInstanceFn, &deleteInstanceFn, &onEvalFn};
119
120 // Maps model symbol name to model context
122
123private:
124 static ImplicitLocOpBuilder createBuilder(ModuleOp &moduleOp) {
125 auto builder = ImplicitLocOpBuilder(moduleOp.getLoc(), moduleOp);
126 builder.setInsertionPointToStart(moduleOp.getBody());
127 return builder;
128 }
129};
130
131struct RuntimeModelContext {
132 RuntimeModelContext() = delete;
133 /// Construct the local context for an Arc model within the global context
134 RuntimeModelContext(GlobalRuntimeContext &globalContext, ModelOp &modelOp,
135 const ModelInfo &modelInfo)
136 : globalContext(globalContext), modelOp(modelOp), modelInfo(modelInfo) {}
137
138 /// Register an MLIR defined instance of our model
139 void addInstance(SimInstantiateOp &instantiateOp) {
140 assert(!instantiateOp.getRuntimeModelAttr());
141 assert(!!runtimeModelOp);
142 instantiateOp.setRuntimeModelAttr(
143 FlatSymbolRefAttr::get(runtimeModelOp.getSymNameAttr()));
144 instances.push_back(instantiateOp);
145 }
146
147 /// Insert runtime calls to the model and its instances
148 LogicalResult lower();
149
150 /// The global runtime context
151 GlobalRuntimeContext &globalContext;
152 /// This context's model
153 ModelOp modelOp;
154 /// Model metadata
155 const ModelInfo &modelInfo;
156 /// List of registered instances
157 SmallVector<SimInstantiateOp> instances;
158 /// The model's corresponding RuntimeModelOp
159 RuntimeModelOp runtimeModelOp;
160
161private:
162 LogicalResult lowerInstance(SimInstantiateOp &instance);
163};
164struct InsertRuntimePass
165 : public arc::impl::InsertRuntimeBase<InsertRuntimePass> {
166 using InsertRuntimeBase::InsertRuntimeBase;
167
168 void runOnOperation() override;
169};
170
171} // namespace
172
173void GlobalRuntimeContext::addModel(ModelOp &modelOp,
174 const ModelInfo &modelInfo) {
175 auto newModel =
176 std::make_unique<RuntimeModelContext>(*this, modelOp, modelInfo);
177 models[modelOp.getNameAttr()] = std::move(newModel);
178}
179
180// Find all instances in the MLIR Module and assign them to their
181// respective Arc Model
182LogicalResult GlobalRuntimeContext::collectInstances() {
183 bool hasFailed = false;
184 mlirModuleOp.getBody()->walk([&](Operation *op) -> WalkResult {
185 if (auto instOp = dyn_cast<SimInstantiateOp>(op)) {
186 // Don't touch instances which somehow already carry a runtime model
187 if (instOp.getRuntimeModel())
188 return WalkResult::skip();
189 auto instanceModelSym = llvm::cast<SimModelInstanceType>(
190 instOp.getBody().getArgument(0).getType())
191 .getModel()
192 .getAttr();
193 auto modelContext = models.find(instanceModelSym);
194 if (modelContext == models.end()) {
195 hasFailed = true;
196 instOp->emitOpError(" does not refer to a known Arc model.");
197 } else {
198 modelContext->second->addInstance(instOp);
199 }
200 return WalkResult::skip();
201 }
202 if (auto instOp = dyn_cast<ModelOp>(op))
203 return WalkResult::skip();
204 return WalkResult::advance();
205 });
206 return success(!hasFailed);
207}
208
209// Build the global RuntimeModelOp for each model
210LogicalResult GlobalRuntimeContext::buildRuntimeModelOps() {
211 auto savedLoc = globalBuilder.getLoc();
212 for (auto &[_, model] : models) {
213 globalBuilder.setLoc(model->modelOp.getLoc());
214 auto symName = globalBuilder.getStringAttr(Twine("arcRuntimeModel_") +
215 model->modelInfo.name);
216 model->runtimeModelOp = RuntimeModelOp::create(
217 globalBuilder, symName,
218 globalBuilder.getStringAttr(model->modelInfo.name),
219 static_cast<uint64_t>(model->modelInfo.numStateBytes));
220 }
221 globalBuilder.setLoc(savedLoc);
222 return success();
223}
224
225// Lower the model and all of its instances
226LogicalResult RuntimeModelContext::lower() {
227 bool hasFailed = false;
228 for (auto &instance : instances)
229 if (failed(lowerInstance(instance)))
230 hasFailed = true;
231 return success(!hasFailed);
232}
233
234LogicalResult RuntimeModelContext::lowerInstance(SimInstantiateOp &instance) {
235 // For now, these get invoked by the lowering of SimInstantiateOp
236 globalContext.allocInstanceFn.used = true;
237 globalContext.deleteInstanceFn.used = true;
238
239 // Insert onEval call for every step call
240 OpBuilder instBodyBuilder(instance);
241 instBodyBuilder.setInsertionPointToStart(
242 &instance.getBody().getBlocks().front());
243 auto runtimeInst =
244 UnrealizedConversionCastOp::create(
245 instBodyBuilder, instance.getLoc(),
246 LLVM::LLVMPointerType::get(instBodyBuilder.getContext()),
247 instance.getBody().getArgument(0))
248 .getResult(0);
249
250 instance.getBody().getBlocks().front().walk([&](SimStepOp stepOp) {
251 instBodyBuilder.setInsertionPoint(stepOp);
252 globalContext.onEvalFn.used = true;
253 LLVM::CallOp::create(instBodyBuilder, stepOp.getLoc(),
254 globalContext.onEvalFn.llvmFuncOp, {runtimeInst});
255 });
256
257 return success();
258}
259
260void InsertRuntimePass::runOnOperation() {
261 // Construct the global context and collect information on all
262 // models and instances
263 auto &modelInfo = getAnalysis<ModelInfoAnalysis>();
264 auto globalContext = std::make_unique<GlobalRuntimeContext>(getOperation());
265 for (auto &[mOp, mInfo] : modelInfo.infoMap)
266 globalContext->addModel(mOp, mInfo);
267 if (failed(globalContext->buildRuntimeModelOps()) ||
268 failed(globalContext->collectInstances())) {
269 signalPassFailure();
270 return;
271 }
272
273 // Lower all models
274 for (auto &[_, model] : globalContext->models) {
275 // If provided, append extra instance arguments
276 if (!extraArgs.empty()) {
277 for (auto &instance : model->instances) {
278 StringAttr newArgs;
279 if (!instance.getRuntimeArgsAttr() ||
280 instance.getRuntimeArgsAttr().getValue().empty())
281 newArgs = StringAttr::get(&getContext(), Twine(extraArgs));
282 else
283 newArgs = StringAttr::get(&getContext(),
284 Twine(instance.getRuntimeArgsAttr()) +
285 Twine(";") + Twine(extraArgs));
286 instance.setRuntimeArgsAttr(newArgs);
287 }
288 }
289
290 if (failed(model->lower()))
291 signalPassFailure();
292 }
293
294 globalContext->deleteUnusedFunctions();
295 markAnalysesPreserved<ModelInfoAnalysis>();
296}
assert(baseType &&"element must be base type")
Definition arc.py:1
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.