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"
27#define DEBUG_TYPE "om-evaluator"
34constexpr StringLiteral skipElaborationTransformAttr =
35 "om.skip_elaboration_transform";
37LogicalResult verifyActualParameters(ClassLike classLike,
38 ArrayRef<EvaluatorValuePtr> actualParams) {
39 auto formalParamNames =
40 classLike.getFormalParamNames().getAsRange<StringAttr>();
41 auto formalParamTypes = classLike.getBodyBlock()->getArgumentTypes();
43 if (actualParams.size() != formalParamTypes.size()) {
44 auto error = classLike.emitError(
"actual parameter list length (")
45 << actualParams.size() <<
") does not match formal "
46 <<
"parameter list length (" << formalParamTypes.size() <<
")";
47 auto &diag = error.attachNote() <<
"actual parameters: ";
49 for (
const auto ¶m : actualParams) {
56 error.attachNote(classLike.getLoc())
57 <<
"formal parameters: " << formalParamTypes;
61 for (
auto [actualParam, formalParamName, formalParamType] :
62 llvm::zip(actualParams, formalParamNames, formalParamTypes)) {
63 if (!actualParam || !actualParam.get())
64 return classLike.emitError(
"actual parameter for ")
65 << formalParamName <<
" is null";
68 if (isa<AnyType>(formalParamType))
71 Type actualParamType = actualParam->getType();
72 assert(actualParamType &&
"actualParamType must be non-null!");
74 if (actualParamType != formalParamType) {
75 auto error = classLike.emitError(
"actual parameter for ")
76 << formalParamName <<
" has invalid type";
77 error.attachNote() <<
"actual parameter: " << *actualParam;
78 error.attachNote() <<
"format parameter type: " << formalParamType;
88class ScratchIRBuilder {
90 struct InstantiationInfo {
92 SmallVector<EvaluatorValuePtr> actualParams;
95 ScratchIRBuilder(ModuleOp module, SymbolTable &symbolTable,
97 : module(module), symbolTable(symbolTable), rootClass(rootClass),
98 wrapperClass(createWrapperClass(rootClass)) {}
100 FailureOr<InstantiationInfo> run(ArrayRef<EvaluatorValuePtr> actualParams);
104 ClassOp createWrapperClass(ClassLike rootClass);
109 Location loc, Type expectedType);
121 SymbolTable &symbolTable;
123 ClassOp wrapperClass;
126 DenseMap<evaluator::EvaluatorValue *, Value> importedValues;
130 SmallPtrSet<evaluator::ObjectValue *, 8> activeObjectImports;
132 SmallVector<Attribute> wrapperArgNames;
133 SmallVector<EvaluatorValuePtr> wrapperActualParams;
136FailureOr<ScratchIRBuilder::InstantiationInfo>
137ScratchIRBuilder::run(ArrayRef<EvaluatorValuePtr> actualParams) {
138 auto *ctx =
module.getContext();
139 assert(rootClass &&
"root class must be resolved before building scratch IR");
140 auto rootLoc = rootClass.getLoc();
141 auto rootClassName = rootClass.getSymNameAttr();
143 OpBuilder builder(wrapperClass.getFieldsOp());
144 builder.setInsertionPoint(wrapperClass.getFieldsOp());
145 SmallVector<Value> importedActualValues;
146 importedActualValues.reserve(actualParams.size());
147 auto formalTypes = rootClass.getBodyBlock()->getArgumentTypes();
148 for (
auto [actual, expectedType] : llvm::zip(actualParams, formalTypes)) {
149 auto imported = materializeInput(actual, rootLoc, expectedType);
150 if (failed(imported))
152 importedActualValues.push_back(*imported);
156 wrapperClass->setAttr(wrapperClass.getFormalParamNamesAttrName(),
157 builder.getArrayAttr(wrapperArgNames));
159 wrapperClass.updateFields(
163 ClassType::get(ctx, FlatSymbolRefAttr::get(rootClassName)),
164 rootClassName, importedActualValues)
166 {builder.getStringAttr(
"root")});
168 if (failed(verify(module)))
172 ElaborateObjectOptions options;
173 auto wrapperName = wrapperClass.getSymNameAttr();
174 options.targetClass = wrapperName.getValue().str();
175 pm.addPass(createElaborateObject(std::move(options)));
176 if (failed(pm.run(module)))
179 return InstantiationInfo{wrapperName, std::move(wrapperActualParams)};
182ClassOp ScratchIRBuilder::createWrapperClass(ClassLike rootClass) {
183 OpBuilder builder(module.getBody(), module.getBody()->end());
184 builder.setInsertionPointToEnd(module.getBody());
186 auto wrapper = ClassOp::create(builder, rootClass.getLoc(),
187 Twine(
"__om_evaluator_wrapper_") +
188 rootClass.getSymName());
189 (void)symbolTable.insert(wrapper);
190 Block *body = &wrapper.getBody().emplaceBlock();
191 builder.setInsertionPointToEnd(body);
192 ClassFieldsOp::create(builder, rootClass.getLoc(), ValueRange(), ArrayAttr{});
200 return emitError(loc,
"cannot materialize null OM evaluator value");
202 loc = value->getLoc();
203 if (isa<evaluator::ReferenceValue>(value.get()))
204 return emitError(loc,
"cannot import OM reference value");
206 return emitError(loc,
"cannot import OM evaluator value without an "
210 if (isa<AnyType>(expectedType))
211 return createWrapperArgument(value, loc, expectedType);
213 if (
auto it = importedValues.find(value.get()); it != importedValues.end())
216 if (value->isUnknown()) {
217 OpBuilder builder(wrapperClass.getFieldsOp());
218 auto result = UnknownValueOp::create(builder, loc, expectedType);
219 importedValues[value.get()] = result.getResult();
220 return result.getResult();
223 return llvm::TypeSwitch<evaluator::EvaluatorValue *, FailureOr<Value>>(
226 auto attr = attrValue->
getAttr();
228 return emitError(loc,
"cannot import OM attribute value without an "
231 OpBuilder builder(wrapperClass.getFieldsOp());
232 auto result = ConstantOp::create(builder, loc, cast<TypedAttr>(attr));
233 importedValues[value.get()] = result.getResult();
234 return result.getResult();
237 return materializeListInput(listValue, loc);
240 return materializeObjectInput(objectValue, loc);
243 auto result = createWrapperArgument(value, loc, expectedType);
244 if (succeeded(result))
245 importedValues[value.get()] = *result;
254 return emitError(loc,
"cannot import partially evaluated OM list value");
257 SmallVector<Value> elementValues;
258 elementValues.reserve(listValue->
getElements().size());
259 for (
const auto &elementValue : listValue->
getElements()) {
260 auto materializedElement =
261 materializeInput(elementValue, loc, listType.getElementType());
262 if (failed(materializedElement))
264 elementValues.push_back(*materializedElement);
267 OpBuilder builder(wrapperClass.getFieldsOp());
268 auto result = ListCreateOp::create(builder, loc, listType, elementValues);
269 importedValues[listValue] = result.getResult();
270 return result.getResult();
280 if (!activeObjectImports.insert(objectValue).second)
281 return emitError(loc,
"cannot import mutually referential OM objects");
283 llvm::scope_exit popActiveObjectImport(
284 [&] { activeObjectImports.erase(objectValue); });
287 SmallVector<Value> fieldValues;
288 auto fieldNames = classLike.getFieldNames();
289 fieldValues.reserve(fieldNames.size());
290 for (
auto fieldName : fieldNames) {
291 auto fieldNameAttr = cast<StringAttr>(fieldName);
292 auto field = objectValue->
getField(fieldNameAttr);
295 auto materializedField = materializeInput(
296 field.value(), loc, classLike.getFieldType(fieldNameAttr).value());
297 if (failed(materializedField))
299 fieldValues.push_back(*materializedField);
302 OpBuilder builder(wrapperClass.getFieldsOp());
304 ElaboratedObjectOp::create(builder, loc, classLike, fieldValues);
305 importedValues[objectValue] = result.getResult();
306 return result.getResult();
312 Builder builder(module.getContext());
313 wrapperArgNames.push_back(
314 builder.getStringAttr(Twine(
"arg") + Twine(wrapperArgNames.size())));
315 wrapperActualParams.push_back(value);
316 return wrapperClass.getBodyBlock()->addArgument(argType, loc);
326 return cast<ModuleOp>(symbolTable.getOp());
329SmallVector<evaluator::EvaluatorValuePtr>
331 ArrayRef<Attribute> attributes) {
332 SmallVector<evaluator::EvaluatorValuePtr> values;
333 values.reserve(attributes.size());
334 for (
auto attr : attributes)
340 using namespace evaluator;
346 assert(isFullyEvaluated());
347 return llvm::TypeSwitch<EvaluatorValue *, LogicalResult>(
this)
353 return llvm::TypeSwitch<const EvaluatorValue *, Type>(
this)
354 .Case<
AttributeValue>([](
auto *attr) -> Type {
return attr->getType(); })
355 .Case<ObjectValue>([](
auto *
object) {
return object->getObjectType(); })
356 .Case<ListValue>([](
auto *list) {
return list->getListType(); })
357 .Case<ReferenceValue>([](
auto *ref) {
return ref->getValueType(); })
358 .Case<BasePathValue>(
359 [
this](
auto *tuple) {
return FrozenBasePathType::get(ctx); })
361 [
this](
auto *tuple) {
return FrozenPathType::get(ctx); });
364FailureOr<evaluator::EvaluatorValuePtr>
369 TypeSwitch<mlir::Type, FailureOr<evaluator::EvaluatorValuePtr>>(type)
370 .Case([&](circt::om::ListType type) {
372 std::make_shared<evaluator::ListValue>(type, loc);
373 return success(result);
375 .Case([&](circt::om::ClassType type)
376 -> FailureOr<evaluator::EvaluatorValuePtr> {
378 symbolTable.lookup<ClassLike>(type.getClassName().getValue());
380 return symbolTable.getOp()->emitError(
"unknown class name ")
381 << type.getClassName();
385 std::make_shared<evaluator::ObjectValue>(classDef, loc);
387 return success(result);
389 .Case([&](circt::om::StringType type) {
392 return success(result);
394 .Default([&](
auto type) {
return failure(); });
396 if (succeeded(result))
397 attachCounter(result.value());
404 LLVM_DEBUG(dbgs() <<
"- get: " << value <<
"\n");
406 auto it = objects.find({value, actualParams});
407 if (it != objects.end()) {
408 auto evalVal = it->second;
409 evalVal->setLocIfUnknown(loc);
413 FailureOr<evaluator::EvaluatorValuePtr> result =
414 TypeSwitch<Value, FailureOr<evaluator::EvaluatorValuePtr>>(value)
415 .Case([&](BlockArgument arg) {
416 auto val = (*actualParams)[arg.getArgNumber()];
420 .Case([&](OpResult result) {
421 return TypeSwitch<Operation *,
422 FailureOr<evaluator::EvaluatorValuePtr>>(
423 result.getDefiningOp())
424 .Case([&](ConstantOp op) {
425 return evaluateConstant(op, actualParams, loc);
427 .Case([&](IntegerBinaryOp op) {
433 return success(result);
435 .Case<ObjectFieldOp>([&](
auto op) {
439 std::make_shared<evaluator::ReferenceValue>(
440 value.getType(), loc);
441 return success(result);
443 .Case<AnyCastOp>([&](AnyCastOp op) {
444 return getOrCreateValue(op.getInput(), actualParams, loc);
446 .Case<FrozenBasePathCreateOp>([&](FrozenBasePathCreateOp op) {
448 std::make_shared<evaluator::BasePathValue>(
449 op.getPathAttr(), loc);
450 return success(result);
452 .Case<FrozenPathCreateOp>([&](FrozenPathCreateOp op) {
454 std::make_shared<evaluator::PathValue>(
455 op.getTargetKindAttr(), op.getPathAttr(),
456 op.getModuleAttr(), op.getRefAttr(),
457 op.getFieldAttr(), loc);
458 return success(result);
460 .Case<FrozenEmptyPathOp>([&](FrozenEmptyPathOp op) {
462 std::make_shared<evaluator::PathValue>(
464 return success(result);
466 .Case([&](BinaryEqualityOp op) {
470 return success(result);
472 .Case<ListCreateOp, ListConcatOp, StringConcatOp,
473 ObjectFieldOp>([&](
auto op) {
474 return getPartiallyEvaluatedValue(op.getType(), loc);
476 .Case<ObjectOp>([&](
auto op) {
477 return getPartiallyEvaluatedValue(op.getType(), op.getLoc());
479 .Case<ElaboratedObjectOp>([&](
auto op) {
480 return getPartiallyEvaluatedValue(op.getType(), op.getLoc());
482 .Case<UnknownValueOp>(
483 [&](
auto op) {
return evaluateUnknownValue(op, loc); })
484 .Default([&](Operation *op) {
485 auto error = op->emitError(
"unable to evaluate value");
486 error.attachNote() <<
"value: " << value;
494 attachCounter(result.value());
495 objects[{value, actualParams}] = result.value();
499FailureOr<evaluator::EvaluatorValuePtr>
507 LLVM_DEBUG(dbgs() <<
"object:\n");
511 LLVM_DEBUG(dbgs() <<
"name: " << className <<
"\n");
513 auto classDef = symbolTable.lookup<ClassLike>(className);
515 return symbolTable.getOp()->emitError(
"unknown class name ") << className;
518 if (isa<ClassExternOp>(classDef)) {
520 std::make_shared<evaluator::ObjectValue>(classDef, loc);
521 attachCounter(result);
522 result->markUnknown();
523 LLVM_DEBUG(dbgs(1) <<
"extern: <unknown-value>\n");
528 ClassOp cls = cast<ClassOp>(classDef);
530 if (failed(verifyActualParameters(cls, *actualParams)))
536 auto *
context = cls.getContext();
538 LLVM_DEBUG(dbgs() <<
"ops:\n");
542 for (
auto &op : cls.getOps())
543 for (
auto result : op.getResults()) {
546 if (failed(getOrCreateValue(result, actualParams,
550 worklist.push_back({result, actualParams});
554 LLVM_DEBUG(dbgs() <<
"fields:\n");
555 auto fieldNames = cls.getFieldNames();
556 auto operands = cls.getFieldsOp()->getOperands();
557 for (
size_t i = 0; i < fieldNames.size(); ++i) {
558 auto name = fieldNames[i];
559 auto value = operands[i];
560 auto fieldLoc = cls.getFieldLocByIndex(i);
561 LLVM_DEBUG(dbgs() <<
"- name: " << name <<
"\n"
562 << indent(1) <<
"evaluate:\n");
566 FailureOr<evaluator::EvaluatorValuePtr> result =
567 evaluateValue(value, actualParams, fieldLoc);
571 LLVM_DEBUG(dbgs() <<
"value: " << result.value() <<
"\n");
572 fields[cast<StringAttr>(name)] = result.value();
577 LLVM_DEBUG(dbgs() <<
"queuing asserts:\n");
578 for (
auto assertOp : cls.getOps<PropertyAssertOp>()) {
579 LLVM_DEBUG(dbgs(1) <<
"- " << assertOp <<
"\n");
580 pendingAsserts.push({assertOp, actualParams});
584 LLVM_DEBUG(dbgs() <<
"object value:\n");
585 if (instanceKey.first) {
587 getOrCreateValue(instanceKey.first, instanceKey.second, loc).value();
588 auto *
object = llvm::cast<evaluator::ObjectValue>(result.get());
589 object->setFields(std::move(fields));
595 std::make_shared<evaluator::ObjectValue>(cls, fields, loc);
597 assert(result->isFullyEvaluated() &&
598 "object with fields should be fully evaluated");
603FailureOr<std::shared_ptr<evaluator::EvaluatorValue>>
605 StringAttr className, ArrayRef<evaluator::EvaluatorValuePtr> actualParams) {
606 LLVM_DEBUG(dbgs() <<
"instantiate:\n");
611 dbgs() <<
"class: " << className <<
"\n" << indent() <<
"params:\n";
612 for (
auto ¶m : actualParams)
613 dbgs() <<
"- " << param <<
"\n";
620 if (getModule()->hasAttr(skipElaborationTransformAttr))
621 return instantiateImpl(className, actualParams);
623 auto rootClass = symbolTable.lookup<ClassLike>(className);
625 return symbolTable.getOp()->emitError(
"unknown class name ") << className;
626 if (failed(verifyActualParameters(rootClass, actualParams)))
629 ScratchIRBuilder scratchBuilder(getModule(), symbolTable, rootClass);
630 auto transformedInstantiation = scratchBuilder.run(actualParams);
631 if (failed(transformedInstantiation))
634 auto wrapper = instantiateImpl(transformedInstantiation->className,
635 transformedInstantiation->actualParams);
640 cast<evaluator::ObjectValue>(wrapper.value().get())->getField(
"root");
646FailureOr<std::shared_ptr<evaluator::EvaluatorValue>>
648 StringAttr className, ArrayRef<evaluator::EvaluatorValuePtr> actualParams) {
649 auto classDef = symbolTable.lookup<ClassLike>(className);
651 return symbolTable.getOp()->emitError(
"unknown class name ") << className;
654 if (isa<ClassExternOp>(classDef)) {
656 std::make_shared<evaluator::ObjectValue>(
657 classDef, UnknownLoc::get(classDef.getContext()));
658 attachCounter(result);
659 result->markUnknown();
660 LLVM_DEBUG(dbgs(1) <<
"result: <unknown extern>\n");
665 ClassOp cls = cast<ClassOp>(classDef);
668 std::make_unique<SmallVector<std::shared_ptr<evaluator::EvaluatorValue>>>(
671 actualParametersBuffers.push_back(std::move(parameters));
673 auto loc = cls.getLoc();
674 LLVM_DEBUG(dbgs() <<
"evaluate object:\n");
675 auto result = evaluateObjectInstance(
676 className, actualParametersBuffers.back().get(), loc);
683 LLVM_DEBUG(dbgs() <<
"worklist:\n");
688 while (!worklist.empty()) {
689 uint64_t countBeforePass = fullyEvaluatedCount;
690 LLVM_DEBUG(dbgs() <<
"- processing " << worklist.size()
691 <<
" items (fully evaluated count: "
692 << fullyEvaluatedCount <<
")\n");
695 while (!worklist.empty()) {
696 auto [value, args] = worklist.back();
698 auto result = evaluateValue(value, args, loc);
704 if (!result.value()->isFullyEvaluated())
705 nextWorklist.push_back({value, args});
709 uint64_t evaluatedThisPass = fullyEvaluatedCount - countBeforePass;
710 LLVM_DEBUG(dbgs() <<
"- evaluated " << evaluatedThisPass
711 <<
" nodes this pass\n");
714 if (evaluatedThisPass == 0 && !nextWorklist.empty())
715 return cls.emitError()
716 <<
"cycle detected: " << nextWorklist.size()
717 <<
" values remain partially evaluated after full pass with no "
718 "progress (total fully evaluated: "
719 << fullyEvaluatedCount <<
")";
722 worklist = std::move(nextWorklist);
723 nextWorklist.clear();
728 LLVM_DEBUG(dbgs() <<
"asserts:\n");
729 bool assertFailed =
false;
730 while (!pendingAsserts.empty()) {
731 auto [assertOp, assertParams] = pendingAsserts.front();
732 pendingAsserts.pop();
733 assertFailed |= failed(evaluatePropertyAssert(assertOp, assertParams));
738 auto &
object = result.value();
741 LLVM_DEBUG(dbgs() <<
"finalizing\n");
742 if (failed(object->finalize()))
743 return cls.emitError() <<
"failed to finalize evaluation. Probably the "
744 "class contains a dataflow cycle";
745 LLVM_DEBUG(dbgs() <<
"result: " <<
object <<
"\n");
749FailureOr<evaluator::EvaluatorValuePtr>
752 auto evaluatorValue = getOrCreateValue(value, actualParams, loc).value();
754 LLVM_DEBUG(dbgs() <<
"- eval: " << value <<
"\n");
757 if (evaluatorValue->isFullyEvaluated()) {
758 LLVM_DEBUG(dbgs(1) <<
"fully evaluated: " << evaluatorValue <<
"\n");
759 return evaluatorValue;
762 return llvm::TypeSwitch<Value, FailureOr<evaluator::EvaluatorValuePtr>>(value)
763 .Case([&](BlockArgument arg) {
764 return evaluateParameter(arg, actualParams, loc);
766 .Case([&](OpResult result) {
767 return TypeSwitch<Operation *, FailureOr<evaluator::EvaluatorValuePtr>>(
768 result.getDefiningOp())
769 .Case([&](ConstantOp op) {
770 return evaluateConstant(op, actualParams, loc);
772 .Case([&](IntegerBinaryOp op) {
773 return evaluateIntegerBinary(op, actualParams, loc);
775 .Case([&](ObjectOp op) {
776 return evaluateObjectInstance(op, actualParams);
778 .Case([&](ElaboratedObjectOp op) {
779 return evaluateElaboratedObject(op, actualParams, loc);
781 .Case([&](ObjectFieldOp op) {
782 return evaluateObjectField(op, actualParams, loc);
784 .Case([&](ListCreateOp op) {
785 return evaluateListCreate(op, actualParams, loc);
787 .Case([&](ListConcatOp op) {
788 return evaluateListConcat(op, actualParams, loc);
790 .Case([&](StringConcatOp op) {
791 return evaluateStringConcat(op, actualParams, loc);
793 .Case([&](BinaryEqualityOp op) {
794 return evaluateBinaryEquality(op, actualParams, loc);
796 .Case([&](AnyCastOp op) {
797 return evaluateValue(op.getInput(), actualParams, loc);
799 .Case([&](FrozenBasePathCreateOp op) {
800 return evaluateBasePathCreate(op, actualParams, loc);
802 .Case([&](FrozenPathCreateOp op) {
803 return evaluatePathCreate(op, actualParams, loc);
805 .Case([&](FrozenEmptyPathOp op) {
806 return evaluateEmptyPath(op, actualParams, loc);
808 .Case<UnknownValueOp>([&](UnknownValueOp op) {
809 return evaluateUnknownValue(op, loc);
811 .Default([&](Operation *op) {
812 auto error = op->emitError(
"unable to evaluate value");
813 error.attachNote() <<
"value: " << value;
822 auto val = (*actualParams)[formalParam.getArgNumber()];
828FailureOr<circt::om::evaluator::EvaluatorValuePtr>
840 auto handle = getOrCreateValue(op.getResult(), actualParams, loc);
843 if (handle.value()->isFullyEvaluated())
848 auto lhsResult = evaluateValue(op.getLhs(), actualParams, loc);
849 if (failed(lhsResult))
851 if (!lhsResult.value()->isFullyEvaluated())
854 auto rhsResult = evaluateValue(op.getRhs(), actualParams, loc);
855 if (failed(rhsResult))
857 if (!rhsResult.value()->isFullyEvaluated())
861 if (lhsResult.value()->isUnknown() || rhsResult.value()->isUnknown()) {
862 handle.value()->markUnknown();
869 return llvm::TypeSwitch<evaluator::EvaluatorValue *, Attribute>(value)
872 return cast<evaluator::AttributeValue>(val->getStrippedValue()->
get())
877 mlir::Attribute lhsAttr = extractAttr(lhsResult.value().get());
878 mlir::Attribute rhsAttr = extractAttr(rhsResult.value().get());
879 assert(lhsAttr && rhsAttr &&
880 "expected attribute for IntegerBinaryOp operands");
882 std::array<Attribute, 2> operandAttrs = {lhsAttr, rhsAttr};
883 SmallVector<mlir::OpFoldResult, 1> results;
884 mlir::Attribute resultAttr;
887 if (failed(op->fold(operandAttrs, results)) || results.size() != 1 ||
888 !(resultAttr = results[0].dyn_cast<Attribute>()))
889 return op->emitError(
"failed to evaluate integer operation");
892 auto *handleValue = cast<evaluator::AttributeValue>(handle.value().get());
893 auto resultStatus = handleValue->setAttr(resultAttr);
894 if (failed(resultStatus))
897 auto finalizeStatus = handleValue->finalize();
898 if (failed(finalizeStatus))
899 return finalizeStatus;
912 auto loc = op.getLoc();
915 LLVM_DEBUG(dbgs() <<
"op: " << op <<
"\n"
916 << indent() <<
"evaluate condition: \n");
917 auto condResult = evaluateValue(op.getCondition(), actualParams, loc);
918 if (failed(condResult))
920 if (!condResult.value()->isFullyEvaluated()) {
921 LLVM_DEBUG(dbgs() <<
"evaluate condition: <not fully evaluated>\n");
926 if (condResult.value()->isUnknown())
929 LLVM_DEBUG(dbgs() <<
"condition: " << condResult.value() <<
"\n");
935 return llvm::TypeSwitch<evaluator::EvaluatorValue *, mlir::Attribute>(value)
938 auto stripped = val->getStrippedValue();
939 if (failed(stripped))
942 dyn_cast<evaluator::AttributeValue>(stripped.value().get()))
946 .Default([](
auto *) -> mlir::Attribute {
return {}; });
949 auto condAttr = extractAttr(condResult.value().get());
953 bool isFalse =
false;
954 if (
auto boolAttr = dyn_cast<BoolAttr>(condAttr))
955 isFalse = !boolAttr.getValue();
956 else if (
auto intAttr = dyn_cast<mlir::IntegerAttr>(condAttr))
957 isFalse = intAttr.getValue().isZero();
959 return op.emitError(
"expected BoolAttr or mlir::IntegerAttr");
962 return op.emitError(
"OM property assertion failed: ") << op.getMessage();
968FailureOr<circt::om::Evaluator::ActualParameters>
972 auto parameters = std::make_unique<
973 SmallVector<std::shared_ptr<evaluator::EvaluatorValue>>>();
976 for (
auto input : range) {
977 auto inputResult = getOrCreateValue(input, actualParams, loc);
978 if (failed(inputResult))
980 parameters->push_back(inputResult.value());
983 actualParametersBuffers.push_back(std::move(parameters));
984 return actualParametersBuffers.back().get();
988FailureOr<evaluator::EvaluatorValuePtr>
991 auto loc = op.getLoc();
992 if (isFullyEvaluated({op, actualParams}))
993 return getOrCreateValue(op, actualParams, loc);
996 createParametersFromOperands(op.getOperands(), actualParams, loc);
999 return evaluateObjectInstance(op.getClassNameAttr(), params.value(), loc,
1000 {op, actualParams});
1003FailureOr<evaluator::EvaluatorValuePtr>
1007 auto objectValue = getOrCreateValue(op, actualParams, loc);
1008 if (failed(objectValue))
1010 auto object = cast<evaluator::ObjectValue>(objectValue.value().get());
1011 if (object->isFullyEvaluated())
1014 auto classLike = symbolTable.lookup<ClassLike>(op.getClassNameAttr());
1016 return symbolTable.getOp()->emitError(
"unknown class name ")
1017 << op.getClassNameAttr();
1019 auto fieldNames = classLike.getFieldNames();
1020 auto fieldValues = op.getFieldValues();
1021 if (fieldNames.size() != fieldValues.size())
1022 return op.emitError(
"field value list doesn't match class field list, "
1024 << fieldNames.size() <<
" values but got " << fieldValues.size();
1027 auto classOp = dyn_cast<ClassOp>(classLike.getOperation());
1028 for (
auto [index, fieldNameAndValue] :
1029 llvm::enumerate(llvm::zip(fieldNames, fieldValues))) {
1030 auto [fieldName, fieldValue] = fieldNameAndValue;
1031 auto fieldLoc = classOp ? classOp.getFieldLocByIndex(index) : loc;
1032 auto fieldResult = getOrCreateValue(fieldValue, actualParams, fieldLoc);
1033 if (failed(fieldResult))
1036 if (!fieldResult.value()->isFullyEvaluated())
1037 worklist.push_back({fieldValue, actualParams});
1039 fields[cast<StringAttr>(fieldName)] = fieldResult.value();
1042 object->setFields(std::move(fields));
1047FailureOr<evaluator::EvaluatorValuePtr>
1052 FailureOr<evaluator::EvaluatorValuePtr> currentObjectResult =
1053 evaluateValue(op.getObject(), actualParams, loc);
1054 if (failed(currentObjectResult))
1055 return currentObjectResult;
1057 auto result = currentObjectResult.value();
1059 auto objectFieldValue = getOrCreateValue(op, actualParams, loc).value();
1061 if (result->isUnknown()) {
1065 llvm::dyn_cast<evaluator::ReferenceValue>(objectFieldValue.get())) {
1066 auto unknownField = createUnknownValue(op.getResult().getType(), loc);
1067 if (failed(unknownField))
1068 return unknownField;
1069 ref->setValue(unknownField.value());
1072 objectFieldValue->markUnknown();
1073 return objectFieldValue;
1077 if (
auto *ref = llvm::dyn_cast<evaluator::ReferenceValue>(result.get())) {
1078 auto stripped = ref->getStrippedValue();
1079 if (failed(stripped))
1081 result = stripped.value();
1084 auto *currentObject = llvm::cast<evaluator::ObjectValue>(result.get());
1086 auto field = op.getFieldAttr();
1089 if (!currentObject->getFields().contains(field))
1090 return objectFieldValue;
1092 auto currentField = currentObject->getField(field);
1093 auto finalField = currentField.value();
1095 if (!finalField->isFullyEvaluated())
1096 return objectFieldValue;
1099 llvm::cast<evaluator::ReferenceValue>(objectFieldValue.get())
1100 ->setValue(finalField);
1103 return objectFieldValue;
1107FailureOr<evaluator::EvaluatorValuePtr>
1112 SmallVector<evaluator::EvaluatorValuePtr> values;
1113 auto list = getOrCreateValue(op, actualParams, loc);
1114 bool hasUnknown =
false;
1115 for (
auto operand : op.getOperands()) {
1116 auto result = evaluateValue(operand, actualParams, loc);
1119 if (!result.value()->isFullyEvaluated())
1122 if (result.value()->isUnknown())
1124 values.push_back(result.value());
1128 llvm::cast<evaluator::ListValue>(list.value().get())
1129 ->setElements(std::move(values));
1135 list.value()->markUnknown();
1141FailureOr<evaluator::EvaluatorValuePtr>
1146 SmallVector<evaluator::EvaluatorValuePtr> values;
1147 auto list = getOrCreateValue(op, actualParams, loc);
1152 llvm::TypeSwitch<evaluator::EvaluatorValue *, evaluator::ListValue *>(
1156 return cast<evaluator::ListValue>(val->getStrippedValue()->get());
1160 bool hasUnknown =
false;
1161 for (
auto operand : op.getOperands()) {
1162 auto result = evaluateValue(operand, actualParams, loc);
1165 if (!result.value()->isFullyEvaluated())
1168 if (result.value()->isUnknown())
1177 for (
const auto &subValue : subList->
getElements())
1178 values.push_back(subValue);
1182 llvm::cast<evaluator::ListValue>(list.value().get())
1183 ->setElements(std::move(values));
1189 list.value()->markUnknown();
1195FailureOr<evaluator::EvaluatorValuePtr>
1200 auto handle = getOrCreateValue(op.getResult(), actualParams, loc);
1205 if (handle.value()->isFullyEvaluated())
1211 return llvm::TypeSwitch<evaluator::EvaluatorValue *, StringAttr>(value)
1213 return val->
getAs<StringAttr>();
1216 return cast<evaluator::AttributeValue>(val->getStrippedValue()->
get())
1217 ->getAs<StringAttr>();
1223 for (
auto operand : op.getOperands()) {
1224 auto operandResult = evaluateValue(operand, actualParams, loc);
1225 if (failed(operandResult))
1226 return operandResult;
1227 if (!operandResult.value()->isFullyEvaluated())
1230 StringAttr str = extractAttr(operandResult.value().get());
1231 assert(str &&
"expected StringAttr for StringConcatOp operand");
1232 result += str.getValue().str();
1236 auto resultStr = StringAttr::get(result, op.getResult().getType());
1239 auto *handleValue = cast<evaluator::AttributeValue>(handle.value().get());
1240 auto resultStatus = handleValue->setAttr(resultStr);
1241 if (failed(resultStatus))
1242 return resultStatus;
1244 auto finalizeStatus = handleValue->finalize();
1245 if (failed(finalizeStatus))
1246 return finalizeStatus;
1252FailureOr<evaluator::EvaluatorValuePtr>
1257 auto handle = getOrCreateValue(op.getResult(), actualParams, loc);
1262 if (handle.value()->isFullyEvaluated())
1267 auto lhsResult = evaluateValue(op.getLhs(), actualParams, loc);
1268 if (failed(lhsResult))
1270 if (!lhsResult.value()->isFullyEvaluated())
1273 auto rhsResult = evaluateValue(op.getRhs(), actualParams, loc);
1274 if (failed(rhsResult))
1276 if (!rhsResult.value()->isFullyEvaluated())
1280 if (lhsResult.value()->isUnknown() || rhsResult.value()->isUnknown()) {
1281 handle.value()->markUnknown();
1288 return llvm::TypeSwitch<evaluator::EvaluatorValue *, mlir::Attribute>(value)
1291 return cast<evaluator::AttributeValue>(val->getStrippedValue()->
get())
1296 mlir::Attribute lhs = extractAttr(lhsResult.value().get());
1297 mlir::Attribute rhs = extractAttr(rhsResult.value().get());
1298 assert(lhs && rhs &&
"expected attribute for BinaryEqualityOp operands");
1301 FailureOr<mlir::Attribute> result = op.evaluateBinaryEquality(lhs, rhs);
1303 return op->emitError(
"failed to evaluate binary equality operation");
1306 auto *handleValue = cast<evaluator::AttributeValue>(handle.value().get());
1307 auto resultStatus = handleValue->setAttr(*result);
1308 if (failed(resultStatus))
1309 return resultStatus;
1311 auto finalizeStatus = handleValue->finalize();
1312 if (failed(finalizeStatus))
1313 return finalizeStatus;
1318FailureOr<evaluator::EvaluatorValuePtr>
1323 auto valueResult = getOrCreateValue(op, actualParams, loc).value();
1324 auto *path = llvm::cast<evaluator::BasePathValue>(valueResult.get());
1325 auto result = evaluateValue(op.getBasePath(), actualParams, loc);
1328 auto &value = result.value();
1329 if (!value->isFullyEvaluated())
1333 if (result.value()->isUnknown()) {
1334 valueResult->markUnknown();
1338 path->setBasepath(*llvm::cast<evaluator::BasePathValue>(value.get()));
1342FailureOr<evaluator::EvaluatorValuePtr>
1347 auto valueResult = getOrCreateValue(op, actualParams, loc).value();
1348 auto *path = llvm::cast<evaluator::PathValue>(valueResult.get());
1349 auto result = evaluateValue(op.getBasePath(), actualParams, loc);
1352 auto &value = result.value();
1353 if (!value->isFullyEvaluated())
1357 if (result.value()->isUnknown()) {
1358 valueResult->markUnknown();
1362 path->setBasepath(*llvm::cast<evaluator::BasePathValue>(value.get()));
1368 auto valueResult = getOrCreateValue(op, actualParams, loc).value();
1373FailureOr<evaluator::EvaluatorValuePtr>
1379 TypeSwitch<Type, FailureOr<EvaluatorValuePtr>>(type)
1380 .Case([&](ListType type) -> FailureOr<EvaluatorValuePtr> {
1382 return success(std::make_shared<ListValue>(type, loc));
1384 .Case([&](ClassType type) -> FailureOr<EvaluatorValuePtr> {
1387 symbolTable.lookup<ClassLike>(type.getClassName().getValue());
1389 return symbolTable.getOp()->emitError(
"unknown class name ")
1390 << type.getClassName();
1393 return success(std::make_shared<ObjectValue>(classDef, loc));
1395 .Case([&](FrozenBasePathType type) -> FailureOr<EvaluatorValuePtr> {
1397 return success(std::make_shared<BasePathValue>(type.getContext()));
1399 .Case([&](FrozenPathType type) -> FailureOr<EvaluatorValuePtr> {
1402 std::make_shared<PathValue>(PathValue::getEmptyPath(loc)));
1404 .Default([&](Type type) -> FailureOr<EvaluatorValuePtr> {
1407 return success(AttributeValue::get(type, LocationAttr(loc)));
1411 if (succeeded(result))
1412 result->get()->markUnknown();
1418FailureOr<evaluator::EvaluatorValuePtr>
1420 return createUnknownValue(op.getType(), loc);
1428FailureOr<EvaluatorValuePtr>
1430 auto field = fields.find(name);
1431 if (field == fields.end())
1432 return cls.emitError(
"field ") << name <<
" does not exist";
1433 return success(fields[name]);
1439 SmallVector<Attribute> fieldNames;
1440 for (
auto &f : fields)
1441 fieldNames.push_back(f.first);
1443 llvm::sort(fieldNames, [](Attribute a, Attribute b) {
1444 return cast<StringAttr>(a).getValue() < cast<StringAttr>(b).getValue();
1447 return ArrayAttr::get(cls.getContext(), fieldNames);
1451 for (
auto &&[e, value] : fields)
1452 if (failed(finalizeEvaluatorValue(value)))
1463 auto result = getStrippedValue();
1466 value = std::move(result.value());
1468 if (failed(finalizeEvaluatorValue(value)))
1479 for (
auto &value : elements) {
1480 if (failed(finalizeEvaluatorValue(value)))
1492 path(PathAttr::get(
context, {})) {
1493 markFullyEvaluated();
1497 :
EvaluatorValue(path.getContext(), Kind::BasePath, loc), path(path) {}
1500 assert(isFullyEvaluated());
1505 assert(!isFullyEvaluated());
1506 auto newPath = llvm::to_vector(basepath.
path.getPath());
1507 auto oldPath = path.getPath();
1508 newPath.append(oldPath.begin(), oldPath.end());
1509 path = PathAttr::get(path.getContext(), newPath);
1510 markFullyEvaluated();
1518 StringAttr module, StringAttr ref,
1519 StringAttr field, Location loc)
1521 path(path), module(module), ref(ref), field(field) {}
1524 PathValue path(
nullptr,
nullptr,
nullptr,
nullptr,
nullptr, loc);
1532 return StringAttr::get(getContext(),
"OMDeleted:");
1533 SmallString<64> result;
1534 switch (targetKind.getValue()) {
1535 case TargetKind::DontTouch:
1536 result +=
"OMDontTouchedReferenceTarget";
1538 case TargetKind::Instance:
1539 result +=
"OMInstanceTarget";
1541 case TargetKind::MemberInstance:
1542 result +=
"OMMemberInstanceTarget";
1544 case TargetKind::MemberReference:
1545 result +=
"OMMemberReferenceTarget";
1547 case TargetKind::Reference:
1548 result +=
"OMReferenceTarget";
1552 if (!path.getPath().empty())
1553 result += path.getPath().front().module;
1555 result +=
module.getValue();
1557 for (
const auto &elt : path) {
1558 result += elt.module.getValue();
1560 result += elt.instance.getValue();
1563 if (!module.getValue().empty())
1564 result += module.getValue();
1565 if (!ref.getValue().empty()) {
1567 result += ref.getValue();
1569 if (!field.getValue().empty())
1570 result += field.getValue();
1571 return StringAttr::get(field.getContext(), result);
1575 assert(!isFullyEvaluated());
1576 auto newPath = llvm::to_vector(basepath.
getPath().getPath());
1577 auto oldPath = path.getPath();
1578 newPath.append(oldPath.begin(), oldPath.end());
1579 path = PathAttr::get(path.getContext(), newPath);
1580 markFullyEvaluated();
1588 if (cast<TypedAttr>(attr).getType() != this->type)
1589 return mlir::emitError(
getLoc(),
"cannot set AttributeValue of type ")
1590 << this->type <<
" to Attribute " << attr;
1591 if (isFullyEvaluated())
1592 return mlir::emitError(
1594 "cannot set AttributeValue that has already been fully evaluated");
1596 markFullyEvaluated();
1601 if (!isFullyEvaluated())
1602 return mlir::emitError(
1603 getLoc(),
"cannot finalize AttributeValue that is not fully evaluated");
1607std::shared_ptr<evaluator::EvaluatorValue>
1609 auto type = cast<TypedAttr>(attr).getType();
1610 auto *
context = type.getContext();
1612 loc = UnknownLoc::get(
context);
1616 if (
auto listType = dyn_cast<circt::om::ListType>(type)) {
1617 SmallVector<EvaluatorValuePtr> elements;
1618 auto listAttr = cast<om::ListAttr>(attr);
1620 listAttr.getContext(), listAttr.getElements().getValue());
1621 elements.append(values.begin(), values.end());
1622 auto list = std::make_shared<evaluator::ListValue>(listType, elements, loc);
1626 return std::shared_ptr<AttributeValue>(
1630std::shared_ptr<evaluator::EvaluatorValue>
1632 auto *
context = type.getContext();
1634 loc = UnknownLoc::get(
context);
1638 if (
auto listType = dyn_cast<circt::om::ListType>(type))
1639 return std::make_shared<evaluator::ListValue>(listType, loc);
1641 return std::shared_ptr<AttributeValue>(
assert(baseType &&"element must be base type")
static std::unique_ptr< Context > context
static Location getLoc(DefSlot slot)
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 > 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.
FailureOr< EvaluatorValuePtr > evaluateStringConcat(StringConcatOp op, ActualParameters actualParams, Location loc)
Evaluator dispatch function for String concatenation.
LogicalResult evaluatePropertyAssert(PropertyAssertOp op, ActualParameters actualParams)
Evaluator dispatch function for property assertions.
mlir::ModuleOp getModule()
Get the Module this Evaluator is built from.
FailureOr< EvaluatorValuePtr > evaluateObjectField(ObjectFieldOp op, ActualParameters actualParams, Location loc)
Evaluator dispatch function for Object fields.
FailureOr< evaluator::EvaluatorValuePtr > createUnknownValue(Type type, Location loc)
Create an unknown value of the specified type.
FailureOr< EvaluatorValuePtr > evaluateIntegerBinary(IntegerBinaryOp op, ActualParameters actualParams, Location loc)
FailureOr< evaluator::EvaluatorValuePtr > evaluateUnknownValue(UnknownValueOp op, Location loc)
Evaluate an unknown value.
Evaluator(ModuleOp mod)
Construct an Evaluator with an IR module.
FailureOr< evaluator::EvaluatorValuePtr > instantiate(StringAttr className, ArrayRef< EvaluatorValuePtr > actualParams)
Instantiate an Object with its class name and actual parameters.
FailureOr< EvaluatorValuePtr > getOrCreateValue(Value value, ActualParameters actualParams, Location loc)
SmallVectorImpl< std::shared_ptr< evaluator::EvaluatorValue > > * ActualParameters
FailureOr< evaluator::EvaluatorValuePtr > instantiateImpl(StringAttr className, ArrayRef< EvaluatorValuePtr > actualParams)
FailureOr< EvaluatorValuePtr > evaluateBinaryEquality(BinaryEqualityOp op, ActualParameters actualParams, Location loc)
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.
std::pair< Value, ActualParameters > ObjectKey
FailureOr< EvaluatorValuePtr > evaluateParameter(BlockArgument formalParam, ActualParameters actualParams, Location loc)
Evaluator dispatch functions for the small expression grammar.
FailureOr< EvaluatorValuePtr > evaluateObjectInstance(StringAttr className, ActualParameters actualParams, Location loc, ObjectKey instanceKey={})
Instantiate an Object with its class name and actual parameters.
FailureOr< evaluator::EvaluatorValuePtr > evaluatePathCreate(FrozenPathCreateOp op, ActualParameters actualParams, Location loc)
FailureOr< ActualParameters > createParametersFromOperands(ValueRange range, ActualParameters actualParams, Location loc)
Evaluator dispatch function for Object instances.
Values which can be directly representable by MLIR attributes.
LogicalResult setAttr(Attribute attr)
LogicalResult finalizeImpl()
friend std::shared_ptr< EvaluatorValue > get(Attribute attr, LocationAttr loc)
Attribute getAttr() const
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.
om::PathAttr getPath() const
Base class for evaluator runtime values.
bool isFullyEvaluated() const
void markFullyEvaluated()
A List which contains variadic length of elements with the same type.
const auto & getElements() const
LogicalResult finalizeImpl()
om::ListType getListType() const
Return the type of the value, which is a ListType.
A composite Object, which has a type and fields.
LogicalResult finalizeImpl()
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
StringAttr getAsString() const
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)
Values which can be used as pointers to different values.
LogicalResult finalizeImpl()
std::shared_ptr< EvaluatorValue > EvaluatorValuePtr
A value of an object in memory.
evaluator::EvaluatorValuePtr EvaluatorValuePtr
SmallVector< EvaluatorValuePtr > getEvaluatorValuesFromAttributes(MLIRContext *context, ArrayRef< Attribute > attributes)
RAII helper to increment/decrement debugNesting.