/*
 * Copyright (C) 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef ART_COMPILER_DEX_POST_OPT_PASSES_H_
#define ART_COMPILER_DEX_POST_OPT_PASSES_H_

#include "dex/quick/mir_to_lir.h"
#include "compiler_internals.h"
#include "pass_me.h"

namespace art {

/**
 * @class InitializeData
 * @brief There is some data that needs to be initialized before performing
 * the post optimization passes.
 */
class InitializeData : public PassME {
 public:
  InitializeData() : PassME("InitializeData") {
  }

  void Start(PassDataHolder* data) const {
    // New blocks may have been inserted so the first thing we do is ensure that
    // the c_unit's number of blocks matches the actual count of basic blocks.
    DCHECK(data != nullptr);
    CompilationUnit* c_unit = down_cast<PassMEDataHolder*>(data)->c_unit;
    DCHECK(c_unit != nullptr);
    c_unit->mir_graph.get()->InitializeBasicBlockData();
    c_unit->mir_graph.get()->SSATransformationStart();
  }
};

/**
 * @class MethodUseCount
 * @brief Count the register uses of the method
 */
class MethodUseCount : public PassME {
 public:
  MethodUseCount() : PassME("UseCount") {
  }

  bool Worker(PassDataHolder* data) const;

  bool Gate(const PassDataHolder* data) const;
};

/**
 * @class ClearPhiInformation
 * @brief Clear the PHI nodes from the CFG.
 */
class ClearPhiInstructions : public PassME {
 public:
  ClearPhiInstructions() : PassME("ClearPhiInstructions") {
  }

  bool Worker(PassDataHolder* data) const;
};

/**
 * @class CalculatePredecessors
 * @brief Calculate the predecessor BitVector of each Basicblock.
 */
class CalculatePredecessors : public PassME {
 public:
  CalculatePredecessors() : PassME("CalculatePredecessors") {
  }

  void Start(PassDataHolder* data) const;
};

/**
 * @class DFSOrders
 * @brief Compute the DFS order of the MIR graph
 */
class DFSOrders : public PassME {
 public:
  DFSOrders() : PassME("DFSOrders") {
  }

  void Start(PassDataHolder* data) const {
    DCHECK(data != nullptr);
    CompilationUnit* c_unit = down_cast<PassMEDataHolder*>(data)->c_unit;
    DCHECK(c_unit != nullptr);
    c_unit->mir_graph.get()->ComputeDFSOrders();
  }
};

/**
 * @class BuildDomination
 * @brief Build the domination information of the MIR Graph
 */
class BuildDomination : public PassME {
 public:
  BuildDomination() : PassME("BuildDomination") {
  }

  void Start(PassDataHolder* data) const {
    DCHECK(data != nullptr);
    CompilationUnit* c_unit = down_cast<PassMEDataHolder*>(data)->c_unit;
    DCHECK(c_unit != nullptr);
    c_unit->mir_graph.get()->ComputeDominators();
    c_unit->mir_graph.get()->CompilerInitializeSSAConversion();
  }

  void End(PassDataHolder* data) const {
    DCHECK(data != nullptr);
    CompilationUnit* c_unit = down_cast<PassMEDataHolder*>(data)->c_unit;
    DCHECK(c_unit != nullptr);
    // Verify the dataflow information after the pass.
    if (c_unit->enable_debug & (1 << kDebugVerifyDataflow)) {
      c_unit->mir_graph->VerifyDataflow();
    }
  }
};

/**
 * @class TopologicalSortOrders
 * @brief Compute the topological sort order of the MIR graph
 */
class TopologicalSortOrders : public PassME {
 public:
  TopologicalSortOrders() : PassME("TopologicalSortOrders") {
  }

  void Start(PassDataHolder* data) const {
    DCHECK(data != nullptr);
    CompilationUnit* c_unit = down_cast<PassMEDataHolder*>(data)->c_unit;
    DCHECK(c_unit != nullptr);
    c_unit->mir_graph.get()->ComputeTopologicalSortOrder();
  }
};

/**
 * @class DefBlockMatrix
 * @brief Calculate the matrix of definition per basic block
 */
class DefBlockMatrix : public PassME {
 public:
  DefBlockMatrix() : PassME("DefBlockMatrix") {
  }

  void Start(PassDataHolder* data) const {
    DCHECK(data != nullptr);
    CompilationUnit* c_unit = down_cast<PassMEDataHolder*>(data)->c_unit;
    DCHECK(c_unit != nullptr);
    c_unit->mir_graph.get()->ComputeDefBlockMatrix();
  }
};

/**
 * @class CreatePhiNodes
 * @brief Pass to create the phi nodes after SSA calculation
 */
class CreatePhiNodes : public PassME {
 public:
  CreatePhiNodes() : PassME("CreatePhiNodes") {
  }

  void Start(PassDataHolder* data) const {
    DCHECK(data != nullptr);
    CompilationUnit* c_unit = down_cast<PassMEDataHolder*>(data)->c_unit;
    DCHECK(c_unit != nullptr);
    c_unit->mir_graph.get()->InsertPhiNodes();
  }
};

/**
 * @class ClearVisitedFlag
 * @brief Pass to clear the visited flag for all basic blocks.
 */

class ClearVisitedFlag : public PassME {
 public:
  ClearVisitedFlag() : PassME("ClearVisitedFlag") {
  }

  void Start(PassDataHolder* data) const {
    DCHECK(data != nullptr);
    CompilationUnit* c_unit = down_cast<PassMEDataHolder*>(data)->c_unit;
    DCHECK(c_unit != nullptr);
    c_unit->mir_graph.get()->ClearAllVisitedFlags();
  }
};

/**
 * @class SSAConversion
 * @brief Pass for SSA conversion of MIRs
 */
class SSAConversion : public PassME {
 public:
  SSAConversion() : PassME("SSAConversion") {
  }

  void Start(PassDataHolder* data) const {
    DCHECK(data != nullptr);
    CompilationUnit* c_unit = down_cast<PassMEDataHolder*>(data)->c_unit;
    DCHECK(c_unit != nullptr);
    MIRGraph *mir_graph = c_unit->mir_graph.get();
    mir_graph->DoDFSPreOrderSSARename(mir_graph->GetEntryBlock());
  }
};

/**
 * @class PhiNodeOperands
 * @brief Pass to insert the Phi node operands to basic blocks
 */
class PhiNodeOperands : public PassME {
 public:
  PhiNodeOperands() : PassME("PhiNodeOperands", kPreOrderDFSTraversal) {
  }

  bool Worker(PassDataHolder* data) const {
    DCHECK(data != nullptr);
    CompilationUnit* c_unit = down_cast<PassMEDataHolder*>(data)->c_unit;
    DCHECK(c_unit != nullptr);
    BasicBlock* bb = down_cast<PassMEDataHolder*>(data)->bb;
    DCHECK(bb != nullptr);
    c_unit->mir_graph->InsertPhiNodeOperands(bb);
    // No need of repeating, so just return false.
    return false;
  }
};

/**
 * @class InitRegLocations
 * @brief Initialize Register Locations.
 */
class PerformInitRegLocations : public PassME {
 public:
  PerformInitRegLocations() : PassME("PerformInitRegLocation") {
  }

  void Start(PassDataHolder* data) const {
    DCHECK(data != nullptr);
    CompilationUnit* c_unit = down_cast<PassMEDataHolder*>(data)->c_unit;
    DCHECK(c_unit != nullptr);
    c_unit->mir_graph->InitRegLocations();
  }
};

/**
 * @class ConstantPropagation
 * @brief Perform a constant propagation pass.
 */
class ConstantPropagation : public PassME {
 public:
  ConstantPropagation() : PassME("ConstantPropagation") {
  }

  bool Worker(PassDataHolder* data) const {
    DCHECK(data != nullptr);
    CompilationUnit* c_unit = down_cast<PassMEDataHolder*>(data)->c_unit;
    DCHECK(c_unit != nullptr);
    BasicBlock* bb = down_cast<PassMEDataHolder*>(data)->bb;
    DCHECK(bb != nullptr);
    c_unit->mir_graph->DoConstantPropagation(bb);
    // No need of repeating, so just return false.
    return false;
  }

  void Start(PassDataHolder* data) const {
    DCHECK(data != nullptr);
    CompilationUnit* c_unit = down_cast<PassMEDataHolder*>(data)->c_unit;
    DCHECK(c_unit != nullptr);
    c_unit->mir_graph->InitializeConstantPropagation();
  }
};

/**
 * @class FreeData
 * @brief There is some data that needs to be freed after performing the post optimization passes.
 */
class FreeData : public PassME {
 public:
  FreeData() : PassME("FreeData") {
  }

  void End(PassDataHolder* data) const {
    DCHECK(data != nullptr);
    CompilationUnit* c_unit = down_cast<PassMEDataHolder*>(data)->c_unit;
    DCHECK(c_unit != nullptr);
    c_unit->mir_graph.get()->SSATransformationEnd();
  }
};

}  // namespace art

#endif  // ART_COMPILER_DEX_POST_OPT_PASSES_H_
