mirror of https://github.com/llvm/circt.git
872 lines
32 KiB
C++
872 lines
32 KiB
C++
//===- Evaluator.cpp - Object Model dialect evaluator ---------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file implements the Object Model dialect Evaluator.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "circt/Dialect/OM/Evaluator/Evaluator.h"
|
|
#include "mlir/IR/BuiltinAttributeInterfaces.h"
|
|
#include "mlir/IR/Location.h"
|
|
#include "mlir/IR/SymbolTable.h"
|
|
#include "llvm/ADT/TypeSwitch.h"
|
|
#include "llvm/Support/Debug.h"
|
|
|
|
#define DEBUG_TYPE "om-evaluator"
|
|
|
|
using namespace mlir;
|
|
using namespace circt::om;
|
|
|
|
/// Construct an Evaluator with an IR module.
|
|
circt::om::Evaluator::Evaluator(ModuleOp mod) : symbolTable(mod) {}
|
|
|
|
/// Get the Module this Evaluator is built from.
|
|
ModuleOp circt::om::Evaluator::getModule() {
|
|
return cast<ModuleOp>(symbolTable.getOp());
|
|
}
|
|
|
|
SmallVector<evaluator::EvaluatorValuePtr>
|
|
circt::om::getEvaluatorValuesFromAttributes(MLIRContext *context,
|
|
ArrayRef<Attribute> attributes) {
|
|
SmallVector<evaluator::EvaluatorValuePtr> values;
|
|
values.reserve(attributes.size());
|
|
for (auto attr : attributes)
|
|
values.push_back(evaluator::AttributeValue::get(cast<TypedAttr>(attr)));
|
|
return values;
|
|
}
|
|
|
|
LogicalResult circt::om::evaluator::EvaluatorValue::finalize() {
|
|
using namespace evaluator;
|
|
// Early return if already finalized.
|
|
if (finalized)
|
|
return success();
|
|
// Enable the flag to avoid infinite recursions.
|
|
finalized = true;
|
|
assert(isFullyEvaluated());
|
|
return llvm::TypeSwitch<EvaluatorValue *, LogicalResult>(this)
|
|
.Case<AttributeValue, ObjectValue, ListValue, ReferenceValue,
|
|
BasePathValue, PathValue>([](auto v) { return v->finalizeImpl(); });
|
|
}
|
|
|
|
Type circt::om::evaluator::EvaluatorValue::getType() const {
|
|
return llvm::TypeSwitch<const EvaluatorValue *, Type>(this)
|
|
.Case<AttributeValue>([](auto *attr) -> Type { return attr->getType(); })
|
|
.Case<ObjectValue>([](auto *object) { return object->getObjectType(); })
|
|
.Case<ListValue>([](auto *list) { return list->getListType(); })
|
|
.Case<ReferenceValue>([](auto *ref) { return ref->getValueType(); })
|
|
.Case<BasePathValue>(
|
|
[this](auto *tuple) { return FrozenBasePathType::get(ctx); })
|
|
.Case<PathValue>(
|
|
[this](auto *tuple) { return FrozenPathType::get(ctx); });
|
|
}
|
|
|
|
FailureOr<evaluator::EvaluatorValuePtr>
|
|
circt::om::Evaluator::getPartiallyEvaluatedValue(Type type, Location loc) {
|
|
using namespace circt::om::evaluator;
|
|
|
|
return TypeSwitch<mlir::Type, FailureOr<evaluator::EvaluatorValuePtr>>(type)
|
|
.Case([&](circt::om::ListType type) {
|
|
evaluator::EvaluatorValuePtr result =
|
|
std::make_shared<evaluator::ListValue>(type, loc);
|
|
return success(result);
|
|
})
|
|
.Case([&](circt::om::ClassType type)
|
|
-> FailureOr<evaluator::EvaluatorValuePtr> {
|
|
ClassOp cls =
|
|
symbolTable.lookup<ClassOp>(type.getClassName().getValue());
|
|
if (!cls)
|
|
return symbolTable.getOp()->emitError("unknown class name ")
|
|
<< type.getClassName();
|
|
|
|
evaluator::EvaluatorValuePtr result =
|
|
std::make_shared<evaluator::ObjectValue>(cls, loc);
|
|
|
|
return success(result);
|
|
})
|
|
.Default([&](auto type) { return failure(); });
|
|
}
|
|
|
|
FailureOr<evaluator::EvaluatorValuePtr> circt::om::Evaluator::getOrCreateValue(
|
|
Value value, ActualParameters actualParams, Location loc) {
|
|
auto it = objects.find({value, actualParams});
|
|
if (it != objects.end()) {
|
|
auto evalVal = it->second;
|
|
evalVal->setLocIfUnknown(loc);
|
|
return evalVal;
|
|
}
|
|
|
|
FailureOr<evaluator::EvaluatorValuePtr> result =
|
|
TypeSwitch<Value, FailureOr<evaluator::EvaluatorValuePtr>>(value)
|
|
.Case([&](BlockArgument arg) {
|
|
auto val = (*actualParams)[arg.getArgNumber()];
|
|
val->setLoc(loc);
|
|
return val;
|
|
})
|
|
.Case([&](OpResult result) {
|
|
return TypeSwitch<Operation *,
|
|
FailureOr<evaluator::EvaluatorValuePtr>>(
|
|
result.getDefiningOp())
|
|
.Case([&](ConstantOp op) {
|
|
return evaluateConstant(op, actualParams, loc);
|
|
})
|
|
.Case([&](IntegerBinaryArithmeticOp op) {
|
|
// Create a partially evaluated AttributeValue of
|
|
// om::IntegerType in case we need to delay evaluation.
|
|
evaluator::EvaluatorValuePtr result =
|
|
evaluator::AttributeValue::get(op.getResult().getType(),
|
|
loc);
|
|
return success(result);
|
|
})
|
|
.Case<ObjectFieldOp>([&](auto op) {
|
|
// Create a reference value since the value pointed by object
|
|
// field op is not created yet.
|
|
evaluator::EvaluatorValuePtr result =
|
|
std::make_shared<evaluator::ReferenceValue>(
|
|
value.getType(), loc);
|
|
return success(result);
|
|
})
|
|
.Case<AnyCastOp>([&](AnyCastOp op) {
|
|
return getOrCreateValue(op.getInput(), actualParams, loc);
|
|
})
|
|
.Case<FrozenBasePathCreateOp>([&](FrozenBasePathCreateOp op) {
|
|
evaluator::EvaluatorValuePtr result =
|
|
std::make_shared<evaluator::BasePathValue>(
|
|
op.getPathAttr(), loc);
|
|
return success(result);
|
|
})
|
|
.Case<FrozenPathCreateOp>([&](FrozenPathCreateOp op) {
|
|
evaluator::EvaluatorValuePtr result =
|
|
std::make_shared<evaluator::PathValue>(
|
|
op.getTargetKindAttr(), op.getPathAttr(),
|
|
op.getModuleAttr(), op.getRefAttr(),
|
|
op.getFieldAttr(), loc);
|
|
return success(result);
|
|
})
|
|
.Case<FrozenEmptyPathOp>([&](FrozenEmptyPathOp op) {
|
|
evaluator::EvaluatorValuePtr result =
|
|
std::make_shared<evaluator::PathValue>(
|
|
evaluator::PathValue::getEmptyPath(loc));
|
|
return success(result);
|
|
})
|
|
.Case<ListCreateOp, ListConcatOp, ObjectFieldOp>([&](auto op) {
|
|
return getPartiallyEvaluatedValue(op.getType(), loc);
|
|
})
|
|
.Case<ObjectOp>([&](auto op) {
|
|
return getPartiallyEvaluatedValue(op.getType(), op.getLoc());
|
|
})
|
|
.Default([&](Operation *op) {
|
|
auto error = op->emitError("unable to evaluate value");
|
|
error.attachNote() << "value: " << value;
|
|
return error;
|
|
});
|
|
});
|
|
if (failed(result))
|
|
return result;
|
|
|
|
objects[{value, actualParams}] = result.value();
|
|
return result;
|
|
}
|
|
|
|
FailureOr<evaluator::EvaluatorValuePtr>
|
|
circt::om::Evaluator::evaluateObjectInstance(StringAttr className,
|
|
ActualParameters actualParams,
|
|
Location loc,
|
|
ObjectKey instanceKey) {
|
|
ClassOp cls = symbolTable.lookup<ClassOp>(className);
|
|
if (!cls)
|
|
return symbolTable.getOp()->emitError("unknown class name ") << className;
|
|
|
|
auto formalParamNames = cls.getFormalParamNames().getAsRange<StringAttr>();
|
|
auto formalParamTypes = cls.getBodyBlock()->getArgumentTypes();
|
|
|
|
// Verify the actual parameters are the right size and types for this class.
|
|
if (actualParams->size() != formalParamTypes.size()) {
|
|
auto error = cls.emitError("actual parameter list length (")
|
|
<< actualParams->size() << ") does not match formal "
|
|
<< "parameter list length (" << formalParamTypes.size() << ")";
|
|
auto &diag = error.attachNote() << "actual parameters: ";
|
|
// FIXME: `diag << actualParams` doesn't work for some reason.
|
|
bool isFirst = true;
|
|
for (const auto ¶m : *actualParams) {
|
|
if (isFirst)
|
|
isFirst = false;
|
|
else
|
|
diag << ", ";
|
|
diag << param;
|
|
}
|
|
error.attachNote(cls.getLoc()) << "formal parameters: " << formalParamTypes;
|
|
return error;
|
|
}
|
|
|
|
// Verify the actual parameter types match.
|
|
for (auto [actualParam, formalParamName, formalParamType] :
|
|
llvm::zip(*actualParams, formalParamNames, formalParamTypes)) {
|
|
if (!actualParam || !actualParam.get())
|
|
return cls.emitError("actual parameter for ")
|
|
<< formalParamName << " is null";
|
|
|
|
// Subtyping: if formal param is any type, any actual param may be passed.
|
|
if (isa<AnyType>(formalParamType))
|
|
continue;
|
|
|
|
Type actualParamType = actualParam->getType();
|
|
|
|
assert(actualParamType && "actualParamType must be non-null!");
|
|
|
|
if (actualParamType != formalParamType) {
|
|
auto error = cls.emitError("actual parameter for ")
|
|
<< formalParamName << " has invalid type";
|
|
error.attachNote() << "actual parameter: " << *actualParam;
|
|
error.attachNote() << "format parameter type: " << formalParamType;
|
|
return error;
|
|
}
|
|
}
|
|
|
|
// Instantiate the fields.
|
|
evaluator::ObjectFields fields;
|
|
|
|
auto *context = cls.getContext();
|
|
for (auto &op : cls.getOps())
|
|
for (auto result : op.getResults()) {
|
|
// Allocate the value, with unknown loc. It will be later set when
|
|
// evaluating the fields.
|
|
if (failed(
|
|
getOrCreateValue(result, actualParams, UnknownLoc::get(context))))
|
|
return failure();
|
|
// Add to the worklist.
|
|
worklist.push({result, actualParams});
|
|
}
|
|
|
|
auto fieldNames = cls.getFieldNames();
|
|
auto operands = cls.getFieldsOp()->getOperands();
|
|
for (size_t i = 0; i < fieldNames.size(); ++i) {
|
|
auto name = fieldNames[i];
|
|
auto value = operands[i];
|
|
auto fieldLoc = cls.getFieldLocByIndex(i);
|
|
FailureOr<evaluator::EvaluatorValuePtr> result =
|
|
evaluateValue(value, actualParams, fieldLoc);
|
|
if (failed(result))
|
|
return result;
|
|
|
|
fields[cast<StringAttr>(name)] = result.value();
|
|
}
|
|
|
|
// If the there is an instance, we must update the object value.
|
|
if (instanceKey.first) {
|
|
auto result =
|
|
getOrCreateValue(instanceKey.first, instanceKey.second, loc).value();
|
|
auto *object = llvm::cast<evaluator::ObjectValue>(result.get());
|
|
object->setFields(std::move(fields));
|
|
return result;
|
|
}
|
|
|
|
// If it's external call, just allocate new ObjectValue.
|
|
evaluator::EvaluatorValuePtr result =
|
|
std::make_shared<evaluator::ObjectValue>(cls, fields, loc);
|
|
return result;
|
|
}
|
|
|
|
/// Instantiate an Object with its class name and actual parameters.
|
|
FailureOr<std::shared_ptr<evaluator::EvaluatorValue>>
|
|
circt::om::Evaluator::instantiate(
|
|
StringAttr className, ArrayRef<evaluator::EvaluatorValuePtr> actualParams) {
|
|
ClassOp cls = symbolTable.lookup<ClassOp>(className);
|
|
if (!cls)
|
|
return symbolTable.getOp()->emitError("unknown class name ") << className;
|
|
|
|
auto parameters =
|
|
std::make_unique<SmallVector<std::shared_ptr<evaluator::EvaluatorValue>>>(
|
|
actualParams);
|
|
|
|
actualParametersBuffers.push_back(std::move(parameters));
|
|
|
|
auto loc = cls.getLoc();
|
|
auto result = evaluateObjectInstance(
|
|
className, actualParametersBuffers.back().get(), loc);
|
|
|
|
if (failed(result))
|
|
return failure();
|
|
|
|
// `evaluateObjectInstance` has populated the worklist. Continue evaluations
|
|
// unless there is a partially evaluated value.
|
|
while (!worklist.empty()) {
|
|
auto [value, args] = worklist.front();
|
|
worklist.pop();
|
|
|
|
auto result = evaluateValue(value, args, loc);
|
|
|
|
if (failed(result))
|
|
return failure();
|
|
|
|
// It's possible that the value is not fully evaluated.
|
|
if (!result.value()->isFullyEvaluated())
|
|
worklist.push({value, args});
|
|
}
|
|
|
|
auto &object = result.value();
|
|
// Finalize the value. This will eliminate intermidiate ReferenceValue used as
|
|
// a placeholder in the initialization.
|
|
if (failed(object->finalize()))
|
|
return cls.emitError() << "failed to finalize evaluation. Probably the "
|
|
"class contains a dataflow cycle";
|
|
return object;
|
|
}
|
|
|
|
FailureOr<evaluator::EvaluatorValuePtr>
|
|
circt::om::Evaluator::evaluateValue(Value value, ActualParameters actualParams,
|
|
Location loc) {
|
|
auto evaluatorValue = getOrCreateValue(value, actualParams, loc).value();
|
|
|
|
// Return if the value is already evaluated.
|
|
if (evaluatorValue->isFullyEvaluated())
|
|
return evaluatorValue;
|
|
|
|
return llvm::TypeSwitch<Value, FailureOr<evaluator::EvaluatorValuePtr>>(value)
|
|
.Case([&](BlockArgument arg) {
|
|
return evaluateParameter(arg, actualParams, loc);
|
|
})
|
|
.Case([&](OpResult result) {
|
|
return TypeSwitch<Operation *, FailureOr<evaluator::EvaluatorValuePtr>>(
|
|
result.getDefiningOp())
|
|
.Case([&](ConstantOp op) {
|
|
return evaluateConstant(op, actualParams, loc);
|
|
})
|
|
.Case([&](IntegerBinaryArithmeticOp op) {
|
|
return evaluateIntegerBinaryArithmetic(op, actualParams, loc);
|
|
})
|
|
.Case([&](ObjectOp op) {
|
|
return evaluateObjectInstance(op, actualParams);
|
|
})
|
|
.Case([&](ObjectFieldOp op) {
|
|
return evaluateObjectField(op, actualParams, loc);
|
|
})
|
|
.Case([&](ListCreateOp op) {
|
|
return evaluateListCreate(op, actualParams, loc);
|
|
})
|
|
.Case([&](ListConcatOp op) {
|
|
return evaluateListConcat(op, actualParams, loc);
|
|
})
|
|
.Case([&](AnyCastOp op) {
|
|
return evaluateValue(op.getInput(), actualParams, loc);
|
|
})
|
|
.Case([&](FrozenBasePathCreateOp op) {
|
|
return evaluateBasePathCreate(op, actualParams, loc);
|
|
})
|
|
.Case([&](FrozenPathCreateOp op) {
|
|
return evaluatePathCreate(op, actualParams, loc);
|
|
})
|
|
.Case([&](FrozenEmptyPathOp op) {
|
|
return evaluateEmptyPath(op, actualParams, loc);
|
|
})
|
|
.Default([&](Operation *op) {
|
|
auto error = op->emitError("unable to evaluate value");
|
|
error.attachNote() << "value: " << value;
|
|
return error;
|
|
});
|
|
});
|
|
}
|
|
|
|
/// Evaluator dispatch function for parameters.
|
|
FailureOr<evaluator::EvaluatorValuePtr> circt::om::Evaluator::evaluateParameter(
|
|
BlockArgument formalParam, ActualParameters actualParams, Location loc) {
|
|
auto val = (*actualParams)[formalParam.getArgNumber()];
|
|
val->setLoc(loc);
|
|
return success(val);
|
|
}
|
|
|
|
/// Evaluator dispatch function for constants.
|
|
FailureOr<circt::om::evaluator::EvaluatorValuePtr>
|
|
circt::om::Evaluator::evaluateConstant(ConstantOp op,
|
|
ActualParameters actualParams,
|
|
Location loc) {
|
|
// For list constants, create ListValue.
|
|
return success(om::evaluator::AttributeValue::get(op.getValue(), loc));
|
|
}
|
|
|
|
// Evaluator dispatch function for integer binary arithmetic.
|
|
FailureOr<EvaluatorValuePtr>
|
|
circt::om::Evaluator::evaluateIntegerBinaryArithmetic(
|
|
IntegerBinaryArithmeticOp op, ActualParameters actualParams, Location loc) {
|
|
// Get the op's EvaluatorValue handle, in case it hasn't been evaluated yet.
|
|
auto handle = getOrCreateValue(op.getResult(), actualParams, loc);
|
|
|
|
// If it's fully evaluated, we can return it.
|
|
if (handle.value()->isFullyEvaluated())
|
|
return handle;
|
|
|
|
// Evaluate operands if necessary, and return the partially evaluated value if
|
|
// they aren't ready.
|
|
auto lhsResult = evaluateValue(op.getLhs(), actualParams, loc);
|
|
if (failed(lhsResult))
|
|
return lhsResult;
|
|
if (!lhsResult.value()->isFullyEvaluated())
|
|
return handle;
|
|
|
|
auto rhsResult = evaluateValue(op.getRhs(), actualParams, loc);
|
|
if (failed(rhsResult))
|
|
return rhsResult;
|
|
if (!rhsResult.value()->isFullyEvaluated())
|
|
return handle;
|
|
|
|
// Extract the integer attributes.
|
|
auto extractAttr = [](evaluator::EvaluatorValue *value) {
|
|
return std::move(
|
|
llvm::TypeSwitch<evaluator::EvaluatorValue *, om::IntegerAttr>(value)
|
|
.Case([](evaluator::AttributeValue *val) {
|
|
return val->getAs<om::IntegerAttr>();
|
|
})
|
|
.Case([](evaluator::ReferenceValue *val) {
|
|
return cast<evaluator::AttributeValue>(
|
|
val->getStrippedValue()->get())
|
|
->getAs<om::IntegerAttr>();
|
|
}));
|
|
};
|
|
|
|
om::IntegerAttr lhs = extractAttr(lhsResult.value().get());
|
|
om::IntegerAttr rhs = extractAttr(rhsResult.value().get());
|
|
assert(lhs && rhs &&
|
|
"expected om::IntegerAttr for IntegerBinaryArithmeticOp operands");
|
|
|
|
// Extend values if necessary to match bitwidth. Most interesting arithmetic
|
|
// on APSInt asserts that both operands are the same bitwidth, but the
|
|
// IntegerAttrs we are working with may have used the smallest necessary
|
|
// bitwidth to represent the number they hold, and won't necessarily match.
|
|
APSInt lhsVal = lhs.getValue().getAPSInt();
|
|
APSInt rhsVal = rhs.getValue().getAPSInt();
|
|
if (lhsVal.getBitWidth() > rhsVal.getBitWidth())
|
|
rhsVal = rhsVal.extend(lhsVal.getBitWidth());
|
|
else if (rhsVal.getBitWidth() > lhsVal.getBitWidth())
|
|
lhsVal = lhsVal.extend(rhsVal.getBitWidth());
|
|
|
|
// Perform arbitrary precision signed integer binary arithmetic.
|
|
FailureOr<APSInt> result = op.evaluateIntegerOperation(lhsVal, rhsVal);
|
|
|
|
if (failed(result))
|
|
return op->emitError("failed to evaluate integer operation");
|
|
|
|
// Package the result as a new om::IntegerAttr.
|
|
MLIRContext *ctx = op->getContext();
|
|
auto resultAttr =
|
|
om::IntegerAttr::get(ctx, mlir::IntegerAttr::get(ctx, result.value()));
|
|
|
|
// Finalize the op result value.
|
|
auto *handleValue = cast<evaluator::AttributeValue>(handle.value().get());
|
|
auto resultStatus = handleValue->setAttr(resultAttr);
|
|
if (failed(resultStatus))
|
|
return resultStatus;
|
|
|
|
auto finalizeStatus = handleValue->finalize();
|
|
if (failed(finalizeStatus))
|
|
return finalizeStatus;
|
|
|
|
return handle;
|
|
}
|
|
|
|
/// Evaluator dispatch function for Object instances.
|
|
FailureOr<circt::om::Evaluator::ActualParameters>
|
|
circt::om::Evaluator::createParametersFromOperands(
|
|
ValueRange range, ActualParameters actualParams, Location loc) {
|
|
// Create an unique storage to store parameters.
|
|
auto parameters = std::make_unique<
|
|
SmallVector<std::shared_ptr<evaluator::EvaluatorValue>>>();
|
|
|
|
// Collect operands' evaluator values in the current instantiation context.
|
|
for (auto input : range) {
|
|
auto inputResult = getOrCreateValue(input, actualParams, loc);
|
|
if (failed(inputResult))
|
|
return failure();
|
|
parameters->push_back(inputResult.value());
|
|
}
|
|
|
|
actualParametersBuffers.push_back(std::move(parameters));
|
|
return actualParametersBuffers.back().get();
|
|
}
|
|
|
|
/// Evaluator dispatch function for Object instances.
|
|
FailureOr<evaluator::EvaluatorValuePtr>
|
|
circt::om::Evaluator::evaluateObjectInstance(ObjectOp op,
|
|
ActualParameters actualParams) {
|
|
auto loc = op.getLoc();
|
|
if (isFullyEvaluated({op, actualParams}))
|
|
return getOrCreateValue(op, actualParams, loc);
|
|
|
|
auto params =
|
|
createParametersFromOperands(op.getOperands(), actualParams, loc);
|
|
if (failed(params))
|
|
return failure();
|
|
return evaluateObjectInstance(op.getClassNameAttr(), params.value(), loc,
|
|
{op, actualParams});
|
|
}
|
|
|
|
/// Evaluator dispatch function for Object fields.
|
|
FailureOr<evaluator::EvaluatorValuePtr>
|
|
circt::om::Evaluator::evaluateObjectField(ObjectFieldOp op,
|
|
ActualParameters actualParams,
|
|
Location loc) {
|
|
// Evaluate the Object itself, in case it hasn't been evaluated yet.
|
|
FailureOr<evaluator::EvaluatorValuePtr> currentObjectResult =
|
|
evaluateValue(op.getObject(), actualParams, loc);
|
|
if (failed(currentObjectResult))
|
|
return currentObjectResult;
|
|
|
|
auto *currentObject =
|
|
llvm::cast<evaluator::ObjectValue>(currentObjectResult.value().get());
|
|
|
|
auto objectFieldValue = getOrCreateValue(op, actualParams, loc).value();
|
|
|
|
// Iteratively access nested fields through the path until we reach the final
|
|
// field in the path.
|
|
evaluator::EvaluatorValuePtr finalField;
|
|
for (auto field : op.getFieldPath().getAsRange<FlatSymbolRefAttr>()) {
|
|
// `currentObject` might no be fully evaluated.
|
|
if (!currentObject->getFields().contains(field.getAttr()))
|
|
return objectFieldValue;
|
|
|
|
auto currentField = currentObject->getField(field.getAttr());
|
|
finalField = currentField.value();
|
|
if (auto *nextObject =
|
|
llvm::dyn_cast<evaluator::ObjectValue>(finalField.get()))
|
|
currentObject = nextObject;
|
|
}
|
|
|
|
// Update the reference.
|
|
llvm::cast<evaluator::ReferenceValue>(objectFieldValue.get())
|
|
->setValue(finalField);
|
|
|
|
// Return the field being accessed.
|
|
return objectFieldValue;
|
|
}
|
|
|
|
/// Evaluator dispatch function for List creation.
|
|
FailureOr<evaluator::EvaluatorValuePtr>
|
|
circt::om::Evaluator::evaluateListCreate(ListCreateOp op,
|
|
ActualParameters actualParams,
|
|
Location loc) {
|
|
// Evaluate the Object itself, in case it hasn't been evaluated yet.
|
|
SmallVector<evaluator::EvaluatorValuePtr> values;
|
|
auto list = getOrCreateValue(op, actualParams, loc);
|
|
for (auto operand : op.getOperands()) {
|
|
auto result = evaluateValue(operand, actualParams, loc);
|
|
if (failed(result))
|
|
return result;
|
|
if (!result.value()->isFullyEvaluated())
|
|
return list;
|
|
values.push_back(result.value());
|
|
}
|
|
|
|
// Return the list.
|
|
llvm::cast<evaluator::ListValue>(list.value().get())
|
|
->setElements(std::move(values));
|
|
return list;
|
|
}
|
|
|
|
/// Evaluator dispatch function for List concatenation.
|
|
FailureOr<evaluator::EvaluatorValuePtr>
|
|
circt::om::Evaluator::evaluateListConcat(ListConcatOp op,
|
|
ActualParameters actualParams,
|
|
Location loc) {
|
|
// Evaluate the List concat op itself, in case it hasn't been evaluated yet.
|
|
SmallVector<evaluator::EvaluatorValuePtr> values;
|
|
auto list = getOrCreateValue(op, actualParams, loc);
|
|
|
|
// Extract the ListValue, either directly or through an object reference.
|
|
auto extractList = [](evaluator::EvaluatorValue *value) {
|
|
return std::move(
|
|
llvm::TypeSwitch<evaluator::EvaluatorValue *, evaluator::ListValue *>(
|
|
value)
|
|
.Case([](evaluator::ListValue *val) { return val; })
|
|
.Case([](evaluator::ReferenceValue *val) {
|
|
return cast<evaluator::ListValue>(val->getStrippedValue()->get());
|
|
}));
|
|
};
|
|
|
|
for (auto operand : op.getOperands()) {
|
|
auto result = evaluateValue(operand, actualParams, loc);
|
|
if (failed(result))
|
|
return result;
|
|
if (!result.value()->isFullyEvaluated())
|
|
return list;
|
|
|
|
// Extract this sublist and ensure it's done evaluating.
|
|
evaluator::ListValue *subList = extractList(result.value().get());
|
|
if (!subList->isFullyEvaluated())
|
|
return list;
|
|
|
|
// Append each EvaluatorValue from the sublist.
|
|
for (const auto &subValue : subList->getElements())
|
|
values.push_back(subValue);
|
|
}
|
|
|
|
// Return the concatenated list.
|
|
llvm::cast<evaluator::ListValue>(list.value().get())
|
|
->setElements(std::move(values));
|
|
return list;
|
|
}
|
|
|
|
FailureOr<evaluator::EvaluatorValuePtr>
|
|
circt::om::Evaluator::evaluateBasePathCreate(FrozenBasePathCreateOp op,
|
|
ActualParameters actualParams,
|
|
Location loc) {
|
|
// Evaluate the Object itself, in case it hasn't been evaluated yet.
|
|
auto valueResult = getOrCreateValue(op, actualParams, loc).value();
|
|
auto *path = llvm::cast<evaluator::BasePathValue>(valueResult.get());
|
|
auto result = evaluateValue(op.getBasePath(), actualParams, loc);
|
|
if (failed(result))
|
|
return result;
|
|
auto &value = result.value();
|
|
if (!value->isFullyEvaluated())
|
|
return valueResult;
|
|
path->setBasepath(*llvm::cast<evaluator::BasePathValue>(value.get()));
|
|
return valueResult;
|
|
}
|
|
|
|
FailureOr<evaluator::EvaluatorValuePtr>
|
|
circt::om::Evaluator::evaluatePathCreate(FrozenPathCreateOp op,
|
|
ActualParameters actualParams,
|
|
Location loc) {
|
|
// Evaluate the Object itself, in case it hasn't been evaluated yet.
|
|
auto valueResult = getOrCreateValue(op, actualParams, loc).value();
|
|
auto *path = llvm::cast<evaluator::PathValue>(valueResult.get());
|
|
auto result = evaluateValue(op.getBasePath(), actualParams, loc);
|
|
if (failed(result))
|
|
return result;
|
|
auto &value = result.value();
|
|
if (!value->isFullyEvaluated())
|
|
return valueResult;
|
|
path->setBasepath(*llvm::cast<evaluator::BasePathValue>(value.get()));
|
|
return valueResult;
|
|
}
|
|
|
|
FailureOr<evaluator::EvaluatorValuePtr> circt::om::Evaluator::evaluateEmptyPath(
|
|
FrozenEmptyPathOp op, ActualParameters actualParams, Location loc) {
|
|
auto valueResult = getOrCreateValue(op, actualParams, loc).value();
|
|
return valueResult;
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// ObjectValue
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
/// Get a field of the Object by name.
|
|
FailureOr<EvaluatorValuePtr>
|
|
circt::om::evaluator::ObjectValue::getField(StringAttr name) {
|
|
auto field = fields.find(name);
|
|
if (field == fields.end())
|
|
return cls.emitError("field ") << name << " does not exist";
|
|
return success(fields[name]);
|
|
}
|
|
|
|
/// Get an ArrayAttr with the names of the fields in the Object. Sort the fields
|
|
/// so there is always a stable order.
|
|
ArrayAttr circt::om::Object::getFieldNames() {
|
|
SmallVector<Attribute> fieldNames;
|
|
for (auto &f : fields)
|
|
fieldNames.push_back(f.first);
|
|
|
|
llvm::sort(fieldNames, [](Attribute a, Attribute b) {
|
|
return cast<StringAttr>(a).getValue() < cast<StringAttr>(b).getValue();
|
|
});
|
|
|
|
return ArrayAttr::get(cls.getContext(), fieldNames);
|
|
}
|
|
|
|
LogicalResult circt::om::evaluator::ObjectValue::finalizeImpl() {
|
|
for (auto &&[e, value] : fields)
|
|
if (failed(finalizeEvaluatorValue(value)))
|
|
return failure();
|
|
|
|
return success();
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// ReferenceValue
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
LogicalResult circt::om::evaluator::ReferenceValue::finalizeImpl() {
|
|
auto result = getStrippedValue();
|
|
if (failed(result))
|
|
return result;
|
|
value = std::move(result.value());
|
|
// the stripped value also needs to be finalized
|
|
if (failed(finalizeEvaluatorValue(value)))
|
|
return failure();
|
|
|
|
return success();
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// ListValue
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
LogicalResult circt::om::evaluator::ListValue::finalizeImpl() {
|
|
for (auto &value : elements) {
|
|
if (failed(finalizeEvaluatorValue(value)))
|
|
return failure();
|
|
}
|
|
return success();
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// BasePathValue
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
evaluator::BasePathValue::BasePathValue(MLIRContext *context)
|
|
: EvaluatorValue(context, Kind::BasePath, UnknownLoc::get(context)),
|
|
path(PathAttr::get(context, {})) {
|
|
markFullyEvaluated();
|
|
}
|
|
|
|
evaluator::BasePathValue::BasePathValue(PathAttr path, Location loc)
|
|
: EvaluatorValue(path.getContext(), Kind::BasePath, loc), path(path) {}
|
|
|
|
PathAttr evaluator::BasePathValue::getPath() const {
|
|
assert(isFullyEvaluated());
|
|
return path;
|
|
}
|
|
|
|
void evaluator::BasePathValue::setBasepath(const BasePathValue &basepath) {
|
|
assert(!isFullyEvaluated());
|
|
auto newPath = llvm::to_vector(basepath.path.getPath());
|
|
auto oldPath = path.getPath();
|
|
newPath.append(oldPath.begin(), oldPath.end());
|
|
path = PathAttr::get(path.getContext(), newPath);
|
|
markFullyEvaluated();
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// PathValue
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
evaluator::PathValue::PathValue(TargetKindAttr targetKind, PathAttr path,
|
|
StringAttr module, StringAttr ref,
|
|
StringAttr field, Location loc)
|
|
: EvaluatorValue(loc.getContext(), Kind::Path, loc), targetKind(targetKind),
|
|
path(path), module(module), ref(ref), field(field) {}
|
|
|
|
evaluator::PathValue evaluator::PathValue::getEmptyPath(Location loc) {
|
|
PathValue path(nullptr, nullptr, nullptr, nullptr, nullptr, loc);
|
|
path.markFullyEvaluated();
|
|
return path;
|
|
}
|
|
|
|
StringAttr evaluator::PathValue::getAsString() const {
|
|
// If the module is null, then this is a path to a deleted object.
|
|
if (!targetKind)
|
|
return StringAttr::get(getContext(), "OMDeleted:");
|
|
SmallString<64> result;
|
|
switch (targetKind.getValue()) {
|
|
case TargetKind::DontTouch:
|
|
result += "OMDontTouchedReferenceTarget";
|
|
break;
|
|
case TargetKind::Instance:
|
|
result += "OMInstanceTarget";
|
|
break;
|
|
case TargetKind::MemberInstance:
|
|
result += "OMMemberInstanceTarget";
|
|
break;
|
|
case TargetKind::MemberReference:
|
|
result += "OMMemberReferenceTarget";
|
|
break;
|
|
case TargetKind::Reference:
|
|
result += "OMReferenceTarget";
|
|
break;
|
|
}
|
|
result += ":~";
|
|
if (!path.getPath().empty())
|
|
result += path.getPath().front().module;
|
|
else
|
|
result += module.getValue();
|
|
result += '|';
|
|
for (const auto &elt : path) {
|
|
result += elt.module.getValue();
|
|
result += '/';
|
|
result += elt.instance.getValue();
|
|
result += ':';
|
|
}
|
|
if (!module.getValue().empty())
|
|
result += module.getValue();
|
|
if (!ref.getValue().empty()) {
|
|
result += '>';
|
|
result += ref.getValue();
|
|
}
|
|
if (!field.getValue().empty())
|
|
result += field.getValue();
|
|
return StringAttr::get(field.getContext(), result);
|
|
}
|
|
|
|
void evaluator::PathValue::setBasepath(const BasePathValue &basepath) {
|
|
assert(!isFullyEvaluated());
|
|
auto newPath = llvm::to_vector(basepath.getPath().getPath());
|
|
auto oldPath = path.getPath();
|
|
newPath.append(oldPath.begin(), oldPath.end());
|
|
path = PathAttr::get(path.getContext(), newPath);
|
|
markFullyEvaluated();
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// AttributeValue
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
LogicalResult circt::om::evaluator::AttributeValue::setAttr(Attribute attr) {
|
|
if (cast<TypedAttr>(attr).getType() != this->type)
|
|
return mlir::emitError(getLoc(), "cannot set AttributeValue of type ")
|
|
<< this->type << " to Attribute " << attr;
|
|
if (isFullyEvaluated())
|
|
return mlir::emitError(
|
|
getLoc(),
|
|
"cannot set AttributeValue that has already been fully evaluated");
|
|
this->attr = attr;
|
|
markFullyEvaluated();
|
|
return success();
|
|
}
|
|
|
|
LogicalResult circt::om::evaluator::AttributeValue::finalizeImpl() {
|
|
if (!isFullyEvaluated())
|
|
return mlir::emitError(
|
|
getLoc(), "cannot finalize AttributeValue that is not fully evaluated");
|
|
return success();
|
|
}
|
|
|
|
std::shared_ptr<evaluator::EvaluatorValue>
|
|
circt::om::evaluator::AttributeValue::get(Attribute attr, LocationAttr loc) {
|
|
auto type = cast<TypedAttr>(attr).getType();
|
|
auto *context = type.getContext();
|
|
if (!loc)
|
|
loc = UnknownLoc::get(context);
|
|
|
|
// Special handling for ListType to create proper ListValue objects instead of
|
|
// AttributeValue objects.
|
|
if (auto listType = dyn_cast<circt::om::ListType>(type)) {
|
|
SmallVector<EvaluatorValuePtr> elements;
|
|
auto listAttr = cast<om::ListAttr>(attr);
|
|
auto values = getEvaluatorValuesFromAttributes(
|
|
listAttr.getContext(), listAttr.getElements().getValue());
|
|
elements.append(values.begin(), values.end());
|
|
auto list = std::make_shared<evaluator::ListValue>(listType, elements, loc);
|
|
return list;
|
|
}
|
|
|
|
return std::shared_ptr<AttributeValue>(
|
|
new AttributeValue(PrivateTag{}, attr, loc));
|
|
}
|
|
|
|
std::shared_ptr<evaluator::EvaluatorValue>
|
|
circt::om::evaluator::AttributeValue::get(Type type, LocationAttr loc) {
|
|
auto *context = type.getContext();
|
|
if (!loc)
|
|
loc = UnknownLoc::get(context);
|
|
|
|
// Special handling for ListType to create proper ListValue objects instead of
|
|
// AttributeValue objects.
|
|
if (auto listType = dyn_cast<circt::om::ListType>(type))
|
|
return std::make_shared<evaluator::ListValue>(listType, loc);
|
|
// Create the AttributeValue with the private tag
|
|
return std::shared_ptr<AttributeValue>(
|
|
new AttributeValue(PrivateTag{}, type, loc));
|
|
}
|