#ifndef LLVM_CODEGEN_GLOBALISEL_IRTRANSLATOR_H
#define LLVM_CODEGEN_GLOBALISEL_IRTRANSLATOR_H
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/CodeGen/CodeGenCommonISel.h"
#include "llvm/CodeGen/FunctionLoweringInfo.h"
#include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h"
#include "llvm/CodeGen/MachineFunctionPass.h"
#include "llvm/CodeGen/SwiftErrorValueTracking.h"
#include "llvm/CodeGen/SwitchLoweringUtils.h"
#include "llvm/Support/Allocator.h"
#include "llvm/Support/CodeGen.h"
#include <memory>
#include <utility>
namespace llvm {
class AllocaInst;
class BasicBlock;
class CallInst;
class CallLowering;
class Constant;
class ConstrainedFPIntrinsic;
class DataLayout;
class Instruction;
class MachineBasicBlock;
class MachineFunction;
class MachineInstr;
class MachineRegisterInfo;
class OptimizationRemarkEmitter;
class PHINode;
class TargetPassConfig;
class User;
class Value;
class IRTranslator : public MachineFunctionPass {
public:
  static char ID;
private:
    const CallLowering *CLI;
    class ValueToVRegInfo {
  public:
    ValueToVRegInfo() = default;
    using VRegListT = SmallVector<Register, 1>;
    using OffsetListT = SmallVector<uint64_t, 1>;
    using const_vreg_iterator =
        DenseMap<const Value *, VRegListT *>::const_iterator;
    using const_offset_iterator =
        DenseMap<const Value *, OffsetListT *>::const_iterator;
    inline const_vreg_iterator vregs_end() const { return ValToVRegs.end(); }
    VRegListT *getVRegs(const Value &V) {
      auto It = ValToVRegs.find(&V);
      if (It != ValToVRegs.end())
        return It->second;
      return insertVRegs(V);
    }
    OffsetListT *getOffsets(const Value &V) {
      auto It = TypeToOffsets.find(V.getType());
      if (It != TypeToOffsets.end())
        return It->second;
      return insertOffsets(V);
    }
    const_vreg_iterator findVRegs(const Value &V) const {
      return ValToVRegs.find(&V);
    }
    bool contains(const Value &V) const {
      return ValToVRegs.find(&V) != ValToVRegs.end();
    }
    void reset() {
      ValToVRegs.clear();
      TypeToOffsets.clear();
      VRegAlloc.DestroyAll();
      OffsetAlloc.DestroyAll();
    }
  private:
    VRegListT *insertVRegs(const Value &V) {
      assert(ValToVRegs.find(&V) == ValToVRegs.end() && "Value already exists");
                  auto *VRegList = new (VRegAlloc.Allocate()) VRegListT();
      ValToVRegs[&V] = VRegList;
      return VRegList;
    }
    OffsetListT *insertOffsets(const Value &V) {
      assert(TypeToOffsets.find(V.getType()) == TypeToOffsets.end() &&
             "Type already exists");
      auto *OffsetList = new (OffsetAlloc.Allocate()) OffsetListT();
      TypeToOffsets[V.getType()] = OffsetList;
      return OffsetList;
    }
    SpecificBumpPtrAllocator<VRegListT> VRegAlloc;
    SpecificBumpPtrAllocator<OffsetListT> OffsetAlloc;
            DenseMap<const Value *, VRegListT*> ValToVRegs;
    DenseMap<const Type *, OffsetListT*> TypeToOffsets;
  };
      ValueToVRegInfo VMap;
        DenseMap<const BasicBlock *, MachineBasicBlock *> BBToMBB;
            using CFGEdge = std::pair<const BasicBlock *, const BasicBlock *>;
  DenseMap<CFGEdge, SmallVector<MachineBasicBlock *, 1>> MachinePreds;
      SmallVector<std::pair<const PHINode *, SmallVector<MachineInstr *, 1>>, 4>
      PendingPHIs;
      DenseMap<const AllocaInst *, int> FrameIndices;
  SwiftErrorValueTracking SwiftError;
      
                                            bool translate(const Instruction &Inst);
            bool translate(const Constant &C, Register Reg);
    bool translateCopy(const User &U, const Value &V,
                     MachineIRBuilder &MIRBuilder);
      bool translateBitCast(const User &U, MachineIRBuilder &MIRBuilder);
    bool translateLoad(const User &U, MachineIRBuilder &MIRBuilder);
    bool translateStore(const User &U, MachineIRBuilder &MIRBuilder);
    bool translateMemFunc(const CallInst &CI, MachineIRBuilder &MIRBuilder,
                        unsigned Opcode);
  void getStackGuard(Register DstReg, MachineIRBuilder &MIRBuilder);
  bool translateOverflowIntrinsic(const CallInst &CI, unsigned Op,
                                  MachineIRBuilder &MIRBuilder);
  bool translateFixedPointIntrinsic(unsigned Op, const CallInst &CI,
                                    MachineIRBuilder &MIRBuilder);
          unsigned getSimpleIntrinsicOpcode(Intrinsic::ID ID);
      bool translateSimpleIntrinsic(const CallInst &CI, Intrinsic::ID ID,
                                MachineIRBuilder &MIRBuilder);
  bool translateConstrainedFPIntrinsic(const ConstrainedFPIntrinsic &FPI,
                                       MachineIRBuilder &MIRBuilder);
  bool translateKnownIntrinsic(const CallInst &CI, Intrinsic::ID ID,
                               MachineIRBuilder &MIRBuilder);
  bool translateInlineAsm(const CallBase &CB, MachineIRBuilder &MIRBuilder);
    bool translateCallBase(const CallBase &CB, MachineIRBuilder &MIRBuilder);
      bool translateCall(const User &U, MachineIRBuilder &MIRBuilder);
                  bool findUnwindDestinations(
      const BasicBlock *EHPadBB, BranchProbability Prob,
      SmallVectorImpl<std::pair<MachineBasicBlock *, BranchProbability>>
          &UnwindDests);
  bool translateInvoke(const User &U, MachineIRBuilder &MIRBuilder);
  bool translateCallBr(const User &U, MachineIRBuilder &MIRBuilder);
  bool translateLandingPad(const User &U, MachineIRBuilder &MIRBuilder);
      bool translateCast(unsigned Opcode, const User &U,
                     MachineIRBuilder &MIRBuilder);
    bool translatePHI(const User &U, MachineIRBuilder &MIRBuilder);
    bool translateCompare(const User &U, MachineIRBuilder &MIRBuilder);
    bool translateICmp(const User &U, MachineIRBuilder &MIRBuilder) {
    return translateCompare(U, MIRBuilder);
  }
    bool translateFCmp(const User &U, MachineIRBuilder &MIRBuilder) {
    return translateCompare(U, MIRBuilder);
  }
      void finishPendingPhis();
      bool translateUnaryOp(unsigned Opcode, const User &U,
                        MachineIRBuilder &MIRBuilder);
      bool translateBinaryOp(unsigned Opcode, const User &U,
                         MachineIRBuilder &MIRBuilder);
        bool shouldEmitAsBranches(const std::vector<SwitchCG::CaseBlock> &Cases);
        void emitBranchForMergedCondition(const Value *Cond, MachineBasicBlock *TBB,
                                    MachineBasicBlock *FBB,
                                    MachineBasicBlock *CurBB,
                                    MachineBasicBlock *SwitchBB,
                                    BranchProbability TProb,
                                    BranchProbability FProb, bool InvertCond);
      void findMergedConditions(const Value *Cond, MachineBasicBlock *TBB,
                            MachineBasicBlock *FBB, MachineBasicBlock *CurBB,
                            MachineBasicBlock *SwitchBB,
                            Instruction::BinaryOps Opc, BranchProbability TProb,
                            BranchProbability FProb, bool InvertCond);
      bool translateBr(const User &U, MachineIRBuilder &MIRBuilder);
    bool emitJumpTableHeader(SwitchCG::JumpTable &JT,
                           SwitchCG::JumpTableHeader &JTH,
                           MachineBasicBlock *HeaderBB);
  void emitJumpTable(SwitchCG::JumpTable &JT, MachineBasicBlock *MBB);
  void emitSwitchCase(SwitchCG::CaseBlock &CB, MachineBasicBlock *SwitchBB,
                      MachineIRBuilder &MIB);
      void emitBitTestHeader(SwitchCG::BitTestBlock &BTB,
                         MachineBasicBlock *SwitchMBB);
    void emitBitTestCase(SwitchCG::BitTestBlock &BB, MachineBasicBlock *NextMBB,
                       BranchProbability BranchProbToNext, Register Reg,
                       SwitchCG::BitTestCase &B, MachineBasicBlock *SwitchBB);
  bool lowerJumpTableWorkItem(
      SwitchCG::SwitchWorkListItem W, MachineBasicBlock *SwitchMBB,
      MachineBasicBlock *CurMBB, MachineBasicBlock *DefaultMBB,
      MachineIRBuilder &MIB, MachineFunction::iterator BBI,
      BranchProbability UnhandledProbs, SwitchCG::CaseClusterIt I,
      MachineBasicBlock *Fallthrough, bool FallthroughUnreachable);
  bool lowerSwitchRangeWorkItem(SwitchCG::CaseClusterIt I, Value *Cond,
                                MachineBasicBlock *Fallthrough,
                                bool FallthroughUnreachable,
                                BranchProbability UnhandledProbs,
                                MachineBasicBlock *CurMBB,
                                MachineIRBuilder &MIB,
                                MachineBasicBlock *SwitchMBB);
  bool lowerBitTestWorkItem(
      SwitchCG::SwitchWorkListItem W, MachineBasicBlock *SwitchMBB,
      MachineBasicBlock *CurMBB, MachineBasicBlock *DefaultMBB,
      MachineIRBuilder &MIB, MachineFunction::iterator BBI,
      BranchProbability DefaultProb, BranchProbability UnhandledProbs,
      SwitchCG::CaseClusterIt I, MachineBasicBlock *Fallthrough,
      bool FallthroughUnreachable);
  bool lowerSwitchWorkItem(SwitchCG::SwitchWorkListItem W, Value *Cond,
                           MachineBasicBlock *SwitchMBB,
                           MachineBasicBlock *DefaultMBB,
                           MachineIRBuilder &MIB);
  bool translateSwitch(const User &U, MachineIRBuilder &MIRBuilder);
  
  bool translateIndirectBr(const User &U, MachineIRBuilder &MIRBuilder);
  bool translateExtractValue(const User &U, MachineIRBuilder &MIRBuilder);
  bool translateInsertValue(const User &U, MachineIRBuilder &MIRBuilder);
  bool translateSelect(const User &U, MachineIRBuilder &MIRBuilder);
  bool translateGetElementPtr(const User &U, MachineIRBuilder &MIRBuilder);
  bool translateAlloca(const User &U, MachineIRBuilder &MIRBuilder);
          bool translateRet(const User &U, MachineIRBuilder &MIRBuilder);
  bool translateFNeg(const User &U, MachineIRBuilder &MIRBuilder);
  bool translateAdd(const User &U, MachineIRBuilder &MIRBuilder) {
    return translateBinaryOp(TargetOpcode::G_ADD, U, MIRBuilder);
  }
  bool translateSub(const User &U, MachineIRBuilder &MIRBuilder) {
    return translateBinaryOp(TargetOpcode::G_SUB, U, MIRBuilder);
  }
  bool translateAnd(const User &U, MachineIRBuilder &MIRBuilder) {
    return translateBinaryOp(TargetOpcode::G_AND, U, MIRBuilder);
  }
  bool translateMul(const User &U, MachineIRBuilder &MIRBuilder) {
    return translateBinaryOp(TargetOpcode::G_MUL, U, MIRBuilder);
  }
  bool translateOr(const User &U, MachineIRBuilder &MIRBuilder) {
    return translateBinaryOp(TargetOpcode::G_OR, U, MIRBuilder);
  }
  bool translateXor(const User &U, MachineIRBuilder &MIRBuilder) {
    return translateBinaryOp(TargetOpcode::G_XOR, U, MIRBuilder);
  }
  bool translateUDiv(const User &U, MachineIRBuilder &MIRBuilder) {
    return translateBinaryOp(TargetOpcode::G_UDIV, U, MIRBuilder);
  }
  bool translateSDiv(const User &U, MachineIRBuilder &MIRBuilder) {
    return translateBinaryOp(TargetOpcode::G_SDIV, U, MIRBuilder);
  }
  bool translateURem(const User &U, MachineIRBuilder &MIRBuilder) {
    return translateBinaryOp(TargetOpcode::G_UREM, U, MIRBuilder);
  }
  bool translateSRem(const User &U, MachineIRBuilder &MIRBuilder) {
    return translateBinaryOp(TargetOpcode::G_SREM, U, MIRBuilder);
  }
  bool translateIntToPtr(const User &U, MachineIRBuilder &MIRBuilder) {
    return translateCast(TargetOpcode::G_INTTOPTR, U, MIRBuilder);
  }
  bool translatePtrToInt(const User &U, MachineIRBuilder &MIRBuilder) {
    return translateCast(TargetOpcode::G_PTRTOINT, U, MIRBuilder);
  }
  bool translateTrunc(const User &U, MachineIRBuilder &MIRBuilder) {
    return translateCast(TargetOpcode::G_TRUNC, U, MIRBuilder);
  }
  bool translateFPTrunc(const User &U, MachineIRBuilder &MIRBuilder) {
    return translateCast(TargetOpcode::G_FPTRUNC, U, MIRBuilder);
  }
  bool translateFPExt(const User &U, MachineIRBuilder &MIRBuilder) {
    return translateCast(TargetOpcode::G_FPEXT, U, MIRBuilder);
  }
  bool translateFPToUI(const User &U, MachineIRBuilder &MIRBuilder) {
    return translateCast(TargetOpcode::G_FPTOUI, U, MIRBuilder);
  }
  bool translateFPToSI(const User &U, MachineIRBuilder &MIRBuilder) {
    return translateCast(TargetOpcode::G_FPTOSI, U, MIRBuilder);
  }
  bool translateUIToFP(const User &U, MachineIRBuilder &MIRBuilder) {
    return translateCast(TargetOpcode::G_UITOFP, U, MIRBuilder);
  }
  bool translateSIToFP(const User &U, MachineIRBuilder &MIRBuilder) {
    return translateCast(TargetOpcode::G_SITOFP, U, MIRBuilder);
  }
  bool translateUnreachable(const User &U, MachineIRBuilder &MIRBuilder);
  bool translateSExt(const User &U, MachineIRBuilder &MIRBuilder) {
    return translateCast(TargetOpcode::G_SEXT, U, MIRBuilder);
  }
  bool translateZExt(const User &U, MachineIRBuilder &MIRBuilder) {
    return translateCast(TargetOpcode::G_ZEXT, U, MIRBuilder);
  }
  bool translateShl(const User &U, MachineIRBuilder &MIRBuilder) {
    return translateBinaryOp(TargetOpcode::G_SHL, U, MIRBuilder);
  }
  bool translateLShr(const User &U, MachineIRBuilder &MIRBuilder) {
    return translateBinaryOp(TargetOpcode::G_LSHR, U, MIRBuilder);
  }
  bool translateAShr(const User &U, MachineIRBuilder &MIRBuilder) {
    return translateBinaryOp(TargetOpcode::G_ASHR, U, MIRBuilder);
  }
  bool translateFAdd(const User &U, MachineIRBuilder &MIRBuilder) {
    return translateBinaryOp(TargetOpcode::G_FADD, U, MIRBuilder);
  }
  bool translateFSub(const User &U, MachineIRBuilder &MIRBuilder) {
    return translateBinaryOp(TargetOpcode::G_FSUB, U, MIRBuilder);
  }
  bool translateFMul(const User &U, MachineIRBuilder &MIRBuilder) {
    return translateBinaryOp(TargetOpcode::G_FMUL, U, MIRBuilder);
  }
  bool translateFDiv(const User &U, MachineIRBuilder &MIRBuilder) {
    return translateBinaryOp(TargetOpcode::G_FDIV, U, MIRBuilder);
  }
  bool translateFRem(const User &U, MachineIRBuilder &MIRBuilder) {
    return translateBinaryOp(TargetOpcode::G_FREM, U, MIRBuilder);
  }
  bool translateVAArg(const User &U, MachineIRBuilder &MIRBuilder);
  bool translateInsertElement(const User &U, MachineIRBuilder &MIRBuilder);
  bool translateExtractElement(const User &U, MachineIRBuilder &MIRBuilder);
  bool translateShuffleVector(const User &U, MachineIRBuilder &MIRBuilder);
  bool translateAtomicCmpXchg(const User &U, MachineIRBuilder &MIRBuilder);
  bool translateAtomicRMW(const User &U, MachineIRBuilder &MIRBuilder);
  bool translateFence(const User &U, MachineIRBuilder &MIRBuilder);
  bool translateFreeze(const User &U, MachineIRBuilder &MIRBuilder);
      bool translateResume(const User &U, MachineIRBuilder &MIRBuilder) {
    return false;
  }
  bool translateCleanupRet(const User &U, MachineIRBuilder &MIRBuilder) {
    return false;
  }
  bool translateCatchRet(const User &U, MachineIRBuilder &MIRBuilder) {
    return false;
  }
  bool translateCatchSwitch(const User &U, MachineIRBuilder &MIRBuilder) {
    return false;
  }
  bool translateAddrSpaceCast(const User &U, MachineIRBuilder &MIRBuilder) {
    return translateCast(TargetOpcode::G_ADDRSPACE_CAST, U, MIRBuilder);
  }
  bool translateCleanupPad(const User &U, MachineIRBuilder &MIRBuilder) {
    return false;
  }
  bool translateCatchPad(const User &U, MachineIRBuilder &MIRBuilder) {
    return false;
  }
  bool translateUserOp1(const User &U, MachineIRBuilder &MIRBuilder) {
    return false;
  }
  bool translateUserOp2(const User &U, MachineIRBuilder &MIRBuilder) {
    return false;
  }
  
            std::unique_ptr<MachineIRBuilder> CurBuilder;
        std::unique_ptr<MachineIRBuilder> EntryBuilder;
    MachineFunction *MF;
    MachineRegisterInfo *MRI = nullptr;
  const DataLayout *DL;
    const TargetPassConfig *TPC;
  CodeGenOpt::Level OptLevel;
    std::unique_ptr<OptimizationRemarkEmitter> ORE;
  AAResults *AA;
  FunctionLoweringInfo FuncInfo;
      bool EnableOpts = false;
      bool HasTailCall = false;
  StackProtectorDescriptor SPDescriptor;
    class GISelSwitchLowering : public SwitchCG::SwitchLowering {
  public:
    GISelSwitchLowering(IRTranslator *irt, FunctionLoweringInfo &funcinfo)
        : SwitchLowering(funcinfo), IRT(irt) {
      assert(irt && "irt is null!");
    }
    void addSuccessorWithProb(
        MachineBasicBlock *Src, MachineBasicBlock *Dst,
        BranchProbability Prob = BranchProbability::getUnknown()) override {
      IRT->addSuccessorWithProb(Src, Dst, Prob);
    }
    virtual ~GISelSwitchLowering() = default;
  private:
    IRTranslator *IRT;
  };
  std::unique_ptr<GISelSwitchLowering> SL;
          void finalizeFunction();
        bool finalizeBasicBlock(const BasicBlock &BB, MachineBasicBlock &MBB);
                  bool emitSPDescriptorParent(StackProtectorDescriptor &SPD,
                              MachineBasicBlock *ParentBB);
                      bool emitSPDescriptorFailure(StackProtectorDescriptor &SPD,
                               MachineBasicBlock *FailureBB);
          ArrayRef<Register> getOrCreateVRegs(const Value &Val);
  Register getOrCreateVReg(const Value &Val) {
    auto Regs = getOrCreateVRegs(Val);
    if (Regs.empty())
      return 0;
    assert(Regs.size() == 1 &&
           "attempt to get single VReg for aggregate or void");
    return Regs[0];
  }
      ValueToVRegInfo::VRegListT &allocateVRegs(const Value &Val);
      int getOrCreateFrameIndex(const AllocaInst &AI);
        Align getMemOpAlign(const Instruction &I);
        MachineBasicBlock &getMBB(const BasicBlock &BB);
          void addMachineCFGPred(CFGEdge Edge, MachineBasicBlock *NewPred);
          SmallVector<MachineBasicBlock *, 1> getMachinePredBBs(CFGEdge Edge) {
    auto RemappedEdge = MachinePreds.find(Edge);
    if (RemappedEdge != MachinePreds.end())
      return RemappedEdge->second;
    return SmallVector<MachineBasicBlock *, 4>(1, &getMBB(*Edge.first));
  }
      BranchProbability getEdgeProbability(const MachineBasicBlock *Src,
                                       const MachineBasicBlock *Dst) const;
  void addSuccessorWithProb(
      MachineBasicBlock *Src, MachineBasicBlock *Dst,
      BranchProbability Prob = BranchProbability::getUnknown());
public:
  IRTranslator(CodeGenOpt::Level OptLevel = CodeGenOpt::None);
  StringRef getPassName() const override { return "IRTranslator"; }
  void getAnalysisUsage(AnalysisUsage &AU) const override;
                          bool runOnMachineFunction(MachineFunction &MF) override;
};
} 
#endif