CIRCT 24.0.0git
Loading...
Searching...
No Matches
Evaluator.cpp
Go to the documentation of this file.
1//===- Evaluator.cpp - Object Model dialect evaluator ---------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This file implements the Object Model dialect Evaluator.
10//
11//===----------------------------------------------------------------------===//
12
15#include "mlir/IR/Builders.h"
16#include "mlir/IR/BuiltinAttributeInterfaces.h"
17#include "mlir/IR/Location.h"
18#include "mlir/IR/SymbolTable.h"
19#include "mlir/IR/Verifier.h"
20#include "mlir/Pass/PassManager.h"
21#include "llvm/ADT/STLExtras.h"
22#include "llvm/ADT/ScopeExit.h"
23#include "llvm/ADT/TypeSwitch.h"
24#include "llvm/ADT/iterator_range.h"
25#include "llvm/Support/Debug.h"
26
27#define DEBUG_TYPE "om-evaluator"
28
29using namespace mlir;
30using namespace circt::om;
31
32namespace {
33
34LogicalResult verifyActualParameters(ClassLike classLike,
35 ArrayRef<EvaluatorValuePtr> actualParams) {
36 auto formalParamNames =
37 classLike.getFormalParamNames().getAsRange<StringAttr>();
38 auto formalParamTypes = classLike.getBodyBlock()->getArgumentTypes();
39
40 if (actualParams.size() != formalParamTypes.size()) {
41 auto error = classLike.emitError("actual parameter list length (")
42 << actualParams.size() << ") does not match formal "
43 << "parameter list length (" << formalParamTypes.size() << ")";
44 auto &diag = error.attachNote() << "actual parameters: ";
45 bool isFirst = true;
46 for (const auto &param : actualParams) {
47 if (isFirst)
48 isFirst = false;
49 else
50 diag << ", ";
51 diag << param;
52 }
53 error.attachNote(classLike.getLoc())
54 << "formal parameters: " << formalParamTypes;
55 return failure();
56 }
57
58 for (auto [actualParam, formalParamName, formalParamType] :
59 llvm::zip(actualParams, formalParamNames, formalParamTypes)) {
60 if (!actualParam || !actualParam.get())
61 return classLike.emitError("actual parameter for ")
62 << formalParamName << " is null";
63
64 // Subtyping: if formal param is any type, any actual param may be passed.
65 if (isa<AnyType>(formalParamType))
66 continue;
67
68 Type actualParamType = actualParam->getType();
69 assert(actualParamType && "actualParamType must be non-null!");
70
71 if (actualParamType != formalParamType) {
72 auto error = classLike.emitError("actual parameter for ")
73 << formalParamName << " has invalid type";
74 error.attachNote() << "actual parameter: " << *actualParam;
75 error.attachNote() << "format parameter type: " << formalParamType;
76 return failure();
77 }
78 }
79 return success();
80}
81
82/// A helper class that builds the scratch IR for evaluating an object. This is
83/// used to convert from the evaluator's API (which uses opaque pointers to
84/// evaluator values) into actual MLIR IR.
85class ScratchIRBuilder {
86public:
87 struct InstantiationInfo {
88 StringAttr className;
89 SmallVector<EvaluatorValuePtr> actualParams;
90 };
91
92 ScratchIRBuilder(ModuleOp module, SymbolTable &symbolTable,
93 ClassLike rootClass)
94 : module(module), symbolTable(symbolTable), rootClass(rootClass),
95 wrapperClass(createWrapperClass(rootClass)) {}
96
97 FailureOr<InstantiationInfo> run(ArrayRef<EvaluatorValuePtr> actualParams);
98
99private:
100 /// Create the temporary class that owns all scratch IR.
101 ClassOp createWrapperClass(ClassLike rootClass);
102
103 /// Convert an API input value into scratch IR, preserving opaque any-typed
104 /// inputs and rejecting runtime references/cycles.
105 FailureOr<Value> materializeInput(const EvaluatorValuePtr &value,
106 Location loc, Type expectedType);
107 /// Convert a fully evaluated list value into scratch IR.
108 FailureOr<Value> materializeListInput(evaluator::ListValue *listValue,
109 Location loc);
110 /// Convert a fully evaluated object value into scratch IR.
111 FailureOr<Value> materializeObjectInput(evaluator::ObjectValue *objectValue,
112 Location loc);
113 /// Add a wrapper class parameter for an input that must stay opaque.
114 FailureOr<Value> createWrapperArgument(EvaluatorValuePtr value, Location loc,
115 Type argType);
116
117 ModuleOp module;
118 SymbolTable &symbolTable;
119 ClassLike rootClass;
120 ClassOp wrapperClass;
121 // A mapping from evaluator input values to their corresponding imported IR
122 // values.
123 DenseMap<evaluator::EvaluatorValue *, Value> importedValues;
124
125 // A set of object values that have been imported into the scratch IR, used to
126 // detect mutual references in the inputs.
127 SmallPtrSet<evaluator::ObjectValue *, 8> activeObjectImports;
128
129 SmallVector<Attribute> wrapperArgNames;
130 SmallVector<EvaluatorValuePtr> wrapperActualParams;
131};
132
133FailureOr<ScratchIRBuilder::InstantiationInfo>
134ScratchIRBuilder::run(ArrayRef<EvaluatorValuePtr> actualParams) {
135 auto *ctx = module.getContext();
136 assert(rootClass && "root class must be resolved before building scratch IR");
137 auto rootLoc = rootClass.getLoc();
138 auto rootClassName = rootClass.getSymNameAttr();
139
140 OpBuilder builder(wrapperClass.getFieldsOp());
141 builder.setInsertionPoint(wrapperClass.getFieldsOp());
142 SmallVector<Value> importedActualValues;
143 importedActualValues.reserve(actualParams.size());
144 auto formalTypes = rootClass.getBodyBlock()->getArgumentTypes();
145 for (auto [actual, expectedType] : llvm::zip(actualParams, formalTypes)) {
146 auto imported = materializeInput(actual, rootLoc, expectedType);
147 if (failed(imported))
148 return failure();
149 importedActualValues.push_back(*imported);
150 }
151
152 // Update wrapper class after materializing actual parameters.
153 wrapperClass->setAttr(wrapperClass.getFormalParamNamesAttrName(),
154 builder.getArrayAttr(wrapperArgNames));
155
156 wrapperClass.updateFields(
157 {rootLoc},
158 {ObjectOp::create(
159 builder, rootLoc,
160 ClassType::get(ctx, FlatSymbolRefAttr::get(rootClassName)),
161 rootClassName, importedActualValues)
162 .getResult()},
163 {builder.getStringAttr("root")});
164
165 // When this flag is present, skip the verifier for mlir::ModuleOp.
166 // Still verify the wrapper class.
167 auto skipGlobalVerifier =
168 module->getAttrOfType<UnitAttr>("om.evaluator_skip_global_verifier");
169 PassManager pm(ctx);
170 ElaborateObjectOptions options;
171 if (skipGlobalVerifier) {
172 if (failed(verify(wrapperClass)))
173 return failure();
174 pm.enableVerifier(false);
175 } else if (failed(verify(module)))
176 return failure();
177
178 auto wrapperName = wrapperClass.getSymNameAttr();
179 options.targetClass = wrapperName.getValue().str();
180 pm.addPass(createElaborateObject(std::move(options)));
181 if (failed(pm.run(module)))
182 return failure();
183
184 // If module verification was skipped, verify the wrapper class instead. The
185 // pass manager has already run the full verifier otherwise.
186 if (skipGlobalVerifier && failed(verify(wrapperClass)))
187 return failure();
188
189 return InstantiationInfo{wrapperName, std::move(wrapperActualParams)};
190}
191
192ClassOp ScratchIRBuilder::createWrapperClass(ClassLike rootClass) {
193 OpBuilder builder(module.getBody(), module.getBody()->end());
194 builder.setInsertionPointToEnd(module.getBody());
195
196 auto wrapper = ClassOp::create(builder, rootClass.getLoc(),
197 Twine("__om_evaluator_wrapper_") +
198 rootClass.getSymName());
199 (void)symbolTable.insert(wrapper);
200 Block *body = &wrapper.getBody().emplaceBlock();
201 builder.setInsertionPointToEnd(body);
202 ClassFieldsOp::create(builder, rootClass.getLoc(), ValueRange(), ArrayAttr{});
203 return wrapper;
204}
205
206FailureOr<Value>
207ScratchIRBuilder::materializeInput(const EvaluatorValuePtr &value, Location loc,
208 Type expectedType) {
209 if (!value)
210 return emitError(loc, "cannot materialize null OM evaluator value");
211
212 loc = value->getLoc();
213 if (!expectedType)
214 return emitError(loc, "cannot import OM evaluator value without an "
215 "expected type");
216
217 // Keep any-typed values opaque at the wrapper boundary.
218 if (isa<AnyType>(expectedType))
219 return createWrapperArgument(value, loc, expectedType);
220
221 if (auto it = importedValues.find(value.get()); it != importedValues.end())
222 return it->second;
223
224 if (value->isUnknown()) {
225 OpBuilder builder(wrapperClass.getFieldsOp());
226 auto result = UnknownValueOp::create(builder, loc, expectedType);
227 importedValues[value.get()] = result.getResult();
228 return result.getResult();
229 }
230
231 return llvm::TypeSwitch<evaluator::EvaluatorValue *, FailureOr<Value>>(
232 value.get())
233 .Case([&](evaluator::AttributeValue *attrValue) -> FailureOr<Value> {
234 auto attr = attrValue->getAttr();
235 if (!attr)
236 return emitError(loc, "cannot import OM attribute value without an "
237 "attribute");
238
239 OpBuilder builder(wrapperClass.getFieldsOp());
240 auto result = ConstantOp::create(builder, loc, cast<TypedAttr>(attr));
241 importedValues[value.get()] = result.getResult();
242 return result.getResult();
243 })
244 .Case([&](evaluator::ListValue *listValue) {
245 return materializeListInput(listValue, loc);
246 })
247 .Case([&](evaluator::ObjectValue *objectValue) {
248 return materializeObjectInput(objectValue, loc);
249 })
250 .Default([&](evaluator::EvaluatorValue *) -> FailureOr<Value> {
251 auto result = createWrapperArgument(value, loc, expectedType);
252 if (succeeded(result))
253 importedValues[value.get()] = *result;
254 return result;
255 });
256}
257
258FailureOr<Value>
259ScratchIRBuilder::materializeListInput(evaluator::ListValue *listValue,
260 Location loc) {
261 if (!listValue->isFullyEvaluated())
262 return emitError(loc, "cannot import partially evaluated OM list value");
263
264 auto listType = listValue->getListType();
265 SmallVector<Value> elementValues;
266 elementValues.reserve(listValue->getElements().size());
267 for (const auto &elementValue : listValue->getElements()) {
268 auto materializedElement =
269 materializeInput(elementValue, loc, listType.getElementType());
270 if (failed(materializedElement))
271 return failure();
272 elementValues.push_back(*materializedElement);
273 }
274
275 OpBuilder builder(wrapperClass.getFieldsOp());
276 auto result = ListCreateOp::create(builder, loc, listType, elementValues);
277 importedValues[listValue] = result.getResult();
278 return result.getResult();
279}
280
281FailureOr<Value>
282ScratchIRBuilder::materializeObjectInput(evaluator::ObjectValue *objectValue,
283 Location loc) {
284 // TODO: Currently we only support importing object values that don't have
285 // mutual references with other object values in the inputs for the
286 // simplicity. We could construct mutually referencing object values with a
287 // backedge builder but currently we don't have a use case for that.
288 if (!activeObjectImports.insert(objectValue).second)
289 return emitError(loc, "cannot import mutually referential OM objects");
290
291 llvm::scope_exit popActiveObjectImport(
292 [&] { activeObjectImports.erase(objectValue); });
293
294 auto classLike = objectValue->getClassOp();
295 SmallVector<Value> fieldValues;
296 auto fieldNames = classLike.getFieldNames();
297 fieldValues.reserve(fieldNames.size());
298 for (auto fieldName : fieldNames) {
299 auto fieldNameAttr = cast<StringAttr>(fieldName);
300 auto field = objectValue->getField(fieldNameAttr);
301 if (failed(field))
302 return failure();
303 auto materializedField = materializeInput(
304 field.value(), loc, classLike.getFieldType(fieldNameAttr).value());
305 if (failed(materializedField))
306 return failure();
307 fieldValues.push_back(*materializedField);
308 }
309
310 OpBuilder builder(wrapperClass.getFieldsOp());
311 auto result =
312 ElaboratedObjectOp::create(builder, loc, classLike, fieldValues);
313 importedValues[objectValue] = result.getResult();
314 return result.getResult();
315}
316
317FailureOr<Value>
318ScratchIRBuilder::createWrapperArgument(EvaluatorValuePtr value, Location loc,
319 Type argType) {
320 Builder builder(module.getContext());
321 wrapperArgNames.push_back(
322 builder.getStringAttr(Twine("arg") + Twine(wrapperArgNames.size())));
323 wrapperActualParams.push_back(value);
324 return wrapperClass.getBodyBlock()->addArgument(argType, loc);
325}
326
327} // namespace
328
329/// Construct an Evaluator with an IR module.
331
332/// Get the Module this Evaluator is built from.
334 return cast<ModuleOp>(symbolTable.getOp());
335}
336
337SmallVector<evaluator::EvaluatorValuePtr>
339 ArrayRef<Attribute> attributes) {
340 SmallVector<evaluator::EvaluatorValuePtr> values;
341 values.reserve(attributes.size());
342 for (auto attr : attributes)
343 values.push_back(evaluator::AttributeValue::get(cast<TypedAttr>(attr)));
344 return values;
345}
346
348 return llvm::TypeSwitch<const EvaluatorValue *, Type>(this)
349 .Case<AttributeValue>([](auto *attr) -> Type { return attr->getType(); })
350 .Case<ObjectValue>([](auto *object) { return object->getObjectType(); })
351 .Case<ListValue>([](auto *list) { return list->getListType(); })
352 .Case<BasePathValue>(
353 [this](auto *tuple) { return FrozenBasePathType::get(ctx); })
354 .Case<PathValue>(
355 [this](auto *tuple) { return FrozenPathType::get(ctx); });
356}
357
358FailureOr<evaluator::EvaluatorValuePtr>
360 using namespace circt::om::evaluator;
361
362 auto result =
363 TypeSwitch<mlir::Type, FailureOr<evaluator::EvaluatorValuePtr>>(type)
364 .Case([&](circt::om::ListType type) {
366 std::make_shared<evaluator::ListValue>(type, loc);
367 return success(result);
368 })
369 .Case([&](circt::om::ClassType type)
370 -> FailureOr<evaluator::EvaluatorValuePtr> {
371 auto classDef =
372 symbolTable.lookup<ClassLike>(type.getClassName().getValue());
373 if (!classDef)
374 return symbolTable.getOp()->emitError("unknown class name ")
375 << type.getClassName();
376
377 // Create an ObjectValue for both ClassOp and ClassExternOp
379 std::make_shared<evaluator::ObjectValue>(classDef, loc);
380
381 return success(result);
382 })
383 .Case([&](circt::om::StringType type) {
386 return success(result);
387 })
388 .Default([&](auto type) { return failure(); });
389
390 return result;
391}
392
393FailureOr<evaluator::EvaluatorValuePtr> circt::om::Evaluator::getOrCreateValue(
394 Value value, ActualParameters actualParams, Location loc) {
395 LLVM_DEBUG(dbgs() << "- get: " << value << "\n");
396
397 auto it = objects.find(value);
398 if (it != objects.end()) {
399 auto evalVal = it->second;
400 evalVal->setLocIfUnknown(loc);
401 return evalVal;
402 }
403
404 FailureOr<evaluator::EvaluatorValuePtr> result =
405 TypeSwitch<Value, FailureOr<evaluator::EvaluatorValuePtr>>(value)
406 .Case([&](BlockArgument arg) {
407 return evaluateParameter(arg, actualParams, loc);
408 })
409 .Case([&](OpResult result) {
410 return TypeSwitch<Operation *,
411 FailureOr<evaluator::EvaluatorValuePtr>>(
412 result.getDefiningOp())
413 .Case([&](ConstantOp op) {
414 return evaluateConstant(op, actualParams, loc);
415 })
416 .Case<AnyCastOp>([&](AnyCastOp op) {
417 return getOrCreateValue(op.getInput(), actualParams, loc);
418 })
419 .Case<FrozenBasePathCreateOp>([&](FrozenBasePathCreateOp op) {
421 std::make_shared<evaluator::BasePathValue>(
422 op.getPathAttr(), loc);
423 return success(result);
424 })
425 .Case<FrozenPathCreateOp>([&](FrozenPathCreateOp op) {
427 std::make_shared<evaluator::PathValue>(
428 op.getTargetKindAttr(), op.getPathAttr(),
429 op.getModuleAttr(), op.getRefAttr(),
430 op.getFieldAttr(), loc);
431 return success(result);
432 })
433 .Case<FrozenEmptyPathOp>([&](FrozenEmptyPathOp op) {
435 std::make_shared<evaluator::PathValue>(
437 return success(result);
438 })
439 .Case<ListCreateOp, ListConcatOp>([&](auto op) {
440 return getPartiallyEvaluatedValue(op.getType(), loc);
441 })
442 .Case<ElaboratedObjectOp>([&](auto op) {
443 return getPartiallyEvaluatedValue(op.getType(), op.getLoc());
444 })
445 .Case<UnknownValueOp>(
446 [&](auto op) { return evaluateUnknownValue(op, loc); })
447 .Default([&](Operation *op) {
448 auto error = op->emitError("unable to evaluate value");
449 error.attachNote() << "value: " << value;
450 return error;
451 });
452 });
453 if (failed(result))
454 return result;
455
456 objects[value] = result.value();
457 return result;
458}
459
460FailureOr<evaluator::EvaluatorValuePtr>
462 ActualParameters actualParams,
463 Location loc) {
464#ifndef NDEBUG
465 DebugNesting nestOne(debugNesting);
466#endif
467 LLVM_DEBUG(dbgs() << "object:\n");
468#ifndef NDEBUG
469 DebugNesting nestTwo(debugNesting);
470#endif
471 LLVM_DEBUG(dbgs() << "name: " << className << "\n");
472
473 auto classDef = symbolTable.lookup<ClassLike>(className);
474 if (!classDef)
475 return symbolTable.getOp()->emitError("unknown class name ") << className;
476
477 // If this is an external class, create an ObjectValue and mark it unknown
478 if (isa<ClassExternOp>(classDef)) {
480 std::make_shared<evaluator::ObjectValue>(classDef, loc);
481 result->markUnknown();
482 LLVM_DEBUG(dbgs(1) << "extern: <unknown-value>\n");
483 return result;
484 }
485
486 // Otherwise, it's a regular class, proceed normally
487 ClassOp cls = cast<ClassOp>(classDef);
488
489 if (failed(verifyActualParameters(cls, actualParams)))
490 return failure();
491
492 // Instantiate the fields.
494
495 auto *context = cls.getContext();
496 {
497 LLVM_DEBUG(dbgs() << "ops:\n");
498#ifndef NDEBUG
499 DebugNesting nestOne(debugNesting);
500#endif
501 // Allocate placeholders for all class-body results before evaluating any
502 // fields. This allows later operations to refer to earlier or later
503 // results without requiring a retry worklist.
504 for (auto &op : cls.getOps())
505 for (auto result : op.getResults())
506 if (failed(getOrCreateValue(result, actualParams,
507 UnknownLoc::get(context))))
508 return failure();
509
510 // Evaluate every operation after all placeholders have been allocated.
511 for (auto &op : cls.getOps())
512 for (auto result : op.getResults()) {
513 auto evaluated = evaluateValue(result, actualParams, op.getLoc());
514 if (failed(evaluated))
515 return failure();
516 }
517 }
518
519 LLVM_DEBUG(dbgs() << "fields:\n");
520 auto fieldNames = cls.getFieldNames();
521 auto operands = cls.getFieldsOp()->getOperands();
522 for (size_t i = 0; i < fieldNames.size(); ++i) {
523 auto name = fieldNames[i];
524 auto value = operands[i];
525 auto fieldLoc = cls.getFieldLocByIndex(i);
526 LLVM_DEBUG(dbgs() << "- name: " << name << "\n"
527 << indent(1) << "evaluate:\n");
528#ifndef NDEBUG
529 DebugNesting nestOne(debugNesting);
530#endif
531 FailureOr<evaluator::EvaluatorValuePtr> result =
532 evaluateValue(value, actualParams, fieldLoc);
533 if (failed(result))
534 return result;
535
536 LLVM_DEBUG(dbgs() << "value: " << result.value() << "\n");
537 fields[cast<StringAttr>(name)] = result.value();
538 }
539
540 // If it's external call, just allocate new ObjectValue.
542 std::make_shared<evaluator::ObjectValue>(cls, fields, loc);
543 // Object is already fully evaluated when created with fields.
544 assert(result->isFullyEvaluated() &&
545 "object with fields should be fully evaluated");
546 return result;
547}
548
549/// Instantiate an Object with its class name and actual parameters.
550FailureOr<std::shared_ptr<evaluator::EvaluatorValue>>
552 StringAttr className, ArrayRef<evaluator::EvaluatorValuePtr> actualParams) {
553 LLVM_DEBUG(dbgs() << "instantiate:\n");
554#ifndef NDEBUG
555 DebugNesting nest(debugNesting);
556#endif
557 LLVM_DEBUG({
558 dbgs() << "class: " << className << "\n" << indent() << "params:\n";
559 for (auto &param : actualParams)
560 dbgs() << "- " << param << "\n";
561 });
562
563 auto rootClass = symbolTable.lookup<ClassLike>(className);
564 if (!rootClass)
565 return symbolTable.getOp()->emitError("unknown class name ") << className;
566 if (failed(verifyActualParameters(rootClass, actualParams)))
567 return failure();
568
569 ScratchIRBuilder scratchBuilder(getModule(), symbolTable, rootClass);
570 auto transformedInstantiation = scratchBuilder.run(actualParams);
571 if (failed(transformedInstantiation))
572 return failure();
573
574 auto wrapper = instantiateImpl(transformedInstantiation->className,
575 transformedInstantiation->actualParams);
576 if (failed(wrapper))
577 return failure();
578
579 auto root =
580 cast<evaluator::ObjectValue>(wrapper.value().get())->getField("root");
581 if (failed(root))
582 return failure();
583 return root.value();
584}
585
586FailureOr<std::shared_ptr<evaluator::EvaluatorValue>>
588 StringAttr className, ArrayRef<evaluator::EvaluatorValuePtr> actualParams) {
589 auto classDef = symbolTable.lookup<ClassLike>(className);
590 if (!classDef)
591 return symbolTable.getOp()->emitError("unknown class name ") << className;
592
593 // If this is an external class, create an ObjectValue and mark it unknown
594 if (isa<ClassExternOp>(classDef)) {
596 std::make_shared<evaluator::ObjectValue>(
597 classDef, UnknownLoc::get(classDef.getContext()));
598 result->markUnknown();
599 LLVM_DEBUG(dbgs(1) << "result: <unknown extern>\n");
600 return result;
601 }
602
603 // Otherwise, it's a regular class, proceed normally
604 ClassOp cls = cast<ClassOp>(classDef);
605
606 auto loc = cls.getLoc();
607 LLVM_DEBUG(dbgs() << "evaluate object:\n");
608 auto result = evaluateObjectInstance(className, actualParams, loc);
609
610 if (failed(result))
611 return failure();
612
613 LLVM_DEBUG(dbgs() << "result: " << result.value() << "\n");
614 return result;
615}
616
617FailureOr<evaluator::EvaluatorValuePtr>
619 Location loc) {
620 auto evaluatorValue = getOrCreateValue(value, actualParams, loc).value();
621
622 LLVM_DEBUG(dbgs() << "- eval: " << value << "\n");
623
624 // Return if the value is already evaluated.
625 if (evaluatorValue->isFullyEvaluated()) {
626 LLVM_DEBUG(dbgs(1) << "fully evaluated: " << evaluatorValue << "\n");
627 return evaluatorValue;
628 }
629
630 return llvm::TypeSwitch<Value, FailureOr<evaluator::EvaluatorValuePtr>>(value)
631 .Case([&](BlockArgument arg) {
632 return evaluateParameter(arg, actualParams, loc);
633 })
634 .Case([&](OpResult result) {
635 return TypeSwitch<Operation *, FailureOr<evaluator::EvaluatorValuePtr>>(
636 result.getDefiningOp())
637 .Case([&](ConstantOp op) {
638 return evaluateConstant(op, actualParams, loc);
639 })
640 .Case([&](ElaboratedObjectOp op) {
641 return evaluateElaboratedObject(op, actualParams, loc);
642 })
643 .Case([&](ListCreateOp op) {
644 return evaluateListCreate(op, actualParams, loc);
645 })
646 .Case([&](ListConcatOp op) {
647 return evaluateListConcat(op, actualParams, loc);
648 })
649 .Case([&](AnyCastOp op) {
650 return evaluateValue(op.getInput(), actualParams, loc);
651 })
652 .Case([&](FrozenBasePathCreateOp op) {
653 return evaluateBasePathCreate(op, actualParams, loc);
654 })
655 .Case([&](FrozenPathCreateOp op) {
656 return evaluatePathCreate(op, actualParams, loc);
657 })
658 .Case([&](FrozenEmptyPathOp op) {
659 return evaluateEmptyPath(op, actualParams, loc);
660 })
661 .Case<UnknownValueOp>([&](UnknownValueOp op) {
662 return evaluateUnknownValue(op, loc);
663 })
664 .Default([&](Operation *op) {
665 auto error = op->emitError("unable to evaluate value");
666 error.attachNote() << "value: " << value;
667 return error;
668 });
669 });
670}
671
672/// Evaluator dispatch function for parameters.
673FailureOr<evaluator::EvaluatorValuePtr> circt::om::Evaluator::evaluateParameter(
674 BlockArgument formalParam, ActualParameters actualParams, Location loc) {
675 auto val = actualParams[formalParam.getArgNumber()];
676 val->setLoc(loc);
677 return success(val);
678}
679
680/// Evaluator dispatch function for constants.
681FailureOr<circt::om::evaluator::EvaluatorValuePtr>
683 ActualParameters actualParams,
684 Location loc) {
685 // For list constants, create ListValue.
686 return success(om::evaluator::AttributeValue::get(op.getValue(), loc));
687}
688
689FailureOr<evaluator::EvaluatorValuePtr>
691 ActualParameters actualParams,
692 Location loc) {
693 auto objectValue = getOrCreateValue(op, actualParams, loc);
694 if (failed(objectValue))
695 return failure();
696 auto *object = cast<evaluator::ObjectValue>(objectValue.value().get());
697 if (object->isFullyEvaluated())
698 return objectValue;
699
700 auto classLike =
701 symbolTable.lookup<ClassLike>(op.getClassNameAttr().getAttr());
702 if (!classLike)
703 return symbolTable.getOp()->emitError("unknown class name ")
704 << op.getClassNameAttr();
705
706 auto fieldNames = classLike.getFieldNames();
707 auto fieldValues = op.getFieldValues();
708 if (fieldNames.size() != fieldValues.size())
709 return op.emitError("field value list doesn't match class field list, "
710 "expected ")
711 << fieldNames.size() << " values but got " << fieldValues.size();
712
714 auto classOp = dyn_cast<ClassOp>(classLike.getOperation());
715 for (auto [index, fieldNameAndValue] :
716 llvm::enumerate(llvm::zip(fieldNames, fieldValues))) {
717 auto [fieldName, fieldValue] = fieldNameAndValue;
718 auto fieldLoc = classOp ? classOp.getFieldLocByIndex(index) : loc;
719 auto fieldResult = getOrCreateValue(fieldValue, actualParams, fieldLoc);
720 if (failed(fieldResult))
721 return failure();
722
723 fields[cast<StringAttr>(fieldName)] = fieldResult.value();
724 }
725
726 object->setFields(std::move(fields));
727 return objectValue;
728}
729
730/// Evaluator dispatch function for List creation.
731FailureOr<evaluator::EvaluatorValuePtr>
733 ActualParameters actualParams,
734 Location loc) {
735 // Evaluate the Object itself, in case it hasn't been evaluated yet.
736 SmallVector<evaluator::EvaluatorValuePtr> values;
737 auto list = getOrCreateValue(op, actualParams, loc);
738 bool hasUnknown = false;
739 for (auto operand : op.getOperands()) {
740 auto result = evaluateValue(operand, actualParams, loc);
741 if (failed(result))
742 return result;
743 if (!result.value()->isFullyEvaluated())
744 return list;
745 // Check if any operand is unknown.
746 if (result.value()->isUnknown())
747 hasUnknown = true;
748 values.push_back(result.value());
749 }
750
751 // Set the list elements (this also marks the list as fully evaluated).
752 llvm::cast<evaluator::ListValue>(list.value().get())
753 ->setElements(std::move(values));
754
755 // If any operand is unknown, mark the list as unknown.
756 // markUnknown() checks if already fully evaluated before calling
757 // markFullyEvaluated().
758 if (hasUnknown)
759 list.value()->markUnknown();
760
761 return list;
762}
763
764/// Evaluator dispatch function for List concatenation.
765FailureOr<evaluator::EvaluatorValuePtr>
767 ActualParameters actualParams,
768 Location loc) {
769 // Evaluate the List concat op itself, in case it hasn't been evaluated yet.
770 SmallVector<evaluator::EvaluatorValuePtr> values;
771 auto list = getOrCreateValue(op, actualParams, loc);
772
773 bool hasUnknown = false;
774 for (auto operand : op.getOperands()) {
775 auto result = evaluateValue(operand, actualParams, loc);
776 if (failed(result))
777 return result;
778 if (!result.value()->isFullyEvaluated())
779 return list;
780 // Check if any operand is unknown.
781 if (result.value()->isUnknown())
782 hasUnknown = true;
783
784 auto *subList = llvm::cast<evaluator::ListValue>(result.value().get());
785
786 // Append each EvaluatorValue from the sublist.
787 for (const auto &subValue : subList->getElements())
788 values.push_back(subValue);
789 }
790
791 // Return the concatenated list.
792 llvm::cast<evaluator::ListValue>(list.value().get())
793 ->setElements(std::move(values));
794
795 // If any operand is unknown, mark the result as unknown.
796 // markUnknown() checks if already fully evaluated before calling
797 // markFullyEvaluated().
798 if (hasUnknown)
799 list.value()->markUnknown();
800
801 return list;
802}
803
804FailureOr<evaluator::EvaluatorValuePtr>
806 ActualParameters actualParams,
807 Location loc) {
808 // Evaluate the Object itself, in case it hasn't been evaluated yet.
809 auto valueResult = getOrCreateValue(op, actualParams, loc).value();
810 auto *path = llvm::cast<evaluator::BasePathValue>(valueResult.get());
811 auto result = evaluateValue(op.getBasePath(), actualParams, loc);
812 if (failed(result))
813 return result;
814 auto &value = result.value();
815 if (!value->isFullyEvaluated())
816 return valueResult;
817
818 // If the base path is unknown, mark the result as unknown.
819 if (result.value()->isUnknown()) {
820 valueResult->markUnknown();
821 return valueResult;
822 }
823
824 path->setBasepath(*llvm::cast<evaluator::BasePathValue>(value.get()));
825 return valueResult;
826}
827
828FailureOr<evaluator::EvaluatorValuePtr>
830 ActualParameters actualParams,
831 Location loc) {
832 // Evaluate the Object itself, in case it hasn't been evaluated yet.
833 auto valueResult = getOrCreateValue(op, actualParams, loc).value();
834 auto *path = llvm::cast<evaluator::PathValue>(valueResult.get());
835 auto result = evaluateValue(op.getBasePath(), actualParams, loc);
836 if (failed(result))
837 return result;
838 auto &value = result.value();
839 if (!value->isFullyEvaluated())
840 return valueResult;
841
842 // If the base path is unknown, mark the result as unknown.
843 if (result.value()->isUnknown()) {
844 valueResult->markUnknown();
845 return valueResult;
846 }
847
848 path->setBasepath(*llvm::cast<evaluator::BasePathValue>(value.get()));
849 return valueResult;
850}
851
852FailureOr<evaluator::EvaluatorValuePtr> circt::om::Evaluator::evaluateEmptyPath(
853 FrozenEmptyPathOp op, ActualParameters actualParams, Location loc) {
854 auto valueResult = getOrCreateValue(op, actualParams, loc).value();
855 return valueResult;
856}
857
858/// Create an unknown value of the specified type
859FailureOr<evaluator::EvaluatorValuePtr>
861 using namespace circt::om::evaluator;
862
863 // Create an unknown value of the appropriate type by switching on the type
864 auto result =
865 TypeSwitch<Type, FailureOr<EvaluatorValuePtr>>(type)
866 .Case([&](ListType type) -> FailureOr<EvaluatorValuePtr> {
867 // Create an empty list
868 return success(std::make_shared<ListValue>(type, loc));
869 })
870 .Case([&](ClassType type) -> FailureOr<EvaluatorValuePtr> {
871 // Look up the class definition
872 auto classDef =
873 symbolTable.lookup<ClassLike>(type.getClassName().getValue());
874 if (!classDef)
875 return symbolTable.getOp()->emitError("unknown class name ")
876 << type.getClassName();
877
878 // Create an ObjectValue for both ClassOp and ClassExternOp
879 return success(std::make_shared<ObjectValue>(classDef, loc));
880 })
881 .Case([&](FrozenBasePathType type) -> FailureOr<EvaluatorValuePtr> {
882 // Create an empty basepath
883 return success(std::make_shared<BasePathValue>(type.getContext()));
884 })
885 .Case([&](FrozenPathType type) -> FailureOr<EvaluatorValuePtr> {
886 // Create an empty path
887 return success(
888 std::make_shared<PathValue>(PathValue::getEmptyPath(loc)));
889 })
890 .Default([&](Type type) -> FailureOr<EvaluatorValuePtr> {
891 // For all other types (primitives like integer, string,
892 // etc.), create an AttributeValue
893 return success(AttributeValue::get(type, LocationAttr(loc)));
894 });
895
896 // Mark the result as unknown if successful.
897 if (succeeded(result))
898 result->get()->markUnknown();
899
900 return result;
901}
902
903/// Evaluate an unknown value
904FailureOr<evaluator::EvaluatorValuePtr>
905circt::om::Evaluator::evaluateUnknownValue(UnknownValueOp op, Location loc) {
906 return createUnknownValue(op.getType(), loc);
907}
908
909//===----------------------------------------------------------------------===//
910// ObjectValue
911//===----------------------------------------------------------------------===//
912
913/// Get a field of the Object by name.
914FailureOr<EvaluatorValuePtr>
916 auto field = fields.find(name);
917 if (field == fields.end())
918 return cls.emitError("field ") << name << " does not exist";
919 return success(fields[name]);
920}
921
922/// Get an ArrayAttr with the names of the fields in the Object. Sort the fields
923/// so there is always a stable order.
925 SmallVector<Attribute> fieldNames;
926 for (auto &f : fields)
927 fieldNames.push_back(f.first);
928
929 llvm::sort(fieldNames, [](Attribute a, Attribute b) {
930 return cast<StringAttr>(a).getValue() < cast<StringAttr>(b).getValue();
931 });
932
933 return ArrayAttr::get(cls.getContext(), fieldNames);
934}
935
936//===----------------------------------------------------------------------===//
937// BasePathValue
938//===----------------------------------------------------------------------===//
939
941 : EvaluatorValue(context, Kind::BasePath, UnknownLoc::get(context)),
942 path(PathAttr::get(context, {})) {
943 markFullyEvaluated();
944}
945
946evaluator::BasePathValue::BasePathValue(PathAttr path, Location loc)
947 : EvaluatorValue(path.getContext(), Kind::BasePath, loc), path(path) {}
948
951 return path;
952}
953
956 auto newPath = llvm::to_vector(basepath.path.getPath());
957 auto oldPath = path.getPath();
958 newPath.append(oldPath.begin(), oldPath.end());
959 path = PathAttr::get(path.getContext(), newPath);
961}
962
963//===----------------------------------------------------------------------===//
964// PathValue
965//===----------------------------------------------------------------------===//
966
968 StringAttr module, StringAttr ref,
969 StringAttr field, Location loc)
972
974 PathValue path(nullptr, nullptr, nullptr, nullptr, nullptr, loc);
975 path.markFullyEvaluated();
976 return path;
977}
978
980 // If the module is null, then this is a path to a deleted object.
981 if (!targetKind)
982 return StringAttr::get(getContext(), "OMDeleted:");
983 SmallString<64> result;
984 switch (targetKind.getValue()) {
985 case TargetKind::DontTouch:
986 result += "OMDontTouchedReferenceTarget";
987 break;
988 case TargetKind::Instance:
989 result += "OMInstanceTarget";
990 break;
991 case TargetKind::MemberInstance:
992 result += "OMMemberInstanceTarget";
993 break;
994 case TargetKind::MemberReference:
995 result += "OMMemberReferenceTarget";
996 break;
997 case TargetKind::Reference:
998 result += "OMReferenceTarget";
999 break;
1000 }
1001 result += ":~";
1002 if (!path.getPath().empty())
1003 result += path.getPath().front().module;
1004 else
1005 result += module.getValue();
1006 result += '|';
1007 for (const auto &elt : path) {
1008 result += elt.module.getValue();
1009 result += '/';
1010 result += elt.instance.getValue();
1011 result += ':';
1012 }
1013 if (!module.getValue().empty())
1014 result += module.getValue();
1015 if (!ref.getValue().empty()) {
1016 result += '>';
1017 result += ref.getValue();
1018 }
1019 if (!field.getValue().empty())
1020 result += field.getValue();
1021 return StringAttr::get(field.getContext(), result);
1022}
1023
1026 auto newPath = llvm::to_vector(basepath.getPath().getPath());
1027 auto oldPath = path.getPath();
1028 newPath.append(oldPath.begin(), oldPath.end());
1029 path = PathAttr::get(path.getContext(), newPath);
1031}
1032
1033//===----------------------------------------------------------------------===//
1034// AttributeValue
1035//===----------------------------------------------------------------------===//
1036
1038 if (cast<TypedAttr>(attr).getType() != this->type)
1039 return mlir::emitError(getLoc(), "cannot set AttributeValue of type ")
1040 << this->type << " to Attribute " << attr;
1041 if (isFullyEvaluated())
1042 return mlir::emitError(
1043 getLoc(),
1044 "cannot set AttributeValue that has already been fully evaluated");
1045 this->attr = attr;
1047 return success();
1048}
1049
1050std::shared_ptr<evaluator::EvaluatorValue>
1052 auto type = cast<TypedAttr>(attr).getType();
1053 auto *context = type.getContext();
1054 if (!loc)
1055 loc = UnknownLoc::get(context);
1056
1057 // Special handling for ListType to create proper ListValue objects instead of
1058 // AttributeValue objects.
1059 if (auto listType = dyn_cast<circt::om::ListType>(type)) {
1060 SmallVector<EvaluatorValuePtr> elements;
1061 auto listAttr = cast<om::ListAttr>(attr);
1063 listAttr.getContext(), listAttr.getElements().getValue());
1064 elements.append(values.begin(), values.end());
1065 auto list = std::make_shared<evaluator::ListValue>(listType, elements, loc);
1066 return list;
1067 }
1068
1069 return std::shared_ptr<AttributeValue>(
1071}
1072
1073std::shared_ptr<evaluator::EvaluatorValue>
1075 auto *context = type.getContext();
1076 if (!loc)
1077 loc = UnknownLoc::get(context);
1078
1079 // Special handling for ListType to create proper ListValue objects instead of
1080 // AttributeValue objects.
1081 if (auto listType = dyn_cast<circt::om::ListType>(type))
1082 return std::make_shared<evaluator::ListValue>(listType, loc);
1083 // Create the AttributeValue with the private tag
1084 return std::shared_ptr<AttributeValue>(
1086}
assert(baseType &&"element must be base type")
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()
FailureOr< evaluator::EvaluatorValuePtr > evaluateBasePathCreate(FrozenBasePathCreateOp op, ActualParameters actualParams, Location loc)
FailureOr< EvaluatorValuePtr > evaluateElaboratedObject(ElaboratedObjectOp op, ActualParameters actualParams, Location loc)
FailureOr< evaluator::EvaluatorValuePtr > evaluateEmptyPath(FrozenEmptyPathOp op, ActualParameters actualParams, Location loc)
FailureOr< evaluator::EvaluatorValuePtr > instantiate(StringAttr className, ArrayRef< EvaluatorValuePtr > actualParams)
Instantiate an Object with its class name and actual parameters.
SymbolTable symbolTable
The symbol table for the IR module the Evaluator was constructed with.
Definition Evaluator.h:368
FailureOr< evaluator::EvaluatorValuePtr > getPartiallyEvaluatedValue(Type type, Location loc)
FailureOr< EvaluatorValuePtr > evaluateValue(Value value, ActualParameters actualParams, Location loc)
Evaluate a Value in a Class body according to the small expression grammar described in the rationale...
FailureOr< EvaluatorValuePtr > evaluateConstant(ConstantOp op, ActualParameters actualParams, Location loc)
Evaluator dispatch function for constants.
DenseMap< Value, std::shared_ptr< evaluator::EvaluatorValue > > objects
Evaluator value storage for the current instantiation.
Definition Evaluator.h:371
mlir::ModuleOp getModule()
Get the Module this Evaluator is built from.
ArrayRef< EvaluatorValuePtr > ActualParameters
Definition Evaluator.h:315
FailureOr< evaluator::EvaluatorValuePtr > createUnknownValue(Type type, Location loc)
Create an unknown value of the specified type.
FailureOr< EvaluatorValuePtr > evaluateObjectInstance(StringAttr className, ActualParameters actualParams, Location loc)
Instantiate an Object with its class name and actual parameters.
llvm::indent indent(unsigned extra=0)
Definition Evaluator.h:388
unsigned debugNesting
Current nesting depth for debug output indentation.
Definition Evaluator.h:375
FailureOr< evaluator::EvaluatorValuePtr > evaluateUnknownValue(UnknownValueOp op, Location loc)
Evaluate an unknown value.
Evaluator(ModuleOp mod)
Construct an Evaluator with an IR module.
raw_ostream & dbgs(unsigned extra=0)
Definition Evaluator.h:384
FailureOr< EvaluatorValuePtr > getOrCreateValue(Value value, ActualParameters actualParams, Location loc)
FailureOr< evaluator::EvaluatorValuePtr > instantiateImpl(StringAttr className, ArrayRef< EvaluatorValuePtr > actualParams)
FailureOr< EvaluatorValuePtr > evaluateListCreate(ListCreateOp op, ActualParameters actualParams, Location loc)
Evaluator dispatch function for List creation.
FailureOr< EvaluatorValuePtr > evaluateListConcat(ListConcatOp op, ActualParameters actualParams, Location loc)
Evaluator dispatch function for List concatenation.
FailureOr< EvaluatorValuePtr > evaluateParameter(BlockArgument formalParam, ActualParameters actualParams, Location loc)
Evaluator dispatch functions for the small expression grammar.
FailureOr< evaluator::EvaluatorValuePtr > evaluatePathCreate(FrozenPathCreateOp op, ActualParameters actualParams, Location loc)
Values which can be directly representable by MLIR attributes.
Definition Evaluator.h:104
LogicalResult setAttr(Attribute attr)
AttributeValue(PrivateTag, Attribute attr, Location loc)
Definition Evaluator.h:130
static std::shared_ptr< EvaluatorValue > get(Attribute attr, LocationAttr loc={})
BasePathValue(MLIRContext *context)
void setBasepath(const BasePathValue &basepath)
Set the basepath which this path is relative to.
Base class for evaluator runtime values.
Definition Evaluator.h:47
EvaluatorValue(MLIRContext *ctx, Kind kind, Location loc)
Definition Evaluator.h:51
MLIRContext * getContext() const
Definition Evaluator.h:54
A List which contains variadic length of elements with the same type.
Definition Evaluator.h:149
const auto & getElements() const
Definition Evaluator.h:167
om::ListType getListType() const
Return the type of the value, which is a ListType.
Definition Evaluator.h:170
A composite Object, which has a type and fields.
Definition Evaluator.h:183
FailureOr< EvaluatorValuePtr > getField(StringAttr field)
Get a field of the Object by name.
ArrayAttr getFieldNames()
Get all the field names of the Object.
om::ClassLike getClassOp() const
Definition Evaluator.h:195
llvm::SmallDenseMap< StringAttr, EvaluatorValuePtr > fields
Definition Evaluator.h:228
StringAttr getAsString() const
om::TargetKindAttr targetKind
Definition Evaluator.h:282
void setBasepath(const BasePathValue &basepath)
PathValue(om::TargetKindAttr targetKind, om::PathAttr path, StringAttr module, StringAttr ref, StringAttr field, Location loc)
Create a path value representing a regular path.
static PathValue getEmptyPath(Location loc)
SmallDenseMap< StringAttr, EvaluatorValuePtr > ObjectFields
The fields of a composite Object, currently represented as a map.
Definition Evaluator.h:41
std::shared_ptr< EvaluatorValue > EvaluatorValuePtr
A value of an object in memory.
Definition Evaluator.h:37
SmallVector< EvaluatorValuePtr > getEvaluatorValuesFromAttributes(MLIRContext *context, ArrayRef< Attribute > attributes)
evaluator::EvaluatorValuePtr EvaluatorValuePtr
Definition Evaluator.h:292
RAII helper to increment/decrement debugNesting.
Definition Evaluator.h:378