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