| //===- Float2Int.cpp - Demote floating point ops to work on integers ------===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| // | |
| // This file implements the Float2Int pass, which aims to demote floating | |
| // point operations to work on integers, where that is losslessly possible. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "llvm/Transforms/Scalar/Float2Int.h" | |
| #include "ScalarOptions.h" | |
| #include "llvm/ADT/APInt.h" | |
| #include "llvm/ADT/APSInt.h" | |
| #include "llvm/ADT/SmallVector.h" | |
| #include "llvm/Analysis/GlobalsModRef.h" | |
| #include "llvm/IR/Constants.h" | |
| #include "llvm/IR/Dominators.h" | |
| #include "llvm/IR/IRBuilder.h" | |
| #include "llvm/IR/Module.h" | |
| #include "llvm/Support/Debug.h" | |
| #include "llvm/Support/raw_ostream.h" | |
| #include <deque> | |
| #define DEBUG_TYPE "float2int" | |
| using namespace llvm; | |
| // The algorithm is simple. Start at instructions that convert from the | |
| // float to the int domain: fptoui, fptosi and fcmp. Walk up the def-use | |
| // graph, using an equivalence datastructure to unify graphs that interfere. | |
| // | |
| // Mappable instructions are those with an integer corrollary that, given | |
| // integer domain inputs, produce an integer output; fadd, for example. | |
| // | |
| // If a non-mappable instruction is seen, this entire def-use graph is marked | |
| // as non-transformable. If we see an instruction that converts from the | |
| // integer domain to FP domain (uitofp,sitofp), we terminate our walk. | |
| // Given a FCmp predicate, return a matching ICmp predicate if one | |
| // exists, otherwise return BAD_ICMP_PREDICATE. | |
| static CmpInst::Predicate mapFCmpPred(CmpInst::Predicate P) { | |
| switch (P) { | |
| case CmpInst::FCMP_OEQ: | |
| case CmpInst::FCMP_UEQ: | |
| return CmpInst::ICMP_EQ; | |
| case CmpInst::FCMP_OGT: | |
| case CmpInst::FCMP_UGT: | |
| return CmpInst::ICMP_SGT; | |
| case CmpInst::FCMP_OGE: | |
| case CmpInst::FCMP_UGE: | |
| return CmpInst::ICMP_SGE; | |
| case CmpInst::FCMP_OLT: | |
| case CmpInst::FCMP_ULT: | |
| return CmpInst::ICMP_SLT; | |
| case CmpInst::FCMP_OLE: | |
| case CmpInst::FCMP_ULE: | |
| return CmpInst::ICMP_SLE; | |
| case CmpInst::FCMP_ONE: | |
| case CmpInst::FCMP_UNE: | |
| return CmpInst::ICMP_NE; | |
| default: | |
| return CmpInst::BAD_ICMP_PREDICATE; | |
| } | |
| } | |
| // Given a floating point binary operator, return the matching | |
| // integer version. | |
| static Instruction::BinaryOps mapBinOpcode(unsigned Opcode) { | |
| switch (Opcode) { | |
| default: llvm_unreachable("Unhandled opcode!"); | |
| case Instruction::FAdd: return Instruction::Add; | |
| case Instruction::FSub: return Instruction::Sub; | |
| case Instruction::FMul: return Instruction::Mul; | |
| } | |
| } | |
| // Find the roots - instructions that convert from the FP domain to | |
| // integer domain. | |
| void Float2IntPass::findRoots(Function &F, const DominatorTree &DT) { | |
| for (BasicBlock &BB : F) { | |
| // Unreachable code can take on strange forms that we are not prepared to | |
| // handle. For example, an instruction may have itself as an operand. | |
| if (!DT.isReachableFromEntry(&BB)) | |
| continue; | |
| for (Instruction &I : BB) { | |
| if (isa<VectorType>(I.getType())) | |
| continue; | |
| switch (I.getOpcode()) { | |
| default: break; | |
| case Instruction::FPToUI: | |
| case Instruction::FPToSI: | |
| Roots.insert(&I); | |
| break; | |
| case Instruction::FCmp: | |
| if (mapFCmpPred(cast<CmpInst>(&I)->getPredicate()) != | |
| CmpInst::BAD_ICMP_PREDICATE) | |
| Roots.insert(&I); | |
| break; | |
| } | |
| } | |
| } | |
| } | |
| // Helper - mark I as having been traversed, having range R. | |
| void Float2IntPass::seen(Instruction *I, ConstantRange R) { | |
| LLVM_DEBUG(dbgs() << "F2I: " << *I << ":" << R << "\n"); | |
| SeenInsts.insert_or_assign(I, std::move(R)); | |
| } | |
| // Helper - get a range representing a poison value. | |
| ConstantRange Float2IntPass::badRange() { | |
| return ConstantRange::getFull(MaxIntegerBW + 1); | |
| } | |
| ConstantRange Float2IntPass::unknownRange() { | |
| return ConstantRange::getEmpty(MaxIntegerBW + 1); | |
| } | |
| ConstantRange Float2IntPass::validateRange(ConstantRange R) { | |
| if (R.getBitWidth() > MaxIntegerBW + 1) | |
| return badRange(); | |
| return R; | |
| } | |
| // The most obvious way to structure the search is a depth-first, eager | |
| // search from each root. However, that require direct recursion and so | |
| // can only handle small instruction sequences. Instead, we split the search | |
| // up into two phases: | |
| // - walkBackwards: A breadth-first walk of the use-def graph starting from | |
| // the roots. Populate "SeenInsts" with interesting | |
| // instructions and poison values if they're obvious and | |
| // cheap to compute. Calculate the equivalance set structure | |
| // while we're here too. | |
| // - walkForwards: Iterate over SeenInsts in reverse order, so we visit | |
| // defs before their uses. Calculate the real range info. | |
| // Breadth-first walk of the use-def graph; determine the set of nodes | |
| // we care about and eagerly determine if some of them are poisonous. | |
| void Float2IntPass::walkBackwards() { | |
| std::deque<Instruction*> Worklist(Roots.begin(), Roots.end()); | |
| while (!Worklist.empty()) { | |
| Instruction *I = Worklist.back(); | |
| Worklist.pop_back(); | |
| if (SeenInsts.contains(I)) | |
| // Seen already. | |
| continue; | |
| switch (I->getOpcode()) { | |
| // FIXME: Handle select and phi nodes. | |
| default: | |
| // Path terminated uncleanly. | |
| seen(I, badRange()); | |
| break; | |
| case Instruction::UIToFP: | |
| case Instruction::SIToFP: { | |
| // Path terminated cleanly - use the type of the integer input to seed | |
| // the analysis. | |
| unsigned BW = I->getOperand(0)->getType()->getPrimitiveSizeInBits(); | |
| auto Input = ConstantRange::getFull(BW); | |
| auto CastOp = (Instruction::CastOps)I->getOpcode(); | |
| seen(I, validateRange(Input.castOp(CastOp, MaxIntegerBW+1))); | |
| continue; | |
| } | |
| case Instruction::FNeg: | |
| case Instruction::FAdd: | |
| case Instruction::FSub: | |
| case Instruction::FMul: | |
| case Instruction::FPToUI: | |
| case Instruction::FPToSI: | |
| case Instruction::FCmp: | |
| seen(I, unknownRange()); | |
| break; | |
| } | |
| for (Value *O : I->operands()) { | |
| if (Instruction *OI = dyn_cast<Instruction>(O)) { | |
| // Unify def-use chains if they interfere. | |
| ECs.unionSets(I, OI); | |
| if (SeenInsts.find(I)->second != badRange()) | |
| Worklist.push_back(OI); | |
| } else if (!isa<ConstantFP>(O)) { | |
| // Not an instruction or ConstantFP? we can't do anything. | |
| seen(I, badRange()); | |
| } | |
| } | |
| } | |
| } | |
| // Calculate result range from operand ranges. | |
| // Return std::nullopt if the range cannot be calculated yet. | |
| std::optional<ConstantRange> Float2IntPass::calcRange(Instruction *I) { | |
| SmallVector<ConstantRange, 4> OpRanges; | |
| for (Value *O : I->operands()) { | |
| if (Instruction *OI = dyn_cast<Instruction>(O)) { | |
| auto OpIt = SeenInsts.find(OI); | |
| assert(OpIt != SeenInsts.end() && "def not seen before use!"); | |
| if (OpIt->second == unknownRange()) | |
| return std::nullopt; // Wait until operand range has been calculated. | |
| OpRanges.push_back(OpIt->second); | |
| } else if (ConstantFP *CF = dyn_cast<ConstantFP>(O)) { | |
| // Work out if the floating point number can be losslessly represented | |
| // as an integer. | |
| // APFloat::convertToInteger(&Exact) purports to do what we want, but | |
| // the exactness can be too precise. For example, negative zero can | |
| // never be exactly converted to an integer. | |
| // | |
| // Instead, we ask APFloat to round itself to an integral value - this | |
| // preserves sign-of-zero - then compare the result with the original. | |
| // | |
| const APFloat &F = CF->getValueAPF(); | |
| // First, weed out obviously incorrect values. Non-finite numbers | |
| // can't be represented and neither can negative zero, unless | |
| // we're in fast math mode. | |
| if (!F.isFinite() || | |
| (F.isZero() && F.isNegative() && isa<FPMathOperator>(I) && | |
| !I->hasNoSignedZeros())) | |
| return badRange(); | |
| APFloat NewF = F; | |
| auto Res = NewF.roundToIntegral(APFloat::rmNearestTiesToEven); | |
| if (Res != APFloat::opOK || NewF != F) | |
| return badRange(); | |
| // OK, it's representable. Now get it. | |
| APSInt Int(MaxIntegerBW+1, false); | |
| bool Exact; | |
| APFloat::opStatus Status = CF->getValueAPF().convertToInteger( | |
| Int, APFloat::rmNearestTiesToEven, &Exact); | |
| // Although the round above is loseless, we still need to check if the | |
| // floating-point value can be represented in the integer type. | |
| if (Status == APFloat::opOK || Status == APFloat::opInexact) | |
| OpRanges.push_back(ConstantRange(Int)); | |
| else | |
| return badRange(); | |
| } else { | |
| llvm_unreachable("Should have already marked this as badRange!"); | |
| } | |
| } | |
| switch (I->getOpcode()) { | |
| // FIXME: Handle select and phi nodes. | |
| default: | |
| case Instruction::UIToFP: | |
| case Instruction::SIToFP: | |
| llvm_unreachable("Should have been handled in walkForwards!"); | |
| case Instruction::FNeg: { | |
| assert(OpRanges.size() == 1 && "FNeg is a unary operator!"); | |
| unsigned Size = OpRanges[0].getBitWidth(); | |
| auto Zero = ConstantRange(APInt::getZero(Size)); | |
| return Zero.sub(OpRanges[0]); | |
| } | |
| case Instruction::FAdd: | |
| case Instruction::FSub: | |
| case Instruction::FMul: { | |
| assert(OpRanges.size() == 2 && "its a binary operator!"); | |
| auto BinOp = (Instruction::BinaryOps) I->getOpcode(); | |
| return OpRanges[0].binaryOp(BinOp, OpRanges[1]); | |
| } | |
| // | |
| // Root-only instructions - we'll only see these if they're the | |
| // first node in a walk. | |
| // | |
| case Instruction::FPToUI: | |
| case Instruction::FPToSI: { | |
| assert(OpRanges.size() == 1 && "FPTo[US]I is a unary operator!"); | |
| // Note: We're ignoring the casts output size here as that's what the | |
| // caller expects. | |
| auto CastOp = (Instruction::CastOps)I->getOpcode(); | |
| return OpRanges[0].castOp(CastOp, MaxIntegerBW+1); | |
| } | |
| case Instruction::FCmp: | |
| assert(OpRanges.size() == 2 && "FCmp is a binary operator!"); | |
| return OpRanges[0].unionWith(OpRanges[1]); | |
| } | |
| } | |
| // Walk forwards down the list of seen instructions, so we visit defs before | |
| // uses. | |
| void Float2IntPass::walkForwards() { | |
| std::deque<Instruction *> Worklist; | |
| for (const auto &Pair : SeenInsts) | |
| if (Pair.second == unknownRange()) | |
| Worklist.push_back(Pair.first); | |
| while (!Worklist.empty()) { | |
| Instruction *I = Worklist.back(); | |
| Worklist.pop_back(); | |
| if (std::optional<ConstantRange> Range = calcRange(I)) | |
| seen(I, *Range); | |
| else | |
| Worklist.push_front(I); // Reprocess later. | |
| } | |
| } | |
| // If there is a valid transform to be done, do it. | |
| bool Float2IntPass::validateAndTransform(const DataLayout &DL) { | |
| bool MadeChange = false; | |
| // Iterate over every disjoint partition of the def-use graph. | |
| for (const auto &E : ECs) { | |
| if (!E->isLeader()) | |
| continue; | |
| ConstantRange R(MaxIntegerBW + 1, false); | |
| bool Fail = false; | |
| Type *ConvertedToTy = nullptr; | |
| // For every member of the partition, union all the ranges together. | |
| for (Instruction *I : ECs.members(*E)) { | |
| auto *SeenI = SeenInsts.find(I); | |
| if (SeenI == SeenInsts.end()) | |
| continue; | |
| R = R.unionWith(SeenI->second); | |
| // We need to ensure I has no users that have not been seen. | |
| // If it does, transformation would be illegal. | |
| // | |
| // Don't count the roots, as they terminate the graphs. | |
| if (!Roots.contains(I)) { | |
| // Set the type of the conversion while we're here. | |
| if (!ConvertedToTy) | |
| ConvertedToTy = I->getType(); | |
| for (User *U : I->users()) { | |
| Instruction *UI = dyn_cast<Instruction>(U); | |
| if (!UI || !SeenInsts.contains(UI)) { | |
| LLVM_DEBUG(dbgs() << "F2I: Failing because of " << *U << "\n"); | |
| Fail = true; | |
| break; | |
| } | |
| } | |
| } | |
| if (Fail) | |
| break; | |
| } | |
| // If the set was empty, or we failed, or the range is poisonous, | |
| // bail out. | |
| if (ECs.member_begin(*E) == ECs.member_end() || Fail || R.isFullSet() || | |
| R.isSignWrappedSet()) | |
| continue; | |
| assert(ConvertedToTy && "Must have set the convertedtoty by this point!"); | |
| // The number of bits required is the maximum of the upper and | |
| // lower limits, plus one so it can be signed. | |
| unsigned MinBW = R.getMinSignedBits() + 1; | |
| LLVM_DEBUG(dbgs() << "F2I: MinBitwidth=" << MinBW << ", R: " << R << "\n"); | |
| // If we've run off the realms of the exactly representable integers, | |
| // the floating point result will differ from an integer approximation. | |
| // Do we need more bits than are in the mantissa of the type we converted | |
| // to? semanticsPrecision returns the number of mantissa bits plus one | |
| // for the sign bit. | |
| unsigned MaxRepresentableBits | |
| = APFloat::semanticsPrecision(ConvertedToTy->getFltSemantics()) - 1; | |
| if (MinBW > MaxRepresentableBits) { | |
| LLVM_DEBUG(dbgs() << "F2I: Value not guaranteed to be representable!\n"); | |
| continue; | |
| } | |
| // OK, R is known to be representable. | |
| // Pick the smallest legal type that will fit. | |
| Type *Ty = DL.getSmallestLegalIntType(*Ctx, MinBW); | |
| if (!Ty) { | |
| // Every supported target supports 64-bit and 32-bit integers, | |
| // so fallback to a 32 or 64-bit integer if the value fits. | |
| if (MinBW <= 32) { | |
| Ty = Type::getInt32Ty(*Ctx); | |
| } else if (MinBW <= 64) { | |
| Ty = Type::getInt64Ty(*Ctx); | |
| } else { | |
| LLVM_DEBUG(dbgs() << "F2I: Value requires more bits to represent than " | |
| "the target supports!\n"); | |
| continue; | |
| } | |
| } | |
| for (Instruction *I : ECs.members(*E)) | |
| convert(I, Ty); | |
| MadeChange = true; | |
| } | |
| return MadeChange; | |
| } | |
| Value *Float2IntPass::convert(Instruction *I, Type *ToTy) { | |
| if (auto It = ConvertedInsts.find(I); It != ConvertedInsts.end()) | |
| // Already converted this instruction. | |
| return It->second; | |
| SmallVector<Value*,4> NewOperands; | |
| for (Value *V : I->operands()) { | |
| // Don't recurse if we're an instruction that terminates the path. | |
| if (I->getOpcode() == Instruction::UIToFP || | |
| I->getOpcode() == Instruction::SIToFP) { | |
| NewOperands.push_back(V); | |
| } else if (Instruction *VI = dyn_cast<Instruction>(V)) { | |
| NewOperands.push_back(convert(VI, ToTy)); | |
| } else if (ConstantFP *CF = dyn_cast<ConstantFP>(V)) { | |
| APSInt Val(ToTy->getPrimitiveSizeInBits(), /*isUnsigned=*/false); | |
| bool Exact; | |
| CF->getValueAPF().convertToInteger(Val, | |
| APFloat::rmNearestTiesToEven, | |
| &Exact); | |
| NewOperands.push_back(ConstantInt::get(ToTy, Val)); | |
| } else { | |
| llvm_unreachable("Unhandled operand type?"); | |
| } | |
| } | |
| // Now create a new instruction. | |
| IRBuilder<> IRB(I); | |
| Value *NewV = nullptr; | |
| switch (I->getOpcode()) { | |
| default: llvm_unreachable("Unhandled instruction!"); | |
| case Instruction::FPToUI: | |
| NewV = IRB.CreateZExtOrTrunc(NewOperands[0], I->getType()); | |
| break; | |
| case Instruction::FPToSI: | |
| NewV = IRB.CreateSExtOrTrunc(NewOperands[0], I->getType()); | |
| break; | |
| case Instruction::FCmp: { | |
| CmpInst::Predicate P = mapFCmpPred(cast<CmpInst>(I)->getPredicate()); | |
| assert(P != CmpInst::BAD_ICMP_PREDICATE && "Unhandled predicate!"); | |
| NewV = IRB.CreateICmp(P, NewOperands[0], NewOperands[1], I->getName()); | |
| break; | |
| } | |
| case Instruction::UIToFP: | |
| NewV = IRB.CreateZExtOrTrunc(NewOperands[0], ToTy); | |
| break; | |
| case Instruction::SIToFP: | |
| NewV = IRB.CreateSExtOrTrunc(NewOperands[0], ToTy); | |
| break; | |
| case Instruction::FNeg: | |
| NewV = IRB.CreateNeg(NewOperands[0], I->getName()); | |
| break; | |
| case Instruction::FAdd: | |
| case Instruction::FSub: | |
| case Instruction::FMul: | |
| NewV = IRB.CreateBinOp(mapBinOpcode(I->getOpcode()), | |
| NewOperands[0], NewOperands[1], | |
| I->getName()); | |
| break; | |
| } | |
| // If we're a root instruction, RAUW. | |
| if (Roots.count(I)) | |
| I->replaceAllUsesWith(NewV); | |
| ConvertedInsts[I] = NewV; | |
| return NewV; | |
| } | |
| // Perform dead code elimination on the instructions we just modified. | |
| void Float2IntPass::cleanup() { | |
| for (auto &I : reverse(ConvertedInsts)) | |
| I.first->eraseFromParent(); | |
| } | |
| bool Float2IntPass::runImpl(Function &F, const DominatorTree &DT) { | |
| LLVM_DEBUG(dbgs() << "F2I: Looking at function " << F.getName() << "\n"); | |
| // Clear out all state. | |
| ECs = EquivalenceClasses<Instruction*>(); | |
| SeenInsts.clear(); | |
| ConvertedInsts.clear(); | |
| Roots.clear(); | |
| Ctx = &F.getParent()->getContext(); | |
| MaxIntegerBW = | |
| Ctx->getLibraryOptions().get<ScalarOptions>().float2int_max_integer_bw; | |
| findRoots(F, DT); | |
| walkBackwards(); | |
| walkForwards(); | |
| const DataLayout &DL = F.getDataLayout(); | |
| bool Modified = validateAndTransform(DL); | |
| if (Modified) | |
| cleanup(); | |
| return Modified; | |
| } | |
| PreservedAnalyses Float2IntPass::run(Function &F, FunctionAnalysisManager &AM) { | |
| const DominatorTree &DT = AM.getResult<DominatorTreeAnalysis>(F); | |
| if (!runImpl(F, DT)) | |
| return PreservedAnalyses::all(); | |
| PreservedAnalyses PA; | |
| PA.preserveSet<CFGAnalyses>(); | |
| return PA; | |
| } |