10 #include "mlir/IR/Builders.h"
11 #include "mlir/IR/OpImplementation.h"
12 #include "mlir/IR/PatternMatch.h"
13 #include "mlir/IR/SymbolTable.h"
14 #include "mlir/Interfaces/FunctionImplementation.h"
15 #include "mlir/Interfaces/SideEffectInterfaces.h"
17 using namespace circt;
26 TypeRange expectedTypeList,
27 TypeRange actualTypeList,
28 StringRef elementName) {
29 if (expectedTypeList.size() != actualTypeList.size())
30 return op->emitOpError(
"incorrect number of ")
31 << elementName <<
"s: expected " << expectedTypeList.size()
32 <<
", but got " << actualTypeList.size();
34 for (
unsigned i = 0, e = expectedTypeList.size(); i != e; ++i) {
35 if (expectedTypeList[i] != actualTypeList[i]) {
36 auto diag = op->emitOpError(elementName)
37 <<
" type mismatch: " << elementName <<
" #" << i;
38 diag.attachNote() <<
"expected type: " << expectedTypeList[i];
39 diag.attachNote() <<
" actual type: " << actualTypeList[i];
49 SymbolTableCollection &symbolTable) {
51 auto arcName = op->getAttrOfType<FlatSymbolRefAttr>(
"arc");
54 assert(arcName &&
"FlatSymbolRefAttr called 'arc' missing");
55 DefineOp arc = symbolTable.lookupNearestSymbolFrom<DefineOp>(op, arcName);
57 return op->emitOpError() <<
"`" << arcName.getValue()
58 <<
"` does not reference a valid `arc.define`";
61 auto type = arc.getFunctionType();
77 ParseResult DefineOp::parse(OpAsmParser &parser, OperationState &result) {
79 [](Builder &
builder, ArrayRef<Type> argTypes, ArrayRef<Type> results,
80 function_interface_impl::VariadicFlag,
81 std::string &) {
return builder.getFunctionType(argTypes, results); };
83 return function_interface_impl::parseFunctionOp(
84 parser, result,
false,
85 getFunctionTypeAttrName(result.name), buildFuncType,
86 getArgAttrsAttrName(result.name), getResAttrsAttrName(result.name));
89 void DefineOp::print(OpAsmPrinter &p) {
90 function_interface_impl::printFunctionOp(
91 p, *
this,
false,
"function_type", getArgAttrsAttrName(),
92 getResAttrsAttrName());
95 LogicalResult DefineOp::verifyRegions() {
101 for (
auto &op : getBodyBlock()) {
102 if (isMemoryEffectFree(&op))
111 auto diag = mlir::emitError(getLoc(),
"body contains non-pure operation");
112 diag.attachNote(op.getLoc()).append(
"first non-pure operation here: ");
118 bool DefineOp::isPassthrough() {
119 if (getNumArguments() != getNumResults())
123 llvm::zip(getArguments(), getBodyBlock().getTerminator()->getOperands()),
124 [](
const auto &argAndRes) {
125 return std::get<0>(argAndRes) == std::get<1>(argAndRes);
133 LogicalResult OutputOp::verify() {
134 auto *parent = (*this)->getParentOp();
135 TypeRange expectedTypes = parent->getResultTypes();
136 if (
auto defOp = dyn_cast<DefineOp>(parent))
137 expectedTypes = defOp.getResultTypes();
139 TypeRange actualTypes = getOperands().getTypes();
147 LogicalResult StateOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
149 getResults().getTypes(), symbolTable);
152 LogicalResult StateOp::verify() {
153 if (getLatency() > 0 && !getOperation()->getParentOfType<ClockDomainOp>() &&
156 "with non-zero latency outside a clock domain requires a clock");
158 if (getLatency() == 0) {
160 return emitOpError(
"with zero latency cannot have a clock");
162 return emitOpError(
"with zero latency cannot have an enable");
164 return emitOpError(
"with zero latency cannot have a reset");
167 if (getOperation()->getParentOfType<ClockDomainOp>() && getClock())
168 return emitOpError(
"inside a clock domain cannot have a clock");
173 bool StateOp::isClocked() {
return getLatency() > 0; }
179 LogicalResult CallOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
181 getResults().getTypes(), symbolTable);
188 SmallVector<Type> MemoryWritePortOp::getArcResultTypes() {
189 auto memType = cast<MemoryType>(getMemory().getType());
190 SmallVector<Type> resultTypes{memType.getAddressType(),
191 memType.getWordType()};
195 resultTypes.push_back(memType.getWordType());
200 MemoryWritePortOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
205 LogicalResult MemoryWritePortOp::verify() {
206 if (getLatency() < 1)
207 return emitOpError(
"latency must be at least 1");
209 if (!getOperation()->getParentOfType<ClockDomainOp>() && !getClock())
210 return emitOpError(
"outside a clock domain requires a clock");
212 if (getOperation()->getParentOfType<ClockDomainOp>() && getClock())
213 return emitOpError(
"inside a clock domain cannot have a clock");
222 LogicalResult ClockDomainOp::verifyRegions() {
224 getInputs().getTypes(),
"input");
232 SmallString<32> buf(
"in_");
234 setNameFn(getState(), buf);
242 SmallString<32> buf(
"out_");
244 setNameFn(getState(), buf);
251 LogicalResult ModelOp::verify() {
252 if (getBodyBlock().getArguments().size() != 1)
253 return emitOpError(
"must have exactly one argument");
254 if (
auto type = getBodyBlock().getArgument(0).getType();
255 !isa<StorageType>(type))
256 return emitOpError(
"argument must be of storage type");
264 LogicalResult LutOp::verify() {
266 const WalkResult result = getBody().walk([&](Operation *op) {
267 if (
auto memOp = dyn_cast<MemoryEffectOpInterface>(op)) {
268 SmallVector<SideEffects::EffectInstance<MemoryEffects::Effect>> effects;
269 memOp.getEffects(effects);
271 if (!effects.empty()) {
272 firstSideEffectOpLoc = memOp->getLoc();
273 return WalkResult::interrupt();
277 return WalkResult::advance();
280 if (result.wasInterrupted())
281 return emitOpError(
"no operations with side-effects allowed inside a LUT")
282 .attachNote(firstSideEffectOpLoc)
283 <<
"first operation with side-effects here";
292 LogicalResult VectorizeOp::verify() {
293 if (getInputs().
empty())
294 return emitOpError(
"there has to be at least one input vector");
296 if (!llvm::all_equal(llvm::map_range(
297 getInputs(), [](OperandRange range) {
return range.size(); })))
298 return emitOpError(
"all input vectors must have the same size");
300 for (OperandRange range : getInputs()) {
301 if (!llvm::all_equal(range.getTypes()))
302 return emitOpError(
"all input vector lane types must match");
305 return emitOpError(
"input vector must have at least one element");
308 if (getInputs().front().size() > 1 &&
309 !isa<IntegerType>(getInputs().front().front().getType()))
310 return emitOpError(
"input vector element type must be a signless integer");
312 if (getResults().
empty())
313 return emitOpError(
"must have at least one result");
315 if (!llvm::all_equal(getResults().getTypes()))
316 return emitOpError(
"all result types must match");
318 if (getResults().size() != getInputs().front().size())
319 return emitOpError(
"number results must match input vector size");
321 if (getResults().size() > 1 &&
322 !isa<IntegerType>(getResults().front().getType()))
324 "may only return a vector type if boundary is already vectorized");
330 if (isa<VectorType>(base))
333 if (
auto vectorTy = dyn_cast<VectorType>(vectorized)) {
334 if (vectorTy.getElementType() != base)
337 return vectorTy.getDimSize(0);
340 if (vectorized.getIntOrFloatBitWidth() < base.getIntOrFloatBitWidth())
343 if (vectorized.getIntOrFloatBitWidth() % base.getIntOrFloatBitWidth() == 0)
344 return vectorized.getIntOrFloatBitWidth() / base.getIntOrFloatBitWidth();
349 LogicalResult VectorizeOp::verifyRegions() {
350 auto returnOp = cast<VectorizeReturnOp>(getBody().front().getTerminator());
351 TypeRange bodyArgTypes = getBody().front().getArgumentTypes();
353 if (bodyArgTypes.size() != getInputs().size())
355 "number of block arguments must match number of input vectors");
358 if (returnOp.getValue().getType() == getResultTypes().front()) {
359 for (
auto [i, argTy] : llvm::enumerate(bodyArgTypes))
360 if (argTy != getInputs()[i].getTypes().front())
361 return emitOpError(
"if terminator type matches result type the "
362 "argument types must match the input types");
369 getResultTypes().front());
371 for (
auto [i, argTy] : llvm::enumerate(bodyArgTypes)) {
372 Type inputTy = getInputs()[i].getTypes().front();
374 if (failed(argWidth))
375 return emitOpError(
"block argument must be a scalar variant of the "
376 "vectorized operand");
378 if (*argWidth !=
width)
379 return emitOpError(
"input and output vector width must match");
387 returnOp.getValue().getType());
389 for (
auto [i, argTy] : llvm::enumerate(bodyArgTypes)) {
390 Type inputTy = getInputs()[i].getTypes().front();
392 if (failed(argWidth))
394 "block argument must be a vectorized variant of the operand");
396 if (*argWidth !=
width)
397 return emitOpError(
"input and output vector width must match");
399 if (getInputs()[i].size() > 1 && argWidth != getInputs()[i].size())
401 "when boundary not vectorized the number of vector element "
402 "operands must match the width of the vectorized body");
408 return returnOp.emitOpError(
409 "operand type must match parent op's result value or be a vectorized or "
410 "non-vectorized variant of it");
413 bool VectorizeOp::isBoundaryVectorized() {
414 return getInputs().front().size() == 1;
416 bool VectorizeOp::isBodyVectorized() {
417 auto returnOp = cast<VectorizeReturnOp>(getBody().front().getTerminator());
418 if (isBoundaryVectorized() &&
419 returnOp.getValue().getType() == getResultTypes().front())
423 returnOp.getValue().getType());
430 #include "circt/Dialect/Arc/ArcInterfaces.cpp.inc"
432 #define GET_OP_CLASSES
433 #include "circt/Dialect/Arc/Arc.cpp.inc"
static LogicalResult verifyArcSymbolUse(Operation *op, TypeRange inputs, TypeRange results, SymbolTableCollection &symbolTable)
static LogicalResult verifyTypeListEquivalence(Operation *op, TypeRange expectedTypeList, TypeRange actualTypeList, StringRef elementName)
static FailureOr< unsigned > getVectorWidth(Type base, Type vectorized)
assert(baseType &&"element must be base type")
static InstancePath empty
llvm::SmallVector< StringAttr > inputs
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
StringAttr getName(ArrayAttr names, size_t idx)
Return the name at the specified index of the ArrayAttr or null if it cannot be determined.
void getAsmResultNames(OpAsmSetValueNameFn setNameFn, StringRef instanceName, ArrayAttr resultNames, ValueRange results)
Suggest a name for each result value based on the saved result names attribute.
This file defines an intermediate representation for circuits acting as an abstraction for constraint...
function_ref< void(Value, StringRef)> OpAsmSetValueNameFn