/*****************************************************************************/
/*                                                                           */
/* File: loop_bound_analysis.c                                               */
/*                                                                           */
/* Created: Fri Oct 28 15:50:28 2005                                         */
/*                                                                           */
/* Author: Balaji V. Iyer and Won So                                         */
/*                                                                           */
/* Revision: $Id: loop_bound_analysis.c,v 1.5 2005/11/29 00:28:32 bviyer Exp $ */
/*                                                                           */
/* Description:$Log: loop_bound_analysis.c,v $                               */
/* Description:Revision 1.5  2005/11/29 00:28:32  bviyer                     */
/* Description:Fixed the GE and LE case                                      */
/* Description:                                                              */
/* Description:Revision 1.4  2005/11/24 00:51:15  bviyer                     */ 
/* Description:Didn't take care of "sra" instruction. Fixed this.            */
/* Description:                                                              */
/* Description:Revision 1.3  2005/11/23 19:05:46  bviyer                     */
/* Description:Fixed some spelling mistakes in print statements.             */
/* Description:                               */
/* Description:Revision 1.2  2005/11/23 15:54:37  wso			     */
/* Description:no message						     */
/* Description: */
/* Description:Revision 1.6  2005/11/22 22:30:58  bviyer */
/* Description:Fixed all the errors I know */
/* Description:                               */
/* Description:Revision 1.5  2005/11/21 05:02:32  bviyer                     */
/* Description:Fixed the updated you wanted Won.                             */
/* Description:                                                              */
/* Description:Revision 1.4  2005/11/15 20:12:40  wso 		             */
/* Description:no message                                                    */
/* Description:                               */
/* Description:Revision 1.2  2005/11/04 19:00:03  bviyer                     */
/* Description:*** empty log message ***                                     */
/* Description:                                                              */
/*                                                                           */
/*****************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <string.h>
#include <math.h>
#include <assert.h>
#include "opt.h"
#include "misc.h"

#define DEBUG 0

int Contains_BBlk(struct blist *, struct bblk *);
int find_initial_value (struct bblk *, char *, int *, char **);
char *find_loop_var_addr (struct bblk *);
int find_loop_bound(struct bblk *, int *, char **, enum CMP_Type *);
int find_loop_increment (struct blist *, char *, int , int, char **, int *);
int is_register(char *reg_value);
int find_in_list (char *, int *);
int Modified_Bound_Value_In_Loop (struct blist *, char*);

int is_nested (struct loopnode *, struct loopnode *);
int find_in_pred(struct bblk *b_block, char *loop_reg, int *ret_value);
void find_nested(void);
void is_rect(void);

int found_li(char *i_var, struct bblk *b_block, int *val);

struct bblk *root_node=NULL;
void find_nested(void)
{
  struct loopnode *loop_ptr = NULL;
  struct loopnode *loop_ptr2 = NULL;
  int ii = 0;
  int jj = 0;
  for (loop_ptr = loops; loop_ptr != NULL; loop_ptr = loop_ptr->next)
  {
    ii++;
    loop_ptr->loop_no = ii;
    loop_ptr->count = 0;
    for (jj = 0; jj < MAX_NESTED_LOOPS; jj++)
    {
      loop_ptr->nested[jj]=0;
    }
  }
  
  for (loop_ptr = loops; loop_ptr != NULL; loop_ptr = loop_ptr->next)
  {
    for (loop_ptr2=loops; loop_ptr2 != NULL; loop_ptr2 = loop_ptr2->next)
    {
      if (loop_ptr2 != loop_ptr)
      {
	if (is_nested(loop_ptr, loop_ptr2))
	{
	  assert (loop_ptr->count < MAX_NESTED_LOOPS);
	  loop_ptr->nested[loop_ptr->count] = loop_ptr2->loop_no;
	  loop_ptr->nested_ptr[loop_ptr->count] = loop_ptr2; 
	  loop_ptr->count++;
	}
      }
    }
  }
  return;
}

char *find_induction_var (int loop_no)
{
  struct loopnode *loopnode_ptr=NULL;

  for (loopnode_ptr = loops; loopnode_ptr != NULL;
       loopnode_ptr = loopnode_ptr->next)
  {
    if (loopnode_ptr->loop_no == loop_no)
    {
      return loopnode_ptr->loop_var_addr;
    }
  }
  return NULL;
}

void is_rect (void)
{
  struct loopnode *loopnode_ptr=NULL;
  char *ind_var = NULL;
  int ii = 0;

  for (loopnode_ptr = loops; loopnode_ptr != NULL;
       loopnode_ptr = loopnode_ptr->next)
  {
    loopnode_ptr->rectangular = TRUE;
    for (ii = 0; ii < loopnode_ptr->count; ii++)
    {
      loopnode_ptr->dependent[ii] = FALSE;
    }
  }

  
  for (loopnode_ptr = loops; loopnode_ptr != NULL;
       loopnode_ptr = loopnode_ptr->next)
  {
    if ((loopnode_ptr->init_val_type == LOOP_GLOBAL) ||
	(loopnode_ptr->init_val_type == LOOP_REGISTER))
    {
      for (ii = 0; ii < loopnode_ptr->count; ii++)
      {
	if (find_induction_var(loopnode_ptr->nested[ii]) != NULL)
	{
	  ind_var = strdup(find_induction_var(loopnode_ptr->nested[ii]));
	}
	if ((ind_var != NULL) &&
	    (loopnode_ptr->init_val_reg != NULL))
	{
	  /* this if loop will check to see if one nested loop is dependent
	   * on the other one. The loopnode_ptr->nested_ptr will keep a
	   * pointer of all the loops that are superseedinng the current loop.
	   * for example:
	   * LOOP1: for (ii = 0; ii < 10; ii++)
	   * LOOP2:    for (jj = 0; jj < 20; jj++)
	   * LOOP3:      for (kk = 0; kk < ii; kk++)

	   * these re the values of the nested, nested_ptr and dependent
	   * field's values (Please note that: &x => address of x)
	   *
	   * loopnode_ptr->count = 2;
	   *
	   * index      nested           nested_ptr            dependent
	   *      
	   * 0            2                &LOOP2                 FALSE
	   * 1            1                &LOOP1                 TRUE
	   * 2-31         ----             NULL                   -----
	   *
	   */
	  if (strcmp(loopnode_ptr->init_val_reg, ind_var) == 0)
	  {
	    free (ind_var);
	    loopnode_ptr->rectangular = FALSE;
	    loopnode_ptr->dependent[ii] = TRUE;
	    // wonso: set rectangular for dependent loop also
	    loopnode_ptr->nested_ptr[ii]->rectangular = FALSE;
	  }
	}
      }
    }
    if ((loopnode_ptr->final_val_type == LOOP_GLOBAL) ||
	(loopnode_ptr->final_val_type == LOOP_REGISTER))
    {
      if (ind_var != NULL)
      {
	free(ind_var);
        ind_var = NULL;
      }
      for (ii = 0; ii < loopnode_ptr->count; ii++)
      {
	if (find_induction_var (loopnode_ptr->nested[ii]) != NULL)
	{  
	  ind_var = strdup(find_induction_var (loopnode_ptr->nested[ii]));
	}
	if ((ind_var != NULL) &&
	    (loopnode_ptr->final_val_reg != NULL))
	{
	  if (strcmp (loopnode_ptr->final_val_reg, ind_var) == 0)
	  {
	    free(ind_var);
	    loopnode_ptr->rectangular = FALSE;
	    loopnode_ptr->dependent[ii] = TRUE;
	    // wonso: set rectangular for dependent loop also
	    loopnode_ptr->nested_ptr[ii]->rectangular = FALSE;
	  }
	}
      }
    }
  }
  return;
}

int is_nested(struct loopnode *node, struct loopnode *node2)
{
  struct blist *temp_blk = NULL;

  for (temp_blk = node->blocks; temp_blk != NULL;
       temp_blk = temp_blk->next)
  {
    if (!inblist(node2->blocks, temp_blk->ptr))
    {
      return FALSE;
    }
  }
  return TRUE;
}


int find_in_list (char *name, int *value)
{
  struct globalinfo *info_ptr=NULL;
  if (name == NULL)
  {
    printf("value == NULL\n");
    return FALSE;
  }

  for (info_ptr = globals; info_ptr != NULL; info_ptr = info_ptr->next)
  {
    if (strcmp(info_ptr->name, name) == 0)
    {
      *value = info_ptr->value;
      return TRUE;
    }
  }
  return FALSE;
}
    
int is_register(char *reg_value)
{
  if (reg_value == NULL)
  {
    return FALSE;
  }
  if (reg_value[0] == '$')
  {
    return TRUE;
  }
  return FALSE;
}

int Modified_Bound_Value_In_Loop (struct blist *loop_block, char *g_var)
{
  struct blist *temp_loop_blk = NULL;
  struct assemline *asm_ptr = NULL;
  
  if (loop_block == NULL)
  {
    printf("Loop block == NULL\n");
    return FALSE;
  }

  if (g_var == NULL)
  {
    printf("global variable is NULL\n");
    return FALSE;
  }

  for (temp_loop_blk = loop_block;
       temp_loop_blk != NULL;
       temp_loop_blk = temp_loop_blk->next)
  {
    asm_ptr = temp_loop_blk->ptr->lines;

    while (asm_ptr != NULL)
    {
      if (((strcmp(asm_ptr->items[0], "sb")  == 0) &&
	   (strcmp(asm_ptr->items[2], g_var) == 0)) ||
	  ((strcmp(asm_ptr->items[0], "sh")  == 0) &&
	   (strcmp(asm_ptr->items[2], g_var) == 0)) ||
	  ((strcmp(asm_ptr->items[0], "sw")  == 0) &&
	   (strcmp(asm_ptr->items[2], g_var) == 0)))
      {
	return FALSE;
      }
      if (asm_ptr == temp_loop_blk->ptr->lineend)
      {
	break;
      }
      asm_ptr = asm_ptr->next;
    }
  }
  return TRUE;
}

int Contains_Bblk(struct blist *loop_block, struct blist *block)
{
  struct blist *temp_loop_blk = NULL;

  if (loop_block == NULL)
  {
    printf("loop block is NULL\n");
    return FALSE;
  }
  
  if (block == NULL)
  {
    printf("block is NULL\n");
    return FALSE;
  }
  
  for (temp_loop_blk = loop_block; temp_loop_blk != NULL;
       temp_loop_blk = temp_loop_blk->next)
  {
    if (temp_loop_blk->ptr->num == block->ptr->num)
    {
      return TRUE;
    }
  }
  return FALSE;
}

void find_simple_loop_bounds(void)
{
  struct loopnode *loopnode_ptr= NULL;
  struct blist *blk_ptr        = NULL;
  struct blist *succ_ptr       = NULL;
  struct blist *pred_ptr       = NULL;
  int init_val                 = 0;
  int final_val                = 0;
  int loop_bound_found         = FALSE;
  int loop_var_addr_found      = FALSE;
  extern struct bblk *top;
  find_nested();
  
  /* for each loops in the code */
  for (loopnode_ptr = loops; loopnode_ptr != NULL;
       loopnode_ptr = loopnode_ptr->next)
  {
    /* initializing the variables that will indicate whether we found
     * a value for loop bound number and the induction variable 
     */
    loop_var_addr_found = FALSE;
    loop_bound_found = FALSE;
    
    /* for each block in the loops */
    for  (blk_ptr = loopnode_ptr->blocks; blk_ptr != NULL;
	  blk_ptr = blk_ptr->next)
    {
#if DEBUG

#endif
      

      /* for each successor for the blk_ptr */
      for (succ_ptr = blk_ptr->ptr->succs; succ_ptr != NULL;
	   succ_ptr = succ_ptr->next)
      {
	
	/* this will check that block that controls the whole
	 * loop.
	 * If we have a case where the successors of a basic block is not
	 * contained in the loop list then we can say that the block controls
	 * the whole Loop
	 * THIS ONLY WORKDS FOR SINGLE EXIT LOOPS 
	 */
	if ((Contains_Bblk(loopnode_ptr->blocks, succ_ptr) == FALSE) &&
	    (strcmp(succ_ptr->ptr->lineend->items[0], "jal") != 0))
	{
	  
	  /* calculating the loop bound variable */
          if (find_loop_var_addr(blk_ptr->ptr))
	  {
	    loopnode_ptr->loop_var_addr =
	      strdup(find_loop_var_addr(blk_ptr->ptr));
	  }
	  /* we indicate that we found the loop induction variable */
	  loop_var_addr_found = TRUE;
	  for (pred_ptr = loopnode_ptr->header->preds; pred_ptr != NULL;
	       pred_ptr = pred_ptr->next)
	  {
	    /* this fi loop looks for the block that was right above
	     * root node. This must be the place where they have initialized
	     * the induction variable
	     */
	    if (Contains_Bblk(blk_ptr, pred_ptr) == FALSE)
	    {
	      /* What we are doing is this :
	       * for a code similar to this:
	       *    for (ii = INITIAL_VALUE; ii != FINAL_VALUE; modify ii)
	       *      - the init val gives you the initial value
	       *      - the find_loop_bound() function give the final value
	       *      - FINAL_VALUE minus INITIAL_VALUE gives the number of
	       *        times we need to through the loop.
	       *        
	       */

	      /* the init_val_type can have 4 values:
	       *  Ths table will tell you what are the correct values they
	       * can hold

	       * THIS APPLIES TO FINAL VALUE TOO (just replace init_val with
	       * final_val
	       * 
	       * init_val_type |   init_val     | init_val_reg   |
               *               |                |                |
	       * LOOP_CONSTANT |   VALID        | GARBAGE        |
	       *               |                |                |
	       * LOOP_GLOBAL   |   GARBAGE      | VALID          |
	       *               |                |                |
	       * LOOP_REGISTER |   GARBAGE      | VALID          |
	       *               |                |                |
	       * LOOP_UNDEFINED|   GARBAGE      | GARBAGE        |
	       */    
	      loopnode_ptr->init_val_type =
		find_initial_value(pred_ptr->ptr,
				   loopnode_ptr->loop_var_addr,
				   &init_val,
				   &loopnode_ptr->init_val_reg);
	      loopnode_ptr->init_val = init_val;
	      /*
	      if ((loopnode_ptr->init_val_type == LOOP_REGISTER) &&
		  (loopnode_ptr->init_val_reg == NULL))
	      {
		loopnode_ptr->init_val_type =
		  find_initial_value(top,
				     loopnode_ptr->loop_var_addr,
				     &init_val,
				     &loopnode_ptr->init_val_reg);
		loopnode_ptr->init_val = init_val;
		} */
	  
	    }
	    if ((loopnode_ptr->init_val_type == LOOP_REGISTER) &&
		(loopnode_ptr->init_val_reg == NULL))
	    {
	      loopnode_ptr->init_val_type =
		find_initial_value(top,
				   loopnode_ptr->loop_var_addr,
				   &init_val,
				   &loopnode_ptr->init_val_reg);
	      loopnode_ptr->init_val = init_val;
	    }
	    /* THe loop bound is found here 
	     */
	    loopnode_ptr->final_val_type =
	      find_loop_bound(blk_ptr->ptr,&final_val,
			      &loopnode_ptr->final_val_reg,
			      &loopnode_ptr->compare_type);
	    
	    loopnode_ptr->final_val = final_val;
	  }
	 
	  /* this if loop will make sure if it is a valid for loop or not
	   * The criterian we use use is this:
	   * if the if loop has a final bound that is hold by a non-constant
	   * value, then we check to see if the value is updated anywhere
	   * in the loop. The way the global value is updated is by using
	   * a store on the global value variable (eg. sw $3,z). We find this
	   * pattern
	   */
	  if ((loopnode_ptr->final_val_type == LOOP_GLOBAL) ||
	      (loopnode_ptr->final_val_type == LOOP_REGISTER))
	  {
	    loopnode_ptr->predictable =
	      Modified_Bound_Value_In_Loop(loopnode_ptr->blocks,
					   loopnode_ptr->final_val_reg);
	  }
	  else
	  {
	    loopnode_ptr->predictable = TRUE;
	  }
	  
	  /* for rectangular loops */
	  if (((loopnode_ptr->final_val_type == LOOP_CONSTANT) ||
	       (loopnode_ptr->final_val_type == LOOP_GLOBAL)) &&
	      ((loopnode_ptr->init_val_type  == LOOP_GLOBAL) || 
	       (loopnode_ptr->init_val_type  == LOOP_CONSTANT)))
	  {
	    /* bviyer: change this to be more restrictive */
	    loopnode_ptr->loop_bound=abs(final_val-init_val);

	    /* if it is Greater than or less athn and equal to, we need to
	     * include one more time. So we increment this value 
	     */
	    if ((loopnode_ptr->compare_type == GE) ||
		(loopnode_ptr->compare_type == LE))
	    {
	      loopnode_ptr->loop_bound++;
	    }

	    loopnode_ptr->init_val = init_val;
	    loopnode_ptr->final_val = final_val;
	    loop_bound_found = TRUE; /* we indicate that we found loop bound */
	  }
	}
      }
    }
    if ((loop_bound_found    == TRUE) &&
	(loop_var_addr_found == TRUE))
    {
      /* calculating the number of times we go through the loop */
      loopnode_ptr->loop_inc =
	find_loop_increment(loopnode_ptr->blocks,
			    loopnode_ptr->loop_var_addr,
			    loopnode_ptr->init_val,
			    loopnode_ptr->final_val,
			    &loopnode_ptr->inc_type, 
			    &loopnode_ptr->inc_value);
    }
    else
    {

      /* this else statement is only there to find the appropriate
       * loop_inc_type
       */
      loopnode_ptr->loop_inc =
	find_loop_increment(loopnode_ptr->blocks,
			    loopnode_ptr->loop_var_addr,
			    0,  /* dummy value */
			    10, /* dummy value */
			    &loopnode_ptr->inc_type,
			    &loopnode_ptr->inc_value);
      
      loopnode_ptr->loop_inc = -1; /* clearing this value */
    }
    if (loopnode_ptr->init_val_type == LOOP_CONSTANT)
    {
      free (loopnode_ptr->init_val_reg);
      loopnode_ptr->init_val_reg = NULL;
    }  
    if (loopnode_ptr->final_val_type == LOOP_CONSTANT)
    {
      free (loopnode_ptr->final_val_reg);
      loopnode_ptr->final_val_reg = NULL;
    }
  }
  is_rect();

  return;
}

/*************************************************************************
 * This function will find the initial value for the the loop value
 * Inputs: blocks in a loop, and induction variable
 * Output: int value for the initial value
 *************************************************************************
 */
int find_initial_value (struct bblk *b_block, char *i_var, int *initial_value,
			char **loop_reg)
{
  struct assemline *asm_ptr=NULL;
  int addr = 3;
  int temp_value=0;
  char *induction_var=NULL;
  //char *tmp_reg=NULL;
  int x = 0;
  // struct bblk *temp_blk=NULL;
  // extern struct bblk *top;

  if (i_var == NULL)
  {
    printf("INvalid induction variable\n");
    return 0;
  }

  if (b_block == NULL)
  {
    printf("b_block = NULL\n");
    return 0;
  }
  
  induction_var = strdup (i_var);
  if (induction_var == NULL)
  {
    printf("INvalid induction variable\n");
    return 0;
  }

  /* this if loop checks if it is an address or register where induction
   * variable is stored
   */
  if (induction_var[0] == '$')
  {
    addr = FALSE;
  }
  else
  {
    addr = TRUE;
  }

  /* we start from the last instruction and go all the way to the first
   * instruction
   */
  asm_ptr = b_block->lineend;
  
  while (asm_ptr != NULL)
  {
    if (addr == FALSE)
    {
      if ((asm_ptr->type != COMMENT_LINE) &&
	  (strcmp(asm_ptr->items[0], "li") == 0) &&
	  (strcmp(asm_ptr->items[1], induction_var) == 0))
      {
	*initial_value = strtol(asm_ptr->items[2],NULL,16);
	return LOOP_CONSTANT;
      }
      if ((asm_ptr->type != COMMENT_LINE) &&
	  (strcmp(asm_ptr->items[0], "move") == 0) &&
	  (strcmp(asm_ptr->items[1], induction_var) == 0) &&
	  (strcmp(asm_ptr->items[2], "$0") == 0))
      {
	*initial_value = 0;
	return LOOP_CONSTANT;
      }
      if ((asm_ptr->type != COMMENT_LINE) &&
	  (strcmp(asm_ptr->items[0], "move") == 0) &&
	  (strcmp(asm_ptr->items[1], induction_var) == 0) &&
	  (strcmp(asm_ptr->items[2], "$0") != 0))
      {
	*loop_reg = strdup(asm_ptr->items[2]);
	return LOOP_REGISTER;
      }
      if ((asm_ptr->type == LOAD_INST) &&
	  ((strcmp(asm_ptr->items[0], "lb" ) == 0) ||
	   (strcmp(asm_ptr->items[0], "lbu") == 0) ||
	   (strcmp(asm_ptr->items[0], "lh" ) == 0) ||
	   (strcmp(asm_ptr->items[0], "lhu") == 0) ||
	   (strcmp(asm_ptr->items[0], "lw" ) == 0)) &&
	  (strcmp(asm_ptr->items[1], induction_var) == 0))
      {
	if (find_in_list(asm_ptr->items[2], &temp_value) == TRUE)
	{
	  *initial_value = temp_value;
	  *loop_reg = strdup(asm_ptr->items[2]);
	  return LOOP_GLOBAL;
	}
	else
	{
	  *initial_value = 0;
	  *loop_reg = strdup(asm_ptr->items[2]);
	  return LOOP_REGISTER;
	}
      }
    }
    else
    {
      /* This will find the initialization case where the induction variable
       * is  astack pointer.
       * We limit our program to having 2 kinds of cases:
       * 1) Storing a zero into the memory location
       * 2) storing the value from a register
       *    PROVIDED THAT THE REGISTER IS SET WITH A FIXED VALUE BY LOADING
       *    AN IMMIDATE VALUE TO IT. THE DETAILS ARE EXPLAINED ABOUT 10 LINES
       *    BELOW.
       */
      if ((asm_ptr->type == STORE_INST) &&
	  (strcmp(asm_ptr->items[2], induction_var) == 0))
      {
	if (strcmp(asm_ptr->items[1], "$0") == 0)
	{
	  *initial_value = 0;
	  return LOOP_CONSTANT;
	}
	else
	{
	  /* what I am doing here is this:
	   * if we have a case like this:
	   * li $REG, SOME_NUMBER
	   * sw $REG, INDUCTION_VARIABLE_PLACE
	   *
	   * this loop will first catch the store,then it has to look for
	   * a place where the $REG is set to some value. The best way to do
	   * this is toi give an illusion for this routine that $REG is the
	   * induction regiser and then find that register
	   *
	   */
	  free(induction_var);
	  induction_var = strdup(asm_ptr->items[1]);
	  addr = FALSE;
	}
      }
    }
    if (asm_ptr == b_block->lines)
    {/*
      for (temp_blk = top; temp_blk != b_block; 
           temp_blk = temp_blk->down)
      {
        x = find_initial_value(temp_blk, i_var, initial_value,loop_reg);
        if ((x == LOOP_CONSTANT) || 
            ((x == LOOP_REGISTER) &&
             (*loop_reg != NULL)))
          return x;
      } 
      */
       
     

      
      if (b_block &&
	  b_block->preds &&
	  b_block->preds->ptr &&
	  b_block->preds->ptr->preds &&
	  b_block->preds->ptr->preds->ptr &&
	  found_li(induction_var, b_block->preds->ptr->preds->ptr, &x) == TRUE)
      {
	*initial_value = x;
	return LOOP_CONSTANT;
	}
      break;
    }
    asm_ptr = asm_ptr->prev;
  }
  return LOOP_CONSTANT;
}

int found_li(char *i_var, struct bblk *b_block, int *val)
{
  struct assemline *asm_ptr=NULL;

  *val = 0;
  asm_ptr = b_block->lineend;
  while (asm_ptr)
  {
    if ((strcmp(asm_ptr->items[0], "li") == 0)  &&
	(strcmp(asm_ptr->items[1], i_var) == 0))
    {
      *val = strtol(asm_ptr->items[2], 0, 16);
      return TRUE;
    }
    if (asm_ptr == b_block->lines) 
    {
      break;
    }
    asm_ptr = asm_ptr->prev;
  }
  return FALSE;
}


/*********
 * This function will return 2 items:
 * LOOP_CONSTANT -> loop bound is constant
 * LOOP_REGISTER -> loop bound is dependent on a register
 */
int find_loop_bound (struct bblk *b_block,
		     int *loop_bound,
		     char **loop_reg,
		     enum CMP_Type *compare_type)
{
  struct assemline *asm_ptr = NULL;
  struct assemline *asm_ptr_ptr = NULL;
  // struct b_blk *pred_ptr=NULL;
  int found = FALSE;
  int ret_value = LOOP_CONSTANT;
  int find_register=FALSE;
  int temp_value = 0;
  char *tmp_reg = NULL;
  int slt_found = FALSE;
  int li_found = FALSE;
  int sp_branch_found = FALSE;
  *loop_bound = 0;

  if (b_block == NULL)
  {
    printf("b_block is NULL\n");
    return 0;
  }
  asm_ptr = b_block->lines;

  /* this while loop will go through and find a compare statement.
   * there is one case where it will NOT find a compare statement. It is for
   * cases where the comparision si doine to a zero. The compiler directly
   * sets the result to beqz  so that it doesn't have to put a compare
   * statement. For example, let's say we have a for-loop from 9-0, then we
   * have to go through it 10 times. We have the loop induction variable
   * initialized to 9 and then we go compare to zero. In those cases, we
   * will check the intitial value of hte register == 9, then we will
   * go througn and not find a compare so we just return the initial value
   * of the loop_bound = -1
   * so our total loop bound will be |-1 - 9| = 10
   */
  while (asm_ptr != NULL)
  {
    if (asm_ptr->type == CMP_INST)
    {
      found = TRUE;
      /* this means we found a loop bound that is not a
       * valid constant. it is a register
       */
      
      if (asm_ptr->items[3][0] == '$')
      {
	*loop_reg = strdup(asm_ptr->items[3]);
	ret_value = LOOP_REGISTER;
	find_register = TRUE;
	break;
      }
      else
      {
	*loop_bound = atoi(asm_ptr->items[3]);
	ret_value = LOOP_CONSTANT;
	find_register = FALSE;
	break;
      }
    }
    
    if (asm_ptr == b_block->lineend)
    {
      break;
    }
    asm_ptr = asm_ptr->next;
  }

  asm_ptr_ptr = asm_ptr;
  while ((find_register == TRUE) &&
	 (asm_ptr_ptr != NULL))
  {
    if ((asm_ptr_ptr->type == LOAD_INST) &&
	(((strcmp(asm_ptr_ptr->items[0], "lw") == 0) ||
	  (strcmp(asm_ptr_ptr->items[0],"lbu") == 0) ||
	  (strcmp(asm_ptr_ptr->items[0],"lhu") == 0) ||
	  (strcmp(asm_ptr_ptr->items[0], "lb") == 0) ||
	  (strcmp(asm_ptr_ptr->items[0], "lh") == 0)) &&
	 (strcmp(asm_ptr_ptr->items[1], *loop_reg) == 0)))
    {
      find_register = FALSE;
      if (find_in_list(asm_ptr_ptr->items[2], &temp_value) == TRUE)
      {
	*loop_bound = temp_value;
	free(*loop_reg);
	*loop_reg = strdup(asm_ptr_ptr->items[2]);
	ret_value = LOOP_GLOBAL;
      }
      else
      {
	*loop_bound = -1;
	free(*loop_reg);
	*loop_reg = strdup(asm_ptr_ptr->items[2]);
	ret_value = LOOP_REGISTER;
      }
    }
    if (asm_ptr_ptr == b_block->lines)
    {
      break;
    }
    
    asm_ptr_ptr = asm_ptr_ptr->prev;
  }

  asm_ptr = b_block->lines;
  
  while (asm_ptr != NULL)
  {
    if ((asm_ptr->type == CMP_INST) &&
	(strcmp(asm_ptr->items[0], "slt") == 0))
    {
      slt_found = TRUE;
    }
    if (strcmp(asm_ptr->items[0], "li") == 0)
    {
      li_found = TRUE;
    }
    if (strcmp(asm_ptr->items[0], "bgez") == 0)
    {
      *compare_type = GE;
      *loop_bound = 0;
      li_found = FALSE;
      slt_found = FALSE;
      sp_branch_found = TRUE;
    }
    if (strcmp(asm_ptr->items[0], "blez") == 0)
    {
      *compare_type = LE;
      *loop_bound=0;
      li_found = FALSE;
      slt_found = FALSE;
      sp_branch_found = TRUE;
    }
    if (strcmp(asm_ptr->items[0], "bltz") == 0)
    {
      *compare_type = LE;
      *loop_bound = 0;
      li_found = FALSE;
      slt_found = FALSE;
      sp_branch_found= TRUE;
    }
    if (strcmp(asm_ptr->items[0], "bgtz") == 0)
    {
      *compare_type = GT;
      *loop_bound = 0;
      li_found = FALSE;
      slt_found = FALSE;
      sp_branch_found = TRUE;
    }
    
    if (slt_found == TRUE)
    {
      if (asm_ptr->type == BRANCH_INST)
      {
	if (strcmp(asm_ptr->items[0], "bne") == 0)
	{
	  *compare_type = LT;
	}
	else if (strcmp(asm_ptr->items[0], "beq") == 0)
	{
	  *compare_type = GT;
	}
      }
    }
    if (li_found == TRUE)
    {
      if (asm_ptr->type == BRANCH_INST)
      {
	if (strcmp(asm_ptr->items[0], "bne") == 0)
	{
	  *compare_type = NE;
	}
	else if (strcmp(asm_ptr->items[0], "beq") == 0)
	{
	  *compare_type = EQ;
	}
      }
    }
    if (asm_ptr == b_block->lineend)
    {
      break;
    }
    asm_ptr = asm_ptr->next;
  }

  asm_ptr = b_block->lineend;
  while ((slt_found != TRUE) &&
	 (sp_branch_found != TRUE) &&
	 (slt_found != TRUE) &&
	 (asm_ptr != NULL))
  {
    if ((strcmp(asm_ptr->items[0],"bne") == 0) ||
	(strcmp(asm_ptr->items[0],"beq") == 0))
    {
      if (strcmp(asm_ptr->items[0], "bne") == 0)
	*compare_type = NE;
      else
	*compare_type = EQ;
    
      if (asm_ptr->items[2])
	tmp_reg = strdup (asm_ptr->items[2]);
    }
    if ((tmp_reg != NULL) &&
	(strcmp(asm_ptr->items[0], "li") == 0) &&
	(strcmp(asm_ptr->items[1], tmp_reg) == 0))
    {
      *loop_bound = strtol(asm_ptr->items[2], NULL, 16);
      ret_value = LOOP_CONSTANT;
      break;
    }
    if (asm_ptr == b_block->lines)
    {
      break;
    }
    asm_ptr = asm_ptr->prev;
  }
		
  return ret_value;
}

int find_in_pred(struct bblk *b_block, char *loop_reg, int *ret_value)
{
  struct assemline *asm_ptr= NULL;
  
  if ((b_block == NULL) ||
      (loop_reg == NULL))
  {
    return 0;
  }

  asm_ptr = b_block->lineend;
  while (asm_ptr != NULL)
  {
    if ((strcmp(asm_ptr->items[0], "li") == 0) &&
	(strcmp(asm_ptr->items[1], loop_reg) == 0))
    {
      *ret_value = LOOP_CONSTANT;
      return strtol(asm_ptr->items[2], 0, 16);
    }
    
    if (asm_ptr == b_block->lines)
    {
      b_block = b_block->preds->ptr;
      if (b_block == NULL)
      {
	return 0;
      }
      asm_ptr = b_block->lineend;
    }
    asm_ptr = asm_ptr->prev;
  }
  return 0;
}

char *find_loop_var_addr (struct bblk *b_block)
{
  struct assemline *cmp_insn = NULL;  /* varaible that will hold the compare
				       * instruction */
  struct assemline *asm_rev_ptr = NULL;
  char *reg_value=NULL;
  char *loop_var_addr=NULL;
  char *branch_var=NULL;
  int cmp_found = FALSE;
  int branch_found = FALSE;
  if (b_block == NULL)
  {
    printf("Empty basic block \n");
    return NULL;
  }
  cmp_insn = b_block->lines;
  
  while (cmp_insn != NULL)
  {
    if (cmp_insn->type == CMP_INST)
    {
      reg_value = strdup(cmp_insn->items[2]);
      cmp_found=TRUE;
      break;
    }
    if (cmp_insn->type == BRANCH_INST)
    {
      branch_found = TRUE;
      branch_var = strdup(cmp_insn->items[1]);
    }
    /* if it reached this statement, then we know we have code of the form:
     * load-immediate  R3,9
     * beqz R3,LABEL
     */
    if (cmp_insn == b_block->lineend)
    {
      /* here we know the value that we need to check is the branch value*/
      if (!branch_found && !cmp_found)
      {
	return NULL;
      }
      else 
      {
	free(reg_value);
	reg_value = strdup(branch_var);
      }
      break;
    }
    cmp_insn = cmp_insn->next; 
  }

  asm_rev_ptr = cmp_insn;
  while (asm_rev_ptr != NULL)
  {
    /* we catch the last definition of this variable */
    /* we mainly look into instructions that will have a destination
     * registers, because those are the only one that can define
     * this variable
     */
    if ((asm_rev_ptr->type == LOAD_INST) ||
	(asm_rev_ptr->type == ARITH_INST) ||
	(asm_rev_ptr->type == CONV_INST))
    {
      if (strcmp(asm_rev_ptr->items[1], reg_value) == 0)
      {
	loop_var_addr = strdup(asm_rev_ptr->items[2]);
	return loop_var_addr;
      }
    }
    if (asm_rev_ptr == b_block->lines)
    {
      /* if we get here and nothing really happens, we have to return  the
       * reg value
       */
      return reg_value;
    }
    asm_rev_ptr = asm_rev_ptr->prev;
    
  }
  return NULL;
}


int find_loop_increment (struct blist *block_list, char *i_var, int init_val,
			 int final_val, char **inc_type, int *inc_value)
{
  char *induction_var=NULL;
  struct blist *temp_loop_blk = NULL;
  struct assemline *asm_ptr = NULL;
  int addr = 3;
  double temp = 0.0000;
  
  int loop_inc = 0;

  if (i_var == NULL)
  {
    printf("Induction var == NULL\n");
    exit(-1);
  }
  if (final_val >= init_val)
  {
    loop_inc = final_val;
  }
  
  induction_var = strdup(i_var);
  
  if (block_list == NULL)
  {
    printf("Block list == NULL\n");
    return FALSE;
  }

  if (induction_var == NULL)
  {
    printf("Induction_var = NULL\n");
    return FALSE;
  }

  if (induction_var[0] == '$')
  {
    addr = FALSE;
  }
  else
  {
    addr = TRUE;
  }
  
  for (temp_loop_blk = block_list; temp_loop_blk != NULL;
       temp_loop_blk = temp_loop_blk->next)
  {
    asm_ptr = temp_loop_blk->ptr->lineend;

    while (asm_ptr != NULL)
    {
      if (addr == FALSE)
      {
	/* we have to look for a self destructive instruction */
	if ((asm_ptr->numitems > 2) &&
	    (strcmp(asm_ptr->items[1], induction_var) == 0) &&
	    (strcmp(asm_ptr->items[2], induction_var) == 0))
	{
	  if ((strcmp(asm_ptr->items[0],"add")   == 0) ||
	      (strcmp(asm_ptr->items[0],"addi")  == 0) ||
	      (strcmp(asm_ptr->items[0],"addu")  == 0) ||
	      (strcmp(asm_ptr->items[0],"addiu") == 0))
	  {
	    loop_inc = (final_val - init_val) / strtol(asm_ptr->items[3],
						       NULL, 16);
	    *inc_type = strdup(asm_ptr->items[0]);
	    *inc_value = strtol(asm_ptr->items[3], NULL, 16);
	  }
	  else if ((strcmp(asm_ptr->items[0],"sub")   == 0) ||
		   (strcmp(asm_ptr->items[0],"subu")  == 0))
	  {
	    loop_inc /= (final_val - init_val) / strtol(asm_ptr->items[3],
							NULL, 16);
	    *inc_type = strdup (asm_ptr->items[0]);
	    *inc_value = strtol (asm_ptr->items[3], NULL, 16);
	  }
	  
	  else if ((strcmp(asm_ptr->items[0],"mult")  == 0) ||
		   (strcmp(asm_ptr->items[0],"multu") == 0))
	  {
	    //temp = (double) strtol (asm_ptr->items[3], NULL, 16);
	    temp = (double) ((double) final_val/(double) init_val);
	    loop_inc = ceil (log(temp)/log ((double) strtol(asm_ptr->items[3],
							    NULL, 16)));
	    *inc_value = (int) strtol(asm_ptr->items[3], NULL, 16);
	    *inc_type = strdup(asm_ptr->items[0]);
	  }
	  else if (strcmp(asm_ptr->items[0],"sll") == 0)
	  {
	    loop_inc = pow ((double) loop_inc, 0.5000)+1;
	    *inc_type = strdup(asm_ptr->items[0]);
	    *inc_value = (int) strtol(asm_ptr->items[3], NULL, 16);
	  }
	  else if ((strcmp(asm_ptr->items[0],"div")  == 0) ||
		   (strcmp(asm_ptr->items[0],"divu") == 0))
	  {
	    // temp = (double) strtol (asm_ptr->items[3], NULL, 16);
	    temp = (double) ((double) final_val/(double) init_val);
	    // loop_inc = pow ((double) loop_inc, (double) (1/temp))+1;
	    loop_inc = ceil(log(temp)/log((double) strtol(asm_ptr->items[3],
							  NULL, 16)));
	    *inc_value = (int) strtol(asm_ptr->items[3], NULL, 16);
	    *inc_type = strdup(asm_ptr->items[0]);
	  }
	  else if ((strcmp(asm_ptr->items[0],"srl") == 0) ||
		   (strcmp(asm_ptr->items[0],"sra") == 0))
	  {
	    temp = (double) ((double) final_val/(double) init_val);
	    loop_inc = ceil (log (temp) / log(2.0000));
	    // loop_inc = pow ((double) loop_inc, 0.5000)+1;
	    *inc_value = (int) strtol(asm_ptr->items[3], NULL, 16);
	    *inc_type = strdup(asm_ptr->items[0]);
	  }
	}
      }
      /* this is the case were we have to take care of condition where the
       * induction variable is stored in the stack
       */
      else if (addr == TRUE)
      {
	if ((asm_ptr->type == STORE_INST) &&
	    (strcmp(asm_ptr->items[2], induction_var) == 0))
	{
	  free(induction_var);
	  induction_var = strdup(asm_ptr->items[1]);
	  if (induction_var == NULL)
	  {
	    printf("Invalid register\n");
	    exit(-1);
	  }
	}
	/* we have to catch all the instructions that com ebefore load
	 * that will use this instruction 
	 */
	else if (INST(asm_ptr->type) &&
		 (asm_ptr->type != LOAD_INST) &&
		 (asm_ptr->type == ARITH_INST) &&
		 (strcmp(asm_ptr->items[1],induction_var) == 0))
	{
	  if (is_register(asm_ptr->items[2]) &&
	      (asm_ptr->numitems > 3))
	  {
	    if (!is_register (asm_ptr->items[3]))
	    {
	      if ((strcmp(asm_ptr->items[0],"add")   == 0) ||
		  (strcmp(asm_ptr->items[0],"addi")  == 0) ||
		  (strcmp(asm_ptr->items[0],"addu")  == 0) ||
		  (strcmp(asm_ptr->items[0],"addiu") == 0))
	      {
		loop_inc = (final_val-init_val);
		loop_inc /= strtol(asm_ptr->items[3], NULL, 16);
		*inc_value = (int) strtol(asm_ptr->items[3], NULL, 16);
		*inc_type = strdup(asm_ptr->items[0]);
	      }
	      else if ((strcmp(asm_ptr->items[0],"sub")   == 0) ||
		       (strcmp(asm_ptr->items[0],"subu")  == 0))
	      {
		loop_inc = (final_val - init_val);
		loop_inc /= strtol(asm_ptr->items[3], NULL, 16);
		*inc_value = (int) strtol(asm_ptr->items[3], NULL, 16);
		*inc_type = strdup(asm_ptr->items[0]);
	      }
	      else if ((strcmp(asm_ptr->items[0],"mult")  == 0) ||
		       (strcmp(asm_ptr->items[0],"multu") == 0))
	      {
		//temp = (double) (final_val/init_val);
		temp = (double) (final_val/init_val);
		loop_inc = ceil (log(temp)/log((double)
					       strtol(asm_ptr->items[3],
						      NULL, 16)));
		*inc_value = (int) strtol(asm_ptr->items[3], NULL, 16);
		*inc_type = strdup(asm_ptr->items[0]); 
		//temp = (double) strtol (asm_ptr->items[3], NULL, 16);
		//loop_inc = pow ((double) loop_inc, (double) (1/temp))+1;
	      }
	      else if ((strcmp(asm_ptr->items[0],"div")  == 0) ||
		       (strcmp(asm_ptr->items[0],"divu") == 0))
	      {
		//temp = (double) (final_val/init_val);
		temp = (double) (final_val/init_val);
		loop_inc = ceil (log(temp)/log((double)
					       strtol(asm_ptr->items[3],
						      NULL, 16)));
		*inc_type = strdup(asm_ptr->items[0]);
		*inc_value = (int) strtol(asm_ptr->items[3], NULL, 16);
		//temp = (double) strtol (asm_ptr->items[3], NULL, 16);
		//loop_inc = pow ((double) loop_inc, (double) (1/temp))+1;
	      }
	      else if (strcmp(asm_ptr->items[0],"sll") == 0)
	      {
		temp = (double) ((double) final_val/(double) init_val);
		loop_inc = ceil (log(temp)/log(2.00000));
		*inc_type = strdup(asm_ptr->items[0]);
		*inc_value = (int) strtol(asm_ptr->items[3], NULL, 16);
		//loop_inc = 1+pow ((double) loop_inc, 0.5000);
	      }
	      else if ((strcmp(asm_ptr->items[0],"srl") == 0) ||
		       (strcmp(asm_ptr->items[0],"sra") == 0))
	      {
		temp = (double) ((double) final_val/(double) init_val);
		loop_inc = ceil (log(temp)/log(2.00000));
		*inc_type = strdup(asm_ptr->items[0]);
		*inc_value = (int) strtol(asm_ptr->items[3], NULL, 16);
	      }
	    }
	    free(induction_var);
	    induction_var = strdup(asm_ptr->items[2]);
	  }
	}
	else if ((asm_ptr->type == MOV_INST) &&
		 (strcmp(asm_ptr->items[1],induction_var) == 0))
	{
	  free(induction_var);
	  induction_var = strdup(asm_ptr->items[2]);
	}  
	else if ((asm_ptr->type == LOAD_INST) &&
		 (strcmp(asm_ptr->items[2], i_var) == 0))
	{
	  free(induction_var);
	  induction_var = strdup(i_var);
	}
      }
      
      if (asm_ptr == temp_loop_blk->ptr->lines)
      {
	break;
      }
      asm_ptr = asm_ptr->prev;
    }
  }
  return abs(loop_inc);
}
