#include <stdio.h>
#include "opt.h"
#include "misc.h"
#include "analysis.h"
#include "vect.h"

struct loopnode *loops;    /* head of linked list of loops */

// wonso ========================================================================
#include <assert.h> 
typedef int bool;

//#define WSO_DEBUG       // print Debug
#undef WSO_DEBUG        // don't pront Debug

#define Error(fmt, args...) fprintf(stderr, fmt, ## args)

#ifdef WSO_DEBUG
#define Debug(fmt, args...) fprintf(stderr, fmt, ## args)
#else
static inline void Noop() {};
#define Debug(fmt, args...) Noop()
#endif

// opposite to bclear(), set all bit vectors but do not allocate mem. (different from ball())
void bset(bvect sptr)
{
   unsigned int *ptr, *end;
   extern int bvectlen;

   /* clear all bits in each word of the vector */
   end = (ptr = (unsigned int *) sptr) + bvectlen;
   do {
      *(--end) = -1;
   }
   while (ptr != end);
}

bool addblktoloop(struct loopnode* loop, struct bblk *cblk);
void addblkstoloop(struct loopnode* loop, struct bblk *cblk);

// ==============================================================================


/*
 * findloops - locate loops in the program and build the loop data structure
 */
void findloops()
{
  struct bblk *cblk;
  struct blist *bptr;
  //struct assemline *ptr;
  extern struct bblk *top;
  

  bool b_anychage;
  int no_passes = 0;
  bvect bvect_tmp = NULL;

  struct loopnode *loop;

  // Don't have to.. it is done at main() just before call findloops()
  //numberblks(); // Set bvectlen, numblks: bvectlen should be set befroe we use bit vector functions in vect.c

  // loops information is required for each function so delete previous one.
  freeloops();

// ==============================================================================
// STEP1: Set up dominator information
/* Algorithm
dom(top) = {top};
for each block n in {1..N}-{top} do
  dom(n) = {1..N};
do {
  for each block n in {1..N}-{top} do
    ///(DONOTNEED) dom(n) = {1..N};
    for each immed pred p of n do
      dom(n) = dom(n) ? dom(p);
    dom(n) = dom(n) ? {n};
}
while (any change to any dom(n));  
*/

  for (cblk = top; cblk; cblk = cblk->down) // Traverse BBs: Code from check_cf()
  {
    // If top, dom = itself.
    if (cblk ->preds == NULL)
    {
      cblk->dom = bnone();
      binsert(&cblk->dom, cblk->num);
    }
    // Else, dom = all
    else
    {
      cblk->dom = ball();
    }
  }

  bnone(bvect_tmp); // allocate and initialize temporary vector


  // Iteratively apply optimization until there is no change occurs.
  do { 
    b_anychage = FALSE; // This increases every time to perform this optimization
    
    for (cblk = top; cblk; cblk = cblk->down) // Traverse BBs: Code from check_cf()
    {
      if (cblk ->preds) // Except top block
      {
        bcpy(&bvect_tmp, cblk->dom); // Copy original BV.

        // algorithm: for each...
        // (DONOTNEED) bset(cblk->dom);
        for (bptr = cblk->preds; bptr; bptr = bptr->next) // Traverse Predecessors: Code from check_cf()
        {
          binter(&cblk->dom, bptr->ptr->dom); //cblk->dom = cblk->dom INTERSECTION bptr->ptr->dom
        }
        binsert(&cblk->dom, cblk->num); // dom = dom UNION itself.

        if (bequal(bvect_tmp, cblk->dom) == FALSE) b_anychage = TRUE;
      }
    }

   no_passes++;
   //Debug("pass %d: finish setup dominator information\n", no_passes);
  } while (b_anychage);   

  bfree(bvect_tmp);

// ==============================================================================
// STEP2: find loops.
/* Algorithm
addblkstoloop(loop, blk) {
   if (addblktoloop(loop, blk))
      for (each pred p of blk) do
         addblkstoloop(loop, p);
}
...
loop = {};
(void) addblktoloop(loop, head);
addblkstoloop(loop, tail);
*/
  // 1) Detect backedges: 
  /*
  for each bb i
    for each j (- dom(i) // j dominate i
      if (edge i->j) 
      {
        j = header
        perform "Algorithm" above.
      }
  === OR ===
  for each bb i
    for each edge (i->j)
      if ( j (- dom(i) )// j dominate i
      {
        j = header
        perform "Algorithm" above.
      }  
  */

  // First implementation: detect a backedge when traversing a tail of a loop
  // Since a inner loop is detected first, it is printed last.
#if 0  
  for (cblk = top; cblk; cblk = cblk->down) // for each block
  {
    for (bptr = cblk->succs; bptr; bptr = bptr->next) // for each sucessor
    {
      // CFG edge: cblk => bptr->ptr
      // Check bptr->ptr dominate cblk: cblk->dom has bptr->ptr->num bit vector
      if ( bin(cblk->dom, bptr->ptr->num) )
      {
        // Backedge detected: Perform Algorithm
        loop = newloop(); // cread new loop with empty blocks
        addblktoloop(loop, bptr->ptr);
        addblkstoloop(loop, cblk);
        loop->header = bptr->ptr;
        loop->preheader = bptr->ptr->preds->ptr; // Don't have to do.. but just did. (what if multiple predecessors?)
      }
    }
  }
#endif

  // 2nd implementation: detect a backedge when traversing a head of a loop
  // Since a inner loop is detected last, it is printed first. This is the order which we want. 
  // Q. what if there are 2 loops which have the same header? a outer loop is detected first?
  // ////It depends on edge traverse order: The edge of the inner loop is added to the list first, (jump instruction
  // ////appreas first) it is traversed last. It results in the opposite order!
  // ////Take care of this later...
  // A. The question is wrong.. they are not two loops but one loop (from msg board)
  //Backedges leading to the same header are considered part of the same loop. 
  //But you will have to detect each backedge and run the addblockstotooloop() for each tail (same head).
  
  for (cblk = top; cblk; cblk = cblk->down) // for each block
  {
    loop= NULL; 
    for (bptr = cblk->preds; bptr; bptr = bptr->next) // for each predecessor
    {
      // CFG edge: cblk <= bptr->ptr
      // Check cblk dominate bptr->ptr: bptr->ptr->dom has cblk->num bit vector
      if ( bin(bptr->ptr->dom, cblk->num) )
      {
        // Backedge detected: Perform Algorithm
        // Create only one loop per one header (though there are multiple back edges)
        if (!loop) 
        {
          loop = newloop(); // cread new loop with empty blocks
          cblk->loop = loop; //*** this is set only to header
          loop->header = cblk;
          loop->preheader = cblk->preds->ptr; // Don't have to do.. but just did. (what if multiple predecessors?)
          addblktoloop(loop, cblk); // Add a header once.
        }
        // Add other tails per tail.
        addblkstoloop(loop, bptr->ptr);
      }
    }
  }



// ==============================================================================

}

// add cblk to loop block list (== loop->blocks)
// Return: TRUE if blk is added, FALSE if blk is already in blocks
bool addblktoloop(struct loopnode* loop, struct bblk *cblk)
{
  struct blist *bptr;

  // Code from addtoblist: change head to loop->blocks
  
  for (bptr = loop->blocks; bptr; bptr = bptr->next) // Traverse blocks in the loop
  {
    if (bptr->ptr == cblk) return FALSE;
  }

  /* allocate the space for the blist element */
  bptr = (struct blist *) alloc(sizeof(struct blist));

  /* link in the block at the head of the list */
  bptr->ptr = cblk;
  bptr->next = loop->blocks;
  loop->blocks = bptr;

  // wonso : increment loopnest for all blocks in the loop (incremented multiple times if it is nested loop)
  cblk->loopnest++;
  //cblk->loop = // this is only needed for head, thus did this when addblktoloop(head)

   return TRUE;

}

// add blk and all predecessors to loop block list recursively until head (head should be in block list before calling this function)
void addblkstoloop(struct loopnode* loop, struct bblk *cblk)
{
  struct blist *bptr;

  if ( addblktoloop(loop, cblk) )
  {
    for (bptr = cblk->preds; bptr; bptr = bptr->next) // Traverse Predecessors
    {
      addblkstoloop(loop, bptr->ptr);
    }
  }
}


