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