CIRCT  18.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 = operands[0].dyn_cast<AttrElementT>()) {
45  return AttrElementT::get(val.getType(), calculate(val.getValue()));
46  } else if (auto val = operands[0].dyn_cast<SplatElementsAttr>()) {
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 = operands[0].dyn_cast<ElementsAttr>()) {
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 (operands[0].isa<AttrElementT>() && operands[1].isa<AttrElementT>() &&
76  operands[2].isa<AttrElementT>()) {
77  auto fst = operands[0].cast<AttrElementT>();
78  auto snd = operands[1].cast<AttrElementT>();
79  auto trd = operands[2].cast<AttrElementT>();
80 
81  return AttrElementT::get(
82  fst.getType(),
83  calculate(fst.getValue(), snd.getValue(), trd.getValue()));
84  }
85  if (operands[0].isa<SplatElementsAttr>() &&
86  operands[1].isa<SplatElementsAttr>() &&
87  operands[2].isa<SplatElementsAttr>()) {
88  // Operands are splats so we can avoid expanding the values out and
89  // just fold based on the splat value.
90  auto fst = operands[0].cast<SplatElementsAttr>();
91  auto snd = operands[1].cast<SplatElementsAttr>();
92  auto trd = operands[2].cast<SplatElementsAttr>();
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 (operands[0].isa<ElementsAttr>() && operands[1].isa<ElementsAttr>() &&
100  operands[2].isa<ElementsAttr>()) {
101  // Operands are ElementsAttr-derived; perform an element-wise fold by
102  // expanding the values.
103  auto fst = operands[0].cast<ElementsAttr>();
104  auto snd = operands[1].cast<ElementsAttr>();
105  auto trd = operands[2].cast<ElementsAttr>();
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 = type.dyn_cast<llhd::SigType>())
134  type = sig.getUnderlyingType();
135  else if (auto ptr = type.dyn_cast<llhd::PtrType>())
136  type = ptr.getUnderlyingType();
137  if (auto array = type.dyn_cast<hw::ArrayType>())
138  return array.getNumElements();
139  if (auto tup = type.dyn_cast<hw::StructType>())
140  return tup.getElements().size();
141  return type.getIntOrFloatBitWidth();
142 }
143 
145  if (auto sig = type.dyn_cast<llhd::SigType>())
146  type = sig.getUnderlyingType();
147  else if (auto ptr = type.dyn_cast<llhd::PtrType>())
148  type = ptr.getUnderlyingType();
149  if (auto array = type.dyn_cast<hw::ArrayType>())
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  operands[1].cast<IntegerAttr>().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  operands[1].cast<IntegerAttr>().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 = operands[0]
271  .getType()
272  .cast<SigPtrType>()
273  .getUnderlyingType()
274  .template cast<hw::StructType>()
275  .getFieldType(attrs.getNamed("field")
276  ->getValue()
277  .cast<StringAttr>()
278  .getValue());
279  if (!type) {
280  context->getDiagEngine().emit(loc.value_or(UnknownLoc()),
281  DiagnosticSeverity::Error)
282  << "invalid field name specified";
283  return failure();
284  }
285  results.push_back(SigPtrType::get(type));
286  return success();
287 }
288 
290  MLIRContext *context, std::optional<Location> loc, ValueRange operands,
291  DictionaryAttr attrs, mlir::OpaqueProperties properties,
292  mlir::RegionRange regions, SmallVectorImpl<Type> &results) {
293  return inferReturnTypesOfStructExtractOp<llhd::SigType>(
294  context, loc, operands, attrs, properties, regions, results);
295 }
296 
298  MLIRContext *context, std::optional<Location> loc, ValueRange operands,
299  DictionaryAttr attrs, mlir::OpaqueProperties properties,
300  mlir::RegionRange regions, SmallVectorImpl<Type> &results) {
301  return inferReturnTypesOfStructExtractOp<llhd::PtrType>(
302  context, loc, operands, attrs, properties, regions, results);
303 }
304 
305 //===----------------------------------------------------------------------===//
306 // DrvOp
307 //===----------------------------------------------------------------------===//
308 
309 LogicalResult llhd::DrvOp::fold(FoldAdaptor adaptor,
310  SmallVectorImpl<OpFoldResult> &result) {
311  if (!getEnable())
312  return failure();
313 
314  if (matchPattern(getEnable(), m_One())) {
315  getEnableMutable().clear();
316  return success();
317  }
318 
319  return failure();
320 }
321 
322 LogicalResult llhd::DrvOp::canonicalize(llhd::DrvOp op,
323  PatternRewriter &rewriter) {
324  if (!op.getEnable())
325  return failure();
326 
327  if (matchPattern(op.getEnable(), m_Zero())) {
328  rewriter.eraseOp(op);
329  return success();
330  }
331 
332  return failure();
333 }
334 
335 //===----------------------------------------------------------------------===//
336 // WaitOp
337 //===----------------------------------------------------------------------===//
338 
339 // Implement this operation for the BranchOpInterface
340 SuccessorOperands llhd::WaitOp::getSuccessorOperands(unsigned index) {
341  assert(index == 0 && "invalid successor index");
342  return SuccessorOperands(getDestOpsMutable());
343 }
344 
345 //===----------------------------------------------------------------------===//
346 // EntityOp
347 //===----------------------------------------------------------------------===//
348 
349 /// Parse an argument list of an entity operation.
350 /// The argument list and argument types are returned in args and argTypes
351 /// respectively.
352 static ParseResult
353 parseArgumentList(OpAsmParser &parser,
354  SmallVectorImpl<OpAsmParser::Argument> &args,
355  SmallVectorImpl<Type> &argTypes) {
356  auto parseElt = [&]() -> ParseResult {
357  OpAsmParser::Argument argument;
358  Type argType;
359  auto optArg = parser.parseOptionalArgument(argument);
360  if (optArg.has_value()) {
361  if (succeeded(optArg.value())) {
362  if (!argument.ssaName.name.empty() &&
363  succeeded(parser.parseColonType(argType))) {
364  args.push_back(argument);
365  argTypes.push_back(argType);
366  args.back().type = argType;
367  }
368  }
369  }
370  return success();
371  };
372 
373  return parser.parseCommaSeparatedList(OpAsmParser::Delimiter::Paren,
374  parseElt);
375 }
376 
377 /// parse an entity signature with syntax:
378 /// (%arg0 : T0, %arg1 : T1, <...>) -> (%out0 : T0, %out1 : T1, <...>)
379 static ParseResult
380 parseEntitySignature(OpAsmParser &parser, OperationState &result,
381  SmallVectorImpl<OpAsmParser::Argument> &args,
382  SmallVectorImpl<Type> &argTypes) {
383  if (parseArgumentList(parser, args, argTypes))
384  return failure();
385  // create the integer attribute with the number of inputs.
386  IntegerAttr insAttr = parser.getBuilder().getI64IntegerAttr(args.size());
387  result.addAttribute("ins", insAttr);
388  if (succeeded(parser.parseOptionalArrow()))
389  if (parseArgumentList(parser, args, argTypes))
390  return failure();
391 
392  return success();
393 }
394 
395 ParseResult llhd::EntityOp::parse(OpAsmParser &parser, OperationState &result) {
396  StringAttr entityName;
397  SmallVector<OpAsmParser::Argument, 4> args;
398  SmallVector<Type, 4> argTypes;
399 
400  if (parser.parseSymbolName(entityName, SymbolTable::getSymbolAttrName(),
401  result.attributes))
402  return failure();
403 
404  if (parseEntitySignature(parser, result, args, argTypes))
405  return failure();
406 
407  if (parser.parseOptionalAttrDictWithKeyword(result.attributes))
408  return failure();
409 
410  auto type = parser.getBuilder().getFunctionType(argTypes, std::nullopt);
411  result.addAttribute(
412  circt::llhd::EntityOp::getFunctionTypeAttrName(result.name),
413  TypeAttr::get(type));
414 
415  auto &body = *result.addRegion();
416  if (parser.parseRegion(body, args))
417  return failure();
418  if (body.empty())
419  body.push_back(std::make_unique<Block>().release());
420 
421  return success();
422 }
423 
424 static void printArgumentList(OpAsmPrinter &printer,
425  std::vector<BlockArgument> args) {
426  printer << "(";
427  llvm::interleaveComma(args, printer, [&](BlockArgument arg) {
428  printer << arg << " : " << arg.getType();
429  });
430  printer << ")";
431 }
432 
433 void llhd::EntityOp::print(OpAsmPrinter &printer) {
434  std::vector<BlockArgument> ins, outs;
435  uint64_t nIns = getInsAttr().getInt();
436  for (uint64_t i = 0; i < getBody().front().getArguments().size(); ++i) {
437  // no furter verification for the attribute type is required, already
438  // handled by verify.
439  if (i < nIns) {
440  ins.push_back(getBody().front().getArguments()[i]);
441  } else {
442  outs.push_back(getBody().front().getArguments()[i]);
443  }
444  }
445  auto entityName =
446  (*this)
447  ->getAttrOfType<StringAttr>(SymbolTable::getSymbolAttrName())
448  .getValue();
449  printer << " ";
450  printer.printSymbolName(entityName);
451  printer << " ";
452  printArgumentList(printer, ins);
453  printer << " -> ";
454  printArgumentList(printer, outs);
455  printer.printOptionalAttrDictWithKeyword(
456  (*this)->getAttrs(),
457  /*elidedAttrs =*/{SymbolTable::getSymbolAttrName(),
458  getFunctionTypeAttrName(), "ins"});
459  printer << " ";
460  printer.printRegion(getBody(), false, false);
461 }
462 
463 LogicalResult llhd::EntityOp::verify() {
464  uint64_t numArgs = getNumArguments();
465  uint64_t nIns = getInsAttr().getInt();
466  // check that there is at most one flag for each argument
467  if (numArgs < nIns) {
468  return emitError(
469  "Cannot have more inputs than arguments, expected at most ")
470  << numArgs << " but got: " << nIns;
471  }
472 
473  // Check that all block arguments are of signal type
474  for (size_t i = 0; i < numArgs; ++i)
475  if (!getArgument(i).getType().isa<llhd::SigType>())
476  return emitError("usage of invalid argument type. Got ")
477  << getArgument(i).getType() << ", expected LLHD signal type";
478 
479  return success();
480 }
481 
482 LogicalResult circt::llhd::EntityOp::verifyType() {
483  FunctionType type = getFunctionType();
484 
485  // Fail if function returns any values. An entity's outputs are specially
486  // marked arguments.
487  if (type.getNumResults() > 0)
488  return emitOpError("an entity cannot have return types.");
489 
490  // Check that all operands are of signal type
491  for (Type inputType : type.getInputs())
492  if (!inputType.isa<llhd::SigType>())
493  return emitOpError("usage of invalid argument type. Got ")
494  << inputType << ", expected LLHD signal type";
495 
496  return success();
497 }
498 
499 LogicalResult circt::llhd::EntityOp::verifyBody() {
500  // check signal names are unique
501  llvm::StringSet<> sigSet;
502  llvm::StringSet<> instSet;
503  auto walkResult = walk([&sigSet, &instSet](Operation *op) -> WalkResult {
504  return TypeSwitch<Operation *, WalkResult>(op)
505  .Case<SigOp>([&](auto sigOp) -> WalkResult {
506  if (!sigSet.insert(sigOp.getName()).second)
507  return sigOp.emitError("redefinition of signal named '")
508  << sigOp.getName() << "'!";
509 
510  return success();
511  })
512  .Case<OutputOp>([&](auto outputOp) -> WalkResult {
513  if (outputOp.getName() && !sigSet.insert(*outputOp.getName()).second)
514  return outputOp.emitError("redefinition of signal named '")
515  << *outputOp.getName() << "'!";
516 
517  return success();
518  })
519  .Case<InstOp>([&](auto instOp) -> WalkResult {
520  if (!instSet.insert(instOp.getName()).second)
521  return instOp.emitError("redefinition of instance named '")
522  << instOp.getName() << "'!";
523 
524  return success();
525  })
526  .Default([](auto op) -> WalkResult { return WalkResult::advance(); });
527  });
528 
529  return failure(walkResult.wasInterrupted());
530 }
531 
532 /// Returns the argument types of this function.
533 ArrayRef<Type> llhd::EntityOp::getArgumentTypes() {
534  return getFunctionType().getInputs();
535 }
536 
537 /// Returns the result types of this function.
538 ArrayRef<Type> llhd::EntityOp::getResultTypes() {
539  return getFunctionType().getResults();
540 }
541 
542 Region *llhd::EntityOp::getCallableRegion() {
543  return isExternal() ? nullptr : &getBody();
544 }
545 
546 //===----------------------------------------------------------------------===//
547 // ProcOp
548 //===----------------------------------------------------------------------===//
549 
550 LogicalResult circt::llhd::ProcOp::verifyType() {
551  // Fail if function returns more than zero values. This is because the
552  // outputs of a process are specially marked arguments.
553  if (getNumResults() > 0) {
554  return emitOpError(
555  "process has more than zero return types, this is not allowed");
556  }
557 
558  // Check that all operands are of signal type
559  for (int i = 0, e = getNumArguments(); i < e; ++i) {
560  if (!getArgument(i).getType().isa<llhd::SigType>()) {
561  return emitOpError("usage of invalid argument type, was ")
562  << getArgument(i).getType() << ", expected LLHD signal type";
563  }
564  }
565  return success();
566 }
567 
568 /// Returns the argument types of this function.
569 ArrayRef<Type> llhd::ProcOp::getArgumentTypes() {
570  return getFunctionType().getInputs();
571 }
572 
573 /// Returns the result types of this function.
574 ArrayRef<Type> llhd::ProcOp::getResultTypes() {
575  return getFunctionType().getResults();
576 }
577 
578 LogicalResult circt::llhd::ProcOp::verifyBody() { return success(); }
579 
580 LogicalResult llhd::ProcOp::verify() {
581  // Check that the ins attribute is smaller or equal the number of
582  // arguments
583  uint64_t numArgs = getNumArguments();
584  uint64_t numIns = getInsAttr().getInt();
585  if (numArgs < numIns) {
586  return emitOpError(
587  "Cannot have more inputs than arguments, expected at most ")
588  << numArgs << ", got " << numIns;
589  }
590  return success();
591 }
592 
593 static ParseResult
594 parseProcArgumentList(OpAsmParser &parser, SmallVectorImpl<Type> &argTypes,
595  SmallVectorImpl<OpAsmParser::Argument> &argNames) {
596  if (parser.parseLParen())
597  return failure();
598 
599  // The argument list either has to consistently have ssa-id's followed by
600  // types, or just be a type list. It isn't ok to sometimes have SSA ID's
601  // and sometimes not.
602  auto parseArgument = [&]() -> ParseResult {
603  llvm::SMLoc loc = parser.getCurrentLocation();
604 
605  // Parse argument name if present.
606  OpAsmParser::Argument argument;
607  Type argumentType;
608  auto optArg = parser.parseOptionalArgument(argument);
609  if (optArg.has_value()) {
610  if (succeeded(optArg.value())) {
611  // Reject this if the preceding argument was missing a name.
612  if (argNames.empty() && !argTypes.empty())
613  return parser.emitError(loc,
614  "expected type instead of SSA identifier");
615  argNames.push_back(argument);
616 
617  if (parser.parseColonType(argumentType))
618  return failure();
619  } else if (!argNames.empty()) {
620  // Reject this if the preceding argument had a name.
621  return parser.emitError(loc, "expected SSA identifier");
622  } else if (parser.parseType(argumentType)) {
623  return failure();
624  }
625  }
626 
627  // Add the argument type.
628  argTypes.push_back(argumentType);
629  argNames.back().type = argumentType;
630 
631  return success();
632  };
633 
634  // Parse the function arguments.
635  if (failed(parser.parseOptionalRParen())) {
636  do {
637  unsigned numTypedArguments = argTypes.size();
638  if (parseArgument())
639  return failure();
640 
641  llvm::SMLoc loc = parser.getCurrentLocation();
642  if (argTypes.size() == numTypedArguments &&
643  succeeded(parser.parseOptionalComma()))
644  return parser.emitError(loc, "variadic arguments are not allowed");
645  } while (succeeded(parser.parseOptionalComma()));
646  if (parser.parseRParen())
647  return failure();
648  }
649 
650  return success();
651 }
652 
653 ParseResult llhd::ProcOp::parse(OpAsmParser &parser, OperationState &result) {
654  StringAttr procName;
655  SmallVector<OpAsmParser::Argument, 8> argNames;
656  SmallVector<Type, 8> argTypes;
657  Builder &builder = parser.getBuilder();
658 
659  if (parser.parseSymbolName(procName, SymbolTable::getSymbolAttrName(),
660  result.attributes))
661  return failure();
662 
663  if (parseProcArgumentList(parser, argTypes, argNames))
664  return failure();
665 
666  result.addAttribute("ins", builder.getI64IntegerAttr(argTypes.size()));
667  if (parser.parseArrow())
668  return failure();
669 
670  if (parseProcArgumentList(parser, argTypes, argNames))
671  return failure();
672 
673  auto type = builder.getFunctionType(argTypes, std::nullopt);
674  result.addAttribute(circt::llhd::ProcOp::getFunctionTypeAttrName(result.name),
675  TypeAttr::get(type));
676 
677  auto *body = result.addRegion();
678  if (parser.parseRegion(*body, argNames))
679  return failure();
680 
681  return success();
682 }
683 
684 /// Print the signature of the `proc` unit. Assumes that it passed the
685 /// verification.
686 static void printProcArguments(OpAsmPrinter &p, Operation *op,
687  ArrayRef<Type> types, uint64_t numIns) {
688  Region &body = op->getRegion(0);
689  auto printList = [&](unsigned i, unsigned max) -> void {
690  for (; i < max; ++i) {
691  p << body.front().getArgument(i) << " : " << types[i];
692  p.printOptionalAttrDict(::mlir::function_interface_impl::getArgAttrs(
693  cast<mlir::FunctionOpInterface>(op), i));
694 
695  if (i < max - 1)
696  p << ", ";
697  }
698  };
699 
700  p << '(';
701  printList(0, numIns);
702  p << ") -> (";
703  printList(numIns, types.size());
704  p << ')';
705 }
706 
707 void llhd::ProcOp::print(OpAsmPrinter &printer) {
708  FunctionType type = getFunctionType();
709  printer << ' ';
710  printer.printSymbolName(getName());
711  printProcArguments(printer, getOperation(), type.getInputs(),
712  getInsAttr().getInt());
713  printer << " ";
714  printer.printRegion(getBody(), false, true);
715 }
716 
717 Region *llhd::ProcOp::getCallableRegion() {
718  return isExternal() ? nullptr : &getBody();
719 }
720 
721 //===----------------------------------------------------------------------===//
722 // InstOp
723 //===----------------------------------------------------------------------===//
724 
725 LogicalResult llhd::InstOp::verify() {
726  // Check that the callee attribute was specified.
727  auto calleeAttr = (*this)->getAttrOfType<FlatSymbolRefAttr>("callee");
728  if (!calleeAttr)
729  return emitOpError("requires a 'callee' symbol reference attribute");
730 
731  auto proc = (*this)->getParentOfType<ModuleOp>().lookupSymbol<llhd::ProcOp>(
732  calleeAttr.getValue());
733  if (proc) {
734  auto type = proc.getFunctionType();
735 
736  if (proc.getIns() != getInputs().size())
737  return emitOpError("incorrect number of inputs for proc instantiation");
738 
739  if (type.getNumInputs() != getNumOperands())
740  return emitOpError("incorrect number of outputs for proc instantiation");
741 
742  for (size_t i = 0, e = type.getNumInputs(); i != e; ++i) {
743  if (getOperand(i).getType() != type.getInput(i))
744  return emitOpError("operand type mismatch");
745  }
746 
747  return success();
748  }
749 
750  auto entity =
751  (*this)->getParentOfType<ModuleOp>().lookupSymbol<llhd::EntityOp>(
752  calleeAttr.getValue());
753  if (entity) {
754  auto type = entity.getFunctionType();
755 
756  if (entity.getIns() != getInputs().size())
757  return emitOpError("incorrect number of inputs for entity instantiation");
758 
759  if (type.getNumInputs() != getNumOperands())
760  return emitOpError(
761  "incorrect number of outputs for entity instantiation");
762 
763  for (size_t i = 0, e = type.getNumInputs(); i != e; ++i) {
764  if (getOperand(i).getType() != type.getInput(i))
765  return emitOpError("operand type mismatch");
766  }
767 
768  return success();
769  }
770 
771  auto module =
772  (*this)->getParentOfType<ModuleOp>().lookupSymbol<hw::HWModuleOp>(
773  calleeAttr.getValue());
774  if (module) {
775 
776  if (module.getNumInputPorts() != getInputs().size())
777  return emitOpError(
778  "incorrect number of inputs for hw.module instantiation");
779 
780  if (module.getNumOutputPorts() + module.getNumInputPorts() !=
781  getNumOperands())
782  return emitOpError(
783  "incorrect number of outputs for hw.module instantiation");
784 
785  // Check input types
786  for (size_t i = 0, e = module.getNumInputPorts(); i != e; ++i) {
787  if (getOperand(i).getType().cast<llhd::SigType>().getUnderlyingType() !=
788  module.getInputTypes()[i])
789  return emitOpError("input type mismatch");
790  }
791 
792  // Check output types
793  for (size_t i = 0, e = module.getNumOutputPorts(); i != e; ++i) {
794  if (getOperand(module.getNumInputPorts() + i)
795  .getType()
796  .cast<llhd::SigType>()
797  .getUnderlyingType() != module.getOutputTypes()[i])
798  return emitOpError("output type mismatch");
799  }
800 
801  return success();
802  }
803 
804  return emitOpError()
805  << "'" << calleeAttr.getValue()
806  << "' does not reference a valid proc, entity, or hw.module";
807 }
808 
809 FunctionType llhd::InstOp::getCalleeType() {
810  SmallVector<Type, 8> argTypes(getOperandTypes());
811  return FunctionType::get(getContext(), argTypes, ArrayRef<Type>());
812 }
813 
814 //===----------------------------------------------------------------------===//
815 // ConnectOp
816 //===----------------------------------------------------------------------===//
817 
818 LogicalResult llhd::ConnectOp::canonicalize(llhd::ConnectOp op,
819  PatternRewriter &rewriter) {
820  if (op.getLhs() == op.getRhs())
821  rewriter.eraseOp(op);
822  return success();
823 }
824 
825 //===----------------------------------------------------------------------===//
826 // RegOp
827 //===----------------------------------------------------------------------===//
828 
829 ParseResult llhd::RegOp::parse(OpAsmParser &parser, OperationState &result) {
830  OpAsmParser::UnresolvedOperand signal;
831  Type signalType;
832  SmallVector<OpAsmParser::UnresolvedOperand, 8> valueOperands;
833  SmallVector<OpAsmParser::UnresolvedOperand, 8> triggerOperands;
834  SmallVector<OpAsmParser::UnresolvedOperand, 8> delayOperands;
835  SmallVector<OpAsmParser::UnresolvedOperand, 8> gateOperands;
836  SmallVector<Type, 8> valueTypes;
837  llvm::SmallVector<int64_t, 8> modesArray;
838  llvm::SmallVector<int64_t, 8> gateMask;
839  int64_t gateCount = 0;
840 
841  if (parser.parseOperand(signal))
842  return failure();
843  while (succeeded(parser.parseOptionalComma())) {
844  OpAsmParser::UnresolvedOperand value;
845  OpAsmParser::UnresolvedOperand trigger;
846  OpAsmParser::UnresolvedOperand delay;
847  OpAsmParser::UnresolvedOperand gate;
848  Type valueType;
849  StringAttr modeAttr;
850  NamedAttrList attrStorage;
851 
852  if (parser.parseLParen())
853  return failure();
854  if (parser.parseOperand(value) || parser.parseComma())
855  return failure();
856  if (parser.parseAttribute(modeAttr, parser.getBuilder().getNoneType(),
857  "modes", attrStorage))
858  return failure();
859  auto attrOptional = llhd::symbolizeRegMode(modeAttr.getValue());
860  if (!attrOptional)
861  return parser.emitError(parser.getCurrentLocation(),
862  "invalid string attribute");
863  modesArray.push_back(static_cast<int64_t>(*attrOptional));
864  if (parser.parseOperand(trigger))
865  return failure();
866  if (parser.parseKeyword("after") || parser.parseOperand(delay))
867  return failure();
868  if (succeeded(parser.parseOptionalKeyword("if"))) {
869  gateMask.push_back(++gateCount);
870  if (parser.parseOperand(gate))
871  return failure();
872  gateOperands.push_back(gate);
873  } else {
874  gateMask.push_back(0);
875  }
876  if (parser.parseColon() || parser.parseType(valueType) ||
877  parser.parseRParen())
878  return failure();
879  valueOperands.push_back(value);
880  triggerOperands.push_back(trigger);
881  delayOperands.push_back(delay);
882  valueTypes.push_back(valueType);
883  }
884  if (parser.parseOptionalAttrDict(result.attributes) || parser.parseColon() ||
885  parser.parseType(signalType))
886  return failure();
887  if (parser.resolveOperand(signal, signalType, result.operands))
888  return failure();
889  if (parser.resolveOperands(valueOperands, valueTypes,
890  parser.getCurrentLocation(), result.operands))
891  return failure();
892  for (auto operand : triggerOperands)
893  if (parser.resolveOperand(operand, parser.getBuilder().getI1Type(),
894  result.operands))
895  return failure();
896  for (auto operand : delayOperands)
897  if (parser.resolveOperand(
898  operand, llhd::TimeType::get(parser.getBuilder().getContext()),
899  result.operands))
900  return failure();
901  for (auto operand : gateOperands)
902  if (parser.resolveOperand(operand, parser.getBuilder().getI1Type(),
903  result.operands))
904  return failure();
905  result.addAttribute("gateMask",
906  parser.getBuilder().getI64ArrayAttr(gateMask));
907  result.addAttribute("modes", parser.getBuilder().getI64ArrayAttr(modesArray));
908  llvm::SmallVector<int32_t, 5> operandSizes;
909  operandSizes.push_back(1);
910  operandSizes.push_back(valueOperands.size());
911  operandSizes.push_back(triggerOperands.size());
912  operandSizes.push_back(delayOperands.size());
913  operandSizes.push_back(gateOperands.size());
914  result.addAttribute("operandSegmentSizes",
915  parser.getBuilder().getDenseI32ArrayAttr(operandSizes));
916 
917  return success();
918 }
919 
920 void llhd::RegOp::print(OpAsmPrinter &printer) {
921  printer << " " << getSignal();
922  for (size_t i = 0, e = getValues().size(); i < e; ++i) {
923  std::optional<llhd::RegMode> mode = llhd::symbolizeRegMode(
924  getModes().getValue()[i].cast<IntegerAttr>().getInt());
925  if (!mode) {
926  emitError("invalid RegMode");
927  return;
928  }
929  printer << ", (" << getValues()[i] << ", \""
930  << llhd::stringifyRegMode(*mode) << "\" " << getTriggers()[i]
931  << " after " << getDelays()[i];
932  if (hasGate(i))
933  printer << " if " << getGateAt(i);
934  printer << " : " << getValues()[i].getType() << ")";
935  }
936  printer.printOptionalAttrDict((*this)->getAttrs(),
937  {"modes", "gateMask", "operandSegmentSizes"});
938  printer << " : " << getSignal().getType();
939 }
940 
941 LogicalResult llhd::RegOp::verify() {
942  // At least one trigger has to be present
943  if (getTriggers().size() < 1)
944  return emitError("At least one trigger quadruple has to be present.");
945 
946  // Values variadic operand must have the same size as the triggers variadic
947  if (getValues().size() != getTriggers().size())
948  return emitOpError("Number of 'values' is not equal to the number of "
949  "'triggers', got ")
950  << getValues().size() << " modes, but " << getTriggers().size()
951  << " triggers!";
952 
953  // Delay variadic operand must have the same size as the triggers variadic
954  if (getDelays().size() != getTriggers().size())
955  return emitOpError("Number of 'delays' is not equal to the number of "
956  "'triggers', got ")
957  << getDelays().size() << " modes, but " << getTriggers().size()
958  << " triggers!";
959 
960  // Array Attribute of RegModes must have the same number of elements as the
961  // variadics
962  if (getModes().size() != getTriggers().size())
963  return emitOpError("Number of 'modes' is not equal to the number of "
964  "'triggers', got ")
965  << getModes().size() << " modes, but " << getTriggers().size()
966  << " triggers!";
967 
968  // Array Attribute 'gateMask' must have the same number of elements as the
969  // triggers and values variadics
970  if (getGateMask().size() != getTriggers().size())
971  return emitOpError("Size of 'gateMask' is not equal to the size of "
972  "'triggers', got ")
973  << getGateMask().size() << " modes, but " << getTriggers().size()
974  << " triggers!";
975 
976  // Number of non-zero elements in 'gateMask' has to be the same as the size
977  // of the gates variadic, also each number from 1 to size-1 has to occur
978  // only once and in increasing order
979  unsigned counter = 0;
980  unsigned prevElement = 0;
981  for (Attribute maskElem : getGateMask().getValue()) {
982  int64_t val = maskElem.cast<IntegerAttr>().getInt();
983  if (val < 0)
984  return emitError("Element in 'gateMask' must not be negative!");
985  if (val == 0)
986  continue;
987  if (val != ++prevElement)
988  return emitError(
989  "'gateMask' has to contain every number from 1 to the "
990  "number of gates minus one exactly once in increasing order "
991  "(may have zeros in-between).");
992  counter++;
993  }
994  if (getGates().size() != counter)
995  return emitError("The number of non-zero elements in 'gateMask' and the "
996  "size of the 'gates' variadic have to match.");
997 
998  // Each value must be either the same type as the 'signal' or the underlying
999  // type of the 'signal'
1000  for (auto val : getValues()) {
1001  if (val.getType() != getSignal().getType() &&
1002  val.getType() !=
1003  getSignal().getType().cast<llhd::SigType>().getUnderlyingType()) {
1004  return emitOpError(
1005  "type of each 'value' has to be either the same as the "
1006  "type of 'signal' or the underlying type of 'signal'");
1007  }
1008  }
1009  return success();
1010 }
1011 
1012 #include "circt/Dialect/LLHD/IR/LLHDEnums.cpp.inc"
1013 
1014 #define GET_OP_CLASSES
1015 #include "circt/Dialect/LLHD/IR/LLHD.cpp.inc"
lowerAnnotationsNoRefTypePorts FirtoolPreserveValuesMode value
Definition: Firtool.cpp:95
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:353
static void printProcArguments(OpAsmPrinter &p, Operation *op, ArrayRef< Type > types, uint64_t numIns)
Print the signature of the proc unit.
Definition: LLHDOps.cpp:686
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:424
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:594
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:380
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:53
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
This file defines an intermediate representation for circuits acting as an abstraction for constraint...
Definition: DebugAnalysis.h:21
mlir::raw_indented_ostream & outs()
Definition: Utility.h:38