CIRCT  19.0.0git
CHIRRTLDialect.cpp
Go to the documentation of this file.
1 //===- CHIRRTLDialect.cpp - Implement the CHIRRTL dialect -----------------===//
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 implements the CHIRRTL dialect.
10 //
11 //===----------------------------------------------------------------------===//
12 
18 #include "circt/Support/LLVM.h"
19 #include "mlir/IR/DialectImplementation.h"
20 #include "mlir/IR/OpDefinition.h"
21 #include "llvm/ADT/SmallString.h"
22 #include "llvm/ADT/TypeSwitch.h"
23 
24 using namespace mlir;
25 using namespace circt;
26 using namespace chirrtl;
27 using namespace firrtl;
28 
29 //===----------------------------------------------------------------------===//
30 // Parsing and Printing helpers.
31 //===----------------------------------------------------------------------===//
32 
33 static ParseResult parseCHIRRTLOp(OpAsmParser &parser,
34  NamedAttrList &resultAttrs) {
35  // Add an empty annotation array if none were parsed.
36  auto result = parser.parseOptionalAttrDict(resultAttrs);
37  if (!resultAttrs.get("annotations"))
38  resultAttrs.append("annotations", parser.getBuilder().getArrayAttr({}));
39 
40  // If the attribute dictionary contains no 'name' attribute, infer it from
41  // the SSA name (if specified).
42  if (resultAttrs.get("name"))
43  return success();
44 
45  auto resultName = parser.getResultName(0).first;
46  if (!resultName.empty() && isdigit(resultName[0]))
47  resultName = "";
48  auto nameAttr = parser.getBuilder().getStringAttr(resultName);
49  auto *context = parser.getBuilder().getContext();
50  resultAttrs.push_back({StringAttr::get(context, "name"), nameAttr});
51  return result;
52 }
53 
54 static void printCHIRRTLOp(OpAsmPrinter &p, Operation *op, DictionaryAttr attr,
55  ArrayRef<StringRef> extraElides = {}) {
56  SmallVector<StringRef> elides(extraElides.begin(), extraElides.end());
57 
58  // Elide the symbol.
59  elides.push_back(hw::InnerSymbolTable::getInnerSymbolAttrName());
60 
61  // Note that we only need to print the "name" attribute if the asmprinter
62  // result name disagrees with it. This can happen in strange cases, e.g.
63  // when there are conflicts.
64  SmallString<32> resultNameStr;
65  llvm::raw_svector_ostream tmpStream(resultNameStr);
66  p.printOperand(op->getResult(0), tmpStream);
67  auto actualName = tmpStream.str().drop_front();
68  auto expectedName = op->getAttrOfType<StringAttr>("name").getValue();
69  // Anonymous names are printed as digits, which is fine.
70  if (actualName == expectedName ||
71  (expectedName.empty() && isdigit(actualName[0])))
72  elides.push_back("name");
73  elides.push_back("nameKind");
74 
75  // Elide "annotations" if it is empty.
76  if (op->getAttrOfType<ArrayAttr>("annotations").empty())
77  elides.push_back("annotations");
78 
79  p.printOptionalAttrDict(op->getAttrs(), elides);
80 }
81 
82 //===----------------------------------------------------------------------===//
83 // NameKind Custom Directive
84 //===----------------------------------------------------------------------===//
85 
86 static ParseResult parseNameKind(OpAsmParser &parser,
87  firrtl::NameKindEnumAttr &result) {
88  StringRef keyword;
89 
90  if (!parser.parseOptionalKeyword(&keyword,
91  {"interesting_name", "droppable_name"})) {
92  auto kind = symbolizeNameKindEnum(keyword);
93  result = NameKindEnumAttr::get(parser.getContext(), kind.value());
94  return success();
95  }
96 
97  // Default is droppable name.
98  result =
99  NameKindEnumAttr::get(parser.getContext(), NameKindEnum::DroppableName);
100  return success();
101 }
102 
103 static void printNameKind(OpAsmPrinter &p, Operation *op,
104  firrtl::NameKindEnumAttr attr,
105  ArrayRef<StringRef> extraElides = {}) {
106  if (attr.getValue() != NameKindEnum::DroppableName)
107  p << " " << stringifyNameKindEnum(attr.getValue());
108 }
109 
110 //===----------------------------------------------------------------------===//
111 // MemoryPortOp
112 //===----------------------------------------------------------------------===//
113 
114 void MemoryPortOp::build(OpBuilder &builder, OperationState &result,
115  Type dataType, Value memory, MemDirAttr direction,
116  StringRef name, ArrayRef<Attribute> annotations) {
117  build(builder, result, CMemoryPortType::get(builder.getContext()), dataType,
118  memory, direction, name, builder.getArrayAttr(annotations));
119 }
120 
121 LogicalResult MemoryPortOp::inferReturnTypes(
122  MLIRContext *context, std::optional<Location> loc, ValueRange operands,
123  DictionaryAttr attrs, mlir::OpaqueProperties properties,
124  mlir::RegionRange regions, SmallVectorImpl<Type> &results) {
125  auto inType = operands[0].getType();
126  auto memType = type_dyn_cast<CMemoryType>(inType);
127  if (!memType) {
128  if (loc)
129  mlir::emitError(*loc, "memory port requires memory operand");
130  return failure();
131  }
132  results.push_back(memType.getElementType());
133  results.push_back(CMemoryPortType::get(context));
134  return success();
135 }
136 
137 LogicalResult MemoryPortOp::verify() {
138  // MemoryPorts require exactly 1 access. Right now there are no other
139  // operations that could be using that value due to the types.
140  if (!getPort().hasOneUse())
141  return emitOpError("port should be used by a chirrtl.memoryport.access");
142  return success();
143 }
144 
145 MemoryPortAccessOp MemoryPortOp::getAccess() {
146  auto uses = getPort().use_begin();
147  if (uses == getPort().use_end())
148  return {};
149  return cast<MemoryPortAccessOp>(uses->getOwner());
150 }
151 
153  function_ref<void(Value, StringRef)> setNameFn) {
154  StringRef base = getName();
155  if (base.empty())
156  base = "memport";
157  setNameFn(getData(), (base + "_data").str());
158  setNameFn(getPort(), (base + "_port").str());
159 }
160 
161 static ParseResult parseMemoryPortOp(OpAsmParser &parser,
162  NamedAttrList &resultAttrs) {
163  // Add an empty annotation array if none were parsed.
164  auto result = parser.parseOptionalAttrDict(resultAttrs);
165  if (!resultAttrs.get("annotations"))
166  resultAttrs.append("annotations", parser.getBuilder().getArrayAttr({}));
167  return result;
168 }
169 
170 /// Always elide "direction" and elide "annotations" if it exists or
171 /// if it is empty.
172 static void printMemoryPortOp(OpAsmPrinter &p, Operation *op,
173  DictionaryAttr attr) {
174  // "direction" is always elided.
175  SmallVector<StringRef> elides = {"direction"};
176  // Annotations elided if empty.
177  if (op->getAttrOfType<ArrayAttr>("annotations").empty())
178  elides.push_back("annotations");
179  p.printOptionalAttrDict(op->getAttrs(), elides);
180 }
181 
182 //===----------------------------------------------------------------------===//
183 // MemoryDebugPortOp
184 //===----------------------------------------------------------------------===//
185 
186 void MemoryDebugPortOp::build(OpBuilder &builder, OperationState &result,
187  Type dataType, Value memory, StringRef name,
188  ArrayRef<Attribute> annotations) {
189  build(builder, result, dataType, memory, name,
190  builder.getArrayAttr(annotations));
191 }
192 
194  MLIRContext *context, std::optional<Location> loc, ValueRange operands,
195  DictionaryAttr attrs, mlir::OpaqueProperties properties,
196  mlir::RegionRange regions, SmallVectorImpl<Type> &results) {
197  auto inType = operands[0].getType();
198  auto memType = type_dyn_cast<CMemoryType>(inType);
199  if (!memType) {
200  if (loc)
201  mlir::emitError(*loc, "memory port requires memory operand");
202  return failure();
203  }
204  results.push_back(RefType::get(
205  FVectorType::get(memType.getElementType(), memType.getNumElements())));
206  return success();
207 }
208 
210  function_ref<void(Value, StringRef)> setNameFn) {
211  StringRef base = getName();
212  if (base.empty())
213  base = "memport";
214  setNameFn(getData(), (base + "_data").str());
215 }
216 
217 static ParseResult parseMemoryDebugPortOp(OpAsmParser &parser,
218  NamedAttrList &resultAttrs) {
219  // Add an empty annotation array if none were parsed.
220  auto result = parser.parseOptionalAttrDict(resultAttrs);
221  if (!resultAttrs.get("annotations"))
222  resultAttrs.append("annotations", parser.getBuilder().getArrayAttr({}));
223  return result;
224 }
225 
226 /// Always elide "direction" and elide "annotations" if it exists or
227 /// if it is empty.
228 static void printMemoryDebugPortOp(OpAsmPrinter &p, Operation *op,
229  DictionaryAttr attr) {
230  SmallVector<StringRef, 1> elides;
231  // Annotations elided if empty.
232  if (op->getAttrOfType<ArrayAttr>("annotations").empty())
233  elides.push_back("annotations");
234  p.printOptionalAttrDict(op->getAttrs(), elides);
235 }
236 
237 //===----------------------------------------------------------------------===//
238 // CombMemOp
239 //===----------------------------------------------------------------------===//
240 
241 static ParseResult parseCombMemOp(OpAsmParser &parser,
242  NamedAttrList &resultAttrs) {
243  return parseCHIRRTLOp(parser, resultAttrs);
244 }
245 
246 static void printCombMemOp(OpAsmPrinter &p, Operation *op,
247  DictionaryAttr attr) {
248  printCHIRRTLOp(p, op, attr);
249 }
250 
251 void CombMemOp::build(OpBuilder &builder, OperationState &result,
253  StringRef name, NameKindEnum nameKind,
254  ArrayAttr annotations, StringAttr innerSym,
255  MemoryInitAttr init) {
256  build(builder, result,
257  CMemoryType::get(builder.getContext(), elementType, numElements), name,
258  nameKind, annotations,
259  innerSym ? hw::InnerSymAttr::get(innerSym) : hw::InnerSymAttr(), init);
260 }
261 
263  setNameFn(getResult(), getName());
264 }
265 
266 std::optional<size_t> CombMemOp::getTargetResultIndex() {
267  // Inner symbols on comb memory operations target the op not any result.
268  return std::nullopt;
269 }
270 
271 //===----------------------------------------------------------------------===//
272 // SeqMemOp
273 //===----------------------------------------------------------------------===//
274 
275 static ParseResult parseSeqMemOp(OpAsmParser &parser,
276  NamedAttrList &resultAttrs) {
277  return parseCHIRRTLOp(parser, resultAttrs);
278 }
279 
280 /// Always elide "ruw" and elide "annotations" if it exists or if it is empty.
281 static void printSeqMemOp(OpAsmPrinter &p, Operation *op, DictionaryAttr attr) {
282  printCHIRRTLOp(p, op, attr, {"ruw"});
283 }
284 
285 void SeqMemOp::build(OpBuilder &builder, OperationState &result,
287  RUWAttr ruw, StringRef name, NameKindEnum nameKind,
288  ArrayAttr annotations, StringAttr innerSym,
289  MemoryInitAttr init) {
290  build(builder, result,
291  CMemoryType::get(builder.getContext(), elementType, numElements), ruw,
292  name, nameKind, annotations,
293  innerSym ? hw::InnerSymAttr::get(innerSym) : hw::InnerSymAttr(), init);
294 }
295 
297  setNameFn(getResult(), getName());
298 }
299 
300 std::optional<size_t> SeqMemOp::getTargetResultIndex() {
301  // Inner symbols on seq memory operations target the op not any result.
302  return std::nullopt;
303 }
304 
305 //===----------------------------------------------------------------------===//
306 // CHIRRTL Dialect
307 //===----------------------------------------------------------------------===//
308 
309 // This is used to give custom SSA names which match the "name" attribute of the
310 // memory operation, which allows us to elide the name attribute.
311 namespace {
312 struct CHIRRTLOpAsmDialectInterface : public OpAsmDialectInterface {
313  using OpAsmDialectInterface::OpAsmDialectInterface;
314  void getAsmResultNames(Operation *op, OpAsmSetValueNameFn setNameFn) const {
315  // Many CHIRRTL dialect operations have an optional 'name' attribute. If
316  // present, use it.
317  if (op->getNumResults() == 1)
318  if (auto nameAttr = op->getAttrOfType<StringAttr>("name"))
319  setNameFn(op->getResult(0), nameAttr.getValue());
320  }
321 };
322 } // namespace
323 
324 #define GET_OP_CLASSES
325 #include "circt/Dialect/FIRRTL/CHIRRTL.cpp.inc"
326 
327 void CHIRRTLDialect::initialize() {
328  // Register operations.
329  addOperations<
330 #define GET_OP_LIST
331 #include "circt/Dialect/FIRRTL/CHIRRTL.cpp.inc"
332  >();
333 
334  // Register types.
335  registerTypes();
336 
337  // Register interface implementations.
338  addInterfaces<CHIRRTLOpAsmDialectInterface>();
339 }
340 
341 #include "circt/Dialect/FIRRTL/CHIRRTLDialect.cpp.inc"
static void printSeqMemOp(OpAsmPrinter &p, Operation *op, DictionaryAttr attr)
Always elide "ruw" and elide "annotations" if it exists or if it is empty.
static void printNameKind(OpAsmPrinter &p, Operation *op, firrtl::NameKindEnumAttr attr, ArrayRef< StringRef > extraElides={})
static void printCHIRRTLOp(OpAsmPrinter &p, Operation *op, DictionaryAttr attr, ArrayRef< StringRef > extraElides={})
static ParseResult parseMemoryDebugPortOp(OpAsmParser &parser, NamedAttrList &resultAttrs)
static ParseResult parseNameKind(OpAsmParser &parser, firrtl::NameKindEnumAttr &result)
static void printMemoryPortOp(OpAsmPrinter &p, Operation *op, DictionaryAttr attr)
Always elide "direction" and elide "annotations" if it exists or if it is empty.
static ParseResult parseSeqMemOp(OpAsmParser &parser, NamedAttrList &resultAttrs)
static ParseResult parseCombMemOp(OpAsmParser &parser, NamedAttrList &resultAttrs)
static ParseResult parseCHIRRTLOp(OpAsmParser &parser, NamedAttrList &resultAttrs)
static void printCombMemOp(OpAsmPrinter &p, Operation *op, DictionaryAttr attr)
static void printMemoryDebugPortOp(OpAsmPrinter &p, Operation *op, DictionaryAttr attr)
Always elide "direction" and elide "annotations" if it exists or if it is empty.
static ParseResult parseMemoryPortOp(OpAsmParser &parser, NamedAttrList &resultAttrs)
MlirType uint64_t numElements
Definition: CHIRRTL.cpp:30
MlirType elementType
Definition: CHIRRTL.cpp:29
#define isdigit(x)
Definition: FIRLexer.cpp:26
static PortInfo getPort(ModuleTy &mod, size_t idx)
Definition: HWOps.cpp:1434
Builder builder
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition: CalyxOps.cpp:54
LogicalResult inferReturnTypes(MLIRContext *context, std::optional< Location > loc, ValueRange operands, DictionaryAttr attrs, mlir::OpaqueProperties properties, mlir::RegionRange regions, SmallVectorImpl< Type > &results, llvm::function_ref< FIRRTLType(ValueRange, ArrayRef< NamedAttribute >, std::optional< Location >)> callback)
StringAttr getName(ArrayAttr names, size_t idx)
Return the name at the specified index of the ArrayAttr or null if it cannot be determined.
void getAsmResultNames(OpAsmSetValueNameFn setNameFn, StringRef instanceName, ArrayAttr resultNames, ValueRange results)
Suggest a name for each result value based on the saved result names attribute.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition: DebugAnalysis.h:21
function_ref< void(Value, StringRef)> OpAsmSetValueNameFn
Definition: LLVM.h:186