CIRCT  19.0.0git
FIRRTLOpInterfaces.cpp
Go to the documentation of this file.
1 //===- FIRRTLOpInterfaces.cpp - Implement the FIRRTL op interfaces --------===//
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 FIRRTL operation interfaces.
10 //
11 //===----------------------------------------------------------------------===//
12 
15 #include "mlir/IR/BuiltinAttributes.h"
16 #include "mlir/IR/BuiltinTypes.h"
17 #include "mlir/IR/PatternMatch.h"
18 #include "llvm/ADT/SmallBitVector.h"
19 #include "llvm/ADT/StringRef.h"
20 
21 using namespace mlir;
22 using namespace llvm;
23 using namespace circt::firrtl;
24 
25 static LogicalResult verifyNoInputProbes(FModuleLike module) {
26  // Helper to check for input-oriented refs.
27  std::function<bool(Type, bool)> hasInputRef = [&](Type type,
28  bool output) -> bool {
29  auto ftype = type_dyn_cast<FIRRTLType>(type);
30  if (!ftype || !ftype.containsReference())
31  return false;
33  .Case<RefType>([&](auto reftype) { return !output; })
34  .Case<OpenVectorType>([&](OpenVectorType ovt) {
35  return hasInputRef(ovt.getElementType(), output);
36  })
37  .Case<OpenBundleType>([&](OpenBundleType obt) {
38  for (auto field : obt.getElements())
39  if (hasInputRef(field.type, field.isFlip ^ output))
40  return true;
41  return false;
42  });
43  };
44 
45  if (module.isPublic()) {
46  for (auto &pi : module.getPorts()) {
47  if (hasInputRef(pi.type, pi.isOutput()))
48  return emitError(pi.loc, "input probe not allowed on public module");
49  }
50  }
51  return success();
52 }
53 
54 LogicalResult circt::firrtl::verifyModuleLikeOpInterface(FModuleLike module) {
55  // Verify port types first. This is used as the basis for the number of
56  // ports required everywhere else.
57  auto portTypes = module.getPortTypesAttr();
58  if (!portTypes || llvm::any_of(portTypes.getValue(), [](Attribute attr) {
59  return !isa<TypeAttr>(attr);
60  }))
61  return module.emitOpError("requires valid port types");
62 
63  auto numPorts = portTypes.size();
64 
65  // Verify the port dirctions.
66  auto portDirections = module.getPortDirectionsAttr();
67  if (!portDirections)
68  return module.emitOpError("requires valid port direction");
69  // TODO: bitwidth is 1 when there are no ports, since APInt previously did not
70  // support 0 bit widths.
71  auto bitWidth = portDirections.size();
72  if (static_cast<size_t>(bitWidth) != numPorts)
73  return module.emitOpError("requires ") << numPorts << " port directions";
74 
75  // Verify the port names.
76  auto portNames = module.getPortNamesAttr();
77  if (!portNames)
78  return module.emitOpError("requires valid port names");
79  if (portNames.size() != numPorts)
80  return module.emitOpError("requires ") << numPorts << " port names";
81  if (llvm::any_of(portNames.getValue(),
82  [](Attribute attr) { return !isa<StringAttr>(attr); }))
83  return module.emitOpError("port names should all be string attributes");
84 
85  // Verify the port annotations.
86  auto portAnnotations = module.getPortAnnotationsAttr();
87  if (!portAnnotations)
88  return module.emitOpError("requires valid port annotations");
89  // TODO: it seems weird to allow empty port annotations.
90  if (!portAnnotations.empty() && portAnnotations.size() != numPorts)
91  return module.emitOpError("requires ") << numPorts << " port annotations";
92  // TODO: Move this into an annotation verifier.
93  for (auto annos : portAnnotations.getValue()) {
94  auto arrayAttr = dyn_cast<ArrayAttr>(annos);
95  if (!arrayAttr)
96  return module.emitOpError(
97  "requires port annotations be array attributes");
98  if (llvm::any_of(arrayAttr.getValue(),
99  [](Attribute attr) { return !isa<DictionaryAttr>(attr); }))
100  return module.emitOpError(
101  "annotations must be dictionaries or subannotations");
102  }
103 
104  // Verify the port symbols.
105  auto portSymbols = module.getPortSymbolsAttr();
106  if (!portSymbols)
107  return module.emitOpError("requires valid port symbols");
108  if (!portSymbols.empty() && portSymbols.size() != numPorts)
109  return module.emitOpError("requires ") << numPorts << " port symbols";
110  if (llvm::any_of(portSymbols.getValue(), [](Attribute attr) {
111  return !attr || !isa<hw::InnerSymAttr>(attr);
112  }))
113  return module.emitOpError("port symbols should all be InnerSym attributes");
114 
115  // Verify the port locations.
116  auto portLocs = module.getPortLocationsAttr();
117  if (!portLocs)
118  return module.emitOpError("requires valid port locations");
119  if (portLocs.size() != numPorts)
120  return module.emitOpError("requires ") << numPorts << " port locations";
121  if (llvm::any_of(portLocs.getValue(), [](Attribute attr) {
122  return !attr || !isa<LocationAttr>(attr);
123  }))
124  return module.emitOpError("port symbols should all be location attributes");
125 
126  // Verify the body.
127  if (module->getNumRegions() != 1)
128  return module.emitOpError("requires one region");
129 
130  if (failed(verifyNoInputProbes(module)))
131  return failure();
132 
133  return success();
134 }
135 
136 //===----------------------------------------------------------------------===//
137 // Forceable
138 //===----------------------------------------------------------------------===//
139 
141  Type type) {
142  auto base = dyn_cast_or_null<FIRRTLBaseType>(type);
143  // TODO: Find a way to not check same things RefType::get/verify does.
144  if (!forceable || !base || base.containsConst())
145  return {};
146  return circt::firrtl::RefType::get(base.getPassiveType(), forceable);
147 }
148 
149 LogicalResult circt::firrtl::detail::verifyForceableOp(Forceable op) {
150  bool forceable = op.isForceable();
151  auto ref = op.getDataRef();
152  if ((bool)ref != forceable)
153  return op.emitOpError("must have ref result iff marked forceable");
154  if (!forceable)
155  return success();
156  auto data = op.getDataRaw();
157  auto baseType = type_dyn_cast<FIRRTLBaseType>(data.getType());
158  if (!baseType)
159  return op.emitOpError("has data that is not a base type");
160  if (baseType.containsConst())
161  return op.emitOpError("cannot force a declaration of constant type");
162  auto expectedRefType = getForceableResultType(forceable, baseType);
163  if (ref.getType() != expectedRefType)
164  return op.emitOpError("reference result of incorrect type, found ")
165  << ref.getType() << ", expected " << expectedRefType;
166  return success();
167 }
168 
169 namespace {
170 /// Simple wrapper to allow construction from a context for local use.
171 class TrivialPatternRewriter : public PatternRewriter {
172 public:
173  explicit TrivialPatternRewriter(MLIRContext *context)
174  : PatternRewriter(context) {}
175 };
176 } // end namespace
177 
178 Forceable
179 circt::firrtl::detail::replaceWithNewForceability(Forceable op, bool forceable,
180  PatternRewriter *rewriter) {
181  if (forceable == op.isForceable())
182  return op;
183 
184  assert(op->getNumRegions() == 0);
185 
186  // Create copy of this operation with/without the forceable marker + result
187  // type.
188 
189  TrivialPatternRewriter localRewriter(op.getContext());
190  PatternRewriter &rw = rewriter ? *rewriter : localRewriter;
191 
192  // Grab the current operation's results and attributes.
193  SmallVector<Type, 8> resultTypes(op->getResultTypes());
194  SmallVector<NamedAttribute, 16> attributes(op->getAttrs());
195 
196  // Add/remove the optional ref result.
197  auto refType = firrtl::detail::getForceableResultType(true, op.getDataType());
198  if (forceable)
199  resultTypes.push_back(refType);
200  else {
201  assert(resultTypes.back() == refType &&
202  "expected forceable type as last result");
203  resultTypes.pop_back();
204  }
205 
206  // Add/remove the forceable marker.
207  auto forceableMarker =
208  rw.getNamedAttr(op.getForceableAttrName(), rw.getUnitAttr());
209  if (forceable)
210  attributes.push_back(forceableMarker);
211  else {
212  llvm::erase(attributes, forceableMarker);
213  assert(attributes.size() != op->getAttrs().size());
214  }
215 
216  // Create the replacement operation.
217  OperationState state(op.getLoc(), op->getName(), op->getOperands(),
218  resultTypes, attributes, op->getSuccessors());
219  rw.setInsertionPoint(op);
220  auto *replace = rw.create(state);
221 
222  // Dropping forceability (!forceable) -> no uses of forceable ref handle.
223  assert(forceable || op.getDataRef().use_empty());
224 
225  // Replace results.
226  for (auto result : llvm::drop_end(op->getResults(), forceable ? 0 : 1))
227  rw.replaceAllUsesWith(result, replace->getResult(result.getResultNumber()));
228  rw.eraseOp(op);
229  return cast<Forceable>(replace);
230 }
231 
232 #include "circt/Dialect/FIRRTL/FIRRTLOpInterfaces.cpp.inc"
assert(baseType &&"element must be base type")
static LogicalResult verifyNoInputProbes(FModuleLike module)
This class implements the same functionality as TypeSwitch except that it uses firrtl::type_dyn_cast ...
Definition: FIRRTLTypes.h:518
FIRRTLTypeSwitch< T, ResultT > & Case(CallableT &&caseFn)
Add a case on the given type.
Definition: FIRRTLTypes.h:528
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition: CalyxOps.cpp:54
LogicalResult verifyForceableOp(Forceable op)
Verify a Forceable op.
Forceable replaceWithNewForceability(Forceable op, bool forceable, ::mlir::PatternRewriter *rewriter=nullptr)
Replace a Forceable op with equivalent, changing whether forceable.
RefType getForceableResultType(bool forceable, Type type)
Return null or forceable reference result type.
LogicalResult verifyModuleLikeOpInterface(FModuleLike module)
Verification hook for verifying module like operations.