CIRCT 22.0.0git
Loading...
Searching...
No Matches
SimOps.cpp
Go to the documentation of this file.
1//===- SimOps.cpp - Implement the Sim operations ------------------------===//
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//
9// This file implements `sim` dialect ops.
10//
11//===----------------------------------------------------------------------===//
12
17#include "mlir/Dialect/Func/IR/FuncOps.h"
18#include "mlir/IR/PatternMatch.h"
19#include "mlir/Interfaces/FunctionImplementation.h"
20#include "llvm/ADT/MapVector.h"
21
22using namespace mlir;
23using namespace circt;
24using namespace sim;
25
26ParseResult DPIFuncOp::parse(OpAsmParser &parser, OperationState &result) {
27 auto builder = parser.getBuilder();
28 // Parse visibility.
29 (void)mlir::impl::parseOptionalVisibilityKeyword(parser, result.attributes);
30
31 // Parse the name as a symbol.
32 StringAttr nameAttr;
33 if (parser.parseSymbolName(nameAttr, SymbolTable::getSymbolAttrName(),
34 result.attributes))
35 return failure();
36
37 SmallVector<hw::module_like_impl::PortParse> ports;
38 TypeAttr modType;
39 if (failed(
40 hw::module_like_impl::parseModuleSignature(parser, ports, modType)))
41 return failure();
42
43 result.addAttribute(DPIFuncOp::getModuleTypeAttrName(result.name), modType);
44
45 // Convert the specified array of dictionary attrs (which may have null
46 // entries) to an ArrayAttr of dictionaries.
47 auto unknownLoc = builder.getUnknownLoc();
48 SmallVector<Attribute> attrs, locs;
49 auto nonEmptyLocsFn = [unknownLoc](Attribute attr) {
50 return attr && cast<Location>(attr) != unknownLoc;
51 };
52
53 for (auto &port : ports) {
54 attrs.push_back(port.attrs ? port.attrs : builder.getDictionaryAttr({}));
55 locs.push_back(port.sourceLoc ? Location(*port.sourceLoc) : unknownLoc);
56 }
57
58 result.addAttribute(DPIFuncOp::getPerArgumentAttrsAttrName(result.name),
59 builder.getArrayAttr(attrs));
60 result.addRegion();
61
62 if (llvm::any_of(locs, nonEmptyLocsFn))
63 result.addAttribute(DPIFuncOp::getArgumentLocsAttrName(result.name),
64 builder.getArrayAttr(locs));
65
66 // Parse the attribute dict.
67 if (failed(parser.parseOptionalAttrDictWithKeyword(result.attributes)))
68 return failure();
69
70 return success();
71}
72
73LogicalResult
74sim::DPICallOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
75 auto referencedOp =
76 symbolTable.lookupNearestSymbolFrom(*this, getCalleeAttr());
77 if (!referencedOp)
78 return emitError("cannot find function declaration '")
79 << getCallee() << "'";
80 if (isa<func::FuncOp, sim::DPIFuncOp>(referencedOp))
81 return success();
82 return emitError("callee must be 'sim.dpi.func' or 'func.func' but got '")
83 << referencedOp->getName() << "'";
84}
85
86void DPIFuncOp::print(OpAsmPrinter &p) {
87 DPIFuncOp op = *this;
88 // Print the operation and the function name.
89 auto funcName =
90 op->getAttrOfType<StringAttr>(SymbolTable::getSymbolAttrName())
91 .getValue();
92 p << ' ';
93
94 StringRef visibilityAttrName = SymbolTable::getVisibilityAttrName();
95 if (auto visibility = op->getAttrOfType<StringAttr>(visibilityAttrName))
96 p << visibility.getValue() << ' ';
97 p.printSymbolName(funcName);
99 p, op->getRegion(0), op.getModuleType(),
100 getPerArgumentAttrsAttr()
101 ? ArrayRef<Attribute>(getPerArgumentAttrsAttr().getValue())
102 : ArrayRef<Attribute>{},
103 getArgumentLocs() ? SmallVector<Location>(
104 getArgumentLocs().value().getAsRange<Location>())
105 : ArrayRef<Location>{});
106
107 mlir::function_interface_impl::printFunctionAttributes(
108 p, op,
109 {visibilityAttrName, getModuleTypeAttrName(),
110 getPerArgumentAttrsAttrName(), getArgumentLocsAttrName()});
111}
112
113OpFoldResult FormatLiteralOp::fold(FoldAdaptor adaptor) {
114 return getLiteralAttr();
115}
116
117OpFoldResult FormatDecOp::fold(FoldAdaptor adaptor) {
118 if (getValue().getType() == IntegerType::get(getContext(), 0U))
119 return StringAttr::get(getContext(), "0");
120
121 if (auto intAttr = llvm::dyn_cast_or_null<IntegerAttr>(adaptor.getValue())) {
122 SmallVector<char, 16> strBuf;
123 intAttr.getValue().toString(strBuf, 10U, getIsSigned());
124
125 unsigned width = intAttr.getType().getIntOrFloatBitWidth();
126 unsigned padWidth = FormatDecOp::getDecimalWidth(width, getIsSigned());
127 padWidth = padWidth > strBuf.size() ? padWidth - strBuf.size() : 0;
128
129 SmallVector<char, 8> padding(padWidth, ' ');
130 return StringAttr::get(getContext(), Twine(padding) + Twine(strBuf));
131 }
132 return {};
133}
134
135OpFoldResult FormatHexOp::fold(FoldAdaptor adaptor) {
136 if (getValue().getType() == IntegerType::get(getContext(), 0U))
137 return StringAttr::get(getContext(), "");
138
139 if (auto intAttr = llvm::dyn_cast_or_null<IntegerAttr>(adaptor.getValue())) {
140 SmallVector<char, 8> strBuf;
141 intAttr.getValue().toString(strBuf, 16U, /*Signed*/ false,
142 /*formatAsCLiteral*/ false,
143 /*UpperCase*/ false);
144
145 unsigned width = intAttr.getType().getIntOrFloatBitWidth();
146 unsigned padWidth = width / 4;
147 if (width % 4 != 0)
148 padWidth++;
149 padWidth = padWidth > strBuf.size() ? padWidth - strBuf.size() : 0;
150
151 SmallVector<char, 8> padding(padWidth, '0');
152 return StringAttr::get(getContext(), Twine(padding) + Twine(strBuf));
153 }
154 return {};
155}
156
157OpFoldResult FormatOctOp::fold(FoldAdaptor adaptor) {
158 if (getValue().getType() == IntegerType::get(getContext(), 0U))
159 return StringAttr::get(getContext(), "");
160
161 if (auto intAttr = llvm::dyn_cast_or_null<IntegerAttr>(adaptor.getValue())) {
162 SmallVector<char, 11> strBuf;
163 intAttr.getValue().toString(strBuf, 8U, /*Signed*/ false,
164 /*formatAsCLiteral*/ false,
165 /*UpperCase*/ false);
166
167 unsigned width = intAttr.getType().getIntOrFloatBitWidth();
168 unsigned padWidth = width / 3;
169 if (width % 3 != 0)
170 padWidth++;
171 padWidth = padWidth > strBuf.size() ? padWidth - strBuf.size() : 0;
172
173 SmallVector<char, 11> padding(padWidth, '0');
174 return StringAttr::get(getContext(), Twine(padding) + Twine(strBuf));
175 }
176 return {};
177}
178
179OpFoldResult FormatBinOp::fold(FoldAdaptor adaptor) {
180 if (getValue().getType() == IntegerType::get(getContext(), 0U))
181 return StringAttr::get(getContext(), "");
182
183 if (auto intAttr = llvm::dyn_cast_or_null<IntegerAttr>(adaptor.getValue())) {
184 SmallVector<char, 32> strBuf;
185 intAttr.getValue().toString(strBuf, 2U, false);
186
187 unsigned width = intAttr.getType().getIntOrFloatBitWidth();
188 unsigned padWidth = width > strBuf.size() ? width - strBuf.size() : 0;
189
190 SmallVector<char, 32> padding(padWidth, '0');
191 return StringAttr::get(getContext(), Twine(padding) + Twine(strBuf));
192 }
193 return {};
194}
195
196OpFoldResult FormatCharOp::fold(FoldAdaptor adaptor) {
197 auto width = getValue().getType().getIntOrFloatBitWidth();
198 if (width > 8)
199 return {};
200 if (width == 0)
201 return StringAttr::get(getContext(), Twine(static_cast<char>(0)));
202
203 if (auto intAttr = llvm::dyn_cast_or_null<IntegerAttr>(adaptor.getValue())) {
204 auto intValue = intAttr.getValue().getZExtValue();
205 return StringAttr::get(getContext(), Twine(static_cast<char>(intValue)));
206 }
207
208 return {};
209}
210
211static StringAttr concatLiterals(MLIRContext *ctxt, ArrayRef<StringRef> lits) {
212 assert(!lits.empty() && "No literals to concatenate");
213 if (lits.size() == 1)
214 return StringAttr::get(ctxt, lits.front());
215 SmallString<64> newLit;
216 for (auto lit : lits)
217 newLit += lit;
218 return StringAttr::get(ctxt, newLit);
219}
220
221OpFoldResult FormatStringConcatOp::fold(FoldAdaptor adaptor) {
222 if (getNumOperands() == 0)
223 return StringAttr::get(getContext(), "");
224 if (getNumOperands() == 1) {
225 // Don't fold to our own result to avoid an infinte loop.
226 if (getResult() == getOperand(0))
227 return {};
228 return getOperand(0);
229 }
230
231 // Fold if all operands are literals.
232 SmallVector<StringRef> lits;
233 for (auto attr : adaptor.getInputs()) {
234 auto lit = dyn_cast_or_null<StringAttr>(attr);
235 if (!lit)
236 return {};
237 lits.push_back(lit);
238 }
239 return concatLiterals(getContext(), lits);
240}
241
242LogicalResult FormatStringConcatOp::getFlattenedInputs(
243 llvm::SmallVectorImpl<Value> &flatOperands) {
244 llvm::SmallMapVector<FormatStringConcatOp, unsigned, 4> concatStack;
245 bool isCyclic = false;
246
247 // Perform a DFS on this operation's concatenated operands,
248 // collect the leaf format string fragments.
249 concatStack.insert({*this, 0});
250 while (!concatStack.empty()) {
251 auto &top = concatStack.back();
252 auto currentConcat = top.first;
253 unsigned operandIndex = top.second;
254
255 // Iterate over concatenated operands
256 while (operandIndex < currentConcat.getNumOperands()) {
257 auto currentOperand = currentConcat.getOperand(operandIndex);
258
259 if (auto nextConcat =
260 currentOperand.getDefiningOp<FormatStringConcatOp>()) {
261 // Concat of a concat
262 if (!concatStack.contains(nextConcat)) {
263 // Save the next operand index to visit on the
264 // stack and put the new concat on top.
265 top.second = operandIndex + 1;
266 concatStack.insert({nextConcat, 0});
267 break;
268 }
269 // Cyclic concatenation encountered. Don't recurse.
270 isCyclic = true;
271 }
272
273 flatOperands.push_back(currentOperand);
274 operandIndex++;
275 }
276
277 // Pop the concat off of the stack if we have visited all operands.
278 if (operandIndex >= currentConcat.getNumOperands())
279 concatStack.pop_back();
280 }
281
282 return success(!isCyclic);
283}
284
285LogicalResult FormatStringConcatOp::verify() {
286 if (llvm::any_of(getOperands(),
287 [&](Value operand) { return operand == getResult(); }))
288 return emitOpError("is infinitely recursive.");
289 return success();
290}
291
292LogicalResult FormatStringConcatOp::canonicalize(FormatStringConcatOp op,
293 PatternRewriter &rewriter) {
294
295 auto fmtStrType = FormatStringType::get(op.getContext());
296
297 // Check if we can flatten concats of concats
298 bool hasBeenFlattened = false;
299 SmallVector<Value, 0> flatOperands;
300 if (!op.isFlat()) {
301 // Get a new, flattened list of operands
302 flatOperands.reserve(op.getNumOperands() + 4);
303 auto isAcyclic = op.getFlattenedInputs(flatOperands);
304
305 if (failed(isAcyclic)) {
306 // Infinite recursion, but we cannot fail compilation right here (can we?)
307 // so just emit a warning and bail out.
308 op.emitWarning("Cyclic concatenation detected.");
309 return failure();
310 }
311
312 hasBeenFlattened = true;
313 }
314
315 if (!hasBeenFlattened && op.getNumOperands() < 2)
316 return failure(); // Should be handled by the folder
317
318 // Check if there are adjacent literals we can merge or empty literals to
319 // remove
320 SmallVector<StringRef> litSequence;
321 SmallVector<Value> newOperands;
322 newOperands.reserve(op.getNumOperands());
323 FormatLiteralOp prevLitOp;
324
325 auto oldOperands = hasBeenFlattened ? flatOperands : op.getOperands();
326 for (auto operand : oldOperands) {
327 if (auto litOp = operand.getDefiningOp<FormatLiteralOp>()) {
328 if (!litOp.getLiteral().empty()) {
329 prevLitOp = litOp;
330 litSequence.push_back(litOp.getLiteral());
331 }
332 } else {
333 if (!litSequence.empty()) {
334 if (litSequence.size() > 1) {
335 // Create a fused literal.
336 auto newLit = rewriter.createOrFold<FormatLiteralOp>(
337 op.getLoc(), fmtStrType,
338 concatLiterals(op.getContext(), litSequence));
339 newOperands.push_back(newLit);
340 } else {
341 // Reuse the existing literal.
342 newOperands.push_back(prevLitOp.getResult());
343 }
344 litSequence.clear();
345 }
346 newOperands.push_back(operand);
347 }
348 }
349
350 // Push trailing literals into the new operand list
351 if (!litSequence.empty()) {
352 if (litSequence.size() > 1) {
353 // Create a fused literal.
354 auto newLit = rewriter.createOrFold<FormatLiteralOp>(
355 op.getLoc(), fmtStrType,
356 concatLiterals(op.getContext(), litSequence));
357 newOperands.push_back(newLit);
358 } else {
359 // Reuse the existing literal.
360 newOperands.push_back(prevLitOp.getResult());
361 }
362 }
363
364 if (!hasBeenFlattened && newOperands.size() == op.getNumOperands())
365 return failure(); // Nothing changed
366
367 if (newOperands.empty())
368 rewriter.replaceOpWithNewOp<FormatLiteralOp>(op, fmtStrType,
369 rewriter.getStringAttr(""));
370 else if (newOperands.size() == 1)
371 rewriter.replaceOp(op, newOperands);
372 else
373 rewriter.modifyOpInPlace(op, [&]() { op->setOperands(newOperands); });
374
375 return success();
376}
377
378LogicalResult PrintFormattedOp::canonicalize(PrintFormattedOp op,
379 PatternRewriter &rewriter) {
380 // Remove ops with constant false condition.
381 if (auto cstCond = op.getCondition().getDefiningOp<hw::ConstantOp>()) {
382 if (cstCond.getValue().isZero()) {
383 rewriter.eraseOp(op);
384 return success();
385 }
386 }
387 return failure();
388}
389
390LogicalResult PrintFormattedProcOp::verify() {
391 // Check if we know for sure that the parent is not procedural.
392 auto *parentOp = getOperation()->getParentOp();
393
394 if (!parentOp)
395 return emitOpError("must be within a procedural region.");
396
397 if (isa_and_nonnull<hw::HWDialect>(parentOp->getDialect())) {
398 if (!isa<hw::TriggeredOp>(parentOp))
399 return emitOpError("must be within a procedural region.");
400 return success();
401 }
402
403 if (isa_and_nonnull<sv::SVDialect>(parentOp->getDialect())) {
404 if (!parentOp->hasTrait<sv::ProceduralRegion>())
405 return emitOpError("must be within a procedural region.");
406 return success();
407 }
408
409 // Don't fail for dialects that are not explicitly handled.
410 return success();
411}
412
413LogicalResult PrintFormattedProcOp::canonicalize(PrintFormattedProcOp op,
414 PatternRewriter &rewriter) {
415 // Remove empty prints.
416 if (auto litInput = op.getInput().getDefiningOp<FormatLiteralOp>()) {
417 if (litInput.getLiteral().empty()) {
418 rewriter.eraseOp(op);
419 return success();
420 }
421 }
422 return failure();
423}
424
425//===----------------------------------------------------------------------===//
426// TableGen generated logic.
427//===----------------------------------------------------------------------===//
428
429// Provide the autogenerated implementation guts for the Op classes.
430#define GET_OP_CLASSES
431#include "circt/Dialect/Sim/Sim.cpp.inc"
assert(baseType &&"element must be base type")
static StringAttr concatLiterals(MLIRContext *ctxt, ArrayRef< StringRef > lits)
Definition SimOps.cpp:211
Signals that an operations regions are procedural.
Definition SVOps.h:176
ParseResult parseModuleSignature(OpAsmParser &parser, SmallVectorImpl< PortParse > &args, TypeAttr &modType)
New Style parsing.
void printModuleSignatureNew(OpAsmPrinter &p, Region &body, hw::ModuleType modType, ArrayRef< Attribute > portAttrs, ArrayRef< Location > locAttrs)
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition sim.py:1