CIRCT 23.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
14#include "mlir/IR/BuiltinAttributeInterfaces.h"
15#include "mlir/IR/Location.h"
16#include "mlir/IR/SymbolTable.h"
17#include "llvm/ADT/TypeSwitch.h"
18#include "llvm/ADT/iterator_range.h"
19#include "llvm/Support/Debug.h"
20
21#define DEBUG_TYPE "om-evaluator"
22
23using namespace mlir;
24using namespace circt::om;
25
26/// Construct an Evaluator with an IR module.
27circt::om::Evaluator::Evaluator(ModuleOp mod) : symbolTable(mod) {}
28
29/// Get the Module this Evaluator is built from.
31 return cast<ModuleOp>(symbolTable.getOp());
32}
33
34SmallVector<evaluator::EvaluatorValuePtr>
36 ArrayRef<Attribute> attributes) {
37 SmallVector<evaluator::EvaluatorValuePtr> values;
38 values.reserve(attributes.size());
39 for (auto attr : attributes)
40 values.push_back(evaluator::AttributeValue::get(cast<TypedAttr>(attr)));
41 return values;
42}
43
45 using namespace evaluator;
46 // Early return if already finalized.
47 if (finalized)
48 return success();
49 // Enable the flag to avoid infinite recursions.
50 finalized = true;
51 assert(isFullyEvaluated());
52 return llvm::TypeSwitch<EvaluatorValue *, LogicalResult>(this)
54 BasePathValue, PathValue>([](auto v) { return v->finalizeImpl(); });
55}
56
58 return llvm::TypeSwitch<const EvaluatorValue *, Type>(this)
59 .Case<AttributeValue>([](auto *attr) -> Type { return attr->getType(); })
60 .Case<ObjectValue>([](auto *object) { return object->getObjectType(); })
61 .Case<ListValue>([](auto *list) { return list->getListType(); })
62 .Case<ReferenceValue>([](auto *ref) { return ref->getValueType(); })
63 .Case<BasePathValue>(
64 [this](auto *tuple) { return FrozenBasePathType::get(ctx); })
65 .Case<PathValue>(
66 [this](auto *tuple) { return FrozenPathType::get(ctx); });
67}
68
69FailureOr<evaluator::EvaluatorValuePtr>
71 using namespace circt::om::evaluator;
72
73 return TypeSwitch<mlir::Type, FailureOr<evaluator::EvaluatorValuePtr>>(type)
74 .Case([&](circt::om::ListType type) {
76 std::make_shared<evaluator::ListValue>(type, loc);
77 return success(result);
78 })
79 .Case([&](circt::om::ClassType type)
80 -> FailureOr<evaluator::EvaluatorValuePtr> {
81 auto classDef =
82 symbolTable.lookup<ClassLike>(type.getClassName().getValue());
83 if (!classDef)
84 return symbolTable.getOp()->emitError("unknown class name ")
85 << type.getClassName();
86
87 // Create an ObjectValue for both ClassOp and ClassExternOp
89 std::make_shared<evaluator::ObjectValue>(classDef, loc);
90
91 return success(result);
92 })
93 .Default([&](auto type) { return failure(); });
94}
95
96FailureOr<evaluator::EvaluatorValuePtr> circt::om::Evaluator::getOrCreateValue(
97 Value value, ActualParameters actualParams, Location loc) {
98 auto it = objects.find({value, actualParams});
99 if (it != objects.end()) {
100 auto evalVal = it->second;
101 evalVal->setLocIfUnknown(loc);
102 return evalVal;
103 }
104
105 FailureOr<evaluator::EvaluatorValuePtr> result =
106 TypeSwitch<Value, FailureOr<evaluator::EvaluatorValuePtr>>(value)
107 .Case([&](BlockArgument arg) {
108 auto val = (*actualParams)[arg.getArgNumber()];
109 val->setLoc(loc);
110 return val;
111 })
112 .Case([&](OpResult result) {
113 return TypeSwitch<Operation *,
114 FailureOr<evaluator::EvaluatorValuePtr>>(
115 result.getDefiningOp())
116 .Case([&](ConstantOp op) {
117 return evaluateConstant(op, actualParams, loc);
118 })
119 .Case([&](IntegerBinaryArithmeticOp op) {
120 // Create a partially evaluated AttributeValue of
121 // om::IntegerType in case we need to delay evaluation.
123 evaluator::AttributeValue::get(op.getResult().getType(),
124 loc);
125 return success(result);
126 })
127 .Case<ObjectFieldOp>([&](auto op) {
128 // Create a reference value since the value pointed by object
129 // field op is not created yet.
131 std::make_shared<evaluator::ReferenceValue>(
132 value.getType(), loc);
133 return success(result);
134 })
135 .Case<AnyCastOp>([&](AnyCastOp op) {
136 return getOrCreateValue(op.getInput(), actualParams, loc);
137 })
138 .Case<FrozenBasePathCreateOp>([&](FrozenBasePathCreateOp op) {
140 std::make_shared<evaluator::BasePathValue>(
141 op.getPathAttr(), loc);
142 return success(result);
143 })
144 .Case<FrozenPathCreateOp>([&](FrozenPathCreateOp op) {
146 std::make_shared<evaluator::PathValue>(
147 op.getTargetKindAttr(), op.getPathAttr(),
148 op.getModuleAttr(), op.getRefAttr(),
149 op.getFieldAttr(), loc);
150 return success(result);
151 })
152 .Case<FrozenEmptyPathOp>([&](FrozenEmptyPathOp op) {
154 std::make_shared<evaluator::PathValue>(
156 return success(result);
157 })
158 .Case<ListCreateOp, ListConcatOp, ObjectFieldOp>([&](auto op) {
159 return getPartiallyEvaluatedValue(op.getType(), loc);
160 })
161 .Case<ObjectOp>([&](auto op) {
162 return getPartiallyEvaluatedValue(op.getType(), op.getLoc());
163 })
164 .Case<UnknownValueOp>(
165 [&](auto op) { return evaluateUnknownValue(op, loc); })
166 .Default([&](Operation *op) {
167 auto error = op->emitError("unable to evaluate value");
168 error.attachNote() << "value: " << value;
169 return error;
170 });
171 });
172 if (failed(result))
173 return result;
174
175 objects[{value, actualParams}] = result.value();
176 return result;
177}
178
179FailureOr<evaluator::EvaluatorValuePtr>
181 ActualParameters actualParams,
182 Location loc,
183 ObjectKey instanceKey) {
184 auto classDef = symbolTable.lookup<ClassLike>(className);
185 if (!classDef)
186 return symbolTable.getOp()->emitError("unknown class name ") << className;
187
188 // If this is an external class, create an ObjectValue and mark it unknown
189 if (isa<ClassExternOp>(classDef)) {
191 std::make_shared<evaluator::ObjectValue>(classDef, loc);
192 result->markUnknown();
193 return result;
194 }
195
196 // Otherwise, it's a regular class, proceed normally
197 ClassOp cls = cast<ClassOp>(classDef);
198
199 auto formalParamNames = cls.getFormalParamNames().getAsRange<StringAttr>();
200 auto formalParamTypes = cls.getBodyBlock()->getArgumentTypes();
201
202 // Verify the actual parameters are the right size and types for this class.
203 if (actualParams->size() != formalParamTypes.size()) {
204 auto error = cls.emitError("actual parameter list length (")
205 << actualParams->size() << ") does not match formal "
206 << "parameter list length (" << formalParamTypes.size() << ")";
207 auto &diag = error.attachNote() << "actual parameters: ";
208 // FIXME: `diag << actualParams` doesn't work for some reason.
209 bool isFirst = true;
210 for (const auto &param : *actualParams) {
211 if (isFirst)
212 isFirst = false;
213 else
214 diag << ", ";
215 diag << param;
216 }
217 error.attachNote(cls.getLoc()) << "formal parameters: " << formalParamTypes;
218 return error;
219 }
220
221 // Verify the actual parameter types match.
222 for (auto [actualParam, formalParamName, formalParamType] :
223 llvm::zip(*actualParams, formalParamNames, formalParamTypes)) {
224 if (!actualParam || !actualParam.get())
225 return cls.emitError("actual parameter for ")
226 << formalParamName << " is null";
227
228 // Subtyping: if formal param is any type, any actual param may be passed.
229 if (isa<AnyType>(formalParamType))
230 continue;
231
232 Type actualParamType = actualParam->getType();
233
234 assert(actualParamType && "actualParamType must be non-null!");
235
236 if (actualParamType != formalParamType) {
237 auto error = cls.emitError("actual parameter for ")
238 << formalParamName << " has invalid type";
239 error.attachNote() << "actual parameter: " << *actualParam;
240 error.attachNote() << "format parameter type: " << formalParamType;
241 return error;
242 }
243 }
244
245 // Instantiate the fields.
247
248 auto *context = cls.getContext();
249 for (auto &op : cls.getOps())
250 for (auto result : op.getResults()) {
251 // Allocate the value, with unknown loc. It will be later set when
252 // evaluating the fields.
253 if (failed(
254 getOrCreateValue(result, actualParams, UnknownLoc::get(context))))
255 return failure();
256 // Add to the worklist.
257 worklist.push({result, actualParams});
258 }
259
260 auto fieldNames = cls.getFieldNames();
261 auto operands = cls.getFieldsOp()->getOperands();
262 for (size_t i = 0; i < fieldNames.size(); ++i) {
263 auto name = fieldNames[i];
264 auto value = operands[i];
265 auto fieldLoc = cls.getFieldLocByIndex(i);
266 FailureOr<evaluator::EvaluatorValuePtr> result =
267 evaluateValue(value, actualParams, fieldLoc);
268 if (failed(result))
269 return result;
270
271 fields[cast<StringAttr>(name)] = result.value();
272 }
273
274 // If the there is an instance, we must update the object value.
275 if (instanceKey.first) {
276 auto result =
277 getOrCreateValue(instanceKey.first, instanceKey.second, loc).value();
278 auto *object = llvm::cast<evaluator::ObjectValue>(result.get());
279 object->setFields(std::move(fields));
280 return result;
281 }
282
283 // If it's external call, just allocate new ObjectValue.
285 std::make_shared<evaluator::ObjectValue>(cls, fields, loc);
286 return result;
287}
288
289/// Instantiate an Object with its class name and actual parameters.
290FailureOr<std::shared_ptr<evaluator::EvaluatorValue>>
292 StringAttr className, ArrayRef<evaluator::EvaluatorValuePtr> actualParams) {
293 auto classDef = symbolTable.lookup<ClassLike>(className);
294 if (!classDef)
295 return symbolTable.getOp()->emitError("unknown class name ") << className;
296
297 // If this is an external class, create an ObjectValue and mark it unknown
298 if (isa<ClassExternOp>(classDef)) {
300 std::make_shared<evaluator::ObjectValue>(
301 classDef, UnknownLoc::get(classDef.getContext()));
302 result->markUnknown();
303 return result;
304 }
305
306 // Otherwise, it's a regular class, proceed normally
307 ClassOp cls = cast<ClassOp>(classDef);
308
309 auto parameters =
310 std::make_unique<SmallVector<std::shared_ptr<evaluator::EvaluatorValue>>>(
311 actualParams);
312
313 actualParametersBuffers.push_back(std::move(parameters));
314
315 auto loc = cls.getLoc();
316 auto result = evaluateObjectInstance(
317 className, actualParametersBuffers.back().get(), loc);
318
319 if (failed(result))
320 return failure();
321
322 // `evaluateObjectInstance` has populated the worklist. Continue evaluations
323 // unless there is a partially evaluated value.
324 while (!worklist.empty()) {
325 auto [value, args] = worklist.front();
326 worklist.pop();
327
328 auto result = evaluateValue(value, args, loc);
329
330 if (failed(result))
331 return failure();
332
333 // It's possible that the value is not fully evaluated.
334 if (!result.value()->isFullyEvaluated())
335 worklist.push({value, args});
336 }
337
338 auto &object = result.value();
339 // Finalize the value. This will eliminate intermidiate ReferenceValue used as
340 // a placeholder in the initialization.
341 if (failed(object->finalize()))
342 return cls.emitError() << "failed to finalize evaluation. Probably the "
343 "class contains a dataflow cycle";
344 return object;
345}
346
347FailureOr<evaluator::EvaluatorValuePtr>
349 Location loc) {
350 auto evaluatorValue = getOrCreateValue(value, actualParams, loc).value();
351
352 // Return if the value is already evaluated.
353 if (evaluatorValue->isFullyEvaluated())
354 return evaluatorValue;
355
356 return llvm::TypeSwitch<Value, FailureOr<evaluator::EvaluatorValuePtr>>(value)
357 .Case([&](BlockArgument arg) {
358 return evaluateParameter(arg, actualParams, loc);
359 })
360 .Case([&](OpResult result) {
361 return TypeSwitch<Operation *, FailureOr<evaluator::EvaluatorValuePtr>>(
362 result.getDefiningOp())
363 .Case([&](ConstantOp op) {
364 return evaluateConstant(op, actualParams, loc);
365 })
366 .Case([&](IntegerBinaryArithmeticOp op) {
367 return evaluateIntegerBinaryArithmetic(op, actualParams, loc);
368 })
369 .Case([&](ObjectOp op) {
370 return evaluateObjectInstance(op, actualParams);
371 })
372 .Case([&](ObjectFieldOp op) {
373 return evaluateObjectField(op, actualParams, loc);
374 })
375 .Case([&](ListCreateOp op) {
376 return evaluateListCreate(op, actualParams, loc);
377 })
378 .Case([&](ListConcatOp op) {
379 return evaluateListConcat(op, actualParams, loc);
380 })
381 .Case([&](AnyCastOp op) {
382 return evaluateValue(op.getInput(), actualParams, loc);
383 })
384 .Case([&](FrozenBasePathCreateOp op) {
385 return evaluateBasePathCreate(op, actualParams, loc);
386 })
387 .Case([&](FrozenPathCreateOp op) {
388 return evaluatePathCreate(op, actualParams, loc);
389 })
390 .Case([&](FrozenEmptyPathOp op) {
391 return evaluateEmptyPath(op, actualParams, loc);
392 })
393 .Case<UnknownValueOp>([&](UnknownValueOp op) {
394 return evaluateUnknownValue(op, loc);
395 })
396 .Default([&](Operation *op) {
397 auto error = op->emitError("unable to evaluate value");
398 error.attachNote() << "value: " << value;
399 return error;
400 });
401 });
402}
403
404/// Evaluator dispatch function for parameters.
405FailureOr<evaluator::EvaluatorValuePtr> circt::om::Evaluator::evaluateParameter(
406 BlockArgument formalParam, ActualParameters actualParams, Location loc) {
407 auto val = (*actualParams)[formalParam.getArgNumber()];
408 val->setLoc(loc);
409 return success(val);
410}
411
412/// Evaluator dispatch function for constants.
413FailureOr<circt::om::evaluator::EvaluatorValuePtr>
415 ActualParameters actualParams,
416 Location loc) {
417 // For list constants, create ListValue.
418 return success(om::evaluator::AttributeValue::get(op.getValue(), loc));
419}
420
421// Evaluator dispatch function for integer binary arithmetic.
422FailureOr<EvaluatorValuePtr>
424 IntegerBinaryArithmeticOp op, ActualParameters actualParams, Location loc) {
425 // Get the op's EvaluatorValue handle, in case it hasn't been evaluated yet.
426 auto handle = getOrCreateValue(op.getResult(), actualParams, loc);
427
428 // If it's fully evaluated, we can return it.
429 if (handle.value()->isFullyEvaluated())
430 return handle;
431
432 // Evaluate operands if necessary, and return the partially evaluated value if
433 // they aren't ready.
434 auto lhsResult = evaluateValue(op.getLhs(), actualParams, loc);
435 if (failed(lhsResult))
436 return lhsResult;
437 if (!lhsResult.value()->isFullyEvaluated())
438 return handle;
439
440 auto rhsResult = evaluateValue(op.getRhs(), actualParams, loc);
441 if (failed(rhsResult))
442 return rhsResult;
443 if (!rhsResult.value()->isFullyEvaluated())
444 return handle;
445
446 // Check if any operand is unknown and propagate the unknown flag.
447 if (lhsResult.value()->isUnknown() || rhsResult.value()->isUnknown()) {
448 handle.value()->markUnknown();
449 return handle;
450 }
451
452 // Extract the integer attributes.
453 auto extractAttr = [](evaluator::EvaluatorValue *value) {
454 return std::move(
455 llvm::TypeSwitch<evaluator::EvaluatorValue *, om::IntegerAttr>(value)
456 .Case([](evaluator::AttributeValue *val) {
457 return val->getAs<om::IntegerAttr>();
458 })
459 .Case([](evaluator::ReferenceValue *val) {
460 return cast<evaluator::AttributeValue>(
461 val->getStrippedValue()->get())
462 ->getAs<om::IntegerAttr>();
463 }));
464 };
465
466 om::IntegerAttr lhs = extractAttr(lhsResult.value().get());
467 om::IntegerAttr rhs = extractAttr(rhsResult.value().get());
468 assert(lhs && rhs &&
469 "expected om::IntegerAttr for IntegerBinaryArithmeticOp operands");
470
471 // Extend values if necessary to match bitwidth. Most interesting arithmetic
472 // on APSInt asserts that both operands are the same bitwidth, but the
473 // IntegerAttrs we are working with may have used the smallest necessary
474 // bitwidth to represent the number they hold, and won't necessarily match.
475 APSInt lhsVal = lhs.getValue().getAPSInt();
476 APSInt rhsVal = rhs.getValue().getAPSInt();
477 if (lhsVal.getBitWidth() > rhsVal.getBitWidth())
478 rhsVal = rhsVal.extend(lhsVal.getBitWidth());
479 else if (rhsVal.getBitWidth() > lhsVal.getBitWidth())
480 lhsVal = lhsVal.extend(rhsVal.getBitWidth());
481
482 // Perform arbitrary precision signed integer binary arithmetic.
483 FailureOr<APSInt> result = op.evaluateIntegerOperation(lhsVal, rhsVal);
484
485 if (failed(result))
486 return op->emitError("failed to evaluate integer operation");
487
488 // Package the result as a new om::IntegerAttr.
489 MLIRContext *ctx = op->getContext();
490 auto resultAttr =
491 om::IntegerAttr::get(ctx, mlir::IntegerAttr::get(ctx, result.value()));
492
493 // Finalize the op result value.
494 auto *handleValue = cast<evaluator::AttributeValue>(handle.value().get());
495 auto resultStatus = handleValue->setAttr(resultAttr);
496 if (failed(resultStatus))
497 return resultStatus;
498
499 auto finalizeStatus = handleValue->finalize();
500 if (failed(finalizeStatus))
501 return finalizeStatus;
502
503 return handle;
504}
505
506/// Evaluator dispatch function for Object instances.
507FailureOr<circt::om::Evaluator::ActualParameters>
509 ValueRange range, ActualParameters actualParams, Location loc) {
510 // Create an unique storage to store parameters.
511 auto parameters = std::make_unique<
512 SmallVector<std::shared_ptr<evaluator::EvaluatorValue>>>();
513
514 // Collect operands' evaluator values in the current instantiation context.
515 for (auto input : range) {
516 auto inputResult = getOrCreateValue(input, actualParams, loc);
517 if (failed(inputResult))
518 return failure();
519 parameters->push_back(inputResult.value());
520 }
521
522 actualParametersBuffers.push_back(std::move(parameters));
523 return actualParametersBuffers.back().get();
524}
525
526/// Evaluator dispatch function for Object instances.
527FailureOr<evaluator::EvaluatorValuePtr>
529 ActualParameters actualParams) {
530 auto loc = op.getLoc();
531 if (isFullyEvaluated({op, actualParams}))
532 return getOrCreateValue(op, actualParams, loc);
533
534 auto params =
535 createParametersFromOperands(op.getOperands(), actualParams, loc);
536 if (failed(params))
537 return failure();
538 return evaluateObjectInstance(op.getClassNameAttr(), params.value(), loc,
539 {op, actualParams});
540}
541
542/// Evaluator dispatch function for Object fields.
543FailureOr<evaluator::EvaluatorValuePtr>
545 ActualParameters actualParams,
546 Location loc) {
547 // Evaluate the Object itself, in case it hasn't been evaluated yet.
548 FailureOr<evaluator::EvaluatorValuePtr> currentObjectResult =
549 evaluateValue(op.getObject(), actualParams, loc);
550 if (failed(currentObjectResult))
551 return currentObjectResult;
552
553 auto result = currentObjectResult.value();
554
555 auto objectFieldValue = getOrCreateValue(op, actualParams, loc).value();
556
557 // If the object is unknown, mark the field as unknown.
558 if (result->isUnknown()) {
559 // If objectFieldValue is a ReferenceValue, set its value to the unknown
560 // object
561 if (auto *ref =
562 llvm::dyn_cast<evaluator::ReferenceValue>(objectFieldValue.get())) {
563 ref->setValue(result);
564 }
565 // markUnknown() also marks the value as fully evaluated
566 objectFieldValue->markUnknown();
567 return objectFieldValue;
568 }
569
570 auto *currentObject = llvm::cast<evaluator::ObjectValue>(result.get());
571
572 // Iteratively access nested fields through the path until we reach the final
573 // field in the path.
575 for (auto field : op.getFieldPath().getAsRange<FlatSymbolRefAttr>()) {
576 // `currentObject` might no be fully evaluated.
577 if (!currentObject->getFields().contains(field.getAttr()))
578 return objectFieldValue;
579
580 auto currentField = currentObject->getField(field.getAttr());
581 finalField = currentField.value();
582 if (auto *nextObject =
583 llvm::dyn_cast<evaluator::ObjectValue>(finalField.get()))
584 currentObject = nextObject;
585 }
586
587 // Update the reference.
588 llvm::cast<evaluator::ReferenceValue>(objectFieldValue.get())
589 ->setValue(finalField);
590
591 // Return the field being accessed.
592 return objectFieldValue;
593}
594
595/// Evaluator dispatch function for List creation.
596FailureOr<evaluator::EvaluatorValuePtr>
598 ActualParameters actualParams,
599 Location loc) {
600 // Evaluate the Object itself, in case it hasn't been evaluated yet.
601 SmallVector<evaluator::EvaluatorValuePtr> values;
602 auto list = getOrCreateValue(op, actualParams, loc);
603 bool hasUnknown = false;
604 for (auto operand : op.getOperands()) {
605 auto result = evaluateValue(operand, actualParams, loc);
606 if (failed(result))
607 return result;
608 if (!result.value()->isFullyEvaluated())
609 return list;
610 // Check if any operand is unknown.
611 if (result.value()->isUnknown())
612 hasUnknown = true;
613 values.push_back(result.value());
614 }
615
616 // Set the list elements (this also marks the list as fully evaluated).
617 llvm::cast<evaluator::ListValue>(list.value().get())
618 ->setElements(std::move(values));
619
620 // If any operand is unknown, mark the list as unknown.
621 // markUnknown() checks if already fully evaluated before calling
622 // markFullyEvaluated().
623 if (hasUnknown)
624 list.value()->markUnknown();
625
626 return list;
627}
628
629/// Evaluator dispatch function for List concatenation.
630FailureOr<evaluator::EvaluatorValuePtr>
632 ActualParameters actualParams,
633 Location loc) {
634 // Evaluate the List concat op itself, in case it hasn't been evaluated yet.
635 SmallVector<evaluator::EvaluatorValuePtr> values;
636 auto list = getOrCreateValue(op, actualParams, loc);
637
638 // Extract the ListValue, either directly or through an object reference.
639 auto extractList = [](evaluator::EvaluatorValue *value) {
640 return std::move(
641 llvm::TypeSwitch<evaluator::EvaluatorValue *, evaluator::ListValue *>(
642 value)
643 .Case([](evaluator::ListValue *val) { return val; })
644 .Case([](evaluator::ReferenceValue *val) {
645 return cast<evaluator::ListValue>(val->getStrippedValue()->get());
646 }));
647 };
648
649 bool hasUnknown = false;
650 for (auto operand : op.getOperands()) {
651 auto result = evaluateValue(operand, actualParams, loc);
652 if (failed(result))
653 return result;
654 if (!result.value()->isFullyEvaluated())
655 return list;
656 // Check if any operand is unknown.
657 if (result.value()->isUnknown())
658 hasUnknown = true;
659
660 // Extract this sublist and ensure it's done evaluating.
661 evaluator::ListValue *subList = extractList(result.value().get());
662 if (!subList->isFullyEvaluated())
663 return list;
664
665 // Append each EvaluatorValue from the sublist.
666 for (const auto &subValue : subList->getElements())
667 values.push_back(subValue);
668 }
669
670 // Return the concatenated list.
671 llvm::cast<evaluator::ListValue>(list.value().get())
672 ->setElements(std::move(values));
673
674 // If any operand is unknown, mark the result as unknown.
675 // markUnknown() checks if already fully evaluated before calling
676 // markFullyEvaluated().
677 if (hasUnknown)
678 list.value()->markUnknown();
679
680 return list;
681}
682
683FailureOr<evaluator::EvaluatorValuePtr>
685 ActualParameters actualParams,
686 Location loc) {
687 // Evaluate the Object itself, in case it hasn't been evaluated yet.
688 auto valueResult = getOrCreateValue(op, actualParams, loc).value();
689 auto *path = llvm::cast<evaluator::BasePathValue>(valueResult.get());
690 auto result = evaluateValue(op.getBasePath(), actualParams, loc);
691 if (failed(result))
692 return result;
693 auto &value = result.value();
694 if (!value->isFullyEvaluated())
695 return valueResult;
696
697 // If the base path is unknown, mark the result as unknown.
698 if (result.value()->isUnknown()) {
699 valueResult->markUnknown();
700 return valueResult;
701 }
702
703 path->setBasepath(*llvm::cast<evaluator::BasePathValue>(value.get()));
704 return valueResult;
705}
706
707FailureOr<evaluator::EvaluatorValuePtr>
709 ActualParameters actualParams,
710 Location loc) {
711 // Evaluate the Object itself, in case it hasn't been evaluated yet.
712 auto valueResult = getOrCreateValue(op, actualParams, loc).value();
713 auto *path = llvm::cast<evaluator::PathValue>(valueResult.get());
714 auto result = evaluateValue(op.getBasePath(), actualParams, loc);
715 if (failed(result))
716 return result;
717 auto &value = result.value();
718 if (!value->isFullyEvaluated())
719 return valueResult;
720
721 // If the base path is unknown, mark the result as unknown.
722 if (result.value()->isUnknown()) {
723 valueResult->markUnknown();
724 return valueResult;
725 }
726
727 path->setBasepath(*llvm::cast<evaluator::BasePathValue>(value.get()));
728 return valueResult;
729}
730
731FailureOr<evaluator::EvaluatorValuePtr> circt::om::Evaluator::evaluateEmptyPath(
732 FrozenEmptyPathOp op, ActualParameters actualParams, Location loc) {
733 auto valueResult = getOrCreateValue(op, actualParams, loc).value();
734 return valueResult;
735}
736
737FailureOr<evaluator::EvaluatorValuePtr>
738circt::om::Evaluator::evaluateUnknownValue(UnknownValueOp op, Location loc) {
739 using namespace circt::om::evaluator;
740
741 // Create an unknown value of the appropriate type by switching on the type
742 auto result =
743 TypeSwitch<Type, FailureOr<EvaluatorValuePtr>>(op.getType())
744 .Case([&](ListType type) -> FailureOr<EvaluatorValuePtr> {
745 // Create an empty list
746 return success(std::make_shared<ListValue>(type, loc));
747 })
748 .Case([&](ClassType type) -> FailureOr<EvaluatorValuePtr> {
749 // Look up the class definition
750 auto classDef =
751 symbolTable.lookup<ClassLike>(type.getClassName().getValue());
752 if (!classDef)
753 return symbolTable.getOp()->emitError("unknown class name ")
754 << type.getClassName();
755
756 // Create an ObjectValue for both ClassOp and ClassExternOp
757 return success(std::make_shared<ObjectValue>(classDef, loc));
758 })
759 .Case([&](FrozenBasePathType type) -> FailureOr<EvaluatorValuePtr> {
760 // Create an empty basepath
761 return success(std::make_shared<BasePathValue>(type.getContext()));
762 })
763 .Case([&](FrozenPathType type) -> FailureOr<EvaluatorValuePtr> {
764 // Create an empty path
765 return success(
766 std::make_shared<PathValue>(PathValue::getEmptyPath(loc)));
767 })
768 .Default([&](Type type) -> FailureOr<EvaluatorValuePtr> {
769 // For all other types (primitives like integer, string,
770 // etc.), create an AttributeValue
771 return success(AttributeValue::get(type, LocationAttr(loc)));
772 });
773
774 // Mark the result as unknown if successful
775 if (succeeded(result))
776 result->get()->markUnknown();
777
778 return result;
779}
780
781//===----------------------------------------------------------------------===//
782// ObjectValue
783//===----------------------------------------------------------------------===//
784
785/// Get a field of the Object by name.
786FailureOr<EvaluatorValuePtr>
788 auto field = fields.find(name);
789 if (field == fields.end())
790 return cls.emitError("field ") << name << " does not exist";
791 return success(fields[name]);
792}
793
794/// Get an ArrayAttr with the names of the fields in the Object. Sort the fields
795/// so there is always a stable order.
797 SmallVector<Attribute> fieldNames;
798 for (auto &f : fields)
799 fieldNames.push_back(f.first);
800
801 llvm::sort(fieldNames, [](Attribute a, Attribute b) {
802 return cast<StringAttr>(a).getValue() < cast<StringAttr>(b).getValue();
803 });
804
805 return ArrayAttr::get(cls.getContext(), fieldNames);
806}
807
809 for (auto &&[e, value] : fields)
810 if (failed(finalizeEvaluatorValue(value)))
811 return failure();
812
813 return success();
814}
815
816//===----------------------------------------------------------------------===//
817// ReferenceValue
818//===----------------------------------------------------------------------===//
819
821 auto result = getStrippedValue();
822 if (failed(result))
823 return result;
824 value = std::move(result.value());
825 // the stripped value also needs to be finalized
826 if (failed(finalizeEvaluatorValue(value)))
827 return failure();
828
829 return success();
830}
831
832//===----------------------------------------------------------------------===//
833// ListValue
834//===----------------------------------------------------------------------===//
835
837 for (auto &value : elements) {
838 if (failed(finalizeEvaluatorValue(value)))
839 return failure();
840 }
841 return success();
842}
843
844//===----------------------------------------------------------------------===//
845// BasePathValue
846//===----------------------------------------------------------------------===//
847
849 : EvaluatorValue(context, Kind::BasePath, UnknownLoc::get(context)),
850 path(PathAttr::get(context, {})) {
851 markFullyEvaluated();
852}
853
854evaluator::BasePathValue::BasePathValue(PathAttr path, Location loc)
855 : EvaluatorValue(path.getContext(), Kind::BasePath, loc), path(path) {}
856
858 assert(isFullyEvaluated());
859 return path;
860}
861
863 assert(!isFullyEvaluated());
864 auto newPath = llvm::to_vector(basepath.path.getPath());
865 auto oldPath = path.getPath();
866 newPath.append(oldPath.begin(), oldPath.end());
867 path = PathAttr::get(path.getContext(), newPath);
868 markFullyEvaluated();
869}
870
871//===----------------------------------------------------------------------===//
872// PathValue
873//===----------------------------------------------------------------------===//
874
875evaluator::PathValue::PathValue(TargetKindAttr targetKind, PathAttr path,
876 StringAttr module, StringAttr ref,
877 StringAttr field, Location loc)
878 : EvaluatorValue(loc.getContext(), Kind::Path, loc), targetKind(targetKind),
879 path(path), module(module), ref(ref), field(field) {}
880
882 PathValue path(nullptr, nullptr, nullptr, nullptr, nullptr, loc);
883 path.markFullyEvaluated();
884 return path;
885}
886
888 // If the module is null, then this is a path to a deleted object.
889 if (!targetKind)
890 return StringAttr::get(getContext(), "OMDeleted:");
891 SmallString<64> result;
892 switch (targetKind.getValue()) {
893 case TargetKind::DontTouch:
894 result += "OMDontTouchedReferenceTarget";
895 break;
896 case TargetKind::Instance:
897 result += "OMInstanceTarget";
898 break;
899 case TargetKind::MemberInstance:
900 result += "OMMemberInstanceTarget";
901 break;
902 case TargetKind::MemberReference:
903 result += "OMMemberReferenceTarget";
904 break;
905 case TargetKind::Reference:
906 result += "OMReferenceTarget";
907 break;
908 }
909 result += ":~";
910 if (!path.getPath().empty())
911 result += path.getPath().front().module;
912 else
913 result += module.getValue();
914 result += '|';
915 for (const auto &elt : path) {
916 result += elt.module.getValue();
917 result += '/';
918 result += elt.instance.getValue();
919 result += ':';
920 }
921 if (!module.getValue().empty())
922 result += module.getValue();
923 if (!ref.getValue().empty()) {
924 result += '>';
925 result += ref.getValue();
926 }
927 if (!field.getValue().empty())
928 result += field.getValue();
929 return StringAttr::get(field.getContext(), result);
930}
931
933 assert(!isFullyEvaluated());
934 auto newPath = llvm::to_vector(basepath.getPath().getPath());
935 auto oldPath = path.getPath();
936 newPath.append(oldPath.begin(), oldPath.end());
937 path = PathAttr::get(path.getContext(), newPath);
938 markFullyEvaluated();
939}
940
941//===----------------------------------------------------------------------===//
942// AttributeValue
943//===----------------------------------------------------------------------===//
944
946 if (cast<TypedAttr>(attr).getType() != this->type)
947 return mlir::emitError(getLoc(), "cannot set AttributeValue of type ")
948 << this->type << " to Attribute " << attr;
949 if (isFullyEvaluated())
950 return mlir::emitError(
951 getLoc(),
952 "cannot set AttributeValue that has already been fully evaluated");
953 this->attr = attr;
954 markFullyEvaluated();
955 return success();
956}
957
959 if (!isFullyEvaluated())
960 return mlir::emitError(
961 getLoc(), "cannot finalize AttributeValue that is not fully evaluated");
962 return success();
963}
964
965std::shared_ptr<evaluator::EvaluatorValue>
966circt::om::evaluator::AttributeValue::get(Attribute attr, LocationAttr loc) {
967 auto type = cast<TypedAttr>(attr).getType();
968 auto *context = type.getContext();
969 if (!loc)
970 loc = UnknownLoc::get(context);
971
972 // Special handling for ListType to create proper ListValue objects instead of
973 // AttributeValue objects.
974 if (auto listType = dyn_cast<circt::om::ListType>(type)) {
975 SmallVector<EvaluatorValuePtr> elements;
976 auto listAttr = cast<om::ListAttr>(attr);
978 listAttr.getContext(), listAttr.getElements().getValue());
979 elements.append(values.begin(), values.end());
980 auto list = std::make_shared<evaluator::ListValue>(listType, elements, loc);
981 return list;
982 }
983
984 return std::shared_ptr<AttributeValue>(
985 new AttributeValue(PrivateTag{}, attr, loc));
986}
987
988std::shared_ptr<evaluator::EvaluatorValue>
989circt::om::evaluator::AttributeValue::get(Type type, LocationAttr loc) {
990 auto *context = type.getContext();
991 if (!loc)
992 loc = UnknownLoc::get(context);
993
994 // Special handling for ListType to create proper ListValue objects instead of
995 // AttributeValue objects.
996 if (auto listType = dyn_cast<circt::om::ListType>(type))
997 return std::make_shared<evaluator::ListValue>(listType, loc);
998 // Create the AttributeValue with the private tag
999 return std::shared_ptr<AttributeValue>(
1000 new AttributeValue(PrivateTag{}, type, loc));
1001}
assert(baseType &&"element must be base type")
static std::unique_ptr< Context > context
static Location getLoc(DefSlot slot)
Definition Mem2Reg.cpp:216
FailureOr< evaluator::EvaluatorValuePtr > evaluateBasePathCreate(FrozenBasePathCreateOp op, ActualParameters actualParams, Location loc)
FailureOr< evaluator::EvaluatorValuePtr > evaluateEmptyPath(FrozenEmptyPathOp op, ActualParameters actualParams, Location loc)
FailureOr< evaluator::EvaluatorValuePtr > getPartiallyEvaluatedValue(Type type, Location loc)
Definition Evaluator.cpp:70
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 > evaluateIntegerBinaryArithmetic(IntegerBinaryArithmeticOp op, ActualParameters actualParams, Location loc)
mlir::ModuleOp getModule()
Get the Module this Evaluator is built from.
Definition Evaluator.cpp:30
FailureOr< EvaluatorValuePtr > evaluateObjectField(ObjectFieldOp op, ActualParameters actualParams, Location loc)
Evaluator dispatch function for Object fields.
FailureOr< evaluator::EvaluatorValuePtr > evaluateUnknownValue(UnknownValueOp op, Location loc)
Evaluator(ModuleOp mod)
Construct an Evaluator with an IR module.
Definition Evaluator.cpp:27
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)
Definition Evaluator.cpp:96
SmallVectorImpl< std::shared_ptr< evaluator::EvaluatorValue > > * ActualParameters
Definition Evaluator.h:391
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
Definition Evaluator.h:393
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.
Definition Evaluator.h:151
LogicalResult setAttr(Attribute attr)
friend std::shared_ptr< EvaluatorValue > get(Attribute attr, LocationAttr loc)
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:48
A List which contains variadic length of elements with the same type.
Definition Evaluator.h:212
const auto & getElements() const
Definition Evaluator.h:233
A composite Object, which has a type and fields.
Definition Evaluator.h:249
FailureOr< EvaluatorValuePtr > getField(StringAttr field)
Get a field of the Object by name.
ArrayAttr getFieldNames()
Get all the field names of the Object.
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.
Definition Evaluator.h:111
std::shared_ptr< EvaluatorValue > EvaluatorValuePtr
A value of an object in memory.
Definition Evaluator.h:38
SmallVector< EvaluatorValuePtr > getEvaluatorValuesFromAttributes(MLIRContext *context, ArrayRef< Attribute > attributes)
Definition Evaluator.cpp:35