//===-- SPIRVDuplicatesTracker.cpp - SPIR-V Duplicates Tracker --*- C++ -*-===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// General infrastructure for keeping track of the values that according to
// the SPIR-V binary layout should be global to the whole module.
//
//===----------------------------------------------------------------------===//

#include "SPIRVDuplicatesTracker.h"

using namespace llvm;

template <typename T>
void SPIRVGeneralDuplicatesTracker::prebuildReg2Entry(
    SPIRVDuplicatesTracker<T> &DT, SPIRVReg2EntryTy &Reg2Entry) {
  for (auto &TPair : DT.getAllUses()) {
    for (auto &RegPair : TPair.second) {
      const MachineFunction *MF = RegPair.first;
      Register R = RegPair.second;
      MachineInstr *MI = MF->getRegInfo().getUniqueVRegDef(R);
      if (!MI)
        continue;
      Reg2Entry[&MI->getOperand(0)] = &TPair.second;
    }
  }
}

void SPIRVGeneralDuplicatesTracker::buildDepsGraph(
    std::vector<SPIRV::DTSortableEntry *> &Graph,
    MachineModuleInfo *MMI = nullptr) {
  SPIRVReg2EntryTy Reg2Entry;
  prebuildReg2Entry(TT, Reg2Entry);
  prebuildReg2Entry(CT, Reg2Entry);
  prebuildReg2Entry(GT, Reg2Entry);
  prebuildReg2Entry(FT, Reg2Entry);
  prebuildReg2Entry(AT, Reg2Entry);
  prebuildReg2Entry(ST, Reg2Entry);

  for (auto &Op2E : Reg2Entry) {
    SPIRV::DTSortableEntry *E = Op2E.second;
    Graph.push_back(E);
    for (auto &U : *E) {
      const MachineRegisterInfo &MRI = U.first->getRegInfo();
      MachineInstr *MI = MRI.getUniqueVRegDef(U.second);
      if (!MI)
        continue;
      assert(MI && MI->getParent() && "No MachineInstr created yet");
      for (auto i = MI->getNumDefs(); i < MI->getNumOperands(); i++) {
        MachineOperand &Op = MI->getOperand(i);
        if (!Op.isReg())
          continue;
        MachineOperand *RegOp = &MRI.getVRegDef(Op.getReg())->getOperand(0);
        assert((MI->getOpcode() == SPIRV::OpVariable && i == 3) ||
               Reg2Entry.count(RegOp));
        if (Reg2Entry.count(RegOp))
          E->addDep(Reg2Entry[RegOp]);
      }

      if (E->getIsFunc()) {
        MachineInstr *Next = MI->getNextNode();
        if (Next && (Next->getOpcode() == SPIRV::OpFunction ||
                     Next->getOpcode() == SPIRV::OpFunctionParameter)) {
          E->addDep(Reg2Entry[&Next->getOperand(0)]);
        }
      }
    }
  }

#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
  if (MMI) {
    const Module *M = MMI->getModule();
    for (auto F = M->begin(), E = M->end(); F != E; ++F) {
      const MachineFunction *MF = MMI->getMachineFunction(*F);
      if (!MF)
        continue;
      for (const MachineBasicBlock &MBB : *MF) {
        for (const MachineInstr &CMI : MBB) {
          MachineInstr &MI = const_cast<MachineInstr &>(CMI);
          MI.dump();
          if (MI.getNumExplicitDefs() > 0 &&
              Reg2Entry.count(&MI.getOperand(0))) {
            dbgs() << "\t[";
            for (SPIRV::DTSortableEntry *D :
                 Reg2Entry.lookup(&MI.getOperand(0))->getDeps())
              dbgs() << Register::virtReg2Index(D->lookup(MF)) << ", ";
            dbgs() << "]\n";
          }
        }
      }
    }
  }
#endif
}
