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