CIRCT  18.0.0git
HWOpInterfaces.h
Go to the documentation of this file.
1 //===- HWOpInterfaces.h - Declare HW op interfaces --------------*- C++ -*-===//
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 declares the operation interfaces for the HW dialect.
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #ifndef CIRCT_DIALECT_HW_HWOPINTERFACES_H
14 #define CIRCT_DIALECT_HW_HWOPINTERFACES_H
15 
20 #include "circt/Support/LLVM.h"
21 #include "mlir/IR/OpDefinition.h"
22 #include "mlir/IR/SymbolTable.h"
23 
24 namespace circt {
25 namespace hw {
26 
27 void populateHWModuleLikeTypeConversionPattern(StringRef moduleLikeOpName,
28  RewritePatternSet &patterns,
29  TypeConverter &converter);
30 
31 /// This holds the name, type, direction of a module's ports
32 struct PortInfo : public ModulePort {
33  /// This is the argument index or the result index depending on the direction.
34  /// "0" for an output means the first output, "0" for a in/inout means the
35  /// first argument.
36  size_t argNum = ~0U;
37 
38  /// The optional symbol for this port.
39  InnerSymAttr sym = {};
40  DictionaryAttr attrs = {};
41  LocationAttr loc = {};
42 
43  StringRef getName() const { return name.getValue(); }
44  bool isInput() const { return dir == ModulePort::Direction::Input; }
45  bool isOutput() const { return dir == ModulePort::Direction::Output; }
46  bool isInOut() const { return dir == ModulePort::Direction::InOut; }
47 
48  /// Return a unique numeric identifier for this port.
49  ssize_t getId() const { return isOutput() ? argNum : (-1 - argNum); };
50 };
51 
52 raw_ostream &operator<<(raw_ostream &printer, PortInfo port);
53 
54 /// This holds a decoded list of input/inout and output ports for a module or
55 /// instance.
57  explicit ModulePortInfo(ArrayRef<PortInfo> inputs,
58  ArrayRef<PortInfo> outputs) {
59  ports.insert(ports.end(), inputs.begin(), inputs.end());
60  ports.insert(ports.end(), outputs.begin(), outputs.end());
61  sanitizeInOut();
62  }
63 
64  explicit ModulePortInfo(ArrayRef<PortInfo> mergedPorts)
65  : ports(mergedPorts.begin(), mergedPorts.end()) {
66  sanitizeInOut();
67  }
68 
69  using iterator = SmallVector<PortInfo>::iterator;
70  using const_iterator = SmallVector<PortInfo>::const_iterator;
71 
72  iterator begin() { return ports.begin(); }
73  iterator end() { return ports.end(); }
74  const_iterator begin() const { return ports.begin(); }
75  const_iterator end() const { return ports.end(); }
76 
77  using PortDirectionRange = llvm::iterator_range<
78  llvm::filter_iterator<iterator, std::function<bool(const PortInfo &)>>>;
79 
80  using ConstPortDirectionRange = llvm::iterator_range<llvm::filter_iterator<
81  const_iterator, std::function<bool(const PortInfo &)>>>;
82 
84  std::function<bool(const PortInfo &)> predicateFn;
85  if (input) {
86  predicateFn = [](const PortInfo &port) -> bool {
87  return port.dir == ModulePort::Direction::Input ||
88  port.dir == ModulePort::Direction::InOut;
89  };
90  } else {
91  predicateFn = [](const PortInfo &port) -> bool {
92  return port.dir == ModulePort::Direction::Output;
93  };
94  }
95  return llvm::make_filter_range(ports, predicateFn);
96  }
97 
99  std::function<bool(const PortInfo &)> predicateFn;
100  if (input) {
101  predicateFn = [](const PortInfo &port) -> bool {
102  return port.dir == ModulePort::Direction::Input ||
103  port.dir == ModulePort::Direction::InOut;
104  };
105  } else {
106  predicateFn = [](const PortInfo &port) -> bool {
107  return port.dir == ModulePort::Direction::Output;
108  };
109  }
110  return llvm::make_filter_range(ports, predicateFn);
111  }
112 
114 
116 
118  return getPortsOfDirection(true);
119  }
120 
122  return getPortsOfDirection(false);
123  }
124 
125  size_t size() const { return ports.size(); }
126  size_t sizeInputs() const {
127  auto r = getInputs();
128  return std::distance(r.begin(), r.end());
129  }
130  size_t sizeOutputs() const {
131  auto r = getOutputs();
132  return std::distance(r.begin(), r.end());
133  }
134 
135  size_t portNumForInput(size_t idx) const {
136  size_t port = 0;
137  while (idx || ports[port].isOutput()) {
138  if (!ports[port].isOutput())
139  --idx;
140  ++port;
141  }
142  return port;
143  }
144 
145  size_t portNumForOutput(size_t idx) const {
146  size_t port = 0;
147  while (idx || !ports[port].isOutput()) {
148  if (ports[port].isOutput())
149  --idx;
150  ++port;
151  }
152  return port;
153  }
154 
155  PortInfo &at(size_t idx) { return ports[idx]; }
156  PortInfo &atInput(size_t idx) { return ports[portNumForInput(idx)]; }
157  PortInfo &atOutput(size_t idx) { return ports[portNumForOutput(idx)]; }
158 
159  const PortInfo &at(size_t idx) const { return ports[idx]; }
160  const PortInfo &atInput(size_t idx) const {
161  return ports[portNumForInput(idx)];
162  }
163  const PortInfo &atOutput(size_t idx) const {
164  return ports[portNumForOutput(idx)];
165  }
166 
167  void eraseInput(size_t idx) {
168  assert(idx < sizeInputs());
169  ports.erase(ports.begin() + portNumForInput(idx));
170  }
171 
172 private:
173  // convert input inout<type> -> inout type
174  void sanitizeInOut() {
175  for (auto &p : ports)
176  if (auto inout = dyn_cast<hw::InOutType>(p.type)) {
177  p.type = inout.getElementType();
179  }
180  }
181 
182  /// This contains a list of all ports. Input first.
183  SmallVector<PortInfo> ports;
184 };
185 
186 // This provides capability for looking up port indices based on port names.
189  lookupPortIndex(const llvm::DenseMap<StringAttr, unsigned> &portMap,
190  StringAttr name) const {
191  auto it = portMap.find(name);
192  if (it == portMap.end())
193  return failure();
194  return it->second;
195  }
196 
197 public:
198  explicit ModulePortLookupInfo(MLIRContext *ctx,
199  const ModulePortInfo &portInfo)
200  : ctx(ctx) {
201  for (auto &in : portInfo.getInputs())
202  inputPortMap[in.name] = in.argNum;
203 
204  for (auto &out : portInfo.getOutputs())
205  outputPortMap[out.name] = out.argNum;
206  }
207 
208  // Return the index of the input port with the specified name.
209  FailureOr<unsigned> getInputPortIndex(StringAttr name) const {
210  return lookupPortIndex(inputPortMap, name);
211  }
212 
213  // Return the index of the output port with the specified name.
214  FailureOr<unsigned> getOutputPortIndex(StringAttr name) const {
215  return lookupPortIndex(outputPortMap, name);
216  }
217 
218  FailureOr<unsigned> getInputPortIndex(StringRef name) const {
219  return getInputPortIndex(StringAttr::get(ctx, name));
220  }
221 
222  FailureOr<unsigned> getOutputPortIndex(StringRef name) const {
223  return getOutputPortIndex(StringAttr::get(ctx, name));
224  }
225 
226 private:
227  llvm::DenseMap<StringAttr, unsigned> inputPortMap;
228  llvm::DenseMap<StringAttr, unsigned> outputPortMap;
229  MLIRContext *ctx;
230 };
231 
232 class InnerSymbolOpInterface;
233 /// Verification hook for verifying InnerSym Attribute.
234 LogicalResult verifyInnerSymAttr(InnerSymbolOpInterface op);
235 
236 namespace detail {
237 LogicalResult verifyInnerRefNamespace(Operation *op);
238 } // namespace detail
239 
240 /// Classify operations that are InnerRefNamespace-like,
241 /// until structure is in place to do this via Traits.
242 /// Useful for getParentOfType<>, or scheduling passes.
243 /// Prefer putting the trait on operations here or downstream.
245  /// Return if this operation is explicitly an IRN or appears compatible.
246  static bool classof(mlir::Operation *op);
247  /// Return if this operation is explicitly an IRN or appears compatible.
248  static bool classof(const mlir::RegisteredOperationName *opInfo);
249 };
250 
251 } // namespace hw
252 } // namespace circt
253 
254 namespace mlir {
255 namespace OpTrait {
256 
257 /// This trait is for operations that define a scope for resolving InnerRef's,
258 /// and provides verification for InnerRef users (via InnerRefUserOpInterface).
259 template <typename ConcreteType>
260 class InnerRefNamespace : public TraitBase<ConcreteType, InnerRefNamespace> {
261 public:
262  static LogicalResult verifyRegionTrait(Operation *op) {
263  static_assert(
264  ConcreteType::template hasTrait<::mlir::OpTrait::SymbolTable>(),
265  "expected operation to be a SymbolTable");
266 
267  if (op->getNumRegions() != 1)
268  return op->emitError("expected operation to have a single region");
269  if (!op->getRegion(0).hasOneBlock())
270  return op->emitError("expected operation to have a single block");
271 
272  // Verify all InnerSymbolTable's and InnerRef users.
274  }
275 };
276 
277 /// A trait for inner symbol table functionality on an operation.
278 template <typename ConcreteType>
279 class InnerSymbolTable : public TraitBase<ConcreteType, InnerSymbolTable> {
280 public:
281  static LogicalResult verifyRegionTrait(Operation *op) {
282  // Insist that ops with InnerSymbolTable's provide a Symbol, this is
283  // essential to how InnerRef's work.
284  static_assert(
285  ConcreteType::template hasTrait<::mlir::SymbolOpInterface::Trait>(),
286  "expected operation to define a Symbol");
287 
288  // InnerSymbolTable's must be directly nested within an InnerRefNamespace.
289  auto *parent = op->getParentOp();
290  if (!parent || !isa<circt::hw::InnerRefNamespaceLike>(parent))
291  return op->emitError(
292  "InnerSymbolTable must have InnerRefNamespace parent");
293 
294  return success();
295  }
296 };
297 } // namespace OpTrait
298 } // namespace mlir
299 
300 #include "circt/Dialect/HW/HWOpInterfaces.h.inc"
301 
302 #endif // CIRCT_DIALECT_HW_HWOPINTERFACES_H
assert(baseType &&"element must be base type")
@ Input
Definition: HW.h:32
@ Output
Definition: HW.h:32
@ InOut
Definition: HW.h:32
llvm::SmallVector< StringAttr > inputs
llvm::SmallVector< StringAttr > outputs
This trait is for operations that define a scope for resolving InnerRef's, and provides verification ...
static LogicalResult verifyRegionTrait(Operation *op)
A trait for inner symbol table functionality on an operation.
static LogicalResult verifyRegionTrait(Operation *op)
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition: CalyxOps.cpp:53
LogicalResult verifyInnerRefNamespace(Operation *op)
raw_ostream & operator<<(raw_ostream &printer, PortInfo port)
void populateHWModuleLikeTypeConversionPattern(StringRef moduleLikeOpName, RewritePatternSet &patterns, TypeConverter &converter)
LogicalResult verifyInnerSymAttr(InnerSymbolOpInterface op)
Verification hook for verifying InnerSym Attribute.
This file defines an intermediate representation for circuits acting as an abstraction for constraint...
Definition: hw.py:1
Classify operations that are InnerRefNamespace-like, until structure is in place to do this via Trait...
static bool classof(mlir::Operation *op)
Return if this operation is explicitly an IRN or appears compatible.
This holds a decoded list of input/inout and output ports for a module or instance.
const PortInfo & at(size_t idx) const
const PortInfo & atInput(size_t idx) const
ModulePortInfo(ArrayRef< PortInfo > inputs, ArrayRef< PortInfo > outputs)
size_t portNumForInput(size_t idx) const
llvm::iterator_range< llvm::filter_iterator< iterator, std::function< bool(const PortInfo &)> >> PortDirectionRange
size_t portNumForOutput(size_t idx) const
ConstPortDirectionRange getPortsOfDirection(bool input) const
llvm::iterator_range< llvm::filter_iterator< const_iterator, std::function< bool(const PortInfo &)> >> ConstPortDirectionRange
SmallVector< PortInfo > ports
This contains a list of all ports. Input first.
PortInfo & at(size_t idx)
ConstPortDirectionRange getOutputs() const
SmallVector< PortInfo >::const_iterator const_iterator
const PortInfo & atOutput(size_t idx) const
SmallVector< PortInfo >::iterator iterator
const_iterator begin() const
PortInfo & atInput(size_t idx)
const_iterator end() const
ConstPortDirectionRange getInputs() const
void eraseInput(size_t idx)
PortDirectionRange getPortsOfDirection(bool input)
PortInfo & atOutput(size_t idx)
PortDirectionRange getInputs()
PortDirectionRange getOutputs()
ModulePortInfo(ArrayRef< PortInfo > mergedPorts)
llvm::DenseMap< StringAttr, unsigned > inputPortMap
llvm::DenseMap< StringAttr, unsigned > outputPortMap
FailureOr< unsigned > getOutputPortIndex(StringRef name) const
FailureOr< unsigned > getInputPortIndex(StringAttr name) const
FailureOr< unsigned > lookupPortIndex(const llvm::DenseMap< StringAttr, unsigned > &portMap, StringAttr name) const
ModulePortLookupInfo(MLIRContext *ctx, const ModulePortInfo &portInfo)
FailureOr< unsigned > getInputPortIndex(StringRef name) const
FailureOr< unsigned > getOutputPortIndex(StringAttr name) const
mlir::StringAttr name
Definition: HWTypes.h:29
This holds the name, type, direction of a module's ports.
bool isOutput() const
ssize_t getId() const
Return a unique numeric identifier for this port.
StringRef getName() const
DictionaryAttr attrs
size_t argNum
This is the argument index or the result index depending on the direction.
InnerSymAttr sym
The optional symbol for this port.
bool isInput() const
bool isInOut() const