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