//===- DomTreeUpdater.h - DomTree/Post DomTree Updater ----------*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file defines the DomTreeUpdater class, which provides a uniform way to
// update dominator tree related data structures.
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_DOMTREEUPDATER_H
#define LLVM_DOMTREEUPDATER_H

#include "llvm/Analysis/PostDominators.h"
#include "llvm/IR/Dominators.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/ValueHandle.h"
#include "llvm/Support/GenericDomTree.h"
#include <functional>
#include <vector>

namespace llvm {
class DomTreeUpdater {
public:
  enum class UpdateStrategy : unsigned char { Eager = 0, Lazy = 1 };

  explicit DomTreeUpdater(UpdateStrategy Strategy_) : Strategy(Strategy_) {}
  DomTreeUpdater(DominatorTree &DT_, UpdateStrategy Strategy_)
      : DT(&DT_), Strategy(Strategy_) {}
  DomTreeUpdater(DominatorTree *DT_, UpdateStrategy Strategy_)
      : DT(DT_), Strategy(Strategy_) {}
  DomTreeUpdater(PostDominatorTree &PDT_, UpdateStrategy Strategy_)
      : PDT(&PDT_), Strategy(Strategy_) {}
  DomTreeUpdater(PostDominatorTree *PDT_, UpdateStrategy Strategy_)
      : PDT(PDT_), Strategy(Strategy_) {}
  DomTreeUpdater(DominatorTree &DT_, PostDominatorTree &PDT_,
                 UpdateStrategy Strategy_)
      : DT(&DT_), PDT(&PDT_), Strategy(Strategy_) {}
  DomTreeUpdater(DominatorTree *DT_, PostDominatorTree *PDT_,
                 UpdateStrategy Strategy_)
      : DT(DT_), PDT(PDT_), Strategy(Strategy_) {}

  ~DomTreeUpdater() { flush(); }

  /// Returns true if the current strategy is Lazy.
  bool isLazy() const { return Strategy == UpdateStrategy::Lazy; };

  /// Returns true if the current strategy is Eager.
  bool isEager() const { return Strategy == UpdateStrategy::Eager; };

  /// Returns true if it holds a DominatorTree.
  bool hasDomTree() const { return DT != nullptr; }

  /// Returns true if it holds a PostDominatorTree.
  bool hasPostDomTree() const { return PDT != nullptr; }

  /// Returns true if there is BasicBlock awaiting deletion.
  /// The deletion will only happen until a flush event and
  /// all available trees are up-to-date.
  /// Returns false under Eager UpdateStrategy.
  bool hasPendingDeletedBB() const { return !DeletedBBs.empty(); }

  /// Returns true if DelBB is awaiting deletion.
  /// Returns false under Eager UpdateStrategy.
  bool isBBPendingDeletion(BasicBlock *DelBB) const;

  /// Returns true if either of DT or PDT is valid and the tree has at
  /// least one update pending. If DT or PDT is nullptr it is treated
  /// as having no pending updates. This function does not check
  /// whether there is BasicBlock awaiting deletion.
  /// Returns false under Eager UpdateStrategy.
  bool hasPendingUpdates() const;

  /// Returns true if there are DominatorTree updates queued.
  /// Returns false under Eager UpdateStrategy or DT is nullptr.
  bool hasPendingDomTreeUpdates() const;

  /// Returns true if there are PostDominatorTree updates queued.
  /// Returns false under Eager UpdateStrategy or PDT is nullptr.
  bool hasPendingPostDomTreeUpdates() const;

  /// Apply updates on all available trees. Under Eager UpdateStrategy with
  /// ForceRemoveDuplicates enabled or under Lazy UpdateStrategy, it will
  /// discard duplicated updates and self-dominance updates. If both DT and PDT
  /// are nullptrs, this function discards all updates. The Eager Strategy
  /// applies the updates immediately while the Lazy Strategy queues the
  /// updates. It is required for the state of the LLVM IR to be updated
  /// *before* applying the Updates because the internal update routine will
  /// analyze the current state of the relationship between a pair of (From, To)
  /// BasicBlocks to determine whether a single update needs to be discarded.
  void applyUpdates(ArrayRef<DominatorTree::UpdateType> Updates,
                    bool ForceRemoveDuplicates = false);

  /// Notify all available trees on an edge insertion. If both DT and PDT are
  /// nullptrs, this function discards the update. Under either Strategy,
  /// self-dominance update will be removed. The Eager Strategy applies
  /// the update immediately while the Lazy Strategy queues the update.
  /// It is recommended to only use this method when you have exactly one
  /// insertion (and no deletions). It is recommended to use applyUpdates() in
  /// all other cases. This function has to be called *after* making the update
  /// on the actual CFG. An internal functions checks if the edge exists in the
  /// CFG in DEBUG mode.
  void insertEdge(BasicBlock *From, BasicBlock *To);

  /// Notify all available trees on an edge insertion.
  /// Under either Strategy, the following updates will be discard silently
  /// 1. Invalid - Inserting an edge that does not exist in the CFG.
  /// 2. Self-dominance update.
  /// 3. Both DT and PDT are nullptrs.
  /// The Eager Strategy applies the update immediately while the Lazy Strategy
  /// queues the update. It is recommended to only use this method when you have
  /// exactly one insertion (and no deletions) and want to discard an invalid
  /// update.
  void insertEdgeRelaxed(BasicBlock *From, BasicBlock *To);

  /// Notify all available trees on an edge deletion. If both DT and PDT are
  /// nullptrs, this function discards the update. Under either Strategy,
  /// self-dominance update will be removed. The Eager Strategy applies
  /// the update immediately while the Lazy Strategy queues the update.
  /// It is recommended to only use this method when you have exactly one
  /// deletion (and no insertions). It is recommended to use applyUpdates() in
  /// all other cases. This function has to be called *after* making the update
  /// on the actual CFG. An internal functions checks if the edge doesn't exist
  /// in the CFG in DEBUG mode.
  void deleteEdge(BasicBlock *From, BasicBlock *To);

  /// Notify all available trees on an edge deletion.
  /// Under either Strategy, the following updates will be discard silently
  /// 1. Invalid - Deleting an edge that still exists in the CFG.
  /// 2. Self-dominance update.
  /// 3. Both DT and PDT are nullptrs.
  /// The Eager Strategy applies the update immediately while the Lazy Strategy
  /// queues the update. It is recommended to only use this method when you have
  /// exactly one deletion (and no insertions) and want to discard an invalid
  /// update.
  void deleteEdgeRelaxed(BasicBlock *From, BasicBlock *To);

  /// Delete DelBB. DelBB will be removed from its Parent and
  /// erased from available trees if it exists and finally get deleted.
  /// Under Eager UpdateStrategy, DelBB will be processed immediately.
  /// Under Lazy UpdateStrategy, DelBB will be queued until a flush event and
  /// all available trees are up-to-date. Assert if any instruction of DelBB is
  /// modified while awaiting deletion. When both DT and PDT are nullptrs, DelBB
  /// will be queued until flush() is called.
  void deleteBB(BasicBlock *DelBB);

  /// Delete DelBB. DelBB will be removed from its Parent and
  /// erased from available trees if it exists. Then the callback will
  /// be called. Finally, DelBB will be deleted.
  /// Under Eager UpdateStrategy, DelBB will be processed immediately.
  /// Under Lazy UpdateStrategy, DelBB will be queued until a flush event and
  /// all available trees are up-to-date. Assert if any instruction of DelBB is
  /// modified while awaiting deletion. Multiple callbacks can be queued for one
  /// DelBB under Lazy UpdateStrategy.
  void callbackDeleteBB(BasicBlock *DelBB,
                        std::function<void(BasicBlock *)> Callback);

  /// Recalculate all available trees.
  /// Under Lazy Strategy, available trees will only be recalculated if there
  /// are pending updates or there is BasicBlock awaiting deletion. Returns true
  /// if at least one tree is recalculated.
  bool recalculate(Function &F);

  /// Flush DomTree updates and return DomTree.
  /// It also flush out of date updates applied by all available trees
  /// and flush Deleted BBs if both trees are up-to-date.
  /// It must only be called when it has a DomTree.
  DominatorTree &getDomTree();

  /// Flush PostDomTree updates and return PostDomTree.
  /// It also flush out of date updates applied by all available trees
  /// and flush Deleted BBs if both trees are up-to-date.
  /// It must only be called when it has a PostDomTree.
  PostDominatorTree &getPostDomTree();

  /// Apply all pending updates to available trees and flush all BasicBlocks
  /// awaiting deletion.
  /// Does nothing under Eager UpdateStrategy.
  void flush();

  /// Debug method to help view the internal state of this class.
  LLVM_DUMP_METHOD void dump() const;

private:
  class CallBackOnDeletion final : public CallbackVH {
  public:
    CallBackOnDeletion(BasicBlock *V,
                       std::function<void(BasicBlock *)> Callback)
        : CallbackVH(V), DelBB(V), Callback_(Callback) {}

  private:
    BasicBlock *DelBB = nullptr;
    std::function<void(BasicBlock *)> Callback_;

    void deleted() override {
      Callback_(DelBB);
      CallbackVH::deleted();
    }
  };

  SmallVector<DominatorTree::UpdateType, 16> PendUpdates;
  size_t PendDTUpdateIndex = 0;
  size_t PendPDTUpdateIndex = 0;
  DominatorTree *DT = nullptr;
  PostDominatorTree *PDT = nullptr;
  const UpdateStrategy Strategy;
  SmallPtrSet<BasicBlock *, 8> DeletedBBs;
  std::vector<CallBackOnDeletion> Callbacks;
  bool IsRecalculatingDomTree = false;
  bool IsRecalculatingPostDomTree = false;

  /// First remove all the instructions of DelBB and then make sure DelBB has a
  /// valid terminator instruction which is necessary to have when DelBB still
  /// has to be inside of its parent Function while awaiting deletion under Lazy
  /// UpdateStrategy to prevent other routines from asserting the state of the
  /// IR is inconsistent. Assert if DelBB is nullptr or has predecessors.
  void validateDeleteBB(BasicBlock *DelBB);

  /// Returns true if at least one BasicBlock is deleted.
  bool forceFlushDeletedBB();

  /// Deduplicate and remove unnecessary updates (no-ops) when using Lazy
  /// UpdateStrategy. Returns true if the update is queued for update.
  bool applyLazyUpdate(DominatorTree::UpdateKind Kind, BasicBlock *From,
                       BasicBlock *To);

  /// Helper function to apply all pending DomTree updates.
  void applyDomTreeUpdates();

  /// Helper function to apply all pending PostDomTree updates.
  void applyPostDomTreeUpdates();

  /// Helper function to flush deleted BasicBlocks if all available
  /// trees are up-to-date.
  void tryFlushDeletedBB();

  /// Drop all updates applied by all available trees and delete BasicBlocks if
  /// all available trees are up-to-date.
  void dropOutOfDateUpdates();

  /// Erase Basic Block node that has been unlinked from Function
  /// in the DomTree and PostDomTree.
  void eraseDelBBNode(BasicBlock *DelBB);

  /// Returns true if the update appears in the LLVM IR.
  /// It is used to check whether an update is valid in
  /// insertEdge/deleteEdge or is unnecessary in the batch update.
  bool isUpdateValid(DominatorTree::UpdateType Update) const;

  /// Returns true if the update is self dominance.
  bool isSelfDominance(DominatorTree::UpdateType Update) const;
};
} // namespace llvm

#endif // LLVM_DOMTREEUPDATER_H
