Files
revng-revng/lib/mlir/Dialect/Clift/IR/CliftAttributes.cpp
2025-03-24 09:26:43 +02:00

644 lines
21 KiB
C++

//
// This file is distributed under the MIT License. See LICENSE.md for details.
//
#include <set>
#include "llvm/ADT/ScopeExit.h"
#include "llvm/ADT/SmallSet.h"
#include "llvm/ADT/TypeSwitch.h"
#include "mlir/IR/Builders.h"
#include "mlir/IR/DialectImplementation.h"
#include "mlir/IR/OpDefinition.h"
#include "mlir/IR/OpImplementation.h"
#include "revng/mlir/Dialect/Clift/IR/Clift.h"
#include "revng/mlir/Dialect/Clift/IR/CliftAttributes.h"
#include "revng/mlir/Dialect/Clift/IR/CliftInterfaces.h"
#include "revng/mlir/Dialect/Clift/IR/CliftTypes.h"
#include "CliftParser.h"
#include "CliftStorage.h"
namespace mlir {
static ParseResult parseCliftDebugName(AsmParser &Parser, std::string &Name) {
if (Parser.parseOptionalKeyword("as").succeeded()) {
if (Parser.parseString(&Name).failed())
return mlir::failure();
}
return mlir::success();
}
static void printCliftDebugName(AsmPrinter &Printer, llvm::StringRef Name) {
if (not Name.empty()) {
Printer << "as \"";
llvm::printEscapedString(Name, Printer.getStream());
Printer << "\"";
}
}
static ParseResult
parseCliftEnumerators(AsmParser &Parser,
llvm::SmallVector<clift::EnumFieldAttr> &Enumerators) {
auto ParseEnumerator = [&]() -> ParseResult {
uint64_t Value;
if (Parser.parseInteger(Value).failed())
return mlir::failure();
std::string Name;
if (Parser.parseOptionalKeyword("as").succeeded()) {
if (Parser.parseString(&Name).failed())
return mlir::failure();
}
Enumerators.push_back(clift::EnumFieldAttr::get(Parser.getContext(),
Value,
std::move(Name)));
return mlir::success();
};
return Parser.parseCommaSeparatedList(mlir::AsmParser::Delimiter::Braces,
ParseEnumerator,
" in enumerator list");
}
static void
printCliftEnumerators(AsmPrinter &Printer,
llvm::ArrayRef<clift::EnumFieldAttr> Enumerators) {
Printer << '{';
for (auto [I, E] : llvm::enumerate(Enumerators)) {
if (I != 0)
Printer << ", ";
Printer << E.getRawValue();
if (llvm::StringRef Name = E.getName(); not Name.empty()) {
Printer << " as \"";
llvm::printEscapedString(Name, Printer.getStream());
Printer << "\"";
}
}
Printer << '}';
}
} // namespace mlir
// This include should stay here for correct build procedure
//
#define GET_ATTRDEF_CLASSES
#include "revng/mlir/Dialect/Clift/IR/CliftAttributes.cpp.inc"
using namespace mlir::clift;
using EmitErrorType = llvm::function_ref<mlir::InFlightDiagnostic()>;
//===---------------------------- CliftDialect ----------------------------===//
void CliftDialect::registerAttributes() {
addAttributes<StructTypeAttr, UnionTypeAttr,
// Include the list of auto-generated attributes
#define GET_ATTRDEF_LIST
#include "revng/mlir/Dialect/Clift/IR/CliftAttributes.cpp.inc"
/* End of auto-generated list */>();
}
/// Parse an attribute registered to this dialect
mlir::Attribute CliftDialect::parseAttribute(mlir::DialectAsmParser &Parser,
mlir::Type Type) const {
llvm::SMLoc TypeLoc = Parser.getCurrentLocation();
llvm::StringRef Mnemonic;
if (mlir::Attribute GenAttr;
generatedAttributeParser(Parser, &Mnemonic, Type, GenAttr).has_value())
return GenAttr;
if (Mnemonic == StructTypeAttr::getMnemonic())
return StructTypeAttr::parse(Parser);
if (Mnemonic == UnionTypeAttr::getMnemonic())
return UnionTypeAttr::parse(Parser);
Parser.emitError(TypeLoc) << "unknown attr `" << Mnemonic << "` in dialect `"
<< getNamespace() << "`";
return {};
}
/// Print an attribute registered to this dialect
void CliftDialect::printAttribute(mlir::Attribute Attr,
mlir::DialectAsmPrinter &Printer) const {
if (mlir::succeeded(generatedAttributePrinter(Attr, Printer)))
return;
if (auto T = mlir::dyn_cast<StructTypeAttr>(Attr)) {
T.print(Printer);
return;
}
if (auto T = mlir::dyn_cast<UnionTypeAttr>(Attr)) {
T.print(Printer);
return;
}
revng_abort("cannot print attribute");
}
//===------------------------------ FieldAttr -----------------------------===//
mlir::LogicalResult FieldAttr::verify(EmitErrorType EmitError,
uint64_t Offset,
clift::ValueType ElementType,
llvm::StringRef Name) {
if (not isObjectType(ElementType)) {
return EmitError() << "Struct and union field types must be object types. "
<< "Field at offset " << Offset << " is not.";
}
return mlir::success();
}
//===---------------------------- EnumFieldAttr ---------------------------===//
mlir::LogicalResult EnumFieldAttr::verify(EmitErrorType EmitError,
uint64_t RawValue,
llvm::StringRef Name) {
return mlir::success();
}
//===---------------------------- EnumTypeAttr ----------------------------===//
static bool getTypeDefinitionAliasImpl(TypeDefinitionAttr Type,
llvm::StringRef Kind,
llvm::raw_ostream &OS) {
auto IsIdentifierChar = [](char const X) {
if (X == '_')
return true;
if ('a' <= X and X <= 'z')
return true;
if ('A' <= X and X <= 'Z')
return true;
if ('0' <= X and X <= '9')
return true;
return false;
};
if (not Type.name().empty()) {
OS << Type.name();
} else if (not Type.getHandle().empty()) {
for (char C : Type.getHandle())
OS << (IsIdentifierChar(C) ? C : '_');
} else {
OS << Kind;
}
return true;
}
static bool getAliasImpl(TypeDefinitionAttr Type, llvm::raw_ostream &OS) {
if (not Type.getTypeDefinitionAlias(OS))
return false;
OS << "$def";
return true;
}
mlir::LogicalResult EnumTypeAttr::verify(EmitErrorType EmitError,
llvm::StringRef Handle,
llvm::StringRef Name,
clift::ValueType UnderlyingType,
llvm::ArrayRef<EnumFieldAttr> Fields) {
auto [DealiasedType, HasConst] = decomposeTypedef(UnderlyingType);
auto PrimitiveType = mlir::dyn_cast<clift::PrimitiveType>(DealiasedType);
if (not PrimitiveType or HasConst or PrimitiveType.isConst())
return EmitError() << "Underlying type of enum must be a non-const "
"primitive type";
const uint64_t BitWidth = PrimitiveType.getSize() * 8;
if (Fields.empty())
return EmitError() << "enum requires at least one field";
uint64_t MinValue = 0;
uint64_t MaxValue = 0;
bool IsSigned = false;
switch (PrimitiveType.getKind()) {
case PrimitiveKind::UnsignedKind:
MaxValue = llvm::APInt::getMaxValue(BitWidth).getZExtValue();
break;
case PrimitiveKind::SignedKind:
MinValue = llvm::APInt::getSignedMinValue(BitWidth).getSExtValue();
MaxValue = llvm::APInt::getSignedMaxValue(BitWidth).getSExtValue();
IsSigned = true;
break;
default:
return EmitError() << "enum underlying type must be an integral type";
}
uint64_t LastValue = 0;
bool CheckEqual = false;
for (const auto &Field : Fields) {
const uint64_t Value = Field.getRawValue();
const auto UsingSigned = [&](auto Callable, const auto... V) {
return IsSigned ? Callable(static_cast<int64_t>(V)...) : Callable(V...);
};
const auto CheckSigned =
[EmitError](const auto Value,
const auto MinValue,
const auto MaxValue) -> mlir::LogicalResult {
if (Value < MinValue)
return EmitError() << "enum field " << Value
<< " is less than the min value of the "
"underlying type "
<< MinValue;
if (Value > MaxValue)
return EmitError() << "enum field " << Value
<< " is greater than the max value of the "
"underlying type "
<< MaxValue;
return mlir::success();
};
const mlir::LogicalResult R = UsingSigned(CheckSigned,
Value,
MinValue,
MaxValue);
if (failed(R))
return R;
if (Value < LastValue || (CheckEqual && Value == LastValue))
return EmitError() << "enum fields must be strictly ordered by their "
"unsigned values";
LastValue = Value;
CheckEqual = true;
}
return mlir::success();
}
uint64_t EnumTypeAttr::getByteSize() const {
return mlir::cast<PrimitiveType>(getUnderlyingType()).getSize();
}
bool EnumTypeAttr::getTypeDefinitionAlias(llvm::raw_ostream &OS) const {
return getTypeDefinitionAliasImpl(*this, "enum", OS);
}
bool EnumTypeAttr::getAlias(llvm::raw_ostream &OS) const {
return getAliasImpl(*this, OS);
}
//===--------------------------- TypedefTypeAttr --------------------------===//
mlir::LogicalResult TypedefTypeAttr::verify(EmitErrorType EmitError,
llvm::StringRef Handle,
llvm::StringRef Name,
clift::ValueType UnderlyingType) {
return mlir::success();
}
uint64_t TypedefTypeAttr::getByteSize() const {
return getUnderlyingType().getByteSize();
}
bool TypedefTypeAttr::getTypeDefinitionAlias(llvm::raw_ostream &OS) const {
return getTypeDefinitionAliasImpl(*this, "typedef", OS);
}
bool TypedefTypeAttr::getAlias(llvm::raw_ostream &OS) const {
return getAliasImpl(*this, OS);
}
//===-------------------------- FunctionTypeAttr --------------------------===//
mlir::LogicalResult FunctionTypeAttr::verify(EmitErrorType EmitError,
llvm::StringRef Handle,
llvm::StringRef Name,
mlir::Type ReturnType,
llvm::ArrayRef<mlir::Type> Args) {
auto R = mlir::dyn_cast<clift::ValueType>(ReturnType);
if (not R)
return EmitError() << "Function return type must be a ValueType";
for (mlir::Type ArgumentType : Args) {
auto T = mlir::dyn_cast<clift::ValueType>(ArgumentType);
if (not T)
return EmitError() << "Function argument types must be ValueTypes";
if (not isObjectType(T))
return EmitError() << "Function parameter type must be an object type";
if (isArrayType(T))
return EmitError() << "Function parameter type may not be an array type";
}
if (not isReturnableType(R))
return EmitError() << "Function return type must be void or a non-array "
"object type.";
return mlir::success();
}
mlir::LogicalResult
FunctionTypeAttr::verify(EmitErrorType EmitError,
llvm::StringRef Handle,
llvm::StringRef Name,
clift::ValueType ReturnType,
llvm::ArrayRef<clift::ValueType> Args) {
if (not isReturnableType(ReturnType))
return EmitError() << "Function return type must be void or a non-array "
"object type.";
return mlir::success();
}
uint64_t FunctionTypeAttr::getByteSize() const {
return 0;
}
bool FunctionTypeAttr::getTypeDefinitionAlias(llvm::raw_ostream &OS) const {
return getTypeDefinitionAliasImpl(*this, "function", OS);
}
bool FunctionTypeAttr::getAlias(llvm::raw_ostream &OS) const {
return getAliasImpl(*this, OS);
}
llvm::ArrayRef<mlir::Type> FunctionTypeAttr::getResultTypes() {
return ArrayRef<Type>(getImpl()->return_type);
}
//===--------------------------- StructTypeAttr ---------------------------===//
mlir::LogicalResult StructTypeAttr::verify(EmitErrorType EmitError,
llvm::StringRef Handle) {
return mlir::success();
}
mlir::LogicalResult
StructTypeAttr::verify(const EmitErrorType EmitError,
const llvm::StringRef Handle,
const llvm::StringRef Name,
const uint64_t Size,
const llvm::ArrayRef<FieldAttr> Fields) {
if (Size == 0)
return EmitError() << "struct type cannot have a size of zero";
if (not Fields.empty()) {
uint64_t LastEndOffset = 0;
llvm::SmallSet<llvm::StringRef, 16> NameSet;
for (const auto &Field : Fields) {
if (Field.getOffset() < LastEndOffset)
return EmitError() << "Fields of structs must be ordered by offset, "
"and "
"they cannot overlap";
LastEndOffset = Field.getOffset() + Field.getType().getByteSize();
if (not Field.getName().empty()) {
if (not NameSet.insert(Field.getName()).second)
return EmitError() << "struct field names must be empty or unique";
}
}
if (LastEndOffset > Size)
return EmitError() << "offset + size of field of struct type is greater "
"than the struct type size.";
}
return mlir::success();
}
StructTypeAttr StructTypeAttr::get(MLIRContext *Context,
llvm::StringRef Handle) {
return Base::get(Context, Handle);
}
StructTypeAttr StructTypeAttr::getChecked(EmitErrorType EmitError,
MLIRContext *Context,
llvm::StringRef Handle) {
return Base::get(Context, Handle);
}
StructTypeAttr StructTypeAttr::get(MLIRContext *Context,
llvm::StringRef Handle,
llvm::StringRef Name,
uint64_t Size,
llvm::ArrayRef<FieldAttr> Fields) {
auto Result = Base::get(Context, Handle);
Result.define(Name, Size, Fields);
return Result;
}
StructTypeAttr StructTypeAttr::getChecked(EmitErrorType EmitError,
MLIRContext *Context,
llvm::StringRef Handle,
llvm::StringRef Name,
uint64_t Size,
llvm::ArrayRef<FieldAttr> Fields) {
if (failed(verify(EmitError, Handle, Name, Size, Fields)))
return {};
return get(Context, Handle, Name, Size, Fields);
}
void StructTypeAttr::define(const llvm::StringRef Name,
const uint64_t Size,
const llvm::ArrayRef<FieldAttr> Fields) {
// Call into the base to mutate the type.
LogicalResult Result = Base::mutate(Name, Fields, Size);
// Most types expect the mutation to always succeed, but types can implement
// custom logic for handling mutation failures.
revng_assert(succeeded(Result)
&& "attempting to change the body of an already-initialized "
"type");
}
llvm::StringRef StructTypeAttr::getHandle() const {
return getImpl()->getHandle();
}
llvm::StringRef StructTypeAttr::getName() const {
return getImpl()->getName();
}
llvm::ArrayRef<FieldAttr> StructTypeAttr::getFields() const {
return getImpl()->getSubobjects();
}
bool StructTypeAttr::isDefinition() const {
return getImpl()->isInitialized();
}
uint64_t StructTypeAttr::getByteSize() const {
return getImpl()->getSize();
}
bool StructTypeAttr::getTypeDefinitionAlias(llvm::raw_ostream &OS) const {
return getTypeDefinitionAliasImpl(*this, "struct", OS);
}
bool StructTypeAttr::getAlias(llvm::raw_ostream &OS) const {
return getAliasImpl(*this, OS);
}
mlir::Attribute StructTypeAttr::parse(AsmParser &Parser) {
return parseCompositeType<StructTypeAttr>(Parser, /*MinSubobjects=*/0);
}
void StructTypeAttr::print(AsmPrinter &Printer) const {
printCompositeType(Printer, *this);
}
void StructTypeAttr::walkImmediateSubElements(function_ref<void(Attribute)>
WalkAttr,
function_ref<void(Type)> WalkType)
const {
if (not getImpl()->isInitialized())
return;
for (auto Field : getFields())
WalkAttr(Field);
}
mlir::Attribute
StructTypeAttr::replaceImmediateSubElements(llvm::ArrayRef<mlir::Attribute>,
llvm::ArrayRef<mlir::Type>) const {
revng_abort("it does not make any sense to replace the elements of a "
"defined struct");
return {};
}
//===---------------------------- UnionTypeAttr ---------------------------===//
mlir::LogicalResult UnionTypeAttr::verify(EmitErrorType EmitError,
llvm::StringRef Handle) {
return mlir::success();
}
mlir::LogicalResult UnionTypeAttr::verify(EmitErrorType EmitError,
llvm::StringRef Handle,
llvm::StringRef Name,
llvm::ArrayRef<FieldAttr> Fields) {
if (Fields.empty())
return EmitError() << "union types must have at least one field";
llvm::SmallSet<llvm::StringRef, 16> NameSet;
for (const auto &Field : Fields) {
if (Field.getOffset() != 0)
return EmitError() << "union field offsets must be zero";
if (not Field.getName().empty()) {
if (not NameSet.insert(Field.getName()).second)
return EmitError() << "union field names must be empty or unique";
}
}
return mlir::success();
}
UnionTypeAttr UnionTypeAttr::get(MLIRContext *Context, llvm::StringRef Handle) {
return Base::get(Context, Handle);
}
UnionTypeAttr UnionTypeAttr::getChecked(EmitErrorType EmitError,
MLIRContext *Context,
llvm::StringRef Handle) {
return Base::get(Context, Handle);
}
UnionTypeAttr UnionTypeAttr::get(MLIRContext *Context,
llvm::StringRef Handle,
llvm::StringRef Name,
llvm::ArrayRef<FieldAttr> Fields) {
auto Result = Base::get(Context, Handle);
Result.define(Name, Fields);
return Result;
}
UnionTypeAttr UnionTypeAttr::getChecked(EmitErrorType EmitError,
MLIRContext *Context,
llvm::StringRef Handle,
llvm::StringRef Name,
llvm::ArrayRef<FieldAttr> Fields) {
if (failed(verify(EmitError, Handle, Name, Fields)))
return {};
return get(Context, Handle, Name, Fields);
}
void UnionTypeAttr::define(const llvm::StringRef Name,
const llvm::ArrayRef<FieldAttr> Fields) {
// Call into the base to mutate the type.
LogicalResult Result = Base::mutate(Name, Fields);
// Most types expect the mutation to always succeed, but types can implement
// custom logic for handling mutation failures.
revng_assert(succeeded(Result)
&& "attempting to change the body of an already-initialized "
"type");
}
llvm::StringRef UnionTypeAttr::getHandle() const {
return getImpl()->getHandle();
}
llvm::StringRef UnionTypeAttr::getName() const {
return getImpl()->getName();
}
llvm::ArrayRef<FieldAttr> UnionTypeAttr::getFields() const {
return getImpl()->getSubobjects();
}
bool UnionTypeAttr::isDefinition() const {
return getImpl()->isInitialized();
}
uint64_t UnionTypeAttr::getByteSize() const {
uint64_t Max = 0;
for (auto const &Field : getFields())
Max = std::max(Max, Field.getType().getByteSize());
return Max;
}
bool UnionTypeAttr::getTypeDefinitionAlias(llvm::raw_ostream &OS) const {
return getTypeDefinitionAliasImpl(*this, "union", OS);
}
bool UnionTypeAttr::getAlias(llvm::raw_ostream &OS) const {
return getAliasImpl(*this, OS);
}
mlir::Attribute UnionTypeAttr::parse(AsmParser &Parser) {
return parseCompositeType<UnionTypeAttr>(Parser, /*MinSubobjects=*/1);
}
void UnionTypeAttr::print(AsmPrinter &Printer) const {
printCompositeType(Printer, *this);
}
void UnionTypeAttr::walkImmediateSubElements(function_ref<void(Attribute)>
WalkAttr,
function_ref<void(Type)> WalkType)
const {
if (not getImpl()->isInitialized())
return;
for (auto Field : getFields())
WalkAttr(Field);
}
mlir::Attribute
UnionTypeAttr::replaceImmediateSubElements(llvm::ArrayRef<mlir::Attribute>,
llvm::ArrayRef<mlir::Type>) const {
revng_abort("it does not make any sense to replace the elements of a "
"defined union");
return {};
}