CIRCT 24.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::PropertyRef 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::PropertyRef 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 RUWBehavior 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
static std::unique_ptr< Context > context
#define isdigit(x)
Definition FIRLexer.cpp:26
static PortInfo getPort(ModuleTy &mod, size_t idx)
Definition HWOps.cpp:1508
false return failure()
Parse an optional keyword or string and set instance into 'result'.
return success()
NamedAttrList attrs
static StringRef getInnerSymbolAttrName()
Return the name of the attribute used for inner symbol names.
BaseTy type_dyn_cast(Type type)
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.
function_ref< void(Value, StringRef)> OpAsmSetValueNameFn
Definition LLVM.h:193