12#include "mlir/Dialect/Func/IR/FuncOps.h"
13#include "mlir/IR/Builders.h"
14#include "mlir/IR/OpImplementation.h"
15#include "mlir/IR/PatternMatch.h"
16#include "mlir/IR/SymbolTable.h"
17#include "mlir/Interfaces/FunctionImplementation.h"
18#include "mlir/Interfaces/SideEffectInterfaces.h"
19#include "llvm/ADT/SmallPtrSet.h"
20#include "llvm/ADT/TypeSwitch.h"
31 TypeRange expectedTypeList,
32 TypeRange actualTypeList,
33 StringRef elementName) {
34 if (expectedTypeList.size() != actualTypeList.size())
35 return op->emitOpError(
"incorrect number of ")
36 << elementName <<
"s: expected " << expectedTypeList.size()
37 <<
", but got " << actualTypeList.size();
39 for (
unsigned i = 0, e = expectedTypeList.size(); i != e; ++i) {
40 if (expectedTypeList[i] != actualTypeList[i]) {
41 auto diag = op->emitOpError(elementName)
42 <<
" type mismatch: " << elementName <<
" #" << i;
43 diag.attachNote() <<
"expected type: " << expectedTypeList[i];
44 diag.attachNote() <<
" actual type: " << actualTypeList[i];
54 SymbolTableCollection &symbolTable) {
56 auto arcName = op->getAttrOfType<FlatSymbolRefAttr>(
"arc");
59 assert(arcName &&
"FlatSymbolRefAttr called 'arc' missing");
60 DefineOp
arc = symbolTable.lookupNearestSymbolFrom<DefineOp>(op, arcName);
62 return op->emitOpError() <<
"`" << arcName.getValue()
63 <<
"` does not reference a valid `arc.define`";
66 auto type =
arc.getFunctionType();
79 return llvm::isa<arc::ModelOp, hw::HWModuleLike>(moduleOp);
85 Operation *pointing, StringAttr symbol) {
86 Operation *moduleOp = symbolTable.lookupNearestSymbolFrom(pointing, symbol);
88 pointing->emitOpError(
"model not found");
93 pointing->emitOpError(
"model symbol does not point to a supported model "
94 "operation, points to ")
95 << moduleOp->getName() <<
" instead";
103 StringRef portName) {
104 auto findRightPort = [&](
auto ports) -> std::optional<hw::ModulePort> {
107 if (port == ports.end())
112 return TypeSwitch<Operation *, std::optional<hw::ModulePort>>(moduleOp)
114 [&](arc::ModelOp modelOp) -> std::optional<hw::ModulePort> {
115 return findRightPort(modelOp.getIo().getPorts());
117 .Case<hw::HWModuleLike>(
118 [&](hw::HWModuleLike moduleLike) -> std::optional<hw::ModulePort> {
119 return findRightPort(moduleLike.getPortList());
121 .Default([](Operation *) {
return std::nullopt; });
128ParseResult DefineOp::parse(OpAsmParser &parser, OperationState &result) {
130 [](Builder &builder, ArrayRef<Type> argTypes, ArrayRef<Type> results,
131 function_interface_impl::VariadicFlag,
132 std::string &) {
return builder.getFunctionType(argTypes, results); };
134 return function_interface_impl::parseFunctionOp(
135 parser, result,
false,
136 getFunctionTypeAttrName(result.name), buildFuncType,
137 getArgAttrsAttrName(result.name), getResAttrsAttrName(result.name));
140void DefineOp::print(OpAsmPrinter &p) {
141 function_interface_impl::printFunctionOp(
142 p, *
this,
false,
"function_type", getArgAttrsAttrName(),
143 getResAttrsAttrName());
146LogicalResult DefineOp::verifyRegions() {
153 if (isMemoryEffectFree(&op))
162 auto diag = mlir::emitError(
getLoc(),
"body contains non-pure operation");
163 diag.attachNote(op.getLoc()).append(
"first non-pure operation here: ");
169bool DefineOp::isPassthrough() {
170 if (getNumArguments() != getNumResults())
174 llvm::zip(getArguments(),
getBodyBlock().getTerminator()->getOperands()),
175 [](
const auto &argAndRes) {
176 return std::get<0>(argAndRes) == std::get<1>(argAndRes);
184LogicalResult OutputOp::verify() {
185 auto *parent = (*this)->getParentOp();
186 TypeRange expectedTypes = parent->getResultTypes();
187 if (
auto defOp = dyn_cast<DefineOp>(parent))
188 expectedTypes = defOp.getResultTypes();
190 TypeRange actualTypes = getOperands().getTypes();
198LogicalResult StateOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
200 getResults().getTypes(), symbolTable);
203LogicalResult StateOp::verify() {
204 if (getLatency() < 1)
205 return emitOpError(
"latency must be a positive integer");
208 return emitOpError(
"requires a clock");
218StateWriteOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
219 if (!getTraceTapModel().has_value())
222 auto modelOp = symbolTable.lookupNearestSymbolFrom<ModelOp>(
223 getOperation(), getTraceTapModelAttr());
225 return emitOpError() <<
"`" << getTraceTapModelAttr()
226 <<
"` does not reference a valid `arc.model`";
227 if (!modelOp.getTraceTaps())
228 return emitOpError() <<
"referenced model has no trace metadata";
229 if (modelOp.getTraceTapsAttr().size() <= *getTraceTapIndex())
230 return emitOpError() <<
"tap index exceeds model's tap array";
232 cast<TraceTapAttr>(modelOp.getTraceTapsAttr()[*getTraceTapIndex()]);
233 if (tapAttr.getSigType().getValue() != getValue().getType())
234 return emitOpError() <<
"incorrect signal type in referenced tap attribute";
239LogicalResult StateWriteOp::verify() {
240 if (getTraceTapIndex().has_value() == getTraceTapModel().has_value())
242 return emitOpError() <<
"must specify both a trace tap model and index";
249LogicalResult CallOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
251 getResults().getTypes(), symbolTable);
254bool CallOp::isClocked() {
return false; }
256Value CallOp::getClock() {
return Value{}; }
258void CallOp::eraseClock() {}
260uint32_t CallOp::getLatency() {
return 0; }
266SmallVector<Type> MemoryWritePortOp::getArcResultTypes() {
267 auto memType = cast<MemoryType>(getMemory().getType());
268 SmallVector<Type> resultTypes{memType.getAddressType(),
269 memType.getWordType()};
271 resultTypes.push_back(IntegerType::get(getContext(), 1));
273 resultTypes.push_back(memType.getWordType());
278MemoryWritePortOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
283LogicalResult MemoryWritePortOp::verify() {
284 if (getLatency() < 1)
285 return emitOpError(
"latency must be at least 1");
288 return emitOpError(
"requires a clock");
298 SmallString<32> buf(
"in_");
300 setNameFn(getState(), buf);
308 SmallString<32> buf(
"out_");
310 setNameFn(getState(), buf);
317LogicalResult ModelOp::verify() {
319 return emitOpError(
"must have exactly one argument");
320 if (
auto type =
getBodyBlock().getArgument(0).getType();
321 !isa<StorageType>(type))
322 return emitOpError(
"argument must be of storage type");
325 return emitOpError(
"inout ports are not supported");
329LogicalResult ModelOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
330 auto fnAttrs = std::array{getInitialFnAttr(), getFinalFnAttr()};
331 auto nouns = std::array{
"initializer",
"finalizer"};
332 for (
auto [fnAttr, noun] :
llvm::zip(fnAttrs, nouns)) {
335 auto fn = symbolTable.lookupNearestSymbolFrom<func::FuncOp>(*
this, fnAttr);
337 return emitOpError() << noun <<
" '" << fnAttr.getValue()
338 <<
"' does not reference a valid function";
339 if (!llvm::equal(fn.getArgumentTypes(), getBody().getArgumentTypes())) {
340 auto diag = emitError() << noun <<
" '" << fnAttr.getValue()
341 <<
"' arguments must match arguments of model";
342 diag.attachNote(fn.getLoc()) << noun <<
" declared here:";
353LogicalResult LutOp::verify() {
354 Location firstSideEffectOpLoc = UnknownLoc::get(getContext());
355 const WalkResult result = getBody().walk([&](Operation *op) {
356 if (
auto memOp = dyn_cast<MemoryEffectOpInterface>(op)) {
357 SmallVector<SideEffects::EffectInstance<MemoryEffects::Effect>> effects;
358 memOp.getEffects(effects);
360 if (!effects.empty()) {
361 firstSideEffectOpLoc = memOp->getLoc();
362 return WalkResult::interrupt();
366 return WalkResult::advance();
369 if (result.wasInterrupted())
370 return emitOpError(
"no operations with side-effects allowed inside a LUT")
371 .attachNote(firstSideEffectOpLoc)
372 <<
"first operation with side-effects here";
381LogicalResult VectorizeOp::verify() {
382 if (getInputs().
empty())
383 return emitOpError(
"there has to be at least one input vector");
385 if (!llvm::all_equal(llvm::map_range(
386 getInputs(), [](OperandRange range) {
return range.size(); })))
387 return emitOpError(
"all input vectors must have the same size");
389 for (OperandRange range : getInputs()) {
390 if (!llvm::all_equal(range.getTypes()))
391 return emitOpError(
"all input vector lane types must match");
394 return emitOpError(
"input vector must have at least one element");
397 if (getResults().
empty())
398 return emitOpError(
"must have at least one result");
400 if (!llvm::all_equal(getResults().getTypes()))
401 return emitOpError(
"all result types must match");
403 if (getResults().size() != getInputs().front().size())
404 return emitOpError(
"number results must match input vector size");
410 if (isa<VectorType>(base))
413 if (
auto vectorTy = dyn_cast<VectorType>(vectorized)) {
414 if (vectorTy.getElementType() != base)
417 return vectorTy.getDimSize(0);
420 if (vectorized.getIntOrFloatBitWidth() < base.getIntOrFloatBitWidth())
423 if (vectorized.getIntOrFloatBitWidth() % base.getIntOrFloatBitWidth() == 0)
424 return vectorized.getIntOrFloatBitWidth() / base.getIntOrFloatBitWidth();
429LogicalResult VectorizeOp::verifyRegions() {
430 auto returnOp = cast<VectorizeReturnOp>(getBody().front().getTerminator());
431 TypeRange bodyArgTypes = getBody().front().getArgumentTypes();
433 if (bodyArgTypes.size() != getInputs().size())
435 "number of block arguments must match number of input vectors");
438 if (returnOp.getValue().getType() == getResultTypes().front()) {
439 for (
auto [i, argTy] :
llvm::enumerate(bodyArgTypes))
440 if (argTy != getInputs()[i].getTypes().front())
441 return emitOpError(
"if terminator type matches result type the "
442 "argument types must match the input types");
449 getResultTypes().front());
451 for (
auto [i, argTy] :
llvm::enumerate(bodyArgTypes)) {
452 Type inputTy = getInputs()[i].getTypes().front();
454 if (failed(argWidth))
455 return emitOpError(
"block argument must be a scalar variant of the "
456 "vectorized operand");
458 if (*argWidth != width)
459 return emitOpError(
"input and output vector width must match");
467 returnOp.getValue().getType());
469 for (
auto [i, argTy] :
llvm::enumerate(bodyArgTypes)) {
470 Type inputTy = getInputs()[i].getTypes().front();
472 if (failed(argWidth))
474 "block argument must be a vectorized variant of the operand");
476 if (*argWidth != width)
477 return emitOpError(
"input and output vector width must match");
479 if (getInputs()[i].size() > 1 && argWidth != getInputs()[i].size())
481 "when boundary not vectorized the number of vector element "
482 "operands must match the width of the vectorized body");
488 return returnOp.emitOpError(
489 "operand type must match parent op's result value or be a vectorized or "
490 "non-vectorized variant of it");
493bool VectorizeOp::isBoundaryVectorized() {
494 return getInputs().front().size() == 1;
496bool VectorizeOp::isBodyVectorized() {
497 auto returnOp = cast<VectorizeReturnOp>(getBody().front().getTerminator());
498 if (isBoundaryVectorized() &&
499 returnOp.getValue().getType() == getResultTypes().front())
503 returnOp.getValue().getType());
514void SimInstantiateOp::print(OpAsmPrinter &p) {
515 BlockArgument modelArg = getBody().getArgument(0);
516 auto modelType = cast<SimModelInstanceType>(modelArg.getType());
518 p <<
" " << modelType.getModel() <<
" as ";
519 p.printRegionArgument(modelArg, {},
true);
521 if (getRuntimeModel() || getRuntimeArgs()) {
523 if (getRuntimeModel())
524 p << getRuntimeModelAttr();
526 if (getRuntimeArgs())
527 p << getRuntimeArgsAttr();
531 p.printOptionalAttrDictWithKeyword(
532 getOperation()->getAttrs(),
533 {getRuntimeModelAttrName(), getRuntimeArgsAttrName()});
537 p.printRegion(getBody(),
false);
540ParseResult SimInstantiateOp::parse(OpAsmParser &parser,
541 OperationState &result) {
542 StringAttr modelName;
543 if (failed(parser.parseSymbolName(modelName)))
546 if (failed(parser.parseKeyword(
"as")))
549 OpAsmParser::Argument modelArg;
550 if (failed(parser.parseArgument(modelArg,
false,
false)))
553 if (succeeded(parser.parseOptionalKeyword(
"runtime"))) {
554 StringAttr runtimeSym;
555 StringAttr runtimeArgs;
556 auto symOpt = parser.parseOptionalSymbolName(runtimeSym);
557 if (parser.parseLParen())
559 auto nameOpt = parser.parseOptionalAttribute(runtimeArgs);
560 if (parser.parseRParen())
562 if (succeeded(symOpt))
564 SimInstantiateOp::getRuntimeModelAttrName(result.name),
565 FlatSymbolRefAttr::get(runtimeSym));
566 if (nameOpt.has_value())
567 result.addAttribute(SimInstantiateOp::getRuntimeArgsAttrName(result.name),
571 if (failed(parser.parseOptionalAttrDictWithKeyword(result.attributes)))
574 MLIRContext *ctxt = result.getContext();
576 SimModelInstanceType::get(ctxt, FlatSymbolRefAttr::get(ctxt, modelName));
578 std::unique_ptr<Region> body = std::make_unique<Region>();
579 if (failed(parser.parseRegion(*body, {modelArg})))
582 result.addRegion(std::move(body));
586LogicalResult SimInstantiateOp::verifyRegions() {
587 Region &body = getBody();
588 if (body.getNumArguments() != 1)
589 return emitError(
"entry block of body region must have the model instance "
590 "as a single argument");
591 if (!llvm::isa<SimModelInstanceType>(body.getArgument(0).getType()))
592 return emitError(
"entry block argument type is not a model instance");
597SimInstantiateOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
600 symbolTable, getOperation(),
601 llvm::cast<SimModelInstanceType>(getBody().getArgument(0).getType())
607 if (getRuntimeModel().has_value()) {
608 Operation *runtimeModelOp = symbolTable.lookupNearestSymbolFrom(
609 getOperation(), getRuntimeModelAttr());
610 if (!runtimeModelOp) {
611 emitOpError(
"runtime model not found");
613 }
else if (!isa<RuntimeModelOp>(runtimeModelOp)) {
614 emitOpError(
"referenced runtime model is not a RuntimeModelOp");
619 return success(!failed);
627SimSetInputOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
629 symbolTable, getOperation(),
630 llvm::cast<SimModelInstanceType>(getInstance().getType())
636 std::optional<hw::ModulePort> port =
getModulePort(moduleOp, getInput());
638 return emitOpError(
"port not found on model");
640 if (port->dir != hw::ModulePort::Direction::Input &&
641 port->dir != hw::ModulePort::Direction::InOut)
642 return emitOpError(
"port is not an input port");
644 if (port->type != getValue().getType())
646 "mismatched types between value and model port, port expects ")
657SimGetPortOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
659 symbolTable, getOperation(),
660 llvm::cast<SimModelInstanceType>(getInstance().getType())
668 return emitOpError(
"port not found on model");
670 if (port->type != getValue().getType())
672 "mismatched types between value and model port, port expects ")
682LogicalResult SimStepOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
684 symbolTable, getOperation(),
685 llvm::cast<SimModelInstanceType>(getInstance().getType())
699SimSetTimeOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
701 symbolTable, getOperation(),
702 llvm::cast<SimModelInstanceType>(getInstance().getType())
718 FlatSymbolRefAttr callee,
721 SymbolTableCollection &symTable) {
723 symTable.lookupNearestSymbolFrom<CoroutineDefineOp>(op, callee);
725 return op->emitOpError() <<
"`" << callee.getValue()
726 <<
"` does not reference a valid "
727 "`arc.coroutine.define`";
729 auto fnType = defineOp.getFunctionType();
739ParseResult CoroutineDefineOp::parse(OpAsmParser &parser,
740 OperationState &result) {
742 [](Builder &builder, ArrayRef<Type> argTypes, ArrayRef<Type> results,
743 function_interface_impl::VariadicFlag,
744 std::string &) {
return builder.getFunctionType(argTypes, results); };
746 return function_interface_impl::parseFunctionOp(
747 parser, result,
false,
748 getFunctionTypeAttrName(result.name), buildFuncType,
749 getArgAttrsAttrName(result.name), getResAttrsAttrName(result.name));
752void CoroutineDefineOp::print(OpAsmPrinter &p) {
753 function_interface_impl::printFunctionOp(
754 p, *
this,
false,
"function_type", getArgAttrsAttrName(),
755 getResAttrsAttrName());
763CoroutineCallOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
768 auto callee = (*this)->getAttrOfType<FlatSymbolRefAttr>(
"callee");
770 getResults().getTypes(), symbolTable);
782CoroutineInstanceOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
783 auto callee = (*this)->getAttrOfType<FlatSymbolRefAttr>(
"callee");
785 symbolTable.lookupNearestSymbolFrom<CoroutineDefineOp>(*
this, callee);
787 return emitOpError() <<
"`" << callee.getValue()
788 <<
"` does not reference a valid "
789 "`arc.coroutine.define`";
791 auto fnType = defineOp.getFunctionType();
792 auto fnResults = fnType.getResults();
793 if (fnResults.size() < 2 || !fnResults.back().isInteger(64))
794 return emitOpError() <<
"referenced coroutine `" << callee.getValue()
795 <<
"` must produce an `i64` wakeup time as its "
799 auto maskType = dyn_cast<IntegerType>(fnResults[fnResults.size() - 2]);
800 if (!maskType || maskType.getWidth() != fnType.getNumInputs())
802 <<
"referenced coroutine `" << callee.getValue()
803 <<
"` must produce an observe bitmask with one bit per "
805 << fnType.getNumInputs() <<
"`) as its second-to-last result";
808 getArgs().getTypes(),
"operand")))
811 getResults().getTypes(),
"result")))
825 TypeRange yieldOperands) {
826 auto parent = op->getParentOfType<CoroutineDefineOp>();
831LogicalResult CoroutineYieldOp::verify() {
840 auto parent = (*this)->getParentOfType<CoroutineDefineOp>();
841 TypeRange coroutineArgTypes = parent.getArgumentTypes();
842 TypeRange destArgTypes = getDest()->getArgumentTypes();
843 if (destArgTypes.size() >= coroutineArgTypes.size())
845 *
this, coroutineArgTypes,
846 destArgTypes.take_front(coroutineArgTypes.size()),
847 "destination resume argument")))
858SuccessorOperands CoroutineYieldOp::getSuccessorOperands(
unsigned index) {
859 assert(index == 0 &&
"invalid successor index");
860 auto parent = (*this)->getParentOfType<CoroutineDefineOp>();
861 return SuccessorOperands(parent.getArgumentTypes().size(),
862 getDestOperandsMutable());
865LogicalResult CoroutineReturnOp::verify() {
869LogicalResult CoroutineHaltOp::verify() {
877LogicalResult ExecuteOp::verifyRegions() {
879 getBody().getArgumentTypes(),
"input");
886LogicalResult ArrayRefAllocOp::verify() {
887 if (
auto init = getInit()) {
888 if (init->size() != getType().getNumElements()) {
889 return emitOpError(
"init size does not match array size; init had size ")
890 << init->size() <<
" but array has size "
891 << getType().getNumElements();
894 if (
auto intTy = dyn_cast<IntegerType>(getType().getElementType())) {
895 unsigned elemBitwidth = intTy.getWidth();
896 for (Attribute attr : *init) {
897 auto intAttr = dyn_cast<IntegerAttr>(attr);
898 if (!intAttr || intAttr.getValue().getBitWidth() != elemBitwidth) {
899 return emitOpError(
"expected element to be of type ")
900 << getType().getElementType();
908#include "circt/Dialect/Arc/ArcInterfaces.cpp.inc"
910#define GET_OP_CLASSES
911#include "circt/Dialect/Arc/Arc.cpp.inc"
static FailureOr< unsigned > getVectorWidth(Type base, Type vectorized)
static std::optional< hw::ModulePort > getModulePort(Operation *moduleOp, StringRef portName)
static bool isSupportedModuleOp(Operation *moduleOp)
static LogicalResult verifyArcSymbolUse(Operation *op, TypeRange inputs, TypeRange results, SymbolTableCollection &symbolTable)
static LogicalResult verifyTypeListEquivalence(Operation *op, TypeRange expectedTypeList, TypeRange actualTypeList, StringRef elementName)
static LogicalResult verifyCoroutineCallTypes(Operation *op, FlatSymbolRefAttr callee, TypeRange operands, TypeRange results, SymbolTableCollection &symTable)
Resolve the callee symbol to a CoroutineDefineOp and verify that the given operand and result types m...
static LogicalResult verifyCoroutineTerminator(Operation *op, TypeRange yieldOperands)
static Operation * getSupportedModuleOp(SymbolTableCollection &symbolTable, Operation *pointing, StringAttr symbol)
Fetches the operation pointed to by pointing with name symbol, checking that it is a supported model ...
assert(baseType &&"element must be base type")
static PortInfo getPort(ModuleTy &mod, size_t idx)
static Location getLoc(DefSlot slot)
static Block * getBodyBlock(FModuleLike mod)
static InstancePath empty
Direction
The direction of a Component or Cell port.
StringAttr getName(ArrayAttr names, size_t idx)
Return the name at the specified index of the ArrayAttr or null if it cannot be determined.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
function_ref< void(Value, StringRef)> OpAsmSetValueNameFn