// // This file is distributed under the MIT License. See LICENSE.md for details. // #include #include "llvm/ADT/SetVector.h" #include "llvm/ADT/SmallVector.h" #include "llvm/IR/Constants.h" #include "llvm/IR/InstIterator.h" #include "llvm/Pass.h" #include "llvm/Transforms/Utils/Local.h" #include "revng/Model/FunctionTags.h" #include "revng/Support/IRBuilder.h" using namespace llvm; static constexpr const char *ExtractValueToGEPFlag = "extractvalue-to-gep"; struct ExtractValueToGEPPass : public llvm::FunctionPass { public: static char ID; public: ExtractValueToGEPPass() : FunctionPass(ID) {} public: bool runOnFunction(llvm::Function &F) override; void getAnalysisUsage(llvm::AnalysisUsage &AU) const override { AU.setPreservesCFG(); } }; static auto getStructValuedInstructions(llvm::Function &F) { SmallSetVector StructValues; for (Instruction &I : llvm::instructions(F)) { Instruction *Struct = nullptr; if (auto *EV = dyn_cast(&I)) { if (EV->getNumIndices() == 1) Struct = dyn_cast(EV->getAggregateOperand()); } else if (auto * OpaqueEV = getCallToTagged(&I, FunctionTags::OpaqueExtractValue)) { Struct = cast(OpaqueEV->getArgOperand(0)); } if (Struct) StructValues.insert(Struct); } return StructValues.takeVector(); } bool ExtractValueToGEPPass::runOnFunction(llvm::Function &F) { SmallVector StructValues = getStructValuedInstructions(F); if (StructValues.empty()) return false; LLVMContext &Context = F.getContext(); const DataLayout &DL = F.getParent()->getDataLayout(); IntegerType *Int8Ty = llvm::IntegerType::getInt8Ty(Context); revng::NonDebugInfoCheckingIRBuilder B(Context); SmallVector IsDead; for (Instruction *StructValue : StructValues) { auto *TheStructType = cast(StructValue->getType()); const StructLayout *Layout = DL.getStructLayout(TheStructType); Value *BasePointer = nullptr; if (auto *Load = dyn_cast(StructValue)) { BasePointer = Load->getPointerOperand(); } else { DebugLoc DebugInfo = StructValue->getDebugLoc(); B.SetInsertPointPastAllocas(&F, DebugInfo); BasePointer = B.CreateAlloca(TheStructType); B.SetInsertPoint(StructValue->getInsertionPointAfterDef(), DebugInfo); B.CreateStore(StructValue, BasePointer); } for (Use &U : StructValue->uses()) { auto *User = U.getUser(); const llvm::ConstantInt *Index = nullptr; if (auto *EV = dyn_cast(User)) { if (EV->getNumIndices() != 1) continue; unsigned I = *EV->indices().begin(); Index = ConstantInt::get(IntegerType::getInt64Ty(Context), I); } else if (auto * OpaqueEV = getCallToTagged(User, FunctionTags::OpaqueExtractValue); OpaqueEV) { Index = cast(OpaqueEV->getArgOperand(1)); } if (not Index) continue; // The following check could be an assertion, but let's be permissive. if (Index->isNegative() or Index->uge(std::numeric_limits::max())) continue; auto *ExtractValue = cast(User); B.SetInsertPoint(ExtractValue, ExtractValue->getDebugLoc()); unsigned FieldId = Index->getZExtValue(); uint64_t FieldOffset = Layout->getElementOffset(FieldId); auto *Offset = llvm::ConstantInt::get(Index->getType(), APInt(64, FieldOffset)); auto *GEP = B.CreateGEP(Int8Ty, BasePointer, Offset); auto *Load = B.CreateLoad(ExtractValue->getType(), GEP); ExtractValue->replaceAllUsesWith(Load); IsDead.push_back(ExtractValue); } } // The following call to RecursivelyDeleteTriviallyDeadInstructionsPermissive // cannot remove dead OpaqueExtractValue, so we have to do that ourselves. // In principle we could change the attributes of the OpaqueExtractValue // functions but that would enable DCE to remove them in other places where we // need them to survive. for (WeakTrackingVH &VH : IsDead) { if (VH.pointsToAliveValue()) { if (auto *OpaqueEV = getCallToTagged(&*VH, FunctionTags::OpaqueExtractValue)) { revng_assert(OpaqueEV); revng_assert(0 == OpaqueEV->getNumUses()); OpaqueEV->eraseFromParent(); } } } // After manually removing the OpaqueExtractValue, let LLVM do its job. RecursivelyDeleteTriviallyDeadInstructions(IsDead); return true; } char ExtractValueToGEPPass::ID = 0; static constexpr const char *Description = "extractvalue-to-i8-GEP replacement"; static llvm::RegisterPass X{ ExtractValueToGEPFlag, Description, false, false };