/*
 * Copyright 2011 Christoph Bumiller
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
 * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#include "nv50_ir.h"
#include "nv50_ir_target.h"

namespace nv50_ir {

// Converts nv50 IR generated from TGSI to SSA form.

// DominatorTree implements an algorithm for finding immediate dominators,
// as described by T. Lengauer & R. Tarjan.
class DominatorTree : public Graph
{
public:
   DominatorTree(Graph *cfg);
   ~DominatorTree() { }
   
   bool dominates(BasicBlock *, BasicBlock *);

   void findDominanceFrontiers();

private:
   void build();
   void buildDFS(Node *);

   void squash(int);
   inline void link(int, int);
   inline int eval(int);

   void debugPrint();

   Graph *cfg;

   Node **vert;
   int *data;
   const int count;

   #define SEMI(i)     (data[(i) + 0 * count])
   #define ANCESTOR(i) (data[(i) + 1 * count])
   #define PARENT(i)   (data[(i) + 2 * count])
   #define LABEL(i)    (data[(i) + 3 * count])
   #define DOM(i)      (data[(i) + 4 * count])
};

void DominatorTree::debugPrint()
{
   for (int i = 0; i < count; ++i) {
      INFO("SEMI(%i) = %i\n", i, SEMI(i));
      INFO("ANCESTOR(%i) = %i\n", i, ANCESTOR(i));
      INFO("PARENT(%i) = %i\n", i, PARENT(i));
      INFO("LABEL(%i) = %i\n", i, LABEL(i));
      INFO("DOM(%i) = %i\n", i, DOM(i));
   }
}

DominatorTree::DominatorTree(Graph *cfgraph) : cfg(cfgraph),
                                               count(cfg->getSize())
{
   int i = 0;

   vert = new Node * [count];
   data = new int[5 * count];

   for (IteratorRef it = cfg->iteratorDFS(true); !it->end(); it->next(), ++i) {
      vert[i] = reinterpret_cast<Node *>(it->get());
      vert[i]->tag = i;
      LABEL(i) = i;
      SEMI(i) = ANCESTOR(i) = -1;
   }

   build();

   delete[] vert;
   delete[] data;
}

void DominatorTree::buildDFS(Graph::Node *node)
{
   SEMI(node->tag) = node->tag;

   for (Graph::EdgeIterator ei = node->outgoing(); !ei.end(); ei.next()) {
      if (SEMI(ei.getNode()->tag) < 0) {
         buildDFS(ei.getNode());
         PARENT(ei.getNode()->tag) = node->tag;
      }
   }
}

void DominatorTree::squash(int v)
{
   if (ANCESTOR(ANCESTOR(v)) >= 0) {
      squash(ANCESTOR(v));

      if (SEMI(LABEL(ANCESTOR(v))) < SEMI(LABEL(v)))
         LABEL(v) = LABEL(ANCESTOR(v));
      ANCESTOR(v) = ANCESTOR(ANCESTOR(v));
   }
}

int DominatorTree::eval(int v)
{
   if (ANCESTOR(v) < 0)
      return v;
   squash(v);
   return LABEL(v);
}

void DominatorTree::link(int v, int w)
{
   ANCESTOR(w) = v;
}

void DominatorTree::build()
{
   DLList *bucket = new DLList[count];
   Node *nv, *nw;
   int p, u, v, w;

   buildDFS(cfg->getRoot());

   for (w = count - 1; w >= 1; --w) {
      nw = vert[w];
      assert(nw->tag == w);
      for (Graph::EdgeIterator ei = nw->incident(); !ei.end(); ei.next()) {
         nv = ei.getNode();
         v = nv->tag;
         u = eval(v);
         if (SEMI(u) < SEMI(w))
            SEMI(w) = SEMI(u);
      }
      p = PARENT(w);
      bucket[SEMI(w)].insert(nw);
      link(p, w);

      for (DLList::Iterator it = bucket[p].iterator(); !it.end(); it.erase()) {
         v = reinterpret_cast<Node *>(it.get())->tag;
         u = eval(v);
         DOM(v) = (SEMI(u) < SEMI(v)) ? u : p;
      }
   }
   for (w = 1; w < count; ++w) {
      if (DOM(w) != SEMI(w))
         DOM(w) = DOM(DOM(w));
   }
   DOM(0) = 0;

   insert(&BasicBlock::get(cfg->getRoot())->dom);
   do {
      p = 0;
      for (v = 1; v < count; ++v) {
         nw = &BasicBlock::get(vert[DOM(v)])->dom;;
         nv = &BasicBlock::get(vert[v])->dom;
         if (nw->getGraph() && !nv->getGraph()) {
            ++p;
            nw->attach(nv, Graph::Edge::TREE);
         }
      }
   } while (p);

   delete[] bucket;
}

#undef SEMI
#undef ANCESTOR
#undef PARENT
#undef LABEL
#undef DOM

void DominatorTree::findDominanceFrontiers()
{
   BasicBlock *bb;

   for (IteratorRef dtIt = iteratorDFS(false); !dtIt->end(); dtIt->next()) {
      EdgeIterator succIt, chldIt;

      bb = BasicBlock::get(reinterpret_cast<Node *>(dtIt->get()));
      bb->getDF().clear();

      for (succIt = bb->cfg.outgoing(); !succIt.end(); succIt.next()) {
         BasicBlock *dfLocal = BasicBlock::get(succIt.getNode());
         if (dfLocal->idom() != bb)
            bb->getDF().insert(dfLocal);
      }

      for (chldIt = bb->dom.outgoing(); !chldIt.end(); chldIt.next()) {
         BasicBlock *cb = BasicBlock::get(chldIt.getNode());

         DLList::Iterator dfIt = cb->getDF().iterator();
         for (; !dfIt.end(); dfIt.next()) {
            BasicBlock *dfUp = BasicBlock::get(dfIt);
            if (dfUp->idom() != bb)
               bb->getDF().insert(dfUp);
         }
      }
   }
}

// liveIn(bb) = usedBeforeAssigned(bb) U (liveOut(bb) - assigned(bb))
void
Function::buildLiveSetsPreSSA(BasicBlock *bb, const int seq)
{
   Function *f = bb->getFunction();
   BitSet usedBeforeAssigned(allLValues.getSize(), true);
   BitSet assigned(allLValues.getSize(), true);

   bb->liveSet.allocate(allLValues.getSize(), false);

   int n = 0;
   for (Graph::EdgeIterator ei = bb->cfg.outgoing(); !ei.end(); ei.next()) {
      BasicBlock *out = BasicBlock::get(ei.getNode());
      if (out == bb)
         continue;
      if (out->cfg.visit(seq))
         buildLiveSetsPreSSA(out, seq);
      if (!n++)
         bb->liveSet = out->liveSet;
      else
         bb->liveSet |= out->liveSet;
   }
   if (!n && !bb->liveSet.marker)
      bb->liveSet.fill(0);
   bb->liveSet.marker = true;

   for (Instruction *i = bb->getEntry(); i; i = i->next) {
      for (int s = 0; i->srcExists(s); ++s)
         if (i->getSrc(s)->asLValue() && !assigned.test(i->getSrc(s)->id))
            usedBeforeAssigned.set(i->getSrc(s)->id);
      for (int d = 0; i->defExists(d); ++d)
         assigned.set(i->getDef(d)->id);
   }

   if (bb == BasicBlock::get(f->cfgExit)) {
      for (std::deque<ValueRef>::iterator it = f->outs.begin();
           it != f->outs.end(); ++it) {
         if (!assigned.test(it->get()->id))
            usedBeforeAssigned.set(it->get()->id);
      }
   }

   bb->liveSet.andNot(assigned);
   bb->liveSet |= usedBeforeAssigned;
}

void
Function::buildDefSetsPreSSA(BasicBlock *bb, const int seq)
{
   bb->defSet.allocate(allLValues.getSize(), !bb->liveSet.marker);
   bb->liveSet.marker = true;

   for (Graph::EdgeIterator ei = bb->cfg.incident(); !ei.end(); ei.next()) {
      BasicBlock *in = BasicBlock::get(ei.getNode());

      if (in->cfg.visit(seq))
         buildDefSetsPreSSA(in, seq);

      bb->defSet |= in->defSet;
   }

   for (Instruction *i = bb->getEntry(); i; i = i->next) {
      for (int d = 0; i->defExists(d); ++d)
         bb->defSet.set(i->getDef(d)->id);
   }
}

class RenamePass
{
public:
   RenamePass(Function *);
   ~RenamePass();

   bool run();
   void search(BasicBlock *);

   inline LValue *getStackTop(Value *);

   LValue *mkUndefined(Value *);

private:
   Stack *stack;
   Function *func;
   Program *prog;
};

bool
Program::convertToSSA()
{
   for (ArrayList::Iterator fi = allFuncs.iterator(); !fi.end(); fi.next()) {
      Function *fn = reinterpret_cast<Function *>(fi.get());
      if (!fn->convertToSSA())
         return false;
   }
   return true;
}

// XXX: add edge from entry to exit ?

// Efficiently Computing Static Single Assignment Form and
//  the Control Dependence Graph,
// R. Cytron, J. Ferrante, B. K. Rosen, M. N. Wegman, F. K. Zadeck
bool
Function::convertToSSA()
{
   // 0. calculate live in variables (for pruned SSA)
   buildLiveSets();

   // 1. create the dominator tree
   domTree = new DominatorTree(&cfg);
   reinterpret_cast<DominatorTree *>(domTree)->findDominanceFrontiers();

   // 2. insert PHI functions
   DLList workList;
   LValue *lval;
   BasicBlock *bb;
   int var;
   int iterCount = 0;
   int *hasAlready = new int[allBBlocks.getSize() * 2];
   int *work = &hasAlready[allBBlocks.getSize()];

   memset(hasAlready, 0, allBBlocks.getSize() * 2 * sizeof(int));

   // for each variable
   for (var = 0; var < allLValues.getSize(); ++var) {
      if (!allLValues.get(var))
         continue;
      lval = reinterpret_cast<Value *>(allLValues.get(var))->asLValue();
      if (!lval || lval->defs.empty())
         continue;
      ++iterCount;

      // TODO: don't add phi functions for values that aren't used outside
      //  the BB they're defined in

      // gather blocks with assignments to lval in workList
      for (Value::DefIterator d = lval->defs.begin();
           d != lval->defs.end(); ++d) {
         bb = ((*d)->getInsn() ? (*d)->getInsn()->bb : NULL);
         if (!bb)
            continue; // instruction likely been removed but not XXX deleted

         if (work[bb->getId()] == iterCount)
            continue;
         work[bb->getId()] = iterCount;
         workList.insert(bb);
      }

      // for each block in workList, insert a phi for lval in the block's
      //  dominance frontier (if we haven't already done so)
      for (DLList::Iterator wI = workList.iterator(); !wI.end(); wI.erase()) {
         bb = BasicBlock::get(wI);

         DLList::Iterator dfIter = bb->getDF().iterator();
         for (; !dfIter.end(); dfIter.next()) {
            Instruction *phi;
            BasicBlock *dfBB = BasicBlock::get(dfIter);

            if (hasAlready[dfBB->getId()] >= iterCount)
               continue;
            hasAlready[dfBB->getId()] = iterCount;

            // pruned SSA: don't need a phi if the value is not live-in
            if (!dfBB->liveSet.test(lval->id))
               continue;

            phi = new_Instruction(this, OP_PHI, typeOfSize(lval->reg.size));
            dfBB->insertTail(phi);

            phi->setDef(0, lval);
            for (int s = 0; s < dfBB->cfg.incidentCount(); ++s)
               phi->setSrc(s, lval);

            if (work[dfBB->getId()] < iterCount) {
               work[dfBB->getId()] = iterCount;
               wI.insert(dfBB);
            }
         }
      }
   }
   delete[] hasAlready;

   RenamePass rename(this);
   return rename.run();
}

RenamePass::RenamePass(Function *fn) : func(fn), prog(fn->getProgram())
{
   stack = new Stack[func->allLValues.getSize()];
}

RenamePass::~RenamePass()
{
   if (stack)
      delete[] stack;
}

LValue *
RenamePass::getStackTop(Value *val)
{
   if (!stack[val->id].getSize())
      return 0;
   return reinterpret_cast<LValue *>(stack[val->id].peek().u.p);
}

LValue *
RenamePass::mkUndefined(Value *val)
{
   LValue *lval = val->asLValue();
   assert(lval);
   LValue *ud = new_LValue(func, lval);
   Instruction *nop = new_Instruction(func, OP_NOP, typeOfSize(lval->reg.size));
   nop->setDef(0, ud);
   BasicBlock::get(func->cfg.getRoot())->insertHead(nop);
   return ud;
}

bool RenamePass::run()
{
   if (!stack)
      return false;
   search(BasicBlock::get(func->domTree->getRoot()));

   return true;
}

void RenamePass::search(BasicBlock *bb)
{
   LValue *lval, *ssa;
   int d, s;
   const Target *targ = prog->getTarget();

   if (bb == BasicBlock::get(func->cfg.getRoot())) {
      for (std::deque<ValueDef>::iterator it = func->ins.begin();
           it != func->ins.end(); ++it) {
         lval = it->get()->asLValue();
         assert(lval);

         ssa = new_LValue(func, targ->nativeFile(lval->reg.file));
         ssa->reg.size = lval->reg.size;
         ssa->reg.data.id = lval->reg.data.id;

         it->setSSA(ssa);
         stack[lval->id].push(ssa);
      }
   }

   for (Instruction *stmt = bb->getFirst(); stmt; stmt = stmt->next) {
      if (stmt->op != OP_PHI) {
         for (s = 0; stmt->srcExists(s); ++s) {
            lval = stmt->getSrc(s)->asLValue();
            if (!lval)
               continue;
            lval = getStackTop(lval);
            if (!lval)
               lval = mkUndefined(stmt->getSrc(s));
            stmt->setSrc(s, lval);
         }
      }
      for (d = 0; stmt->defExists(d); ++d) {
         lval = stmt->def(d).get()->asLValue();
         assert(lval);
         stmt->def(d).setSSA(
            new_LValue(func, targ->nativeFile(lval->reg.file)));
         stmt->def(d).get()->reg.size = lval->reg.size;
         stmt->def(d).get()->reg.data.id = lval->reg.data.id;
         stack[lval->id].push(stmt->def(d).get());
      }
   }

   for (Graph::EdgeIterator ei = bb->cfg.outgoing(); !ei.end(); ei.next()) {
      Instruction *phi;
      int p = 0;
      BasicBlock *sb = BasicBlock::get(ei.getNode());

      // which predecessor of sb is bb ?
      for (Graph::EdgeIterator ei = sb->cfg.incident(); !ei.end(); ei.next()) {
         if (ei.getNode() == &bb->cfg)
            break;
         ++p;
      }
      assert(p < sb->cfg.incidentCount());

      for (phi = sb->getPhi(); phi && phi->op == OP_PHI; phi = phi->next) {
         lval = getStackTop(phi->getSrc(p));
         if (!lval)
            lval = mkUndefined(phi->getSrc(p));
         phi->setSrc(p, lval);
      }
   }

   for (Graph::EdgeIterator ei = bb->dom.outgoing(); !ei.end(); ei.next())
      search(BasicBlock::get(ei.getNode()));

   if (bb == BasicBlock::get(func->cfgExit)) {
      for (std::deque<ValueRef>::iterator it = func->outs.begin();
           it != func->outs.end(); ++it) {
         lval = it->get()->asLValue();
         if (!lval)
            continue;
         lval = getStackTop(lval);
         if (!lval)
            lval = mkUndefined(it->get());
         it->set(lval);
      }
   }

   for (Instruction *stmt = bb->getFirst(); stmt; stmt = stmt->next) {
      if (stmt->op == OP_NOP)
         continue;
      for (d = 0; stmt->defExists(d); ++d)
         stack[stmt->def(d).preSSA()->id].pop();
   }
}

} // namespace nv50_ir
