CIRCT 20.0.0git
Loading...
Searching...
No Matches
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
24using namespace mlir;
25using namespace circt;
26using namespace chirrtl;
27using namespace firrtl;
28
29//===----------------------------------------------------------------------===//
30// Parsing and Printing helpers.
31//===----------------------------------------------------------------------===//
32
33static 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
54static 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.
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
86static 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
103static 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
114void 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
121LogicalResult 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
137LogicalResult 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
145MemoryPortAccessOp MemoryPortOp::getAccess() {
146 auto uses = getPort().use_begin();
147 if (uses == getPort().use_end())
148 return {};
149 return cast<MemoryPortAccessOp>(uses->getOwner());
150}
151
152void MemoryPortOp::getAsmResultNames(
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
161static 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.
172static 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
186void 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
193LogicalResult MemoryDebugPortOp::inferReturnTypes(
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
209void MemoryDebugPortOp::getAsmResultNames(
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
217static 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.
228static 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
241static ParseResult parseCombMemOp(OpAsmParser &parser,
242 NamedAttrList &resultAttrs) {
243 return parseCHIRRTLOp(parser, resultAttrs);
244}
245
246static void printCombMemOp(OpAsmPrinter &p, Operation *op,
247 DictionaryAttr attr) {
248 printCHIRRTLOp(p, op, attr);
249}
250
251void 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}
262
263void CombMemOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
264 setNameFn(getResult(), getName());
265}
266
267std::optional<size_t> CombMemOp::getTargetResultIndex() {
268 // Inner symbols on comb memory operations target the op not any result.
269 return std::nullopt;
270}
271
272//===----------------------------------------------------------------------===//
273// SeqMemOp
274//===----------------------------------------------------------------------===//
275
276static ParseResult parseSeqMemOp(OpAsmParser &parser,
277 NamedAttrList &resultAttrs) {
278 return parseCHIRRTLOp(parser, resultAttrs);
279}
280
281/// Always elide "ruw" and elide "annotations" if it exists or if it is empty.
282static void printSeqMemOp(OpAsmPrinter &p, Operation *op, DictionaryAttr attr) {
283 printCHIRRTLOp(p, op, attr, {"ruw"});
284}
285
286void SeqMemOp::build(OpBuilder &builder, OperationState &result,
288 RUWAttr ruw, StringRef name, NameKindEnum nameKind,
289 ArrayAttr annotations, StringAttr innerSym,
290 MemoryInitAttr init) {
291 build(builder, result,
292 CMemoryType::get(builder.getContext(), elementType, numElements), ruw,
293 name, nameKind, annotations,
294 innerSym ? hw::InnerSymAttr::get(innerSym) : hw::InnerSymAttr(), init,
295 {});
296}
297
298void SeqMemOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
299 setNameFn(getResult(), getName());
300}
301
302std::optional<size_t> SeqMemOp::getTargetResultIndex() {
303 // Inner symbols on seq memory operations target the op not any result.
304 return std::nullopt;
305}
306
307//===----------------------------------------------------------------------===//
308// CHIRRTL Dialect
309//===----------------------------------------------------------------------===//
310
311// This is used to give custom SSA names which match the "name" attribute of the
312// memory operation, which allows us to elide the name attribute.
313namespace {
314struct CHIRRTLOpAsmDialectInterface : public OpAsmDialectInterface {
315 using OpAsmDialectInterface::OpAsmDialectInterface;
316 void getAsmResultNames(Operation *op, OpAsmSetValueNameFn setNameFn) const {
317 // Many CHIRRTL dialect operations have an optional 'name' attribute. If
318 // present, use it.
319 if (op->getNumResults() == 1)
320 if (auto nameAttr = op->getAttrOfType<StringAttr>("name"))
321 setNameFn(op->getResult(0), nameAttr.getValue());
322 }
323};
324} // namespace
325
326#define GET_OP_CLASSES
327#include "circt/Dialect/FIRRTL/CHIRRTL.cpp.inc"
328
329void CHIRRTLDialect::initialize() {
330 // Register operations.
331 addOperations<
332#define GET_OP_LIST
333#include "circt/Dialect/FIRRTL/CHIRRTL.cpp.inc"
334 >();
335
336 // Register types.
337 registerTypes();
338
339 // Register interface implementations.
340 addInterfaces<CHIRRTLOpAsmDialectInterface>();
341}
342
343#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:1440
static StringRef getInnerSymbolAttrName()
Return the name of the attribute used for inner symbol names.
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 hw.py:1
function_ref< void(Value, StringRef)> OpAsmSetValueNameFn
Definition LLVM.h:182