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
134template <typename ClassTy>
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, ClassTy::getSymNameAttrName(state.name),
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 =
197 mlir::SymbolOpInterface::getDefaultVisibilityAttrName();
198 if (auto visibility =
199 classLike->getAttrOfType<StringAttr>(visibilityAttrName))
200 printer << visibility.getValue() << ' ';
201
202 // Print the Class symbol name.
203 printer.printSymbolName(classLike.getSymName());
204
205 // Retrieve the formal parameter names and values.
206 auto argNames = SmallVector<StringRef>(
207 classLike.getFormalParamNames().getAsValueRange<StringAttr>());
208 ArrayRef<BlockArgument> args = classLike.getBodyBlock()->getArguments();
209
210 // Print the formal parameters.
211 printer << '(';
212 for (size_t i = 0, e = args.size(); i < e; ++i) {
213 printer << '%' << argNames[i] << ": " << args[i].getType();
214 if (i < e - 1)
215 printer << ", ";
216 }
217 printer << ") ";
218
219 ArrayRef<Attribute> fieldNames =
220 cast<ArrayAttr>(classLike->getAttr("fieldNames")).getValue();
221
222 if (!fieldNames.empty()) {
223 printer << " -> (";
224 for (size_t i = 0, e = fieldNames.size(); i < e; ++i) {
225 if (i != 0)
226 printer << ", ";
227 StringAttr name = cast<StringAttr>(fieldNames[i]);
228 printer.printKeywordOrString(name.getValue());
229 printer << ": ";
230 Type type = classLike.getFieldType(name).value();
231 printer.printType(type);
232 }
233 printer << ") ";
234 }
235
236 // Print the optional attribute dictionary.
237 SmallVector<StringRef> elidedAttrs{
238 classLike.getSymNameAttrName(), classLike.getFormalParamNamesAttrName(),
239 visibilityAttrName, "fieldTypes", "fieldNames"};
240 printer.printOptionalAttrDictWithKeyword(classLike.getOperation()->getAttrs(),
241 elidedAttrs);
242
243 // Print the body.
244 printer.printRegion(classLike.getBody(), /*printEntryBlockArgs=*/false,
245 /*printBlockTerminators=*/true);
246}
247
248LogicalResult verifyClassLike(ClassLike classLike) {
249 // Verify the formal parameter names match up with the values.
250 if (classLike.getFormalParamNames().size() !=
251 classLike.getBodyBlock()->getArguments().size()) {
252 auto error = classLike.emitOpError(
253 "formal parameter name list doesn't match formal parameter value list");
254 error.attachNote(classLike.getLoc())
255 << "formal parameter names: " << classLike.getFormalParamNames();
256 error.attachNote(classLike.getLoc())
257 << "formal parameter values: "
258 << classLike.getBodyBlock()->getArguments();
259 return error;
260 }
261
262 return success();
263}
264
265void getClassLikeAsmBlockArgumentNames(ClassLike classLike, Region &region,
266 OpAsmSetValueNameFn setNameFn) {
267 // Retrieve the formal parameter names and values.
268 auto argNames = SmallVector<StringRef>(
269 classLike.getFormalParamNames().getAsValueRange<StringAttr>());
270 ArrayRef<BlockArgument> args = classLike.getBodyBlock()->getArguments();
271
272 // Use the formal parameter names as the SSA value names.
273 for (size_t i = 0, e = args.size(); i < e; ++i)
274 setNameFn(args[i], argNames[i]);
275}
276
277NamedAttribute makeFieldType(StringAttr name, Type type) {
278 return NamedAttribute(name, TypeAttr::get(type));
279}
280
281NamedAttribute makeFieldIdx(MLIRContext *ctx, mlir::StringAttr name,
282 unsigned i) {
283 return NamedAttribute(StringAttr(name),
284 mlir::IntegerAttr::get(mlir::IndexType::get(ctx), i));
285}
286
287std::optional<Type> getClassLikeFieldType(ClassLike classLike,
288 StringAttr name) {
289 DictionaryAttr fieldTypes = mlir::cast<DictionaryAttr>(
290 classLike.getOperation()->getAttr("fieldTypes"));
291 Attribute type = fieldTypes.get(name);
292 if (auto field = dyn_cast_or_null<TypeAttr>(type))
293 return field.getValue();
294 return std::nullopt;
295}
296
297void replaceClassLikeFieldTypes(ClassLike classLike,
298 AttrTypeReplacer &replacer) {
299 classLike->setAttr("fieldTypes", cast<DictionaryAttr>(replacer.replace(
300 classLike.getFieldTypes())));
301}
302
303//===----------------------------------------------------------------------===//
304// ClassOp
305//===----------------------------------------------------------------------===//
306
307ParseResult circt::om::ClassOp::parse(OpAsmParser &parser,
308 OperationState &state) {
309 return parseClassLike<ClassOp>(parser, state);
310}
311
312circt::om::ClassOp circt::om::ClassOp::buildSimpleClassOp(
313 OpBuilder &odsBuilder, Location loc, Twine name,
314 ArrayRef<StringRef> formalParamNames, ArrayRef<StringRef> fieldNames,
315 ArrayRef<Type> fieldTypes) {
316 circt::om::ClassOp classOp = circt::om::ClassOp::create(
317 odsBuilder, loc, odsBuilder.getStringAttr(name), /*sym_visibility=*/{},
318 odsBuilder.getStrArrayAttr(formalParamNames),
319 odsBuilder.getStrArrayAttr(fieldNames),
320 odsBuilder.getDictionaryAttr(llvm::map_to_vector(
321 llvm::zip(fieldNames, fieldTypes), [&](auto field) -> NamedAttribute {
322 return NamedAttribute(odsBuilder.getStringAttr(std::get<0>(field)),
323 TypeAttr::get(std::get<1>(field)));
324 })));
325 Block *body = &classOp.getRegion().emplaceBlock();
326 auto prevLoc = odsBuilder.saveInsertionPoint();
327 odsBuilder.setInsertionPointToEnd(body);
328
329 mlir::SmallVector<Attribute> locAttrs(fieldNames.size(), LocationAttr(loc));
330
331 ClassFieldsOp::create(odsBuilder, loc,
332 llvm::map_to_vector(fieldTypes,
333 [&](Type type) -> Value {
334 return body->addArgument(type,
335 loc);
336 }),
337 odsBuilder.getArrayAttr(locAttrs));
338
339 odsBuilder.restoreInsertionPoint(prevLoc);
340
341 return classOp;
342}
343
344void circt::om::ClassOp::print(OpAsmPrinter &printer) {
345 printClassLike(*this, printer);
346}
347
348LogicalResult circt::om::ClassOp::verify() {
349 if (getLinkonceOdr() && !isPublic())
350 return emitOpError("linkonce_odr requires public visibility");
351 return verifyClassLike(*this);
352}
353
354LogicalResult circt::om::ClassOp::verifyRegions() {
355 auto fieldsOp =
356 dyn_cast_or_null<ClassFieldsOp>(this->getBodyBlock()->getTerminator());
357 if (!fieldsOp)
358 return this->emitOpError("expected terminator to be ClassFieldsOp");
359
360 // The number of results matches the number of terminator operands.
361 if (fieldsOp.getNumOperands() != this->getFieldNames().size()) {
362 auto diag = this->emitOpError()
363 << "returns '" << this->getFieldNames().size()
364 << "' fields, but its terminator returned '"
365 << fieldsOp.getNumOperands() << "' fields";
366 return diag.attachNote(fieldsOp.getLoc()) << "see terminator:";
367 }
368
369 // The type of each result matches the corresponding terminator operand type.
370 auto types = this->getFieldTypes();
371 for (auto [fieldName, terminatorOperandType] :
372 llvm::zip(this->getFieldNames(), fieldsOp.getOperandTypes())) {
373
374 auto fieldNameAttr = dyn_cast_or_null<StringAttr>(fieldName);
375 if (!fieldNameAttr)
376 return this->emitOpError("field name is not a StringAttr");
377
378 if (auto fieldType = types.get(fieldNameAttr))
379 if (auto typeAttr = dyn_cast<TypeAttr>(fieldType))
380 if (typeAttr.getValue() == terminatorOperandType)
381 continue;
382
383 auto diag = this->emitOpError()
384 << "returns different field types than its terminator";
385 return diag.attachNote(fieldsOp.getLoc()) << "see terminator:";
386 }
387
388 return success();
389}
390
391void circt::om::ClassOp::getAsmBlockArgumentNames(
392 Region &region, OpAsmSetValueNameFn setNameFn) {
393 getClassLikeAsmBlockArgumentNames(*this, region, setNameFn);
394}
395
396std::optional<mlir::Type>
397circt::om::ClassOp::getFieldType(mlir::StringAttr field) {
398 return getClassLikeFieldType(*this, field);
399}
400
401void circt::om::ClassOp::replaceFieldTypes(AttrTypeReplacer &replacer) {
402 replaceClassLikeFieldTypes(*this, replacer);
403}
404
405void circt::om::ClassOp::updateFields(
406 mlir::ArrayRef<mlir::Location> newLocations,
407 mlir::ArrayRef<mlir::Value> newValues,
408 mlir::ArrayRef<mlir::Attribute> newNames) {
409
410 auto fieldsOp = getFieldsOp();
411 assert(fieldsOp && "The fields op should exist");
412 // Get field names.
413 SmallVector<Attribute> names(getFieldNamesAttr().getAsRange<StringAttr>());
414 // Get the field types.
415 SmallVector<NamedAttribute> fieldTypes(getFieldTypesAttr().getValue());
416 // Get the field values.
417 SmallVector<Value> fieldVals(fieldsOp.getFields());
418 // Get the field locations.
419 Location fieldOpLoc = fieldsOp->getLoc();
420
421 // Extract the locations per field.
422 SmallVector<Location> locations;
423 if (auto fl = dyn_cast<FusedLoc>(fieldOpLoc)) {
424 if (auto metadataArr = dyn_cast_or_null<ArrayAttr>(fl.getMetadata())) {
425 if (names.size() == metadataArr.size()) {
426 auto r = metadataArr.getAsRange<LocationAttr>();
427 locations.append(r.begin(), r.end());
428 } else {
429 locations.append(names.size(), fieldOpLoc);
430 }
431 } else {
432 auto fieldLocs = fieldsOp.getFieldLocs();
433 if (fieldLocs.has_value() && names.size() == fieldLocs.value().size()) {
434 auto r = fieldLocs.value().getAsRange<LocationAttr>();
435 locations.append(r.begin(), r.end());
436 } else {
437 locations.append(names.size(), fieldOpLoc);
438 }
439 }
440 } else {
441 // Assume same loc for every field.
442 locations.append(names.size(), fieldOpLoc);
443 }
444
445 // Append the new names, locations and values.
446 names.append(newNames.begin(), newNames.end());
447 locations.append(newLocations.begin(), newLocations.end());
448 fieldVals.append(newValues.begin(), newValues.end());
449
450 // Construct the new field types from values and names.
451 for (auto [v, n] : llvm::zip(newValues, newNames))
452 fieldTypes.emplace_back(
453 NamedAttribute(llvm::cast<StringAttr>(n), TypeAttr::get(v.getType())));
454
455 // Keep the locations as array on the metadata.
456 SmallVector<Attribute> locationsAttr;
457 llvm::for_each(locations, [&](Location &l) {
458 locationsAttr.push_back(cast<Attribute>(l));
459 });
460
461 ImplicitLocOpBuilder builder(getLoc(), *this);
462 // Update the field names attribute.
463 setFieldNamesAttr(builder.getArrayAttr(names));
464 // Update the fields type attribute.
465 setFieldTypesAttr(builder.getDictionaryAttr(fieldTypes));
466 fieldsOp.getFieldsMutable().assign(fieldVals);
467 // Update the location.
468 fieldsOp->setLoc(builder.getFusedLoc(
469 locations, ArrayAttr::get(getContext(), locationsAttr)));
470}
471
472void circt::om::ClassOp::addNewFieldsOp(mlir::OpBuilder &builder,
473 mlir::ArrayRef<Location> locs,
474 mlir::ArrayRef<Value> values) {
475 // Store the original locations as a metadata array so that unique locations
476 // are preserved as a mapping from field index to location
477 assert(locs.size() == values.size() && "Expected a location per value");
478 mlir::SmallVector<Attribute> locAttrs;
479 for (auto loc : locs) {
480 locAttrs.push_back(cast<Attribute>(LocationAttr(loc)));
481 }
482 // Also store the locations incase there's some other analysis that might
483 // be able to use the default FusedLoc representation.
484 ClassFieldsOp::create(builder, builder.getFusedLoc(locs), values,
485 builder.getArrayAttr(locAttrs));
486}
487
488mlir::Location circt::om::ClassOp::getFieldLocByIndex(size_t i) {
489 auto fieldsOp = this->getFieldsOp();
490 auto fieldLocs = fieldsOp.getFieldLocs();
491 if (!fieldLocs.has_value())
492 return fieldsOp.getLoc();
493 assert(i < fieldLocs.value().size() &&
494 "field index too large for location array");
495 return cast<LocationAttr>(fieldLocs.value()[i]);
496}
497
498//===----------------------------------------------------------------------===//
499// ClassExternOp
500//===----------------------------------------------------------------------===//
501
502ParseResult circt::om::ClassExternOp::parse(OpAsmParser &parser,
503 OperationState &state) {
504 return parseClassLike<ClassExternOp>(parser, state);
505}
506
507void circt::om::ClassExternOp::print(OpAsmPrinter &printer) {
508 printClassLike(*this, printer);
509}
510
511LogicalResult circt::om::ClassExternOp::verify() {
512 if (failed(verifyClassLike(*this))) {
513 return failure();
514 }
515 // Verify body is empty
516 if (!this->getBodyBlock()->getOperations().empty()) {
517 return this->emitOpError("external class body should be empty");
518 }
519
520 return success();
521}
522
523void circt::om::ClassExternOp::getAsmBlockArgumentNames(
524 Region &region, OpAsmSetValueNameFn setNameFn) {
525 getClassLikeAsmBlockArgumentNames(*this, region, setNameFn);
526}
527
528std::optional<mlir::Type>
529circt::om::ClassExternOp::getFieldType(mlir::StringAttr field) {
530 return getClassLikeFieldType(*this, field);
531}
532
533void circt::om::ClassExternOp::replaceFieldTypes(AttrTypeReplacer &replacer) {
534 replaceClassLikeFieldTypes(*this, replacer);
535}
536
537//===----------------------------------------------------------------------===//
538// ClassFieldsOp
539//===----------------------------------------------------------------------===//
540//
541LogicalResult circt::om::ClassFieldsOp::verify() {
542 auto fieldLocs = this->getFieldLocs();
543 if (fieldLocs.has_value()) {
544 auto fieldLocsVal = fieldLocs.value();
545 if (fieldLocsVal.size() != this->getFields().size()) {
546 auto error = this->emitOpError("size of field_locs (")
547 << fieldLocsVal.size()
548 << ") does not match number of fields ("
549 << this->getFields().size() << ")";
550 }
551 }
552 return success();
553}
554
555//===----------------------------------------------------------------------===//
556// ObjectOp
557//===----------------------------------------------------------------------===//
558
559void circt::om::ObjectOp::build(::mlir::OpBuilder &odsBuilder,
560 ::mlir::OperationState &odsState,
561 om::ClassOp classOp,
562 ::mlir::ValueRange actualParams) {
563 return build(odsBuilder, odsState,
564 om::ClassType::get(odsBuilder.getContext(),
565 mlir::FlatSymbolRefAttr::get(classOp)),
566 mlir::FlatSymbolRefAttr::get(classOp.getNameAttr()),
567 actualParams);
568}
569
570static FailureOr<ClassLike>
571verifyClassLikeSymbolUser(Operation *op, SymbolTableCollection &symbolTable,
572 ClassType resultType, StringAttr className) {
573 StringAttr resultClassName = resultType.getClassName().getAttr();
574 if (resultClassName != className)
575 return op->emitOpError("result type (")
576 << resultClassName << ") does not match referred to class ("
577 << className << ')';
578
579 auto classDef = dyn_cast_or_null<ClassLike>(
580 symbolTable.lookupNearestSymbolFrom(op, className));
581 if (!classDef)
582 return op->emitOpError("refers to non-existant class (")
583 << className << ')';
584 return classDef;
585}
586
587LogicalResult
588circt::om::ObjectOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
589 auto classDef =
590 verifyClassLikeSymbolUser((*this), symbolTable, getResult().getType(),
591 getClassNameAttr().getAttr());
592 if (failed(classDef))
593 return failure();
594
595 auto actualTypes = getActualParams().getTypes();
596 auto formalTypes = classDef->getBodyBlock()->getArgumentTypes();
597
598 // Verify the actual parameter list matches the formal parameter list.
599 if (actualTypes.size() != formalTypes.size()) {
600 auto error = emitOpError(
601 "actual parameter list doesn't match formal parameter list");
602 error.attachNote(classDef->getLoc())
603 << "formal parameters: " << classDef->getBodyBlock()->getArguments();
604 error.attachNote(getLoc()) << "actual parameters: " << getActualParams();
605 return error;
606 }
607
608 // Verify the actual parameter types match the formal parameter types.
609 for (size_t i = 0, e = actualTypes.size(); i < e; ++i) {
610 if (actualTypes[i] != formalTypes[i]) {
611 return emitOpError("actual parameter type (")
612 << actualTypes[i] << ") doesn't match formal parameter type ("
613 << formalTypes[i] << ')';
614 }
615 }
616
617 return success();
618}
619
620//===----------------------------------------------------------------------===//
621// ObjectFieldOp
622//===----------------------------------------------------------------------===//
623
624LogicalResult
625circt::om::ObjectFieldOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
626 auto classType = getObject().getType();
627 auto className = classType.getClassName().getAttr();
628
629 // Verify the referred-to class exists.
630 auto classDef = dyn_cast_or_null<ClassLike>(
631 symbolTable.lookupNearestSymbolFrom(*this, className));
632 if (!classDef)
633 return emitOpError("class ") << className << " was not found";
634
635 // Verify the field exists in the class.
636 auto fieldName = getFieldAttr();
637 std::optional<Type> fieldType = classDef.getFieldType(fieldName);
638 if (!fieldType) {
639 auto diag = emitOpError("referenced non-existent field ") << fieldName;
640 diag.attachNote(classDef.getLoc()) << "class defined here";
641 return diag;
642 }
643
644 // Verify the result type matches the field type.
645 if (getResult().getType() != fieldType.value())
646 return emitOpError("expected type ")
647 << getResult().getType() << ", but accessed field has type "
648 << fieldType.value();
649 return success();
650}
651
652//===----------------------------------------------------------------------===//
653// ElaboratedObjectOp
654//===----------------------------------------------------------------------===//
655
656void circt::om::ElaboratedObjectOp::build(OpBuilder &odsBuilder,
657 OperationState &odsState,
658 om::ClassLike classOp,
659 ValueRange fieldValues) {
660 return build(odsBuilder, odsState,
661 om::ClassType::get(
662 odsBuilder.getContext(),
663 mlir::FlatSymbolRefAttr::get(classOp.getSymNameAttr())),
664 mlir::FlatSymbolRefAttr::get(classOp.getSymNameAttr()),
665 fieldValues);
666}
667
668LogicalResult circt::om::ElaboratedObjectOp::verifySymbolUses(
669 SymbolTableCollection &symbolTable) {
670 auto classDef =
671 verifyClassLikeSymbolUser((*this), symbolTable, getResult().getType(),
672 getClassNameAttr().getAttr());
673 if (failed(classDef))
674 return failure();
675
676 auto fieldNames = classDef->getFieldNames();
677 auto fieldValues = getFieldValues();
678 if (fieldValues.size() != fieldNames.size())
679 return emitOpError("field value list doesn't match class field list, "
680 "expected ")
681 << fieldNames.size() << " values but got " << fieldValues.size();
682
683 for (auto [fieldName, fieldValue] : llvm::zip(fieldNames, fieldValues)) {
684 Type expectedType =
685 classDef->getFieldType(cast<StringAttr>(fieldName)).value();
686 if (fieldValue.getType() != expectedType)
687 return emitOpError("field value type for ")
688 << cast<StringAttr>(fieldName) << " (" << fieldValue.getType()
689 << ") doesn't match class field type (" << expectedType << ')';
690 }
691
692 return success();
693}
694
695//===----------------------------------------------------------------------===//
696// ConstantOp
697//===----------------------------------------------------------------------===//
698
699void circt::om::ConstantOp::build(::mlir::OpBuilder &odsBuilder,
700 ::mlir::OperationState &odsState,
701 ::mlir::TypedAttr constVal) {
702 return build(odsBuilder, odsState, constVal.getType(), constVal);
703}
704
705OpFoldResult circt::om::ConstantOp::fold(FoldAdaptor adaptor) {
706 assert(adaptor.getOperands().empty() && "constant has no operands");
707 return getValueAttr();
708}
709
710//===----------------------------------------------------------------------===//
711// ListCreateOp
712//===----------------------------------------------------------------------===//
713
714OpFoldResult circt::om::ListCreateOp::fold(FoldAdaptor adaptor) {
715 auto inputs = adaptor.getInputs();
716 if (!llvm::all_of(inputs, [](Attribute attr) { return attr; }))
717 return {};
718
719 return ListAttr::get(getContext(), getType().getElementType(),
720 ArrayAttr::get(getContext(), inputs));
721}
722
723void circt::om::ListCreateOp::print(OpAsmPrinter &p) {
724 p << " ";
725 p.printOperands(getInputs());
726 p.printOptionalAttrDict((*this)->getAttrs());
727 p << " : " << getType().getElementType();
728}
729
730ParseResult circt::om::ListCreateOp::parse(OpAsmParser &parser,
731 OperationState &result) {
732 llvm::SmallVector<OpAsmParser::UnresolvedOperand, 16> operands;
733 Type elemType;
734
735 if (parser.parseOperandList(operands) ||
736 parser.parseOptionalAttrDict(result.attributes) || parser.parseColon() ||
737 parser.parseType(elemType))
738 return failure();
739 result.addTypes({circt::om::ListType::get(elemType)});
740
741 for (auto operand : operands)
742 if (parser.resolveOperand(operand, elemType, result.operands))
743 return failure();
744 return success();
745}
746
747//===----------------------------------------------------------------------===//
748// ListConcatOp
749//===----------------------------------------------------------------------===//
750
751OpFoldResult circt::om::ListConcatOp::fold(FoldAdaptor adaptor) {
752 auto subLists = adaptor.getSubLists();
753 if (subLists.size() == 1)
754 return getSubLists().front();
755
756 SmallVector<Attribute> elements;
757 for (Attribute attr : subLists) {
758 auto listAttr = dyn_cast_or_null<ListAttr>(attr);
759 if (!listAttr)
760 return {};
761 llvm::append_range(elements, listAttr.getElements());
762 }
763
764 return ListAttr::get(getContext(), getType().getElementType(),
765 ArrayAttr::get(getContext(), elements));
766}
767
768//===----------------------------------------------------------------------===//
769// BasePathCreateOp
770//===----------------------------------------------------------------------===//
771
772LogicalResult
773BasePathCreateOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
774 auto hierPath = symbolTable.lookupNearestSymbolFrom<hw::HierPathOp>(
775 *this, getTargetAttr());
776 if (!hierPath)
777 return emitOpError("invalid symbol reference");
778 return success();
779}
780
781//===----------------------------------------------------------------------===//
782// PathCreateOp
783//===----------------------------------------------------------------------===//
784
785LogicalResult
786PathCreateOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
787 auto hierPath = symbolTable.lookupNearestSymbolFrom<hw::HierPathOp>(
788 *this, getTargetAttr());
789 if (!hierPath)
790 return emitOpError("invalid symbol reference");
791 return success();
792}
793
794//===----------------------------------------------------------------------===//
795// AnyCastOp
796//===----------------------------------------------------------------------===//
797
798OpFoldResult AnyCastOp::fold(FoldAdaptor adaptor) {
799 if (getInput().getType() == getType() && getInput() != getResult())
800 return getInput();
801 return {};
802}
803
804//===----------------------------------------------------------------------===//
805// IntegerBinaryOp (arithmetic)
806//===----------------------------------------------------------------------===//
807
808static APSInt getAPSIntForOMIntegerAttr(circt::om::IntegerAttr attr) {
809 auto value = attr.getValue();
810 if (value.getType().isSignedInteger())
811 return value.getAPSInt();
812
813 // OM integers use signed semantics, but their underlying IntegerAttr may be
814 // signless. Construct an APSInt with signed interpretation explicitly instead
815 // of calling IntegerAttr::getAPSInt(), which asserts for signless integers.
816 return APSInt(value.getValue(), /*isUnsigned=*/false);
817}
818
820 llvm::function_ref<FailureOr<APSInt>(const APSInt &, const APSInt &)>;
821
822static OpFoldResult foldIntegerBinaryArithmetic(Attribute lhsAttr,
823 Attribute rhsAttr,
824 IntegerBinaryFn evaluate) {
825 auto lhs = dyn_cast_or_null<circt::om::IntegerAttr>(lhsAttr);
826 auto rhs = dyn_cast_or_null<circt::om::IntegerAttr>(rhsAttr);
827 if (!lhs || !rhs)
828 return {};
829 // Extend values if necessary to match bitwidth. Most interesting arithmetic
830 // on APSInt asserts that both operands are the same bitwidth, but the
831 // IntegerAttrs we are working with may have used the smallest necessary
832 // bitwidth to represent the number they hold, and won't necessarily match.
833 APSInt lhsVal = getAPSIntForOMIntegerAttr(lhs);
834 APSInt rhsVal = getAPSIntForOMIntegerAttr(rhs);
835 if (lhsVal.getBitWidth() > rhsVal.getBitWidth())
836 rhsVal = rhsVal.extend(lhsVal.getBitWidth());
837 else if (rhsVal.getBitWidth() > lhsVal.getBitWidth())
838 lhsVal = lhsVal.extend(rhsVal.getBitWidth());
839
840 // Perform arbitrary precision signed integer binary arithmetic.
841 auto result = evaluate(lhsVal, rhsVal);
842 if (failed(result))
843 return {};
844
845 // Return the result as a new om::IntegerAttr.
846 auto *ctx = lhsAttr.getContext();
847 return circt::om::IntegerAttr::get(
848 ctx, mlir::IntegerAttr::get(ctx, result.value()));
849}
850
851//===----------------------------------------------------------------------===//
852// IntegerAddOp
853//===----------------------------------------------------------------------===//
854
855OpFoldResult IntegerAddOp::fold(FoldAdaptor adaptor) {
857 adaptor.getLhs(), adaptor.getRhs(),
858 [](const APSInt &lhs, const APSInt &rhs) { return success(lhs + rhs); });
859}
860
861//===----------------------------------------------------------------------===//
862// IntegerMulOp
863//===----------------------------------------------------------------------===//
864
865OpFoldResult IntegerMulOp::fold(FoldAdaptor adaptor) {
867 adaptor.getLhs(), adaptor.getRhs(),
868 [](const APSInt &lhs, const APSInt &rhs) { return success(lhs * rhs); });
869}
870
871//===----------------------------------------------------------------------===//
872// IntegerShrOp
873//===----------------------------------------------------------------------===//
874
875OpFoldResult IntegerShrOp::fold(FoldAdaptor adaptor) {
877 adaptor.getLhs(), adaptor.getRhs(),
878 [&](const APSInt &lhs, const APSInt &rhs) -> FailureOr<APSInt> {
879 // Check non-negative constraint from operation semantics.
880 if (!rhs.isNonNegative())
881 return (emitOpError("shift amount must be non-negative"), failure());
882 // Check size constraint from implementation detail of using
883 // getExtValue.
884 if (!rhs.isRepresentableByInt64())
885 return (emitOpError("shift amount must be representable in 64 bits"),
886 failure());
887 return success(lhs >> rhs.getExtValue());
888 });
889}
890
891//===----------------------------------------------------------------------===//
892// IntegerShlOp
893//===----------------------------------------------------------------------===//
894
895OpFoldResult IntegerShlOp::fold(FoldAdaptor adaptor) {
897 adaptor.getLhs(), adaptor.getRhs(),
898 [&](const APSInt &lhs, const APSInt &rhs) -> FailureOr<APSInt> {
899 // Check non-negative constraint from operation semantics.
900 if (!rhs.isNonNegative())
901 return (emitOpError("shift amount must be non-negative"), failure());
902 // Check size constraint from implementation detail of using
903 // getExtValue.
904 if (!rhs.isRepresentableByInt64())
905 return (emitOpError("shift amount must be representable in 64 bits"),
906 failure());
907 int64_t shiftAmt = rhs.getExtValue();
908 // Extend lhs to lhsWidth + shiftAmt bits so no bits are truncated.
909 return success(lhs.extend(lhs.getBitWidth() + shiftAmt) << shiftAmt);
910 });
911}
912
913//===----------------------------------------------------------------------===//
914// IntegerBitAndOp
915//===----------------------------------------------------------------------===//
916
917OpFoldResult IntegerBitAndOp::fold(FoldAdaptor adaptor) {
919 adaptor.getLhs(), adaptor.getRhs(),
920 [](const APSInt &lhs, const APSInt &rhs) { return success(lhs & rhs); });
921}
922
923//===----------------------------------------------------------------------===//
924// IntegerBitOrOp
925//===----------------------------------------------------------------------===//
926
927OpFoldResult IntegerBitOrOp::fold(FoldAdaptor adaptor) {
929 adaptor.getLhs(), adaptor.getRhs(),
930 [](const APSInt &lhs, const APSInt &rhs) { return success(lhs | rhs); });
931}
932
933//===----------------------------------------------------------------------===//
934// IntegerBitNotOp
935//===----------------------------------------------------------------------===//
936
937OpFoldResult IntegerBitNotOp::fold(FoldAdaptor adaptor) {
938 auto input = dyn_cast_or_null<circt::om::IntegerAttr>(adaptor.getInput());
939 if (!input)
940 return {};
941
942 APSInt result = ~getAPSIntForOMIntegerAttr(input);
943 return circt::om::IntegerAttr::get(
944 getContext(), mlir::IntegerAttr::get(getContext(), result));
945}
946
947//===----------------------------------------------------------------------===//
948// StringConcatOp
949//===----------------------------------------------------------------------===//
950
951OpFoldResult StringConcatOp::fold(FoldAdaptor adaptor) {
952 // Fold single-operand concat to just the operand.
953 if (getStrings().size() == 1) {
954 if (auto strAttr = adaptor.getStrings()[0])
955 return strAttr;
956
957 return getStrings()[0];
958 }
959
960 // Check if all operands are constant strings before accumulating.
961 if (!llvm::all_of(adaptor.getStrings(), [](Attribute operand) {
962 return isa_and_nonnull<StringAttr>(operand);
963 }))
964 return {};
965
966 // All operands are constant strings, concatenate them.
967 SmallString<64> result;
968 for (auto operand : adaptor.getStrings())
969 result += cast<StringAttr>(operand).getValue();
970
971 return StringAttr::get(result, getResult().getType());
972}
973
974namespace {
975/// Flatten nested string.concat operations into a single concat.
976/// string.concat(a, string.concat(b, c), d) -> string.concat(a, b, c, d)
977class FlattenOMStringConcat : public mlir::OpRewritePattern<StringConcatOp> {
978public:
979 using OpRewritePattern::OpRewritePattern;
980
981 LogicalResult
982 matchAndRewrite(StringConcatOp concat,
983 mlir::PatternRewriter &rewriter) const override {
984
985 // Check if any operands are nested concats with a single use. Only inline
986 // single-use nested concats to avoid fighting with DCE.
987 bool hasNestedConcat = llvm::any_of(concat.getStrings(), [](Value operand) {
988 auto nestedConcat = operand.getDefiningOp<StringConcatOp>();
989 return nestedConcat && operand.hasOneUse();
990 });
991
992 if (!hasNestedConcat)
993 return failure();
994
995 // Flatten nested concats that have a single use.
996 SmallVector<Value> flatOperands;
997 for (auto input : concat.getStrings()) {
998 if (auto nestedConcat = input.getDefiningOp<StringConcatOp>();
999 nestedConcat && input.hasOneUse())
1000 llvm::append_range(flatOperands, nestedConcat.getStrings());
1001 else
1002 flatOperands.push_back(input);
1003 }
1004
1005 rewriter.modifyOpInPlace(concat,
1006 [&]() { concat->setOperands(flatOperands); });
1007 return success();
1008 }
1009};
1010
1011/// Merge consecutive constant strings in a concat and remove empty strings.
1012/// string.concat("a", "b", x, "", "c", "d") -> string.concat("ab", x, "cd")
1013class MergeAdjacentOMStringConstants
1014 : public mlir::OpRewritePattern<StringConcatOp> {
1015public:
1016 using OpRewritePattern::OpRewritePattern;
1017
1018 LogicalResult
1019 matchAndRewrite(StringConcatOp concat,
1020 mlir::PatternRewriter &rewriter) const override {
1021
1022 SmallVector<Value> newOperands;
1023 SmallString<64> accumulatedLit;
1024 SmallVector<ConstantOp> accumulatedOps;
1025 bool changed = false;
1026
1027 auto flushLiterals = [&]() {
1028 if (accumulatedOps.empty())
1029 return;
1030
1031 // If only one literal, reuse it.
1032 if (accumulatedOps.size() == 1) {
1033 newOperands.push_back(accumulatedOps[0]);
1034 } else {
1035 // Multiple literals - merge them.
1036 auto newLit = rewriter.createOrFold<ConstantOp>(
1037 concat.getLoc(),
1038 StringAttr::get(accumulatedLit, concat.getResult().getType()));
1039 newOperands.push_back(newLit);
1040 changed = true;
1041 }
1042 accumulatedLit.clear();
1043 accumulatedOps.clear();
1044 };
1045
1046 for (auto operand : concat.getStrings()) {
1047 if (auto litOp = operand.getDefiningOp<ConstantOp>()) {
1048 if (auto strAttr = dyn_cast<StringAttr>(litOp.getValue())) {
1049 // Skip empty strings.
1050 if (strAttr.getValue().empty()) {
1051 changed = true;
1052 continue;
1053 }
1054 accumulatedLit += strAttr.getValue();
1055 accumulatedOps.push_back(litOp);
1056 continue;
1057 }
1058 }
1059
1060 flushLiterals();
1061 newOperands.push_back(operand);
1062 }
1063
1064 // Flush any remaining literals.
1065 flushLiterals();
1066
1067 if (!changed)
1068 return failure();
1069
1070 // If no operands remain, replace with empty string.
1071 if (newOperands.empty())
1072 return rewriter.replaceOpWithNewOp<ConstantOp>(
1073 concat, StringAttr::get("", concat.getResult().getType())),
1074 success();
1075
1076 // Single-operand case is handled by the folder.
1077 rewriter.modifyOpInPlace(concat,
1078 [&]() { concat->setOperands(newOperands); });
1079 return success();
1080 }
1081};
1082
1083} // namespace
1084
1085void StringConcatOp::getCanonicalizationPatterns(RewritePatternSet &results,
1086 MLIRContext *context) {
1087 results.insert<FlattenOMStringConcat, MergeAdjacentOMStringConstants>(
1088 context);
1089}
1090
1091//===----------------------------------------------------------------------===//
1092// PropEqOp
1093//===----------------------------------------------------------------------===//
1094
1095static FailureOr<mlir::Attribute>
1096evaluateBinaryEquality(mlir::Attribute lhsAttr, mlir::Attribute rhsAttr) {
1097 auto resultType = mlir::IntegerType::get(lhsAttr.getContext(), 1);
1098
1099 // String equality.
1100 if (auto lhs = dyn_cast<mlir::StringAttr>(lhsAttr))
1101 if (auto rhs = dyn_cast<mlir::StringAttr>(rhsAttr))
1102 return mlir::Attribute(
1103 mlir::IntegerAttr::get(resultType, lhs == rhs ? 1 : 0));
1104
1105 // OM integer equality (arbitrary precision).
1106 if (auto lhs = dyn_cast<circt::om::IntegerAttr>(lhsAttr))
1107 if (auto rhs = dyn_cast<circt::om::IntegerAttr>(rhsAttr)) {
1108 APSInt lhsVal = getAPSIntForOMIntegerAttr(lhs);
1109 APSInt rhsVal = getAPSIntForOMIntegerAttr(rhs);
1110 if (lhsVal.getBitWidth() > rhsVal.getBitWidth())
1111 rhsVal = rhsVal.extend(lhsVal.getBitWidth());
1112 else if (rhsVal.getBitWidth() > lhsVal.getBitWidth())
1113 lhsVal = lhsVal.extend(rhsVal.getBitWidth());
1114 return mlir::Attribute(
1115 mlir::IntegerAttr::get(resultType, lhsVal == rhsVal ? 1 : 0));
1116 }
1117
1118 // Boolean (i1) equality.
1119 if (auto lhs = dyn_cast<mlir::IntegerAttr>(lhsAttr))
1120 if (auto rhs = dyn_cast<mlir::IntegerAttr>(rhsAttr))
1121 return mlir::Attribute(
1122 mlir::IntegerAttr::get(resultType, lhs == rhs ? 1 : 0));
1123
1124 return failure();
1125}
1126
1127OpFoldResult PropEqOp::fold(FoldAdaptor adaptor) {
1128 auto lhsAttr = adaptor.getLhs();
1129 auto rhsAttr = adaptor.getRhs();
1130 if (!lhsAttr || !rhsAttr)
1131 return {};
1132
1133 auto result = evaluateBinaryEquality(lhsAttr, rhsAttr);
1134 if (failed(result))
1135 return {};
1136
1137 return *result;
1138}
1139
1140//===----------------------------------------------------------------------===//
1141// ObjectEqOp
1142//===----------------------------------------------------------------------===//
1143
1144OpFoldResult ObjectEqOp::fold(FoldAdaptor adaptor) {
1145 // object.eq(x, x) -> true, even for unknown x.
1146 if (getLhs() == getRhs())
1147 return mlir::IntegerAttr::get(getType(), 1);
1148
1149 // object.eq(a, b) -> false for distinct elaborated objects.
1150 if (getLhs().getDefiningOp<ElaboratedObjectOp>() &&
1151 getRhs().getDefiningOp<ElaboratedObjectOp>())
1152 return mlir::IntegerAttr::get(getType(), 0);
1153
1154 // object.eq(any_cast(x), y) -> object.eq(x, y).
1155 if (auto cast = getLhs().getDefiningOp<AnyCastOp>();
1156 cast && isa<ClassType>(cast.getInput().getType())) {
1157 getLhsMutable().assign(cast.getInput());
1158 return getResult();
1159 }
1160
1161 // object.eq(x, any_cast(y)) -> object.eq(x, y).
1162 if (auto cast = getRhs().getDefiningOp<AnyCastOp>();
1163 cast && isa<ClassType>(cast.getInput().getType())) {
1164 getRhsMutable().assign(cast.getInput());
1165 return getResult();
1166 }
1167
1168 return {};
1169}
1170
1171//===----------------------------------------------------------------------===//
1172// ListContainsOp
1173//===----------------------------------------------------------------------===//
1174
1175OpFoldResult ListContainsOp::fold(FoldAdaptor adaptor) {
1176 // Fold membership based on SSA identity for a directly constructed list.
1177 if (auto listCreate = getList().getDefiningOp<ListCreateOp>();
1178 listCreate && llvm::is_contained(listCreate.getInputs(), getElement()))
1179 return mlir::IntegerAttr::get(getResult().getType(), 1);
1180
1181 auto listAttr = dyn_cast_or_null<ListAttr>(adaptor.getList());
1182 Attribute elementAttr = adaptor.getElement();
1183 // Both the list and element must fold to constant attributes. Even an empty
1184 // list requires a constant element.
1185 if (!listAttr || !elementAttr)
1186 return {};
1187
1188 bool found = false;
1189 for (Attribute candidate : listAttr.getElements()) {
1190 auto equality = evaluateBinaryEquality(candidate, elementAttr);
1191 if (failed(equality))
1192 return {};
1193 found |= !cast<mlir::IntegerAttr>(*equality).getValue().isZero();
1194 }
1195
1196 return mlir::IntegerAttr::get(getResult().getType(), found);
1197}
1198
1199//===----------------------------------------------------------------------===//
1200// IntegerAndOp / IntegerOrOp / IntegerXorOp
1201//===----------------------------------------------------------------------===//
1202
1203static OpFoldResult foldIntegerBitwise(Attribute lhsAttr, Attribute rhsAttr,
1204 IntegerBinaryFn evaluate) {
1205 auto lhsInt = dyn_cast_or_null<mlir::IntegerAttr>(lhsAttr);
1206 auto rhsInt = dyn_cast_or_null<mlir::IntegerAttr>(rhsAttr);
1207 if (!lhsInt || !rhsInt)
1208 return {};
1209 APSInt lhsVal(lhsInt.getValue());
1210 APSInt rhsVal(rhsInt.getValue());
1211 auto result = evaluate(lhsVal, rhsVal);
1212 if (failed(result))
1213 return {};
1214 return mlir::IntegerAttr::get(
1215 lhsInt.getType(), result->extOrTrunc(lhsInt.getValue().getBitWidth()));
1216}
1217
1218// Returns true if attr is an IntegerAttr whose value is all-zeros.
1219static bool isZeroInt(Attribute a) {
1220 auto i = dyn_cast_or_null<mlir::IntegerAttr>(a);
1221 return i && i.getValue().isZero();
1222}
1223
1224// Returns true if attr is an IntegerAttr whose value is all-ones.
1225static bool isAllOnesInt(Attribute a) {
1226 auto i = dyn_cast_or_null<mlir::IntegerAttr>(a);
1227 return i && i.getValue().isAllOnes();
1228}
1229
1230OpFoldResult IntegerAndOp::fold(FoldAdaptor adaptor) {
1231 if (auto result = foldIntegerBitwise(
1232 adaptor.getLhs(), adaptor.getRhs(),
1233 [](const APSInt &lhs, const APSInt &rhs) {
1234 return success(APSInt(lhs & rhs, /*isUnsigned=*/false));
1235 }))
1236 return result;
1237 // AND with all-zeros is always zero.
1238 if (isZeroInt(adaptor.getLhs()) || isZeroInt(adaptor.getRhs()))
1239 return mlir::IntegerAttr::get(getResult().getType(),
1240 APInt::getZero(getType().getWidth()));
1241 // AND with all-ones is identity.
1242 if (isAllOnesInt(adaptor.getLhs()))
1243 return getRhs();
1244 if (isAllOnesInt(adaptor.getRhs()))
1245 return getLhs();
1246 return {};
1247}
1248
1249OpFoldResult IntegerOrOp::fold(FoldAdaptor adaptor) {
1250 if (auto result = foldIntegerBitwise(
1251 adaptor.getLhs(), adaptor.getRhs(),
1252 [](const APSInt &lhs, const APSInt &rhs) {
1253 return success(APSInt(lhs | rhs, /*isUnsigned=*/false));
1254 }))
1255 return result;
1256 // OR with all-ones is always all-ones.
1257 if (isAllOnesInt(adaptor.getLhs()) || isAllOnesInt(adaptor.getRhs()))
1258 return mlir::IntegerAttr::get(getResult().getType(),
1259 APInt::getAllOnes(getType().getWidth()));
1260 // OR with all-zeros is identity.
1261 if (isZeroInt(adaptor.getLhs()))
1262 return getRhs();
1263 if (isZeroInt(adaptor.getRhs()))
1264 return getLhs();
1265 return {};
1266}
1267
1268OpFoldResult IntegerXorOp::fold(FoldAdaptor adaptor) {
1269 if (auto result = foldIntegerBitwise(
1270 adaptor.getLhs(), adaptor.getRhs(),
1271 [](const APSInt &lhs, const APSInt &rhs) {
1272 return success(APSInt(lhs ^ rhs, /*isUnsigned=*/false));
1273 }))
1274 return result;
1275 // XOR with all-zeros is identity.
1276 if (isZeroInt(adaptor.getLhs()))
1277 return getRhs();
1278 if (isZeroInt(adaptor.getRhs()))
1279 return getLhs();
1280 return {};
1281}
1282
1283//===----------------------------------------------------------------------===//
1284// UnknownValueOp
1285//===----------------------------------------------------------------------===//
1286
1287LogicalResult circt::om::UnknownValueOp::verifySymbolUses(
1288 SymbolTableCollection &symbolTable) {
1289
1290 // Unknown values of non-class type don't need to be verified.
1291 auto classType = dyn_cast<ClassType>(getType());
1292 if (!classType)
1293 return success();
1294
1295 // Verify the referred to ClassOp exists.
1296 auto className = classType.getClassName();
1297 if (symbolTable.lookupNearestSymbolFrom<ClassLike>(*this, className))
1298 return success();
1299
1300 return emitOpError() << "refers to non-existant class (\""
1301 << className.getValue() << "\")";
1302}
1303
1304//===----------------------------------------------------------------------===//
1305// TableGen generated logic.
1306//===----------------------------------------------------------------------===//
1307
1308#define GET_OP_CLASSES
1309#include "circt/Dialect/OM/OM.cpp.inc"
assert(baseType &&"element must be base type")
static SmallVector< T > concat(const SmallVectorImpl< T > &a, const SmallVectorImpl< T > &b)
Returns a new vector containing the concatenation of vectors a and b.
Definition CalyxOps.cpp:555
static std::unique_ptr< Context > context
static Location getLoc(DefSlot slot)
Definition Mem2Reg.cpp:193
false return failure()
Parse an optional keyword or string and set instance into 'result'.
return success()
static OpFoldResult foldIntegerBitwise(Attribute lhsAttr, Attribute rhsAttr, IntegerBinaryFn evaluate)
Definition OMOps.cpp:1203
LogicalResult verifyClassLike(ClassLike classLike)
Definition OMOps.cpp:248
std::optional< Type > getClassLikeFieldType(ClassLike classLike, StringAttr name)
Definition OMOps.cpp:287
void getClassLikeAsmBlockArgumentNames(ClassLike classLike, Region &region, OpAsmSetValueNameFn setNameFn)
Definition OMOps.cpp:265
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:808
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:819
static FailureOr< ClassLike > verifyClassLikeSymbolUser(Operation *op, SymbolTableCollection &symbolTable, ClassType resultType, StringAttr className)
Definition OMOps.cpp:571
static void printFieldLocs(OpAsmPrinter &printer, Operation *op, ArrayAttr fieldLocs)
Definition OMOps.cpp:89
static bool isZeroInt(Attribute a)
Definition OMOps.cpp:1219
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:1096
static void printClassLike(ClassLike classLike, OpAsmPrinter &printer)
Definition OMOps.cpp:192
void replaceClassLikeFieldTypes(ClassLike classLike, AttrTypeReplacer &replacer)
Definition OMOps.cpp:297
NamedAttribute makeFieldType(StringAttr name, Type type)
Definition OMOps.cpp:277
NamedAttribute makeFieldIdx(MLIRContext *ctx, mlir::StringAttr name, unsigned i)
Definition OMOps.cpp:281
static void printPathString(OpAsmPrinter &p, Operation *op, PathAttr path, StringAttr module, StringAttr ref, StringAttr field)
Definition OMOps.cpp:64
static ParseResult parseClassLike(OpAsmParser &parser, OperationState &state)
Definition OMOps.cpp:135
static OpFoldResult foldIntegerBinaryArithmetic(Attribute lhsAttr, Attribute rhsAttr, IntegerBinaryFn evaluate)
Definition OMOps.cpp:822
static bool isAllOnesInt(Attribute a)
Definition OMOps.cpp:1225
static Block * getBodyBlock(FModuleLike mod)
static InstancePath empty
uint64_t getWidth(Type t)
Definition ESIPasses.cpp:30
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 module
mlir::StringAttr instance