CIRCT 24.0.0git
Loading...
Searching...
No Matches
MooreOps.cpp
Go to the documentation of this file.
1//===- MooreOps.cpp - Implement the Moore 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 the Moore dialect operations.
10//
11//===----------------------------------------------------------------------===//
12
18#include "mlir/IR/Builders.h"
19#include "mlir/Interfaces/FunctionImplementation.h"
20#include "llvm/ADT/APSInt.h"
21#include "llvm/ADT/SmallString.h"
22#include "llvm/ADT/TypeSwitch.h"
23#include <mlir/Dialect/Func/IR/FuncOps.h>
24
25using namespace circt;
26using namespace circt::moore;
27using namespace mlir;
28
29//===----------------------------------------------------------------------===//
30// SVModuleOp
31//===----------------------------------------------------------------------===//
32
33void SVModuleOp::build(mlir::OpBuilder &builder, mlir::OperationState &state,
34 llvm::StringRef name, hw::ModuleType type) {
35 state.addAttribute(SVModuleOp::getSymNameAttrName(state.name),
36 builder.getStringAttr(name));
37 state.addAttribute(getModuleTypeAttrName(state.name), TypeAttr::get(type));
38 state.addRegion();
39}
40
41void SVModuleOp::print(OpAsmPrinter &p) {
42 p << " ";
43
44 // Print the visibility of the module.
45 StringRef visibilityAttrName =
46 mlir::SymbolOpInterface::getDefaultVisibilityAttrName();
47 if (auto visibility = (*this)->getAttrOfType<StringAttr>(visibilityAttrName))
48 p << visibility.getValue() << ' ';
49
50 p.printSymbolName(SymbolTable::getSymbolName(*this).getValue());
52 getModuleType(), {}, {});
53 p.printOptionalAttrDictWithKeyword(getOperation()->getAttrs(),
54 getAttributeNames());
55 p << " ";
56 p.printRegion(getBodyRegion(), /*printEntryBlockArgs=*/false,
57 /*printBlockTerminators=*/true);
58}
59
60ParseResult SVModuleOp::parse(OpAsmParser &parser, OperationState &result) {
61 // Parse the visibility attribute.
62 (void)mlir::impl::parseOptionalVisibilityKeyword(parser, result.attributes);
63
64 // Parse the module name.
65 StringAttr nameAttr;
66 if (parser.parseSymbolName(nameAttr, getSymNameAttrName(result.name),
67 result.attributes))
68 return failure();
69
70 // Parse the ports.
71 SmallVector<hw::module_like_impl::PortParse> ports;
72 TypeAttr modType;
73 if (failed(
74 hw::module_like_impl::parseModuleSignature(parser, ports, modType)))
75 return failure();
76 result.addAttribute(getModuleTypeAttrName(result.name), modType);
77
78 // Parse the attributes.
79 if (failed(parser.parseOptionalAttrDictWithKeyword(result.attributes)))
80 return failure();
81
82 // Add the entry block arguments.
83 SmallVector<OpAsmParser::Argument, 4> entryArgs;
84 for (auto &port : ports)
85 if (port.direction != hw::ModulePort::Direction::Output)
86 entryArgs.push_back(port);
87
88 // Parse the optional function body.
89 auto &bodyRegion = *result.addRegion();
90 if (parser.parseRegion(bodyRegion, entryArgs))
91 return failure();
92
93 ensureTerminator(bodyRegion, parser.getBuilder(), result.location);
94 return success();
95}
96
97void SVModuleOp::getAsmBlockArgumentNames(mlir::Region &region,
98 mlir::OpAsmSetValueNameFn setNameFn) {
99 if (&region != &getBodyRegion())
100 return;
101 auto moduleType = getModuleType();
102 for (auto [index, arg] : llvm::enumerate(region.front().getArguments()))
103 setNameFn(arg, moduleType.getInputNameAttr(index));
104}
105
106OutputOp SVModuleOp::getOutputOp() {
107 return cast<OutputOp>(getBody()->getTerminator());
108}
109
110OperandRange SVModuleOp::getOutputs() { return getOutputOp().getOperands(); }
111
112//===----------------------------------------------------------------------===//
113// OutputOp
114//===----------------------------------------------------------------------===//
115
116LogicalResult OutputOp::verify() {
117 auto module = getParentOp();
118
119 // Check that the number of operands matches the number of output ports.
120 auto outputTypes = module.getModuleType().getOutputTypes();
121 if (outputTypes.size() != getNumOperands())
122 return emitOpError("has ")
123 << getNumOperands() << " operands, but enclosing module @"
124 << module.getSymName() << " has " << outputTypes.size()
125 << " outputs";
126
127 // Check that the operand types match the output ports.
128 for (unsigned i = 0, e = outputTypes.size(); i != e; ++i)
129 if (outputTypes[i] != getOperand(i).getType())
130 return emitOpError() << "operand " << i << " (" << getOperand(i).getType()
131 << ") does not match output type (" << outputTypes[i]
132 << ") of module @" << module.getSymName();
133
134 return success();
135}
136
137//===----------------------------------------------------------------------===//
138// InstanceOp
139//===----------------------------------------------------------------------===//
140
141LogicalResult InstanceOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
142 // Resolve the target symbol.
143 auto *symbol =
144 symbolTable.lookupNearestSymbolFrom(*this, getModuleNameAttr());
145 if (!symbol)
146 return emitOpError("references unknown symbol @") << getModuleName();
147
148 // Check that the symbol is a SVModuleOp.
149 auto module = dyn_cast<SVModuleOp>(symbol);
150 if (!module)
151 return emitOpError("must reference a 'moore.module', but @")
152 << getModuleName() << " is a '" << symbol->getName() << "'";
153
154 // Check that the input ports match.
155 auto moduleType = module.getModuleType();
156 auto inputTypes = moduleType.getInputTypes();
157
158 if (inputTypes.size() != getNumOperands())
159 return emitOpError("has ")
160 << getNumOperands() << " operands, but target module @"
161 << module.getSymName() << " has " << inputTypes.size() << " inputs";
162
163 for (unsigned i = 0, e = inputTypes.size(); i != e; ++i)
164 if (inputTypes[i] != getOperand(i).getType())
165 return emitOpError() << "operand " << i << " (" << getOperand(i).getType()
166 << ") does not match input type (" << inputTypes[i]
167 << ") of module @" << module.getSymName();
168
169 // Check that the output ports match.
170 auto outputTypes = moduleType.getOutputTypes();
171
172 if (outputTypes.size() != getNumResults())
173 return emitOpError("has ")
174 << getNumOperands() << " results, but target module @"
175 << module.getSymName() << " has " << outputTypes.size()
176 << " outputs";
177
178 for (unsigned i = 0, e = outputTypes.size(); i != e; ++i)
179 if (outputTypes[i] != getResult(i).getType())
180 return emitOpError() << "result " << i << " (" << getResult(i).getType()
181 << ") does not match output type (" << outputTypes[i]
182 << ") of module @" << module.getSymName();
183
184 return success();
185}
186
187void InstanceOp::print(OpAsmPrinter &p) {
188 p << " ";
189 p.printAttributeWithoutType(getInstanceNameAttr());
190 p << " ";
191 p.printAttributeWithoutType(getModuleNameAttr());
192 printInputPortList(p, getOperation(), getInputs(), getInputs().getTypes(),
193 getInputNames());
194 p << " -> ";
195 printOutputPortList(p, getOperation(), getOutputs().getTypes(),
196 getOutputNames());
197 p.printOptionalAttrDict(getOperation()->getAttrs(), getAttributeNames());
198}
199
200ParseResult InstanceOp::parse(OpAsmParser &parser, OperationState &result) {
201 // Parse the instance name.
202 StringAttr instanceName;
203 if (parser.parseAttribute(instanceName, "instanceName", result.attributes))
204 return failure();
205
206 // Parse the module name.
207 FlatSymbolRefAttr moduleName;
208 if (parser.parseAttribute(moduleName, "moduleName", result.attributes))
209 return failure();
210
211 // Parse the input port list.
212 auto loc = parser.getCurrentLocation();
213 SmallVector<OpAsmParser::UnresolvedOperand> inputs;
214 SmallVector<Type> types;
215 ArrayAttr names;
216 if (parseInputPortList(parser, inputs, types, names))
217 return failure();
218 if (parser.resolveOperands(inputs, types, loc, result.operands))
219 return failure();
220 result.addAttribute("inputNames", names);
221
222 // Parse `->`.
223 if (parser.parseArrow())
224 return failure();
225
226 // Parse the output port list.
227 types.clear();
228 if (parseOutputPortList(parser, types, names))
229 return failure();
230 result.addAttribute("outputNames", names);
231 result.addTypes(types);
232
233 // Parse the attributes.
234 if (parser.parseOptionalAttrDict(result.attributes))
235 return failure();
236
237 return success();
238}
239
240void InstanceOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
241 SmallString<32> name;
242 name += getInstanceName();
243 name += '.';
244 auto baseLen = name.size();
245
246 for (auto [result, portName] :
247 llvm::zip(getOutputs(), getOutputNames().getAsRange<StringAttr>())) {
248 if (!portName || portName.empty())
249 continue;
250 name.resize(baseLen);
251 name += portName.getValue();
252 setNameFn(result, name);
253 }
254}
255
256//===----------------------------------------------------------------------===//
257// CoroutineOp
258//===----------------------------------------------------------------------===//
259
260ParseResult CoroutineOp::parse(OpAsmParser &parser, OperationState &result) {
261 auto buildFuncType =
262 [](Builder &builder, ArrayRef<Type> argTypes, ArrayRef<Type> results,
263 function_interface_impl::VariadicFlag,
264 std::string &) { return builder.getFunctionType(argTypes, results); };
265
266 return function_interface_impl::parseFunctionOp(
267 parser, result, /*allowVariadic=*/false,
268 getFunctionTypeAttrName(result.name), buildFuncType,
269 getArgAttrsAttrName(result.name), getResAttrsAttrName(result.name));
270}
271
272void CoroutineOp::print(OpAsmPrinter &p) {
273 function_interface_impl::printFunctionOp(
274 p, *this, /*isVariadic=*/false, getFunctionTypeAttrName(),
275 getArgAttrsAttrName(), getResAttrsAttrName());
276}
277
278//===----------------------------------------------------------------------===//
279// CallCoroutineOp
280//===----------------------------------------------------------------------===//
281
282LogicalResult
283CallCoroutineOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
284 auto calleeName = getCalleeAttr();
285 auto coroutine =
286 symbolTable.lookupNearestSymbolFrom<CoroutineOp>(*this, calleeName);
287 if (!coroutine)
288 return emitOpError() << "'" << calleeName.getValue()
289 << "' does not reference a valid 'moore.coroutine'";
290
291 auto type = coroutine.getFunctionType();
292 if (type.getNumInputs() != getNumOperands())
293 return emitOpError() << "has " << getNumOperands()
294 << " operands, but callee expects "
295 << type.getNumInputs();
296
297 for (unsigned i = 0, e = type.getNumInputs(); i != e; ++i)
298 if (getOperand(i).getType() != type.getInput(i))
299 return emitOpError() << "operand " << i << " type mismatch: expected "
300 << type.getInput(i) << ", got "
301 << getOperand(i).getType();
302
303 if (type.getNumResults() != getNumResults())
304 return emitOpError() << "has " << getNumResults()
305 << " results, but callee returns "
306 << type.getNumResults();
307
308 for (unsigned i = 0, e = type.getNumResults(); i != e; ++i)
309 if (getResult(i).getType() != type.getResult(i))
310 return emitOpError() << "result " << i << " type mismatch: expected "
311 << type.getResult(i) << ", got "
312 << getResult(i).getType();
313
314 return success();
315}
316
317//===----------------------------------------------------------------------===//
318// VariableOp
319//===----------------------------------------------------------------------===//
320
321void VariableOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
322 if (getName() && !getName()->empty())
323 setNameFn(getResult(), *getName());
324}
325
326LogicalResult VariableOp::canonicalize(VariableOp op,
327 PatternRewriter &rewriter) {
328 // If the variable is embedded in an SSACFG region, move the initial value
329 // into an assignment immediately after the variable op. This allows the
330 // mem2reg pass which cannot handle variables with initial values.
331 auto initial = op.getInitial();
332 if (initial && mlir::mayHaveSSADominance(*op->getParentRegion())) {
333 rewriter.modifyOpInPlace(op, [&] { op.getInitialMutable().clear(); });
334 rewriter.setInsertionPointAfter(op);
335 BlockingAssignOp::create(rewriter, initial.getLoc(), op, initial);
336 return success();
337 }
338
339 // Check if the variable has one unique continuous assignment to it, all other
340 // uses are reads, and that all uses are in the same block as the variable
341 // itself.
342 auto *block = op->getBlock();
343 ContinuousAssignOp uniqueAssignOp;
344 for (auto *user : op->getUsers()) {
345 // Ensure that all users of the variable are in the same block.
346 if (user->getBlock() != block)
347 return failure();
348
349 // Ensure there is at most one unique continuous assignment to the variable.
350 if (auto assignOp = dyn_cast<ContinuousAssignOp>(user)) {
351 if (uniqueAssignOp)
352 return failure();
353 uniqueAssignOp = assignOp;
354 continue;
355 }
356
357 // Ensure all other users are reads.
358 if (!isa<ReadOp>(user))
359 return failure();
360 }
361 if (!uniqueAssignOp)
362 return failure();
363
364 // If the original variable had a name, create an `AssignedVariableOp` as a
365 // replacement. Otherwise substitute the assigned value directly.
366 Value assignedValue = uniqueAssignOp.getSrc();
367 if (auto name = op.getNameAttr(); name && !name.empty())
368 assignedValue = AssignedVariableOp::create(rewriter, op.getLoc(), name,
369 uniqueAssignOp.getSrc());
370
371 // Remove the assign op and replace all reads with the new assigned var op.
372 rewriter.eraseOp(uniqueAssignOp);
373 for (auto *user : llvm::make_early_inc_range(op->getUsers())) {
374 auto readOp = cast<ReadOp>(user);
375 rewriter.replaceOp(readOp, assignedValue);
376 }
377
378 // Remove the original variable.
379 rewriter.eraseOp(op);
380 return success();
381}
382
383SmallVector<MemorySlot> VariableOp::getPromotableSlots() {
384 // We cannot promote variables with an initial value, since that value may not
385 // dominate the location where the default value needs to be constructed.
386 if (mlir::mayBeGraphRegion(*getOperation()->getParentRegion()) ||
387 getInitial())
388 return {};
389
390 // Ensure that `getDefaultValue` can conjure up a default value for the
391 // variable's type.
392 auto nestedType = dyn_cast<PackedType>(getType().getNestedType());
393 if (!nestedType || !nestedType.getBitSize())
394 return {};
395
396 return {MemorySlot{getResult(), getType().getNestedType()}};
397}
398
399Value VariableOp::getDefaultValue(const MemorySlot &slot, OpBuilder &builder) {
400 auto packedType = dyn_cast<PackedType>(slot.valueType);
401 if (!packedType)
402 return {};
403 auto bitWidth = packedType.getBitSize();
404 if (!bitWidth)
405 return {};
406 auto fvint = packedType.getDomain() == Domain::FourValued
407 ? FVInt::getAllX(*bitWidth)
408 : FVInt::getZero(*bitWidth);
409 Value value = ConstantOp::create(
410 builder, getLoc(),
411 IntType::get(getContext(), *bitWidth, packedType.getDomain()), fvint);
412 if (value.getType() != packedType)
413 value = SBVToPackedOp::create(builder, getLoc(), packedType, value);
414 return value;
415}
416
417void VariableOp::handleBlockArgument(const MemorySlot &slot,
418 BlockArgument argument,
419 OpBuilder &builder) {}
420
421std::optional<mlir::PromotableAllocationOpInterface>
422VariableOp::handlePromotionComplete(const MemorySlot &slot, Value defaultValue,
423 OpBuilder &builder) {
424 if (defaultValue && defaultValue.use_empty())
425 defaultValue.getDefiningOp()->erase();
426 this->erase();
427 return {};
428}
429
430SmallVector<DestructurableMemorySlot> VariableOp::getDestructurableSlots() {
431 if (isa<SVModuleOp>(getOperation()->getParentOp()))
432 return {};
433 if (getInitial())
434 return {};
435
436 auto refType = getType();
437 auto destructurable = llvm::dyn_cast<DestructurableTypeInterface>(refType);
438 if (!destructurable)
439 return {};
440
441 auto destructuredType = destructurable.getSubelementIndexMap();
442 if (!destructuredType)
443 return {};
444
445 return {DestructurableMemorySlot{{getResult(), refType}, *destructuredType}};
446}
447
448DenseMap<Attribute, MemorySlot> VariableOp::destructure(
449 const DestructurableMemorySlot &slot,
450 const SmallPtrSetImpl<Attribute> &usedIndices, OpBuilder &builder,
451 SmallVectorImpl<DestructurableAllocationOpInterface> &newAllocators) {
452 assert(slot.ptr == getResult());
453 assert(!getInitial());
454 builder.setInsertionPointAfter(*this);
455
456 auto destructurableType = cast<DestructurableTypeInterface>(getType());
457 DenseMap<Attribute, MemorySlot> slotMap;
458 for (Attribute index : usedIndices) {
459 auto elemType = cast<RefType>(destructurableType.getTypeAtIndex(index));
460 assert(elemType && "used index must exist");
461 StringAttr varName;
462 if (auto name = getName(); name && !name->empty())
463 varName = StringAttr::get(
464 getContext(), (*name) + "." + cast<StringAttr>(index).getValue());
465 auto varOp =
466 VariableOp::create(builder, getLoc(), elemType, varName, Value());
467 newAllocators.push_back(varOp);
468 slotMap.try_emplace<MemorySlot>(index, {varOp.getResult(), elemType});
469 }
470
471 return slotMap;
472}
473
474std::optional<DestructurableAllocationOpInterface>
475VariableOp::handleDestructuringComplete(const DestructurableMemorySlot &slot,
476 OpBuilder &builder) {
477 assert(slot.ptr == getResult());
478 this->erase();
479 return std::nullopt;
480}
481
482//===----------------------------------------------------------------------===//
483// NetOp
484//===----------------------------------------------------------------------===//
485
486void NetOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
487 if (getName() && !getName()->empty())
488 setNameFn(getResult(), *getName());
489}
490
491LogicalResult NetOp::canonicalize(NetOp op, PatternRewriter &rewriter) {
492 bool modified = false;
493
494 // Check if the net has one unique continuous assignment to it, and
495 // additionally if all other users are reads.
496 auto *block = op->getBlock();
497 ContinuousAssignOp uniqueAssignOp;
498 bool allUsesAreReads = true;
499 for (auto *user : op->getUsers()) {
500 // Ensure that all users of the net are in the same block.
501 if (user->getBlock() != block)
502 return failure();
503
504 // Ensure there is at most one unique continuous assignment to the net.
505 if (auto assignOp = dyn_cast<ContinuousAssignOp>(user)) {
506 if (uniqueAssignOp)
507 return failure();
508 uniqueAssignOp = assignOp;
509 continue;
510 }
511
512 // Ensure all other users are reads.
513 if (!isa<ReadOp>(user))
514 allUsesAreReads = false;
515 }
516
517 // If there was one unique assignment, and the `NetOp` does not yet have an
518 // assigned value set, fold the assignment into the net.
519 if (uniqueAssignOp && !op.getAssignment()) {
520 rewriter.modifyOpInPlace(
521 op, [&] { op.getAssignmentMutable().assign(uniqueAssignOp.getSrc()); });
522 rewriter.eraseOp(uniqueAssignOp);
523 modified = true;
524 uniqueAssignOp = {};
525 }
526
527 // If all users of the net op are reads, and any potential unique assignment
528 // has been folded into the net op itself, directly replace the reads with the
529 // net's assigned value.
530 if (!uniqueAssignOp && allUsesAreReads && op.getAssignment()) {
531 // If the original net had a name, create an `AssignedVariableOp` as a
532 // replacement. Otherwise substitute the assigned value directly.
533 auto assignedValue = op.getAssignment();
534 if (auto name = op.getNameAttr(); name && !name.empty())
535 assignedValue = AssignedVariableOp::create(rewriter, op.getLoc(), name,
536 assignedValue);
537
538 // Replace all reads with the new assigned var op and remove the original
539 // net op.
540 for (auto *user : llvm::make_early_inc_range(op->getUsers())) {
541 auto readOp = cast<ReadOp>(user);
542 rewriter.replaceOp(readOp, assignedValue);
543 }
544 rewriter.eraseOp(op);
545 modified = true;
546 }
547
548 return success(modified);
549}
550
551//===----------------------------------------------------------------------===//
552// AssignedVariableOp
553//===----------------------------------------------------------------------===//
554
555void AssignedVariableOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
556 if (getName() && !getName()->empty())
557 setNameFn(getResult(), *getName());
558}
559
560LogicalResult AssignedVariableOp::canonicalize(AssignedVariableOp op,
561 PatternRewriter &rewriter) {
562 // Eliminate chained variables with the same name.
563 // var(name, var(name, x)) -> var(name, x)
564 if (auto otherOp = op.getInput().getDefiningOp<AssignedVariableOp>()) {
565 if (otherOp != op && otherOp.getNameAttr() == op.getNameAttr()) {
566 rewriter.replaceOp(op, otherOp);
567 return success();
568 }
569 }
570
571 // Eliminate variables that alias an input port of the same name.
572 if (auto blockArg = dyn_cast<BlockArgument>(op.getInput())) {
573 if (auto moduleOp =
574 dyn_cast<SVModuleOp>(blockArg.getOwner()->getParentOp())) {
575 auto moduleType = moduleOp.getModuleType();
576 auto portName = moduleType.getInputNameAttr(blockArg.getArgNumber());
577 if (portName == op.getNameAttr()) {
578 rewriter.replaceOp(op, blockArg);
579 return success();
580 }
581 }
582 }
583
584 // Eliminate variables that feed an output port of the same name.
585 for (auto &use : op->getUses()) {
586 auto *useOwner = use.getOwner();
587 if (auto outputOp = dyn_cast<OutputOp>(useOwner)) {
588 if (auto moduleOp = dyn_cast<SVModuleOp>(outputOp->getParentOp())) {
589 auto moduleType = moduleOp.getModuleType();
590 auto portName = moduleType.getOutputNameAttr(use.getOperandNumber());
591 if (portName == op.getNameAttr()) {
592 rewriter.replaceOp(op, op.getInput());
593 return success();
594 }
595 } else
596 break;
597 }
598 }
599
600 return failure();
601}
602
603//===----------------------------------------------------------------------===//
604// GlobalVariableOp
605//===----------------------------------------------------------------------===//
606
607LogicalResult GlobalVariableOp::verifyRegions() {
608 if (auto *block = getInitBlock()) {
609 auto &terminator = block->back();
610 if (!isa<YieldOp>(terminator))
611 return emitOpError() << "must have a 'moore.yield' terminator";
612 }
613 return success();
614}
615
616Block *GlobalVariableOp::getInitBlock() {
617 if (getInitRegion().empty())
618 return nullptr;
619 return &getInitRegion().front();
620}
621
622//===----------------------------------------------------------------------===//
623// GetGlobalVariableOp
624//===----------------------------------------------------------------------===//
625
626LogicalResult
627GetGlobalVariableOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
628 // Resolve the target symbol.
629 auto *symbol =
630 symbolTable.lookupNearestSymbolFrom(*this, getGlobalNameAttr());
631 if (!symbol)
632 return emitOpError() << "references unknown symbol " << getGlobalNameAttr();
633
634 // Check that the symbol is a global variable.
635 auto var = dyn_cast<GlobalVariableOp>(symbol);
636 if (!var)
637 return emitOpError() << "must reference a 'moore.global_variable', but "
638 << getGlobalNameAttr() << " is a '"
639 << symbol->getName() << "'";
640
641 // Check that the types match.
642 auto expType = var.getType();
643 auto actType = getType().getNestedType();
644 if (expType != actType)
645 return emitOpError() << "returns a " << actType << " reference, but "
646 << getGlobalNameAttr() << " is of type " << expType;
647
648 return success();
649}
650
651//===----------------------------------------------------------------------===//
652// ConstantOp
653//===----------------------------------------------------------------------===//
654
655void ConstantOp::print(OpAsmPrinter &p) {
656 p << " ";
657 printFVInt(p, getValue());
658 p.printOptionalAttrDict((*this)->getAttrs(), /*elidedAttrs=*/{"value"});
659 p << " : ";
660 p.printStrippedAttrOrType(getType());
661}
662
663ParseResult ConstantOp::parse(OpAsmParser &parser, OperationState &result) {
664 // Parse the constant value.
665 FVInt value;
666 auto valueLoc = parser.getCurrentLocation();
667 if (parseFVInt(parser, value))
668 return failure();
669
670 // Parse any optional attributes and the `:`.
671 if (parser.parseOptionalAttrDict(result.attributes) || parser.parseColon())
672 return failure();
673
674 // Parse the result type.
675 IntType type;
676 if (parser.parseCustomTypeWithFallback(type))
677 return failure();
678
679 // Extend or truncate the constant value to match the size of the type.
680 if (type.getWidth() > value.getBitWidth()) {
681 // sext is always safe here, even for unsigned values, because the
682 // parseOptionalInteger method will return something with a zero in the
683 // top bits if it is a positive number.
684 value = value.sext(type.getWidth());
685 } else if (type.getWidth() < value.getBitWidth()) {
686 // The parser can return an unnecessarily wide result with leading
687 // zeros. This isn't a problem, but truncating off bits is bad.
688 unsigned neededBits =
689 value.isNegative() ? value.getSignificantBits() : value.getActiveBits();
690 if (type.getWidth() < neededBits)
691 return parser.emitError(valueLoc)
692 << "value requires " << neededBits
693 << " bits, but result type only has " << type.getWidth();
694 value = value.trunc(type.getWidth());
695 }
696
697 // If the constant contains any X or Z bits, the result type must be
698 // four-valued.
699 if (value.hasUnknown() && type.getDomain() != Domain::FourValued)
700 return parser.emitError(valueLoc)
701 << "value contains X or Z bits, but result type " << type
702 << " only allows two-valued bits";
703
704 // Build the attribute and op.
705 auto attrValue = FVIntegerAttr::get(parser.getContext(), value);
706 result.addAttribute("value", attrValue);
707 result.addTypes(type);
708 return success();
709}
710
711LogicalResult ConstantOp::verify() {
712 auto attrWidth = getValue().getBitWidth();
713 auto typeWidth = getType().getWidth();
714 if (attrWidth != typeWidth)
715 return emitError("attribute width ")
716 << attrWidth << " does not match return type's width " << typeWidth;
717 return success();
718}
719
720void ConstantOp::build(OpBuilder &builder, OperationState &result, IntType type,
721 const FVInt &value) {
722 assert(type.getWidth() == value.getBitWidth() &&
723 "FVInt width must match type width");
724 build(builder, result, type, FVIntegerAttr::get(builder.getContext(), value));
725}
726
727void ConstantOp::build(OpBuilder &builder, OperationState &result, IntType type,
728 const APInt &value) {
729 assert(type.getWidth() == value.getBitWidth() &&
730 "APInt width must match type width");
731 build(builder, result, type, FVInt(value));
732}
733
734/// This builder allows construction of small signed integers like 0, 1, -1
735/// matching a specified MLIR type. This shouldn't be used for general constant
736/// folding because it only works with values that can be expressed in an
737/// `int64_t`.
738void ConstantOp::build(OpBuilder &builder, OperationState &result, IntType type,
739 int64_t value, bool isSigned) {
740 build(builder, result, type,
741 APInt(type.getWidth(), (uint64_t)value, isSigned));
742}
743
744/// This builder constructs a 1-bit boolean constant in the specified domain.
745void ConstantOp::build(OpBuilder &builder, OperationState &result,
746 Domain domain, bool value) {
747 auto type = IntType::get(builder.getContext(), 1, domain);
748 build(builder, result, type, value ? 1 : 0, /*isSigned=*/false);
749}
750
751OpFoldResult ConstantOp::fold(FoldAdaptor adaptor) {
752 assert(adaptor.getOperands().empty() && "constant has no operands");
753 return getValueAttr();
754}
755
756//===----------------------------------------------------------------------===//
757// ConstantTimeOp
758//===----------------------------------------------------------------------===//
759
760OpFoldResult ConstantTimeOp::fold(FoldAdaptor adaptor) {
761 return getValueAttr();
762}
763
764//===----------------------------------------------------------------------===//
765// ConstantRealOp
766//===----------------------------------------------------------------------===//
767
768LogicalResult ConstantRealOp::inferReturnTypes(
769 MLIRContext *context, std::optional<Location> loc, ValueRange operands,
770 DictionaryAttr attrs, mlir::PropertyRef properties,
771 mlir::RegionRange regions, SmallVectorImpl<Type> &results) {
772 ConstantRealOp::Adaptor adaptor(operands, attrs, properties);
773 results.push_back(RealType::get(
774 context, static_cast<RealWidth>(
775 adaptor.getValueAttr().getType().getIntOrFloatBitWidth())));
776 return success();
777}
778
779//===----------------------------------------------------------------------===//
780// ConcatOp
781//===----------------------------------------------------------------------===//
782
783LogicalResult ConcatOp::inferReturnTypes(
784 MLIRContext *context, std::optional<Location> loc, ValueRange operands,
785 DictionaryAttr attrs, mlir::PropertyRef properties,
786 mlir::RegionRange regions, SmallVectorImpl<Type> &results) {
787 Domain domain = Domain::TwoValued;
788 unsigned width = 0;
789 for (auto operand : operands) {
790 auto type = cast<IntType>(operand.getType());
791 if (type.getDomain() == Domain::FourValued)
792 domain = Domain::FourValued;
793 width += type.getWidth();
794 }
795 results.push_back(IntType::get(context, width, domain));
796 return success();
797}
798
799//===----------------------------------------------------------------------===//
800// ConcatRefOp
801//===----------------------------------------------------------------------===//
802
803LogicalResult ConcatRefOp::inferReturnTypes(
804 MLIRContext *context, std::optional<Location> loc, ValueRange operands,
805 DictionaryAttr attrs, mlir::PropertyRef properties,
806 mlir::RegionRange regions, SmallVectorImpl<Type> &results) {
807 if (operands.empty())
808 return emitOptionalError(loc, "requires at least one operand");
809 Domain domain = Domain::TwoValued;
810 unsigned width = 0;
811 for (Value operand : operands) {
812 if (operand.getType().getTypeID() != operands[0].getType().getTypeID())
813 return emitOptionalError(loc, "operands must all be refs or all be nets");
814 UnpackedType nestedType =
815 cast<WrapperType>(operand.getType()).getNestedType();
816 PackedType packedType = dyn_cast<PackedType>(nestedType);
817
818 if (!packedType) {
819 return failure();
820 }
821
822 if (packedType.getDomain() == Domain::FourValued)
823 domain = Domain::FourValued;
824
825 // getBitSize() for PackedType returns an optional, so we must check it.
826 std::optional<int> bitSize = packedType.getBitSize();
827 if (!bitSize) {
828 return failure();
829 }
830 width += *bitSize;
831 }
832 results.push_back(cast<WrapperType>(operands[0].getType())
833 .withNestedType(IntType::get(context, width, domain)));
834 return success();
835}
836
837//===----------------------------------------------------------------------===//
838// ArrayCreateOp
839//===----------------------------------------------------------------------===//
840
841static std::pair<unsigned, UnpackedType> getArrayElements(Type type) {
842 if (auto arrayType = dyn_cast<ArrayType>(type))
843 return {arrayType.getSize(), arrayType.getElementType()};
844 if (auto arrayType = dyn_cast<UnpackedArrayType>(type))
845 return {arrayType.getSize(), arrayType.getElementType()};
846 assert(0 && "expected ArrayType or UnpackedArrayType");
847 return {};
848}
849
850LogicalResult ArrayCreateOp::verify() {
851 auto [size, elementType] = getArrayElements(getType());
852
853 // Check that the number of operands matches the array size.
854 if (getElements().size() != size)
855 return emitOpError() << "has " << getElements().size()
856 << " operands, but result type requires " << size;
857
858 // Check that the operand types match the array element type. We only need to
859 // check one of the operands, since the `SameTypeOperands` trait ensures all
860 // operands have the same type.
861 if (size > 0) {
862 auto value = getElements()[0];
863 if (value.getType() != elementType)
864 return emitOpError() << "operands have type " << value.getType()
865 << ", but array requires " << elementType;
866 }
867 return success();
868}
869
870//===----------------------------------------------------------------------===//
871// StructCreateOp
872//===----------------------------------------------------------------------===//
873
874static std::optional<uint32_t> getStructFieldIndex(Type type, StringAttr name) {
875 if (auto structType = dyn_cast<StructType>(type))
876 return structType.getFieldIndex(name);
877 if (auto structType = dyn_cast<UnpackedStructType>(type))
878 return structType.getFieldIndex(name);
879 assert(0 && "expected StructType or UnpackedStructType");
880 return {};
881}
882
883static ArrayRef<StructLikeMember> getStructMembers(Type type) {
884 if (auto structType = dyn_cast<StructType>(type))
885 return structType.getMembers();
886 if (auto structType = dyn_cast<UnpackedStructType>(type))
887 return structType.getMembers();
888 assert(0 && "expected StructType or UnpackedStructType");
889 return {};
890}
891
892static UnpackedType getStructFieldType(Type type, StringAttr name) {
893 if (auto index = getStructFieldIndex(type, name))
894 return getStructMembers(type)[*index].type;
895 return {};
896}
897
898LogicalResult StructCreateOp::verify() {
899 auto members = getStructMembers(getType());
900
901 // Check that the number of operands matches the number of struct fields.
902 if (getFields().size() != members.size())
903 return emitOpError() << "has " << getFields().size()
904 << " operands, but result type requires "
905 << members.size();
906
907 // Check that the operand types match the struct field types.
908 for (auto [index, pair] : llvm::enumerate(llvm::zip(getFields(), members))) {
909 auto [value, member] = pair;
910 if (value.getType() != member.type)
911 return emitOpError() << "operand #" << index << " has type "
912 << value.getType() << ", but struct field "
913 << member.name << " requires " << member.type;
914 }
915 return success();
916}
917
918OpFoldResult StructCreateOp::fold(FoldAdaptor adaptor) {
919 SmallVector<NamedAttribute> fields;
920 for (auto [member, field] :
921 llvm::zip(getStructMembers(getType()), adaptor.getFields())) {
922 if (!field)
923 return {};
924 fields.push_back(NamedAttribute(member.name, field));
925 }
926 return DictionaryAttr::get(getContext(), fields);
927}
928
929//===----------------------------------------------------------------------===//
930// StructExtractOp
931//===----------------------------------------------------------------------===//
932
933LogicalResult StructExtractOp::verify() {
934 auto type = getStructFieldType(getInput().getType(), getFieldNameAttr());
935 if (!type)
936 return emitOpError() << "extracts field " << getFieldNameAttr()
937 << " which does not exist in " << getInput().getType();
938 if (type != getType())
939 return emitOpError() << "result type " << getType()
940 << " must match struct field type " << type;
941 return success();
942}
943
944OpFoldResult StructExtractOp::fold(FoldAdaptor adaptor) {
945 // Extract on a constant struct input.
946 if (auto fields = dyn_cast_or_null<DictionaryAttr>(adaptor.getInput()))
947 if (auto value = fields.get(getFieldNameAttr()))
948 return value;
949
950 // extract(inject(s, "field", v), "field") -> v
951 if (auto inject = getInput().getDefiningOp<StructInjectOp>()) {
952 if (inject.getFieldNameAttr() == getFieldNameAttr())
953 return inject.getNewValue();
954 return {};
955 }
956
957 // extract(create({"field": v, ...}), "field") -> v
958 if (auto create = getInput().getDefiningOp<StructCreateOp>()) {
959 if (auto index = getStructFieldIndex(create.getType(), getFieldNameAttr()))
960 return create.getFields()[*index];
961 return {};
962 }
963
964 return {};
965}
966
967//===----------------------------------------------------------------------===//
968// StructExtractRefOp
969//===----------------------------------------------------------------------===//
970
971LogicalResult StructExtractRefOp::verify() {
972 auto type = getStructFieldType(
973 cast<RefType>(getInput().getType()).getNestedType(), getFieldNameAttr());
974 if (!type)
975 return emitOpError() << "extracts field " << getFieldNameAttr()
976 << " which does not exist in " << getInput().getType();
977 if (type != getType().getNestedType())
978 return emitOpError() << "result ref of type " << getType().getNestedType()
979 << " must match struct field type " << type;
980 return success();
981}
982
983bool StructExtractRefOp::canRewire(
984 const DestructurableMemorySlot &slot,
985 SmallPtrSetImpl<Attribute> &usedIndices,
986 SmallVectorImpl<MemorySlot> &mustBeSafelyUsed,
987 const DataLayout &dataLayout) {
988 if (slot.ptr != getInput())
989 return false;
990 auto index = getFieldNameAttr();
991 if (!index || !slot.subelementTypes.contains(index))
992 return false;
993 usedIndices.insert(index);
994 return true;
995}
996
997DeletionKind
998StructExtractRefOp::rewire(const DestructurableMemorySlot &slot,
999 DenseMap<Attribute, MemorySlot> &subslots,
1000 OpBuilder &builder, const DataLayout &dataLayout) {
1001 auto index = getFieldNameAttr();
1002 const MemorySlot &memorySlot = subslots.at(index);
1003 replaceAllUsesWith(memorySlot.ptr);
1004 getInputMutable().drop();
1005 erase();
1006 return DeletionKind::Keep;
1007}
1008
1009//===----------------------------------------------------------------------===//
1010// StructInjectOp
1011//===----------------------------------------------------------------------===//
1012
1013LogicalResult StructInjectOp::verify() {
1014 auto type = getStructFieldType(getInput().getType(), getFieldNameAttr());
1015 if (!type)
1016 return emitOpError() << "injects field " << getFieldNameAttr()
1017 << " which does not exist in " << getInput().getType();
1018 if (type != getNewValue().getType())
1019 return emitOpError() << "injected value " << getNewValue().getType()
1020 << " must match struct field type " << type;
1021 return success();
1022}
1023
1024OpFoldResult StructInjectOp::fold(FoldAdaptor adaptor) {
1025 auto input = adaptor.getInput();
1026 auto newValue = adaptor.getNewValue();
1027 if (!input || !newValue)
1028 return {};
1029 NamedAttrList fields(cast<DictionaryAttr>(input));
1030 fields.set(getFieldNameAttr(), newValue);
1031 return fields.getDictionary(getContext());
1032}
1033
1034LogicalResult StructInjectOp::canonicalize(StructInjectOp op,
1035 PatternRewriter &rewriter) {
1036 auto members = getStructMembers(op.getType());
1037
1038 // Chase a chain of `struct_inject` ops, with an optional final
1039 // `struct_create`, and take note of the values assigned to each field.
1040 SmallPtrSet<Operation *, 4> injectOps;
1041 DenseMap<StringAttr, Value> fieldValues;
1042 Value input = op;
1043 while (auto injectOp = input.getDefiningOp<StructInjectOp>()) {
1044 if (!injectOps.insert(injectOp).second)
1045 return failure();
1046 fieldValues.insert({injectOp.getFieldNameAttr(), injectOp.getNewValue()});
1047 input = injectOp.getInput();
1048 }
1049 if (auto createOp = input.getDefiningOp<StructCreateOp>())
1050 for (auto [value, member] : llvm::zip(createOp.getFields(), members))
1051 fieldValues.insert({member.name, value});
1052
1053 // If the inject chain sets all fields, canonicalize to a `struct_create`.
1054 if (fieldValues.size() == members.size()) {
1055 SmallVector<Value> values;
1056 values.reserve(fieldValues.size());
1057 for (auto member : members)
1058 values.push_back(fieldValues.lookup(member.name));
1059 rewriter.replaceOpWithNewOp<StructCreateOp>(op, op.getType(), values);
1060 return success();
1061 }
1062
1063 // If each inject op in the chain assigned to a unique field, there is nothing
1064 // to canonicalize.
1065 if (injectOps.size() == fieldValues.size())
1066 return failure();
1067
1068 // Otherwise we can eliminate overwrites by creating new injects. The hash map
1069 // of field values contains the last assigned value for each field.
1070 for (auto member : members)
1071 if (auto value = fieldValues.lookup(member.name))
1072 input = StructInjectOp::create(rewriter, op.getLoc(), op.getType(), input,
1073 member.name, value);
1074 rewriter.replaceOp(op, input);
1075 return success();
1076}
1077
1078//===----------------------------------------------------------------------===//
1079// UnionCreateOp
1080//===----------------------------------------------------------------------===//
1081
1082LogicalResult UnionCreateOp::verify() {
1083 /// checks if the types of the input is exactly equal to the union field
1084 /// type
1085 return TypeSwitch<Type, LogicalResult>(getType())
1086 .Case<UnionType, UnpackedUnionType>([this](auto &type) {
1087 auto members = type.getMembers();
1088 auto inputType = getInput().getType();
1089 auto fieldName = getFieldName();
1090 for (const auto &member : members)
1091 if (member.name == fieldName && member.type == inputType)
1092 return success();
1093 for (const auto &member : members) {
1094 if (member.name == fieldName) {
1095 emitOpError() << "input type " << inputType
1096 << " does not match union field '" << fieldName
1097 << "' type " << member.type;
1098 return failure();
1099 }
1100 }
1101 emitOpError() << "field '" << fieldName << "' not found in union type";
1102 return failure();
1103 })
1104 .Default([this](auto &) {
1105 emitOpError("input type must be UnionType or UnpackedUnionType");
1106 return failure();
1107 });
1108}
1109
1110//===----------------------------------------------------------------------===//
1111// UnionExtractOp
1112//===----------------------------------------------------------------------===//
1113
1114LogicalResult UnionExtractOp::verify() {
1115 /// checks if the types of the input is exactly equal to the one of the
1116 /// types of the result union fields
1117 return TypeSwitch<Type, LogicalResult>(getInput().getType())
1118 .Case<UnionType, UnpackedUnionType>([this](auto &type) {
1119 auto members = type.getMembers();
1120 auto fieldName = getFieldName();
1121 auto resultType = getType();
1122 for (const auto &member : members)
1123 if (member.name == fieldName && member.type == resultType)
1124 return success();
1125 emitOpError("result type must match the union field type");
1126 return failure();
1127 })
1128 .Default([this](auto &) {
1129 emitOpError("input type must be UnionType or UnpackedUnionType");
1130 return failure();
1131 });
1132}
1133
1134//===----------------------------------------------------------------------===//
1135// UnionExtractOp
1136//===----------------------------------------------------------------------===//
1137
1138LogicalResult UnionExtractRefOp::verify() {
1139 /// checks if the types of the result is exactly equal to the type of the
1140 /// refe union field
1141 return TypeSwitch<Type, LogicalResult>(getInput().getType().getNestedType())
1142 .Case<UnionType, UnpackedUnionType>([this](auto &type) {
1143 auto members = type.getMembers();
1144 auto fieldName = getFieldName();
1145 auto resultType = getType().getNestedType();
1146 for (const auto &member : members)
1147 if (member.name == fieldName && member.type == resultType)
1148 return success();
1149 emitOpError("result type must match the union field type");
1150 return failure();
1151 })
1152 .Default([this](auto &) {
1153 emitOpError("input type must be UnionType or UnpackedUnionType");
1154 return failure();
1155 });
1156}
1157
1158//===----------------------------------------------------------------------===//
1159// YieldOp
1160//===----------------------------------------------------------------------===//
1161
1162LogicalResult YieldOp::verify() {
1163 Type expType;
1164 auto *parentOp = getOperation()->getParentOp();
1165 if (auto cond = dyn_cast<ConditionalOp>(parentOp)) {
1166 expType = cond.getType();
1167 } else if (auto varOp = dyn_cast<GlobalVariableOp>(parentOp)) {
1168 expType = varOp.getType();
1169 } else {
1170 llvm_unreachable("all in ParentOneOf handled");
1171 }
1172
1173 auto actType = getOperand().getType();
1174 if (expType != actType) {
1175 return emitOpError() << "yields " << actType << ", but parent expects "
1176 << expType;
1177 }
1178 return success();
1179}
1180
1181//===----------------------------------------------------------------------===//
1182// LogicToIntOp
1183//===----------------------------------------------------------------------===//
1184
1185OpFoldResult LogicToIntOp::fold(FoldAdaptor adaptor) {
1186 // logic_to_int(int_to_logic(x)) -> x
1187 if (auto reverseOp = getInput().getDefiningOp<IntToLogicOp>())
1188 return reverseOp.getInput();
1189
1190 // Map all unknown bits to zero (the default in SystemVerilog) and return a
1191 // new constant.
1192 if (auto intInput = dyn_cast_or_null<FVIntegerAttr>(adaptor.getInput()))
1193 return FVIntegerAttr::get(getContext(), intInput.getValue().toAPInt(false));
1194
1195 return {};
1196}
1197
1198//===----------------------------------------------------------------------===//
1199// IntToLogicOp
1200//===----------------------------------------------------------------------===//
1201
1202OpFoldResult IntToLogicOp::fold(FoldAdaptor adaptor) {
1203 // Cannot fold int_to_logic(logic_to_int(x)) -> x since that would lose
1204 // information.
1205
1206 // Simply pass through constants.
1207 if (auto intInput = dyn_cast_or_null<FVIntegerAttr>(adaptor.getInput()))
1208 return intInput;
1209
1210 return {};
1211}
1212
1213//===----------------------------------------------------------------------===//
1214// TimeToLogicOp
1215//===----------------------------------------------------------------------===//
1216
1217OpFoldResult TimeToLogicOp::fold(FoldAdaptor adaptor) {
1218 // time_to_logic(logic_to_time(x)) -> x
1219 if (auto reverseOp = getInput().getDefiningOp<LogicToTimeOp>())
1220 return reverseOp.getInput();
1221
1222 // Convert constants.
1223 if (auto attr = dyn_cast_or_null<IntegerAttr>(adaptor.getInput()))
1224 return FVIntegerAttr::get(getContext(), attr.getValue());
1225
1226 return {};
1227}
1228
1229//===----------------------------------------------------------------------===//
1230// LogicToTimeOp
1231//===----------------------------------------------------------------------===//
1232
1233OpFoldResult LogicToTimeOp::fold(FoldAdaptor adaptor) {
1234 // logic_to_time(time_to_logic(x)) -> x
1235 if (auto reverseOp = getInput().getDefiningOp<TimeToLogicOp>())
1236 return reverseOp.getInput();
1237
1238 // Convert constants.
1239 if (auto attr = dyn_cast_or_null<FVIntegerAttr>(adaptor.getInput()))
1240 return IntegerAttr::get(getContext(), APSInt(attr.getValue().toAPInt(false),
1241 /*isUnsigned=*/true));
1242
1243 return {};
1244}
1245
1246//===----------------------------------------------------------------------===//
1247// ConvertRealOp
1248//===----------------------------------------------------------------------===//
1249
1250OpFoldResult ConvertRealOp::fold(FoldAdaptor adaptor) {
1251 if (getInput().getType() == getResult().getType())
1252 return getInput();
1253
1254 return {};
1255}
1256
1257//===----------------------------------------------------------------------===//
1258// TruncOp
1259//===----------------------------------------------------------------------===//
1260
1261OpFoldResult TruncOp::fold(FoldAdaptor adaptor) {
1262 // Truncate constants.
1263 if (auto intAttr = dyn_cast_or_null<FVIntegerAttr>(adaptor.getInput())) {
1264 auto width = getType().getWidth();
1265 return FVIntegerAttr::get(getContext(), intAttr.getValue().trunc(width));
1266 }
1267
1268 return {};
1269}
1270
1271//===----------------------------------------------------------------------===//
1272// ZExtOp
1273//===----------------------------------------------------------------------===//
1274
1275OpFoldResult ZExtOp::fold(FoldAdaptor adaptor) {
1276 // Zero-extend constants.
1277 if (auto intAttr = dyn_cast_or_null<FVIntegerAttr>(adaptor.getInput())) {
1278 auto width = getType().getWidth();
1279 return FVIntegerAttr::get(getContext(), intAttr.getValue().zext(width));
1280 }
1281
1282 return {};
1283}
1284
1285//===----------------------------------------------------------------------===//
1286// SExtOp
1287//===----------------------------------------------------------------------===//
1288
1289OpFoldResult SExtOp::fold(FoldAdaptor adaptor) {
1290 // Sign-extend constants.
1291 if (auto intAttr = dyn_cast_or_null<FVIntegerAttr>(adaptor.getInput())) {
1292 auto width = getType().getWidth();
1293 return FVIntegerAttr::get(getContext(), intAttr.getValue().sext(width));
1294 }
1295
1296 return {};
1297}
1298
1299//===----------------------------------------------------------------------===//
1300// BoolCastOp
1301//===----------------------------------------------------------------------===//
1302
1303OpFoldResult BoolCastOp::fold(FoldAdaptor adaptor) {
1304 // Fold away no-op casts.
1305 if (getInput().getType() == getResult().getType())
1306 return getInput();
1307 return {};
1308}
1309
1310//===----------------------------------------------------------------------===//
1311// BlockingAssignOp
1312//===----------------------------------------------------------------------===//
1313
1314bool BlockingAssignOp::loadsFrom(const MemorySlot &slot) { return false; }
1315
1316bool BlockingAssignOp::storesTo(const MemorySlot &slot) {
1317 return getDst() == slot.ptr;
1318}
1319
1320Value BlockingAssignOp::getStored(const MemorySlot &slot, OpBuilder &builder,
1321 Value reachingDef,
1322 const DataLayout &dataLayout) {
1323 return getSrc();
1324}
1325
1326bool BlockingAssignOp::canUsesBeRemoved(
1327 const MemorySlot &slot, const SmallPtrSetImpl<OpOperand *> &blockingUses,
1328 SmallVectorImpl<OpOperand *> &newBlockingUses,
1329 const DataLayout &dataLayout) {
1330
1331 if (blockingUses.size() != 1)
1332 return false;
1333 Value blockingUse = (*blockingUses.begin())->get();
1334 return blockingUse == slot.ptr && getDst() == slot.ptr &&
1335 getSrc() != slot.ptr && getSrc().getType() == slot.valueType;
1336}
1337
1338DeletionKind BlockingAssignOp::removeBlockingUses(
1339 const MemorySlot &slot, const SmallPtrSetImpl<OpOperand *> &blockingUses,
1340 OpBuilder &builder, Value reachingDefinition,
1341 const DataLayout &dataLayout) {
1342 return DeletionKind::Delete;
1343}
1344
1345//===----------------------------------------------------------------------===//
1346// ReadOp
1347//===----------------------------------------------------------------------===//
1348
1349bool ReadOp::loadsFrom(const MemorySlot &slot) {
1350 return getInput() == slot.ptr;
1351}
1352
1353bool ReadOp::storesTo(const MemorySlot &slot) { return false; }
1354
1355Value ReadOp::getStored(const MemorySlot &slot, OpBuilder &builder,
1356 Value reachingDef, const DataLayout &dataLayout) {
1357 llvm_unreachable("getStored should not be called on ReadOp");
1358}
1359
1360bool ReadOp::canUsesBeRemoved(const MemorySlot &slot,
1361 const SmallPtrSetImpl<OpOperand *> &blockingUses,
1362 SmallVectorImpl<OpOperand *> &newBlockingUses,
1363 const DataLayout &dataLayout) {
1364
1365 if (blockingUses.size() != 1)
1366 return false;
1367 Value blockingUse = (*blockingUses.begin())->get();
1368 return blockingUse == slot.ptr && getOperand() == slot.ptr &&
1369 getResult().getType() == slot.valueType;
1370}
1371
1372DeletionKind
1373ReadOp::removeBlockingUses(const MemorySlot &slot,
1374 const SmallPtrSetImpl<OpOperand *> &blockingUses,
1375 OpBuilder &builder, Value reachingDefinition,
1376 const DataLayout &dataLayout) {
1377 getResult().replaceAllUsesWith(reachingDefinition);
1378 return DeletionKind::Delete;
1379}
1380
1381//===----------------------------------------------------------------------===//
1382// PowSOp
1383//===----------------------------------------------------------------------===//
1384
1385static OpFoldResult powCommonFolding(MLIRContext *ctxt, Attribute lhs,
1386 Attribute rhs) {
1387 auto lhsValue = dyn_cast_or_null<FVIntegerAttr>(lhs);
1388 if (lhsValue && lhsValue.getValue() == 1)
1389 return lhs;
1390
1391 auto rhsValue = dyn_cast_or_null<FVIntegerAttr>(rhs);
1392 if (rhsValue && rhsValue.getValue().isZero())
1393 return FVIntegerAttr::get(ctxt,
1394 FVInt(rhsValue.getValue().getBitWidth(), 1));
1395
1396 return {};
1397}
1398
1399OpFoldResult PowSOp::fold(FoldAdaptor adaptor) {
1400 return powCommonFolding(getContext(), adaptor.getLhs(), adaptor.getRhs());
1401}
1402
1403LogicalResult PowSOp::canonicalize(PowSOp op, PatternRewriter &rewriter) {
1404 Location loc = op.getLoc();
1405 auto intType = cast<IntType>(op.getRhs().getType());
1406 if (auto baseOp = op.getLhs().getDefiningOp<ConstantOp>()) {
1407 if (baseOp.getValue() == 2) {
1408 Value constOne = ConstantOp::create(rewriter, loc, intType, 1);
1409 Value constZero = ConstantOp::create(rewriter, loc, intType, 0);
1410 Value shift = ShlOp::create(rewriter, loc, constOne, op.getRhs());
1411 Value isNegative = SltOp::create(rewriter, loc, op.getRhs(), constZero);
1412 auto condOp = rewriter.replaceOpWithNewOp<ConditionalOp>(
1413 op, op.getLhs().getType(), isNegative);
1414 Block *thenBlock = rewriter.createBlock(&condOp.getTrueRegion());
1415 rewriter.setInsertionPointToStart(thenBlock);
1416 YieldOp::create(rewriter, loc, constZero);
1417 Block *elseBlock = rewriter.createBlock(&condOp.getFalseRegion());
1418 rewriter.setInsertionPointToStart(elseBlock);
1419 YieldOp::create(rewriter, loc, shift);
1420 return success();
1421 }
1422 }
1423
1424 return failure();
1425}
1426
1427//===----------------------------------------------------------------------===//
1428// PowUOp
1429//===----------------------------------------------------------------------===//
1430
1431OpFoldResult PowUOp::fold(FoldAdaptor adaptor) {
1432 return powCommonFolding(getContext(), adaptor.getLhs(), adaptor.getRhs());
1433}
1434
1435LogicalResult PowUOp::canonicalize(PowUOp op, PatternRewriter &rewriter) {
1436 Location loc = op.getLoc();
1437 auto intType = cast<IntType>(op.getRhs().getType());
1438 if (auto baseOp = op.getLhs().getDefiningOp<ConstantOp>()) {
1439 if (baseOp.getValue() == 2) {
1440 Value constOne = ConstantOp::create(rewriter, loc, intType, 1);
1441 rewriter.replaceOpWithNewOp<ShlOp>(op, constOne, op.getRhs());
1442 return success();
1443 }
1444 }
1445
1446 return failure();
1447}
1448
1449//===----------------------------------------------------------------------===//
1450// SubOp
1451//===----------------------------------------------------------------------===//
1452
1453OpFoldResult SubOp::fold(FoldAdaptor adaptor) {
1454 if (auto intAttr = dyn_cast_or_null<FVIntegerAttr>(adaptor.getRhs()))
1455 if (intAttr.getValue().isZero())
1456 return getLhs();
1457
1458 return {};
1459}
1460
1461//===----------------------------------------------------------------------===//
1462// MulOp
1463//===----------------------------------------------------------------------===//
1464
1465OpFoldResult MulOp::fold(FoldAdaptor adaptor) {
1466 auto lhs = dyn_cast_or_null<FVIntegerAttr>(adaptor.getLhs());
1467 auto rhs = dyn_cast_or_null<FVIntegerAttr>(adaptor.getRhs());
1468 if (lhs && rhs)
1469 return FVIntegerAttr::get(getContext(), lhs.getValue() * rhs.getValue());
1470 return {};
1471}
1472
1473//===----------------------------------------------------------------------===//
1474// DivUOp
1475//===----------------------------------------------------------------------===//
1476
1477OpFoldResult DivUOp::fold(FoldAdaptor adaptor) {
1478 auto lhs = dyn_cast_or_null<FVIntegerAttr>(adaptor.getLhs());
1479 auto rhs = dyn_cast_or_null<FVIntegerAttr>(adaptor.getRhs());
1480 if (lhs && rhs)
1481 return FVIntegerAttr::get(getContext(),
1482 lhs.getValue().udiv(rhs.getValue()));
1483 return {};
1484}
1485
1486//===----------------------------------------------------------------------===//
1487// DivSOp
1488//===----------------------------------------------------------------------===//
1489
1490OpFoldResult DivSOp::fold(FoldAdaptor adaptor) {
1491 auto lhs = dyn_cast_or_null<FVIntegerAttr>(adaptor.getLhs());
1492 auto rhs = dyn_cast_or_null<FVIntegerAttr>(adaptor.getRhs());
1493 if (lhs && rhs)
1494 return FVIntegerAttr::get(getContext(),
1495 lhs.getValue().sdiv(rhs.getValue()));
1496 return {};
1497}
1498
1499//===----------------------------------------------------------------------===//
1500// Classes
1501//===----------------------------------------------------------------------===//
1502
1503LogicalResult ClassDeclOp::verify() {
1504 mlir::Region &body = getBody();
1505 if (body.empty())
1506 return mlir::success();
1507
1508 auto &block = body.front();
1509 for (mlir::Operation &op : block) {
1510
1511 // allow only property and method decls and terminator
1512 if (llvm::isa<circt::moore::ClassPropertyDeclOp,
1513 circt::moore::ClassMethodDeclOp>(&op))
1514 continue;
1515
1516 return emitOpError()
1517 << "body may only contain 'moore.class.propertydecl' operations";
1518 }
1519 return mlir::success();
1520}
1521
1522LogicalResult ClassNewOp::verify() {
1523 // The result is constrained to ClassHandleType in ODS, so this cast should be
1524 // safe.
1525 auto handleTy = cast<ClassHandleType>(getResult().getType());
1526 mlir::SymbolRefAttr classSym = handleTy.getClassSym();
1527 if (!classSym)
1528 return emitOpError("result type is missing a class symbol");
1529
1530 // Resolve the referenced symbol starting from the nearest symbol table.
1531 mlir::Operation *sym =
1532 mlir::SymbolTable::lookupNearestSymbolFrom(getOperation(), classSym);
1533 if (!sym)
1534 return emitOpError("referenced class symbol `")
1535 << classSym << "` was not found";
1536
1537 if (!llvm::isa<ClassDeclOp>(sym))
1538 return emitOpError("symbol `")
1539 << classSym << "` does not name a `moore.class.classdecl`";
1540
1541 return mlir::success();
1542}
1543
1544void ClassNewOp::getEffects(
1545 SmallVectorImpl<SideEffects::EffectInstance<MemoryEffects::Effect>>
1546 &effects) {
1547 // Always allocates heap memory.
1548 effects.emplace_back(MemoryEffects::Allocate::get());
1549}
1550
1551LogicalResult
1552ClassUpcastOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1553 // 1) Type checks.
1554 auto srcTy = dyn_cast<ClassHandleType>(getOperand().getType());
1555 if (!srcTy)
1556 return emitOpError() << "operand must be !moore.class<...>; got "
1557 << getOperand().getType();
1558
1559 auto dstTy = dyn_cast<ClassHandleType>(getResult().getType());
1560 if (!dstTy)
1561 return emitOpError() << "result must be !moore.class<...>; got "
1562 << getResult().getType();
1563
1564 if (srcTy == dstTy)
1565 return success();
1566
1567 auto *op = getOperation();
1568
1569 auto *srcDeclOp =
1570 symbolTable.lookupNearestSymbolFrom(op, srcTy.getClassSym());
1571 auto *dstDeclOp =
1572 symbolTable.lookupNearestSymbolFrom(op, dstTy.getClassSym());
1573 if (!srcDeclOp || !dstDeclOp)
1574 return emitOpError() << "failed to resolve class symbol(s): src="
1575 << srcTy.getClassSym()
1576 << ", dst=" << dstTy.getClassSym();
1577
1578 auto srcDecl = dyn_cast<ClassDeclOp>(srcDeclOp);
1579 auto dstDecl = dyn_cast<ClassDeclOp>(dstDeclOp);
1580 if (!srcDecl || !dstDecl)
1581 return emitOpError()
1582 << "symbol(s) do not name `moore.class.classdecl` ops: src="
1583 << srcTy.getClassSym() << ", dst=" << dstTy.getClassSym();
1584
1585 auto cur = srcDecl;
1586 while (cur) {
1587 if (cur == dstDecl)
1588 return success(); // legal upcast: dst is src or an ancestor
1589
1590 auto baseSym = cur.getBaseAttr();
1591 if (!baseSym)
1592 break;
1593
1594 auto *baseOp = symbolTable.lookupNearestSymbolFrom(op, baseSym);
1595 cur = llvm::dyn_cast_or_null<ClassDeclOp>(baseOp);
1596 }
1597
1598 return emitOpError() << "cannot upcast from " << srcTy.getClassSym() << " to "
1599 << dstTy.getClassSym()
1600 << " (destination is not a base class)";
1601}
1602
1603LogicalResult
1604ClassPropertyRefOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1605 // The operand is constrained to ClassHandleType in ODS; unwrap it.
1606 Type instTy = getInstance().getType();
1607 auto handleTy = dyn_cast<moore::ClassHandleType>(instTy);
1608 if (!handleTy)
1609 return emitOpError() << "instance must be a !moore.class<@C> value, got "
1610 << instTy;
1611
1612 // Extract the referenced class symbol from the handle type.
1613 SymbolRefAttr classSym = handleTy.getClassSym();
1614 if (!classSym)
1615 return emitOpError("instance type is missing a class symbol");
1616
1617 // Resolve the class symbol starting from the nearest symbol table.
1618 Operation *clsSym =
1619 symbolTable.lookupNearestSymbolFrom(getOperation(), classSym);
1620 if (!clsSym)
1621 return emitOpError("referenced class symbol `")
1622 << classSym << "` was not found";
1623 auto classDecl = dyn_cast<ClassDeclOp>(clsSym);
1624 if (!classDecl)
1625 return emitOpError("symbol `")
1626 << classSym << "` does not name a `moore.class.classdecl`";
1627
1628 // Look up the field symbol inside the class declaration's symbol table.
1629 FlatSymbolRefAttr fieldSym = getPropertyAttr();
1630 if (!fieldSym)
1631 return emitOpError("missing field symbol");
1632
1633 Operation *fldSym = symbolTable.lookupSymbolIn(classDecl, fieldSym.getAttr());
1634 if (!fldSym)
1635 return emitOpError("no field `") << fieldSym << "` in class " << classSym;
1636
1637 auto fieldDecl = dyn_cast<ClassPropertyDeclOp>(fldSym);
1638 if (!fieldDecl)
1639 return emitOpError("symbol `")
1640 << fieldSym << "` is not a `moore.class.propertydecl`";
1641
1642 // Result must be !moore.ref<T> where T matches the field's declared type.
1643 auto resRefTy = cast<RefType>(getPropertyRef().getType());
1644 if (!resRefTy)
1645 return emitOpError("result must be a !moore.ref<T>");
1646
1647 Type expectedElemTy = fieldDecl.getPropertyType();
1648 if (resRefTy.getNestedType() != expectedElemTy)
1649 return emitOpError("result element type (")
1650 << resRefTy.getNestedType() << ") does not match field type ("
1651 << expectedElemTy << ")";
1652
1653 return success();
1654}
1655
1656LogicalResult
1657VTableLoadMethodOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1658 Operation *op = getOperation();
1659
1660 auto object = getObject();
1661 auto implSym = object.getType().getClassSym();
1662
1663 // Check that classdecl of class handle exists
1664 Operation *implOp = symbolTable.lookupNearestSymbolFrom(op, implSym);
1665 if (!implOp)
1666 return emitOpError() << "implementing class " << implSym << " not found";
1667 auto implClass = cast<moore::ClassDeclOp>(implOp);
1668
1669 StringAttr methodName = getMethodSymAttr().getLeafReference();
1670 if (!methodName || methodName.getValue().empty())
1671 return emitOpError() << "empty method name";
1672
1673 moore::ClassDeclOp cursor = implClass;
1674 Operation *methodDeclOp = nullptr;
1675
1676 // Find method in class decl or parents' class decl
1677 while (cursor && !methodDeclOp) {
1678 methodDeclOp = symbolTable.lookupSymbolIn(cursor, methodName);
1679 if (methodDeclOp)
1680 break;
1681 SymbolRefAttr baseSym = cursor.getBaseAttr();
1682 if (!baseSym)
1683 break;
1684 Operation *baseOp = symbolTable.lookupNearestSymbolFrom(op, baseSym);
1685 cursor = baseOp ? cast<moore::ClassDeclOp>(baseOp) : moore::ClassDeclOp();
1686 }
1687
1688 if (!methodDeclOp)
1689 return emitOpError() << "no method `" << methodName << "` found in "
1690 << implClass.getSymName() << " or its bases";
1691
1692 // Make sure method decl is a ClassMethodDeclOp
1693 auto methodDecl = dyn_cast<moore::ClassMethodDeclOp>(methodDeclOp);
1694 if (!methodDecl)
1695 return emitOpError() << "`" << methodName
1696 << "` is not a method declaration";
1697
1698 // Make sure method signature matches
1699 auto resFnTy = cast<FunctionType>(getResult().getType());
1700 auto declFnTy = cast<FunctionType>(methodDecl.getFunctionType());
1701 if (resFnTy != declFnTy)
1702 return emitOpError() << "result type " << resFnTy
1703 << " does not match method erased ABI " << declFnTy;
1704
1705 return success();
1706}
1707
1708LogicalResult VTableOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1709 Operation *self = getOperation();
1710
1711 // sym_name's root must be a ClassDeclOp
1712 SymbolRefAttr name = getSymNameAttr();
1713 if (!name)
1714 return emitOpError("requires 'sym_name' SymbolRefAttr");
1715
1716 // Root symbol must resolve (from the nearest symbol table) to a ClassDeclOp.
1717 Operation *rootDef = symbolTable.lookupNearestSymbolFrom(
1718 self, SymbolRefAttr::get(name.getRootReference()));
1719 if (!rootDef)
1720 return emitOpError() << "cannot resolve root class symbol '"
1721 << name.getRootReference() << "' for sym_name "
1722 << name;
1723
1724 if (!isa<ClassDeclOp>(rootDef))
1725 return emitOpError()
1726 << "root of sym_name must name a 'moore.class.classdecl', got "
1727 << name;
1728
1729 // All good.
1730 return success();
1731}
1732
1733LogicalResult VTableOp::verifyRegions() {
1734 // Ensure only allowed ops appear inside.
1735 for (Operation &op : getBody().front()) {
1736 if (!isa<VTableOp, VTableEntryOp>(op))
1737 return emitOpError(
1738 "body may only contain 'moore.vtable' or 'moore.vtable_entry' ops");
1739 }
1740 return mlir::success();
1741}
1742
1743LogicalResult
1744VTableEntryOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1745 Operation *self = getOperation();
1746
1747 // 'target' must exist and resolve from the top-level symbol table of a func
1748 // op
1749 SymbolRefAttr target = getTargetAttr();
1750 func::FuncOp def =
1751 symbolTable.lookupNearestSymbolFrom<func::FuncOp>(self, target);
1752 if (!def)
1753 return emitOpError()
1754 << "cannot resolve target symbol to a function operation " << target;
1755
1756 // VTableEntries may only exist in VTables.
1757 if (!isa<VTableOp>(self->getParentOp()))
1758 return emitOpError("must be nested directly inside a 'moore.vtable' op");
1759
1760 Operation *currentOp = self;
1761 VTableOp currentVTable;
1762 bool defined = false;
1763
1764 // Walk up the VTable tree and check whether the corresponding classDeclOp
1765 // declares a method with the same implementation. Further checks all the way
1766 // up the tree if another classdeclop overrides the implementation.
1767 // The entry is correct iff the impl matches the most derived classdeclop's
1768 // methoddeclop implementing the virtual method.
1769 while (auto parentOp = dyn_cast<VTableOp>(currentOp->getParentOp())) {
1770 currentOp = parentOp;
1771 currentVTable = cast<VTableOp>(currentOp);
1772
1773 auto classSymName = currentVTable.getSymName();
1774 ClassDeclOp parentClassDecl =
1775 symbolTable.lookupNearestSymbolFrom<ClassDeclOp>(
1776 parentOp, classSymName.getRootReference());
1777 assert(parentClassDecl && "VTableOp must point to a classdeclop");
1778
1779 for (auto method : parentClassDecl.getBody().getOps<ClassMethodDeclOp>()) {
1780 // A virtual interface declaration. Ignore.
1781 if (!method.getImpl())
1782 continue;
1783
1784 // A matching definition.
1785 if (method.getSymName() == getName() && method.getImplAttr() == target)
1786 defined = true;
1787
1788 // All definitions of the same method up the tree must be the same as the
1789 // current definition, there is no shadowing.
1790 // Hence, if we encounter a methoddeclop that has the same name but a
1791 // different implementation that means this vtableentry should point to
1792 // the op's implementation - that's an error.
1793 else if (method.getSymName() == getName() &&
1794 method.getImplAttr() != target && defined)
1795 return emitOpError() << "Target " << target
1796 << " should be overridden by " << classSymName;
1797 }
1798 }
1799 if (!defined)
1800 return emitOpError()
1801 << "Parent class does not point to any implementation!";
1802
1803 return success();
1804}
1805
1806LogicalResult DynQueueExtractOp::verify() {
1807 auto elementType = cast<QueueType>(getInput().getType()).getElementType();
1808
1809 // If the result type indicates we are extracting a single element,
1810 // the upper/lower indexes should be the same register.
1811 if (getResult().getType() == elementType && getLowerIdx() != getUpperIdx()) {
1812 return failure();
1813 }
1814
1815 return success();
1816}
1817
1818LogicalResult QueueResizeOp::verify() {
1819 if (cast<QueueType>(getInput().getType()).getElementType() !=
1820 cast<QueueType>(getResult().getType()).getElementType())
1821 return failure();
1822
1823 return success();
1824}
1825
1826LogicalResult QueueFromUnpackedArrayOp::verify() {
1827 // Verify the source and result have the same element type
1828 auto queueElementType =
1829 cast<QueueType>(getResult().getType()).getElementType();
1830
1831 auto arrayElementType =
1832 cast<UnpackedArrayType>(getInput().getType()).getElementType();
1833
1834 if (queueElementType != arrayElementType) {
1835 return emitOpError()
1836 << "Queue element type doesn't match unpacked array element type";
1837 }
1838
1839 return success();
1840}
1841
1842LogicalResult QueueConcatOp::verify() {
1843 // Verify the element types of all concatenated queues equal that of the
1844 // result queue.
1845 // We do not require the queue bounds to match.
1846 auto resultElType = cast<QueueType>(getResult().getType()).getElementType();
1847
1848 for (Value input : getInputs()) {
1849 auto inpElType = cast<QueueType>(input.getType()).getElementType();
1850 if (inpElType != resultElType) {
1851 return emitOpError() << "Queue element type " << inpElType
1852 << " doesn't match result element type "
1853 << resultElType;
1854 }
1855 }
1856
1857 return success();
1858}
1859
1860void DPIFuncOp::build(OpBuilder &odsBuilder, OperationState &odsState,
1861 StringAttr symName, ArrayRef<DPIArgInfo> dpiArgs,
1862 ArrayAttr argumentLocs, StringAttr verilogName) {
1863 auto *ctx = odsBuilder.getContext();
1864 odsState.addAttribute(getSymNameAttrName(odsState.name), symName);
1865
1866 // Derive FunctionType, direction array, and name array from dpiArgs.
1867 SmallVector<Type> inputTypes, resultTypes;
1868 SmallVector<Attribute> dirAttrs, nameAttrs;
1869 for (auto &arg : dpiArgs) {
1870 dirAttrs.push_back(DPIArgDirectionAttr::get(ctx, arg.dir));
1871 nameAttrs.push_back(arg.name);
1872 if (isCallOperandDir(arg.dir))
1873 inputTypes.push_back(arg.type);
1874 if (arg.dir == DPIArgDirection::Out || arg.dir == DPIArgDirection::InOut ||
1875 arg.dir == DPIArgDirection::Return)
1876 resultTypes.push_back(arg.type);
1877 }
1878
1879 odsState.addAttribute(
1880 getFunctionTypeAttrName(odsState.name),
1881 TypeAttr::get(FunctionType::get(ctx, inputTypes, resultTypes)));
1882 odsState.addAttribute(getDpiArgDirsAttrName(odsState.name),
1883 odsBuilder.getArrayAttr(dirAttrs));
1884 odsState.addAttribute(getDpiArgNamesAttrName(odsState.name),
1885 odsBuilder.getArrayAttr(nameAttrs));
1886
1887 if (argumentLocs)
1888 odsState.addAttribute(getArgumentLocsAttrName(odsState.name), argumentLocs);
1889 if (verilogName)
1890 odsState.addAttribute(getVerilogNameAttrName(odsState.name), verilogName);
1891 odsState.addRegion();
1892}
1893
1894/// Helper: parse a DPI direction keyword.
1895static std::optional<DPIArgDirection> parseDPIArgDirKeyword(StringRef keyword) {
1896 return llvm::StringSwitch<std::optional<DPIArgDirection>>(keyword)
1897 .Case("in", DPIArgDirection::In)
1898 .Case("out", DPIArgDirection::Out)
1899 .Case("inout", DPIArgDirection::InOut)
1900 .Case("return", DPIArgDirection::Return)
1901 .Default(std::nullopt);
1902}
1903
1904/// Helper: stringify a DPI direction.
1905static StringRef stringifyDPIArgDir(DPIArgDirection dir) {
1906 switch (dir) {
1907 case DPIArgDirection::In:
1908 return "in";
1909 case DPIArgDirection::Out:
1910 return "out";
1911 case DPIArgDirection::InOut:
1912 return "inout";
1913 case DPIArgDirection::Return:
1914 return "return";
1915 }
1916 llvm_unreachable("unknown DPIArgDirection");
1917}
1918
1919ParseResult DPIFuncOp::parse(OpAsmParser &parser, OperationState &result) {
1920 auto builder = parser.getBuilder();
1921 auto *ctx = builder.getContext();
1922
1923 (void)mlir::impl::parseOptionalVisibilityKeyword(parser, result.attributes);
1924
1925 StringAttr nameAttr;
1926 if (parser.parseSymbolName(nameAttr,
1927 DPIFuncOp::getSymNameAttrName(result.name),
1928 result.attributes))
1929 return failure();
1930
1931 SmallVector<DPIArgDirection> argDirs;
1932 SmallVector<StringAttr> argNames;
1933 SmallVector<Type> argTypes;
1934 SmallVector<Attribute> argLocs;
1935 auto unknownLoc = builder.getUnknownLoc();
1936 bool hasLocs = false;
1937
1938 auto parseOneArg = [&]() -> ParseResult {
1939 StringRef dirKeyword;
1940 auto keyLoc = parser.getCurrentLocation();
1941 if (parser.parseKeyword(&dirKeyword))
1942 return failure();
1943 auto dir = parseDPIArgDirKeyword(dirKeyword);
1944 if (!dir)
1945 return parser.emitError(keyLoc,
1946 "expected DPI argument direction keyword");
1947
1948 // For input/inout args, parse SSA name; for output/return, bare name.
1949 bool hasSSA = DPIFuncOp::isCallOperandDir(*dir);
1950 std::string argName;
1951 if (hasSSA) {
1952 OpAsmParser::UnresolvedOperand ssaName;
1953 if (parser.parseOperand(ssaName, /*allowResultNumber=*/false))
1954 return failure();
1955 argName = ssaName.name.substr(1).str();
1956 } else {
1957 if (parser.parseKeywordOrString(&argName))
1958 return failure();
1959 }
1960
1961 Type argType;
1962 if (parser.parseColonType(argType))
1963 return failure();
1964
1965 argDirs.push_back(*dir);
1966 argNames.push_back(StringAttr::get(ctx, argName));
1967 argTypes.push_back(argType);
1968
1969 std::optional<Location> maybeLoc;
1970 if (failed(parser.parseOptionalLocationSpecifier(maybeLoc)))
1971 return failure();
1972 if (maybeLoc) {
1973 argLocs.push_back(*maybeLoc);
1974 hasLocs = true;
1975 } else {
1976 argLocs.push_back(unknownLoc);
1977 }
1978 return success();
1979 };
1980
1981 if (parser.parseCommaSeparatedList(OpAsmParser::Delimiter::Paren, parseOneArg,
1982 " in DPI argument list"))
1983 return failure();
1984
1985 // Derive FunctionType from directions + types.
1986 SmallVector<Type> inputTypes, resultTypes;
1987 for (auto [dir, type] : llvm::zip(argDirs, argTypes)) {
1988 if (DPIFuncOp::isCallOperandDir(dir))
1989 inputTypes.push_back(type);
1990 if (dir == DPIArgDirection::Out || dir == DPIArgDirection::InOut ||
1991 dir == DPIArgDirection::Return)
1992 resultTypes.push_back(type);
1993 }
1994 auto funcType = FunctionType::get(ctx, inputTypes, resultTypes);
1995 result.addAttribute(DPIFuncOp::getFunctionTypeAttrName(result.name),
1996 TypeAttr::get(funcType));
1997
1998 // Store directions.
1999 SmallVector<Attribute> dirAttrs;
2000 for (auto d : argDirs)
2001 dirAttrs.push_back(DPIArgDirectionAttr::get(ctx, d));
2002 result.addAttribute(DPIFuncOp::getDpiArgDirsAttrName(result.name),
2003 builder.getArrayAttr(dirAttrs));
2004
2005 // Store argument names.
2006 SmallVector<Attribute> nameAttrs(argNames.begin(), argNames.end());
2007 result.addAttribute(DPIFuncOp::getDpiArgNamesAttrName(result.name),
2008 builder.getArrayAttr(nameAttrs));
2009
2010 if (hasLocs)
2011 result.addAttribute(DPIFuncOp::getArgumentLocsAttrName(result.name),
2012 builder.getArrayAttr(argLocs));
2013 result.addRegion();
2014
2015 if (failed(parser.parseOptionalAttrDictWithKeyword(result.attributes)))
2016 return failure();
2017 return success();
2018}
2019
2020//===----------------------------------------------------------------------===//
2021// DPIFuncOp FunctionOpInterface
2022//===----------------------------------------------------------------------===//
2023
2024::mlir::Type DPIFuncOp::cloneTypeWith(::mlir::TypeRange inputs,
2025 ::mlir::TypeRange results) {
2026 return FunctionType::get(getContext(), inputs, results);
2027}
2028
2029void DPIFuncOp::getDPIArgTypes(SmallVectorImpl<Type> &argTypes) {
2030 auto funcType = getFunctionType();
2031 auto inputs = funcType.getInputs();
2032 auto results = funcType.getResults();
2033 auto dirs = getDpiArgDirsAttr();
2034 unsigned inputIdx = 0, resultIdx = 0;
2035 for (auto dirAttr : dirs) {
2036 auto dir = cast<DPIArgDirectionAttr>(dirAttr).getValue();
2037 switch (dir) {
2038 case DPIArgDirection::In:
2039 argTypes.push_back(inputs[inputIdx++]);
2040 break;
2041 case DPIArgDirection::Out:
2042 argTypes.push_back(results[resultIdx++]);
2043 break;
2044 case DPIArgDirection::InOut:
2045 argTypes.push_back(inputs[inputIdx++]);
2046 resultIdx++;
2047 break;
2048 case DPIArgDirection::Return:
2049 argTypes.push_back(results[resultIdx++]);
2050 break;
2051 }
2052 }
2053}
2054
2055LogicalResult DPIFuncOp::verify() {
2056 auto dirs = getDpiArgDirs();
2057 auto names = getDpiArgNames();
2058 if (dirs.size() != names.size())
2059 return emitOpError("argument directions and names must have the same size");
2060
2061 // Check return constraints: at most one, must be last.
2062 bool seenReturn = false;
2063 for (auto [i, dirAttr] : llvm::enumerate(dirs)) {
2064 auto dir = cast<DPIArgDirectionAttr>(dirAttr).getValue();
2065 if (dir == DPIArgDirection::Return) {
2066 if (seenReturn)
2067 return emitOpError("'return' argument must be the last argument");
2068 if (i != dirs.size() - 1)
2069 return emitOpError("'return' argument must be the last argument");
2070 seenReturn = true;
2071 }
2072 }
2073 return success();
2074}
2075
2076void DPIFuncOp::print(OpAsmPrinter &p) {
2077 p << ' ';
2078
2079 StringRef visibilityAttrName =
2080 mlir::SymbolOpInterface::getDefaultVisibilityAttrName();
2081 if (auto visibility = (*this)->getAttrOfType<StringAttr>(visibilityAttrName))
2082 p << visibility.getValue() << ' ';
2083 p.printSymbolName(getSymName());
2084
2085 auto dirs = getDpiArgDirs();
2086 auto names = getDpiArgNames();
2087 SmallVector<Type> argTypes;
2088 getDPIArgTypes(argTypes);
2089
2090 p << '(';
2091 llvm::interleaveComma(llvm::enumerate(dirs), p, [&](auto it) {
2092 auto dir = cast<DPIArgDirectionAttr>(it.value()).getValue();
2093 auto i = it.index();
2094 auto name = cast<StringAttr>(names[i]).getValue();
2095 auto type = argTypes[i];
2096
2097 p << stringifyDPIArgDir(dir) << ' ';
2098
2099 if (isCallOperandDir(dir))
2100 p << '%';
2101 p.printKeywordOrString(name);
2102 p << " : ";
2103 p.printType(type);
2104
2105 if (getArgumentLocs()) {
2106 auto loc = cast<Location>(getArgumentLocsAttr()[i]);
2107 if (loc != UnknownLoc::get(getContext()))
2108 p.printOptionalLocationSpecifier(loc);
2109 }
2110 });
2111 p << ')';
2112
2113 mlir::function_interface_impl::printFunctionAttributes(
2114 p, *this,
2115 {visibilityAttrName, getFunctionTypeAttrName(), getDpiArgDirsAttrName(),
2116 getDpiArgNamesAttrName(), getArgumentLocsAttrName()});
2117}
2118
2119LogicalResult
2120FuncDPICallOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
2121 auto *referencedOp =
2122 symbolTable.lookupNearestSymbolFrom(*this, getCalleeAttr());
2123 if (!referencedOp)
2124 return emitError("cannot find function declaration '")
2125 << getCallee() << "'";
2126 if (auto dpiFunc = dyn_cast<DPIFuncOp>(referencedOp)) {
2127 auto funcType = cast<FunctionType>(dpiFunc.getFunctionType());
2128 auto expectedInputs = funcType.getInputs();
2129 auto expectedResults = funcType.getResults();
2130 if (getInputs().size() != expectedInputs.size())
2131 return emitError("expects ")
2132 << expectedInputs.size() << " DPI operands, but got "
2133 << getInputs().size();
2134 if (getResults().size() != expectedResults.size())
2135 return emitError("expects ")
2136 << expectedResults.size() << " DPI results, but got "
2137 << getResults().size();
2138 for (auto [operand, expectedType] : llvm::zip(getInputs(), expectedInputs))
2139 if (operand.getType() != expectedType)
2140 return emitError("operand type mismatch: expected ")
2141 << expectedType << ", but got " << operand.getType();
2142 for (auto [result, expectedType] : llvm::zip(getResults(), expectedResults))
2143 if (result.getType() != expectedType)
2144 return emitError("result type mismatch: expected ")
2145 << expectedType << ", but got " << result.getType();
2146 return success();
2147 }
2148 if (isa<func::FuncOp>(referencedOp))
2149 return success();
2150 return emitError("callee must be 'moore.func.dpi' or 'func.func' but got '")
2151 << referencedOp->getName() << "'";
2152}
2153
2154LogicalResult ReadMemBIOp::verify() {
2155 if (getFinishAddr() && !getStartAddr())
2156 return emitOpError("'finishAddr' requires 'startAddr' to be present");
2157
2158 if (getSliceLeft() && !getSliceRight())
2159 return emitOpError("'sliceLeft' requires 'sliceRight' to be present");
2160 if (getSliceRight() && !getSliceLeft())
2161 return emitOpError("'sliceRight' requires 'sliceLeft' to be present");
2162
2163 auto ref = dyn_cast<moore::RefType>(getDest().getType());
2164 if (!ref)
2165 return emitOpError("'dest' must be a Moore reference type, got ")
2166 << getDest().getType();
2167
2168 unsigned numDims = 0;
2169 Type nested = ref.getNestedType();
2170
2171 if (isa<moore::QueueType>(nested)) {
2172 numDims = 1;
2173 } else {
2174 while (auto arr = dyn_cast<moore::UnpackedArrayType>(nested)) {
2175 ++numDims;
2176 nested = arr.getElementType();
2177 }
2178
2179 if (numDims == 0)
2180 return emitOpError(
2181 "'dest' must reference an unpacked array or queue, got ")
2182 << ref.getNestedType();
2183 }
2184 if (getDimLows().size() != numDims || getDimDescending().size() != numDims)
2185 return emitOpError("'dimLows' and 'dimDescending' must have one entry per "
2186 "unpacked dimension");
2187
2188 return success();
2189}
2190
2191//===----------------------------------------------------------------------===//
2192// TableGen generated logic.
2193//===----------------------------------------------------------------------===//
2194
2195// Provide the autogenerated implementation guts for the Op classes.
2196#define GET_OP_CLASSES
2197#include "circt/Dialect/Moore/Moore.cpp.inc"
2198#include "circt/Dialect/Moore/MooreEnums.cpp.inc"
assert(baseType &&"element must be base type")
MlirType elementType
Definition CHIRRTL.cpp:29
static std::unique_ptr< Context > context
static hw::OutputOp getOutputOp(hw::HWModuleOp module)
static bool getFieldName(const FieldRef &fieldRef, SmallString< 32 > &string)
static Location getLoc(DefSlot slot)
Definition Mem2Reg.cpp:193
false return failure()
Parse an optional keyword or string and set instance into 'result'.
return success()
NamedAttrList attrs
static std::optional< DPIArgDirection > parseDPIArgDirKeyword(StringRef keyword)
Helper: parse a DPI direction keyword.
static OpFoldResult powCommonFolding(MLIRContext *ctxt, Attribute lhs, Attribute rhs)
static StringRef stringifyDPIArgDir(DPIArgDirection dir)
Helper: stringify a DPI direction.
static ArrayRef< StructLikeMember > getStructMembers(Type type)
Definition MooreOps.cpp:883
static std::optional< uint32_t > getStructFieldIndex(Type type, StringAttr name)
Definition MooreOps.cpp:874
static UnpackedType getStructFieldType(Type type, StringAttr name)
Definition MooreOps.cpp:892
static std::pair< unsigned, UnpackedType > getArrayElements(Type type)
Definition MooreOps.cpp:841
static InstancePath empty
Four-valued arbitrary precision integers.
Definition FVInt.h:37
bool isNegative() const
Determine whether the integer interpreted as a signed number would be negative.
Definition FVInt.h:185
FVInt sext(unsigned bitWidth) const
Sign-extend the integer to a new bit width.
Definition FVInt.h:148
unsigned getSignificantBits() const
Compute the minimum bit width necessary to accurately represent this integer's value and sign.
Definition FVInt.h:102
static FVInt getAllX(unsigned numBits)
Construct an FVInt with all bits set to X.
Definition FVInt.h:75
bool hasUnknown() const
Determine if any bits are X or Z.
Definition FVInt.h:168
unsigned getActiveBits() const
Compute the number of active bits in the value.
Definition FVInt.h:92
unsigned getBitWidth() const
Return the number of bits this integer has.
Definition FVInt.h:85
FVInt trunc(unsigned bitWidth) const
Truncate the integer to a smaller bit width.
Definition FVInt.h:132
A packed SystemVerilog type.
Definition MooreTypes.h:155
std::optional< unsigned > getBitSize() const
Get the size of this type in bits.
Domain getDomain() const
Get the value domain of this type.
An unpacked SystemVerilog type.
Definition MooreTypes.h:103
std::string getInstanceName(mlir::func::CallOp callOp)
A helper function to get the instance name.
StringAttr getName(ArrayAttr names, size_t idx)
Return the name at the specified index of the ArrayAttr or null if it cannot be determined.
ParseResult parseModuleSignature(OpAsmParser &parser, SmallVectorImpl< PortParse > &args, TypeAttr &modType)
New Style parsing.
void printModuleSignatureNew(OpAsmPrinter &p, Region &body, hw::ModuleType modType, ArrayRef< Attribute > portAttrs, ArrayRef< Location > locAttrs)
FunctionType getModuleType(Operation *module)
Return the signature for the specified module as a function type.
Definition HWOps.cpp:573
Domain
The number of values each bit of a type can assume.
Definition MooreTypes.h:51
@ FourValued
Four-valued types such as logic or integer.
Definition MooreTypes.h:55
@ TwoValued
Two-valued types such as bit or int.
Definition MooreTypes.h:53
RealWidth
The type of floating point / real number behind a RealType.
Definition MooreTypes.h:59
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.
ParseResult parseInputPortList(OpAsmParser &parser, SmallVectorImpl< OpAsmParser::UnresolvedOperand > &inputs, SmallVectorImpl< Type > &inputTypes, ArrayAttr &inputNames)
Parse a list of instance input ports.
void printOutputPortList(OpAsmPrinter &p, Operation *op, TypeRange resultTypes, ArrayAttr resultNames)
Print a list of instance output ports.
void printFVInt(AsmPrinter &p, const FVInt &value)
Print a four-valued integer usign an AsmPrinter.
Definition FVInt.cpp:147
ParseResult parseFVInt(AsmParser &p, FVInt &result)
Parse a four-valued integer using an AsmParser.
Definition FVInt.cpp:162
void printInputPortList(OpAsmPrinter &p, Operation *op, OperandRange inputs, TypeRange inputTypes, ArrayAttr inputNames)
Print a list of instance input ports.
ParseResult parseOutputPortList(OpAsmParser &parser, SmallVectorImpl< Type > &resultTypes, ArrayAttr &resultNames)
Parse a list of instance output ports.
function_ref< void(Value, StringRef)> OpAsmSetValueNameFn
Definition LLVM.h:193