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