CIRCT 23.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(SymbolTable::getSymbolAttrName(),
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 = SymbolTable::getVisibilityAttrName();
46 if (auto visibility = (*this)->getAttrOfType<StringAttr>(visibilityAttrName))
47 p << visibility.getValue() << ' ';
48
49 p.printSymbolName(SymbolTable::getSymbolName(*this).getValue());
51 getModuleType(), {}, {});
52 p << " ";
53 p.printRegion(getBodyRegion(), /*printEntryBlockArgs=*/false,
54 /*printBlockTerminators=*/true);
55
56 p.printOptionalAttrDictWithKeyword(getOperation()->getAttrs(),
57 getAttributeNames());
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.elemType);
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 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::OpaqueProperties 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::OpaqueProperties 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::OpaqueProperties properties,
806 mlir::RegionRange regions, SmallVectorImpl<Type> &results) {
807 Domain domain = Domain::TwoValued;
808 unsigned width = 0;
809 for (Value operand : operands) {
810 UnpackedType nestedType = cast<RefType>(operand.getType()).getNestedType();
811 PackedType packedType = dyn_cast<PackedType>(nestedType);
812
813 if (!packedType) {
814 return failure();
815 }
816
817 if (packedType.getDomain() == Domain::FourValued)
818 domain = Domain::FourValued;
819
820 // getBitSize() for PackedType returns an optional, so we must check it.
821 std::optional<int> bitSize = packedType.getBitSize();
822 if (!bitSize) {
823 return failure();
824 }
825 width += *bitSize;
826 }
827 results.push_back(RefType::get(IntType::get(context, width, domain)));
828 return success();
829}
830
831//===----------------------------------------------------------------------===//
832// ArrayCreateOp
833//===----------------------------------------------------------------------===//
834
835static std::pair<unsigned, UnpackedType> getArrayElements(Type type) {
836 if (auto arrayType = dyn_cast<ArrayType>(type))
837 return {arrayType.getSize(), arrayType.getElementType()};
838 if (auto arrayType = dyn_cast<UnpackedArrayType>(type))
839 return {arrayType.getSize(), arrayType.getElementType()};
840 assert(0 && "expected ArrayType or UnpackedArrayType");
841 return {};
842}
843
844LogicalResult ArrayCreateOp::verify() {
845 auto [size, elementType] = getArrayElements(getType());
846
847 // Check that the number of operands matches the array size.
848 if (getElements().size() != size)
849 return emitOpError() << "has " << getElements().size()
850 << " operands, but result type requires " << size;
851
852 // Check that the operand types match the array element type. We only need to
853 // check one of the operands, since the `SameTypeOperands` trait ensures all
854 // operands have the same type.
855 if (size > 0) {
856 auto value = getElements()[0];
857 if (value.getType() != elementType)
858 return emitOpError() << "operands have type " << value.getType()
859 << ", but array requires " << elementType;
860 }
861 return success();
862}
863
864//===----------------------------------------------------------------------===//
865// StructCreateOp
866//===----------------------------------------------------------------------===//
867
868static std::optional<uint32_t> getStructFieldIndex(Type type, StringAttr name) {
869 if (auto structType = dyn_cast<StructType>(type))
870 return structType.getFieldIndex(name);
871 if (auto structType = dyn_cast<UnpackedStructType>(type))
872 return structType.getFieldIndex(name);
873 assert(0 && "expected StructType or UnpackedStructType");
874 return {};
875}
876
877static ArrayRef<StructLikeMember> getStructMembers(Type type) {
878 if (auto structType = dyn_cast<StructType>(type))
879 return structType.getMembers();
880 if (auto structType = dyn_cast<UnpackedStructType>(type))
881 return structType.getMembers();
882 assert(0 && "expected StructType or UnpackedStructType");
883 return {};
884}
885
886static UnpackedType getStructFieldType(Type type, StringAttr name) {
887 if (auto index = getStructFieldIndex(type, name))
888 return getStructMembers(type)[*index].type;
889 return {};
890}
891
892LogicalResult StructCreateOp::verify() {
893 auto members = getStructMembers(getType());
894
895 // Check that the number of operands matches the number of struct fields.
896 if (getFields().size() != members.size())
897 return emitOpError() << "has " << getFields().size()
898 << " operands, but result type requires "
899 << members.size();
900
901 // Check that the operand types match the struct field types.
902 for (auto [index, pair] : llvm::enumerate(llvm::zip(getFields(), members))) {
903 auto [value, member] = pair;
904 if (value.getType() != member.type)
905 return emitOpError() << "operand #" << index << " has type "
906 << value.getType() << ", but struct field "
907 << member.name << " requires " << member.type;
908 }
909 return success();
910}
911
912OpFoldResult StructCreateOp::fold(FoldAdaptor adaptor) {
913 SmallVector<NamedAttribute> fields;
914 for (auto [member, field] :
915 llvm::zip(getStructMembers(getType()), adaptor.getFields())) {
916 if (!field)
917 return {};
918 fields.push_back(NamedAttribute(member.name, field));
919 }
920 return DictionaryAttr::get(getContext(), fields);
921}
922
923//===----------------------------------------------------------------------===//
924// StructExtractOp
925//===----------------------------------------------------------------------===//
926
927LogicalResult StructExtractOp::verify() {
928 auto type = getStructFieldType(getInput().getType(), getFieldNameAttr());
929 if (!type)
930 return emitOpError() << "extracts field " << getFieldNameAttr()
931 << " which does not exist in " << getInput().getType();
932 if (type != getType())
933 return emitOpError() << "result type " << getType()
934 << " must match struct field type " << type;
935 return success();
936}
937
938OpFoldResult StructExtractOp::fold(FoldAdaptor adaptor) {
939 // Extract on a constant struct input.
940 if (auto fields = dyn_cast_or_null<DictionaryAttr>(adaptor.getInput()))
941 if (auto value = fields.get(getFieldNameAttr()))
942 return value;
943
944 // extract(inject(s, "field", v), "field") -> v
945 if (auto inject = getInput().getDefiningOp<StructInjectOp>()) {
946 if (inject.getFieldNameAttr() == getFieldNameAttr())
947 return inject.getNewValue();
948 return {};
949 }
950
951 // extract(create({"field": v, ...}), "field") -> v
952 if (auto create = getInput().getDefiningOp<StructCreateOp>()) {
953 if (auto index = getStructFieldIndex(create.getType(), getFieldNameAttr()))
954 return create.getFields()[*index];
955 return {};
956 }
957
958 return {};
959}
960
961//===----------------------------------------------------------------------===//
962// StructExtractRefOp
963//===----------------------------------------------------------------------===//
964
965LogicalResult StructExtractRefOp::verify() {
966 auto type = getStructFieldType(
967 cast<RefType>(getInput().getType()).getNestedType(), getFieldNameAttr());
968 if (!type)
969 return emitOpError() << "extracts field " << getFieldNameAttr()
970 << " which does not exist in " << getInput().getType();
971 if (type != getType().getNestedType())
972 return emitOpError() << "result ref of type " << getType().getNestedType()
973 << " must match struct field type " << type;
974 return success();
975}
976
977bool StructExtractRefOp::canRewire(
978 const DestructurableMemorySlot &slot,
979 SmallPtrSetImpl<Attribute> &usedIndices,
980 SmallVectorImpl<MemorySlot> &mustBeSafelyUsed,
981 const DataLayout &dataLayout) {
982 if (slot.ptr != getInput())
983 return false;
984 auto index = getFieldNameAttr();
985 if (!index || !slot.subelementTypes.contains(index))
986 return false;
987 usedIndices.insert(index);
988 return true;
989}
990
991DeletionKind
992StructExtractRefOp::rewire(const DestructurableMemorySlot &slot,
993 DenseMap<Attribute, MemorySlot> &subslots,
994 OpBuilder &builder, const DataLayout &dataLayout) {
995 auto index = getFieldNameAttr();
996 const MemorySlot &memorySlot = subslots.at(index);
997 replaceAllUsesWith(memorySlot.ptr);
998 getInputMutable().drop();
999 erase();
1000 return DeletionKind::Keep;
1001}
1002
1003//===----------------------------------------------------------------------===//
1004// StructInjectOp
1005//===----------------------------------------------------------------------===//
1006
1007LogicalResult StructInjectOp::verify() {
1008 auto type = getStructFieldType(getInput().getType(), getFieldNameAttr());
1009 if (!type)
1010 return emitOpError() << "injects field " << getFieldNameAttr()
1011 << " which does not exist in " << getInput().getType();
1012 if (type != getNewValue().getType())
1013 return emitOpError() << "injected value " << getNewValue().getType()
1014 << " must match struct field type " << type;
1015 return success();
1016}
1017
1018OpFoldResult StructInjectOp::fold(FoldAdaptor adaptor) {
1019 auto input = adaptor.getInput();
1020 auto newValue = adaptor.getNewValue();
1021 if (!input || !newValue)
1022 return {};
1023 NamedAttrList fields(cast<DictionaryAttr>(input));
1024 fields.set(getFieldNameAttr(), newValue);
1025 return fields.getDictionary(getContext());
1026}
1027
1028LogicalResult StructInjectOp::canonicalize(StructInjectOp op,
1029 PatternRewriter &rewriter) {
1030 auto members = getStructMembers(op.getType());
1031
1032 // Chase a chain of `struct_inject` ops, with an optional final
1033 // `struct_create`, and take note of the values assigned to each field.
1034 SmallPtrSet<Operation *, 4> injectOps;
1035 DenseMap<StringAttr, Value> fieldValues;
1036 Value input = op;
1037 while (auto injectOp = input.getDefiningOp<StructInjectOp>()) {
1038 if (!injectOps.insert(injectOp).second)
1039 return failure();
1040 fieldValues.insert({injectOp.getFieldNameAttr(), injectOp.getNewValue()});
1041 input = injectOp.getInput();
1042 }
1043 if (auto createOp = input.getDefiningOp<StructCreateOp>())
1044 for (auto [value, member] : llvm::zip(createOp.getFields(), members))
1045 fieldValues.insert({member.name, value});
1046
1047 // If the inject chain sets all fields, canonicalize to a `struct_create`.
1048 if (fieldValues.size() == members.size()) {
1049 SmallVector<Value> values;
1050 values.reserve(fieldValues.size());
1051 for (auto member : members)
1052 values.push_back(fieldValues.lookup(member.name));
1053 rewriter.replaceOpWithNewOp<StructCreateOp>(op, op.getType(), values);
1054 return success();
1055 }
1056
1057 // If each inject op in the chain assigned to a unique field, there is nothing
1058 // to canonicalize.
1059 if (injectOps.size() == fieldValues.size())
1060 return failure();
1061
1062 // Otherwise we can eliminate overwrites by creating new injects. The hash map
1063 // of field values contains the last assigned value for each field.
1064 for (auto member : members)
1065 if (auto value = fieldValues.lookup(member.name))
1066 input = StructInjectOp::create(rewriter, op.getLoc(), op.getType(), input,
1067 member.name, value);
1068 rewriter.replaceOp(op, input);
1069 return success();
1070}
1071
1072//===----------------------------------------------------------------------===//
1073// UnionCreateOp
1074//===----------------------------------------------------------------------===//
1075
1076LogicalResult UnionCreateOp::verify() {
1077 /// checks if the types of the input is exactly equal to the union field
1078 /// type
1079 return TypeSwitch<Type, LogicalResult>(getType())
1080 .Case<UnionType, UnpackedUnionType>([this](auto &type) {
1081 auto members = type.getMembers();
1082 auto inputType = getInput().getType();
1083 auto fieldName = getFieldName();
1084 for (const auto &member : members)
1085 if (member.name == fieldName && member.type == inputType)
1086 return success();
1087 for (const auto &member : members) {
1088 if (member.name == fieldName) {
1089 emitOpError() << "input type " << inputType
1090 << " does not match union field '" << fieldName
1091 << "' type " << member.type;
1092 return failure();
1093 }
1094 }
1095 emitOpError() << "field '" << fieldName << "' not found in union type";
1096 return failure();
1097 })
1098 .Default([this](auto &) {
1099 emitOpError("input type must be UnionType or UnpackedUnionType");
1100 return failure();
1101 });
1102}
1103
1104//===----------------------------------------------------------------------===//
1105// UnionExtractOp
1106//===----------------------------------------------------------------------===//
1107
1108LogicalResult UnionExtractOp::verify() {
1109 /// checks if the types of the input is exactly equal to the one of the
1110 /// types of the result union fields
1111 return TypeSwitch<Type, LogicalResult>(getInput().getType())
1112 .Case<UnionType, UnpackedUnionType>([this](auto &type) {
1113 auto members = type.getMembers();
1114 auto fieldName = getFieldName();
1115 auto resultType = getType();
1116 for (const auto &member : members)
1117 if (member.name == fieldName && member.type == resultType)
1118 return success();
1119 emitOpError("result type must match the union field type");
1120 return failure();
1121 })
1122 .Default([this](auto &) {
1123 emitOpError("input type must be UnionType or UnpackedUnionType");
1124 return failure();
1125 });
1126}
1127
1128//===----------------------------------------------------------------------===//
1129// UnionExtractOp
1130//===----------------------------------------------------------------------===//
1131
1132LogicalResult UnionExtractRefOp::verify() {
1133 /// checks if the types of the result is exactly equal to the type of the
1134 /// refe union field
1135 return TypeSwitch<Type, LogicalResult>(getInput().getType().getNestedType())
1136 .Case<UnionType, UnpackedUnionType>([this](auto &type) {
1137 auto members = type.getMembers();
1138 auto fieldName = getFieldName();
1139 auto resultType = getType().getNestedType();
1140 for (const auto &member : members)
1141 if (member.name == fieldName && member.type == resultType)
1142 return success();
1143 emitOpError("result type must match the union field type");
1144 return failure();
1145 })
1146 .Default([this](auto &) {
1147 emitOpError("input type must be UnionType or UnpackedUnionType");
1148 return failure();
1149 });
1150}
1151
1152//===----------------------------------------------------------------------===//
1153// YieldOp
1154//===----------------------------------------------------------------------===//
1155
1156LogicalResult YieldOp::verify() {
1157 Type expType;
1158 auto *parentOp = getOperation()->getParentOp();
1159 if (auto cond = dyn_cast<ConditionalOp>(parentOp)) {
1160 expType = cond.getType();
1161 } else if (auto varOp = dyn_cast<GlobalVariableOp>(parentOp)) {
1162 expType = varOp.getType();
1163 } else {
1164 llvm_unreachable("all in ParentOneOf handled");
1165 }
1166
1167 auto actType = getOperand().getType();
1168 if (expType != actType) {
1169 return emitOpError() << "yields " << actType << ", but parent expects "
1170 << expType;
1171 }
1172 return success();
1173}
1174
1175//===----------------------------------------------------------------------===//
1176// ConversionOp
1177//===----------------------------------------------------------------------===//
1178
1179OpFoldResult ConversionOp::fold(FoldAdaptor adaptor) {
1180 // Fold away no-op casts.
1181 if (getInput().getType() == getResult().getType())
1182 return getInput();
1183
1184 // Convert domains of constant integer inputs.
1185 auto intInput = dyn_cast_or_null<FVIntegerAttr>(adaptor.getInput());
1186 auto fromIntType = dyn_cast<IntType>(getInput().getType());
1187 auto toIntType = dyn_cast<IntType>(getResult().getType());
1188 if (intInput && fromIntType && toIntType &&
1189 fromIntType.getWidth() == toIntType.getWidth()) {
1190 // If we are going *to* a four-valued type, simply pass through the
1191 // constant.
1192 if (toIntType.getDomain() == Domain::FourValued)
1193 return intInput;
1194
1195 // Otherwise map all unknown bits to zero (the default in SystemVerilog) and
1196 // return a new constant.
1197 return FVIntegerAttr::get(getContext(), intInput.getValue().toAPInt(false));
1198 }
1199
1200 return {};
1201}
1202
1203//===----------------------------------------------------------------------===//
1204// LogicToIntOp
1205//===----------------------------------------------------------------------===//
1206
1207OpFoldResult LogicToIntOp::fold(FoldAdaptor adaptor) {
1208 // logic_to_int(int_to_logic(x)) -> x
1209 if (auto reverseOp = getInput().getDefiningOp<IntToLogicOp>())
1210 return reverseOp.getInput();
1211
1212 // Map all unknown bits to zero (the default in SystemVerilog) and return a
1213 // new constant.
1214 if (auto intInput = dyn_cast_or_null<FVIntegerAttr>(adaptor.getInput()))
1215 return FVIntegerAttr::get(getContext(), intInput.getValue().toAPInt(false));
1216
1217 return {};
1218}
1219
1220//===----------------------------------------------------------------------===//
1221// IntToLogicOp
1222//===----------------------------------------------------------------------===//
1223
1224OpFoldResult IntToLogicOp::fold(FoldAdaptor adaptor) {
1225 // Cannot fold int_to_logic(logic_to_int(x)) -> x since that would lose
1226 // information.
1227
1228 // Simply pass through constants.
1229 if (auto intInput = dyn_cast_or_null<FVIntegerAttr>(adaptor.getInput()))
1230 return intInput;
1231
1232 return {};
1233}
1234
1235//===----------------------------------------------------------------------===//
1236// TimeToLogicOp
1237//===----------------------------------------------------------------------===//
1238
1239OpFoldResult TimeToLogicOp::fold(FoldAdaptor adaptor) {
1240 // time_to_logic(logic_to_time(x)) -> x
1241 if (auto reverseOp = getInput().getDefiningOp<LogicToTimeOp>())
1242 return reverseOp.getInput();
1243
1244 // Convert constants.
1245 if (auto attr = dyn_cast_or_null<IntegerAttr>(adaptor.getInput()))
1246 return FVIntegerAttr::get(getContext(), attr.getValue());
1247
1248 return {};
1249}
1250
1251//===----------------------------------------------------------------------===//
1252// LogicToTimeOp
1253//===----------------------------------------------------------------------===//
1254
1255OpFoldResult LogicToTimeOp::fold(FoldAdaptor adaptor) {
1256 // logic_to_time(time_to_logic(x)) -> x
1257 if (auto reverseOp = getInput().getDefiningOp<TimeToLogicOp>())
1258 return reverseOp.getInput();
1259
1260 // Convert constants.
1261 if (auto attr = dyn_cast_or_null<FVIntegerAttr>(adaptor.getInput()))
1262 return IntegerAttr::get(getContext(), APSInt(attr.getValue().toAPInt(false),
1263 /*isUnsigned=*/true));
1264
1265 return {};
1266}
1267
1268//===----------------------------------------------------------------------===//
1269// ConvertRealOp
1270//===----------------------------------------------------------------------===//
1271
1272OpFoldResult ConvertRealOp::fold(FoldAdaptor adaptor) {
1273 if (getInput().getType() == getResult().getType())
1274 return getInput();
1275
1276 return {};
1277}
1278
1279//===----------------------------------------------------------------------===//
1280// TruncOp
1281//===----------------------------------------------------------------------===//
1282
1283OpFoldResult TruncOp::fold(FoldAdaptor adaptor) {
1284 // Truncate constants.
1285 if (auto intAttr = dyn_cast_or_null<FVIntegerAttr>(adaptor.getInput())) {
1286 auto width = getType().getWidth();
1287 return FVIntegerAttr::get(getContext(), intAttr.getValue().trunc(width));
1288 }
1289
1290 return {};
1291}
1292
1293//===----------------------------------------------------------------------===//
1294// ZExtOp
1295//===----------------------------------------------------------------------===//
1296
1297OpFoldResult ZExtOp::fold(FoldAdaptor adaptor) {
1298 // Zero-extend constants.
1299 if (auto intAttr = dyn_cast_or_null<FVIntegerAttr>(adaptor.getInput())) {
1300 auto width = getType().getWidth();
1301 return FVIntegerAttr::get(getContext(), intAttr.getValue().zext(width));
1302 }
1303
1304 return {};
1305}
1306
1307//===----------------------------------------------------------------------===//
1308// SExtOp
1309//===----------------------------------------------------------------------===//
1310
1311OpFoldResult SExtOp::fold(FoldAdaptor adaptor) {
1312 // Sign-extend constants.
1313 if (auto intAttr = dyn_cast_or_null<FVIntegerAttr>(adaptor.getInput())) {
1314 auto width = getType().getWidth();
1315 return FVIntegerAttr::get(getContext(), intAttr.getValue().sext(width));
1316 }
1317
1318 return {};
1319}
1320
1321//===----------------------------------------------------------------------===//
1322// BoolCastOp
1323//===----------------------------------------------------------------------===//
1324
1325OpFoldResult BoolCastOp::fold(FoldAdaptor adaptor) {
1326 // Fold away no-op casts.
1327 if (getInput().getType() == getResult().getType())
1328 return getInput();
1329 return {};
1330}
1331
1332//===----------------------------------------------------------------------===//
1333// BlockingAssignOp
1334//===----------------------------------------------------------------------===//
1335
1336bool BlockingAssignOp::loadsFrom(const MemorySlot &slot) { return false; }
1337
1338bool BlockingAssignOp::storesTo(const MemorySlot &slot) {
1339 return getDst() == slot.ptr;
1340}
1341
1342Value BlockingAssignOp::getStored(const MemorySlot &slot, OpBuilder &builder,
1343 Value reachingDef,
1344 const DataLayout &dataLayout) {
1345 return getSrc();
1346}
1347
1348bool BlockingAssignOp::canUsesBeRemoved(
1349 const MemorySlot &slot, const SmallPtrSetImpl<OpOperand *> &blockingUses,
1350 SmallVectorImpl<OpOperand *> &newBlockingUses,
1351 const DataLayout &dataLayout) {
1352
1353 if (blockingUses.size() != 1)
1354 return false;
1355 Value blockingUse = (*blockingUses.begin())->get();
1356 return blockingUse == slot.ptr && getDst() == slot.ptr &&
1357 getSrc() != slot.ptr && getSrc().getType() == slot.elemType;
1358}
1359
1360DeletionKind BlockingAssignOp::removeBlockingUses(
1361 const MemorySlot &slot, const SmallPtrSetImpl<OpOperand *> &blockingUses,
1362 OpBuilder &builder, Value reachingDefinition,
1363 const DataLayout &dataLayout) {
1364 return DeletionKind::Delete;
1365}
1366
1367//===----------------------------------------------------------------------===//
1368// ReadOp
1369//===----------------------------------------------------------------------===//
1370
1371bool ReadOp::loadsFrom(const MemorySlot &slot) {
1372 return getInput() == slot.ptr;
1373}
1374
1375bool ReadOp::storesTo(const MemorySlot &slot) { return false; }
1376
1377Value ReadOp::getStored(const MemorySlot &slot, OpBuilder &builder,
1378 Value reachingDef, const DataLayout &dataLayout) {
1379 llvm_unreachable("getStored should not be called on ReadOp");
1380}
1381
1382bool ReadOp::canUsesBeRemoved(const MemorySlot &slot,
1383 const SmallPtrSetImpl<OpOperand *> &blockingUses,
1384 SmallVectorImpl<OpOperand *> &newBlockingUses,
1385 const DataLayout &dataLayout) {
1386
1387 if (blockingUses.size() != 1)
1388 return false;
1389 Value blockingUse = (*blockingUses.begin())->get();
1390 return blockingUse == slot.ptr && getOperand() == slot.ptr &&
1391 getResult().getType() == slot.elemType;
1392}
1393
1394DeletionKind
1395ReadOp::removeBlockingUses(const MemorySlot &slot,
1396 const SmallPtrSetImpl<OpOperand *> &blockingUses,
1397 OpBuilder &builder, Value reachingDefinition,
1398 const DataLayout &dataLayout) {
1399 getResult().replaceAllUsesWith(reachingDefinition);
1400 return DeletionKind::Delete;
1401}
1402
1403//===----------------------------------------------------------------------===//
1404// PowSOp
1405//===----------------------------------------------------------------------===//
1406
1407static OpFoldResult powCommonFolding(MLIRContext *ctxt, Attribute lhs,
1408 Attribute rhs) {
1409 auto lhsValue = dyn_cast_or_null<FVIntegerAttr>(lhs);
1410 if (lhsValue && lhsValue.getValue() == 1)
1411 return lhs;
1412
1413 auto rhsValue = dyn_cast_or_null<FVIntegerAttr>(rhs);
1414 if (rhsValue && rhsValue.getValue().isZero())
1415 return FVIntegerAttr::get(ctxt,
1416 FVInt(rhsValue.getValue().getBitWidth(), 1));
1417
1418 return {};
1419}
1420
1421OpFoldResult PowSOp::fold(FoldAdaptor adaptor) {
1422 return powCommonFolding(getContext(), adaptor.getLhs(), adaptor.getRhs());
1423}
1424
1425LogicalResult PowSOp::canonicalize(PowSOp op, PatternRewriter &rewriter) {
1426 Location loc = op.getLoc();
1427 auto intType = cast<IntType>(op.getRhs().getType());
1428 if (auto baseOp = op.getLhs().getDefiningOp<ConstantOp>()) {
1429 if (baseOp.getValue() == 2) {
1430 Value constOne = ConstantOp::create(rewriter, loc, intType, 1);
1431 Value constZero = ConstantOp::create(rewriter, loc, intType, 0);
1432 Value shift = ShlOp::create(rewriter, loc, constOne, op.getRhs());
1433 Value isNegative = SltOp::create(rewriter, loc, op.getRhs(), constZero);
1434 auto condOp = rewriter.replaceOpWithNewOp<ConditionalOp>(
1435 op, op.getLhs().getType(), isNegative);
1436 Block *thenBlock = rewriter.createBlock(&condOp.getTrueRegion());
1437 rewriter.setInsertionPointToStart(thenBlock);
1438 YieldOp::create(rewriter, loc, constZero);
1439 Block *elseBlock = rewriter.createBlock(&condOp.getFalseRegion());
1440 rewriter.setInsertionPointToStart(elseBlock);
1441 YieldOp::create(rewriter, loc, shift);
1442 return success();
1443 }
1444 }
1445
1446 return failure();
1447}
1448
1449//===----------------------------------------------------------------------===//
1450// PowUOp
1451//===----------------------------------------------------------------------===//
1452
1453OpFoldResult PowUOp::fold(FoldAdaptor adaptor) {
1454 return powCommonFolding(getContext(), adaptor.getLhs(), adaptor.getRhs());
1455}
1456
1457LogicalResult PowUOp::canonicalize(PowUOp op, PatternRewriter &rewriter) {
1458 Location loc = op.getLoc();
1459 auto intType = cast<IntType>(op.getRhs().getType());
1460 if (auto baseOp = op.getLhs().getDefiningOp<ConstantOp>()) {
1461 if (baseOp.getValue() == 2) {
1462 Value constOne = ConstantOp::create(rewriter, loc, intType, 1);
1463 rewriter.replaceOpWithNewOp<ShlOp>(op, constOne, op.getRhs());
1464 return success();
1465 }
1466 }
1467
1468 return failure();
1469}
1470
1471//===----------------------------------------------------------------------===//
1472// SubOp
1473//===----------------------------------------------------------------------===//
1474
1475OpFoldResult SubOp::fold(FoldAdaptor adaptor) {
1476 if (auto intAttr = dyn_cast_or_null<FVIntegerAttr>(adaptor.getRhs()))
1477 if (intAttr.getValue().isZero())
1478 return getLhs();
1479
1480 return {};
1481}
1482
1483//===----------------------------------------------------------------------===//
1484// MulOp
1485//===----------------------------------------------------------------------===//
1486
1487OpFoldResult MulOp::fold(FoldAdaptor adaptor) {
1488 auto lhs = dyn_cast_or_null<FVIntegerAttr>(adaptor.getLhs());
1489 auto rhs = dyn_cast_or_null<FVIntegerAttr>(adaptor.getRhs());
1490 if (lhs && rhs)
1491 return FVIntegerAttr::get(getContext(), lhs.getValue() * rhs.getValue());
1492 return {};
1493}
1494
1495//===----------------------------------------------------------------------===//
1496// DivUOp
1497//===----------------------------------------------------------------------===//
1498
1499OpFoldResult DivUOp::fold(FoldAdaptor adaptor) {
1500 auto lhs = dyn_cast_or_null<FVIntegerAttr>(adaptor.getLhs());
1501 auto rhs = dyn_cast_or_null<FVIntegerAttr>(adaptor.getRhs());
1502 if (lhs && rhs)
1503 return FVIntegerAttr::get(getContext(),
1504 lhs.getValue().udiv(rhs.getValue()));
1505 return {};
1506}
1507
1508//===----------------------------------------------------------------------===//
1509// DivSOp
1510//===----------------------------------------------------------------------===//
1511
1512OpFoldResult DivSOp::fold(FoldAdaptor adaptor) {
1513 auto lhs = dyn_cast_or_null<FVIntegerAttr>(adaptor.getLhs());
1514 auto rhs = dyn_cast_or_null<FVIntegerAttr>(adaptor.getRhs());
1515 if (lhs && rhs)
1516 return FVIntegerAttr::get(getContext(),
1517 lhs.getValue().sdiv(rhs.getValue()));
1518 return {};
1519}
1520
1521//===----------------------------------------------------------------------===//
1522// Classes
1523//===----------------------------------------------------------------------===//
1524
1525LogicalResult ClassDeclOp::verify() {
1526 mlir::Region &body = getBody();
1527 if (body.empty())
1528 return mlir::success();
1529
1530 auto &block = body.front();
1531 for (mlir::Operation &op : block) {
1532
1533 // allow only property and method decls and terminator
1534 if (llvm::isa<circt::moore::ClassPropertyDeclOp,
1535 circt::moore::ClassMethodDeclOp>(&op))
1536 continue;
1537
1538 return emitOpError()
1539 << "body may only contain 'moore.class.propertydecl' operations";
1540 }
1541 return mlir::success();
1542}
1543
1544LogicalResult ClassNewOp::verify() {
1545 // The result is constrained to ClassHandleType in ODS, so this cast should be
1546 // safe.
1547 auto handleTy = cast<ClassHandleType>(getResult().getType());
1548 mlir::SymbolRefAttr classSym = handleTy.getClassSym();
1549 if (!classSym)
1550 return emitOpError("result type is missing a class symbol");
1551
1552 // Resolve the referenced symbol starting from the nearest symbol table.
1553 mlir::Operation *sym =
1554 mlir::SymbolTable::lookupNearestSymbolFrom(getOperation(), classSym);
1555 if (!sym)
1556 return emitOpError("referenced class symbol `")
1557 << classSym << "` was not found";
1558
1559 if (!llvm::isa<ClassDeclOp>(sym))
1560 return emitOpError("symbol `")
1561 << classSym << "` does not name a `moore.class.classdecl`";
1562
1563 return mlir::success();
1564}
1565
1566void ClassNewOp::getEffects(
1567 SmallVectorImpl<SideEffects::EffectInstance<MemoryEffects::Effect>>
1568 &effects) {
1569 // Always allocates heap memory.
1570 effects.emplace_back(MemoryEffects::Allocate::get());
1571}
1572
1573LogicalResult
1574ClassUpcastOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1575 // 1) Type checks.
1576 auto srcTy = dyn_cast<ClassHandleType>(getOperand().getType());
1577 if (!srcTy)
1578 return emitOpError() << "operand must be !moore.class<...>; got "
1579 << getOperand().getType();
1580
1581 auto dstTy = dyn_cast<ClassHandleType>(getResult().getType());
1582 if (!dstTy)
1583 return emitOpError() << "result must be !moore.class<...>; got "
1584 << getResult().getType();
1585
1586 if (srcTy == dstTy)
1587 return success();
1588
1589 auto *op = getOperation();
1590
1591 auto *srcDeclOp =
1592 symbolTable.lookupNearestSymbolFrom(op, srcTy.getClassSym());
1593 auto *dstDeclOp =
1594 symbolTable.lookupNearestSymbolFrom(op, dstTy.getClassSym());
1595 if (!srcDeclOp || !dstDeclOp)
1596 return emitOpError() << "failed to resolve class symbol(s): src="
1597 << srcTy.getClassSym()
1598 << ", dst=" << dstTy.getClassSym();
1599
1600 auto srcDecl = dyn_cast<ClassDeclOp>(srcDeclOp);
1601 auto dstDecl = dyn_cast<ClassDeclOp>(dstDeclOp);
1602 if (!srcDecl || !dstDecl)
1603 return emitOpError()
1604 << "symbol(s) do not name `moore.class.classdecl` ops: src="
1605 << srcTy.getClassSym() << ", dst=" << dstTy.getClassSym();
1606
1607 auto cur = srcDecl;
1608 while (cur) {
1609 if (cur == dstDecl)
1610 return success(); // legal upcast: dst is src or an ancestor
1611
1612 auto baseSym = cur.getBaseAttr();
1613 if (!baseSym)
1614 break;
1615
1616 auto *baseOp = symbolTable.lookupNearestSymbolFrom(op, baseSym);
1617 cur = llvm::dyn_cast_or_null<ClassDeclOp>(baseOp);
1618 }
1619
1620 return emitOpError() << "cannot upcast from " << srcTy.getClassSym() << " to "
1621 << dstTy.getClassSym()
1622 << " (destination is not a base class)";
1623}
1624
1625LogicalResult
1626ClassPropertyRefOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1627 // The operand is constrained to ClassHandleType in ODS; unwrap it.
1628 Type instTy = getInstance().getType();
1629 auto handleTy = dyn_cast<moore::ClassHandleType>(instTy);
1630 if (!handleTy)
1631 return emitOpError() << "instance must be a !moore.class<@C> value, got "
1632 << instTy;
1633
1634 // Extract the referenced class symbol from the handle type.
1635 SymbolRefAttr classSym = handleTy.getClassSym();
1636 if (!classSym)
1637 return emitOpError("instance type is missing a class symbol");
1638
1639 // Resolve the class symbol starting from the nearest symbol table.
1640 Operation *clsSym =
1641 symbolTable.lookupNearestSymbolFrom(getOperation(), classSym);
1642 if (!clsSym)
1643 return emitOpError("referenced class symbol `")
1644 << classSym << "` was not found";
1645 auto classDecl = dyn_cast<ClassDeclOp>(clsSym);
1646 if (!classDecl)
1647 return emitOpError("symbol `")
1648 << classSym << "` does not name a `moore.class.classdecl`";
1649
1650 // Look up the field symbol inside the class declaration's symbol table.
1651 FlatSymbolRefAttr fieldSym = getPropertyAttr();
1652 if (!fieldSym)
1653 return emitOpError("missing field symbol");
1654
1655 Operation *fldSym = symbolTable.lookupSymbolIn(classDecl, fieldSym.getAttr());
1656 if (!fldSym)
1657 return emitOpError("no field `") << fieldSym << "` in class " << classSym;
1658
1659 auto fieldDecl = dyn_cast<ClassPropertyDeclOp>(fldSym);
1660 if (!fieldDecl)
1661 return emitOpError("symbol `")
1662 << fieldSym << "` is not a `moore.class.propertydecl`";
1663
1664 // Result must be !moore.ref<T> where T matches the field's declared type.
1665 auto resRefTy = cast<RefType>(getPropertyRef().getType());
1666 if (!resRefTy)
1667 return emitOpError("result must be a !moore.ref<T>");
1668
1669 Type expectedElemTy = fieldDecl.getPropertyType();
1670 if (resRefTy.getNestedType() != expectedElemTy)
1671 return emitOpError("result element type (")
1672 << resRefTy.getNestedType() << ") does not match field type ("
1673 << expectedElemTy << ")";
1674
1675 return success();
1676}
1677
1678LogicalResult
1679VTableLoadMethodOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1680 Operation *op = getOperation();
1681
1682 auto object = getObject();
1683 auto implSym = object.getType().getClassSym();
1684
1685 // Check that classdecl of class handle exists
1686 Operation *implOp = symbolTable.lookupNearestSymbolFrom(op, implSym);
1687 if (!implOp)
1688 return emitOpError() << "implementing class " << implSym << " not found";
1689 auto implClass = cast<moore::ClassDeclOp>(implOp);
1690
1691 StringAttr methodName = getMethodSymAttr().getLeafReference();
1692 if (!methodName || methodName.getValue().empty())
1693 return emitOpError() << "empty method name";
1694
1695 moore::ClassDeclOp cursor = implClass;
1696 Operation *methodDeclOp = nullptr;
1697
1698 // Find method in class decl or parents' class decl
1699 while (cursor && !methodDeclOp) {
1700 methodDeclOp = symbolTable.lookupSymbolIn(cursor, methodName);
1701 if (methodDeclOp)
1702 break;
1703 SymbolRefAttr baseSym = cursor.getBaseAttr();
1704 if (!baseSym)
1705 break;
1706 Operation *baseOp = symbolTable.lookupNearestSymbolFrom(op, baseSym);
1707 cursor = baseOp ? cast<moore::ClassDeclOp>(baseOp) : moore::ClassDeclOp();
1708 }
1709
1710 if (!methodDeclOp)
1711 return emitOpError() << "no method `" << methodName << "` found in "
1712 << implClass.getSymName() << " or its bases";
1713
1714 // Make sure method decl is a ClassMethodDeclOp
1715 auto methodDecl = dyn_cast<moore::ClassMethodDeclOp>(methodDeclOp);
1716 if (!methodDecl)
1717 return emitOpError() << "`" << methodName
1718 << "` is not a method declaration";
1719
1720 // Make sure method signature matches
1721 auto resFnTy = cast<FunctionType>(getResult().getType());
1722 auto declFnTy = cast<FunctionType>(methodDecl.getFunctionType());
1723 if (resFnTy != declFnTy)
1724 return emitOpError() << "result type " << resFnTy
1725 << " does not match method erased ABI " << declFnTy;
1726
1727 return success();
1728}
1729
1730LogicalResult VTableOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1731 Operation *self = getOperation();
1732
1733 // sym_name's root must be a ClassDeclOp
1734 SymbolRefAttr name = getSymNameAttr();
1735 if (!name)
1736 return emitOpError("requires 'sym_name' SymbolRefAttr");
1737
1738 // Root symbol must resolve (from the nearest symbol table) to a ClassDeclOp.
1739 Operation *rootDef = symbolTable.lookupNearestSymbolFrom(
1740 self, SymbolRefAttr::get(name.getRootReference()));
1741 if (!rootDef)
1742 return emitOpError() << "cannot resolve root class symbol '"
1743 << name.getRootReference() << "' for sym_name "
1744 << name;
1745
1746 if (!isa<ClassDeclOp>(rootDef))
1747 return emitOpError()
1748 << "root of sym_name must name a 'moore.class.classdecl', got "
1749 << name;
1750
1751 // All good.
1752 return success();
1753}
1754
1755LogicalResult VTableOp::verifyRegions() {
1756 // Ensure only allowed ops appear inside.
1757 for (Operation &op : getBody().front()) {
1758 if (!isa<VTableOp, VTableEntryOp>(op))
1759 return emitOpError(
1760 "body may only contain 'moore.vtable' or 'moore.vtable_entry' ops");
1761 }
1762 return mlir::success();
1763}
1764
1765LogicalResult
1766VTableEntryOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1767 Operation *self = getOperation();
1768
1769 // 'target' must exist and resolve from the top-level symbol table of a func
1770 // op
1771 SymbolRefAttr target = getTargetAttr();
1772 func::FuncOp def =
1773 symbolTable.lookupNearestSymbolFrom<func::FuncOp>(self, target);
1774 if (!def)
1775 return emitOpError()
1776 << "cannot resolve target symbol to a function operation " << target;
1777
1778 // VTableEntries may only exist in VTables.
1779 if (!isa<VTableOp>(self->getParentOp()))
1780 return emitOpError("must be nested directly inside a 'moore.vtable' op");
1781
1782 Operation *currentOp = self;
1783 VTableOp currentVTable;
1784 bool defined = false;
1785
1786 // Walk up the VTable tree and check whether the corresponding classDeclOp
1787 // declares a method with the same implementation. Further checks all the way
1788 // up the tree if another classdeclop overrides the implementation.
1789 // The entry is correct iff the impl matches the most derived classdeclop's
1790 // methoddeclop implementing the virtual method.
1791 while (auto parentOp = dyn_cast<VTableOp>(currentOp->getParentOp())) {
1792 currentOp = parentOp;
1793 currentVTable = cast<VTableOp>(currentOp);
1794
1795 auto classSymName = currentVTable.getSymName();
1796 ClassDeclOp parentClassDecl =
1797 symbolTable.lookupNearestSymbolFrom<ClassDeclOp>(
1798 parentOp, classSymName.getRootReference());
1799 assert(parentClassDecl && "VTableOp must point to a classdeclop");
1800
1801 for (auto method : parentClassDecl.getBody().getOps<ClassMethodDeclOp>()) {
1802 // A virtual interface declaration. Ignore.
1803 if (!method.getImpl())
1804 continue;
1805
1806 // A matching definition.
1807 if (method.getSymName() == getName() && method.getImplAttr() == target)
1808 defined = true;
1809
1810 // All definitions of the same method up the tree must be the same as the
1811 // current definition, there is no shadowing.
1812 // Hence, if we encounter a methoddeclop that has the same name but a
1813 // different implementation that means this vtableentry should point to
1814 // the op's implementation - that's an error.
1815 else if (method.getSymName() == getName() &&
1816 method.getImplAttr() != target && defined)
1817 return emitOpError() << "Target " << target
1818 << " should be overridden by " << classSymName;
1819 }
1820 }
1821 if (!defined)
1822 return emitOpError()
1823 << "Parent class does not point to any implementation!";
1824
1825 return success();
1826}
1827
1828LogicalResult DynQueueExtractOp::verify() {
1829 auto elementType = cast<QueueType>(getInput().getType()).getElementType();
1830
1831 // If the result type indicates we are extracting a single element,
1832 // the upper/lower indexes should be the same register.
1833 if (getResult().getType() == elementType && getLowerIdx() != getUpperIdx()) {
1834 return failure();
1835 }
1836
1837 return success();
1838}
1839
1840LogicalResult QueueResizeOp::verify() {
1841 if (cast<QueueType>(getInput().getType()).getElementType() !=
1842 cast<QueueType>(getResult().getType()).getElementType())
1843 return failure();
1844
1845 return success();
1846}
1847
1848LogicalResult QueueFromUnpackedArrayOp::verify() {
1849 // Verify the source and result have the same element type
1850 auto queueElementType =
1851 cast<QueueType>(getResult().getType()).getElementType();
1852
1853 auto arrayElementType =
1854 cast<UnpackedArrayType>(getInput().getType()).getElementType();
1855
1856 if (queueElementType != arrayElementType) {
1857 return emitOpError()
1858 << "Queue element type doesn't match unpacked array element type";
1859 }
1860
1861 return success();
1862}
1863
1864LogicalResult QueueConcatOp::verify() {
1865 // Verify the element types of all concatenated queues equal that of the
1866 // result queue.
1867 // We do not require the queue bounds to match.
1868 auto resultElType = cast<QueueType>(getResult().getType()).getElementType();
1869
1870 for (Value input : getInputs()) {
1871 auto inpElType = cast<QueueType>(input.getType()).getElementType();
1872 if (inpElType != resultElType) {
1873 return emitOpError() << "Queue element type " << inpElType
1874 << " doesn't match result element type "
1875 << resultElType;
1876 }
1877 }
1878
1879 return success();
1880}
1881
1882//===----------------------------------------------------------------------===//
1883// TableGen generated logic.
1884//===----------------------------------------------------------------------===//
1885
1886// Provide the autogenerated implementation guts for the Op classes.
1887#define GET_OP_CLASSES
1888#include "circt/Dialect/Moore/Moore.cpp.inc"
1889#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:216
static OpFoldResult powCommonFolding(MLIRContext *ctxt, Attribute lhs, Attribute rhs)
static ArrayRef< StructLikeMember > getStructMembers(Type type)
Definition MooreOps.cpp:877
static std::optional< uint32_t > getStructFieldIndex(Type type, StringAttr name)
Definition MooreOps.cpp:868
static UnpackedType getStructFieldType(Type type, StringAttr name)
Definition MooreOps.cpp:886
static std::pair< unsigned, UnpackedType > getArrayElements(Type type)
Definition MooreOps.cpp:835
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:55
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
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:183