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