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// --- FormatDecOp ---
330
331StringAttr FormatDecOp::formatConstant(Attribute constVal) {
332 auto intAttr = llvm::dyn_cast<IntegerAttr>(constVal);
333 if (!intAttr)
334 return {};
335 SmallVector<char, 16> strBuf;
336 intAttr.getValue().toString(strBuf, 10, getIsSigned());
337 unsigned padWidth;
338 if (getSpecifierWidth().has_value()) {
339 padWidth = getSpecifierWidth().value();
340 } else {
341 unsigned width = intAttr.getType().getIntOrFloatBitWidth();
342 padWidth = FormatDecOp::getDecimalWidth(width, getIsSigned());
343 }
344
345 padWidth = padWidth > strBuf.size() ? padWidth - strBuf.size() : 0;
346
347 SmallVector<char, 10> padding(padWidth, getPaddingChar());
348 if (getIsLeftAligned())
349 return StringAttr::get(getContext(), Twine(strBuf) + Twine(padding));
350 return StringAttr::get(getContext(), Twine(padding) + Twine(strBuf));
351}
352
353OpFoldResult FormatDecOp::fold(FoldAdaptor adaptor) {
354 if (getValue().getType().getIntOrFloatBitWidth() == 0)
355 return StringAttr::get(getContext(), "0");
356 return {};
357}
358
359// --- FormatHexOp ---
360
361StringAttr FormatHexOp::formatConstant(Attribute constVal) {
362 return formatIntegersByRadix(constVal.getContext(), 16, constVal,
363 getIsHexUppercase(), getIsLeftAligned(),
364 getPaddingChar(), getSpecifierWidth());
365}
366
367OpFoldResult FormatHexOp::fold(FoldAdaptor adaptor) {
368 if (getValue().getType().getIntOrFloatBitWidth() == 0)
370 getContext(), 16, IntegerAttr::get(getValue().getType(), 0), false,
371 getIsLeftAligned(), getPaddingChar(), getSpecifierWidth());
372 return {};
373}
374
375// --- FormatOctOp ---
376
377StringAttr FormatOctOp::formatConstant(Attribute constVal) {
378 return formatIntegersByRadix(constVal.getContext(), 8, constVal, false,
379 getIsLeftAligned(), getPaddingChar(),
380 getSpecifierWidth());
381}
382
383OpFoldResult FormatOctOp::fold(FoldAdaptor adaptor) {
384 if (getValue().getType().getIntOrFloatBitWidth() == 0)
386 getContext(), 8, IntegerAttr::get(getValue().getType(), 0), false,
387 getIsLeftAligned(), getPaddingChar(), getSpecifierWidth());
388 return {};
389}
390
391// --- FormatBinOp ---
392
393StringAttr FormatBinOp::formatConstant(Attribute constVal) {
394 return formatIntegersByRadix(constVal.getContext(), 2, constVal, false,
395 getIsLeftAligned(), getPaddingChar(),
396 getSpecifierWidth());
397}
398
399OpFoldResult FormatBinOp::fold(FoldAdaptor adaptor) {
400 if (getValue().getType().getIntOrFloatBitWidth() == 0)
402 getContext(), 2, IntegerAttr::get(getValue().getType(), 0), false,
403 getIsLeftAligned(), getPaddingChar(), getSpecifierWidth());
404 return {};
405}
406
407// --- FormatScientificOp ---
408
409StringAttr FormatScientificOp::formatConstant(Attribute constVal) {
410 return formatFloatsBySpecifier(getContext(), constVal, getIsLeftAligned(),
411 getFieldWidth(), getFracDigits(), "e");
412}
413
414// --- FormatFloatOp ---
415
416StringAttr FormatFloatOp::formatConstant(Attribute constVal) {
417 return formatFloatsBySpecifier(getContext(), constVal, getIsLeftAligned(),
418 getFieldWidth(), getFracDigits(), "f");
419}
420
421// --- FormatGeneralOp ---
422
423StringAttr FormatGeneralOp::formatConstant(Attribute constVal) {
424 return formatFloatsBySpecifier(getContext(), constVal, getIsLeftAligned(),
425 getFieldWidth(), getFracDigits(), "g");
426}
427
428// --- FormatCharOp ---
429
430StringAttr FormatCharOp::formatConstant(Attribute constVal) {
431 auto intCst = dyn_cast<IntegerAttr>(constVal);
432 if (!intCst)
433 return {};
434 if (intCst.getType().getIntOrFloatBitWidth() == 0)
435 return StringAttr::get(getContext(), Twine(static_cast<char>(0)));
436 if (intCst.getType().getIntOrFloatBitWidth() > 8)
437 return {};
438 auto intValue = intCst.getValue().getZExtValue();
439 return StringAttr::get(getContext(), Twine(static_cast<char>(intValue)));
440}
441
442OpFoldResult FormatCharOp::fold(FoldAdaptor adaptor) {
443 if (getValue().getType().getIntOrFloatBitWidth() == 0)
444 return StringAttr::get(getContext(), Twine(static_cast<char>(0)));
445 return {};
446}
447
448static StringAttr concatLiterals(MLIRContext *ctxt, ArrayRef<StringRef> lits) {
449 assert(!lits.empty() && "No literals to concatenate");
450 if (lits.size() == 1)
451 return StringAttr::get(ctxt, lits.front());
452 SmallString<64> newLit;
453 for (auto lit : lits)
454 newLit += lit;
455 return StringAttr::get(ctxt, newLit);
456}
457
458OpFoldResult FormatStringConcatOp::fold(FoldAdaptor adaptor) {
459 if (getNumOperands() == 0)
460 return StringAttr::get(getContext(), "");
461 if (getNumOperands() == 1) {
462 // Don't fold to our own result to avoid an infinte loop.
463 if (getResult() == getOperand(0))
464 return {};
465 return getOperand(0);
466 }
467
468 // Fold if all operands are literals.
469 SmallVector<StringRef> lits;
470 for (auto attr : adaptor.getInputs()) {
471 auto lit = dyn_cast_or_null<StringAttr>(attr);
472 if (!lit)
473 return {};
474 lits.push_back(lit);
475 }
476 return concatLiterals(getContext(), lits);
477}
478
479LogicalResult FormatStringConcatOp::getFlattenedInputs(
480 llvm::SmallVectorImpl<Value> &flatOperands) {
482 bool isCyclic = false;
483
484 // Perform a DFS on this operation's concatenated operands,
485 // collect the leaf format string fragments.
486 concatStack.insert({*this, 0});
487 while (!concatStack.empty()) {
488 auto &top = concatStack.back();
489 auto currentConcat = top.first;
490 unsigned operandIndex = top.second;
491
492 // Iterate over concatenated operands
493 while (operandIndex < currentConcat.getNumOperands()) {
494 auto currentOperand = currentConcat.getOperand(operandIndex);
495
496 if (auto nextConcat =
497 currentOperand.getDefiningOp<FormatStringConcatOp>()) {
498 // Concat of a concat
499 if (!concatStack.contains(nextConcat)) {
500 // Save the next operand index to visit on the
501 // stack and put the new concat on top.
502 top.second = operandIndex + 1;
503 concatStack.insert({nextConcat, 0});
504 break;
505 }
506 // Cyclic concatenation encountered. Don't recurse.
507 isCyclic = true;
508 }
509
510 flatOperands.push_back(currentOperand);
511 operandIndex++;
512 }
513
514 // Pop the concat off of the stack if we have visited all operands.
515 if (operandIndex >= currentConcat.getNumOperands())
516 concatStack.pop_back();
517 }
518
519 return success(!isCyclic);
520}
521
522LogicalResult FormatStringConcatOp::verify() {
523 if (llvm::any_of(getOperands(),
524 [&](Value operand) { return operand == getResult(); }))
525 return emitOpError("is infinitely recursive.");
526 return success();
527}
528
529LogicalResult FormatStringConcatOp::canonicalize(FormatStringConcatOp op,
530 PatternRewriter &rewriter) {
531 // Any helper literals created during canonicalization must dominate `op`.
532 rewriter.setInsertionPoint(op);
533
534 auto fmtStrType = FormatStringType::get(op.getContext());
535
536 // Check if we can flatten concats of concats
537 bool hasBeenFlattened = false;
538 SmallVector<Value, 0> flatOperands;
539 if (!op.isFlat()) {
540 // Get a new, flattened list of operands
541 flatOperands.reserve(op.getNumOperands() + 4);
542 auto isAcyclic = op.getFlattenedInputs(flatOperands);
543
544 if (failed(isAcyclic)) {
545 // Infinite recursion, but we cannot fail compilation right here (can we?)
546 // so just emit a warning and bail out.
547 op.emitWarning("Cyclic concatenation detected.");
548 return failure();
549 }
550
551 hasBeenFlattened = true;
552 }
553
554 if (!hasBeenFlattened && op.getNumOperands() < 2)
555 return failure(); // Should be handled by the folder
556
557 // Check if there are adjacent literals we can merge or empty literals to
558 // remove
559 SmallVector<StringRef> litSequence;
560 SmallVector<Value> newOperands;
561 newOperands.reserve(op.getNumOperands());
562 FormatLiteralOp prevLitOp;
563
564 auto oldOperands = hasBeenFlattened ? flatOperands : op.getOperands();
565 for (auto operand : oldOperands) {
566 if (auto litOp = operand.getDefiningOp<FormatLiteralOp>()) {
567 if (!litOp.getLiteral().empty()) {
568 prevLitOp = litOp;
569 litSequence.push_back(litOp.getLiteral());
570 }
571 } else {
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 litSequence.clear();
584 }
585 newOperands.push_back(operand);
586 }
587 }
588
589 // Push trailing literals into the new operand list
590 if (!litSequence.empty()) {
591 if (litSequence.size() > 1) {
592 // Create a fused literal.
593 auto newLit = rewriter.createOrFold<FormatLiteralOp>(
594 op.getLoc(), fmtStrType,
595 concatLiterals(op.getContext(), litSequence));
596 newOperands.push_back(newLit);
597 } else {
598 // Reuse the existing literal.
599 newOperands.push_back(prevLitOp.getResult());
600 }
601 }
602
603 if (!hasBeenFlattened && newOperands.size() == op.getNumOperands())
604 return failure(); // Nothing changed
605
606 if (newOperands.empty())
607 rewriter.replaceOpWithNewOp<FormatLiteralOp>(op, fmtStrType,
608 rewriter.getStringAttr(""));
609 else if (newOperands.size() == 1)
610 rewriter.replaceOp(op, newOperands);
611 else
612 rewriter.modifyOpInPlace(op, [&]() { op->setOperands(newOperands); });
613
614 return success();
615}
616
617LogicalResult PrintFormattedOp::canonicalize(PrintFormattedOp op,
618 PatternRewriter &rewriter) {
619 // Remove ops with constant false condition.
620 if (auto cstCond = op.getCondition().getDefiningOp<hw::ConstantOp>()) {
621 if (cstCond.getValue().isZero()) {
622 rewriter.eraseOp(op);
623 return success();
624 }
625 }
626 return failure();
627}
628
629LogicalResult PrintFormattedProcOp::canonicalize(PrintFormattedProcOp op,
630 PatternRewriter &rewriter) {
631 // Remove empty prints.
632 if (auto litInput = op.getInput().getDefiningOp<FormatLiteralOp>()) {
633 if (litInput.getLiteral().empty()) {
634 rewriter.eraseOp(op);
635 return success();
636 }
637 }
638 return failure();
639}
640
641OpFoldResult StringConstantOp::fold(FoldAdaptor adaptor) {
642 return adaptor.getLiteralAttr();
643}
644
645OpFoldResult StringConcatOp::fold(FoldAdaptor adaptor) {
646 auto operands = adaptor.getInputs();
647 if (operands.empty())
648 return StringAttr::get(getContext(), "");
649
650 SmallString<128> result;
651 for (auto &operand : operands) {
652 auto strAttr = cast_if_present<StringAttr>(operand);
653 if (!strAttr)
654 return {};
655 result += strAttr.getValue();
656 }
657
658 return StringAttr::get(getContext(), result);
659}
660
661OpFoldResult StringLengthOp::fold(FoldAdaptor adaptor) {
662 auto inputAttr = adaptor.getInput();
663 if (!inputAttr)
664 return {};
665
666 if (auto strAttr = cast<StringAttr>(inputAttr))
667 return IntegerAttr::get(getType(), strAttr.getValue().size());
668
669 return {};
670}
671
672OpFoldResult IntToStringOp::fold(FoldAdaptor adaptor) {
673 auto intAttr = cast_or_null<IntegerAttr>(adaptor.getInput());
674 if (!intAttr)
675 return {};
676
677 SmallString<128> result;
678 auto width = intAttr.getType().getIntOrFloatBitWidth();
679 // Starting from the LSB, we extract the values byte-by-byte,
680 // and convert each non-null byte to a char
681
682 // For example 0x00_00_00_48_00_00_6C_6F would look like "Hlo"
683 for (unsigned int i = 0; i < width; i += 8) {
684 auto byte =
685 intAttr.getValue().extractBitsAsZExtValue(std::min(width - i, 8U), i);
686 if (byte)
687 result.push_back(static_cast<char>(byte));
688 }
689 std::reverse(result.begin(), result.end());
690 return StringAttr::get(getContext(), result);
691 return {};
692}
693
694//===----------------------------------------------------------------------===//
695// StringGetOp
696//===----------------------------------------------------------------------===//
697
698OpFoldResult StringGetOp::fold(FoldAdaptor adaptor) {
699 auto strAttr = cast_or_null<StringAttr>(adaptor.getStr());
700 auto indexAttr = cast_or_null<IntegerAttr>(adaptor.getIndex());
701 if (!strAttr || !indexAttr)
702 return {};
703
704 auto str = strAttr.getValue();
705 int64_t index = indexAttr.getValue().getSExtValue();
706
707 // Out-of-bounds access returns 0 (null character) per IEEE 1800-2023 ยง 6.16
708 if (index < 0 || index >= static_cast<int64_t>(str.size()))
709 return IntegerAttr::get(getType(), 0);
710
711 // Return the character at the specified index
712 uint8_t ch = static_cast<uint8_t>(str[index]);
713 return IntegerAttr::get(getType(), ch);
714}
715
716//===----------------------------------------------------------------------===//
717// QueueResizeOp
718//===----------------------------------------------------------------------===//
719
720LogicalResult QueueResizeOp::verify() {
721 if (cast<QueueType>(getInput().getType()).getElementType() !=
722 cast<QueueType>(getResult().getType()).getElementType())
723 return failure();
724 return success();
725}
726
727LogicalResult QueueFromArrayOp::verify() {
728 auto queueElementType =
729 cast<QueueType>(getResult().getType()).getElementType();
730
731 auto arrayElementType =
732 cast<hw::ArrayType>(getInput().getType()).getElementType();
733
734 if (queueElementType != arrayElementType) {
735 return emitOpError() << "sim::Queue element type " << queueElementType
736 << " doesn't match hw::ArrayType element type "
737 << arrayElementType;
738 }
739
740 return success();
741}
742
743LogicalResult QueueConcatOp::verify() {
744 // Verify the element types of all concatenated queues equal that of the
745 // result queue. (but not the bounds)
746 auto resultElType = cast<QueueType>(getResult().getType()).getElementType();
747
748 for (Value input : getInputs()) {
749 auto inpElType = cast<QueueType>(input.getType()).getElementType();
750 if (inpElType != resultElType) {
751 return emitOpError() << "sim::Queue element type " << inpElType
752 << " doesn't match result sim::Queue element type "
753 << resultElType;
754 }
755 }
756
757 return success();
758}
759
760//===----------------------------------------------------------------------===//
761// TableGen generated logic.
762//===----------------------------------------------------------------------===//
763
764#include "circt/Dialect/Sim/SimOpInterfaces.cpp.inc"
765
766// Provide the autogenerated implementation guts for the Op classes.
767#define GET_OP_CLASSES
768#include "circt/Dialect/Sim/Sim.cpp.inc"
769#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:448
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