CIRCT 20.0.0git
Loading...
Searching...
No Matches
HWInstanceImplementation.cpp
Go to the documentation of this file.
1//===- InstanceImplementation.cpp - Utilities for instance-like ops -------===//
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
12
13using namespace circt;
14using namespace circt::hw;
15
17 Operation *instanceOp, SymbolTableCollection &symbolTable,
18 mlir::FlatSymbolRefAttr moduleName, Operation *&module) {
19 module = symbolTable.lookupNearestSymbolFrom(instanceOp, moduleName);
20 if (module == nullptr)
21 return instanceOp->emitError("Cannot find module definition '")
22 << moduleName.getValue() << "'";
23
24 // It must be some sort of module.
25 if (!isa<HWModuleLike>(module))
26 return instanceOp->emitError("symbol reference '")
27 << moduleName.getValue() << "' isn't a module";
28
29 return success();
30}
31
33 Location loc, ArrayAttr parameters, ArrayRef<Type> types,
34 SmallVectorImpl<Type> &resolvedTypes, const EmitErrorFn &emitError) {
35 for (auto type : types) {
36 auto expectedType = evaluateParametricType(loc, parameters, type);
37 if (failed(expectedType)) {
38 emitError([&](auto &diag) {
39 diag << "failed to resolve parametric input of instantiated module";
40 return true;
41 });
42 return failure();
43 }
44
45 resolvedTypes.push_back(*expectedType);
46 }
47
48 return success();
49}
50
51LogicalResult instance_like_impl::verifyInputs(ArrayAttr argNames,
52 ArrayAttr moduleArgNames,
53 TypeRange inputTypes,
54 ArrayRef<Type> moduleInputTypes,
55 const EmitErrorFn &emitError) {
56 // Check operand types first.
57 if (moduleInputTypes.size() != inputTypes.size()) {
58 emitError([&](auto &diag) {
59 diag << "has a wrong number of operands; expected "
60 << moduleInputTypes.size() << " but got " << inputTypes.size();
61 return true;
62 });
63 return failure();
64 }
65
66 if (argNames.size() != inputTypes.size()) {
67 emitError([&](auto &diag) {
68 diag << "has a wrong number of input port names; expected "
69 << inputTypes.size() << " but got " << argNames.size();
70 return true;
71 });
72 return failure();
73 }
74
75 for (size_t i = 0; i != inputTypes.size(); ++i) {
76 auto expectedType = moduleInputTypes[i];
77 auto operandType = inputTypes[i];
78
79 if (operandType != expectedType) {
80 emitError([&](auto &diag) {
81 diag << "operand type #" << i << " must be " << expectedType
82 << ", but got " << operandType;
83 return true;
84 });
85 return failure();
86 }
87
88 if (argNames[i] != moduleArgNames[i]) {
89 emitError([&](auto &diag) {
90 diag << "input label #" << i << " must be " << moduleArgNames[i]
91 << ", but got " << argNames[i];
92 return true;
93 });
94 return failure();
95 }
96 }
97
98 return success();
99}
100
102 ArrayAttr resultNames, ArrayAttr moduleResultNames, TypeRange resultTypes,
103 ArrayRef<Type> moduleResultTypes, const EmitErrorFn &emitError) {
104 // Check result types and labels.
105 if (moduleResultTypes.size() != resultTypes.size()) {
106 emitError([&](auto &diag) {
107 diag << "has a wrong number of results; expected "
108 << moduleResultTypes.size() << " but got " << resultTypes.size();
109 return true;
110 });
111 return failure();
112 }
113
114 if (resultNames.size() != resultTypes.size()) {
115 emitError([&](auto &diag) {
116 diag << "has a wrong number of results port labels; expected "
117 << resultTypes.size() << " but got " << resultNames.size();
118 return true;
119 });
120 return failure();
121 }
122
123 for (size_t i = 0; i != resultTypes.size(); ++i) {
124 auto expectedType = moduleResultTypes[i];
125 auto resultType = resultTypes[i];
126
127 if (resultType != expectedType) {
128 emitError([&](auto &diag) {
129 diag << "result type #" << i << " must be " << expectedType
130 << ", but got " << resultType;
131 return true;
132 });
133 return failure();
134 }
135
136 if (resultNames[i] != moduleResultNames[i]) {
137 emitError([&](auto &diag) {
138 diag << "result label #" << i << " must be " << moduleResultNames[i]
139 << ", but got " << resultNames[i];
140 return true;
141 });
142 return failure();
143 }
144 }
145
146 return success();
147}
148
150 ArrayAttr parameters, ArrayAttr moduleParameters,
151 ArrayRef<Type> resolvedModParametersRefs, const EmitErrorFn &emitError) {
152 // Check parameters match up.
153 auto numParameters = parameters.size();
154 if (numParameters != moduleParameters.size()) {
155 emitError([&](auto &diag) {
156 diag << "expected " << moduleParameters.size() << " parameters but had "
157 << numParameters;
158 return true;
159 });
160 return failure();
161 }
162
163 for (size_t i = 0; i != numParameters; ++i) {
164 auto param = cast<ParamDeclAttr>(parameters[i]);
165 auto modParam = cast<ParamDeclAttr>(moduleParameters[i]);
166 auto resolvedModParamType = resolvedModParametersRefs[i];
167
168 auto paramName = param.getName();
169 if (paramName != modParam.getName()) {
170 emitError([&](auto &diag) {
171 diag << "parameter #" << i << " should have name " << modParam.getName()
172 << " but has name " << paramName;
173 return true;
174 });
175 return failure();
176 }
177
178 if (param.getType() != resolvedModParamType) {
179 emitError([&](auto &diag) {
180 diag << "parameter " << paramName << " should have type "
181 << modParam.getType() << " but has type " << param.getType();
182 return true;
183 });
184 return failure();
185 }
186
187 // All instance parameters must have a value. Specify the same value as
188 // a module's default value if you want the default.
189 if (!param.getValue()) {
190 emitError([&](auto &diag) {
191 diag << "parameter " << paramName << " must have a value";
192 return false;
193 });
194 return failure();
195 }
196 }
197
198 return success();
199}
200
202 Operation *instance, FlatSymbolRefAttr moduleRef, OperandRange inputs,
203 TypeRange results, ArrayAttr argNames, ArrayAttr resultNames,
204 ArrayAttr parameters, SymbolTableCollection &symbolTable) {
205 // Verify that we reference some kind of HW module and get the module on
206 // success.
207 Operation *module;
208 if (failed(instance_like_impl::verifyReferencedModule(instance, symbolTable,
209 moduleRef, module)))
210 return failure();
211
212 // Emit an error message on the instance, with a note indicating which module
213 // is being referenced. The error message on the instance is added by the
214 // verification function this lambda is passed to.
215 EmitErrorFn emitError =
216 [&](const std::function<bool(InFlightDiagnostic & diag)> &fn) {
217 auto diag = instance->emitOpError();
218 if (fn(diag))
219 diag.attachNote(module->getLoc()) << "module declared here";
220 };
221
222 // Check that input types are consistent with the referenced module.
223 auto mod = cast<HWModuleLike>(module);
224 auto modArgNames =
225 ArrayAttr::get(instance->getContext(), mod.getInputNames());
226 auto modResultNames =
227 ArrayAttr::get(instance->getContext(), mod.getOutputNames());
228
229 ArrayRef<Type> resolvedModInputTypesRef = getModuleType(module).getInputs();
230
231 SmallVector<Type> resolvedModInputTypes;
232 if (parameters) {
234 instance->getLoc(), parameters, getModuleType(module).getInputs(),
235 resolvedModInputTypes, emitError)))
236 return failure();
237 resolvedModInputTypesRef = resolvedModInputTypes;
238 }
239
241 argNames, modArgNames, inputs.getTypes(), resolvedModInputTypesRef,
242 emitError)))
243 return failure();
244
245 // Check that result types are consistent with the referenced module.
246 ArrayRef<Type> resolvedModResultTypesRef = getModuleType(module).getResults();
247 SmallVector<Type> resolvedModResultTypes;
248 if (parameters) {
250 instance->getLoc(), parameters, getModuleType(module).getResults(),
251 resolvedModResultTypes, emitError)))
252 return failure();
253 resolvedModResultTypesRef = resolvedModResultTypes;
254 }
256 resultNames, modResultNames, results, resolvedModResultTypesRef,
257 emitError)))
258 return failure();
259
260 if (parameters) {
261 auto modParameters = module->getAttrOfType<ArrayAttr>("parameters");
262 SmallVector<Type> rawModParameters, resolvedModParameters;
263 rawModParameters.reserve(modParameters.size());
264 resolvedModParameters.reserve(modParameters.size());
265 for (auto paramDecl : modParameters.getAsRange<ParamDeclAttr>())
266 rawModParameters.push_back(paramDecl.getType());
267
268 // resolve parameters
270 instance->getLoc(), parameters, rawModParameters,
271 resolvedModParameters, emitError)))
272 return failure();
273
274 // Check that the parameters are consistent with the referenced module.
276 parameters, modParameters, resolvedModParameters, emitError)))
277 return failure();
278 }
279
280 return success();
281}
282
283LogicalResult
285 ArrayAttr moduleParameters,
286 const EmitErrorFn &emitError) {
287 // Check that all the parameter values specified to the instance are
288 // structurally valid.
289 for (auto param : parameters) {
290 auto paramAttr = cast<ParamDeclAttr>(param);
291 auto value = paramAttr.getValue();
292 // The SymbolUses verifier which checks that this exists may not have been
293 // run yet. Let it issue the error.
294 if (!value)
295 continue;
296
297 auto typedValue = dyn_cast<mlir::TypedAttr>(value);
298 if (!typedValue) {
299 emitError([&](auto &diag) {
300 diag << "parameter " << paramAttr
301 << " should have a typed value; has value " << value;
302 return false;
303 });
304 return failure();
305 }
306
307 if (typedValue.getType() != paramAttr.getType()) {
308 emitError([&](auto &diag) {
309 diag << "parameter " << paramAttr << " should have type "
310 << paramAttr.getType() << "; has type " << typedValue.getType();
311 return false;
312 });
313 return failure();
314 }
315
316 if (failed(checkParameterInContext(value, moduleParameters, emitError)))
317 return failure();
318 }
319 return success();
320}
321
322StringAttr instance_like_impl::getName(ArrayAttr names, size_t idx) {
323 // Tolerate malformed IR here to enable debug printing etc.
324 if (names && idx < names.size())
325 return cast<StringAttr>(names[idx]);
326 return StringAttr();
327}
328
329ArrayAttr instance_like_impl::updateName(ArrayAttr oldNames, size_t i,
330 StringAttr name) {
331 SmallVector<Attribute> newNames(oldNames.begin(), oldNames.end());
332 if (newNames[i] == name)
333 return oldNames;
334 newNames[i] = name;
335 return ArrayAttr::get(oldNames.getContext(), oldNames);
336}
337
338void instance_like_impl::getAsmResultNames(OpAsmSetValueNameFn setNameFn,
339 StringRef instanceName,
340 ArrayAttr resultNames,
341 ValueRange results) {
342 // Provide default names for instance results.
343 std::string name = instanceName.str() + ".";
344 size_t baseNameLen = name.size();
345
346 for (size_t i = 0, e = resultNames.size(); i != e; ++i) {
347 auto resName = getName(resultNames, i);
348 name.resize(baseNameLen);
349 if (resName && !resName.getValue().empty())
350 name += resName.getValue().str();
351 else
352 name += std::to_string(i);
353 setNameFn(results[i], name);
354 }
355}
356
357SmallVector<PortInfo> instance_like_impl::getPortList(Operation *instanceOp) {
358 auto moduleTy = getModuleType(instanceOp);
359
360 SmallVector<PortInfo> ports;
361 auto emptyDict = DictionaryAttr::get(instanceOp->getContext());
362 auto argNames = instanceOp->getAttrOfType<ArrayAttr>("argNames");
363 auto argTypes = moduleTy.getInputs();
364 auto argLocs = instanceOp->getAttrOfType<ArrayAttr>("argLocs");
365
366 auto resultNames = instanceOp->getAttrOfType<ArrayAttr>("resultNames");
367 auto resultTypes = moduleTy.getResults();
368 auto resultLocs = instanceOp->getAttrOfType<ArrayAttr>("resultLocs");
369
370 ports.reserve(argTypes.size() + resultTypes.size());
371 for (unsigned i = 0, e = argTypes.size(); i < e; ++i) {
372 auto type = argTypes[i];
373 auto direction = ModulePort::Direction::Input;
374
375 if (auto inout = dyn_cast<InOutType>(type)) {
376 type = inout.getElementType();
377 direction = ModulePort::Direction::InOut;
378 }
379
380 LocationAttr loc;
381 if (argLocs)
382 loc = cast<LocationAttr>(argLocs[i]);
383 ports.push_back(
384 {{cast<StringAttr>(argNames[i]), type, direction}, i, emptyDict, loc});
385 }
386
387 for (unsigned i = 0, e = resultTypes.size(); i < e; ++i) {
388 LocationAttr loc;
389 if (resultLocs)
390 loc = cast<LocationAttr>(resultLocs[i]);
391 ports.push_back({{cast<StringAttr>(resultNames[i]), resultTypes[i],
392 ModulePort::Direction::Output},
393 i,
394 emptyDict,
395 loc});
396 }
397 return ports;
398}
LogicalResult verifyParameterStructure(ArrayAttr parameters, ArrayAttr moduleParameters, const EmitErrorFn &emitError)
Check that all the parameter values specified to the instance are structurally valid.
std::function< void(std::function< bool(InFlightDiagnostic &)>)> EmitErrorFn
Whenever the nested function returns true, a note referring to the referenced module is attached to t...
LogicalResult verifyOutputs(ArrayAttr resultNames, ArrayAttr moduleResultNames, TypeRange resultTypes, ArrayRef< Type > moduleResultTypes, const EmitErrorFn &emitError)
Verify that the list of outputs of the instance and the module match in terms of length,...
LogicalResult verifyInstanceOfHWModule(Operation *instance, FlatSymbolRefAttr moduleRef, OperandRange inputs, TypeRange results, ArrayAttr argNames, ArrayAttr resultNames, ArrayAttr parameters, SymbolTableCollection &symbolTable)
Combines verifyReferencedModule, verifyInputs, verifyOutputs, and verifyParameters.
SmallVector< PortInfo > getPortList(Operation *instanceOp)
Return the port list of an instance, based on the name, type and location attributes present on the i...
StringAttr getName(ArrayAttr names, size_t idx)
Return the name at the specified index of the ArrayAttr or null if it cannot be determined.
LogicalResult resolveParametricTypes(Location loc, ArrayAttr parameters, ArrayRef< Type > types, SmallVectorImpl< Type > &resolvedTypes, const EmitErrorFn &emitError)
Stores a resolved version of each type in.
LogicalResult verifyInputs(ArrayAttr argNames, ArrayAttr moduleArgNames, TypeRange inputTypes, ArrayRef< Type > moduleInputTypes, const EmitErrorFn &emitError)
Verify that the list of inputs of the instance and the module match in terms of length,...
LogicalResult verifyParameters(ArrayAttr parameters, ArrayAttr moduleParameters, ArrayRef< Type > resolvedModParametersRefs, const EmitErrorFn &emitError)
Verify that the parameter lists of the instance and the module match in terms of length,...
ArrayAttr updateName(ArrayAttr oldNames, size_t i, StringAttr name)
Change the name at the specified index of the.
LogicalResult verifyReferencedModule(Operation *instanceOp, SymbolTableCollection &symbolTable, mlir::FlatSymbolRefAttr moduleName, Operation *&module)
Verify that the instance refers to a valid HW module.
void getAsmResultNames(OpAsmSetValueNameFn setNameFn, StringRef instanceName, ArrayAttr resultNames, ValueRange results)
Suggest a name for each result value based on the saved result names attribute.
FunctionType getModuleType(Operation *module)
Return the signature for the specified module as a function type.
Definition HWOps.cpp:521
LogicalResult checkParameterInContext(Attribute value, Operation *module, Operation *usingOp, bool disallowParamRefs=false)
Check parameter specified by value to see if it is valid within the scope of the specified module mod...
Definition HWOps.cpp:202
mlir::FailureOr< mlir::Type > evaluateParametricType(mlir::Location loc, mlir::ArrayAttr parameters, mlir::Type type, bool emitErrors=true)
Returns a resolved version of 'type' wherein any parameter reference has been evaluated based on the ...
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.