11#include "mlir/Dialect/Func/IR/FuncOps.h" 
   12#include "mlir/IR/Builders.h" 
   13#include "mlir/IR/OpImplementation.h" 
   14#include "mlir/IR/PatternMatch.h" 
   15#include "mlir/IR/SymbolTable.h" 
   16#include "mlir/Interfaces/FunctionImplementation.h" 
   17#include "mlir/Interfaces/SideEffectInterfaces.h" 
   18#include "llvm/ADT/SmallPtrSet.h" 
   19#include "llvm/ADT/TypeSwitch.h" 
   30                                               TypeRange expectedTypeList,
 
   31                                               TypeRange actualTypeList,
 
   32                                               StringRef elementName) {
 
   33  if (expectedTypeList.size() != actualTypeList.size())
 
   34    return op->emitOpError(
"incorrect number of ")
 
   35           << elementName << 
"s: expected " << expectedTypeList.size()
 
   36           << 
", but got " << actualTypeList.size();
 
   38  for (
unsigned i = 0, e = expectedTypeList.size(); i != e; ++i) {
 
   39    if (expectedTypeList[i] != actualTypeList[i]) {
 
   40      auto diag = op->emitOpError(elementName)
 
   41                  << 
" type mismatch: " << elementName << 
" #" << i;
 
   42      diag.attachNote() << 
"expected type: " << expectedTypeList[i];
 
   43      diag.attachNote() << 
"  actual type: " << actualTypeList[i];
 
 
   53                                        SymbolTableCollection &symbolTable) {
 
   55  auto arcName = op->getAttrOfType<FlatSymbolRefAttr>(
"arc");
 
   58  assert(arcName && 
"FlatSymbolRefAttr called 'arc' missing");
 
   59  DefineOp arc = symbolTable.lookupNearestSymbolFrom<DefineOp>(op, arcName);
 
   61    return op->emitOpError() << 
"`" << arcName.getValue()
 
   62                             << 
"` does not reference a valid `arc.define`";
 
   65  auto type = arc.getFunctionType();
 
 
   78  return llvm::isa<arc::ModelOp, hw::HWModuleLike>(moduleOp);
 
 
   84                                       Operation *pointing, StringAttr symbol) {
 
   85  Operation *moduleOp = symbolTable.lookupNearestSymbolFrom(pointing, symbol);
 
   87    pointing->emitOpError(
"model not found");
 
   92    pointing->emitOpError(
"model symbol does not point to a supported model " 
   93                          "operation, points to ")
 
   94        << moduleOp->getName() << 
" instead";
 
 
  102                                                   StringRef portName) {
 
  103  auto findRightPort = [&](
auto ports) -> std::optional<hw::ModulePort> {
 
  106    if (port == ports.end())
 
  111  return TypeSwitch<Operation *, std::optional<hw::ModulePort>>(moduleOp)
 
  113          [&](arc::ModelOp modelOp) -> std::optional<hw::ModulePort> {
 
  114            return findRightPort(modelOp.getIo().getPorts());
 
  116      .Case<hw::HWModuleLike>(
 
  117          [&](hw::HWModuleLike moduleLike) -> std::optional<hw::ModulePort> {
 
  118            return findRightPort(moduleLike.getPortList());
 
  120      .Default([](Operation *) { 
return std::nullopt; });
 
 
  127ParseResult DefineOp::parse(OpAsmParser &parser, OperationState &result) {
 
  129      [](Builder &builder, ArrayRef<Type> argTypes, ArrayRef<Type> results,
 
  130         function_interface_impl::VariadicFlag,
 
  131         std::string &) { 
return builder.getFunctionType(argTypes, results); };
 
  133  return function_interface_impl::parseFunctionOp(
 
  134      parser, result, 
false,
 
  135      getFunctionTypeAttrName(result.name), buildFuncType,
 
  136      getArgAttrsAttrName(result.name), getResAttrsAttrName(result.name));
 
  139void DefineOp::print(OpAsmPrinter &p) {
 
  140  function_interface_impl::printFunctionOp(
 
  141      p, *
this, 
false, 
"function_type", getArgAttrsAttrName(),
 
  142      getResAttrsAttrName());
 
  145LogicalResult DefineOp::verifyRegions() {
 
  152    if (isMemoryEffectFree(&op))
 
  161    auto diag = mlir::emitError(
getLoc(), 
"body contains non-pure operation");
 
  162    diag.attachNote(op.getLoc()).append(
"first non-pure operation here: ");
 
  168bool DefineOp::isPassthrough() {
 
  169  if (getNumArguments() != getNumResults())
 
  173      llvm::zip(getArguments(), 
getBodyBlock().getTerminator()->getOperands()),
 
  174      [](
const auto &argAndRes) {
 
  175        return std::get<0>(argAndRes) == std::get<1>(argAndRes);
 
  183LogicalResult OutputOp::verify() {
 
  184  auto *parent = (*this)->getParentOp();
 
  185  TypeRange expectedTypes = parent->getResultTypes();
 
  186  if (
auto defOp = dyn_cast<DefineOp>(parent))
 
  187    expectedTypes = defOp.getResultTypes();
 
  189  TypeRange actualTypes = getOperands().getTypes();
 
  197LogicalResult StateOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
 
  199                            getResults().getTypes(), symbolTable);
 
  202LogicalResult StateOp::verify() {
 
  203  if (getLatency() < 1)
 
  204    return emitOpError(
"latency must be a positive integer");
 
  206  if (!getOperation()->getParentOfType<ClockDomainOp>() && !getClock())
 
  207    return emitOpError(
"outside a clock domain requires a clock");
 
  209  if (getOperation()->getParentOfType<ClockDomainOp>() && getClock())
 
  210    return emitOpError(
"inside a clock domain cannot have a clock");
 
  219LogicalResult CallOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
 
  221                            getResults().getTypes(), symbolTable);
 
  224bool CallOp::isClocked() { 
return false; }
 
  226Value CallOp::getClock() { 
return Value{}; }
 
  228void CallOp::eraseClock() {}
 
  230uint32_t CallOp::getLatency() { 
return 0; }
 
  236SmallVector<Type> MemoryWritePortOp::getArcResultTypes() {
 
  237  auto memType = cast<MemoryType>(getMemory().getType());
 
  238  SmallVector<Type> resultTypes{memType.getAddressType(),
 
  239                                memType.getWordType()};
 
  241    resultTypes.push_back(IntegerType::get(getContext(), 1));
 
  243    resultTypes.push_back(memType.getWordType());
 
  248MemoryWritePortOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
 
  253LogicalResult MemoryWritePortOp::verify() {
 
  254  if (getLatency() < 1)
 
  255    return emitOpError(
"latency must be at least 1");
 
  257  if (!getOperation()->getParentOfType<ClockDomainOp>() && !getClock())
 
  258    return emitOpError(
"outside a clock domain requires a clock");
 
  260  if (getOperation()->getParentOfType<ClockDomainOp>() && getClock())
 
  261    return emitOpError(
"inside a clock domain cannot have a clock");
 
  270LogicalResult ClockDomainOp::verifyRegions() {
 
  272                                   getInputs().getTypes(), 
"input");
 
  280  SmallString<32> buf(
"in_");
 
  282  setNameFn(getState(), buf);
 
  290  SmallString<32> buf(
"out_");
 
  292  setNameFn(getState(), buf);
 
  299LogicalResult ModelOp::verify() {
 
  301    return emitOpError(
"must have exactly one argument");
 
  302  if (
auto type = 
getBodyBlock().getArgument(0).getType();
 
  303      !isa<StorageType>(type))
 
  304    return emitOpError(
"argument must be of storage type");
 
  307      return emitOpError(
"inout ports are not supported");
 
  311LogicalResult ModelOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
 
  312  auto fnAttrs = std::array{getInitialFnAttr(), getFinalFnAttr()};
 
  313  auto nouns = std::array{
"initializer", 
"finalizer"};
 
  314  for (
auto [fnAttr, noun] : 
llvm::zip(fnAttrs, nouns)) {
 
  317    auto fn = symbolTable.lookupNearestSymbolFrom<func::FuncOp>(*
this, fnAttr);
 
  319      return emitOpError() << noun << 
" '" << fnAttr.getValue()
 
  320                           << 
"' does not reference a valid function";
 
  321    if (!llvm::equal(fn.getArgumentTypes(), getBody().getArgumentTypes())) {
 
  322      auto diag = emitError() << noun << 
" '" << fnAttr.getValue()
 
  323                              << 
"' arguments must match arguments of model";
 
  324      diag.attachNote(fn.getLoc()) << noun << 
" declared here:";
 
  335LogicalResult LutOp::verify() {
 
  336  Location firstSideEffectOpLoc = UnknownLoc::get(getContext());
 
  337  const WalkResult result = getBody().walk([&](Operation *op) {
 
  338    if (
auto memOp = dyn_cast<MemoryEffectOpInterface>(op)) {
 
  339      SmallVector<SideEffects::EffectInstance<MemoryEffects::Effect>> effects;
 
  340      memOp.getEffects(effects);
 
  342      if (!effects.empty()) {
 
  343        firstSideEffectOpLoc = memOp->getLoc();
 
  344        return WalkResult::interrupt();
 
  348    return WalkResult::advance();
 
  351  if (result.wasInterrupted())
 
  352    return emitOpError(
"no operations with side-effects allowed inside a LUT")
 
  353               .attachNote(firstSideEffectOpLoc)
 
  354           << 
"first operation with side-effects here";
 
  363LogicalResult VectorizeOp::verify() {
 
  364  if (getInputs().
empty())
 
  365    return emitOpError(
"there has to be at least one input vector");
 
  367  if (!llvm::all_equal(llvm::map_range(
 
  368          getInputs(), [](OperandRange range) { 
return range.size(); })))
 
  369    return emitOpError(
"all input vectors must have the same size");
 
  371  for (OperandRange range : getInputs()) {
 
  372    if (!llvm::all_equal(range.getTypes()))
 
  373      return emitOpError(
"all input vector lane types must match");
 
  376      return emitOpError(
"input vector must have at least one element");
 
  379  if (getResults().
empty())
 
  380    return emitOpError(
"must have at least one result");
 
  382  if (!llvm::all_equal(getResults().getTypes()))
 
  383    return emitOpError(
"all result types must match");
 
  385  if (getResults().size() != getInputs().front().size())
 
  386    return emitOpError(
"number results must match input vector size");
 
  392  if (isa<VectorType>(base))
 
  395  if (
auto vectorTy = dyn_cast<VectorType>(vectorized)) {
 
  396    if (vectorTy.getElementType() != base)
 
  399    return vectorTy.getDimSize(0);
 
  402  if (vectorized.getIntOrFloatBitWidth() < base.getIntOrFloatBitWidth())
 
  405  if (vectorized.getIntOrFloatBitWidth() % base.getIntOrFloatBitWidth() == 0)
 
  406    return vectorized.getIntOrFloatBitWidth() / base.getIntOrFloatBitWidth();
 
 
  411LogicalResult VectorizeOp::verifyRegions() {
 
  412  auto returnOp = cast<VectorizeReturnOp>(getBody().front().getTerminator());
 
  413  TypeRange bodyArgTypes = getBody().front().getArgumentTypes();
 
  415  if (bodyArgTypes.size() != getInputs().size())
 
  417        "number of block arguments must match number of input vectors");
 
  420  if (returnOp.getValue().getType() == getResultTypes().front()) {
 
  421    for (
auto [i, argTy] : 
llvm::enumerate(bodyArgTypes))
 
  422      if (argTy != getInputs()[i].getTypes().front())
 
  423        return emitOpError(
"if terminator type matches result type the " 
  424                           "argument types must match the input types");
 
  431                                  getResultTypes().front());
 
  433    for (
auto [i, argTy] : 
llvm::enumerate(bodyArgTypes)) {
 
  434      Type inputTy = getInputs()[i].getTypes().front();
 
  436      if (failed(argWidth))
 
  437        return emitOpError(
"block argument must be a scalar variant of the " 
  438                           "vectorized operand");
 
  440      if (*argWidth != width)
 
  441        return emitOpError(
"input and output vector width must match");
 
  449                                  returnOp.getValue().getType());
 
  451    for (
auto [i, argTy] : 
llvm::enumerate(bodyArgTypes)) {
 
  452      Type inputTy = getInputs()[i].getTypes().front();
 
  454      if (failed(argWidth))
 
  456            "block argument must be a vectorized variant of the operand");
 
  458      if (*argWidth != width)
 
  459        return emitOpError(
"input and output vector width must match");
 
  461      if (getInputs()[i].size() > 1 && argWidth != getInputs()[i].size())
 
  463            "when boundary not vectorized the number of vector element " 
  464            "operands must match the width of the vectorized body");
 
  470  return returnOp.emitOpError(
 
  471      "operand type must match parent op's result value or be a vectorized or " 
  472      "non-vectorized variant of it");
 
  475bool VectorizeOp::isBoundaryVectorized() {
 
  476  return getInputs().front().size() == 1;
 
  478bool VectorizeOp::isBodyVectorized() {
 
  479  auto returnOp = cast<VectorizeReturnOp>(getBody().front().getTerminator());
 
  480  if (isBoundaryVectorized() &&
 
  481      returnOp.getValue().getType() == getResultTypes().front())
 
  485                                  returnOp.getValue().getType());
 
  496void SimInstantiateOp::print(OpAsmPrinter &p) {
 
  497  BlockArgument modelArg = getBody().getArgument(0);
 
  498  auto modelType = cast<SimModelInstanceType>(modelArg.getType());
 
  500  p << 
" " << modelType.getModel() << 
" as ";
 
  501  p.printRegionArgument(modelArg, {}, 
true);
 
  503  p.printOptionalAttrDictWithKeyword(getOperation()->getAttrs());
 
  507  p.printRegion(getBody(), 
false);
 
  510ParseResult SimInstantiateOp::parse(OpAsmParser &parser,
 
  511                                    OperationState &result) {
 
  512  StringAttr modelName;
 
  513  if (failed(parser.parseSymbolName(modelName)))
 
  516  if (failed(parser.parseKeyword(
"as")))
 
  519  OpAsmParser::Argument modelArg;
 
  520  if (failed(parser.parseArgument(modelArg, 
false, 
false)))
 
  523  if (failed(parser.parseOptionalAttrDictWithKeyword(result.attributes)))
 
  526  MLIRContext *
ctxt = result.getContext();
 
  528      SimModelInstanceType::get(ctxt, FlatSymbolRefAttr::get(ctxt, modelName));
 
  530  std::unique_ptr<Region> body = std::make_unique<Region>();
 
  531  if (failed(parser.parseRegion(*body, {modelArg})))
 
  534  result.addRegion(std::move(body));
 
  538LogicalResult SimInstantiateOp::verifyRegions() {
 
  539  Region &body = getBody();
 
  540  if (body.getNumArguments() != 1)
 
  541    return emitError(
"entry block of body region must have the model instance " 
  542                     "as a single argument");
 
  543  if (!llvm::isa<SimModelInstanceType>(body.getArgument(0).getType()))
 
  544    return emitError(
"entry block argument type is not a model instance");
 
  549SimInstantiateOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
 
  551      symbolTable, getOperation(),
 
  552      llvm::cast<SimModelInstanceType>(getBody().getArgument(0).getType())
 
  566SimSetInputOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
 
  568      symbolTable, getOperation(),
 
  569      llvm::cast<SimModelInstanceType>(getInstance().getType())
 
  575  std::optional<hw::ModulePort> port = 
getModulePort(moduleOp, getInput());
 
  577    return emitOpError(
"port not found on model");
 
  579  if (port->dir != hw::ModulePort::Direction::Input &&
 
  580      port->dir != hw::ModulePort::Direction::InOut)
 
  581    return emitOpError(
"port is not an input port");
 
  583  if (port->type != getValue().getType())
 
  585               "mismatched types between value and model port, port expects ")
 
  596SimGetPortOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
 
  598      symbolTable, getOperation(),
 
  599      llvm::cast<SimModelInstanceType>(getInstance().getType())
 
  607    return emitOpError(
"port not found on model");
 
  609  if (port->type != getValue().getType())
 
  611               "mismatched types between value and model port, port expects ")
 
  621LogicalResult SimStepOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
 
  623      symbolTable, getOperation(),
 
  624      llvm::cast<SimModelInstanceType>(getInstance().getType())
 
  637LogicalResult ExecuteOp::verifyRegions() {
 
  639                                   getBody().getArgumentTypes(), 
"input");
 
  642#include "circt/Dialect/Arc/ArcInterfaces.cpp.inc" 
  644#define GET_OP_CLASSES 
  645#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 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