// // This file is distributed under the MIT License. See LICENSE.md for details. // #include "llvm/ADT/STLExtras.h" #include "llvm/IR/IRBuilder.h" #include "llvm/IR/Instruction.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/Use.h" #include "llvm/Pass.h" #include "revng/ABI/FunctionType/Layout.h" #include "revng/EarlyFunctionAnalysis/FunctionMetadataCache.h" #include "revng/Model/Binary.h" #include "revng/Model/IRHelpers.h" #include "revng/Model/LoadModelPass.h" #include "revng/Model/QualifiedType.h" #include "revng/Support/Assert.h" #include "revng/Support/FunctionTags.h" #include "revng/Support/YAMLTraits.h" #include "revng-c/InitModelTypes/InitModelTypes.h" #include "revng-c/Support/FunctionTags.h" #include "revng-c/Support/IRHelpers.h" #include "revng-c/Support/ModelHelpers.h" #include "revng-c/TypeNames/LLVMTypeNames.h" using namespace llvm; using ModelTypesMap = std::map; struct SerializedType { Constant *StringType; uint64_t OperandId; SerializedType(Constant *StringType, uint64_t OperandId = 0) : StringType(StringType), OperandId(OperandId) {} }; struct MakeModelCastPass : public llvm::FunctionPass { private: ModelTypesMap TypeMap; const model::Function *ModelFunction = nullptr; public: static char ID; MakeModelCastPass() : FunctionPass(ID) {} bool runOnFunction(llvm::Function &F) override; void getAnalysisUsage(llvm::AnalysisUsage &AU) const override { AU.setPreservesCFG(); AU.addRequired(); AU.addRequired(); } private: std::vector serializeTypesForModelCast(FunctionMetadataCache &Cache, Instruction *, const model::Binary &); void createAndInjectModelCast(Instruction *, const SerializedType &, OpaqueFunctionsPool &); }; using MMCP = MakeModelCastPass; std::vector MMCP::serializeTypesForModelCast(FunctionMetadataCache &Cache, Instruction *I, const model::Binary &Model) { using namespace model; using namespace abi::FunctionType; std::vector Result; Module *M = I->getModule(); auto SerializeTypeFor = [this, &Model, &Result, &M, &Cache](const llvm::Use &Op) { // Check if we have strong model information about this operand auto ModelTypes = getExpectedModelType(Cache, &Op, Model); // Aggregates that do not correspond to model structs (e.g. return types // of RawFunctionTypes that return more than one value) cannot be handled // with casts, since we don't have a model::Type to cast them to. if (ModelTypes.size() == 1) { QualifiedType ExpectedType = ModelTypes.back(); revng_assert(ExpectedType.UnqualifiedType().isValid()); const QualifiedType &OperandType = TypeMap.at(Op.get()); if (ExpectedType.skipTypedefs() != OperandType.skipTypedefs()) { revng_assert(ExpectedType.isScalar() and OperandType.isScalar()); // Create a cast only if the expected type is different from the // actual type propagated until here auto Type = SerializedType(serializeToLLVMString(ExpectedType, *M), Op.getOperandNo()); Result.emplace_back(std::move(Type)); } } }; if (auto *Call = dyn_cast(I)) { // Lifted functions have their prototype on the model auto *Callee = Call->getCalledFunction(); if (isCallToIsolatedFunction(Call)) { // For indirect calls, cast the callee to the right function type if (not Callee) SerializeTypeFor(Call->getCalledOperandUse()); // For all calls, check the formal arguments types for (llvm::Use &Op : Call->args()) SerializeTypeFor(Op); } else if (FunctionTags::ModelGEP.isTagOf(Callee) or FunctionTags::ModelGEPRef.isTagOf(Callee) or FunctionTags::AddressOf.isTagOf(Callee)) { // Check the type of the base operand SerializeTypeFor(Call->getArgOperandUse(1)); } else if (FunctionTags::BinaryNot.isTagOf(Callee)) { SerializeTypeFor(Call->getArgOperandUse(0)); } else if (FunctionTags::StructInitializer.isTagOf(Callee)) { // StructInitializers are used to pack together a returned struct, so // we know the types of each element by looking at the Prototype for (llvm::Use &Op : Call->args()) SerializeTypeFor(Op); } } else if (auto *Ret = dyn_cast(I)) { // Check the formal return type if (Ret->getNumOperands() > 0) SerializeTypeFor(Ret->getOperandUse(0)); } else if (auto *SI = dyn_cast(I)) { auto &PtrOperandPtrType = TypeMap.at(SI->getPointerOperand()); auto &ValOperandType = TypeMap.at(SI->getValueOperand()); const model::Architecture::Values &Arch = Model.Architecture(); QualifiedType ValOperandPtrType = ValOperandType.getPointerTo(Arch); if (PtrOperandPtrType != ValOperandPtrType) { bool IsPtrOperandOfAggregateType = false; QualifiedType PtrOperandType; if (PtrOperandPtrType.isPointer()) { PtrOperandType = dropPointer(PtrOperandPtrType); const auto *Unqualified = PtrOperandType.UnqualifiedType().getConst(); IsPtrOperandOfAggregateType = isa(Unqualified) || isa(Unqualified); } if (isa(SI->getPointerOperand()) || IsPtrOperandOfAggregateType) { // Pointer operand needs to be casted to the model::QualifiedType of // the value operand, added pointer qualified. auto Type = SerializedType(serializeToLLVMString(ValOperandPtrType, *M), SI->getPointerOperandIndex()); Result.emplace_back(std::move(Type)); } else { // Value operand needs to be casted to the drop'd ptr of the // model::QualifiedType of the pointer type. revng_assert(PtrOperandPtrType.isPointer()); auto *LLVMString = serializeToLLVMString(PtrOperandType, *M); auto Type = SerializedType(LLVMString); Result.emplace_back(std::move(Type)); } } } else if (isa(I) or isa(I) or isa(I)) { for (unsigned Index = 0; Index < I->getNumOperands(); ++Index) SerializeTypeFor(I->getOperandUse(Index)); } else if (auto *Switch = dyn_cast(I)) { SerializeTypeFor(Switch->getOperandUse(0)); } return Result; } static FunctionType *getModelCastType(TypePair Key) { LLVMContext &LLVMCtxt = Key.RetType->getContext(); Type *StringPtrType = getStringPtrType(LLVMCtxt); IntegerType *Int1 = llvm::IntegerType::getInt1Ty(LLVMCtxt); return FunctionType::get(Key.RetType, { StringPtrType, Key.ArgType, Int1 }, /* VarArg */ false); } static Function * getModelCastFunction(TypePair Key, OpaqueFunctionsPool &ModelCastPool) { FunctionType *ModelCastType = getModelCastType(Key); return ModelCastPool.get(Key, ModelCastType, "ModelCast"); } // Create a call to explicit ModelCast. Later on, we run `ImplicitModelCastPass` // to detect implicit casts. static Value *createCallToModelCast(IRBuilder<> &Builder, TypePair Key, Constant *SerializedTypeAsString, Value *Operand, OpaqueFunctionsPool &Pool) { auto *ModelCastFunction = getModelCastFunction(Key, Pool); LLVMContext &Context = Builder.getContext(); ConstantInt *IsImplicit = llvm::ConstantInt::getFalse(Context); Value *Call = Builder.CreateCall(ModelCastFunction, { SerializedTypeAsString, Operand, IsImplicit }); return Call; } void MMCP::createAndInjectModelCast(Instruction *Ins, const SerializedType &ST, OpaqueFunctionsPool &Pool) { IRBuilder<> Builder(Ins); uint64_t OperandId = ST.OperandId; Value *Operand = Ins->getOperand(OperandId); Type *BaseAddressTy = Ins->getOperand(OperandId)->getType(); Value *CallToModelCast = createCallToModelCast(Builder, { BaseAddressTy, BaseAddressTy }, ST.StringType, Operand, Pool); Ins->setOperand(OperandId, CallToModelCast); } bool MMCP::runOnFunction(Function &F) { bool Changed = false; Module *M = F.getParent(); OpaqueFunctionsPool ModelCastPool(M, false); initModelCastPool(ModelCastPool, M); auto &ModelWrapper = getAnalysis().get(); const TupleTree &Model = ModelWrapper.getReadOnlyModel(); ModelFunction = llvmToModelFunction(*Model, F); revng_assert(ModelFunction != nullptr); auto &Cache = getAnalysis().get(); // First of all, remove all SExt, ZExt and Trunc, and replace them with // ModelCasts. { LLVMContext &LLVMCtxt = F.getContext(); IRBuilder<> Builder(LLVMCtxt); for (BasicBlock &BB : F) { for (Instruction &I : llvm::make_early_inc_range(BB)) { auto *SExt = dyn_cast(&I); auto *ZExt = dyn_cast(&I); auto *Trunc = dyn_cast(&I); if (not SExt and not ZExt and not Trunc) continue; llvm::Value *CastedOperand = I.getOperand(0); // Build a FunctionType for the ModelCast function, and add it to the // pool auto *ResultTypeOnLLVM = cast(I.getType()); TypePair Key = TypePair{ .RetType = ResultTypeOnLLVM, .ArgType = CastedOperand->getType() }; // Create the ModelCast call. Builder.SetInsertPoint(&I); // Compute the target type of the cast, depending on the cast we're // replacing. unsigned ResultBitWidth = ResultTypeOnLLVM->getBitWidth(); revng_assert(std::has_single_bit(ResultBitWidth)); revng_assert(ResultBitWidth == 1 or ResultBitWidth >= 8); unsigned ByteSize = (ResultBitWidth == 1) ? 1 : ResultBitWidth / 8; using model::PrimitiveTypeKind::Values::Number; using model::PrimitiveTypeKind::Values::Signed; using model::PrimitiveTypeKind::Values::Unsigned; model::PrimitiveTypeKind::Values Kind = SExt ? Signed : (ZExt ? Unsigned : Number); auto ResultModelType = model::QualifiedType{ Model->getPrimitiveType(Kind, ByteSize), {} }; // Create a string constant to pass as first argument of the call to // ModelCast, to represent the target model type. Constant *TargetModelTypeString = serializeToLLVMString(ResultModelType, *M); revng_assert(TargetModelTypeString); Value *CallToModelCast = createCallToModelCast(Builder, Key, TargetModelTypeString, CastedOperand, ModelCastPool); I.replaceAllUsesWith(CallToModelCast); I.eraseFromParent(); } } } TypeMap = initModelTypes(Cache, F, ModelFunction, *Model, false); for (BasicBlock &BB : F) { for (Instruction &I : BB) { auto SerializedTypes = serializeTypesForModelCast(Cache, &I, *Model); if (SerializedTypes.empty()) continue; Changed = true; for (unsigned Idx = 0; Idx < SerializedTypes.size(); ++Idx) createAndInjectModelCast(&I, SerializedTypes[Idx], ModelCastPool); } } return Changed; } char MakeModelCastPass::ID = 0; using Pass = MakeModelCastPass; static RegisterPass X("make-model-cast", "A pass that pulls out casts from " "some instructions that embed casts " "into their own dedicated calls.", false, false);