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