CIRCT 24.0.0git
Loading...
Searching...
No Matches
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
21using namespace mlir;
22using namespace circt::firrtl;
23
24static LogicalResult verifyNoInputProbes(FModuleLike module) {
25 // Helper to check for input-oriented refs.
26 std::function<bool(Type, bool)> hasInputRef = [&](Type type,
27 bool output) -> bool {
28 auto ftype = type_dyn_cast<FIRRTLType>(type);
29 if (!ftype || !ftype.containsReference())
30 return false;
32 .Case<RefType>([&](auto reftype) { return !output; })
33 .Case<OpenVectorType>([&](OpenVectorType ovt) {
34 return hasInputRef(ovt.getElementType(), output);
35 })
36 .Case<OpenBundleType>([&](OpenBundleType obt) {
37 for (auto field : obt.getElements())
38 if (hasInputRef(field.type, field.isFlip ^ output))
39 return true;
40 return false;
41 });
42 };
43
44 for (auto &pi : module.getPorts())
45 if (hasInputRef(pi.type, pi.isOutput()))
46 return emitError(pi.loc, "input probe not allowed");
47 return success();
48}
49
50LogicalResult circt::firrtl::verifyModuleLikeOpInterface(FModuleLike module) {
51 // Verify port types first. This is used as the basis for the number of
52 // ports required everywhere else.
53 auto portTypes = module.getPortTypesAttr();
54 if (!portTypes || llvm::any_of(portTypes.getValue(), [](Attribute attr) {
55 return !isa<TypeAttr>(attr);
56 }))
57 return module.emitOpError("requires valid port types");
58
59 auto numPorts = portTypes.size();
60
61 // Verify the port dirctions.
62 auto portDirections = module.getPortDirectionsAttr();
63 if (!portDirections)
64 return module.emitOpError("requires valid port direction");
65 // TODO: bitwidth is 1 when there are no ports, since APInt previously did not
66 // support 0 bit widths.
67 auto bitWidth = portDirections.size();
68 if (static_cast<size_t>(bitWidth) != numPorts)
69 return module.emitOpError("requires ") << numPorts << " port directions";
70
71 // Verify the port names.
72 auto portNames = module.getPortNamesAttr();
73 if (!portNames)
74 return module.emitOpError("requires valid port names");
75 if (portNames.size() != numPorts)
76 return module.emitOpError("requires ") << numPorts << " port names";
77 if (llvm::any_of(portNames.getValue(),
78 [](Attribute attr) { return !isa<StringAttr>(attr); }))
79 return module.emitOpError("port names should all be string attributes");
80
81 // Verify the port annotations.
82 auto portAnnotations = module.getPortAnnotationsAttr();
83 if (!portAnnotations)
84 return module.emitOpError("requires valid port annotations");
85 // TODO: it seems weird to allow empty port annotations.
86 if (!portAnnotations.empty() && portAnnotations.size() != numPorts)
87 return module.emitOpError("requires ") << numPorts << " port annotations";
88 // TODO: Move this into an annotation verifier.
89 for (auto annos : portAnnotations.getValue()) {
90 auto arrayAttr = dyn_cast<ArrayAttr>(annos);
91 if (!arrayAttr)
92 return module.emitOpError(
93 "requires port annotations be array attributes");
94 if (llvm::any_of(arrayAttr.getValue(),
95 [](Attribute attr) { return !isa<DictionaryAttr>(attr); }))
96 return module.emitOpError(
97 "annotations must be dictionaries or subannotations");
98 }
99
100 // Verify the port symbols.
101 auto portSymbols = module.getPortSymbolsAttr();
102 if (!portSymbols)
103 return module.emitOpError("requires valid port symbols");
104 if (!portSymbols.empty() && portSymbols.size() != numPorts)
105 return module.emitOpError("requires ") << numPorts << " port symbols";
106 if (llvm::any_of(portSymbols.getValue(), [](Attribute attr) {
107 return !attr || !isa<hw::InnerSymAttr>(attr);
108 }))
109 return module.emitOpError("port symbols should all be InnerSym attributes");
110
111 // Verify the port locations.
112 auto portLocs = module.getPortLocationsAttr();
113 if (!portLocs)
114 return module.emitOpError("requires valid port locations");
115 if (portLocs.size() != numPorts)
116 return module.emitOpError("requires ") << numPorts << " port locations";
117 if (llvm::any_of(portLocs.getValue(), [](Attribute attr) {
118 return !attr || !isa<LocationAttr>(attr);
119 }))
120 return module.emitOpError("port symbols should all be location attributes");
121
122 // Verify the port domain associations. This can be either:
123 // 1. An empty ArrayAttr.
124 // 2. An ArrayAttr, one entry-per-port, of ArrayAttr<IntegerAttr> for
125 // associations.
126 //
127 // Note: Domain information is now stored in the DomainType itself, not
128 // in the domainInfo attribute.
129 //
130 // Note: error handling here intentionally does _not_ use port info
131 // locations. This is because if any of these fail, this is almost always
132 // a CIRCT-internal bug. These code paths are essoentially inaccessible
133 // from FIRRTL text.
134 auto domains = module.getDomainInfoAttr();
135 // Domain info cannot be null.
136 if (!domains)
137 return module.emitOpError("requires valid port domain associations");
138 // If non-empty, then one-entry-per-port.
139 if (!domains.empty() && domains.size() != numPorts)
140 return module.emitOpError("requires ")
141 << numPorts << " port domain associations, but has "
142 << domains.size();
143
144 // Domain type ports should have empty associations (domain info is in the
145 // type). Non-domain type ports can have associations.
146 for (auto [index, port] : llvm::enumerate(module.getPorts())) {
147 auto type = cast<TypeAttr>(portTypes[index]).getValue();
148
149 // Non-domain type ports can have no domain information.
150 if (domains.empty())
151 continue;
152
153 // Domain associations must be an array of integers, each pointing to a
154 // domain type port.
155 auto domain = domains[index];
156 auto arrayAttr = dyn_cast<ArrayAttr>(domain);
157 if (!arrayAttr)
158 return module.emitOpError()
159 << "domain associations for port '" << module.getPortName(index)
160 << "' must be an 'ArrayAttr'";
161
162 // Domain type ports must have empty associations.
163 if (isa<DomainType>(type)) {
164 if (!arrayAttr.empty())
165 return module.emitOpError()
166 << "domain type port '" << module.getPortName(index)
167 << "' must have empty domain associations";
168 continue;
169 }
170
171 // Non-domain type ports: verify associations point to domain type ports.
172 for (auto attr : arrayAttr) {
173 auto association = dyn_cast<IntegerAttr>(attr);
174 if (!association)
175 return module.emitOpError()
176 << "domain associations for port '" << module.getPortName(index)
177 << "' must be an 'ArrayAttr<IntegerAttr>'";
178 auto associationIdx = association.getValue().getZExtValue();
179 if (associationIdx >= numPorts)
180 return module.emitOpError()
181 << "has domain association " << associationIdx << " for port '"
182 << module.getPortName(index) << "', but the module only has "
183 << numPorts << " ports";
184 if (!type_isa<DomainType>(module.getPortType(associationIdx)))
185 return module.emitOpError()
186 << "has port '" << module.getPortName(index)
187 << "' which has a domain association with non-domain port '"
188 << module.getPortName(associationIdx) << "'";
189 }
190 }
191
192 // Verify the body.
193 if (module->getNumRegions() != 1)
194 return module.emitOpError("requires one region");
195
196 if (failed(verifyNoInputProbes(module)))
197 return failure();
198
199 return success();
200}
201
202//===----------------------------------------------------------------------===//
203// Forceable
204//===----------------------------------------------------------------------===//
205
207 Type type) {
208 auto base = dyn_cast_or_null<FIRRTLBaseType>(type);
209 // TODO: Find a way to not check same things RefType::get/verify does.
210 if (!forceable || !base || base.containsConst())
211 return {};
212 return circt::firrtl::RefType::get(base.getPassiveType(), forceable);
213}
214
215LogicalResult circt::firrtl::detail::verifyForceableOp(Forceable op) {
216 bool forceable = op.isForceable();
217 auto ref = op.getDataRef();
218 if ((bool)ref != forceable)
219 return op.emitOpError("must have ref result iff marked forceable");
220 if (!forceable)
221 return success();
222 auto data = op.getDataRaw();
223 auto baseType = type_dyn_cast<FIRRTLBaseType>(data.getType());
224 if (!baseType)
225 return op.emitOpError("has data that is not a base type");
226 if (baseType.containsConst())
227 return op.emitOpError("cannot force a declaration of constant type");
228 auto expectedRefType = getForceableResultType(forceable, baseType);
229 if (ref.getType() != expectedRefType)
230 return op.emitOpError("reference result of incorrect type, found ")
231 << ref.getType() << ", expected " << expectedRefType;
232 return success();
233}
234
235namespace {
236/// Simple wrapper to allow construction from a context for local use.
237class TrivialPatternRewriter : public PatternRewriter {
238public:
239 explicit TrivialPatternRewriter(MLIRContext *context)
240 : PatternRewriter(context) {}
241};
242} // end namespace
243
244Forceable
245circt::firrtl::detail::replaceWithNewForceability(Forceable op, bool forceable,
246 PatternRewriter *rewriter) {
247 if (forceable == op.isForceable())
248 return op;
249
250 assert(op->getNumRegions() == 0);
251
252 // Create copy of this operation with/without the forceable marker + result
253 // type.
254
255 TrivialPatternRewriter localRewriter(op.getContext());
256 PatternRewriter &rw = rewriter ? *rewriter : localRewriter;
257
258 // Grab the current operation's results and attributes.
259 SmallVector<Type, 8> resultTypes(op->getResultTypes());
260 SmallVector<NamedAttribute, 16> attributes(op->getAttrs());
261
262 // Add/remove the optional ref result.
263 auto refType = firrtl::detail::getForceableResultType(true, op.getDataType());
264 if (forceable)
265 resultTypes.push_back(refType);
266 else {
267 assert(resultTypes.back() == refType &&
268 "expected forceable type as last result");
269 resultTypes.pop_back();
270 }
271
272 // Add/remove the forceable marker.
273 auto forceableMarker =
274 rw.getNamedAttr(op.getForceableAttrName(), rw.getUnitAttr());
275 if (forceable)
276 attributes.push_back(forceableMarker);
277 else {
278 llvm::erase(attributes, forceableMarker);
279 assert(attributes.size() != op->getAttrs().size());
280 }
281
282 // Create the replacement operation.
283 OperationState state(op.getLoc(), op->getName(), op->getOperands(),
284 resultTypes, attributes, op->getSuccessors());
285 rw.setInsertionPoint(op);
286 auto *replace = rw.create(state);
287
288 // Dropping forceability (!forceable) -> no uses of forceable ref handle.
289 assert(forceable || op.getDataRef().use_empty());
290
291 // Replace results.
292 for (auto result : llvm::drop_end(op->getResults(), forceable ? 0 : 1))
293 rw.replaceAllUsesWith(result, replace->getResult(result.getResultNumber()));
294 rw.eraseOp(op);
295 return cast<Forceable>(replace);
296}
297
298#include "circt/Dialect/FIRRTL/FIRRTLOpInterfaces.cpp.inc"
assert(baseType &&"element must be base type")
static std::unique_ptr< Context > context
static LogicalResult verifyNoInputProbes(FModuleLike module)
false return failure()
Parse an optional keyword or string and set instance into 'result'.
return success()
This class implements the same functionality as TypeSwitch except that it uses firrtl::type_dyn_cast ...
FIRRTLTypeSwitch< T, ResultT > & Case(CallableT &&caseFn)
Add a case on the given type.
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.
BaseTy type_dyn_cast(Type type)
bool type_isa(Type type)