CIRCT  20.0.0git
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 
10 #include "circt/Dialect/HW/HWOps.h"
12 
13 using namespace circt;
14 using 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 
51 LogicalResult 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 
149 LogicalResult
151  ArrayAttr moduleParameters,
152  const EmitErrorFn &emitError) {
153  // Check parameters match up.
154  auto numParameters = parameters.size();
155  if (numParameters != moduleParameters.size()) {
156  emitError([&](auto &diag) {
157  diag << "expected " << moduleParameters.size() << " parameters but had "
158  << numParameters;
159  return true;
160  });
161  return failure();
162  }
163 
164  for (size_t i = 0; i != numParameters; ++i) {
165  auto param = cast<ParamDeclAttr>(parameters[i]);
166  auto modParam = cast<ParamDeclAttr>(moduleParameters[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() != modParam.getType()) {
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  SmallVector<Type> resolvedModInputTypes;
231  if (parameters) {
233  instance->getLoc(), parameters, getModuleType(module).getInputs(),
234  resolvedModInputTypes, emitError)))
235  return failure();
236  resolvedModInputTypesRef = resolvedModInputTypes;
237  }
239  argNames, modArgNames, inputs.getTypes(), resolvedModInputTypesRef,
240  emitError)))
241  return failure();
242 
243  // Check that result types are consistent with the referenced module.
244  ArrayRef<Type> resolvedModResultTypesRef = getModuleType(module).getResults();
245  SmallVector<Type> resolvedModResultTypes;
246  if (parameters) {
248  instance->getLoc(), parameters, getModuleType(module).getResults(),
249  resolvedModResultTypes, emitError)))
250  return failure();
251  resolvedModResultTypesRef = resolvedModResultTypes;
252  }
254  resultNames, modResultNames, results, resolvedModResultTypesRef,
255  emitError)))
256  return failure();
257 
258  if (parameters) {
259  // Check that the parameters are consistent with the referenced module.
260  ArrayAttr modParameters = module->getAttrOfType<ArrayAttr>("parameters");
261  if (failed(instance_like_impl::verifyParameters(parameters, modParameters,
262  emitError)))
263  return failure();
264  }
265 
266  return success();
267 }
268 
269 LogicalResult
271  ArrayAttr moduleParameters,
272  const EmitErrorFn &emitError) {
273  // Check that all the parameter values specified to the instance are
274  // structurally valid.
275  for (auto param : parameters) {
276  auto paramAttr = cast<ParamDeclAttr>(param);
277  auto value = paramAttr.getValue();
278  // The SymbolUses verifier which checks that this exists may not have been
279  // run yet. Let it issue the error.
280  if (!value)
281  continue;
282 
283  auto typedValue = dyn_cast<mlir::TypedAttr>(value);
284  if (!typedValue) {
285  emitError([&](auto &diag) {
286  diag << "parameter " << paramAttr
287  << " should have a typed value; has value " << value;
288  return false;
289  });
290  return failure();
291  }
292 
293  if (typedValue.getType() != paramAttr.getType()) {
294  emitError([&](auto &diag) {
295  diag << "parameter " << paramAttr << " should have type "
296  << paramAttr.getType() << "; has type " << typedValue.getType();
297  return false;
298  });
299  return failure();
300  }
301 
302  if (failed(checkParameterInContext(value, moduleParameters, emitError)))
303  return failure();
304  }
305  return success();
306 }
307 
308 StringAttr instance_like_impl::getName(ArrayAttr names, size_t idx) {
309  // Tolerate malformed IR here to enable debug printing etc.
310  if (names && idx < names.size())
311  return cast<StringAttr>(names[idx]);
312  return StringAttr();
313 }
314 
315 ArrayAttr instance_like_impl::updateName(ArrayAttr oldNames, size_t i,
316  StringAttr name) {
317  SmallVector<Attribute> newNames(oldNames.begin(), oldNames.end());
318  if (newNames[i] == name)
319  return oldNames;
320  newNames[i] = name;
321  return ArrayAttr::get(oldNames.getContext(), oldNames);
322 }
323 
325  StringRef instanceName,
326  ArrayAttr resultNames,
327  ValueRange results) {
328  // Provide default names for instance results.
329  std::string name = instanceName.str() + ".";
330  size_t baseNameLen = name.size();
331 
332  for (size_t i = 0, e = resultNames.size(); i != e; ++i) {
333  auto resName = getName(resultNames, i);
334  name.resize(baseNameLen);
335  if (resName && !resName.getValue().empty())
336  name += resName.getValue().str();
337  else
338  name += std::to_string(i);
339  setNameFn(results[i], name);
340  }
341 }
342 
343 SmallVector<PortInfo> instance_like_impl::getPortList(Operation *instanceOp) {
344  auto moduleTy = getModuleType(instanceOp);
345 
346  SmallVector<PortInfo> ports;
347  auto emptyDict = DictionaryAttr::get(instanceOp->getContext());
348  auto argNames = instanceOp->getAttrOfType<ArrayAttr>("argNames");
349  auto argTypes = moduleTy.getInputs();
350  auto argLocs = instanceOp->getAttrOfType<ArrayAttr>("argLocs");
351 
352  auto resultNames = instanceOp->getAttrOfType<ArrayAttr>("resultNames");
353  auto resultTypes = moduleTy.getResults();
354  auto resultLocs = instanceOp->getAttrOfType<ArrayAttr>("resultLocs");
355 
356  ports.reserve(argTypes.size() + resultTypes.size());
357  for (unsigned i = 0, e = argTypes.size(); i < e; ++i) {
358  auto type = argTypes[i];
359  auto direction = ModulePort::Direction::Input;
360 
361  if (auto inout = dyn_cast<InOutType>(type)) {
362  type = inout.getElementType();
363  direction = ModulePort::Direction::InOut;
364  }
365 
366  LocationAttr loc;
367  if (argLocs)
368  loc = cast<LocationAttr>(argLocs[i]);
369  ports.push_back(
370  {{cast<StringAttr>(argNames[i]), type, direction}, i, emptyDict, loc});
371  }
372 
373  for (unsigned i = 0, e = resultTypes.size(); i < e; ++i) {
374  LocationAttr loc;
375  if (resultLocs)
376  loc = cast<LocationAttr>(resultLocs[i]);
377  ports.push_back({{cast<StringAttr>(resultNames[i]), resultTypes[i],
379  i,
380  emptyDict,
381  loc});
382  }
383  return ports;
384 }
@ Input
Definition: HW.h:35
@ Output
Definition: HW.h:35
@ InOut
Definition: HW.h:35
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition: CalyxOps.cpp:55
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,...
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.
LogicalResult verifyParameters(ArrayAttr parameters, ArrayAttr moduleParameters, const EmitErrorFn &emitError)
Verify that the parameter lists of the instance and the module match in terms of length,...
FunctionType getModuleType(Operation *module)
Return the signature for the specified module as a function type.
Definition: HWOps.cpp:515
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
enum PEO uint32_t 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.
Definition: DebugAnalysis.h:21
function_ref< void(Value, StringRef)> OpAsmSetValueNameFn
Definition: LLVM.h:182