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