CIRCT 22.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)
55 [](auto v) { return v->finalizeImpl(); });
56}
57
59 return llvm::TypeSwitch<const EvaluatorValue *, Type>(this)
60 .Case<AttributeValue>([](auto *attr) -> Type { return attr->getType(); })
61 .Case<ObjectValue>([](auto *object) { return object->getObjectType(); })
62 .Case<ListValue>([](auto *list) { return list->getListType(); })
63 .Case<ReferenceValue>([](auto *ref) { return ref->getValueType(); })
64 .Case<BasePathValue>(
65 [this](auto *tuple) { return FrozenBasePathType::get(ctx); })
66 .Case<PathValue>([this](auto *tuple) { return FrozenPathType::get(ctx); })
67 .Case<UnknownValue>([this](auto _) { return AnyType::get(ctx); });
68}
69
70FailureOr<evaluator::EvaluatorValuePtr>
72 using namespace circt::om::evaluator;
73
74 return TypeSwitch<mlir::Type, FailureOr<evaluator::EvaluatorValuePtr>>(type)
75 .Case([&](circt::om::ListType type) {
77 std::make_shared<evaluator::ListValue>(type, loc);
78 return success(result);
79 })
80 .Case([&](circt::om::ClassType type)
81 -> FailureOr<evaluator::EvaluatorValuePtr> {
82 ClassOp cls =
83 symbolTable.lookup<ClassOp>(type.getClassName().getValue());
84 if (!cls)
85 return symbolTable.getOp()->emitError("unknown class name ")
86 << type.getClassName();
87
89 std::make_shared<evaluator::ObjectValue>(cls, 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 ClassOp cls = symbolTable.lookup<ClassOp>(className);
185 if (!cls)
186 return symbolTable.getOp()->emitError("unknown class name ") << className;
187
188 auto formalParamNames = cls.getFormalParamNames().getAsRange<StringAttr>();
189 auto formalParamTypes = cls.getBodyBlock()->getArgumentTypes();
190
191 // Verify the actual parameters are the right size and types for this class.
192 if (actualParams->size() != formalParamTypes.size()) {
193 auto error = cls.emitError("actual parameter list length (")
194 << actualParams->size() << ") does not match formal "
195 << "parameter list length (" << formalParamTypes.size() << ")";
196 auto &diag = error.attachNote() << "actual parameters: ";
197 // FIXME: `diag << actualParams` doesn't work for some reason.
198 bool isFirst = true;
199 for (const auto &param : *actualParams) {
200 if (isFirst)
201 isFirst = false;
202 else
203 diag << ", ";
204 diag << param;
205 }
206 error.attachNote(cls.getLoc()) << "formal parameters: " << formalParamTypes;
207 return error;
208 }
209
210 // Verify the actual parameter types match.
211 for (auto [actualParam, formalParamName, formalParamType] :
212 llvm::zip(*actualParams, formalParamNames, formalParamTypes)) {
213 if (!actualParam || !actualParam.get())
214 return cls.emitError("actual parameter for ")
215 << formalParamName << " is null";
216
217 // Subtyping: if formal param is any type, any actual param may be passed.
218 if (isa<AnyType>(formalParamType))
219 continue;
220
221 Type actualParamType = actualParam->getType();
222
223 assert(actualParamType && "actualParamType must be non-null!");
224
225 if (!isa<evaluator::UnknownValue>(actualParam.get()) &&
226 actualParamType != formalParamType) {
227 auto error = cls.emitError("actual parameter for ")
228 << formalParamName << " has invalid type";
229 error.attachNote() << "actual parameter: " << *actualParam;
230 error.attachNote() << "format parameter type: " << formalParamType;
231 return error;
232 }
233 }
234
235 // Instantiate the fields.
237
238 auto *context = cls.getContext();
239 for (auto &op : cls.getOps())
240 for (auto result : op.getResults()) {
241 // Allocate the value, with unknown loc. It will be later set when
242 // evaluating the fields.
243 if (failed(
244 getOrCreateValue(result, actualParams, UnknownLoc::get(context))))
245 return failure();
246 // Add to the worklist.
247 worklist.push({result, actualParams});
248 }
249
250 auto fieldNames = cls.getFieldNames();
251 auto operands = cls.getFieldsOp()->getOperands();
252 for (size_t i = 0; i < fieldNames.size(); ++i) {
253 auto name = fieldNames[i];
254 auto value = operands[i];
255 auto fieldLoc = cls.getFieldLocByIndex(i);
256 FailureOr<evaluator::EvaluatorValuePtr> result =
257 evaluateValue(value, actualParams, fieldLoc);
258 if (failed(result))
259 return result;
260
261 fields[cast<StringAttr>(name)] = result.value();
262 }
263
264 // If the there is an instance, we must update the object value.
265 if (instanceKey.first) {
266 auto result =
267 getOrCreateValue(instanceKey.first, instanceKey.second, loc).value();
268 auto *object = llvm::cast<evaluator::ObjectValue>(result.get());
269 object->setFields(std::move(fields));
270 return result;
271 }
272
273 // If it's external call, just allocate new ObjectValue.
275 std::make_shared<evaluator::ObjectValue>(cls, fields, loc);
276 return result;
277}
278
279/// Instantiate an Object with its class name and actual parameters.
280FailureOr<std::shared_ptr<evaluator::EvaluatorValue>>
282 StringAttr className, ArrayRef<evaluator::EvaluatorValuePtr> actualParams) {
283 ClassOp cls = symbolTable.lookup<ClassOp>(className);
284 if (!cls)
285 return symbolTable.getOp()->emitError("unknown class name ") << className;
286
287 auto parameters =
288 std::make_unique<SmallVector<std::shared_ptr<evaluator::EvaluatorValue>>>(
289 actualParams);
290
291 actualParametersBuffers.push_back(std::move(parameters));
292
293 auto loc = cls.getLoc();
294 auto result = evaluateObjectInstance(
295 className, actualParametersBuffers.back().get(), loc);
296
297 if (failed(result))
298 return failure();
299
300 // `evaluateObjectInstance` has populated the worklist. Continue evaluations
301 // unless there is a partially evaluated value.
302 while (!worklist.empty()) {
303 auto [value, args] = worklist.front();
304 worklist.pop();
305
306 auto result = evaluateValue(value, args, loc);
307
308 if (failed(result))
309 return failure();
310
311 // It's possible that the value is not fully evaluated.
312 if (!result.value()->isFullyEvaluated())
313 worklist.push({value, args});
314 }
315
316 auto &object = result.value();
317 // Finalize the value. This will eliminate intermidiate ReferenceValue used as
318 // a placeholder in the initialization.
319 if (failed(object->finalize()))
320 return cls.emitError() << "failed to finalize evaluation. Probably the "
321 "class contains a dataflow cycle";
322 return object;
323}
324
325FailureOr<evaluator::EvaluatorValuePtr>
327 Location loc) {
328 auto evaluatorValue = getOrCreateValue(value, actualParams, loc).value();
329
330 // Return if the value is already evaluated.
331 if (evaluatorValue->isFullyEvaluated())
332 return evaluatorValue;
333
334 return llvm::TypeSwitch<Value, FailureOr<evaluator::EvaluatorValuePtr>>(value)
335 .Case([&](BlockArgument arg) {
336 return evaluateParameter(arg, actualParams, loc);
337 })
338 .Case([&](OpResult result) {
339 return TypeSwitch<Operation *, FailureOr<evaluator::EvaluatorValuePtr>>(
340 result.getDefiningOp())
341 .Case([&](ConstantOp op) {
342 return evaluateConstant(op, actualParams, loc);
343 })
344 .Case([&](IntegerBinaryArithmeticOp op) {
345 return evaluateIntegerBinaryArithmetic(op, actualParams, loc);
346 })
347 .Case([&](ObjectOp op) {
348 return evaluateObjectInstance(op, actualParams);
349 })
350 .Case([&](ObjectFieldOp op) {
351 return evaluateObjectField(op, actualParams, loc);
352 })
353 .Case([&](ListCreateOp op) {
354 return evaluateListCreate(op, actualParams, loc);
355 })
356 .Case([&](ListConcatOp op) {
357 return evaluateListConcat(op, actualParams, loc);
358 })
359 .Case([&](AnyCastOp op) {
360 return evaluateValue(op.getInput(), actualParams, loc);
361 })
362 .Case([&](FrozenBasePathCreateOp op) {
363 return evaluateBasePathCreate(op, actualParams, loc);
364 })
365 .Case([&](FrozenPathCreateOp op) {
366 return evaluatePathCreate(op, actualParams, loc);
367 })
368 .Case([&](FrozenEmptyPathOp op) {
369 return evaluateEmptyPath(op, actualParams, loc);
370 })
371 .Case<UnknownValueOp>([&](UnknownValueOp op) {
372 return evaluateUnknownValue(op, loc);
373 })
374 .Default([&](Operation *op) {
375 auto error = op->emitError("unable to evaluate value");
376 error.attachNote() << "value: " << value;
377 return error;
378 });
379 });
380}
381
382/// Evaluator dispatch function for parameters.
383FailureOr<evaluator::EvaluatorValuePtr> circt::om::Evaluator::evaluateParameter(
384 BlockArgument formalParam, ActualParameters actualParams, Location loc) {
385 auto val = (*actualParams)[formalParam.getArgNumber()];
386 val->setLoc(loc);
387 return success(val);
388}
389
390/// Evaluator dispatch function for constants.
391FailureOr<circt::om::evaluator::EvaluatorValuePtr>
393 ActualParameters actualParams,
394 Location loc) {
395 // For list constants, create ListValue.
396 return success(om::evaluator::AttributeValue::get(op.getValue(), loc));
397}
398
399// Evaluator dispatch function for integer binary arithmetic.
400FailureOr<EvaluatorValuePtr>
402 IntegerBinaryArithmeticOp op, ActualParameters actualParams, Location loc) {
403 // Get the op's EvaluatorValue handle, in case it hasn't been evaluated yet.
404 auto handle = getOrCreateValue(op.getResult(), actualParams, loc);
405
406 // If it's fully evaluated, we can return it.
407 if (handle.value()->isFullyEvaluated())
408 return handle;
409
410 // Evaluate operands if necessary, and return the partially evaluated value if
411 // they aren't ready.
412 auto lhsResult = evaluateValue(op.getLhs(), actualParams, loc);
413 if (failed(lhsResult))
414 return lhsResult;
415 if (!lhsResult.value()->isFullyEvaluated())
416 return handle;
417
418 auto rhsResult = evaluateValue(op.getRhs(), actualParams, loc);
419 if (failed(rhsResult))
420 return rhsResult;
421 if (!rhsResult.value()->isFullyEvaluated())
422 return handle;
423
424 // Short circuit if this is an UnknownValue.
425 if (isa<evaluator::UnknownValue>(*lhsResult.value()))
426 return lhsResult;
427 if (isa<evaluator::UnknownValue>(*rhsResult.value()))
428 return rhsResult;
429
430 // Extract the integer attributes.
431 auto extractAttr = [](evaluator::EvaluatorValue *value) {
432 return std::move(
433 llvm::TypeSwitch<evaluator::EvaluatorValue *, om::IntegerAttr>(value)
434 .Case([](evaluator::AttributeValue *val) {
435 return val->getAs<om::IntegerAttr>();
436 })
437 .Case([](evaluator::ReferenceValue *val) {
438 return cast<evaluator::AttributeValue>(
439 val->getStrippedValue()->get())
440 ->getAs<om::IntegerAttr>();
441 }));
442 };
443
444 om::IntegerAttr lhs = extractAttr(lhsResult.value().get());
445 om::IntegerAttr rhs = extractAttr(rhsResult.value().get());
446 assert(lhs && rhs &&
447 "expected om::IntegerAttr for IntegerBinaryArithmeticOp operands");
448
449 // Extend values if necessary to match bitwidth. Most interesting arithmetic
450 // on APSInt asserts that both operands are the same bitwidth, but the
451 // IntegerAttrs we are working with may have used the smallest necessary
452 // bitwidth to represent the number they hold, and won't necessarily match.
453 APSInt lhsVal = lhs.getValue().getAPSInt();
454 APSInt rhsVal = rhs.getValue().getAPSInt();
455 if (lhsVal.getBitWidth() > rhsVal.getBitWidth())
456 rhsVal = rhsVal.extend(lhsVal.getBitWidth());
457 else if (rhsVal.getBitWidth() > lhsVal.getBitWidth())
458 lhsVal = lhsVal.extend(rhsVal.getBitWidth());
459
460 // Perform arbitrary precision signed integer binary arithmetic.
461 FailureOr<APSInt> result = op.evaluateIntegerOperation(lhsVal, rhsVal);
462
463 if (failed(result))
464 return op->emitError("failed to evaluate integer operation");
465
466 // Package the result as a new om::IntegerAttr.
467 MLIRContext *ctx = op->getContext();
468 auto resultAttr =
469 om::IntegerAttr::get(ctx, mlir::IntegerAttr::get(ctx, result.value()));
470
471 // Finalize the op result value.
472 auto *handleValue = cast<evaluator::AttributeValue>(handle.value().get());
473 auto resultStatus = handleValue->setAttr(resultAttr);
474 if (failed(resultStatus))
475 return resultStatus;
476
477 auto finalizeStatus = handleValue->finalize();
478 if (failed(finalizeStatus))
479 return finalizeStatus;
480
481 return handle;
482}
483
484/// Evaluator dispatch function for Object instances.
485FailureOr<circt::om::Evaluator::ActualParameters>
487 ValueRange range, ActualParameters actualParams, Location loc) {
488 // Create an unique storage to store parameters.
489 auto parameters = std::make_unique<
490 SmallVector<std::shared_ptr<evaluator::EvaluatorValue>>>();
491
492 // Collect operands' evaluator values in the current instantiation context.
493 for (auto input : range) {
494 auto inputResult = getOrCreateValue(input, actualParams, loc);
495 if (failed(inputResult))
496 return failure();
497 parameters->push_back(inputResult.value());
498 }
499
500 actualParametersBuffers.push_back(std::move(parameters));
501 return actualParametersBuffers.back().get();
502}
503
504/// Evaluator dispatch function for Object instances.
505FailureOr<evaluator::EvaluatorValuePtr>
507 ActualParameters actualParams) {
508 auto loc = op.getLoc();
509 if (isFullyEvaluated({op, actualParams}))
510 return getOrCreateValue(op, actualParams, loc);
511
512 auto params =
513 createParametersFromOperands(op.getOperands(), actualParams, loc);
514 if (failed(params))
515 return failure();
516 return evaluateObjectInstance(op.getClassNameAttr(), params.value(), loc,
517 {op, actualParams});
518}
519
520/// Evaluator dispatch function for Object fields.
521FailureOr<evaluator::EvaluatorValuePtr>
523 ActualParameters actualParams,
524 Location loc) {
525 // Evaluate the Object itself, in case it hasn't been evaluated yet.
526 FailureOr<evaluator::EvaluatorValuePtr> currentObjectResult =
527 evaluateValue(op.getObject(), actualParams, loc);
528 if (failed(currentObjectResult))
529 return currentObjectResult;
530
531 auto result = currentObjectResult.value();
532
533 // If the field is an unknown value, return that.
534 if (isa<evaluator::UnknownValue>(result.get()))
535 return result;
536
537 auto *currentObject = llvm::cast<evaluator::ObjectValue>(result.get());
538
539 auto objectFieldValue = getOrCreateValue(op, actualParams, loc).value();
540
541 // Iteratively access nested fields through the path until we reach the final
542 // field in the path.
544 for (auto field : op.getFieldPath().getAsRange<FlatSymbolRefAttr>()) {
545 // `currentObject` might no be fully evaluated.
546 if (!currentObject->getFields().contains(field.getAttr()))
547 return objectFieldValue;
548
549 auto currentField = currentObject->getField(field.getAttr());
550 finalField = currentField.value();
551 if (auto *nextObject =
552 llvm::dyn_cast<evaluator::ObjectValue>(finalField.get()))
553 currentObject = nextObject;
554 }
555
556 // Update the reference.
557 llvm::cast<evaluator::ReferenceValue>(objectFieldValue.get())
558 ->setValue(finalField);
559
560 // Return the field being accessed.
561 return objectFieldValue;
562}
563
564/// Evaluator dispatch function for List creation.
565FailureOr<evaluator::EvaluatorValuePtr>
567 ActualParameters actualParams,
568 Location loc) {
569 // Evaluate the Object itself, in case it hasn't been evaluated yet.
570 SmallVector<evaluator::EvaluatorValuePtr> values;
571 auto list = getOrCreateValue(op, actualParams, loc);
572 for (auto operand : op.getOperands()) {
573 auto result = evaluateValue(operand, actualParams, loc);
574 if (failed(result))
575 return result;
576 if (!result.value()->isFullyEvaluated())
577 return list;
578 // If any operand is an unknown value, directly return that.
579 if (isa<evaluator::UnknownValue>(result.value().get()))
580 return result;
581 values.push_back(result.value());
582 }
583
584 // Return the list.
585 llvm::cast<evaluator::ListValue>(list.value().get())
586 ->setElements(std::move(values));
587 return list;
588}
589
590/// Evaluator dispatch function for List concatenation.
591FailureOr<evaluator::EvaluatorValuePtr>
593 ActualParameters actualParams,
594 Location loc) {
595 // Evaluate the List concat op itself, in case it hasn't been evaluated yet.
596 SmallVector<evaluator::EvaluatorValuePtr> values;
597 auto list = getOrCreateValue(op, actualParams, loc);
598
599 // Extract the ListValue, either directly or through an object reference.
600 auto extractList = [](evaluator::EvaluatorValue *value) {
601 return std::move(
602 llvm::TypeSwitch<evaluator::EvaluatorValue *, evaluator::ListValue *>(
603 value)
604 .Case([](evaluator::ListValue *val) { return val; })
605 .Case([](evaluator::ReferenceValue *val) {
606 return cast<evaluator::ListValue>(val->getStrippedValue()->get());
607 }));
608 };
609
610 for (auto operand : op.getOperands()) {
611 auto result = evaluateValue(operand, actualParams, loc);
612 if (failed(result))
613 return result;
614 if (!result.value()->isFullyEvaluated())
615 return list;
616 // Return an unknown value if any unknown value is observed.
617 if (isa<evaluator::UnknownValue>(result.value().get()))
618 return result;
619
620 // Extract this sublist and ensure it's done evaluating.
621 evaluator::ListValue *subList = extractList(result.value().get());
622 if (!subList->isFullyEvaluated())
623 return list;
624
625 // Append each EvaluatorValue from the sublist.
626 for (const auto &subValue : subList->getElements())
627 values.push_back(subValue);
628 }
629
630 // Return the concatenated list.
631 llvm::cast<evaluator::ListValue>(list.value().get())
632 ->setElements(std::move(values));
633 return list;
634}
635
636FailureOr<evaluator::EvaluatorValuePtr>
638 ActualParameters actualParams,
639 Location loc) {
640 // Evaluate the Object itself, in case it hasn't been evaluated yet.
641 auto valueResult = getOrCreateValue(op, actualParams, loc).value();
642 auto *path = llvm::cast<evaluator::BasePathValue>(valueResult.get());
643 auto result = evaluateValue(op.getBasePath(), actualParams, loc);
644 if (failed(result))
645 return result;
646 auto &value = result.value();
647 if (!value->isFullyEvaluated())
648 return valueResult;
649
650 // Return an unknown value if any unknown value is observed.
651 if (isa<evaluator::UnknownValue>(result.value().get()))
652 return result;
653
654 path->setBasepath(*llvm::cast<evaluator::BasePathValue>(value.get()));
655 return valueResult;
656}
657
658FailureOr<evaluator::EvaluatorValuePtr>
660 ActualParameters actualParams,
661 Location loc) {
662 // Evaluate the Object itself, in case it hasn't been evaluated yet.
663 auto valueResult = getOrCreateValue(op, actualParams, loc).value();
664 auto *path = llvm::cast<evaluator::PathValue>(valueResult.get());
665 auto result = evaluateValue(op.getBasePath(), actualParams, loc);
666 if (failed(result))
667 return result;
668 auto &value = result.value();
669 if (!value->isFullyEvaluated())
670 return valueResult;
671
672 // Return an unknown value if any unknown value is observed.
673 if (isa<evaluator::UnknownValue>(result.value().get()))
674 return result;
675
676 path->setBasepath(*llvm::cast<evaluator::BasePathValue>(value.get()));
677 return valueResult;
678}
679
680FailureOr<evaluator::EvaluatorValuePtr> circt::om::Evaluator::evaluateEmptyPath(
681 FrozenEmptyPathOp op, ActualParameters actualParams, Location loc) {
682 auto valueResult = getOrCreateValue(op, actualParams, loc).value();
683 return valueResult;
684}
685
686FailureOr<evaluator::EvaluatorValuePtr>
687circt::om::Evaluator::evaluateUnknownValue(UnknownValueOp op, Location loc) {
688 return success(std::make_shared<evaluator::UnknownValue>(
689 evaluator::UnknownValue(op.getContext(), loc)));
690}
691
692//===----------------------------------------------------------------------===//
693// ObjectValue
694//===----------------------------------------------------------------------===//
695
696/// Get a field of the Object by name.
697FailureOr<EvaluatorValuePtr>
699 auto field = fields.find(name);
700 if (field == fields.end())
701 return cls.emitError("field ") << name << " does not exist";
702 return success(fields[name]);
703}
704
705/// Get an ArrayAttr with the names of the fields in the Object. Sort the fields
706/// so there is always a stable order.
708 SmallVector<Attribute> fieldNames;
709 for (auto &f : fields)
710 fieldNames.push_back(f.first);
711
712 llvm::sort(fieldNames, [](Attribute a, Attribute b) {
713 return cast<StringAttr>(a).getValue() < cast<StringAttr>(b).getValue();
714 });
715
716 return ArrayAttr::get(cls.getContext(), fieldNames);
717}
718
720 for (auto &&[e, value] : fields)
721 if (failed(finalizeEvaluatorValue(value)))
722 return failure();
723
724 return success();
725}
726
727//===----------------------------------------------------------------------===//
728// ReferenceValue
729//===----------------------------------------------------------------------===//
730
732 auto result = getStrippedValue();
733 if (failed(result))
734 return result;
735 value = std::move(result.value());
736 // the stripped value also needs to be finalized
737 if (failed(finalizeEvaluatorValue(value)))
738 return failure();
739
740 return success();
741}
742
743//===----------------------------------------------------------------------===//
744// ListValue
745//===----------------------------------------------------------------------===//
746
748 for (auto &value : elements) {
749 if (failed(finalizeEvaluatorValue(value)))
750 return failure();
751 }
752 return success();
753}
754
755//===----------------------------------------------------------------------===//
756// BasePathValue
757//===----------------------------------------------------------------------===//
758
760 : EvaluatorValue(context, Kind::BasePath, UnknownLoc::get(context)),
761 path(PathAttr::get(context, {})) {
762 markFullyEvaluated();
763}
764
765evaluator::BasePathValue::BasePathValue(PathAttr path, Location loc)
766 : EvaluatorValue(path.getContext(), Kind::BasePath, loc), path(path) {}
767
769 assert(isFullyEvaluated());
770 return path;
771}
772
774 assert(!isFullyEvaluated());
775 auto newPath = llvm::to_vector(basepath.path.getPath());
776 auto oldPath = path.getPath();
777 newPath.append(oldPath.begin(), oldPath.end());
778 path = PathAttr::get(path.getContext(), newPath);
779 markFullyEvaluated();
780}
781
782//===----------------------------------------------------------------------===//
783// PathValue
784//===----------------------------------------------------------------------===//
785
786evaluator::PathValue::PathValue(TargetKindAttr targetKind, PathAttr path,
787 StringAttr module, StringAttr ref,
788 StringAttr field, Location loc)
789 : EvaluatorValue(loc.getContext(), Kind::Path, loc), targetKind(targetKind),
790 path(path), module(module), ref(ref), field(field) {}
791
793 PathValue path(nullptr, nullptr, nullptr, nullptr, nullptr, loc);
794 path.markFullyEvaluated();
795 return path;
796}
797
799 // If the module is null, then this is a path to a deleted object.
800 if (!targetKind)
801 return StringAttr::get(getContext(), "OMDeleted:");
802 SmallString<64> result;
803 switch (targetKind.getValue()) {
804 case TargetKind::DontTouch:
805 result += "OMDontTouchedReferenceTarget";
806 break;
807 case TargetKind::Instance:
808 result += "OMInstanceTarget";
809 break;
810 case TargetKind::MemberInstance:
811 result += "OMMemberInstanceTarget";
812 break;
813 case TargetKind::MemberReference:
814 result += "OMMemberReferenceTarget";
815 break;
816 case TargetKind::Reference:
817 result += "OMReferenceTarget";
818 break;
819 }
820 result += ":~";
821 if (!path.getPath().empty())
822 result += path.getPath().front().module;
823 else
824 result += module.getValue();
825 result += '|';
826 for (const auto &elt : path) {
827 result += elt.module.getValue();
828 result += '/';
829 result += elt.instance.getValue();
830 result += ':';
831 }
832 if (!module.getValue().empty())
833 result += module.getValue();
834 if (!ref.getValue().empty()) {
835 result += '>';
836 result += ref.getValue();
837 }
838 if (!field.getValue().empty())
839 result += field.getValue();
840 return StringAttr::get(field.getContext(), result);
841}
842
844 assert(!isFullyEvaluated());
845 auto newPath = llvm::to_vector(basepath.getPath().getPath());
846 auto oldPath = path.getPath();
847 newPath.append(oldPath.begin(), oldPath.end());
848 path = PathAttr::get(path.getContext(), newPath);
849 markFullyEvaluated();
850}
851
852//===----------------------------------------------------------------------===//
853// AttributeValue
854//===----------------------------------------------------------------------===//
855
857 if (cast<TypedAttr>(attr).getType() != this->type)
858 return mlir::emitError(getLoc(), "cannot set AttributeValue of type ")
859 << this->type << " to Attribute " << attr;
860 if (isFullyEvaluated())
861 return mlir::emitError(
862 getLoc(),
863 "cannot set AttributeValue that has already been fully evaluated");
864 this->attr = attr;
865 markFullyEvaluated();
866 return success();
867}
868
870 if (!isFullyEvaluated())
871 return mlir::emitError(
872 getLoc(), "cannot finalize AttributeValue that is not fully evaluated");
873 return success();
874}
875
876std::shared_ptr<evaluator::EvaluatorValue>
877circt::om::evaluator::AttributeValue::get(Attribute attr, LocationAttr loc) {
878 auto type = cast<TypedAttr>(attr).getType();
879 auto *context = type.getContext();
880 if (!loc)
881 loc = UnknownLoc::get(context);
882
883 // Special handling for ListType to create proper ListValue objects instead of
884 // AttributeValue objects.
885 if (auto listType = dyn_cast<circt::om::ListType>(type)) {
886 SmallVector<EvaluatorValuePtr> elements;
887 auto listAttr = cast<om::ListAttr>(attr);
889 listAttr.getContext(), listAttr.getElements().getValue());
890 elements.append(values.begin(), values.end());
891 auto list = std::make_shared<evaluator::ListValue>(listType, elements, loc);
892 return list;
893 }
894
895 return std::shared_ptr<AttributeValue>(
896 new AttributeValue(PrivateTag{}, attr, loc));
897}
898
899std::shared_ptr<evaluator::EvaluatorValue>
900circt::om::evaluator::AttributeValue::get(Type type, LocationAttr loc) {
901 auto *context = type.getContext();
902 if (!loc)
903 loc = UnknownLoc::get(context);
904
905 // Special handling for ListType to create proper ListValue objects instead of
906 // AttributeValue objects.
907 if (auto listType = dyn_cast<circt::om::ListType>(type))
908 return std::make_shared<evaluator::ListValue>(listType, loc);
909 // Create the AttributeValue with the private tag
910 return std::shared_ptr<AttributeValue>(
911 new AttributeValue(PrivateTag{}, type, loc));
912}
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:71
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:395
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:397
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:134
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:195
const auto & getElements() const
Definition Evaluator.h:216
A composite Object, which has a type and fields.
Definition Evaluator.h:232
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:94
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