CIRCT 23.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
19#include "mlir/Dialect/Func/IR/FuncOps.h"
20#include "mlir/Dialect/LLVMIR/LLVMTypes.h"
21#include "mlir/IR/PatternMatch.h"
22#include "mlir/Interfaces/FunctionImplementation.h"
23#include "llvm/ADT/MapVector.h"
24
25using namespace mlir;
26using namespace circt;
27using namespace sim;
28
29//===----------------------------------------------------------------------===//
30// DPIFuncOp
31//===----------------------------------------------------------------------===//
32
33void DPIFuncOp::build(OpBuilder &odsBuilder, OperationState &odsState,
34 StringAttr symName, ArrayRef<StringAttr> argNames,
35 ArrayRef<Type> argTypes,
36 ArrayRef<DPIDirection> argDirections, ArrayAttr argLocs,
37 StringAttr verilogName) {
38 // Build DPIFunctionType from argument info.
39 SmallVector<DPIArgument> args;
40 args.reserve(argNames.size());
41 for (auto [name, type, dir] : llvm::zip(argNames, argTypes, argDirections))
42 args.push_back({name, type, dir});
43 auto dpiType = DPIFunctionType::get(odsBuilder.getContext(), args);
44 build(odsBuilder, odsState, symName, dpiType, argLocs, verilogName);
45}
46
47void DPIFuncOp::build(OpBuilder &odsBuilder, OperationState &odsState,
48 StringAttr symName, DPIFunctionType dpiFunctionType,
49 ArrayAttr argLocs, StringAttr verilogName) {
50 odsState.addAttribute(getSymNameAttrName(odsState.name), symName);
51 odsState.addAttribute(getDpiFunctionTypeAttrName(odsState.name),
52 TypeAttr::get(dpiFunctionType));
53 if (argLocs)
54 odsState.addAttribute(getArgumentLocsAttrName(odsState.name), argLocs);
55 if (verilogName)
56 odsState.addAttribute(getVerilogNameAttrName(odsState.name), verilogName);
57 odsState.addRegion();
58}
59
60::mlir::Type DPIFuncOp::getFunctionType() {
61 return getDpiFunctionType().getFunctionType();
62}
63
64void DPIFuncOp::setFunctionTypeAttr(::mlir::TypeAttr type) {
65 // function_type is always derived from dpi_function_type.
66 auto dpiType = llvm::dyn_cast<DPIFunctionType>(type.getValue());
67 assert(dpiType && "DPIFuncOp function type can only be set via "
68 "DPIFunctionType, not a plain FunctionType");
69 setDpiFunctionType(dpiType);
70}
71
72::mlir::Type DPIFuncOp::cloneTypeWith(::mlir::TypeRange inputs,
73 ::mlir::TypeRange results) {
74 return FunctionType::get(getContext(), inputs, results);
75}
76
77ParseResult DPIFuncOp::parse(OpAsmParser &parser, OperationState &result) {
78 auto builder = parser.getBuilder();
79 auto ctx = builder.getContext();
80
81 (void)mlir::impl::parseOptionalVisibilityKeyword(parser, result.attributes);
82
83 StringAttr nameAttr;
84 if (parser.parseSymbolName(nameAttr, SymbolTable::getSymbolAttrName(),
85 result.attributes))
86 return failure();
87
88 SmallVector<DPIArgument> args;
89 SmallVector<Attribute> argLocs;
90 auto unknownLoc = builder.getUnknownLoc();
91 bool hasLocs = false;
92
93 auto parseOneArg = [&]() -> ParseResult {
94 StringRef dirKeyword;
95 auto keyLoc = parser.getCurrentLocation();
96 if (parser.parseKeyword(&dirKeyword))
97 return failure();
98 auto dir = parseDPIDirectionKeyword(dirKeyword);
99 if (!dir)
100 return parser.emitError(keyLoc,
101 "expected DPI argument direction keyword");
102
103 // For input/inout/ref args, parse SSA name; for output/return, bare name.
104 bool hasSSA = isCallOperandDir(*dir);
105 std::string argName;
106 if (hasSSA) {
107 OpAsmParser::UnresolvedOperand ssaName;
108 if (parser.parseOperand(ssaName, /*allowResultNumber=*/false))
109 return failure();
110 argName = ssaName.name.substr(1).str();
111 } else {
112 if (parser.parseKeywordOrString(&argName))
113 return failure();
114 }
115
116 Type argType;
117 if (parser.parseColonType(argType))
118 return failure();
119 args.push_back({StringAttr::get(ctx, argName), argType, *dir});
120
121 std::optional<Location> maybeLoc;
122 if (failed(parser.parseOptionalLocationSpecifier(maybeLoc)))
123 return failure();
124 if (maybeLoc) {
125 argLocs.push_back(*maybeLoc);
126 hasLocs = true;
127 } else {
128 argLocs.push_back(unknownLoc);
129 }
130 return success();
131 };
132
133 if (parser.parseCommaSeparatedList(OpAsmParser::Delimiter::Paren, parseOneArg,
134 " in DPI argument list"))
135 return failure();
136
137 auto dpiType = DPIFunctionType::get(ctx, args);
138
139 result.addAttribute(DPIFuncOp::getDpiFunctionTypeAttrName(result.name),
140 TypeAttr::get(dpiType));
141 if (hasLocs)
142 result.addAttribute(DPIFuncOp::getArgumentLocsAttrName(result.name),
143 builder.getArrayAttr(argLocs));
144 result.addRegion();
145
146 if (failed(parser.parseOptionalAttrDictWithKeyword(result.attributes)))
147 return failure();
148 return success();
149}
150
151void DPIFuncOp::print(OpAsmPrinter &p) {
152 p << ' ';
153
154 StringRef visibilityAttrName = SymbolTable::getVisibilityAttrName();
155 if (auto visibility = (*this)->getAttrOfType<StringAttr>(visibilityAttrName))
156 p << visibility.getValue() << ' ';
157 p.printSymbolName(getSymName());
158
159 auto dpiType = getDpiFunctionType();
160 auto dpiArgs = dpiType.getArguments();
161
162 p << '(';
163 llvm::interleaveComma(llvm::enumerate(dpiArgs), p, [&](auto it) {
164 auto &arg = it.value();
165 auto i = it.index();
166
167 p << stringifyDPIDirectionKeyword(arg.dir) << ' ';
168
169 if (isCallOperandDir(arg.dir))
170 p << '%';
171 p.printKeywordOrString(arg.name.getValue());
172 p << " : ";
173 p.printType(arg.type);
174
175 if (getArgumentLocs()) {
176 auto loc = cast<Location>(getArgumentLocsAttr()[i]);
177 if (loc != UnknownLoc::get(getContext()))
178 p.printOptionalLocationSpecifier(loc);
179 }
180 });
181 p << ')';
182
183 mlir::function_interface_impl::printFunctionAttributes(
184 p, *this,
185 {visibilityAttrName, getDpiFunctionTypeAttrName(),
186 getArgumentLocsAttrName()});
187}
188
189LogicalResult DPIFuncOp::verify() {
190 auto dpiType = getDpiFunctionType();
191
192 // Structural constraints shared with all DPIFunctionType users.
193 if (failed(dpiType.verify([&]() { return emitOpError(); })))
194 return failure();
195
196 // Sim-specific constraints.
197 for (auto &arg : dpiType.getArguments()) {
198 if (arg.dir == DPIDirection::Ref) {
199 if (!isa<LLVM::LLVMPointerType>(arg.type))
200 return emitOpError("'ref' arguments must use !llvm.ptr type");
201 }
202 }
203
204 return success();
205}
206
207LogicalResult
208sim::DPICallOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
209 auto referencedOp =
210 symbolTable.lookupNearestSymbolFrom(*this, getCalleeAttr());
211 if (!referencedOp)
212 return emitError("cannot find function declaration '")
213 << getCallee() << "'";
214 if (auto dpiFunc = dyn_cast<sim::DPIFuncOp>(referencedOp)) {
215 auto expectedFuncType = cast<FunctionType>(dpiFunc.getFunctionType());
216 auto expectedInputs = expectedFuncType.getInputs();
217 auto expectedResults = expectedFuncType.getResults();
218 if (getInputs().size() != expectedInputs.size())
219 return emitError("expects ")
220 << expectedInputs.size() << " DPI operands, but got "
221 << getInputs().size();
222 if (getResults().size() != expectedResults.size())
223 return emitError("expects ")
224 << expectedResults.size() << " DPI results, but got "
225 << getResults().size();
226 for (auto [operand, expectedType] : llvm::zip(getInputs(), expectedInputs))
227 if (operand.getType() != expectedType)
228 return emitError("operand type mismatch: expected ")
229 << expectedType << ", but got " << operand.getType();
230 for (auto [result, expectedType] : llvm::zip(getResults(), expectedResults))
231 if (result.getType() != expectedType)
232 return emitError("result type mismatch: expected ")
233 << expectedType << ", but got " << result.getType();
234 return success();
235 }
236 if (isa<func::FuncOp>(referencedOp))
237 return success();
238 return emitError("callee must be 'sim.func.dpi' or 'func.func' but got '")
239 << referencedOp->getName() << "'";
240}
241
242static StringAttr formatIntegersByRadix(MLIRContext *ctx, unsigned radix,
243 const Attribute &value,
244 bool isUpperCase, bool isLeftAligned,
245 char paddingChar,
246 std::optional<unsigned> specifierWidth,
247 bool isSigned = false) {
248 auto intAttr = llvm::dyn_cast_or_null<IntegerAttr>(value);
249 if (!intAttr)
250 return {};
251 if (intAttr.getType().getIntOrFloatBitWidth() == 0)
252 return StringAttr::get(ctx, "");
253
254 SmallVector<char, 32> strBuf;
255 intAttr.getValue().toString(strBuf, radix, isSigned, false, isUpperCase);
256 unsigned width = intAttr.getType().getIntOrFloatBitWidth();
257
258 unsigned padWidth;
259 switch (radix) {
260 case 2:
261 padWidth = width;
262 break;
263 case 8:
264 padWidth = (width + 2) / 3;
265 break;
266 case 16:
267 padWidth = (width + 3) / 4;
268 break;
269 default:
270 padWidth = width;
271 break;
272 }
273
274 unsigned numSpaces = 0;
275 if (specifierWidth.has_value() &&
276 (specifierWidth.value() >
277 std::max(padWidth, static_cast<unsigned>(strBuf.size())))) {
278 numSpaces = std::max(
279 0U, specifierWidth.value() -
280 std::max(padWidth, static_cast<unsigned>(strBuf.size())));
281 }
282
283 SmallVector<char, 1> spacePadding(numSpaces, ' ');
284
285 padWidth = padWidth > strBuf.size() ? padWidth - strBuf.size() : 0;
286
287 SmallVector<char, 32> padding(padWidth, paddingChar);
288 if (isLeftAligned) {
289 return StringAttr::get(ctx, Twine(padding) + Twine(strBuf) +
290 Twine(spacePadding));
291 }
292 return StringAttr::get(ctx,
293 Twine(spacePadding) + Twine(padding) + Twine(strBuf));
294}
295
296static StringAttr formatFloatsBySpecifier(MLIRContext *ctx, Attribute value,
297 bool isLeftAligned,
298 std::optional<unsigned> fieldWidth,
299 std::optional<unsigned> fracDigits,
300 std::string formatSpecifier) {
301 if (auto floatAttr = llvm::dyn_cast_or_null<FloatAttr>(value)) {
302 std::string widthString = isLeftAligned ? "-" : "";
303 if (fieldWidth.has_value()) {
304 widthString += std::to_string(fieldWidth.value());
305 }
306 std::string fmtSpecifier = "%" + widthString + "." +
307 std::to_string(fracDigits.value()) +
308 formatSpecifier;
309
310 // Calculates number of bytes needed to store the format string
311 // excluding the null terminator
312 int bufferSize = std::snprintf(nullptr, 0, fmtSpecifier.c_str(),
313 floatAttr.getValue().convertToDouble());
314 std::string floatFmtBuffer(bufferSize, '\0');
315 snprintf(floatFmtBuffer.data(), bufferSize + 1, fmtSpecifier.c_str(),
316 floatAttr.getValue().convertToDouble());
317 return StringAttr::get(ctx, floatFmtBuffer);
318 }
319 return {};
320}
321
322// (DPIFuncOp parse/print/verify are now defined above, near the top of the
323// file)
324
325OpFoldResult FormatLiteralOp::fold(FoldAdaptor adaptor) {
326 return getLiteralAttr();
327}
328
329// --- FormatStringOp ---
330
331StringAttr FormatStringOp::formatConstant(Attribute constVal) {
332 auto strAttr = llvm::dyn_cast<StringAttr>(constVal);
333 if (!strAttr)
334 return {};
335
336 SmallString<128> strBuf(strAttr.getValue());
337 if (getSpecifierWidth().has_value()) {
338 auto padChar = static_cast<char>(getPaddingChar());
339 unsigned padWidth = getSpecifierWidth().value();
340 padWidth = padWidth > strBuf.size() ? padWidth - strBuf.size() : 0;
341 if (getIsLeftAligned())
342 strBuf.append(padWidth, padChar);
343 else
344 strBuf.insert(strBuf.begin(), padWidth, padChar);
345 }
346 return StringAttr::get(getContext(), strBuf);
347}
348
349// --- FormatDecOp ---
350
351StringAttr FormatDecOp::formatConstant(Attribute constVal) {
352 auto intAttr = llvm::dyn_cast<IntegerAttr>(constVal);
353 if (!intAttr)
354 return {};
355 SmallVector<char, 16> strBuf;
356 intAttr.getValue().toString(strBuf, 10, getIsSigned());
357 unsigned padWidth;
358 if (getSpecifierWidth().has_value()) {
359 padWidth = getSpecifierWidth().value();
360 } else {
361 unsigned width = intAttr.getType().getIntOrFloatBitWidth();
362 padWidth = FormatDecOp::getDecimalWidth(width, getIsSigned());
363 }
364
365 padWidth = padWidth > strBuf.size() ? padWidth - strBuf.size() : 0;
366
367 SmallVector<char, 10> padding(padWidth, getPaddingChar());
368 if (getIsLeftAligned())
369 return StringAttr::get(getContext(), Twine(strBuf) + Twine(padding));
370 return StringAttr::get(getContext(), Twine(padding) + Twine(strBuf));
371}
372
373OpFoldResult FormatDecOp::fold(FoldAdaptor adaptor) {
374 if (getValue().getType().getIntOrFloatBitWidth() == 0)
375 return StringAttr::get(getContext(), "0");
376 return {};
377}
378
379// --- FormatHexOp ---
380
381StringAttr FormatHexOp::formatConstant(Attribute constVal) {
382 return formatIntegersByRadix(constVal.getContext(), 16, constVal,
383 getIsHexUppercase(), getIsLeftAligned(),
384 getPaddingChar(), getSpecifierWidth());
385}
386
387OpFoldResult FormatHexOp::fold(FoldAdaptor adaptor) {
388 if (getValue().getType().getIntOrFloatBitWidth() == 0)
390 getContext(), 16, IntegerAttr::get(getValue().getType(), 0), false,
391 getIsLeftAligned(), getPaddingChar(), getSpecifierWidth());
392 return {};
393}
394
395// --- FormatOctOp ---
396
397StringAttr FormatOctOp::formatConstant(Attribute constVal) {
398 return formatIntegersByRadix(constVal.getContext(), 8, constVal, false,
399 getIsLeftAligned(), getPaddingChar(),
400 getSpecifierWidth());
401}
402
403OpFoldResult FormatOctOp::fold(FoldAdaptor adaptor) {
404 if (getValue().getType().getIntOrFloatBitWidth() == 0)
406 getContext(), 8, IntegerAttr::get(getValue().getType(), 0), false,
407 getIsLeftAligned(), getPaddingChar(), getSpecifierWidth());
408 return {};
409}
410
411// --- FormatBinOp ---
412
413StringAttr FormatBinOp::formatConstant(Attribute constVal) {
414 return formatIntegersByRadix(constVal.getContext(), 2, constVal, false,
415 getIsLeftAligned(), getPaddingChar(),
416 getSpecifierWidth());
417}
418
419OpFoldResult FormatBinOp::fold(FoldAdaptor adaptor) {
420 if (getValue().getType().getIntOrFloatBitWidth() == 0)
422 getContext(), 2, IntegerAttr::get(getValue().getType(), 0), false,
423 getIsLeftAligned(), getPaddingChar(), getSpecifierWidth());
424 return {};
425}
426
427// --- FormatScientificOp ---
428
429StringAttr FormatScientificOp::formatConstant(Attribute constVal) {
430 return formatFloatsBySpecifier(getContext(), constVal, getIsLeftAligned(),
431 getFieldWidth(), getFracDigits(), "e");
432}
433
434// --- FormatFloatOp ---
435
436StringAttr FormatFloatOp::formatConstant(Attribute constVal) {
437 return formatFloatsBySpecifier(getContext(), constVal, getIsLeftAligned(),
438 getFieldWidth(), getFracDigits(), "f");
439}
440
441// --- FormatGeneralOp ---
442
443StringAttr FormatGeneralOp::formatConstant(Attribute constVal) {
444 return formatFloatsBySpecifier(getContext(), constVal, getIsLeftAligned(),
445 getFieldWidth(), getFracDigits(), "g");
446}
447
448// --- FormatCharOp ---
449
450StringAttr FormatCharOp::formatConstant(Attribute constVal) {
451 auto intCst = dyn_cast<IntegerAttr>(constVal);
452 if (!intCst)
453 return {};
454 if (intCst.getType().getIntOrFloatBitWidth() == 0)
455 return StringAttr::get(getContext(), Twine(static_cast<char>(0)));
456 if (intCst.getType().getIntOrFloatBitWidth() > 8)
457 return {};
458 auto intValue = intCst.getValue().getZExtValue();
459 return StringAttr::get(getContext(), Twine(static_cast<char>(intValue)));
460}
461
462OpFoldResult FormatCharOp::fold(FoldAdaptor adaptor) {
463 if (getValue().getType().getIntOrFloatBitWidth() == 0)
464 return StringAttr::get(getContext(), Twine(static_cast<char>(0)));
465 return {};
466}
467
468static StringAttr concatLiterals(MLIRContext *ctxt, ArrayRef<StringRef> lits) {
469 assert(!lits.empty() && "No literals to concatenate");
470 if (lits.size() == 1)
471 return StringAttr::get(ctxt, lits.front());
472 SmallString<64> newLit;
473 for (auto lit : lits)
474 newLit += lit;
475 return StringAttr::get(ctxt, newLit);
476}
477
478OpFoldResult FormatStringConcatOp::fold(FoldAdaptor adaptor) {
479 if (getNumOperands() == 0)
480 return StringAttr::get(getContext(), "");
481 if (getNumOperands() == 1) {
482 // Don't fold to our own result to avoid an infinte loop.
483 if (getResult() == getOperand(0))
484 return {};
485 return getOperand(0);
486 }
487
488 // Fold if all operands are literals.
489 SmallVector<StringRef> lits;
490 for (auto attr : adaptor.getInputs()) {
491 auto lit = dyn_cast_or_null<StringAttr>(attr);
492 if (!lit)
493 return {};
494 lits.push_back(lit);
495 }
496 return concatLiterals(getContext(), lits);
497}
498
499LogicalResult FormatStringConcatOp::getFlattenedInputs(
500 llvm::SmallVectorImpl<Value> &flatOperands) {
502 bool isCyclic = false;
503
504 // Perform a DFS on this operation's concatenated operands,
505 // collect the leaf format string fragments.
506 concatStack.insert({*this, 0});
507 while (!concatStack.empty()) {
508 auto &top = concatStack.back();
509 auto currentConcat = top.first;
510 unsigned operandIndex = top.second;
511
512 // Iterate over concatenated operands
513 while (operandIndex < currentConcat.getNumOperands()) {
514 auto currentOperand = currentConcat.getOperand(operandIndex);
515
516 if (auto nextConcat =
517 currentOperand.getDefiningOp<FormatStringConcatOp>()) {
518 // Concat of a concat
519 if (!concatStack.contains(nextConcat)) {
520 // Save the next operand index to visit on the
521 // stack and put the new concat on top.
522 top.second = operandIndex + 1;
523 concatStack.insert({nextConcat, 0});
524 break;
525 }
526 // Cyclic concatenation encountered. Don't recurse.
527 isCyclic = true;
528 }
529
530 flatOperands.push_back(currentOperand);
531 operandIndex++;
532 }
533
534 // Pop the concat off of the stack if we have visited all operands.
535 if (operandIndex >= currentConcat.getNumOperands())
536 concatStack.pop_back();
537 }
538
539 return success(!isCyclic);
540}
541
542LogicalResult FormatStringConcatOp::verify() {
543 if (llvm::any_of(getOperands(),
544 [&](Value operand) { return operand == getResult(); }))
545 return emitOpError("is infinitely recursive.");
546 return success();
547}
548
549LogicalResult FormatStringConcatOp::canonicalize(FormatStringConcatOp op,
550 PatternRewriter &rewriter) {
551 // Any helper literals created during canonicalization must dominate `op`.
552 rewriter.setInsertionPoint(op);
553
554 auto fmtStrType = FormatStringType::get(op.getContext());
555
556 // Check if we can flatten concats of concats
557 bool hasBeenFlattened = false;
558 SmallVector<Value, 0> flatOperands;
559 if (!op.isFlat()) {
560 // Get a new, flattened list of operands
561 flatOperands.reserve(op.getNumOperands() + 4);
562 auto isAcyclic = op.getFlattenedInputs(flatOperands);
563
564 if (failed(isAcyclic)) {
565 // Infinite recursion, but we cannot fail compilation right here (can we?)
566 // so just emit a warning and bail out.
567 op.emitWarning("Cyclic concatenation detected.");
568 return failure();
569 }
570
571 hasBeenFlattened = true;
572 }
573
574 if (!hasBeenFlattened && op.getNumOperands() < 2)
575 return failure(); // Should be handled by the folder
576
577 // Check if there are adjacent literals we can merge or empty literals to
578 // remove
579 SmallVector<StringRef> litSequence;
580 SmallVector<Value> newOperands;
581 newOperands.reserve(op.getNumOperands());
582 FormatLiteralOp prevLitOp;
583
584 auto oldOperands = hasBeenFlattened ? flatOperands : op.getOperands();
585 for (auto operand : oldOperands) {
586 if (auto litOp = operand.getDefiningOp<FormatLiteralOp>()) {
587 if (!litOp.getLiteral().empty()) {
588 prevLitOp = litOp;
589 litSequence.push_back(litOp.getLiteral());
590 }
591 } else {
592 if (!litSequence.empty()) {
593 if (litSequence.size() > 1) {
594 // Create a fused literal.
595 auto newLit = rewriter.createOrFold<FormatLiteralOp>(
596 op.getLoc(), fmtStrType,
597 concatLiterals(op.getContext(), litSequence));
598 newOperands.push_back(newLit);
599 } else {
600 // Reuse the existing literal.
601 newOperands.push_back(prevLitOp.getResult());
602 }
603 litSequence.clear();
604 }
605 newOperands.push_back(operand);
606 }
607 }
608
609 // Push trailing literals into the new operand list
610 if (!litSequence.empty()) {
611 if (litSequence.size() > 1) {
612 // Create a fused literal.
613 auto newLit = rewriter.createOrFold<FormatLiteralOp>(
614 op.getLoc(), fmtStrType,
615 concatLiterals(op.getContext(), litSequence));
616 newOperands.push_back(newLit);
617 } else {
618 // Reuse the existing literal.
619 newOperands.push_back(prevLitOp.getResult());
620 }
621 }
622
623 if (!hasBeenFlattened && newOperands.size() == op.getNumOperands())
624 return failure(); // Nothing changed
625
626 if (newOperands.empty())
627 rewriter.replaceOpWithNewOp<FormatLiteralOp>(op, fmtStrType,
628 rewriter.getStringAttr(""));
629 else if (newOperands.size() == 1)
630 rewriter.replaceOp(op, newOperands);
631 else
632 rewriter.modifyOpInPlace(op, [&]() { op->setOperands(newOperands); });
633
634 return success();
635}
636
637LogicalResult PrintFormattedOp::canonicalize(PrintFormattedOp op,
638 PatternRewriter &rewriter) {
639 // Remove ops with constant false condition.
640 if (auto cstCond = op.getCondition().getDefiningOp<hw::ConstantOp>()) {
641 if (cstCond.getValue().isZero()) {
642 rewriter.eraseOp(op);
643 return success();
644 }
645 }
646 return failure();
647}
648
649LogicalResult PrintFormattedProcOp::canonicalize(PrintFormattedProcOp op,
650 PatternRewriter &rewriter) {
651 // Remove empty prints.
652 if (auto litInput = op.getInput().getDefiningOp<FormatLiteralOp>()) {
653 if (litInput.getLiteral().empty()) {
654 rewriter.eraseOp(op);
655 return success();
656 }
657 }
658 return failure();
659}
660
661OpFoldResult StringConstantOp::fold(FoldAdaptor adaptor) {
662 return adaptor.getLiteralAttr();
663}
664
665OpFoldResult StringConcatOp::fold(FoldAdaptor adaptor) {
666 auto operands = adaptor.getInputs();
667 if (operands.empty())
668 return StringAttr::get(getContext(), "");
669
670 SmallString<128> result;
671 for (auto &operand : operands) {
672 auto strAttr = cast_if_present<StringAttr>(operand);
673 if (!strAttr)
674 return {};
675 result += strAttr.getValue();
676 }
677
678 return StringAttr::get(getContext(), result);
679}
680
681OpFoldResult StringLengthOp::fold(FoldAdaptor adaptor) {
682 auto inputAttr = adaptor.getInput();
683 if (!inputAttr)
684 return {};
685
686 if (auto strAttr = cast<StringAttr>(inputAttr))
687 return IntegerAttr::get(getType(), strAttr.getValue().size());
688
689 return {};
690}
691
692OpFoldResult IntToStringOp::fold(FoldAdaptor adaptor) {
693 auto intAttr = cast_or_null<IntegerAttr>(adaptor.getInput());
694 if (!intAttr)
695 return {};
696
697 SmallString<128> result;
698 auto width = intAttr.getType().getIntOrFloatBitWidth();
699 // Starting from the LSB, we extract the values byte-by-byte,
700 // and convert each non-null byte to a char
701
702 // For example 0x00_00_00_48_00_00_6C_6F would look like "Hlo"
703 for (unsigned int i = 0; i < width; i += 8) {
704 auto byte =
705 intAttr.getValue().extractBitsAsZExtValue(std::min(width - i, 8U), i);
706 if (byte)
707 result.push_back(static_cast<char>(byte));
708 }
709 std::reverse(result.begin(), result.end());
710 return StringAttr::get(getContext(), result);
711 return {};
712}
713
714//===----------------------------------------------------------------------===//
715// StringGetOp
716//===----------------------------------------------------------------------===//
717
718OpFoldResult StringGetOp::fold(FoldAdaptor adaptor) {
719 auto strAttr = cast_or_null<StringAttr>(adaptor.getStr());
720 auto indexAttr = cast_or_null<IntegerAttr>(adaptor.getIndex());
721 if (!strAttr || !indexAttr)
722 return {};
723
724 auto str = strAttr.getValue();
725 int64_t index = indexAttr.getValue().getSExtValue();
726
727 // Out-of-bounds access returns 0 (null character) per IEEE 1800-2023 ยง 6.16
728 if (index < 0 || index >= static_cast<int64_t>(str.size()))
729 return IntegerAttr::get(getType(), 0);
730
731 // Return the character at the specified index
732 uint8_t ch = static_cast<uint8_t>(str[index]);
733 return IntegerAttr::get(getType(), ch);
734}
735
736//===----------------------------------------------------------------------===//
737// QueueResizeOp
738//===----------------------------------------------------------------------===//
739
740LogicalResult QueueResizeOp::verify() {
741 if (cast<QueueType>(getInput().getType()).getElementType() !=
742 cast<QueueType>(getResult().getType()).getElementType())
743 return failure();
744 return success();
745}
746
747LogicalResult QueueFromArrayOp::verify() {
748 auto queueElementType =
749 cast<QueueType>(getResult().getType()).getElementType();
750
751 auto arrayElementType =
752 cast<hw::ArrayType>(getInput().getType()).getElementType();
753
754 if (queueElementType != arrayElementType) {
755 return emitOpError() << "sim::Queue element type " << queueElementType
756 << " doesn't match hw::ArrayType element type "
757 << arrayElementType;
758 }
759
760 return success();
761}
762
763LogicalResult QueueConcatOp::verify() {
764 // Verify the element types of all concatenated queues equal that of the
765 // result queue. (but not the bounds)
766 auto resultElType = cast<QueueType>(getResult().getType()).getElementType();
767
768 for (Value input : getInputs()) {
769 auto inpElType = cast<QueueType>(input.getType()).getElementType();
770 if (inpElType != resultElType) {
771 return emitOpError() << "sim::Queue element type " << inpElType
772 << " doesn't match result sim::Queue element type "
773 << resultElType;
774 }
775 }
776
777 return success();
778}
779
780//===----------------------------------------------------------------------===//
781// TriggeredOp
782//===----------------------------------------------------------------------===//
783
784void TriggeredOp::build(OpBuilder &builder, OperationState &odsState,
785 Value clock, Value condition) {
786 odsState.addOperands(clock);
787 if (condition)
788 odsState.addOperands(condition);
789
790 auto *region = odsState.addRegion();
791 region->push_back(new Block());
792}
793
794void TriggeredOp::build(OpBuilder &builder, OperationState &odsState,
795 Value clock, Value condition,
796 llvm::function_ref<void()> bodyCtor) {
797 OpBuilder::InsertionGuard guard(builder);
798
799 odsState.addOperands(clock);
800 if (condition)
801 odsState.addOperands(condition);
802
803 builder.createBlock(odsState.addRegion());
804 if (bodyCtor)
805 bodyCtor();
806}
807
808//===----------------------------------------------------------------------===//
809// TableGen generated logic.
810//===----------------------------------------------------------------------===//
811
812#include "circt/Dialect/Sim/SimOpInterfaces.cpp.inc"
813
814// Provide the autogenerated implementation guts for the Op classes.
815#define GET_OP_CLASSES
816#include "circt/Dialect/Sim/Sim.cpp.inc"
817#include "circt/Dialect/Sim/SimEnums.cpp.inc"
assert(baseType &&"element must be base type")
static StringAttr formatFloatsBySpecifier(MLIRContext *ctx, Attribute value, bool isLeftAligned, std::optional< unsigned > fieldWidth, std::optional< unsigned > fracDigits, std::string formatSpecifier)
Definition SimOps.cpp:296
static StringAttr formatIntegersByRadix(MLIRContext *ctx, unsigned radix, const Attribute &value, bool isUpperCase, bool isLeftAligned, char paddingChar, std::optional< unsigned > specifierWidth, bool isSigned=false)
Definition SimOps.cpp:242
static StringAttr concatLiterals(MLIRContext *ctxt, ArrayRef< StringRef > lits)
Definition SimOps.cpp:468
llvm::StringRef stringifyDPIDirectionKeyword(DPIDirection dir)
Return the keyword string for a DPIDirection (e.g. "in", "return").
Definition SimTypes.cpp:27
std::optional< DPIDirection > parseDPIDirectionKeyword(llvm::StringRef keyword)
Parse a keyword string to a DPIDirection. Returns std::nullopt on failure.
bool isCallOperandDir(DPIDirection dir)
True if an argument with this direction is a call operand (input/inout/ref).
Definition SimTypes.cpp:53
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition sim.py:1