CIRCT  19.0.0git
LLHDOps.cpp
Go to the documentation of this file.
1 //===- LLHDOps.cpp - Implement the LLHD 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 implement the LLHD ops.
10 //
11 //===----------------------------------------------------------------------===//
12 
14 #include "circt/Dialect/HW/HWOps.h"
16 #include "mlir/Dialect/CommonFolders.h"
17 #include "mlir/IR/Attributes.h"
18 #include "mlir/IR/BuiltinOps.h"
19 #include "mlir/IR/BuiltinTypes.h"
20 #include "mlir/IR/Matchers.h"
21 #include "mlir/IR/OpImplementation.h"
22 #include "mlir/IR/PatternMatch.h"
23 #include "mlir/IR/Region.h"
24 #include "mlir/IR/Types.h"
25 #include "mlir/IR/Value.h"
26 #include "mlir/Support/LogicalResult.h"
27 #include "llvm/ADT/ArrayRef.h"
28 #include "llvm/ADT/SmallVector.h"
29 #include "llvm/ADT/StringSet.h"
30 #include "llvm/ADT/TypeSwitch.h"
31 
32 using namespace circt;
33 using namespace mlir;
34 
35 template <class AttrElementT,
36  class ElementValueT = typename AttrElementT::ValueType,
37  class CalculationT = function_ref<ElementValueT(ElementValueT)>>
38 static Attribute constFoldUnaryOp(ArrayRef<Attribute> operands,
39  const CalculationT &calculate) {
40  assert(operands.size() == 1 && "unary op takes one operand");
41  if (!operands[0])
42  return {};
43 
44  if (auto val = dyn_cast<AttrElementT>(operands[0])) {
45  return AttrElementT::get(val.getType(), calculate(val.getValue()));
46  } else if (auto val = dyn_cast<SplatElementsAttr>(operands[0])) {
47  // Operand is a splat so we can avoid expanding the value out and
48  // just fold based on the splat value.
49  auto elementResult = calculate(val.getSplatValue<ElementValueT>());
50  return DenseElementsAttr::get(val.getType(), elementResult);
51  }
52  if (auto val = dyn_cast<ElementsAttr>(operands[0])) {
53  // Operand is ElementsAttr-derived; perform an element-wise fold by
54  // expanding the values.
55  auto valIt = val.getValues<ElementValueT>().begin();
56  SmallVector<ElementValueT, 4> elementResults;
57  elementResults.reserve(val.getNumElements());
58  for (size_t i = 0, e = val.getNumElements(); i < e; ++i, ++valIt)
59  elementResults.push_back(calculate(*valIt));
60  return DenseElementsAttr::get(val.getType(), elementResults);
61  }
62  return {};
63 }
64 
65 template <class AttrElementT,
66  class ElementValueT = typename AttrElementT::ValueType,
67  class CalculationT = function_ref<
68  ElementValueT(ElementValueT, ElementValueT, ElementValueT)>>
69 static Attribute constFoldTernaryOp(ArrayRef<Attribute> operands,
70  const CalculationT &calculate) {
71  assert(operands.size() == 3 && "ternary op takes three operands");
72  if (!operands[0] || !operands[1] || !operands[2])
73  return {};
74 
75  if (isa<AttrElementT>(operands[0]) && isa<AttrElementT>(operands[1]) &&
76  isa<AttrElementT>(operands[2])) {
77  auto fst = cast<AttrElementT>(operands[0]);
78  auto snd = cast<AttrElementT>(operands[1]);
79  auto trd = cast<AttrElementT>(operands[2]);
80 
81  return AttrElementT::get(
82  fst.getType(),
83  calculate(fst.getValue(), snd.getValue(), trd.getValue()));
84  }
85  if (isa<SplatElementsAttr>(operands[0]) &&
86  isa<SplatElementsAttr>(operands[1]) &&
87  isa<SplatElementsAttr>(operands[2])) {
88  // Operands are splats so we can avoid expanding the values out and
89  // just fold based on the splat value.
90  auto fst = cast<SplatElementsAttr>(operands[0]);
91  auto snd = cast<SplatElementsAttr>(operands[1]);
92  auto trd = cast<SplatElementsAttr>(operands[2]);
93 
94  auto elementResult = calculate(fst.getSplatValue<ElementValueT>(),
95  snd.getSplatValue<ElementValueT>(),
96  trd.getSplatValue<ElementValueT>());
97  return DenseElementsAttr::get(fst.getType(), elementResult);
98  }
99  if (isa<ElementsAttr>(operands[0]) && isa<ElementsAttr>(operands[1]) &&
100  isa<ElementsAttr>(operands[2])) {
101  // Operands are ElementsAttr-derived; perform an element-wise fold by
102  // expanding the values.
103  auto fst = cast<ElementsAttr>(operands[0]);
104  auto snd = cast<ElementsAttr>(operands[1]);
105  auto trd = cast<ElementsAttr>(operands[2]);
106 
107  auto fstIt = fst.getValues<ElementValueT>().begin();
108  auto sndIt = snd.getValues<ElementValueT>().begin();
109  auto trdIt = trd.getValues<ElementValueT>().begin();
110  SmallVector<ElementValueT, 4> elementResults;
111  elementResults.reserve(fst.getNumElements());
112  for (size_t i = 0, e = fst.getNumElements(); i < e;
113  ++i, ++fstIt, ++sndIt, ++trdIt)
114  elementResults.push_back(calculate(*fstIt, *sndIt, *trdIt));
115  return DenseElementsAttr::get(fst.getType(), elementResults);
116  }
117  return {};
118 }
119 
120 namespace {
121 
122 struct constant_int_all_ones_matcher {
123  bool match(Operation *op) {
124  APInt value;
125  return mlir::detail::constant_int_value_binder(&value).match(op) &&
126  value.isAllOnes();
127  }
128 };
129 
130 } // anonymous namespace
131 
132 unsigned circt::llhd::getLLHDTypeWidth(Type type) {
133  if (auto sig = dyn_cast<llhd::SigType>(type))
134  type = sig.getUnderlyingType();
135  else if (auto ptr = dyn_cast<llhd::PtrType>(type))
136  type = ptr.getUnderlyingType();
137  if (auto array = dyn_cast<hw::ArrayType>(type))
138  return array.getNumElements();
139  if (auto tup = dyn_cast<hw::StructType>(type))
140  return tup.getElements().size();
141  return type.getIntOrFloatBitWidth();
142 }
143 
145  if (auto sig = dyn_cast<llhd::SigType>(type))
146  type = sig.getUnderlyingType();
147  else if (auto ptr = dyn_cast<llhd::PtrType>(type))
148  type = ptr.getUnderlyingType();
149  if (auto array = dyn_cast<hw::ArrayType>(type))
150  return array.getElementType();
151  return type;
152 }
153 
154 //===---------------------------------------------------------------------===//
155 // LLHD Operations
156 //===---------------------------------------------------------------------===//
157 
158 //===----------------------------------------------------------------------===//
159 // ConstantTimeOp
160 //===----------------------------------------------------------------------===//
161 
162 OpFoldResult llhd::ConstantTimeOp::fold(FoldAdaptor adaptor) {
163  assert(adaptor.getOperands().empty() && "const has no operands");
164  return getValueAttr();
165 }
166 
167 void llhd::ConstantTimeOp::build(OpBuilder &builder, OperationState &result,
168  unsigned time, const StringRef &timeUnit,
169  unsigned delta, unsigned epsilon) {
170  auto *ctx = builder.getContext();
171  auto attr = TimeAttr::get(ctx, time, timeUnit, delta, epsilon);
172  return build(builder, result, TimeType::get(ctx), attr);
173 }
174 
175 //===----------------------------------------------------------------------===//
176 // SigExtractOp and PtrExtractOp
177 //===----------------------------------------------------------------------===//
178 
179 template <class Op>
180 static OpFoldResult foldSigPtrExtractOp(Op op, ArrayRef<Attribute> operands) {
181 
182  if (!operands[1])
183  return nullptr;
184 
185  // llhd.sig.extract(input, 0) with inputWidth == resultWidth => input
186  if (op.getResultWidth() == op.getInputWidth() &&
187  cast<IntegerAttr>(operands[1]).getValue().isZero())
188  return op.getInput();
189 
190  return nullptr;
191 }
192 
193 OpFoldResult llhd::SigExtractOp::fold(FoldAdaptor adaptor) {
194  return foldSigPtrExtractOp(*this, adaptor.getOperands());
195 }
196 
197 OpFoldResult llhd::PtrExtractOp::fold(FoldAdaptor adaptor) {
198  return foldSigPtrExtractOp(*this, adaptor.getOperands());
199 }
200 
201 //===----------------------------------------------------------------------===//
202 // SigArraySliceOp and PtrArraySliceOp
203 //===----------------------------------------------------------------------===//
204 
205 template <class Op>
206 static OpFoldResult foldSigPtrArraySliceOp(Op op,
207  ArrayRef<Attribute> operands) {
208  if (!operands[1])
209  return nullptr;
210 
211  // llhd.sig.array_slice(input, 0) with inputWidth == resultWidth => input
212  if (op.getResultWidth() == op.getInputWidth() &&
213  cast<IntegerAttr>(operands[1]).getValue().isZero())
214  return op.getInput();
215 
216  return nullptr;
217 }
218 
219 OpFoldResult llhd::SigArraySliceOp::fold(FoldAdaptor adaptor) {
220  return foldSigPtrArraySliceOp(*this, adaptor.getOperands());
221 }
222 
223 OpFoldResult llhd::PtrArraySliceOp::fold(FoldAdaptor adaptor) {
224  return foldSigPtrArraySliceOp(*this, adaptor.getOperands());
225 }
226 
227 template <class Op>
228 static LogicalResult canonicalizeSigPtrArraySliceOp(Op op,
229  PatternRewriter &rewriter) {
230  IntegerAttr indexAttr;
231  if (!matchPattern(op.getLowIndex(), m_Constant(&indexAttr)))
232  return failure();
233 
234  // llhd.sig.array_slice(llhd.sig.array_slice(target, a), b)
235  // => llhd.sig.array_slice(target, a+b)
236  IntegerAttr a;
237  if (matchPattern(op.getInput(),
238  m_Op<Op>(matchers::m_Any(), m_Constant(&a)))) {
239  auto sliceOp = op.getInput().template getDefiningOp<Op>();
240  op.getInputMutable().assign(sliceOp.getInput());
241  Value newIndex = rewriter.create<hw::ConstantOp>(
242  op->getLoc(), a.getValue() + indexAttr.getValue());
243  op.getLowIndexMutable().assign(newIndex);
244 
245  return success();
246  }
247 
248  return failure();
249 }
250 
251 LogicalResult llhd::SigArraySliceOp::canonicalize(llhd::SigArraySliceOp op,
252  PatternRewriter &rewriter) {
253  return canonicalizeSigPtrArraySliceOp(op, rewriter);
254 }
255 
256 LogicalResult llhd::PtrArraySliceOp::canonicalize(llhd::PtrArraySliceOp op,
257  PatternRewriter &rewriter) {
258  return canonicalizeSigPtrArraySliceOp(op, rewriter);
259 }
260 
261 //===----------------------------------------------------------------------===//
262 // SigStructExtractOp and PtrStructExtractOp
263 //===----------------------------------------------------------------------===//
264 
265 template <class SigPtrType>
267  MLIRContext *context, std::optional<Location> loc, ValueRange operands,
268  DictionaryAttr attrs, mlir::OpaqueProperties properties,
269  mlir::RegionRange regions, SmallVectorImpl<Type> &results) {
270  Type type =
271  cast<hw::StructType>(
272  cast<SigPtrType>(operands[0].getType()).getUnderlyingType())
273  .getFieldType(
274  cast<StringAttr>(attrs.getNamed("field")->getValue()).getValue());
275  if (!type) {
276  context->getDiagEngine().emit(loc.value_or(UnknownLoc()),
277  DiagnosticSeverity::Error)
278  << "invalid field name specified";
279  return failure();
280  }
281  results.push_back(SigPtrType::get(type));
282  return success();
283 }
284 
286  MLIRContext *context, std::optional<Location> loc, ValueRange operands,
287  DictionaryAttr attrs, mlir::OpaqueProperties properties,
288  mlir::RegionRange regions, SmallVectorImpl<Type> &results) {
289  return inferReturnTypesOfStructExtractOp<llhd::SigType>(
290  context, loc, operands, attrs, properties, regions, results);
291 }
292 
294  MLIRContext *context, std::optional<Location> loc, ValueRange operands,
295  DictionaryAttr attrs, mlir::OpaqueProperties properties,
296  mlir::RegionRange regions, SmallVectorImpl<Type> &results) {
297  return inferReturnTypesOfStructExtractOp<llhd::PtrType>(
298  context, loc, operands, attrs, properties, regions, results);
299 }
300 
301 //===----------------------------------------------------------------------===//
302 // DrvOp
303 //===----------------------------------------------------------------------===//
304 
305 LogicalResult llhd::DrvOp::fold(FoldAdaptor adaptor,
306  SmallVectorImpl<OpFoldResult> &result) {
307  if (!getEnable())
308  return failure();
309 
310  if (matchPattern(getEnable(), m_One())) {
311  getEnableMutable().clear();
312  return success();
313  }
314 
315  return failure();
316 }
317 
318 LogicalResult llhd::DrvOp::canonicalize(llhd::DrvOp op,
319  PatternRewriter &rewriter) {
320  if (!op.getEnable())
321  return failure();
322 
323  if (matchPattern(op.getEnable(), m_Zero())) {
324  rewriter.eraseOp(op);
325  return success();
326  }
327 
328  return failure();
329 }
330 
331 //===----------------------------------------------------------------------===//
332 // WaitOp
333 //===----------------------------------------------------------------------===//
334 
335 // Implement this operation for the BranchOpInterface
336 SuccessorOperands llhd::WaitOp::getSuccessorOperands(unsigned index) {
337  assert(index == 0 && "invalid successor index");
338  return SuccessorOperands(getDestOpsMutable());
339 }
340 
341 //===----------------------------------------------------------------------===//
342 // EntityOp
343 //===----------------------------------------------------------------------===//
344 
345 /// Parse an argument list of an entity operation.
346 /// The argument list and argument types are returned in args and argTypes
347 /// respectively.
348 static ParseResult
349 parseArgumentList(OpAsmParser &parser,
350  SmallVectorImpl<OpAsmParser::Argument> &args,
351  SmallVectorImpl<Type> &argTypes) {
352  auto parseElt = [&]() -> ParseResult {
353  OpAsmParser::Argument argument;
354  Type argType;
355  auto optArg = parser.parseOptionalArgument(argument);
356  if (optArg.has_value()) {
357  if (succeeded(optArg.value())) {
358  if (!argument.ssaName.name.empty() &&
359  succeeded(parser.parseColonType(argType))) {
360  args.push_back(argument);
361  argTypes.push_back(argType);
362  args.back().type = argType;
363  }
364  }
365  }
366  return success();
367  };
368 
369  return parser.parseCommaSeparatedList(OpAsmParser::Delimiter::Paren,
370  parseElt);
371 }
372 
373 /// parse an entity signature with syntax:
374 /// (%arg0 : T0, %arg1 : T1, <...>) -> (%out0 : T0, %out1 : T1, <...>)
375 static ParseResult
376 parseEntitySignature(OpAsmParser &parser, OperationState &result,
377  SmallVectorImpl<OpAsmParser::Argument> &args,
378  SmallVectorImpl<Type> &argTypes) {
379  if (parseArgumentList(parser, args, argTypes))
380  return failure();
381  // create the integer attribute with the number of inputs.
382  IntegerAttr insAttr = parser.getBuilder().getI64IntegerAttr(args.size());
383  result.addAttribute("ins", insAttr);
384  if (succeeded(parser.parseOptionalArrow()))
385  if (parseArgumentList(parser, args, argTypes))
386  return failure();
387 
388  return success();
389 }
390 
391 ParseResult llhd::EntityOp::parse(OpAsmParser &parser, OperationState &result) {
392  StringAttr entityName;
393  SmallVector<OpAsmParser::Argument, 4> args;
394  SmallVector<Type, 4> argTypes;
395 
396  if (parser.parseSymbolName(entityName, SymbolTable::getSymbolAttrName(),
397  result.attributes))
398  return failure();
399 
400  if (parseEntitySignature(parser, result, args, argTypes))
401  return failure();
402 
403  if (parser.parseOptionalAttrDictWithKeyword(result.attributes))
404  return failure();
405 
406  auto type = parser.getBuilder().getFunctionType(argTypes, std::nullopt);
407  result.addAttribute(
408  circt::llhd::EntityOp::getFunctionTypeAttrName(result.name),
409  TypeAttr::get(type));
410 
411  auto &body = *result.addRegion();
412  if (parser.parseRegion(body, args))
413  return failure();
414  if (body.empty())
415  body.push_back(std::make_unique<Block>().release());
416 
417  return success();
418 }
419 
420 static void printArgumentList(OpAsmPrinter &printer,
421  std::vector<BlockArgument> args) {
422  printer << "(";
423  llvm::interleaveComma(args, printer, [&](BlockArgument arg) {
424  printer << arg << " : " << arg.getType();
425  });
426  printer << ")";
427 }
428 
429 void llhd::EntityOp::print(OpAsmPrinter &printer) {
430  std::vector<BlockArgument> ins, outs;
431  uint64_t nIns = getInsAttr().getInt();
432  for (uint64_t i = 0; i < getBody().front().getArguments().size(); ++i) {
433  // no furter verification for the attribute type is required, already
434  // handled by verify.
435  if (i < nIns) {
436  ins.push_back(getBody().front().getArguments()[i]);
437  } else {
438  outs.push_back(getBody().front().getArguments()[i]);
439  }
440  }
441  auto entityName =
442  (*this)
443  ->getAttrOfType<StringAttr>(SymbolTable::getSymbolAttrName())
444  .getValue();
445  printer << " ";
446  printer.printSymbolName(entityName);
447  printer << " ";
448  printArgumentList(printer, ins);
449  printer << " -> ";
450  printArgumentList(printer, outs);
451  printer.printOptionalAttrDictWithKeyword(
452  (*this)->getAttrs(),
453  /*elidedAttrs =*/{SymbolTable::getSymbolAttrName(),
454  getFunctionTypeAttrName(), "ins"});
455  printer << " ";
456  printer.printRegion(getBody(), false, false);
457 }
458 
459 LogicalResult llhd::EntityOp::verify() {
460  uint64_t numArgs = getNumArguments();
461  uint64_t nIns = getInsAttr().getInt();
462  // check that there is at most one flag for each argument
463  if (numArgs < nIns) {
464  return emitError(
465  "Cannot have more inputs than arguments, expected at most ")
466  << numArgs << " but got: " << nIns;
467  }
468 
469  // Check that all block arguments are of signal type
470  for (size_t i = 0; i < numArgs; ++i)
471  if (!isa<llhd::SigType>(getArgument(i).getType()))
472  return emitError("usage of invalid argument type. Got ")
473  << getArgument(i).getType() << ", expected LLHD signal type";
474 
475  return success();
476 }
477 
478 LogicalResult circt::llhd::EntityOp::verifyType() {
479  FunctionType type = getFunctionType();
480 
481  // Fail if function returns any values. An entity's outputs are specially
482  // marked arguments.
483  if (type.getNumResults() > 0)
484  return emitOpError("an entity cannot have return types.");
485 
486  // Check that all operands are of signal type
487  for (Type inputType : type.getInputs())
488  if (!isa<llhd::SigType>(inputType))
489  return emitOpError("usage of invalid argument type. Got ")
490  << inputType << ", expected LLHD signal type";
491 
492  return success();
493 }
494 
495 LogicalResult circt::llhd::EntityOp::verifyBody() {
496  // check signal names are unique
497  llvm::StringSet<> sigSet;
498  llvm::StringSet<> instSet;
499  auto walkResult = walk([&sigSet, &instSet](Operation *op) -> WalkResult {
500  return TypeSwitch<Operation *, WalkResult>(op)
501  .Case<SigOp>([&](auto sigOp) -> WalkResult {
502  if (!sigSet.insert(sigOp.getName()).second)
503  return sigOp.emitError("redefinition of signal named '")
504  << sigOp.getName() << "'!";
505 
506  return success();
507  })
508  .Case<OutputOp>([&](auto outputOp) -> WalkResult {
509  if (outputOp.getName() && !sigSet.insert(*outputOp.getName()).second)
510  return outputOp.emitError("redefinition of signal named '")
511  << *outputOp.getName() << "'!";
512 
513  return success();
514  })
515  .Case<InstOp>([&](auto instOp) -> WalkResult {
516  if (!instSet.insert(instOp.getName()).second)
517  return instOp.emitError("redefinition of instance named '")
518  << instOp.getName() << "'!";
519 
520  return success();
521  })
522  .Default([](auto op) -> WalkResult { return WalkResult::advance(); });
523  });
524 
525  return failure(walkResult.wasInterrupted());
526 }
527 
528 /// Returns the argument types of this function.
529 ArrayRef<Type> llhd::EntityOp::getArgumentTypes() {
530  return getFunctionType().getInputs();
531 }
532 
533 /// Returns the result types of this function.
534 ArrayRef<Type> llhd::EntityOp::getResultTypes() {
535  return getFunctionType().getResults();
536 }
537 
538 Region *llhd::EntityOp::getCallableRegion() {
539  return isExternal() ? nullptr : &getBody();
540 }
541 
542 //===----------------------------------------------------------------------===//
543 // ProcOp
544 //===----------------------------------------------------------------------===//
545 
546 LogicalResult circt::llhd::ProcOp::verifyType() {
547  // Fail if function returns more than zero values. This is because the
548  // outputs of a process are specially marked arguments.
549  if (getNumResults() > 0) {
550  return emitOpError(
551  "process has more than zero return types, this is not allowed");
552  }
553 
554  // Check that all operands are of signal type
555  for (int i = 0, e = getNumArguments(); i < e; ++i) {
556  if (!isa<llhd::SigType>(getArgument(i).getType())) {
557  return emitOpError("usage of invalid argument type, was ")
558  << getArgument(i).getType() << ", expected LLHD signal type";
559  }
560  }
561  return success();
562 }
563 
564 /// Returns the argument types of this function.
565 ArrayRef<Type> llhd::ProcOp::getArgumentTypes() {
566  return getFunctionType().getInputs();
567 }
568 
569 /// Returns the result types of this function.
570 ArrayRef<Type> llhd::ProcOp::getResultTypes() {
571  return getFunctionType().getResults();
572 }
573 
574 LogicalResult circt::llhd::ProcOp::verifyBody() { return success(); }
575 
576 LogicalResult llhd::ProcOp::verify() {
577  // Check that the ins attribute is smaller or equal the number of
578  // arguments
579  uint64_t numArgs = getNumArguments();
580  uint64_t numIns = getInsAttr().getInt();
581  if (numArgs < numIns) {
582  return emitOpError(
583  "Cannot have more inputs than arguments, expected at most ")
584  << numArgs << ", got " << numIns;
585  }
586  return success();
587 }
588 
589 static ParseResult
590 parseProcArgumentList(OpAsmParser &parser, SmallVectorImpl<Type> &argTypes,
591  SmallVectorImpl<OpAsmParser::Argument> &argNames) {
592  if (parser.parseLParen())
593  return failure();
594 
595  // The argument list either has to consistently have ssa-id's followed by
596  // types, or just be a type list. It isn't ok to sometimes have SSA ID's
597  // and sometimes not.
598  auto parseArgument = [&]() -> ParseResult {
599  llvm::SMLoc loc = parser.getCurrentLocation();
600 
601  // Parse argument name if present.
602  OpAsmParser::Argument argument;
603  Type argumentType;
604  auto optArg = parser.parseOptionalArgument(argument);
605  if (optArg.has_value()) {
606  if (succeeded(optArg.value())) {
607  // Reject this if the preceding argument was missing a name.
608  if (argNames.empty() && !argTypes.empty())
609  return parser.emitError(loc,
610  "expected type instead of SSA identifier");
611  argNames.push_back(argument);
612 
613  if (parser.parseColonType(argumentType))
614  return failure();
615  } else if (!argNames.empty()) {
616  // Reject this if the preceding argument had a name.
617  return parser.emitError(loc, "expected SSA identifier");
618  } else if (parser.parseType(argumentType)) {
619  return failure();
620  }
621  }
622 
623  // Add the argument type.
624  argTypes.push_back(argumentType);
625  argNames.back().type = argumentType;
626 
627  return success();
628  };
629 
630  // Parse the function arguments.
631  if (failed(parser.parseOptionalRParen())) {
632  do {
633  unsigned numTypedArguments = argTypes.size();
634  if (parseArgument())
635  return failure();
636 
637  llvm::SMLoc loc = parser.getCurrentLocation();
638  if (argTypes.size() == numTypedArguments &&
639  succeeded(parser.parseOptionalComma()))
640  return parser.emitError(loc, "variadic arguments are not allowed");
641  } while (succeeded(parser.parseOptionalComma()));
642  if (parser.parseRParen())
643  return failure();
644  }
645 
646  return success();
647 }
648 
649 ParseResult llhd::ProcOp::parse(OpAsmParser &parser, OperationState &result) {
650  StringAttr procName;
651  SmallVector<OpAsmParser::Argument, 8> argNames;
652  SmallVector<Type, 8> argTypes;
653  Builder &builder = parser.getBuilder();
654 
655  if (parser.parseSymbolName(procName, SymbolTable::getSymbolAttrName(),
656  result.attributes))
657  return failure();
658 
659  if (parseProcArgumentList(parser, argTypes, argNames))
660  return failure();
661 
662  result.addAttribute("ins", builder.getI64IntegerAttr(argTypes.size()));
663  if (parser.parseArrow())
664  return failure();
665 
666  if (parseProcArgumentList(parser, argTypes, argNames))
667  return failure();
668 
669  auto type = builder.getFunctionType(argTypes, std::nullopt);
670  result.addAttribute(circt::llhd::ProcOp::getFunctionTypeAttrName(result.name),
671  TypeAttr::get(type));
672 
673  auto *body = result.addRegion();
674  if (parser.parseRegion(*body, argNames))
675  return failure();
676 
677  return success();
678 }
679 
680 /// Print the signature of the `proc` unit. Assumes that it passed the
681 /// verification.
682 static void printProcArguments(OpAsmPrinter &p, Operation *op,
683  ArrayRef<Type> types, uint64_t numIns) {
684  Region &body = op->getRegion(0);
685  auto printList = [&](unsigned i, unsigned max) -> void {
686  for (; i < max; ++i) {
687  p << body.front().getArgument(i) << " : " << types[i];
688  p.printOptionalAttrDict(::mlir::function_interface_impl::getArgAttrs(
689  cast<mlir::FunctionOpInterface>(op), i));
690 
691  if (i < max - 1)
692  p << ", ";
693  }
694  };
695 
696  p << '(';
697  printList(0, numIns);
698  p << ") -> (";
699  printList(numIns, types.size());
700  p << ')';
701 }
702 
703 void llhd::ProcOp::print(OpAsmPrinter &printer) {
704  FunctionType type = getFunctionType();
705  printer << ' ';
706  printer.printSymbolName(getName());
707  printProcArguments(printer, getOperation(), type.getInputs(),
708  getInsAttr().getInt());
709  printer << " ";
710  printer.printRegion(getBody(), false, true);
711 }
712 
713 Region *llhd::ProcOp::getCallableRegion() {
714  return isExternal() ? nullptr : &getBody();
715 }
716 
717 //===----------------------------------------------------------------------===//
718 // InstOp
719 //===----------------------------------------------------------------------===//
720 
721 LogicalResult llhd::InstOp::verify() {
722  // Check that the callee attribute was specified.
723  auto calleeAttr = (*this)->getAttrOfType<FlatSymbolRefAttr>("callee");
724  if (!calleeAttr)
725  return emitOpError("requires a 'callee' symbol reference attribute");
726 
727  auto proc = (*this)->getParentOfType<ModuleOp>().lookupSymbol<llhd::ProcOp>(
728  calleeAttr.getValue());
729  if (proc) {
730  auto type = proc.getFunctionType();
731 
732  if (proc.getIns() != getInputs().size())
733  return emitOpError("incorrect number of inputs for proc instantiation");
734 
735  if (type.getNumInputs() != getNumOperands())
736  return emitOpError("incorrect number of outputs for proc instantiation");
737 
738  for (size_t i = 0, e = type.getNumInputs(); i != e; ++i) {
739  if (getOperand(i).getType() != type.getInput(i))
740  return emitOpError("operand type mismatch");
741  }
742 
743  return success();
744  }
745 
746  auto entity =
747  (*this)->getParentOfType<ModuleOp>().lookupSymbol<llhd::EntityOp>(
748  calleeAttr.getValue());
749  if (entity) {
750  auto type = entity.getFunctionType();
751 
752  if (entity.getIns() != getInputs().size())
753  return emitOpError("incorrect number of inputs for entity instantiation");
754 
755  if (type.getNumInputs() != getNumOperands())
756  return emitOpError(
757  "incorrect number of outputs for entity instantiation");
758 
759  for (size_t i = 0, e = type.getNumInputs(); i != e; ++i) {
760  if (getOperand(i).getType() != type.getInput(i))
761  return emitOpError("operand type mismatch");
762  }
763 
764  return success();
765  }
766 
767  auto module =
768  (*this)->getParentOfType<ModuleOp>().lookupSymbol<hw::HWModuleOp>(
769  calleeAttr.getValue());
770  if (module) {
771 
772  if (module.getNumInputPorts() != getInputs().size())
773  return emitOpError(
774  "incorrect number of inputs for hw.module instantiation");
775 
776  if (module.getNumOutputPorts() + module.getNumInputPorts() !=
777  getNumOperands())
778  return emitOpError(
779  "incorrect number of outputs for hw.module instantiation");
780 
781  // Check input types
782  for (size_t i = 0, e = module.getNumInputPorts(); i != e; ++i) {
783  if (cast<llhd::SigType>(getOperand(i).getType()).getUnderlyingType() !=
784  module.getInputTypes()[i])
785  return emitOpError("input type mismatch");
786  }
787 
788  // Check output types
789  for (size_t i = 0, e = module.getNumOutputPorts(); i != e; ++i) {
790  if (cast<llhd::SigType>(
791  getOperand(module.getNumInputPorts() + i).getType())
792  .getUnderlyingType() != module.getOutputTypes()[i])
793  return emitOpError("output type mismatch");
794  }
795 
796  return success();
797  }
798 
799  return emitOpError()
800  << "'" << calleeAttr.getValue()
801  << "' does not reference a valid proc, entity, or hw.module";
802 }
803 
804 FunctionType llhd::InstOp::getCalleeType() {
805  SmallVector<Type, 8> argTypes(getOperandTypes());
806  return FunctionType::get(getContext(), argTypes, ArrayRef<Type>());
807 }
808 
809 //===----------------------------------------------------------------------===//
810 // ConnectOp
811 //===----------------------------------------------------------------------===//
812 
813 LogicalResult llhd::ConnectOp::canonicalize(llhd::ConnectOp op,
814  PatternRewriter &rewriter) {
815  if (op.getLhs() == op.getRhs())
816  rewriter.eraseOp(op);
817  return success();
818 }
819 
820 //===----------------------------------------------------------------------===//
821 // RegOp
822 //===----------------------------------------------------------------------===//
823 
824 ParseResult llhd::RegOp::parse(OpAsmParser &parser, OperationState &result) {
825  OpAsmParser::UnresolvedOperand signal;
826  Type signalType;
827  SmallVector<OpAsmParser::UnresolvedOperand, 8> valueOperands;
828  SmallVector<OpAsmParser::UnresolvedOperand, 8> triggerOperands;
829  SmallVector<OpAsmParser::UnresolvedOperand, 8> delayOperands;
830  SmallVector<OpAsmParser::UnresolvedOperand, 8> gateOperands;
831  SmallVector<Type, 8> valueTypes;
832  llvm::SmallVector<int64_t, 8> modesArray;
833  llvm::SmallVector<int64_t, 8> gateMask;
834  int64_t gateCount = 0;
835 
836  if (parser.parseOperand(signal))
837  return failure();
838  while (succeeded(parser.parseOptionalComma())) {
839  OpAsmParser::UnresolvedOperand value;
840  OpAsmParser::UnresolvedOperand trigger;
841  OpAsmParser::UnresolvedOperand delay;
842  OpAsmParser::UnresolvedOperand gate;
843  Type valueType;
844  StringAttr modeAttr;
845  NamedAttrList attrStorage;
846 
847  if (parser.parseLParen())
848  return failure();
849  if (parser.parseOperand(value) || parser.parseComma())
850  return failure();
851  if (parser.parseAttribute(modeAttr, parser.getBuilder().getNoneType(),
852  "modes", attrStorage))
853  return failure();
854  auto attrOptional = llhd::symbolizeRegMode(modeAttr.getValue());
855  if (!attrOptional)
856  return parser.emitError(parser.getCurrentLocation(),
857  "invalid string attribute");
858  modesArray.push_back(static_cast<int64_t>(*attrOptional));
859  if (parser.parseOperand(trigger))
860  return failure();
861  if (parser.parseKeyword("after") || parser.parseOperand(delay))
862  return failure();
863  if (succeeded(parser.parseOptionalKeyword("if"))) {
864  gateMask.push_back(++gateCount);
865  if (parser.parseOperand(gate))
866  return failure();
867  gateOperands.push_back(gate);
868  } else {
869  gateMask.push_back(0);
870  }
871  if (parser.parseColon() || parser.parseType(valueType) ||
872  parser.parseRParen())
873  return failure();
874  valueOperands.push_back(value);
875  triggerOperands.push_back(trigger);
876  delayOperands.push_back(delay);
877  valueTypes.push_back(valueType);
878  }
879  if (parser.parseOptionalAttrDict(result.attributes) || parser.parseColon() ||
880  parser.parseType(signalType))
881  return failure();
882  if (parser.resolveOperand(signal, signalType, result.operands))
883  return failure();
884  if (parser.resolveOperands(valueOperands, valueTypes,
885  parser.getCurrentLocation(), result.operands))
886  return failure();
887  for (auto operand : triggerOperands)
888  if (parser.resolveOperand(operand, parser.getBuilder().getI1Type(),
889  result.operands))
890  return failure();
891  for (auto operand : delayOperands)
892  if (parser.resolveOperand(
893  operand, llhd::TimeType::get(parser.getBuilder().getContext()),
894  result.operands))
895  return failure();
896  for (auto operand : gateOperands)
897  if (parser.resolveOperand(operand, parser.getBuilder().getI1Type(),
898  result.operands))
899  return failure();
900  result.addAttribute("gateMask",
901  parser.getBuilder().getI64ArrayAttr(gateMask));
902  result.addAttribute("modes", parser.getBuilder().getI64ArrayAttr(modesArray));
903  llvm::SmallVector<int32_t, 5> operandSizes;
904  operandSizes.push_back(1);
905  operandSizes.push_back(valueOperands.size());
906  operandSizes.push_back(triggerOperands.size());
907  operandSizes.push_back(delayOperands.size());
908  operandSizes.push_back(gateOperands.size());
909  result.addAttribute("operandSegmentSizes",
910  parser.getBuilder().getDenseI32ArrayAttr(operandSizes));
911 
912  return success();
913 }
914 
915 void llhd::RegOp::print(OpAsmPrinter &printer) {
916  printer << " " << getSignal();
917  for (size_t i = 0, e = getValues().size(); i < e; ++i) {
918  std::optional<llhd::RegMode> mode = llhd::symbolizeRegMode(
919  cast<IntegerAttr>(getModes().getValue()[i]).getInt());
920  if (!mode) {
921  emitError("invalid RegMode");
922  return;
923  }
924  printer << ", (" << getValues()[i] << ", \""
925  << llhd::stringifyRegMode(*mode) << "\" " << getTriggers()[i]
926  << " after " << getDelays()[i];
927  if (hasGate(i))
928  printer << " if " << getGateAt(i);
929  printer << " : " << getValues()[i].getType() << ")";
930  }
931  printer.printOptionalAttrDict((*this)->getAttrs(),
932  {"modes", "gateMask", "operandSegmentSizes"});
933  printer << " : " << getSignal().getType();
934 }
935 
936 LogicalResult llhd::RegOp::verify() {
937  // At least one trigger has to be present
938  if (getTriggers().size() < 1)
939  return emitError("At least one trigger quadruple has to be present.");
940 
941  // Values variadic operand must have the same size as the triggers variadic
942  if (getValues().size() != getTriggers().size())
943  return emitOpError("Number of 'values' is not equal to the number of "
944  "'triggers', got ")
945  << getValues().size() << " modes, but " << getTriggers().size()
946  << " triggers!";
947 
948  // Delay variadic operand must have the same size as the triggers variadic
949  if (getDelays().size() != getTriggers().size())
950  return emitOpError("Number of 'delays' is not equal to the number of "
951  "'triggers', got ")
952  << getDelays().size() << " modes, but " << getTriggers().size()
953  << " triggers!";
954 
955  // Array Attribute of RegModes must have the same number of elements as the
956  // variadics
957  if (getModes().size() != getTriggers().size())
958  return emitOpError("Number of 'modes' is not equal to the number of "
959  "'triggers', got ")
960  << getModes().size() << " modes, but " << getTriggers().size()
961  << " triggers!";
962 
963  // Array Attribute 'gateMask' must have the same number of elements as the
964  // triggers and values variadics
965  if (getGateMask().size() != getTriggers().size())
966  return emitOpError("Size of 'gateMask' is not equal to the size of "
967  "'triggers', got ")
968  << getGateMask().size() << " modes, but " << getTriggers().size()
969  << " triggers!";
970 
971  // Number of non-zero elements in 'gateMask' has to be the same as the size
972  // of the gates variadic, also each number from 1 to size-1 has to occur
973  // only once and in increasing order
974  unsigned counter = 0;
975  unsigned prevElement = 0;
976  for (Attribute maskElem : getGateMask().getValue()) {
977  int64_t val = cast<IntegerAttr>(maskElem).getInt();
978  if (val < 0)
979  return emitError("Element in 'gateMask' must not be negative!");
980  if (val == 0)
981  continue;
982  if (val != ++prevElement)
983  return emitError(
984  "'gateMask' has to contain every number from 1 to the "
985  "number of gates minus one exactly once in increasing order "
986  "(may have zeros in-between).");
987  counter++;
988  }
989  if (getGates().size() != counter)
990  return emitError("The number of non-zero elements in 'gateMask' and the "
991  "size of the 'gates' variadic have to match.");
992 
993  // Each value must be either the same type as the 'signal' or the underlying
994  // type of the 'signal'
995  for (auto val : getValues()) {
996  if (val.getType() != getSignal().getType() &&
997  val.getType() !=
998  cast<llhd::SigType>(getSignal().getType()).getUnderlyingType()) {
999  return emitOpError(
1000  "type of each 'value' has to be either the same as the "
1001  "type of 'signal' or the underlying type of 'signal'");
1002  }
1003  }
1004  return success();
1005 }
1006 
1007 #include "circt/Dialect/LLHD/IR/LLHDEnums.cpp.inc"
1008 
1009 #define GET_OP_CLASSES
1010 #include "circt/Dialect/LLHD/IR/LLHD.cpp.inc"
assert(baseType &&"element must be base type")
static Attribute constFoldTernaryOp(ArrayRef< Attribute > operands, const CalculationT &calculate)
Definition: LLHDOps.cpp:69
static ParseResult parseArgumentList(OpAsmParser &parser, SmallVectorImpl< OpAsmParser::Argument > &args, SmallVectorImpl< Type > &argTypes)
Parse an argument list of an entity operation.
Definition: LLHDOps.cpp:349
static void printProcArguments(OpAsmPrinter &p, Operation *op, ArrayRef< Type > types, uint64_t numIns)
Print the signature of the proc unit.
Definition: LLHDOps.cpp:682
static LogicalResult inferReturnTypesOfStructExtractOp(MLIRContext *context, std::optional< Location > loc, ValueRange operands, DictionaryAttr attrs, mlir::OpaqueProperties properties, mlir::RegionRange regions, SmallVectorImpl< Type > &results)
Definition: LLHDOps.cpp:266
static void printArgumentList(OpAsmPrinter &printer, std::vector< BlockArgument > args)
Definition: LLHDOps.cpp:420
static OpFoldResult foldSigPtrArraySliceOp(Op op, ArrayRef< Attribute > operands)
Definition: LLHDOps.cpp:206
static LogicalResult canonicalizeSigPtrArraySliceOp(Op op, PatternRewriter &rewriter)
Definition: LLHDOps.cpp:228
static Attribute constFoldUnaryOp(ArrayRef< Attribute > operands, const CalculationT &calculate)
Definition: LLHDOps.cpp:38
static OpFoldResult foldSigPtrExtractOp(Op op, ArrayRef< Attribute > operands)
Definition: LLHDOps.cpp:180
static ParseResult parseProcArgumentList(OpAsmParser &parser, SmallVectorImpl< Type > &argTypes, SmallVectorImpl< OpAsmParser::Argument > &argNames)
Definition: LLHDOps.cpp:590
static ParseResult parseEntitySignature(OpAsmParser &parser, OperationState &result, SmallVectorImpl< OpAsmParser::Argument > &args, SmallVectorImpl< Type > &argTypes)
parse an entity signature with syntax: (arg0 : T0, arg1 : T1, <...>) -> (out0 : T0,...
Definition: LLHDOps.cpp:376
Builder builder
static std::optional< APInt > getInt(Value value)
Helper to convert a value to a constant integer if it is one.
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition: CalyxOps.cpp:54
LogicalResult inferReturnTypes(MLIRContext *context, std::optional< Location > loc, ValueRange operands, DictionaryAttr attrs, mlir::OpaqueProperties properties, mlir::RegionRange regions, SmallVectorImpl< Type > &results, llvm::function_ref< FIRRTLType(ValueRange, ArrayRef< NamedAttribute >, std::optional< Location >)> callback)
StringAttr getName(ArrayAttr names, size_t idx)
Return the name at the specified index of the ArrayAttr or null if it cannot be determined.
unsigned getLLHDTypeWidth(Type type)
Definition: LLHDOps.cpp:132
Type getLLHDElementType(Type type)
Definition: LLHDOps.cpp:144
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition: DebugAnalysis.h:21