// // This file is distributed under the MIT License. See LICENSE.md for details. // #include "revng/ADT/ScopedExchange.h" #include "revng/CliftTransforms/Passes.h" namespace clift { #define GEN_PASS_DEF_CLIFTIMPLICITCASTELISION #include "revng/CliftTransforms/Passes.h.inc" } // namespace clift using namespace clift; namespace { class ImplicitCastElider { const CDataModel &DataModel; public: explicit ImplicitCastElider(const CDataModel &DataModel) : DataModel(DataModel) {} void elide(mlir::Operation *Op) { if (auto Yield = mlir::dyn_cast(Op)) { if (mlir::isa(Yield->getParentOp())) return elideReturnCasts(Yield); } if (auto Assignment = mlir::dyn_cast(Op)) return elideAssignmentCasts(Assignment); if (auto Call = mlir::dyn_cast(Op)) return elideArgumentCasts(Call); if (mlir::isa(Op)) return elideArithmeticCasts(Op); } private: class OperatorType { mlir::Type T1; mlir::Type T2; public: OperatorType() = default; OperatorType(mlir::Type T1) : T1(T1), T2() {} OperatorType(mlir::Type T1, mlir::Type T2) : T1(T1), T2(T2) {} [[nodiscard]] explicit operator bool() const { return static_cast(T1); } [[nodiscard]] friend bool operator==(const OperatorType &, const OperatorType &) = default; }; mlir::Type promote(mlir::Type T) { auto IntType = getUnderlyingIntegerType(T); if (IntType.getSize() < DataModel.getIntSize()) { T = IntegerType::get(T.getContext(), IntegerKind::Signed, DataModel.getIntSize()); } return T; } mlir::Type getCommonIntegerType(mlir::MLIRContext *Context, llvm::ArrayRef Types) { uint64_t MaxSize = 0; uint64_t MaxIsUnsigned = false; for (mlir::Type T : Types) { auto IntType = getUnderlyingIntegerType(T); auto IntSize = IntType.getSize(); if (IntSize > MaxSize) { MaxSize = IntSize; MaxIsUnsigned = false; } if (IntSize == MaxSize) MaxSize |= IntType.isUnsigned(); } if (MaxSize < DataModel.getIntSize()) { MaxSize = DataModel.getIntSize(); MaxIsUnsigned = false; } return IntegerType::get(Context, MaxIsUnsigned ? IntegerKind::Unsigned : IntegerKind::Signed, MaxSize); } OperatorType getArithmeticOperatorType(mlir::Operation *Op, llvm::ArrayRef Types) { if (mlir::isa(Op)) return promote(Types.front()); if (mlir::isa(Op)) return { promote(Types.front()), promote(Types.back()) }; if (mlir::isa(Op)) return getCommonIntegerType(Op->getContext(), Types); revng_abort(); } void markAsImplicit(CastOpInterface Cast) { Cast->setAttr("clift.implicit", mlir::UnitAttr::get(Cast->getContext())); } bool isImplicitConversion(CastOpInterface Cast) { mlir::Type DstT = unwrapTypedefs(Cast.getResult().getType()); mlir::Type SrcT = unwrapTypedefs(Cast.getValue().getType()); if (mlir::isa(SrcT) and mlir::isa(DstT)) return true; if (mlir::isa(Cast)) { auto DstPtrT = mlir::dyn_cast(DstT); auto SrcPtrT = mlir::dyn_cast(SrcT); if (DstPtrT and SrcPtrT) { auto DstPointeeT = collapseTypedefs(DstPtrT.getPointeeType()); auto SrcPointeeT = collapseTypedefs(SrcPtrT.getPointeeType()); if (isConst(SrcPointeeT) and not isConst(DstPointeeT)) return false; if (equivalent(SrcPointeeT, DstPointeeT)) return true; if (mlir::isa(DstPointeeT)) return true; return false; } } return false; } void elideCoercingContextCasts(mlir::OpOperand &Operand) { if (auto Cast = Operand.get().getDefiningOp()) { if (isImplicitConversion(Cast)) markAsImplicit(Cast); } } void elideReturnCasts(YieldOp Op) { elideCoercingContextCasts(Op->getOpOperand(0)); } void elideAssignmentCasts(AssignOp Op) { elideCoercingContextCasts(Op->getOpOperand(1)); } void elideArgumentCasts(CallOp Op) { auto FuncType = Op.getFunctionType(); for (auto [I, T] : llvm::enumerate(FuncType.getArgumentTypes())) elideCoercingContextCasts(Op->getOpOperand(I + 1)); } void elideArithmeticCasts(mlir::Operation *Op) { llvm::SmallVector Types(Op->getOperandTypes()); auto OperatorType = getArithmeticOperatorType(Op, Types); revng_assert(OperatorType); for (auto [I, T] : llvm::enumerate(Types)) { auto Cast = Op->getOperand(I).getDefiningOp(); if (not Cast) continue; mlir::Type CastOperandType = Cast.getValue().getType(); if (not unwrapped_isa(CastOperandType)) continue; ScopedExchange Transaction(T, CastOperandType); if (getArithmeticOperatorType(Op, Types) != OperatorType) continue; Transaction.commit(); markAsImplicit(Cast); } } }; template using PassBase = clift::impl::CliftImplicitCastElisionBase; struct ImplicitCastElisionPass : PassBase { void runOnOperation() override { FunctionOp Function = getOperation(); ImplicitCastElider Elider(getDataModel(Function)); Function->walk([&](mlir::Operation *Op) { Elider.elide(Op); }); } }; } // namespace PassPtr clift::createImplicitCastElisionPass() { return std::make_unique(); }