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