/*========================== begin_copyright_notice ============================

Copyright (C) 2017-2021 Intel Corporation

SPDX-License-Identifier: MIT

============================= end_copyright_notice ===========================*/

/*========================== begin_copyright_notice ============================

This file is distributed under the University of Illinois Open Source License.
See LICENSE.TXT for details.

============================= end_copyright_notice ===========================*/

//===-------- DeSSA.hpp - divide phi variables into congruent class -------===//
//
//  Intel Extention to LLVM core
//===----------------------------------------------------------------------===//
//
// This pass is originated from the StrongPHIElimination on the machine-ir.
// We have adopted it to work on llvm-ir. Also note that we have changed it
// from a transformation to an analysis, meaning which only divides phi-vars
// into congruent classes, and does NOT insert the copies. A separate code-gen
// pass can use this analysis to emit non-ssa target code.
//
//===----------------------------------------------------------------------===//

#pragma once

#include "Compiler/CISACodeGen/LiveVars.hpp"
#include "Compiler/CISACodeGen/WIAnalysis.hpp"
#include "Compiler/CISACodeGen/PatternMatchPass.hpp"
#include "Compiler/MetaDataUtilsWrapper.h"
#include "common/LLVMWarningsPush.hpp"
#include <llvm/Pass.h>
#include <llvm/IR/Function.h>
#include <llvm/IR/Instruction.h>
#include <llvm/IR/Instructions.h>
#include <llvm/Support/Allocator.h>
#include <llvm/IR/CFG.h>
#include <llvm/Support/Debug.h>
#include <llvm/IR/Dominators.h>
#include <llvm/ADT/DenseMap.h>
#include <llvm/ADT/MapVector.h>
#include <llvm/ADT/SetVector.h>
#include <llvm/ADT/SmallSet.h>
#include <llvm/ADT/SmallVector.h>
#include <llvm/ADT/DenseSet.h>
#include "common/LLVMWarningsPop.hpp"
#include <map>
#include "Probe/Assertion.h"

namespace IGC {

    class CShader;
    class CVariable;
    class CodeGenPatternMatch;
    class CodeGenContextWrapper;

    class DeSSA : public llvm::FunctionPass {
    public:
        static char ID; // Pass identification, replacement for typeid

        DeSSA();
        virtual void getAnalysisUsage(llvm::AnalysisUsage& AU) const override {
            AU.setPreservesAll();
            AU.addRequired<llvm::DominatorTreeWrapperPass>();
            AU.addRequired<WIAnalysis>();
            AU.addRequired<LiveVarsAnalysis>();
            AU.addRequired<CodeGenPatternMatch>();
            AU.addRequired<llvm::LoopInfoWrapperPass>();
            AU.addRequired<MetaDataUtilsWrapper>();
            AU.addRequired<CodeGenContextWrapper>();
        }
        bool runOnFunction(llvm::Function&) override;

        virtual void releaseMemory() override {
            Allocator.Reset();
            PHISrcDefs.clear();
            PHISrcArgs.clear();
            RegNodeMap.clear();
            InsEltMap.clear();
            AliasMap.clear();
        }

        virtual llvm::StringRef getPassName() const  override {
            return "DeSSA";
        }

        /// Look at the phi-union and insert-element union, find the root value.
        /// return 0 if reg is isolated, and not in any insert-element union
        llvm::Value* getRootValue(llvm::Value*, e_alignment* pAlign = 0) const;

        bool isIsolated(llvm::Value*) const;

        bool isUniform(llvm::Value* v) const {
            return (WIA->isUniform(v));
        }

        void getAllValuesInCongruentClass(
            llvm::Value* V,
            llvm::SmallVector<llvm::Value*, 16> & ValsInCC);

        // getAllValuesInCongruentClass() + aliasMap + insEltMap, etc.
        void getAllCoalescedValues(
            llvm::Value* V,
            llvm::SmallVector<llvm::Value*, 16>& Vals);

        /// print - print partitions in human readable form
        virtual void print(llvm::raw_ostream& OS, const llvm::Module* = 0) const override;

        /// dump - Dump the partitions to dbgs().
        void dump() const;

    private:
        /// This struct represents a single node in the union-find data structure
        /// representing the variable congruence classes. There is one difference
        /// from a normal union-find data structure. We steal two bits from the parent
        /// pointer . One of these bits is used to represent whether the register
        /// itself has been isolated, and the other is used to represent whether the
        /// PHI with that register as its destination has been isolated.
        ///
        /// Note that this leads to the strange situation where the leader of a
        /// congruence class may no longer logically be a member, due to being
        /// isolated.
        struct Node {
            Node(llvm::Value* v, int c, e_alignment align)
                : parent(this), next(this), prev(this), value(v)
                , rank(0), alignment(align), color(c)
            {
            }

            /// Find leader (representative of a congruent class) by path halving
            Node* getLeader();

            Node* parent;
            // double-linked circular list. All values are in the same congruent class
            // except those that have been isolated.
            Node* next;
            Node* prev;
            llvm::Value* value;
            unsigned rank;
            e_alignment alignment;

            // Unique one for each node. Used to represent the color
            // (in another word, id or label) of a congruent class.
            // Start from 1
            int color;
        };

        /// Get the union-root of a register. The root is 0 if the register has been
        /// isolated.
        llvm::Value* getRegRoot(llvm::Value*, e_alignment* pAlign = 0) const;

        // Return color (>0) if V is in a congruent class; return 0 otherwise.
        // (This is needed during traversal of algo. The color is used as the
        // reprentative of a congruent class that remains unchanged during traversal.)
        int getRootColor(llvm::Value* V);

        // Isolate a register.
        void isolateReg(llvm::Value*);

        /// Is it isolated (single-valued congruent class)
        bool isIsolated(Node* N) const { return (N == N->next); }

        // Split node from its existing congurent class, and
        // node itself becomes a new single-value congruent class
        void splitNode(Node* ND);

        /// Traverses a basic block, splitting any interferences found between
        /// registers in the same congruence class. It takes two DenseMaps as
        /// arguments that it also updates: CurrentDominatingParent, which maps
        /// a color to the register in that congruence class whose definition was
        /// most recently seen, and ImmediateDominatingParent, which maps a register
        /// to the register in the same congruence class that most immediately
        /// dominates it.
        ///
        /// This function assumes that it is being called in a depth-first traversal
        /// of the dominator tree.
        void SplitInterferencesForBasicBlock(
            llvm::BasicBlock*,
            llvm::DenseMap<int, llvm::Value*>& CurrentDominatingParent,
            llvm::DenseMap<llvm::Value*, llvm::Value*>& ImmediateDominatingParent);

        void SplitInterferencesForArgument(
            llvm::DenseMap<int, llvm::Value*>& CurrentDominatingParent,
            llvm::DenseMap<llvm::Value*, llvm::Value*>& ImmediateDominatingParent);

        void SplitInterferencesForAlignment();

        llvm::DominatorTree* DT;
        LiveVars* LV;
        WIAnalysis* WIA;
        llvm::LoopInfo* LI;
        CodeGenPatternMatch* CG;
        const llvm::DataLayout* DL;
        CodeGenContext* CTX;
        llvm::Function* m_F;  // Current Function

        llvm::BumpPtrAllocator Allocator;

        // Color (label) assigned to each congruent class
        // start from 1. Make sure each Node has a different
        // color number.
        int CurrColor;

    public:
        LiveVars* getLiveVars() const { return LV; }

        llvm::MapVector<llvm::Value*, Node*> RegNodeMap;

        // Maps a basic block to a list of its defs of registers that appear as PHI
        // sources.
        llvm::DenseMap<llvm::BasicBlock*, std::vector<llvm::Instruction*> > PHISrcDefs;
        llvm::SetVector<llvm::Value*> PHISrcArgs;

        // Maps a color to a pair of a llvm::Instruction* and a virtual register, which
        // is the operand of that PHI corresponding to the current basic block.
        llvm::DenseMap<int, std::pair<llvm::Instruction*, llvm::Value*> > CurrentPHIForColor;

        // Implement reuse for InsertElement only
        // Hierarchical coalescing:
        // - step 1, union insert-elements into trees, update the liveness
        // - step 2, build phi-union, the root of the InsElt-union-tree can be added to the phi-union.
        // - step 3, detect interferences of every phi-union, a set of insert-elements are associated
        //           with a single-node in phi-union. When being isolated, they are isolated together
        llvm::MapVector<llvm::Value*, llvm::Value*> InsEltMap;

        // Value Alias map
        //   This is used for maitaining aliases among values. It maps a value, called 'aliaser',
        //   to its 'aliasee' (denoted as alias(aliaer, aliasee). This map has the following
        //   properties:
        //       1. No alias chain, that is, the map does not have both alias(v0, v1) and
        //          alias(v1, v2) with v0 != V1 != v2.
        //
        //          Note that for each aliasee, say V,  alias(V, V) is in map for convenience to
        //          indicate V is aliasee and to help setup aliasing entry of the map.
        //       2. The aliasee's liveness info has been updated to be the union of all its aliasers.
        //          In this way, only aliasee will be used in DeSSA node.
        //
        // The DeSSA/Coalescing procedure:
        //   1. Follow Dominance tree to set up alias map. While setting up alias map,
        //      update liveness for aliasee.
        //   2. Make sure InsEltMap only use aliasee
        //   3. Make sure DeSSA node only use aliasee.
        llvm::DenseMap<llvm::Value*, llvm::Value*> AliasMap;

        // If an inst is an aliaser and no need to generate code
        // due to aliasing, it will be added in this map.
        llvm::DenseMap<llvm::Value*, int> NoopAliasMap;

        /// If there is no node for Val, create a new one.
        void addReg(llvm::Value* Val, e_alignment Align);

        int getGRFSize() const { return CTX->platform.getGRFSize(); }

        /// union-by-rank:
        ///   Join the congruence classes of two registers by attaching
        ///   a shorter tree to a taller tree. If they have the same height,
        ///   attaching Val2 to Val1. Note that unionRegs() expects that
        ///   nodes for Val1 and Val2 have been created already.
        void unionRegs(llvm::Value* Val1, llvm::Value* Val2) {
            unionRegs(RegNodeMap[Val1], RegNodeMap[Val2]);
        }

        // For a value, return its representative value that is used
        // to create dessa node, which is its aliasee's InsElt root.
        llvm::Value* getNodeValue(llvm::Value* V) const {
            llvm::Value* aliasee = getAliasee(V);
            return getInsEltRoot(aliasee);
        }

        llvm::Value* getInsEltRoot(llvm::Value* Val) const;
        llvm::Value* getAliasee(llvm::Value* V) const;
        bool isAliasee(llvm::Value* V) const;
        bool isAliaser(llvm::Value* V) const;
        bool isNoopAliaser(llvm::Value* V) const;
        bool isSingleValued(llvm::Value* V) const;
        bool interfere(llvm::Value* V0, llvm::Value* V1);
        bool aliasInterfere(llvm::Value* V0, llvm::Value* V1);
        bool alignInterfere(e_alignment a1, e_alignment a2);

    private:
        void CoalesceInsertElements();

        void InsEltMapAddValue(llvm::Value* Val) {
            if (InsEltMap.find(Val) == InsEltMap.end()) {
                InsEltMap[Val] = Val;
            }
        }
        void InsEltMapUnionValue(llvm::Value* SrcVal, llvm::Value* DefVal) {
            IGC_ASSERT(InsEltMap.find(SrcVal) != InsEltMap.end());
            InsEltMap[DefVal] = InsEltMap[SrcVal];
        }

        void unionRegs(Node* N1, Node* N2);

        void CoalesceAliasInst();
        int checkInsertElementAlias(
            llvm::InsertElementInst* IEI,
            llvm::SmallVector<llvm::Value*, 16> & AllIEIs);
        void coalesceAliasInsertValue(llvm::InsertValueInst* theIVI);

        // Add Val->Val into aliasMap if it is not in the map yet.
        // Return Val's aliasee.
        void AddAlias(llvm::Value* Val) {
            if (AliasMap.find(Val) == AliasMap.end())
                AliasMap[Val] = Val;
        }

        // If V is an arg or a needed inst (by patternmatch),
        // return true; otherwise, return false;
        bool isArgOrNeededInst(llvm::Value* V) {
            if (llvm::Instruction * I = llvm::dyn_cast<llvm::Instruction>(V))
            {
                return CG->NeedInstruction(*I);
            }
            return llvm::isa<llvm::Argument>(V);
        }
    };

    struct MIIndexCompare {
        MIIndexCompare(LiveVars* _lv) : LV(_lv) { }

        bool operator()(const llvm::Instruction* LHS, const llvm::Instruction* RHS) const {
            return LV->getDistance(LHS) < LV->getDistance(RHS);
        }

        LiveVars* LV;
    };

} // End CISA namespace
