CIRCT 24.0.0git
Loading...
Searching...
No Matches
OMOps.cpp
Go to the documentation of this file.
1//===- OMOps.cpp - Object Model operation definitions ---------------------===//
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 contains the Object Model operation definitions.
10//
11//===----------------------------------------------------------------------===//
12
16#include "mlir/IR/Builders.h"
17#include "mlir/IR/ImplicitLocOpBuilder.h"
18#include "mlir/IR/SymbolTable.h"
19#include "llvm/ADT/STLExtras.h"
20
21using namespace mlir;
22using namespace circt::om;
23
24//===----------------------------------------------------------------------===//
25// Custom Printers and Parsers
26//===----------------------------------------------------------------------===//
27
28static ParseResult parseBasePathString(OpAsmParser &parser, PathAttr &path) {
29 auto *context = parser.getContext();
30 auto loc = parser.getCurrentLocation();
31 std::string rawPath;
32 if (parser.parseString(&rawPath))
33 return failure();
34 if (parseBasePath(context, rawPath, path))
35 return parser.emitError(loc, "invalid base path");
36 return success();
37}
38
39static void printBasePathString(OpAsmPrinter &p, Operation *op, PathAttr path) {
40 p << '\"';
41 llvm::interleave(
42 path, p,
43 [&](const PathElement &elt) {
44 p << elt.module.getValue() << '/' << elt.instance.getValue();
45 },
46 ":");
47 p << '\"';
48}
49
50static ParseResult parsePathString(OpAsmParser &parser, PathAttr &path,
51 StringAttr &module, StringAttr &ref,
52 StringAttr &field) {
53
54 auto *context = parser.getContext();
55 auto loc = parser.getCurrentLocation();
56 std::string rawPath;
57 if (parser.parseString(&rawPath))
58 return failure();
59 if (parsePath(context, rawPath, path, module, ref, field))
60 return parser.emitError(loc, "invalid path");
61 return success();
62}
63
64static void printPathString(OpAsmPrinter &p, Operation *op, PathAttr path,
65 StringAttr module, StringAttr ref,
66 StringAttr field) {
67 p << '\"';
68 for (const auto &elt : path)
69 p << elt.module.getValue() << '/' << elt.instance.getValue() << ':';
70 if (!module.getValue().empty())
71 p << module.getValue();
72 if (!ref.getValue().empty())
73 p << '>' << ref.getValue();
74 if (!field.getValue().empty())
75 p << field.getValue();
76 p << '\"';
77}
78
79static ParseResult parseFieldLocs(OpAsmParser &parser, ArrayAttr &fieldLocs) {
80 if (parser.parseOptionalKeyword("field_locs"))
81 return success();
82 if (parser.parseLParen() || parser.parseAttribute(fieldLocs) ||
83 parser.parseRParen()) {
84 return failure();
85 }
86 return success();
87}
88
89static void printFieldLocs(OpAsmPrinter &printer, Operation *op,
90 ArrayAttr fieldLocs) {
91 mlir::OpPrintingFlags flags;
92 if (!flags.shouldPrintDebugInfo() || !fieldLocs)
93 return;
94 printer << "field_locs(";
95 printer.printAttribute(fieldLocs);
96 printer << ")";
97}
98
99//===----------------------------------------------------------------------===//
100// Shared definitions
101//===----------------------------------------------------------------------===//
102static ParseResult parseClassFieldsList(OpAsmParser &parser,
103 SmallVectorImpl<Attribute> &fieldNames,
104 SmallVectorImpl<Type> &fieldTypes) {
105
106 llvm::StringMap<SMLoc> nameLocMap;
107 auto parseElt = [&]() -> ParseResult {
108 // Parse the field name.
109 std::string fieldName;
110 if (parser.parseKeywordOrString(&fieldName))
111 return failure();
112 SMLoc currLoc = parser.getCurrentLocation();
113 if (nameLocMap.count(fieldName)) {
114 parser.emitError(currLoc, "field \"")
115 << fieldName << "\" is defined twice";
116 parser.emitError(nameLocMap[fieldName]) << "previous definition is here";
117 return failure();
118 }
119 nameLocMap[fieldName] = currLoc;
120 fieldNames.push_back(StringAttr::get(parser.getContext(), fieldName));
121
122 // Parse the field type.
123 fieldTypes.emplace_back();
124 if (parser.parseColonType(fieldTypes.back()))
125 return failure();
126
127 return success();
128 };
129
130 return parser.parseCommaSeparatedList(OpAsmParser::Delimiter::Paren,
131 parseElt);
132}
133
134static ParseResult parseClassLike(OpAsmParser &parser, OperationState &state) {
135 // Parse the optional symbol visibility.
136 (void)mlir::impl::parseOptionalVisibilityKeyword(parser, state.attributes);
137
138 // Parse the Class symbol name.
139 StringAttr symName;
140 if (parser.parseSymbolName(symName, mlir::SymbolTable::getSymbolAttrName(),
141 state.attributes))
142 return failure();
143
144 // Parse the formal parameters.
145 SmallVector<OpAsmParser::Argument> args;
146 if (parser.parseArgumentList(args, OpAsmParser::Delimiter::Paren,
147 /*allowType=*/true, /*allowAttrs=*/false))
148 return failure();
149
150 SmallVector<Type> fieldTypes;
151 SmallVector<Attribute> fieldNames;
152 if (succeeded(parser.parseOptionalArrow()))
153 if (failed(parseClassFieldsList(parser, fieldNames, fieldTypes)))
154 return failure();
155
156 SmallVector<NamedAttribute> fieldTypesMap;
157 if (!fieldNames.empty()) {
158 for (auto [name, type] : zip(fieldNames, fieldTypes))
159 fieldTypesMap.push_back(
160 NamedAttribute(cast<StringAttr>(name), TypeAttr::get(type)));
161 }
162 auto *ctx = parser.getContext();
163 state.addAttribute("fieldNames", mlir::ArrayAttr::get(ctx, fieldNames));
164 state.addAttribute("fieldTypes",
165 mlir::DictionaryAttr::get(ctx, fieldTypesMap));
166
167 // Parse the optional attribute dictionary.
168 if (failed(parser.parseOptionalAttrDictWithKeyword(state.attributes)))
169 return failure();
170
171 // Parse the body.
172 Region *region = state.addRegion();
173 if (parser.parseRegion(*region, args))
174 return failure();
175
176 // If the region was empty, add an empty block so it's still a SizedRegion<1>.
177 if (region->empty())
178 region->emplaceBlock();
179
180 // Remember the formal parameter names in an attribute.
181 auto argNames = llvm::map_range(args, [&](OpAsmParser::Argument arg) {
182 return StringAttr::get(parser.getContext(), arg.ssaName.name.drop_front());
183 });
184 state.addAttribute(
185 "formalParamNames",
186 ArrayAttr::get(parser.getContext(), SmallVector<Attribute>(argNames)));
187
188 return success();
189}
190
191static void printClassLike(ClassLike classLike, OpAsmPrinter &printer) {
192 printer << " ";
193
194 // Print the optional symbol visibility.
195 StringRef visibilityAttrName = SymbolTable::getVisibilityAttrName();
196 if (auto visibility =
197 classLike->getAttrOfType<StringAttr>(visibilityAttrName))
198 printer << visibility.getValue() << ' ';
199
200 // Print the Class symbol name.
201 printer.printSymbolName(classLike.getSymName());
202
203 // Retrieve the formal parameter names and values.
204 auto argNames = SmallVector<StringRef>(
205 classLike.getFormalParamNames().getAsValueRange<StringAttr>());
206 ArrayRef<BlockArgument> args = classLike.getBodyBlock()->getArguments();
207
208 // Print the formal parameters.
209 printer << '(';
210 for (size_t i = 0, e = args.size(); i < e; ++i) {
211 printer << '%' << argNames[i] << ": " << args[i].getType();
212 if (i < e - 1)
213 printer << ", ";
214 }
215 printer << ") ";
216
217 ArrayRef<Attribute> fieldNames =
218 cast<ArrayAttr>(classLike->getAttr("fieldNames")).getValue();
219
220 if (!fieldNames.empty()) {
221 printer << " -> (";
222 for (size_t i = 0, e = fieldNames.size(); i < e; ++i) {
223 if (i != 0)
224 printer << ", ";
225 StringAttr name = cast<StringAttr>(fieldNames[i]);
226 printer.printKeywordOrString(name.getValue());
227 printer << ": ";
228 Type type = classLike.getFieldType(name).value();
229 printer.printType(type);
230 }
231 printer << ") ";
232 }
233
234 // Print the optional attribute dictionary.
235 SmallVector<StringRef> elidedAttrs{
236 classLike.getSymNameAttrName(), classLike.getFormalParamNamesAttrName(),
237 visibilityAttrName, "fieldTypes", "fieldNames"};
238 printer.printOptionalAttrDictWithKeyword(classLike.getOperation()->getAttrs(),
239 elidedAttrs);
240
241 // Print the body.
242 printer.printRegion(classLike.getBody(), /*printEntryBlockArgs=*/false,
243 /*printBlockTerminators=*/true);
244}
245
246LogicalResult verifyClassLike(ClassLike classLike) {
247 // Verify the formal parameter names match up with the values.
248 if (classLike.getFormalParamNames().size() !=
249 classLike.getBodyBlock()->getArguments().size()) {
250 auto error = classLike.emitOpError(
251 "formal parameter name list doesn't match formal parameter value list");
252 error.attachNote(classLike.getLoc())
253 << "formal parameter names: " << classLike.getFormalParamNames();
254 error.attachNote(classLike.getLoc())
255 << "formal parameter values: "
256 << classLike.getBodyBlock()->getArguments();
257 return error;
258 }
259
260 return success();
261}
262
263void getClassLikeAsmBlockArgumentNames(ClassLike classLike, Region &region,
264 OpAsmSetValueNameFn setNameFn) {
265 // Retrieve the formal parameter names and values.
266 auto argNames = SmallVector<StringRef>(
267 classLike.getFormalParamNames().getAsValueRange<StringAttr>());
268 ArrayRef<BlockArgument> args = classLike.getBodyBlock()->getArguments();
269
270 // Use the formal parameter names as the SSA value names.
271 for (size_t i = 0, e = args.size(); i < e; ++i)
272 setNameFn(args[i], argNames[i]);
273}
274
275NamedAttribute makeFieldType(StringAttr name, Type type) {
276 return NamedAttribute(name, TypeAttr::get(type));
277}
278
279NamedAttribute makeFieldIdx(MLIRContext *ctx, mlir::StringAttr name,
280 unsigned i) {
281 return NamedAttribute(StringAttr(name),
282 mlir::IntegerAttr::get(mlir::IndexType::get(ctx), i));
283}
284
285std::optional<Type> getClassLikeFieldType(ClassLike classLike,
286 StringAttr name) {
287 DictionaryAttr fieldTypes = mlir::cast<DictionaryAttr>(
288 classLike.getOperation()->getAttr("fieldTypes"));
289 Attribute type = fieldTypes.get(name);
290 if (auto field = dyn_cast_or_null<TypeAttr>(type))
291 return field.getValue();
292 return std::nullopt;
293}
294
295void replaceClassLikeFieldTypes(ClassLike classLike,
296 AttrTypeReplacer &replacer) {
297 classLike->setAttr("fieldTypes", cast<DictionaryAttr>(replacer.replace(
298 classLike.getFieldTypes())));
299}
300
301//===----------------------------------------------------------------------===//
302// ClassOp
303//===----------------------------------------------------------------------===//
304
305ParseResult circt::om::ClassOp::parse(OpAsmParser &parser,
306 OperationState &state) {
307 return parseClassLike(parser, state);
308}
309
310circt::om::ClassOp circt::om::ClassOp::buildSimpleClassOp(
311 OpBuilder &odsBuilder, Location loc, Twine name,
312 ArrayRef<StringRef> formalParamNames, ArrayRef<StringRef> fieldNames,
313 ArrayRef<Type> fieldTypes) {
314 circt::om::ClassOp classOp = circt::om::ClassOp::create(
315 odsBuilder, loc, odsBuilder.getStringAttr(name),
316 odsBuilder.getStrArrayAttr(formalParamNames),
317 odsBuilder.getStrArrayAttr(fieldNames),
318 odsBuilder.getDictionaryAttr(llvm::map_to_vector(
319 llvm::zip(fieldNames, fieldTypes), [&](auto field) -> NamedAttribute {
320 return NamedAttribute(odsBuilder.getStringAttr(std::get<0>(field)),
321 TypeAttr::get(std::get<1>(field)));
322 })));
323 Block *body = &classOp.getRegion().emplaceBlock();
324 auto prevLoc = odsBuilder.saveInsertionPoint();
325 odsBuilder.setInsertionPointToEnd(body);
326
327 mlir::SmallVector<Attribute> locAttrs(fieldNames.size(), LocationAttr(loc));
328
329 ClassFieldsOp::create(odsBuilder, loc,
330 llvm::map_to_vector(fieldTypes,
331 [&](Type type) -> Value {
332 return body->addArgument(type,
333 loc);
334 }),
335 odsBuilder.getArrayAttr(locAttrs));
336
337 odsBuilder.restoreInsertionPoint(prevLoc);
338
339 return classOp;
340}
341
342void circt::om::ClassOp::print(OpAsmPrinter &printer) {
343 printClassLike(*this, printer);
344}
345
346LogicalResult circt::om::ClassOp::verify() { return verifyClassLike(*this); }
347
348LogicalResult circt::om::ClassOp::verifyRegions() {
349 auto fieldsOp =
350 dyn_cast_or_null<ClassFieldsOp>(this->getBodyBlock()->getTerminator());
351 if (!fieldsOp)
352 return this->emitOpError("expected terminator to be ClassFieldsOp");
353
354 // The number of results matches the number of terminator operands.
355 if (fieldsOp.getNumOperands() != this->getFieldNames().size()) {
356 auto diag = this->emitOpError()
357 << "returns '" << this->getFieldNames().size()
358 << "' fields, but its terminator returned '"
359 << fieldsOp.getNumOperands() << "' fields";
360 return diag.attachNote(fieldsOp.getLoc()) << "see terminator:";
361 }
362
363 // The type of each result matches the corresponding terminator operand type.
364 auto types = this->getFieldTypes();
365 for (auto [fieldName, terminatorOperandType] :
366 llvm::zip(this->getFieldNames(), fieldsOp.getOperandTypes())) {
367
368 auto fieldNameAttr = dyn_cast_or_null<StringAttr>(fieldName);
369 if (!fieldNameAttr)
370 return this->emitOpError("field name is not a StringAttr");
371
372 if (auto fieldType = types.get(fieldNameAttr))
373 if (auto typeAttr = dyn_cast<TypeAttr>(fieldType))
374 if (typeAttr.getValue() == terminatorOperandType)
375 continue;
376
377 auto diag = this->emitOpError()
378 << "returns different field types than its terminator";
379 return diag.attachNote(fieldsOp.getLoc()) << "see terminator:";
380 }
381
382 return success();
383}
384
385void circt::om::ClassOp::getAsmBlockArgumentNames(
386 Region &region, OpAsmSetValueNameFn setNameFn) {
387 getClassLikeAsmBlockArgumentNames(*this, region, setNameFn);
388}
389
390std::optional<mlir::Type>
391circt::om::ClassOp::getFieldType(mlir::StringAttr field) {
392 return getClassLikeFieldType(*this, field);
393}
394
395void circt::om::ClassOp::replaceFieldTypes(AttrTypeReplacer replacer) {
396 replaceClassLikeFieldTypes(*this, replacer);
397}
398
399void circt::om::ClassOp::updateFields(
400 mlir::ArrayRef<mlir::Location> newLocations,
401 mlir::ArrayRef<mlir::Value> newValues,
402 mlir::ArrayRef<mlir::Attribute> newNames) {
403
404 auto fieldsOp = getFieldsOp();
405 assert(fieldsOp && "The fields op should exist");
406 // Get field names.
407 SmallVector<Attribute> names(getFieldNamesAttr().getAsRange<StringAttr>());
408 // Get the field types.
409 SmallVector<NamedAttribute> fieldTypes(getFieldTypesAttr().getValue());
410 // Get the field values.
411 SmallVector<Value> fieldVals(fieldsOp.getFields());
412 // Get the field locations.
413 Location fieldOpLoc = fieldsOp->getLoc();
414
415 // Extract the locations per field.
416 SmallVector<Location> locations;
417 if (auto fl = dyn_cast<FusedLoc>(fieldOpLoc)) {
418 auto metadataArr = dyn_cast<ArrayAttr>(fl.getMetadata());
419 assert(metadataArr && "Expected the metadata for the fused location");
420 auto r = metadataArr.getAsRange<LocationAttr>();
421 locations.append(r.begin(), r.end());
422 } else {
423 // Assume same loc for every field.
424 locations.append(names.size(), fieldOpLoc);
425 }
426
427 // Append the new names, locations and values.
428 names.append(newNames.begin(), newNames.end());
429 locations.append(newLocations.begin(), newLocations.end());
430 fieldVals.append(newValues.begin(), newValues.end());
431
432 // Construct the new field types from values and names.
433 for (auto [v, n] : llvm::zip(newValues, newNames))
434 fieldTypes.emplace_back(
435 NamedAttribute(llvm::cast<StringAttr>(n), TypeAttr::get(v.getType())));
436
437 // Keep the locations as array on the metadata.
438 SmallVector<Attribute> locationsAttr;
439 llvm::for_each(locations, [&](Location &l) {
440 locationsAttr.push_back(cast<Attribute>(l));
441 });
442
443 ImplicitLocOpBuilder builder(getLoc(), *this);
444 // Update the field names attribute.
445 setFieldNamesAttr(builder.getArrayAttr(names));
446 // Update the fields type attribute.
447 setFieldTypesAttr(builder.getDictionaryAttr(fieldTypes));
448 fieldsOp.getFieldsMutable().assign(fieldVals);
449 // Update the location.
450 fieldsOp->setLoc(builder.getFusedLoc(
451 locations, ArrayAttr::get(getContext(), locationsAttr)));
452}
453
454void circt::om::ClassOp::addNewFieldsOp(mlir::OpBuilder &builder,
455 mlir::ArrayRef<Location> locs,
456 mlir::ArrayRef<Value> values) {
457 // Store the original locations as a metadata array so that unique locations
458 // are preserved as a mapping from field index to location
459 assert(locs.size() == values.size() && "Expected a location per value");
460 mlir::SmallVector<Attribute> locAttrs;
461 for (auto loc : locs) {
462 locAttrs.push_back(cast<Attribute>(LocationAttr(loc)));
463 }
464 // Also store the locations incase there's some other analysis that might
465 // be able to use the default FusedLoc representation.
466 ClassFieldsOp::create(builder, builder.getFusedLoc(locs), values,
467 builder.getArrayAttr(locAttrs));
468}
469
470mlir::Location circt::om::ClassOp::getFieldLocByIndex(size_t i) {
471 auto fieldsOp = this->getFieldsOp();
472 auto fieldLocs = fieldsOp.getFieldLocs();
473 if (!fieldLocs.has_value())
474 return fieldsOp.getLoc();
475 assert(i < fieldLocs.value().size() &&
476 "field index too large for location array");
477 return cast<LocationAttr>(fieldLocs.value()[i]);
478}
479
480//===----------------------------------------------------------------------===//
481// ClassExternOp
482//===----------------------------------------------------------------------===//
483
484ParseResult circt::om::ClassExternOp::parse(OpAsmParser &parser,
485 OperationState &state) {
486 return parseClassLike(parser, state);
487}
488
489void circt::om::ClassExternOp::print(OpAsmPrinter &printer) {
490 printClassLike(*this, printer);
491}
492
493LogicalResult circt::om::ClassExternOp::verify() {
494 if (failed(verifyClassLike(*this))) {
495 return failure();
496 }
497 // Verify body is empty
498 if (!this->getBodyBlock()->getOperations().empty()) {
499 return this->emitOpError("external class body should be empty");
500 }
501
502 return success();
503}
504
505void circt::om::ClassExternOp::getAsmBlockArgumentNames(
506 Region &region, OpAsmSetValueNameFn setNameFn) {
507 getClassLikeAsmBlockArgumentNames(*this, region, setNameFn);
508}
509
510std::optional<mlir::Type>
511circt::om::ClassExternOp::getFieldType(mlir::StringAttr field) {
512 return getClassLikeFieldType(*this, field);
513}
514
515void circt::om::ClassExternOp::replaceFieldTypes(AttrTypeReplacer replacer) {
516 replaceClassLikeFieldTypes(*this, replacer);
517}
518
519//===----------------------------------------------------------------------===//
520// ClassFieldsOp
521//===----------------------------------------------------------------------===//
522//
523LogicalResult circt::om::ClassFieldsOp::verify() {
524 auto fieldLocs = this->getFieldLocs();
525 if (fieldLocs.has_value()) {
526 auto fieldLocsVal = fieldLocs.value();
527 if (fieldLocsVal.size() != this->getFields().size()) {
528 auto error = this->emitOpError("size of field_locs (")
529 << fieldLocsVal.size()
530 << ") does not match number of fields ("
531 << this->getFields().size() << ")";
532 }
533 }
534 return success();
535}
536
537//===----------------------------------------------------------------------===//
538// ObjectOp
539//===----------------------------------------------------------------------===//
540
541void circt::om::ObjectOp::build(::mlir::OpBuilder &odsBuilder,
542 ::mlir::OperationState &odsState,
543 om::ClassOp classOp,
544 ::mlir::ValueRange actualParams) {
545 return build(odsBuilder, odsState,
546 om::ClassType::get(odsBuilder.getContext(),
547 mlir::FlatSymbolRefAttr::get(classOp)),
548 mlir::FlatSymbolRefAttr::get(classOp.getNameAttr()),
549 actualParams);
550}
551
552static FailureOr<ClassLike>
553verifyClassLikeSymbolUser(Operation *op, SymbolTableCollection &symbolTable,
554 ClassType resultType, StringAttr className) {
555 StringAttr resultClassName = resultType.getClassName().getAttr();
556 if (resultClassName != className)
557 return op->emitOpError("result type (")
558 << resultClassName << ") does not match referred to class ("
559 << className << ')';
560
561 auto classDef = dyn_cast_or_null<ClassLike>(
562 symbolTable.lookupNearestSymbolFrom(op, className));
563 if (!classDef)
564 return op->emitOpError("refers to non-existant class (")
565 << className << ')';
566 return classDef;
567}
568
569LogicalResult
570circt::om::ObjectOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
571 auto classDef =
572 verifyClassLikeSymbolUser((*this), symbolTable, getResult().getType(),
573 getClassNameAttr().getAttr());
574 if (failed(classDef))
575 return failure();
576
577 auto actualTypes = getActualParams().getTypes();
578 auto formalTypes = classDef->getBodyBlock()->getArgumentTypes();
579
580 // Verify the actual parameter list matches the formal parameter list.
581 if (actualTypes.size() != formalTypes.size()) {
582 auto error = emitOpError(
583 "actual parameter list doesn't match formal parameter list");
584 error.attachNote(classDef->getLoc())
585 << "formal parameters: " << classDef->getBodyBlock()->getArguments();
586 error.attachNote(getLoc()) << "actual parameters: " << getActualParams();
587 return error;
588 }
589
590 // Verify the actual parameter types match the formal parameter types.
591 for (size_t i = 0, e = actualTypes.size(); i < e; ++i) {
592 if (actualTypes[i] != formalTypes[i]) {
593 return emitOpError("actual parameter type (")
594 << actualTypes[i] << ") doesn't match formal parameter type ("
595 << formalTypes[i] << ')';
596 }
597 }
598
599 return success();
600}
601
602//===----------------------------------------------------------------------===//
603// ObjectFieldOp
604//===----------------------------------------------------------------------===//
605
606LogicalResult
607circt::om::ObjectFieldOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
608 auto classType = getObject().getType();
609 auto className = classType.getClassName().getAttr();
610
611 // Verify the referred-to class exists.
612 auto classDef = dyn_cast_or_null<ClassLike>(
613 symbolTable.lookupNearestSymbolFrom(*this, className));
614 if (!classDef)
615 return emitOpError("class ") << className << " was not found";
616
617 // Verify the field exists in the class.
618 auto fieldName = getFieldAttr();
619 std::optional<Type> fieldType = classDef.getFieldType(fieldName);
620 if (!fieldType) {
621 auto diag = emitOpError("referenced non-existent field ") << fieldName;
622 diag.attachNote(classDef.getLoc()) << "class defined here";
623 return diag;
624 }
625
626 // Verify the result type matches the field type.
627 if (getResult().getType() != fieldType.value())
628 return emitOpError("expected type ")
629 << getResult().getType() << ", but accessed field has type "
630 << fieldType.value();
631 return success();
632}
633
634//===----------------------------------------------------------------------===//
635// ElaboratedObjectOp
636//===----------------------------------------------------------------------===//
637
638void circt::om::ElaboratedObjectOp::build(OpBuilder &odsBuilder,
639 OperationState &odsState,
640 om::ClassLike classOp,
641 ValueRange fieldValues) {
642 return build(odsBuilder, odsState,
643 om::ClassType::get(
644 odsBuilder.getContext(),
645 mlir::FlatSymbolRefAttr::get(classOp.getSymNameAttr())),
646 mlir::FlatSymbolRefAttr::get(classOp.getSymNameAttr()),
647 fieldValues);
648}
649
650LogicalResult circt::om::ElaboratedObjectOp::verifySymbolUses(
651 SymbolTableCollection &symbolTable) {
652 auto classDef =
653 verifyClassLikeSymbolUser((*this), symbolTable, getResult().getType(),
654 getClassNameAttr().getAttr());
655 if (failed(classDef))
656 return failure();
657
658 auto fieldNames = classDef->getFieldNames();
659 auto fieldValues = getFieldValues();
660 if (fieldValues.size() != fieldNames.size())
661 return emitOpError("field value list doesn't match class field list, "
662 "expected ")
663 << fieldNames.size() << " values but got " << fieldValues.size();
664
665 for (auto [fieldName, fieldValue] : llvm::zip(fieldNames, fieldValues)) {
666 Type expectedType =
667 classDef->getFieldType(cast<StringAttr>(fieldName)).value();
668 if (fieldValue.getType() != expectedType)
669 return emitOpError("field value type for ")
670 << cast<StringAttr>(fieldName) << " (" << fieldValue.getType()
671 << ") doesn't match class field type (" << expectedType << ')';
672 }
673
674 return success();
675}
676
677//===----------------------------------------------------------------------===//
678// ConstantOp
679//===----------------------------------------------------------------------===//
680
681void circt::om::ConstantOp::build(::mlir::OpBuilder &odsBuilder,
682 ::mlir::OperationState &odsState,
683 ::mlir::TypedAttr constVal) {
684 return build(odsBuilder, odsState, constVal.getType(), constVal);
685}
686
687OpFoldResult circt::om::ConstantOp::fold(FoldAdaptor adaptor) {
688 assert(adaptor.getOperands().empty() && "constant has no operands");
689 return getValueAttr();
690}
691
692//===----------------------------------------------------------------------===//
693// ListCreateOp
694//===----------------------------------------------------------------------===//
695
696void circt::om::ListCreateOp::print(OpAsmPrinter &p) {
697 p << " ";
698 p.printOperands(getInputs());
699 p.printOptionalAttrDict((*this)->getAttrs());
700 p << " : " << getType().getElementType();
701}
702
703ParseResult circt::om::ListCreateOp::parse(OpAsmParser &parser,
704 OperationState &result) {
705 llvm::SmallVector<OpAsmParser::UnresolvedOperand, 16> operands;
706 Type elemType;
707
708 if (parser.parseOperandList(operands) ||
709 parser.parseOptionalAttrDict(result.attributes) || parser.parseColon() ||
710 parser.parseType(elemType))
711 return failure();
712 result.addTypes({circt::om::ListType::get(elemType)});
713
714 for (auto operand : operands)
715 if (parser.resolveOperand(operand, elemType, result.operands))
716 return failure();
717 return success();
718}
719
720//===----------------------------------------------------------------------===//
721// BasePathCreateOp
722//===----------------------------------------------------------------------===//
723
724LogicalResult
725BasePathCreateOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
726 auto hierPath = symbolTable.lookupNearestSymbolFrom<hw::HierPathOp>(
727 *this, getTargetAttr());
728 if (!hierPath)
729 return emitOpError("invalid symbol reference");
730 return success();
731}
732
733//===----------------------------------------------------------------------===//
734// PathCreateOp
735//===----------------------------------------------------------------------===//
736
737LogicalResult
738PathCreateOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
739 auto hierPath = symbolTable.lookupNearestSymbolFrom<hw::HierPathOp>(
740 *this, getTargetAttr());
741 if (!hierPath)
742 return emitOpError("invalid symbol reference");
743 return success();
744}
745
746//===----------------------------------------------------------------------===//
747// IntegerBinaryOp (arithmetic)
748//===----------------------------------------------------------------------===//
749
750static APSInt getAPSIntForOMIntegerAttr(circt::om::IntegerAttr attr) {
751 auto value = attr.getValue();
752 if (value.getType().isSignedInteger())
753 return value.getAPSInt();
754
755 // OM integers use signed semantics, but their underlying IntegerAttr may be
756 // signless. Construct an APSInt with signed interpretation explicitly instead
757 // of calling IntegerAttr::getAPSInt(), which asserts for signless integers.
758 return APSInt(value.getValue(), /*isUnsigned=*/false);
759}
760
762 llvm::function_ref<FailureOr<APSInt>(const APSInt &, const APSInt &)>;
763
764static OpFoldResult foldIntegerBinaryArithmetic(Attribute lhsAttr,
765 Attribute rhsAttr,
766 IntegerBinaryFn evaluate) {
767 auto lhs = dyn_cast_or_null<circt::om::IntegerAttr>(lhsAttr);
768 auto rhs = dyn_cast_or_null<circt::om::IntegerAttr>(rhsAttr);
769 if (!lhs || !rhs)
770 return {};
771 // Extend values if necessary to match bitwidth. Most interesting arithmetic
772 // on APSInt asserts that both operands are the same bitwidth, but the
773 // IntegerAttrs we are working with may have used the smallest necessary
774 // bitwidth to represent the number they hold, and won't necessarily match.
775 APSInt lhsVal = getAPSIntForOMIntegerAttr(lhs);
776 APSInt rhsVal = getAPSIntForOMIntegerAttr(rhs);
777 if (lhsVal.getBitWidth() > rhsVal.getBitWidth())
778 rhsVal = rhsVal.extend(lhsVal.getBitWidth());
779 else if (rhsVal.getBitWidth() > lhsVal.getBitWidth())
780 lhsVal = lhsVal.extend(rhsVal.getBitWidth());
781
782 // Perform arbitrary precision signed integer binary arithmetic.
783 auto result = evaluate(lhsVal, rhsVal);
784 if (failed(result))
785 return {};
786
787 // Return the result as a new om::IntegerAttr.
788 auto *ctx = lhsAttr.getContext();
789 return circt::om::IntegerAttr::get(
790 ctx, mlir::IntegerAttr::get(ctx, result.value()));
791}
792
793//===----------------------------------------------------------------------===//
794// IntegerAddOp
795//===----------------------------------------------------------------------===//
796
797OpFoldResult IntegerAddOp::fold(FoldAdaptor adaptor) {
799 adaptor.getLhs(), adaptor.getRhs(),
800 [](const APSInt &lhs, const APSInt &rhs) { return success(lhs + rhs); });
801}
802
803//===----------------------------------------------------------------------===//
804// IntegerMulOp
805//===----------------------------------------------------------------------===//
806
807OpFoldResult IntegerMulOp::fold(FoldAdaptor adaptor) {
809 adaptor.getLhs(), adaptor.getRhs(),
810 [](const APSInt &lhs, const APSInt &rhs) { return success(lhs * rhs); });
811}
812
813//===----------------------------------------------------------------------===//
814// IntegerShrOp
815//===----------------------------------------------------------------------===//
816
817OpFoldResult IntegerShrOp::fold(FoldAdaptor adaptor) {
819 adaptor.getLhs(), adaptor.getRhs(),
820 [&](const APSInt &lhs, const APSInt &rhs) -> FailureOr<APSInt> {
821 // Check non-negative constraint from operation semantics.
822 if (!rhs.isNonNegative())
823 return (emitOpError("shift amount must be non-negative"), failure());
824 // Check size constraint from implementation detail of using
825 // getExtValue.
826 if (!rhs.isRepresentableByInt64())
827 return (emitOpError("shift amount must be representable in 64 bits"),
828 failure());
829 return success(lhs >> rhs.getExtValue());
830 });
831}
832
833//===----------------------------------------------------------------------===//
834// IntegerShlOp
835//===----------------------------------------------------------------------===//
836
837OpFoldResult IntegerShlOp::fold(FoldAdaptor adaptor) {
839 adaptor.getLhs(), adaptor.getRhs(),
840 [&](const APSInt &lhs, const APSInt &rhs) -> FailureOr<APSInt> {
841 // Check non-negative constraint from operation semantics.
842 if (!rhs.isNonNegative())
843 return (emitOpError("shift amount must be non-negative"), failure());
844 // Check size constraint from implementation detail of using
845 // getExtValue.
846 if (!rhs.isRepresentableByInt64())
847 return (emitOpError("shift amount must be representable in 64 bits"),
848 failure());
849 int64_t shiftAmt = rhs.getExtValue();
850 // Extend lhs to lhsWidth + shiftAmt bits so no bits are truncated.
851 return success(lhs.extend(lhs.getBitWidth() + shiftAmt) << shiftAmt);
852 });
853}
854
855//===----------------------------------------------------------------------===//
856// StringConcatOp
857//===----------------------------------------------------------------------===//
858
859OpFoldResult StringConcatOp::fold(FoldAdaptor adaptor) {
860 // Fold single-operand concat to just the operand.
861 if (getStrings().size() == 1) {
862 if (auto strAttr = adaptor.getStrings()[0])
863 return strAttr;
864
865 return getStrings()[0];
866 }
867
868 // Check if all operands are constant strings before accumulating.
869 if (!llvm::all_of(adaptor.getStrings(), [](Attribute operand) {
870 return isa_and_nonnull<StringAttr>(operand);
871 }))
872 return {};
873
874 // All operands are constant strings, concatenate them.
875 SmallString<64> result;
876 for (auto operand : adaptor.getStrings())
877 result += cast<StringAttr>(operand).getValue();
878
879 return StringAttr::get(result, getResult().getType());
880}
881
882namespace {
883/// Flatten nested string.concat operations into a single concat.
884/// string.concat(a, string.concat(b, c), d) -> string.concat(a, b, c, d)
885class FlattenOMStringConcat : public mlir::OpRewritePattern<StringConcatOp> {
886public:
887 using OpRewritePattern::OpRewritePattern;
888
889 LogicalResult
890 matchAndRewrite(StringConcatOp concat,
891 mlir::PatternRewriter &rewriter) const override {
892
893 // Check if any operands are nested concats with a single use. Only inline
894 // single-use nested concats to avoid fighting with DCE.
895 bool hasNestedConcat = llvm::any_of(concat.getStrings(), [](Value operand) {
896 auto nestedConcat = operand.getDefiningOp<StringConcatOp>();
897 return nestedConcat && operand.hasOneUse();
898 });
899
900 if (!hasNestedConcat)
901 return failure();
902
903 // Flatten nested concats that have a single use.
904 SmallVector<Value> flatOperands;
905 for (auto input : concat.getStrings()) {
906 if (auto nestedConcat = input.getDefiningOp<StringConcatOp>();
907 nestedConcat && input.hasOneUse())
908 llvm::append_range(flatOperands, nestedConcat.getStrings());
909 else
910 flatOperands.push_back(input);
911 }
912
913 rewriter.modifyOpInPlace(concat,
914 [&]() { concat->setOperands(flatOperands); });
915 return success();
916 }
917};
918
919/// Merge consecutive constant strings in a concat and remove empty strings.
920/// string.concat("a", "b", x, "", "c", "d") -> string.concat("ab", x, "cd")
921class MergeAdjacentOMStringConstants
922 : public mlir::OpRewritePattern<StringConcatOp> {
923public:
924 using OpRewritePattern::OpRewritePattern;
925
926 LogicalResult
927 matchAndRewrite(StringConcatOp concat,
928 mlir::PatternRewriter &rewriter) const override {
929
930 SmallVector<Value> newOperands;
931 SmallString<64> accumulatedLit;
932 SmallVector<ConstantOp> accumulatedOps;
933 bool changed = false;
934
935 auto flushLiterals = [&]() {
936 if (accumulatedOps.empty())
937 return;
938
939 // If only one literal, reuse it.
940 if (accumulatedOps.size() == 1) {
941 newOperands.push_back(accumulatedOps[0]);
942 } else {
943 // Multiple literals - merge them.
944 auto newLit = rewriter.createOrFold<ConstantOp>(
945 concat.getLoc(),
946 StringAttr::get(accumulatedLit, concat.getResult().getType()));
947 newOperands.push_back(newLit);
948 changed = true;
949 }
950 accumulatedLit.clear();
951 accumulatedOps.clear();
952 };
953
954 for (auto operand : concat.getStrings()) {
955 if (auto litOp = operand.getDefiningOp<ConstantOp>()) {
956 if (auto strAttr = dyn_cast<StringAttr>(litOp.getValue())) {
957 // Skip empty strings.
958 if (strAttr.getValue().empty()) {
959 changed = true;
960 continue;
961 }
962 accumulatedLit += strAttr.getValue();
963 accumulatedOps.push_back(litOp);
964 continue;
965 }
966 }
967
968 flushLiterals();
969 newOperands.push_back(operand);
970 }
971
972 // Flush any remaining literals.
973 flushLiterals();
974
975 if (!changed)
976 return failure();
977
978 // If no operands remain, replace with empty string.
979 if (newOperands.empty())
980 return rewriter.replaceOpWithNewOp<ConstantOp>(
981 concat, StringAttr::get("", concat.getResult().getType())),
982 success();
983
984 // Single-operand case is handled by the folder.
985 rewriter.modifyOpInPlace(concat,
986 [&]() { concat->setOperands(newOperands); });
987 return success();
988 }
989};
990
991} // namespace
992
993void StringConcatOp::getCanonicalizationPatterns(RewritePatternSet &results,
994 MLIRContext *context) {
995 results.insert<FlattenOMStringConcat, MergeAdjacentOMStringConstants>(
996 context);
997}
998
999//===----------------------------------------------------------------------===//
1000// PropEqOp
1001//===----------------------------------------------------------------------===//
1002
1003static FailureOr<mlir::Attribute>
1004evaluateBinaryEquality(mlir::Attribute lhsAttr, mlir::Attribute rhsAttr) {
1005 auto resultType = mlir::IntegerType::get(lhsAttr.getContext(), 1);
1006
1007 // String equality.
1008 if (auto lhs = dyn_cast<mlir::StringAttr>(lhsAttr))
1009 if (auto rhs = dyn_cast<mlir::StringAttr>(rhsAttr))
1010 return mlir::Attribute(
1011 mlir::IntegerAttr::get(resultType, lhs == rhs ? 1 : 0));
1012
1013 // OM integer equality (arbitrary precision).
1014 if (auto lhs = dyn_cast<circt::om::IntegerAttr>(lhsAttr))
1015 if (auto rhs = dyn_cast<circt::om::IntegerAttr>(rhsAttr)) {
1016 APSInt lhsVal = getAPSIntForOMIntegerAttr(lhs);
1017 APSInt rhsVal = getAPSIntForOMIntegerAttr(rhs);
1018 if (lhsVal.getBitWidth() > rhsVal.getBitWidth())
1019 rhsVal = rhsVal.extend(lhsVal.getBitWidth());
1020 else if (rhsVal.getBitWidth() > lhsVal.getBitWidth())
1021 lhsVal = lhsVal.extend(rhsVal.getBitWidth());
1022 return mlir::Attribute(
1023 mlir::IntegerAttr::get(resultType, lhsVal == rhsVal ? 1 : 0));
1024 }
1025
1026 // Boolean (i1) equality.
1027 if (auto lhs = dyn_cast<mlir::IntegerAttr>(lhsAttr))
1028 if (auto rhs = dyn_cast<mlir::IntegerAttr>(rhsAttr))
1029 return mlir::Attribute(
1030 mlir::IntegerAttr::get(resultType, lhs == rhs ? 1 : 0));
1031
1032 return failure();
1033}
1034
1035OpFoldResult PropEqOp::fold(FoldAdaptor adaptor) {
1036 auto lhsAttr = adaptor.getLhs();
1037 auto rhsAttr = adaptor.getRhs();
1038 if (!lhsAttr || !rhsAttr)
1039 return {};
1040
1041 auto result = evaluateBinaryEquality(lhsAttr, rhsAttr);
1042 if (failed(result))
1043 return {};
1044
1045 return *result;
1046}
1047
1048//===----------------------------------------------------------------------===//
1049// IntegerAndOp / IntegerOrOp / IntegerXorOp
1050//===----------------------------------------------------------------------===//
1051
1052static OpFoldResult foldIntegerBitwise(Attribute lhsAttr, Attribute rhsAttr,
1053 IntegerBinaryFn evaluate) {
1054 auto lhsInt = dyn_cast_or_null<mlir::IntegerAttr>(lhsAttr);
1055 auto rhsInt = dyn_cast_or_null<mlir::IntegerAttr>(rhsAttr);
1056 if (!lhsInt || !rhsInt)
1057 return {};
1058 APSInt lhsVal(lhsInt.getValue());
1059 APSInt rhsVal(rhsInt.getValue());
1060 auto result = evaluate(lhsVal, rhsVal);
1061 if (failed(result))
1062 return {};
1063 return mlir::IntegerAttr::get(
1064 lhsInt.getType(), result->extOrTrunc(lhsInt.getValue().getBitWidth()));
1065}
1066
1067// Returns true if attr is an IntegerAttr whose value is all-zeros.
1068static bool isZeroInt(Attribute a) {
1069 auto i = dyn_cast_or_null<mlir::IntegerAttr>(a);
1070 return i && i.getValue().isZero();
1071}
1072
1073// Returns true if attr is an IntegerAttr whose value is all-ones.
1074static bool isAllOnesInt(Attribute a) {
1075 auto i = dyn_cast_or_null<mlir::IntegerAttr>(a);
1076 return i && i.getValue().isAllOnes();
1077}
1078
1079OpFoldResult IntegerAndOp::fold(FoldAdaptor adaptor) {
1080 if (auto result = foldIntegerBitwise(
1081 adaptor.getLhs(), adaptor.getRhs(),
1082 [](const APSInt &lhs, const APSInt &rhs) {
1083 return success(APSInt(lhs & rhs, /*isUnsigned=*/false));
1084 }))
1085 return result;
1086 // AND with all-zeros is always zero.
1087 if (isZeroInt(adaptor.getLhs()) || isZeroInt(adaptor.getRhs()))
1088 return mlir::IntegerAttr::get(getResult().getType(),
1089 APInt::getZero(getType().getWidth()));
1090 // AND with all-ones is identity.
1091 if (isAllOnesInt(adaptor.getLhs()))
1092 return getRhs();
1093 if (isAllOnesInt(adaptor.getRhs()))
1094 return getLhs();
1095 return {};
1096}
1097
1098OpFoldResult IntegerOrOp::fold(FoldAdaptor adaptor) {
1099 if (auto result = foldIntegerBitwise(
1100 adaptor.getLhs(), adaptor.getRhs(),
1101 [](const APSInt &lhs, const APSInt &rhs) {
1102 return success(APSInt(lhs | rhs, /*isUnsigned=*/false));
1103 }))
1104 return result;
1105 // OR with all-ones is always all-ones.
1106 if (isAllOnesInt(adaptor.getLhs()) || isAllOnesInt(adaptor.getRhs()))
1107 return mlir::IntegerAttr::get(getResult().getType(),
1108 APInt::getAllOnes(getType().getWidth()));
1109 // OR with all-zeros is identity.
1110 if (isZeroInt(adaptor.getLhs()))
1111 return getRhs();
1112 if (isZeroInt(adaptor.getRhs()))
1113 return getLhs();
1114 return {};
1115}
1116
1117OpFoldResult IntegerXorOp::fold(FoldAdaptor adaptor) {
1118 if (auto result = foldIntegerBitwise(
1119 adaptor.getLhs(), adaptor.getRhs(),
1120 [](const APSInt &lhs, const APSInt &rhs) {
1121 return success(APSInt(lhs ^ rhs, /*isUnsigned=*/false));
1122 }))
1123 return result;
1124 // XOR with all-zeros is identity.
1125 if (isZeroInt(adaptor.getLhs()))
1126 return getRhs();
1127 if (isZeroInt(adaptor.getRhs()))
1128 return getLhs();
1129 return {};
1130}
1131
1132//===----------------------------------------------------------------------===//
1133// UnknownValueOp
1134//===----------------------------------------------------------------------===//
1135
1136LogicalResult circt::om::UnknownValueOp::verifySymbolUses(
1137 SymbolTableCollection &symbolTable) {
1138
1139 // Unknown values of non-class type don't need to be verified.
1140 auto classType = dyn_cast<ClassType>(getType());
1141 if (!classType)
1142 return success();
1143
1144 // Verify the referred to ClassOp exists.
1145 auto className = classType.getClassName();
1146 if (symbolTable.lookupNearestSymbolFrom<ClassLike>(*this, className))
1147 return success();
1148
1149 return emitOpError() << "refers to non-existant class (\""
1150 << className.getValue() << "\")";
1151}
1152
1153//===----------------------------------------------------------------------===//
1154// TableGen generated logic.
1155//===----------------------------------------------------------------------===//
1156
1157#define GET_OP_CLASSES
1158#include "circt/Dialect/OM/OM.cpp.inc"
assert(baseType &&"element must be base type")
static std::unique_ptr< Context > context
static Location getLoc(DefSlot slot)
Definition Mem2Reg.cpp:222
static OpFoldResult foldIntegerBitwise(Attribute lhsAttr, Attribute rhsAttr, IntegerBinaryFn evaluate)
Definition OMOps.cpp:1052
static ParseResult parseClassLike(OpAsmParser &parser, OperationState &state)
Definition OMOps.cpp:134
LogicalResult verifyClassLike(ClassLike classLike)
Definition OMOps.cpp:246
std::optional< Type > getClassLikeFieldType(ClassLike classLike, StringAttr name)
Definition OMOps.cpp:285
void getClassLikeAsmBlockArgumentNames(ClassLike classLike, Region &region, OpAsmSetValueNameFn setNameFn)
Definition OMOps.cpp:263
static ParseResult parseBasePathString(OpAsmParser &parser, PathAttr &path)
Definition OMOps.cpp:28
static ParseResult parsePathString(OpAsmParser &parser, PathAttr &path, StringAttr &module, StringAttr &ref, StringAttr &field)
Definition OMOps.cpp:50
static APSInt getAPSIntForOMIntegerAttr(circt::om::IntegerAttr attr)
Definition OMOps.cpp:750
static void printBasePathString(OpAsmPrinter &p, Operation *op, PathAttr path)
Definition OMOps.cpp:39
llvm::function_ref< FailureOr< APSInt >(const APSInt &, const APSInt &)> IntegerBinaryFn
Definition OMOps.cpp:762
static FailureOr< ClassLike > verifyClassLikeSymbolUser(Operation *op, SymbolTableCollection &symbolTable, ClassType resultType, StringAttr className)
Definition OMOps.cpp:553
static void printFieldLocs(OpAsmPrinter &printer, Operation *op, ArrayAttr fieldLocs)
Definition OMOps.cpp:89
static bool isZeroInt(Attribute a)
Definition OMOps.cpp:1068
static ParseResult parseFieldLocs(OpAsmParser &parser, ArrayAttr &fieldLocs)
Definition OMOps.cpp:79
static ParseResult parseClassFieldsList(OpAsmParser &parser, SmallVectorImpl< Attribute > &fieldNames, SmallVectorImpl< Type > &fieldTypes)
Definition OMOps.cpp:102
static FailureOr< mlir::Attribute > evaluateBinaryEquality(mlir::Attribute lhsAttr, mlir::Attribute rhsAttr)
Definition OMOps.cpp:1004
static void printClassLike(ClassLike classLike, OpAsmPrinter &printer)
Definition OMOps.cpp:191
void replaceClassLikeFieldTypes(ClassLike classLike, AttrTypeReplacer &replacer)
Definition OMOps.cpp:295
NamedAttribute makeFieldType(StringAttr name, Type type)
Definition OMOps.cpp:275
NamedAttribute makeFieldIdx(MLIRContext *ctx, mlir::StringAttr name, unsigned i)
Definition OMOps.cpp:279
static void printPathString(OpAsmPrinter &p, Operation *op, PathAttr path, StringAttr module, StringAttr ref, StringAttr field)
Definition OMOps.cpp:64
static OpFoldResult foldIntegerBinaryArithmetic(Attribute lhsAttr, Attribute rhsAttr, IntegerBinaryFn evaluate)
Definition OMOps.cpp:764
static bool isAllOnesInt(Attribute a)
Definition OMOps.cpp:1074
static Block * getBodyBlock(FModuleLike mod)
static InstancePath empty
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition CalyxOps.cpp:56
uint64_t getWidth(Type t)
Definition ESIPasses.cpp:32
void error(Twine message)
Definition LSPUtils.cpp:16
ParseResult parsePath(MLIRContext *context, StringRef spelling, PathAttr &path, StringAttr &module, StringAttr &ref, StringAttr &field)
Parse a target string in to a path.
Definition OMUtils.cpp:182
ParseResult parseBasePath(MLIRContext *context, StringRef spelling, PathAttr &path)
Parse a target string of the form "Foo/bar:Bar/baz" in to a base path.
Definition OMUtils.cpp:177
function_ref< void(Value, StringRef)> OpAsmSetValueNameFn
Definition LLVM.h:193
A module name, and the name of an instance inside that module.
mlir::StringAttr mlir::StringAttr instance