CIRCT  18.0.0git
InstanceImplementation.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 
16 Operation *
18  Operation *instanceOp,
19  mlir::FlatSymbolRefAttr moduleName) {
20  if (cache)
21  if (auto *result = cache->getDefinition(moduleName))
22  return result;
23 
24  auto topLevelModuleOp = instanceOp->getParentOfType<ModuleOp>();
25  return topLevelModuleOp.lookupSymbol(moduleName.getValue());
26 }
27 
29  Operation *instanceOp, SymbolTableCollection &symbolTable,
30  mlir::FlatSymbolRefAttr moduleName, Operation *&module) {
31  module = getReferencedModule(nullptr, instanceOp, moduleName);
32  if (module == nullptr)
33  return instanceOp->emitError("Cannot find module definition '")
34  << moduleName.getValue() << "'";
35 
36  // It must be some sort of module.
37  if (!isa<HWModuleLike>(module))
38  return instanceOp->emitError("symbol reference '")
39  << moduleName.getValue() << "' isn't a module";
40 
41  return success();
42 }
43 
45  Location loc, ArrayAttr parameters, ArrayRef<Type> types,
46  SmallVectorImpl<Type> &resolvedTypes, const EmitErrorFn &emitError) {
47  for (auto type : types) {
48  auto expectedType = evaluateParametricType(loc, parameters, type);
49  if (failed(expectedType)) {
50  emitError([&](auto &diag) {
51  diag << "failed to resolve parametric input of instantiated module";
52  return true;
53  });
54  return failure();
55  }
56 
57  resolvedTypes.push_back(*expectedType);
58  }
59 
60  return success();
61 }
62 
63 LogicalResult instance_like_impl::verifyInputs(ArrayAttr argNames,
64  ArrayAttr moduleArgNames,
65  TypeRange inputTypes,
66  ArrayRef<Type> moduleInputTypes,
67  const EmitErrorFn &emitError) {
68  // Check operand types first.
69  if (moduleInputTypes.size() != inputTypes.size()) {
70  emitError([&](auto &diag) {
71  diag << "has a wrong number of operands; expected "
72  << moduleInputTypes.size() << " but got " << inputTypes.size();
73  return true;
74  });
75  return failure();
76  }
77 
78  if (argNames.size() != inputTypes.size()) {
79  emitError([&](auto &diag) {
80  diag << "has a wrong number of input port names; expected "
81  << inputTypes.size() << " but got " << argNames.size();
82  return true;
83  });
84  return failure();
85  }
86 
87  for (size_t i = 0; i != inputTypes.size(); ++i) {
88  auto expectedType = moduleInputTypes[i];
89  auto operandType = inputTypes[i];
90 
91  if (operandType != expectedType) {
92  emitError([&](auto &diag) {
93  diag << "operand type #" << i << " must be " << expectedType
94  << ", but got " << operandType;
95  return true;
96  });
97  return failure();
98  }
99 
100  if (argNames[i] != moduleArgNames[i]) {
101  emitError([&](auto &diag) {
102  diag << "input label #" << i << " must be " << moduleArgNames[i]
103  << ", but got " << argNames[i];
104  return true;
105  });
106  return failure();
107  }
108  }
109 
110  return success();
111 }
112 
114  ArrayAttr resultNames, ArrayAttr moduleResultNames, TypeRange resultTypes,
115  ArrayRef<Type> moduleResultTypes, const EmitErrorFn &emitError) {
116  // Check result types and labels.
117  if (moduleResultTypes.size() != resultTypes.size()) {
118  emitError([&](auto &diag) {
119  diag << "has a wrong number of results; expected "
120  << moduleResultTypes.size() << " but got " << resultTypes.size();
121  return true;
122  });
123  return failure();
124  }
125 
126  if (resultNames.size() != resultTypes.size()) {
127  emitError([&](auto &diag) {
128  diag << "has a wrong number of results port labels; expected "
129  << resultTypes.size() << " but got " << resultNames.size();
130  return true;
131  });
132  return failure();
133  }
134 
135  for (size_t i = 0; i != resultTypes.size(); ++i) {
136  auto expectedType = moduleResultTypes[i];
137  auto resultType = resultTypes[i];
138 
139  if (resultType != expectedType) {
140  emitError([&](auto &diag) {
141  diag << "result type #" << i << " must be " << expectedType
142  << ", but got " << resultType;
143  return true;
144  });
145  return failure();
146  }
147 
148  if (resultNames[i] != moduleResultNames[i]) {
149  emitError([&](auto &diag) {
150  diag << "result label #" << i << " must be " << moduleResultNames[i]
151  << ", but got " << resultNames[i];
152  return true;
153  });
154  return failure();
155  }
156  }
157 
158  return success();
159 }
160 
161 LogicalResult
163  ArrayAttr moduleParameters,
164  const EmitErrorFn &emitError) {
165  // Check parameters match up.
166  auto numParameters = parameters.size();
167  if (numParameters != moduleParameters.size()) {
168  emitError([&](auto &diag) {
169  diag << "expected " << moduleParameters.size() << " parameters but had "
170  << numParameters;
171  return true;
172  });
173  return failure();
174  }
175 
176  for (size_t i = 0; i != numParameters; ++i) {
177  auto param = parameters[i].cast<ParamDeclAttr>();
178  auto modParam = moduleParameters[i].cast<ParamDeclAttr>();
179 
180  auto paramName = param.getName();
181  if (paramName != modParam.getName()) {
182  emitError([&](auto &diag) {
183  diag << "parameter #" << i << " should have name " << modParam.getName()
184  << " but has name " << paramName;
185  return true;
186  });
187  return failure();
188  }
189 
190  if (param.getType() != modParam.getType()) {
191  emitError([&](auto &diag) {
192  diag << "parameter " << paramName << " should have type "
193  << modParam.getType() << " but has type " << param.getType();
194  return true;
195  });
196  return failure();
197  }
198 
199  // All instance parameters must have a value. Specify the same value as
200  // a module's default value if you want the default.
201  if (!param.getValue()) {
202  emitError([&](auto &diag) {
203  diag << "parameter " << paramName << " must have a value";
204  return false;
205  });
206  return failure();
207  }
208  }
209 
210  return success();
211 }
212 
214  Operation *instance, FlatSymbolRefAttr moduleRef, OperandRange inputs,
215  TypeRange results, ArrayAttr argNames, ArrayAttr resultNames,
216  ArrayAttr parameters, SymbolTableCollection &symbolTable) {
217  // Verify that we reference some kind of HW module and get the module on
218  // success.
219  Operation *module;
220  if (failed(instance_like_impl::verifyReferencedModule(instance, symbolTable,
221  moduleRef, module)))
222  return failure();
223 
224  // Emit an error message on the instance, with a note indicating which module
225  // is being referenced. The error message on the instance is added by the
226  // verification function this lambda is passed to.
227  EmitErrorFn emitError =
228  [&](const std::function<bool(InFlightDiagnostic & diag)> &fn) {
229  auto diag = instance->emitOpError();
230  if (fn(diag))
231  diag.attachNote(module->getLoc()) << "module declared here";
232  };
233 
234  // Check that input types are consistent with the referenced module.
235  auto mod = cast<HWModuleLike>(module);
236  auto modArgNames =
237  ArrayAttr::get(instance->getContext(), mod.getInputNames());
238  auto modResultNames =
239  ArrayAttr::get(instance->getContext(), mod.getOutputNames());
240 
241  ArrayRef<Type> resolvedModInputTypesRef = getModuleType(module).getInputs();
242  SmallVector<Type> resolvedModInputTypes;
243  if (parameters) {
245  instance->getLoc(), parameters, getModuleType(module).getInputs(),
246  resolvedModInputTypes, emitError)))
247  return failure();
248  resolvedModInputTypesRef = resolvedModInputTypes;
249  }
251  argNames, modArgNames, inputs.getTypes(), resolvedModInputTypesRef,
252  emitError)))
253  return failure();
254 
255  // Check that result types are consistent with the referenced module.
256  ArrayRef<Type> resolvedModResultTypesRef = getModuleType(module).getResults();
257  SmallVector<Type> resolvedModResultTypes;
258  if (parameters) {
260  instance->getLoc(), parameters, getModuleType(module).getResults(),
261  resolvedModResultTypes, emitError)))
262  return failure();
263  resolvedModResultTypesRef = resolvedModResultTypes;
264  }
266  resultNames, modResultNames, results, resolvedModResultTypesRef,
267  emitError)))
268  return failure();
269 
270  if (parameters) {
271  // Check that the parameters are consistent with the referenced module.
272  ArrayAttr modParameters = module->getAttrOfType<ArrayAttr>("parameters");
273  if (failed(instance_like_impl::verifyParameters(parameters, modParameters,
274  emitError)))
275  return failure();
276  }
277 
278  return success();
279 }
280 
281 LogicalResult
283  ArrayAttr moduleParameters,
284  const EmitErrorFn &emitError) {
285  // Check that all the parameter values specified to the instance are
286  // structurally valid.
287  for (auto param : parameters) {
288  auto paramAttr = param.cast<ParamDeclAttr>();
289  auto value = paramAttr.getValue();
290  // The SymbolUses verifier which checks that this exists may not have been
291  // run yet. Let it issue the error.
292  if (!value)
293  continue;
294 
295  auto typedValue = value.dyn_cast<mlir::TypedAttr>();
296  if (!typedValue) {
297  emitError([&](auto &diag) {
298  diag << "parameter " << paramAttr
299  << " should have a typed value; has value " << value;
300  return false;
301  });
302  return failure();
303  }
304 
305  if (typedValue.getType() != paramAttr.getType()) {
306  emitError([&](auto &diag) {
307  diag << "parameter " << paramAttr << " should have type "
308  << paramAttr.getType() << "; has type " << typedValue.getType();
309  return false;
310  });
311  return failure();
312  }
313 
314  if (failed(checkParameterInContext(value, moduleParameters, emitError)))
315  return failure();
316  }
317  return success();
318 }
319 
320 StringAttr instance_like_impl::getName(ArrayAttr names, size_t idx) {
321  // Tolerate malformed IR here to enable debug printing etc.
322  if (names && idx < names.size())
323  return names[idx].cast<StringAttr>();
324  return StringAttr();
325 }
326 
327 ArrayAttr instance_like_impl::updateName(ArrayAttr oldNames, size_t i,
328  StringAttr name) {
329  SmallVector<Attribute> newNames(oldNames.begin(), oldNames.end());
330  if (newNames[i] == name)
331  return oldNames;
332  newNames[i] = name;
333  return ArrayAttr::get(oldNames.getContext(), oldNames);
334 }
335 
337  StringRef instanceName,
338  ArrayAttr resultNames,
339  ValueRange results) {
340  // Provide default names for instance results.
341  std::string name = instanceName.str() + ".";
342  size_t baseNameLen = name.size();
343 
344  for (size_t i = 0, e = resultNames.size(); i != e; ++i) {
345  auto resName = getName(resultNames, i);
346  name.resize(baseNameLen);
347  if (resName && !resName.getValue().empty())
348  name += resName.getValue().str();
349  else
350  name += std::to_string(i);
351  setNameFn(results[i], name);
352  }
353 }
llvm::SmallVector< StringAttr > inputs
This stores lookup tables to make manipulating and working with the IR more efficient.
Definition: HWSymCache.h:27
mlir::Operation * getDefinition(mlir::Attribute attr) const override
Lookup a definition for 'symbol' in the cache.
Definition: HWSymCache.h:56
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition: CalyxOps.cpp:53
LogicalResult verifyParameterStructure(ArrayAttr parameters, ArrayAttr moduleParameters, const EmitErrorFn &emitError)
Check that all the parameter values specified to the instance are structurally valid.
Operation * getReferencedModule(const HWSymbolCache *cache, Operation *instanceOp, mlir::FlatSymbolRefAttr moduleName)
Return a pointer to the referenced module operation.
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.
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:505
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:204
enum PEO uint32_t mlir::FailureOr< mlir::Type > evaluateParametricType(mlir::Location loc, mlir::ArrayAttr parameters, mlir::Type type)
Returns a resolved version of 'type' wherein any parameter reference has been evaluated based on the ...
This file defines an intermediate representation for circuits acting as an abstraction for constraint...
function_ref< void(Value, StringRef)> OpAsmSetValueNameFn
Definition: LLVM.h:186