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