// // This file is distributed under the MIT License. See LICENSE.md for details. // #include "mlir/IR/AsmState.h" #include "mlir/IR/DialectImplementation.h" #include "mlir/IR/OpImplementation.h" #include "revng/Clift/Clift.h" #include "revng/Clift/CliftDialect.h" #include "revng/Clift/CliftTypes.h" #include "revng/Clift/ModuleVisitor.h" #include "CliftBytecode.h" // comment to force generated files to be included be after regular includes #include "revng/Clift/CliftDialect.cpp.inc" using namespace mlir::clift; namespace { class CliftOpAsmDialectInterface final : public mlir::OpAsmDialectInterface { public: using mlir::OpAsmDialectInterface::OpAsmDialectInterface; AliasResult getAlias(mlir::Attribute Attr, llvm::raw_ostream &OS) const { if (auto Aliasable = mlir::dyn_cast(Attr)) { if (Aliasable.getAlias(OS)) return AliasResult::FinalAlias; } return AliasResult::NoAlias; } AliasResult getAlias(mlir::Type Type, llvm::raw_ostream &OS) const { if (auto Aliasable = mlir::dyn_cast(Type)) { if (Aliasable.getAlias(OS)) return AliasResult::FinalAlias; } return AliasResult::NoAlias; } }; class CliftBytecodeDialectInterface final : public mlir::BytecodeDialectInterface { public: CliftBytecodeDialectInterface(mlir::Dialect *Dialect) : BytecodeDialectInterface(Dialect) {} mlir::Attribute readAttribute(mlir::DialectBytecodeReader &Reader) const override { return mlir::clift::readAttr(Reader); } mlir::Type readType(mlir::DialectBytecodeReader &Reader) const override { return mlir::clift::readType(Reader); } mlir::LogicalResult writeAttribute(mlir::Attribute Attr, mlir::DialectBytecodeWriter &Writer) const override { return mlir::clift::writeAttr(Attr, Writer); } mlir::LogicalResult writeType(mlir::Type Type, mlir::DialectBytecodeWriter &Writer) const override { return mlir::clift::writeType(Type, Writer); } }; class ModuleVerifier : public ModuleVisitor { enum class LoopOrSwitch : uint8_t { Loop, Switch, }; public: // Visit a field type of a class type attribute. // RootType is the root class type attribute and is used to detect recursion. mlir::LogicalResult visitFieldType(mlir::Type FieldType, ClassType RootType) { FieldType = unwrapTypedefs(FieldType); if (auto T = mlir::dyn_cast(FieldType)) { if (equivalent(T, RootType)) return getCurrentOp()->emitError() << "Clift ModuleOp contains a " "recursive class type."; return visitClassType(T, RootType); } return mlir::success(); } mlir::LogicalResult visitClassType(ClassType Type, ClassType RootType) { for (FieldAttr Field : Type.getFields()) { if (visitFieldType(Field.getType(), RootType).failed()) return mlir::failure(); } return mlir::success(); } mlir::LogicalResult visitDefinedType(DefinedType Type) { auto UnqualifiedType = mlir::cast(removeConst(Type)); auto const [Iterator, Inserted] = Definitions.try_emplace(Type.getHandle(), UnqualifiedType); if (not Inserted and Iterator->second != UnqualifiedType) return getCurrentOp()->emitError() << "Found two distinct type " "definitions with the same " "handle: '" << UnqualifiedType.getHandle() << '\''; if (auto Class = mlir::dyn_cast(Type)) { if (visitClassType(Class, Class).failed()) return mlir::failure(); ClassTypes.insert(Class); } return mlir::success(); } mlir::LogicalResult visitAddressableType(AddressableType Type) { Type = unwrapTypedefs(Type); if (not isCompleteType(Type)) return getCurrentOp()->emitError() << "Clift ModuleOp contains an " "incomplete type"; if (auto T = mlir::dyn_cast(Type)) { if (visitDefinedType(T).failed()) return mlir::failure(); } return mlir::success(); } mlir::LogicalResult visitType(mlir::Type Type) { if (Type.getDialect().getTypeID() != mlir::TypeID::get()) return getCurrentOp()->emitError() << "Clift ModuleOp a contains " "non-Clift type"; if (auto T = mlir::dyn_cast(Type)) { if (visitAddressableType(Type).failed()) return mlir::failure(); } return mlir::success(); } mlir::LogicalResult visitNestedOp(mlir::Operation *Op) { if (mlir::isa(Op)) return Op->emitOpError() << Op->getName() << " must be directly nested within a" " ModuleOp."; return mlir::success(); } mlir::LogicalResult visitModuleLevelOp(mlir::Operation *Op) { if (not mlir::isa(Op)) return Op->emitOpError() << Op->getName() << " cannot be directly nested within a" " ModuleOp."; return mlir::success(); } mlir::LogicalResult finish() { auto EmitError = [&]() -> mlir::InFlightDiagnostic { return getCurrentModule()->emitError(); }; for (ClassType Class : ClassTypes) { if (auto T = mlir::dyn_cast(Class)) { if (T.getDefinition().verifyDefinition(EmitError).failed()) return mlir::failure(); } if (auto T = mlir::dyn_cast(Class)) { if (T.getDefinition().verifyDefinition(EmitError).failed()) return mlir::failure(); } } return mlir::success(); } private: llvm::DenseMap Definitions; llvm::DenseSet ClassTypes; }; static mlir::LogicalResult verifyModuleAttr(mlir::Operation *Op, mlir::Attribute Attr) { if (not mlir::isa(Attr)) return Op->emitOpError() << "expected '" << CliftDialect::getModuleAttrName() << "' " "attribute to be mlir::UnitAttr."; auto Module = mlir::dyn_cast(Op); if (not Module) return Op->emitOpError() << "expected '" << CliftDialect::getModuleAttrName() << "' " "attribute to be attached to '" << mlir::ModuleOp::getOperationName() << "'"; return ModuleVerifier::visit(Module); } } // namespace void CliftDialect::initialize() { registerTypes(); registerOperations(); registerAttributes(); addInterfaces(); } mlir::LogicalResult CliftDialect::verifyOperationAttribute(mlir::Operation *Op, mlir::NamedAttribute Attr) { if (Attr.getName() == getModuleAttrName()) return verifyModuleAttr(Op, Attr.getValue()); return mlir::success(); } void dumpMlirOp(mlir::Operation *Op, const char *Path) { mlir::AsmState AsmState(Op); std::error_code EC; llvm::raw_fd_ostream OS(Path, EC); if (EC) llvm::errs() << EC.message() << "\n"; else Op->print(OS, AsmState); } void dumpMlirModule(mlir::ModuleOp Module, const char *Path) { dumpMlirOp(Module, Path); }