#include "interface.h"  // Do NOT edit interface .h
#include "sample_prefetcher.h"

const int mshr_size = 128;
const int segment_size = 1000000;
const double useful_thresh_high1 = 0.45;
const double useful_thresh_low1 = 0.45;
const double late_thresh1 = 0.07;
const double poll_thresh1 = 0.01;
const int queue_thresh1 = 35;
const double bandwidth_thresh1 = 0.07;
const int pref_deg1 = 2;
const int pref_dist1 = 16;
const double useful_thresh_high2 = 0.45;
const double useful_thresh_low2 = 0.45;
const double late_thresh2 = 0.07;
const double poll_thresh2 = 0.01;
const int queue_thresh2 = 10;
const int pref_deg2 = 2;
const int pref_dist2 = 16;
const int pfilter_size_lg = 0;
const double bandwidth_thresh2 = 0.07;

/*
#define MSHR_SIZE mshr_size
#define PFILTER_SIZE_LG (pfilter_size_lg)
#define SEGMENT_SIZE (segment_size)

#define USEFUL_THRESH_HIGH1 (useful_thresh_high1)
#define USEFUL_THRESH_LOW1  (useful_thresh_low1)
#define LATE_THRESH1 (late_thresh1)
#define POLL_THRESH1 (poll_thresh1)
#define QUEUE_THRESH1 (queue_thresh1)
#define PREF_DEGREE1 (pref_degree1)
#define PREF_DISTANCE1 (pref_distance1)

#define USEFUL_THRESH_HIGH2 (useful_thresh_high2)
#define USEFUL_THRESH_LOW2 (useful_thresh_low2)
#define LATE_THRESH2 (late_thresh2)
#define POLL_THRESH2 (poll_thresh2)
#define QUEUE_THRESH2 (queue_thresh2)
#define PREF_DEGREE2 (pref_degree2)
#define PREF_DISTANCE2 (pref_distance2)
*/

COUNTER cycleNum;

struct MSHR_t
{
  CacheAddr_t insn_addr;
  UINT32 cache_level;
  bool valid_bit;
  bool prefetch_bit0;
  bool prefetch_bit1;
};

// Pollution Filter
// Increment on prefetch lines brought into set, decrement on demand miss to set
struct PFilter_t
{
  int set_count;
};

class Prefetch_Evaluator {
public:
  Prefetch_Evaluator(){

    total_prefs1 = 0;
    useful_prefs1 = 0;
    total_demand_misses1 = 0;
    pref_demand_misses1 = 0;
    late_prefs1 = 0;

    total_prefs2 = 0;
    total_prefs2_from1 = 0;
    useful_prefs2 = 0;
    total_demand_misses2 = 0;
    pref_demand_misses2 = 0;
    late_prefs2 = 0;

    //Next three just for analyzing data
    queuelim_l1count = 0;
    queuelim_l2count = 0;
    queuelim_l1numcycles = 0;
    queuelim_l2numcycles = 0;
    changeaggr_count = 0;

    pref_distance1 = pref_dist1;
    pref_distance2 = pref_dist2;

    pref_degree1 = pref_deg1;
    pref_degree2 = pref_deg2;

    total_segment = 0;
    total_segment_l2 = 0;

    MSHR_list = new MSHR_t[mshr_size];
    
    for(int i = 0;i < mshr_size;i++)
      {
	MSHR_list[i].valid_bit = false;
      }

   int PFilter_Size = 1 << pfilter_size_lg;

    PFilt_table = new PFilter_t[PFilter_Size];

    for(int i = 0;i < PFilter_Size;i++)
      {
        PFilt_table[i].set_count = 0;
      }    
  };
  
  // For a given cache level, this Returns -1 if element is not in MSHR, 0 if demand 
  // request, 1 if prefetch request and 2 if present in MSHR at different level
  int LookupMSHR(CacheAddr_t lookup_addr, bool demand_request, UINT32 cache_level); 
  int UpdateMSHRS(int *l1_outstanding, int *l2_outstanding);

  //Always call LookupMSHR before calling the next two functions

  // Insert assuming that the address is not in the set of valid MSHRs
  bool InsertMSHR(CacheAddr_t insn_addr, UINT32 cache_level, bool is_prefetch);

  // Used to modify the cache level of an existing entry
  bool ModifyLevelMSHR(CacheAddr_t insn_addr, UINT32 cache_level, bool is_prefetch);

  //void IssuePrefetches
  //(COUNTER cycle, PrefetchData_t *L1Data, PrefetchData_t *L2Data );

  COUNTER total_prefs1, useful_prefs1, total_demand_misses1, pref_demand_misses1, late_prefs1;
  COUNTER total_prefs2, useful_prefs2, total_demand_misses2, pref_demand_misses2, late_prefs2;
  COUNTER total_prefs2_from1; // L2 prefetches due to L1 misses
  int pref_degree1, pref_degree2; // The number of prefetches issued on a miss
  int pref_distance1, pref_distance2; // The distance from the miss that prefetches are issued
  int total_segment,total_segment_l2; // The total number of memory instructions in this segment

  int queuelim_l1count,queuelim_l2count;
  int queuelim_l1numcycles,queuelim_l2numcycles;
  int changeaggr_count;

  MSHR_t *MSHR_list;
  PFilter_t *PFilt_table; // Bloom Filter
};

Prefetch_Evaluator *pref_evaluator;

SamplePrefetcher *pref1;   // This is the L1 prefetcher
SamplePrefetcher *pref2;   // This is the L2 prefetcher

// Assumes that element is not already in MSHR, always call LookupMSHR first
bool
Prefetch_Evaluator::InsertMSHR
(CacheAddr_t insn_addr, UINT32 cache_level, bool is_prefetch)
{
  int i = 0;
  
  while(i < mshr_size && MSHR_list[i].valid_bit == true)
    i++;

  if(i == mshr_size)
    return false;
  
  MSHR_list[i].insn_addr = (insn_addr >> 6) << 6;

  //fprintf(out,"Now inserting cache address %x into MSHR with prefetch bit %d\n",
  //        (unsigned int)MSHR_list[i].insn_addr,(int)is_prefetch);
  
  MSHR_list[i].cache_level = cache_level;
  if(cache_level == 0)
    {
      MSHR_list[i].prefetch_bit0 = is_prefetch;
      MSHR_list[i].prefetch_bit1 = 0;
    }
  else
    {
      MSHR_list[i].prefetch_bit1 = is_prefetch;
      MSHR_list[i].prefetch_bit0 = 0;
    }
  MSHR_list[i].valid_bit = true;
  
  return true;
}

// Assumes that element is already in MSHR, always call LookupMSHR first
// Used to modify the cache level of an existing entry
bool
Prefetch_Evaluator::ModifyLevelMSHR
(CacheAddr_t insn_addr, UINT32 cache_level, bool is_prefetch)
{
  insn_addr = (insn_addr >> 6) << 6;

  for(int i = 0;i < mshr_size;i++)
    {
      if(MSHR_list[i].insn_addr == insn_addr)
        {
          //fprintf(out,"Modifying the MSHR level of cache address %x to %d\n",
          //        (unsigned int)insn_addr,cache_level);

          // If previous cache level is 0 and this 
          // is a prefetch request (it will be for level 1)
          if(is_prefetch && MSHR_list[i].cache_level == 0)
            {
              MSHR_list[i].prefetch_bit1 = true;
            }
          else if(is_prefetch && MSHR_list[i].cache_level == 1)
            {
              MSHR_list[i].prefetch_bit0 = true;
            }

          MSHR_list[i].cache_level = cache_level;
          return true;
        }
    }

  return false;
}

int
Prefetch_Evaluator::UpdateMSHRS(int *l1_outstanding, int *l2_outstanding)
{
  int num_new_lines = 0;

  *l1_outstanding = 0;
  *l2_outstanding = 0;

  for(int i = 0;i < mshr_size;i++)
    {      
      MSHR_t *MSHR_elt = &MSHR_list[i];

      if(MSHR_elt->valid_bit == false) continue;

      // There is a request for both L1 and L2
      if(MSHR_elt->cache_level == 100)
        {
          int prefetch_bit1;
          int prefetch_bit2;
                    
          prefetch_bit1 = GetPrefetchBit(0,MSHR_elt->insn_addr);
          prefetch_bit2 = GetPrefetchBit(1,MSHR_elt->insn_addr);

          //Both requests are still outstanding
          if(prefetch_bit1 == -1 && prefetch_bit2 == -1)
            {
              *l1_outstanding = *l1_outstanding + 1;
              *l2_outstanding = *l2_outstanding + 1;
            }
          // The L1 request is still outstanding but L2 is not
          else if(prefetch_bit1 == -1) 
            {
              MSHR_elt->cache_level = 0;              
              //This will ensure that a late/useful L2 prefetch is not double counted as useful later
              if(prefetch_bit2 == 1 && MSHR_elt->prefetch_bit1 == 0)
                UnSetPrefetchBit(1,MSHR_elt->insn_addr);

              *l1_outstanding = *l1_outstanding + 1;              
              num_new_lines++;
            }
          // The L2 request is still outstanding but L1 is not
          else if(prefetch_bit2 == -1) 
            {                          
              MSHR_elt->cache_level = 1;

              if(prefetch_bit1 == 1 && MSHR_elt->prefetch_bit0 == 0)
                UnSetPrefetchBit(0,MSHR_elt->insn_addr);

              *l2_outstanding = *l2_outstanding + 1;             
              num_new_lines++;
            }
           //Both requests are completed
          else if(prefetch_bit1 != -1 && prefetch_bit2 != -1)
            {            
              if(prefetch_bit2 == 1 && MSHR_elt->prefetch_bit1 == 0)
                UnSetPrefetchBit(1,MSHR_elt->insn_addr);
              
              if(prefetch_bit1 == 1 && MSHR_elt->prefetch_bit0 == 0)
                UnSetPrefetchBit(0,MSHR_elt->insn_addr);               

              MSHR_elt->valid_bit = false;
              num_new_lines++;
            }
        }
      else
	{                    
          int prefetch_bit = GetPrefetchBit(MSHR_elt->cache_level,MSHR_elt->insn_addr);
          
          // Data is now in the cache and hence no longer in flight or in queue
          if(prefetch_bit != -1)
            {            
              num_new_lines++;
                                           
              if(prefetch_bit == 1)
                {
                  if(MSHR_elt->cache_level == 0 && MSHR_elt->prefetch_bit0 == 0)
                    {
                      UnSetPrefetchBit(0,MSHR_elt->insn_addr);
                    }
                  else if(MSHR_elt->cache_level == 1 && MSHR_elt->prefetch_bit1 == 0)
                    {
                      UnSetPrefetchBit(1,MSHR_elt->insn_addr); 
                    }
                }

              MSHR_elt->valid_bit = false;
            }
          else
            {
              MSHR_elt->cache_level == 0 ? *l1_outstanding = *l1_outstanding + 1 
                :  *l2_outstanding = *l2_outstanding + 1;
            }
	}
    }
  return num_new_lines;
}

// For a given cache level, this Returns -1 if element is not in MSHR, 0 if demand 
// request, 1 if prefetch request and 2 if present in MSHR at different cache level
int 
Prefetch_Evaluator::LookupMSHR
(CacheAddr_t lookup_addr, bool demand_request, UINT32 cache_level)
{
  lookup_addr = (lookup_addr >> 6) << 6;

  for(int i = 0;i < mshr_size;i++)
    {
      MSHR_t *MSHR_elt = &MSHR_list[i];

      if(MSHR_elt->valid_bit && MSHR_elt->insn_addr == lookup_addr)
	{
          bool cache_level_ok 
            = MSHR_elt->cache_level == 100 || MSHR_elt->cache_level == cache_level;

	  // If lookup is done for demand request and prefetch bit is set, change bit
          // Do this only if the lookup is for the same cache level as the entry 
	  if(cache_level_ok)
	    {

              if(cache_level == 0)
                {
                  int temp = MSHR_elt->prefetch_bit0 == false ? 0 : 1;
                  // If true, set to false since demand request. If false, it will be unchanged
                  if(demand_request)
                    {
                      MSHR_elt->prefetch_bit0 = false;
                    }
                  return temp;
                }
              else if (cache_level == 1)
                {
                  int temp = MSHR_elt->prefetch_bit1 == false ? 0 : 1;
                  if(demand_request)
                    {                      
                      MSHR_elt->prefetch_bit1 = false;
                    }
                  return temp;
                }
	    }
          else // If address is in MSHR but at a different level than the lookup
            {
              return 2;
            }
	}
    }

  return -1;
}

//
//  Function to initialize the prefetchers.  DO NOT change the prototype of this
//  function.  You can change the body of the function by calling your necessary
//  initialization functions.
//
void InitPrefetchers() // DO NOT CHANGE THE PROTOTYPE
{
  // INSERT YOUR CHANGES IN HERE
  pref1 = new SamplePrefetcher();
  pref2 = new SamplePrefetcher();
  pref_evaluator = new Prefetch_Evaluator();
}

//
//  Function that is called every cycle to issue prefetches should the
//  prefetcher want to.  The arguments to the function are the current cycle,
//  the demand requests to L1 and L2 cache.  Again, DO NOT change the prototype of this
//  function.  You can change the body of the function by calling your necessary
//  routines to invoke your prefetcher.
//

// DO NOT CHANGE THE PROTOTYPE
void 
IssuePrefetches( COUNTER cycle, PrefetchData_t *L1Data, PrefetchData_t * L2Data )
{    

    // INSERT YOUR CHANGES IN HERE
    MemLogEntry *entry;
    CacheAddr_t addr;

    int l1_outstanding, l2_outstanding;    
    
    cycleNum = cycle;

    pref_evaluator->UpdateMSHRS(&l1_outstanding, &l2_outstanding);
    pref_evaluator->total_segment++;

    if(l1_outstanding > queue_thresh1 - 5)
      pref_evaluator->queuelim_l1numcycles++;

    if(l2_outstanding > queue_thresh2 - 3)
      pref_evaluator->queuelim_l2numcycles++;

    // Issue L1 prefetches
    for(int i = 0; i < 4; i++) 
      {
	PrefetchData_t* const pd = &L1Data[i];
	
	if(pd->LastRequestCycle != cycle) continue;

        //DEBUGGING
        if(pd->hit)
          {
            //fprintf(out,"L1 hit in cycle %d for insn address %x and data address %x\n",
            //        (unsigned int)cycle,(unsigned int)pd->LastRequestAddr,(unsigned int)pd->DataAddr);    
          }

        entry = pref1->AccessEntry(0,pd->LastRequestAddr,pd->DataAddr);
        
        if(pd->hit && GetPrefetchBit(0,pd->DataAddr) == 1)
          {
            //fprintf(out,"A useful L1 prefetch detected for line %x address %x in cycle %d\n",
            //        (unsigned int)((pd->DataAddr >> 6) << 6),(unsigned int)pd->DataAddr,(unsigned int)cycle);
            pref_evaluator->useful_prefs1++;
            UnSetPrefetchBit(0,pd->DataAddr);

            //Prefetch if a useful prefetch is found            

	    if(entry && entry->count >= 2) 
	      { 		    
                if(entry->count == 2)
                  {
                    addr = pd->DataAddr + (pref_evaluator->pref_distance1)*entry->stride;
                  }
                else 
                  {
                    addr = pd->DataAddr 
                      + (pref_evaluator->pref_distance1 + pref_evaluator->pref_degree1 - 1)*entry->stride;
                  }
                
                if(l1_outstanding < queue_thresh1)
                  {                   
		    bool pref_issued_flag = false; // Issue only one prefetch at most
		    
                    for(int j = 0; j < pref_evaluator->pref_degree1; j++) 
                      {
			if(pref_issued_flag) continue;
			
                        int lookupValB = pref_evaluator->LookupMSHR(addr,false,0);
			
                        if(GetPrefetchBit(0,addr) == -1 && (lookupValB == -1 || lookupValB == 2))
                          {
                            if(lookupValB == -1)
                              pref_evaluator->InsertMSHR(addr,0,true);       
                            else 
                              pref_evaluator->ModifyLevelMSHR(addr,100,true);
                            
                            IssueL1Prefetch(cycle,addr);
			    pref_issued_flag = true;
                            pref_evaluator->total_prefs1++;
                          }                        
                        addr += entry->stride;
                      }
                  }
		else
		  {
		    pref_evaluator->queuelim_l1count++;
		  }
              } 	     
	     else    // Do sequential prefetching if previous line is present in cache
	       {
		 CacheAddr_t line_addr = ((pd->DataAddr >> 6) << 6);
		 if(GetPrefetchBit(0,line_addr - 64) != -1)
		   {
		     addr = line_addr + 64;
		     //fprintf(out,"Doing L1 sequential prefetching since line addr %x is prefetched\n",
		     //    (unsigned)line_addr);
		     bool pref_issued_flag = false;		     
		     for(int i = 0;i < pref_evaluator->pref_degree1;i++)
		      {
			if(pref_issued_flag) continue;

			int lookupValB = pref_evaluator->LookupMSHR(addr,false,0);

			if(GetPrefetchBit(0,addr) == -1 && (lookupValB == -1 || lookupValB == 2))
			  {
			    if(lookupValB == -1)
                              pref_evaluator->InsertMSHR(addr,0,true);       
                            else 
                              pref_evaluator->ModifyLevelMSHR(addr,100,true);

			    pref_issued_flag = true;
			    //fprintf(out,"Issuing sequential prefetch for addr %x\n",(unsigned int)addr);
			    IssueL1Prefetch(cycle,addr);
			    pref_evaluator->total_prefs1++;
			  }
			addr = addr + 64;
		      }
		   }
	       } 	     
	  }

	if(pd->hit == 0)
	  {
            //DEBUGGING
            //fprintf(out,"L1 miss in cycle %d for insn address %x and data address %x with line address %x\n",
            //        (unsigned int)cycle,(unsigned int)pd->LastRequestAddr,(unsigned int)pd->DataAddr,
            //        (unsigned int)((pd->DataAddr >> 6) << 6));             
            //pref_evaluator->total_segment++;
            pref_evaluator->total_demand_misses1++;
                        
            // If this address is being prefetched and in MSHR, late prefetch
            if(pref_evaluator->LookupMSHR(pd->DataAddr,true,0) == 1)
              {
                //fprintf(out,"A late/useful L1 prefetch detected in cycle %d for line %x address %x\n",
                //        (unsigned int)cycle,(unsigned int)((pd->DataAddr >> 6) << 6),(unsigned int)pd->DataAddr);                
                pref_evaluator->late_prefs1++;
                pref_evaluator->useful_prefs1++;
              }

            int lookupVal = pref_evaluator->LookupMSHR(pd->DataAddr,false,0);
            if(lookupVal == -1 || lookupVal == 2)
              {                
                if(lookupVal == -1)
                  pref_evaluator->InsertMSHR(pd->DataAddr,0,false);          
                // This request is inflight from both L1 and L2
                // Hence it is a level 100 request
                else
                  pref_evaluator->ModifyLevelMSHR(pd->DataAddr,100,false);
              }
                	    
	    if(entry && entry->count >= 2) 
	      { 		    		
                //The first time this is being prefetched
                if(entry->count == 2)
                  {
                    addr = pd->DataAddr + (pref_evaluator->pref_distance1)*entry->stride;
                  }
                else //After the first set of prefetches, go further ahead
                  {
                    addr = pd->DataAddr 
                    + (pref_evaluator->pref_distance1 + pref_evaluator->pref_degree1 - 1)*entry->stride;
                  }

                if(l1_outstanding < queue_thresh1)
                  {                    		    
                    for(int j = 0; j < pref_evaluator->pref_degree1; j++) 
                      {									
                        int lookupValB = pref_evaluator->LookupMSHR(addr,false,0);

                        // If address is not in cache or at the right level of the MSHR
                        if(GetPrefetchBit(0,addr) == -1 && (lookupValB == -1 || lookupValB == 2))
                          {
                            if(lookupValB == -1)
                              pref_evaluator->InsertMSHR(addr,0,true);       
                            else 
                              pref_evaluator->ModifyLevelMSHR(addr,100,true);
                            
                            //fprintf(out,"Issuing L1 prefetch for instruction %x address %x line %x in cycle %d\n",
                            //        (unsigned int)pd->LastRequestAddr,(unsigned int)addr,
                            //        (unsigned int)((addr >> 6) << 6),(unsigned int)cycle);

                            //fprintf(out,"The stride for this prefetch is %d\n",entry->stride);

                            IssueL1Prefetch(cycle,addr);
                            pref_evaluator->total_prefs1++;                            
                          }                        
                        addr += entry->stride;
                      }
                  }
                else
                  {
		    pref_evaluator->queuelim_l1count++; 
                  }
	      }	    
	    else    // Do sequential prefetching if previous line is present in cache
	      {
		CacheAddr_t line_addr = ((pd->DataAddr >> 6) << 6);
		
		if(GetPrefetchBit(0,line_addr - 64) != -1)
		  {
		    addr = line_addr + 64;
		    //fprintf(out,"Doing L1 sequential prefetching since there is a miss to line addr %x\n",
		    //     (unsigned)line_addr);
		    for(int i = 0;i < pref_evaluator->pref_degree1;i++)
		      {
			int lookupValB = pref_evaluator->LookupMSHR(addr,false,0);

			if(lookupValB == -1)
			  pref_evaluator->InsertMSHR(addr,0,true);       
			else 
			  pref_evaluator->ModifyLevelMSHR(addr,100,true);

			if(GetPrefetchBit(0,addr) == -1 && (lookupValB == -1 || lookupValB == 2))
			  {			    
			    //fprintf(out,"Issuing sequential prefetch for addr %x\n",(unsigned int)addr);
			    IssueL1Prefetch(cycle,addr);
			    pref_evaluator->total_prefs1++;
			  }
			addr = addr + 64;
		      }
		  }
	      }	    
	  }
      }
    
    PrefetchData_t* const pd2 = L2Data;

    // Issue L2 prefetches
    if(cycle != pd2->LastRequestCycle) return;

    // We do not want L1 prefetch hits cause by L2 misses to be counted as a useful L2 prefetch
    if(pd2->hit && pd2->LastRequestPrefetch)
      {
	UnSetPrefetchBit(1, pd2->DataAddr);

        //fprintf(out,"L2 hit in cycle %d for insn address %x and data address %x\n",
        //      (unsigned int)cycle,(unsigned int)pd2->LastRequestAddr,(unsigned int)pd2->DataAddr);
      }

    //To count L2 prefetches which are caused by L1 prefetch misses, remove comments    

    /*
    if(pd2->LastRequestPrefetch)
      {
        //pref_evaluator->total_prefs2++;
        pref_evaluator->total_prefs2_from1++;
      }
    */

    entry = pref2->AccessEntry(0,pd2->LastRequestAddr,pd2->DataAddr);

    //FOR USEFUL PREFETCHES
    if(pd2->hit && GetPrefetchBit(1,pd2->DataAddr) == 1)
      {
	//fprintf(out,"A useful L2 prefetch detected for address %x line %x\n",
        //        (unsigned int)pd2->DataAddr,(unsigned int)((pd2->DataAddr >> 6) << 6));
        pref_evaluator->useful_prefs2++;
       
        UnSetPrefetchBit(1,pd2->DataAddr);

         if(entry && entry->count >= 2) 
            {  
              if(entry->count == 2)
                {
                  addr = pd2->DataAddr + (pref_evaluator->pref_distance2)*entry->stride;
                }
              else //After the first set of prefetches, go further ahead
                {
                  addr = pd2->DataAddr 
                    + (pref_evaluator->pref_distance2 + pref_evaluator->pref_degree2 - 1)*entry->stride;
                }
              
              if(l2_outstanding < queue_thresh2)
                {
		  bool pref_issued_flag = false; // Issue only one prefetch at most

                  for(int j = 0; j < pref_evaluator->pref_degree2; j++) 
                    {
		      if(pref_issued_flag) continue;
		      
                      int lookupValB = pref_evaluator->LookupMSHR(addr,false,1);
                      if(GetPrefetchBit(1,addr) == -1 && (lookupValB == -1 || lookupValB == 2))
                        {                
                          if(lookupValB == -1)
                            pref_evaluator->InsertMSHR(addr,1,true);                         
                          else 
                            pref_evaluator->ModifyLevelMSHR(addr,100,true);
                          
                          IssueL2Prefetch(cycle,addr);
                          pref_evaluator->total_prefs2++;
                        }
                      addr += entry->stride;
                    }
                }
              else
                {
                  pref_evaluator->queuelim_l2count++;
                }
            }	 	 
	 else    // Do sequential prefetching if previous line is present in cache
	   {	     
	     CacheAddr_t line_addr = ((pd2->DataAddr >> 6) << 6);
	     if(GetPrefetchBit(1,line_addr - 64) != -1)
	       {		 
		 addr = line_addr + 64;
		 //fprintf(out,"Doing L2 sequential prefetching since line addr %x is prefetched\n",
		 //     (unsigned)line_addr);
		 bool pref_issued_flag = false;
		 for(int i = 0;i < pref_evaluator->pref_degree2;i++)
		   {
		     if(pref_issued_flag) continue;

		     int lookupValB = pref_evaluator->LookupMSHR(addr,false,1);
		     
		     if(lookupValB == -1)
		       pref_evaluator->InsertMSHR(addr,1,true);       
		     else 
		       pref_evaluator->ModifyLevelMSHR(addr,100,true);

		     if(GetPrefetchBit(1,addr) == -1 && (lookupValB == -1 || lookupValB == 2))
		       {
			 pref_issued_flag = true;
			 //fprintf(out,"Issuing sequential prefetch for addr %x\n",(unsigned int)addr);
			 IssueL2Prefetch(cycle,addr);
			 pref_evaluator->total_prefs2++;
		       }
		     addr = addr + 64;
		   }		 
	       }
	   }	 
      }

    if(pd2->hit == 0)
      {
        pref_evaluator->total_segment_l2++;
        pref_evaluator->total_demand_misses2++;
        
        //fprintf(out,"L2 miss in cycle %d for insn address %x and data address %x\n",
        //        (unsigned int)cycle,(unsigned int)pd2->LastRequestAddr,(unsigned int)pd2->DataAddr);
                
        //If this address is being prefetched and in MSHR, late prefetch
          if(pref_evaluator->LookupMSHR(pd2->DataAddr,true,1) == 1)
            {
              //fprintf(out,"A late/useful L2 prefetch detected in cycle %d line %x for address %x\n",
              //        (unsigned int)cycle,(unsigned int)((pd2->DataAddr >> 6) << 6),(unsigned int)pd2->DataAddr);
              pref_evaluator->late_prefs2++;
              pref_evaluator->useful_prefs2++;              
            }
          
          int lookupVal = pref_evaluator->LookupMSHR(pd2->DataAddr,false,1);
          if(lookupVal == -1 || lookupVal == 2)
            {              
              //To count L2 prefetches that are caused by L1 prefetch misses, change false to pd2->LastRequestPrefetch
              if(lookupVal == -1)
                pref_evaluator->InsertMSHR(pd2->DataAddr,1,false);              
              else 
                pref_evaluator->ModifyLevelMSHR(pd2->DataAddr,100,false);
            }
                 
          if(entry && entry->count >= 2) 
            {                
              //The first time this is being prefetched
              if(entry->count == 2)
                {
                  addr = pd2->DataAddr + (pref_evaluator->pref_distance2)*entry->stride;
                }
              else //After the first set of prefetches, go further ahead
                {
                  addr = pd2->DataAddr 
                    + (pref_evaluator->pref_distance2 + pref_evaluator->pref_degree2 - 1)*entry->stride;
                }
              
              if(l2_outstanding < queue_thresh2)
                {                  
                  for(int j = 0; j < pref_evaluator->pref_degree2; j++) 
                    {
                      int lookupValB = pref_evaluator->LookupMSHR(addr,false,1);
                      if(GetPrefetchBit(1,addr) == -1 && (lookupValB == -1 || lookupValB == 2))
                        {                          
                          if(lookupValB == -1)
                            pref_evaluator->InsertMSHR(addr,1,true);                          
                          else 
                            pref_evaluator->ModifyLevelMSHR(addr,100,true);
                          
                          //fprintf(out,"Issuing L2 prefetch for address %x line %x in cycle %d\n",
                          //        (unsigned int)addr,(unsigned int)((addr >> 6) << 6),(unsigned int)cycle);
                          IssueL2Prefetch(cycle,addr);
                          pref_evaluator->total_prefs2++;
                        }
                      addr += entry->stride;
                    }
                }
              else
                {
                  pref_evaluator->queuelim_l2count++;
                }
            }	 	  
	  else    // Do sequential prefetching if previous line is present in cache
	    {
	      CacheAddr_t line_addr = ((pd2->DataAddr >> 6) << 6);
	      if(GetPrefetchBit(1,line_addr - 64) != -1)
		{
		  addr = line_addr + 64;
		  //fprintf(out,"Doing L1 sequential prefetching since there is a miss to line addr %x\n",
		  //(unsigned)line_addr);
		  for(int i = 0;i < pref_evaluator->pref_degree2;i++)
		    {		      
		      int lookupValB = pref_evaluator->LookupMSHR(addr,false,1);

		      if(GetPrefetchBit(1,addr) == -1 && (lookupValB == -1 || lookupValB == 2))
			{			  
			  if(lookupValB == -1)
			    pref_evaluator->InsertMSHR(addr,1,true);       
			  else 
			    pref_evaluator->ModifyLevelMSHR(addr,100,true);
			  
			  IssueL2Prefetch(cycle,addr);
			  pref_evaluator->total_prefs2++;
			  //fprintf(out,"Issuing sequential prefetch for addr %x\n",(unsigned int)addr);
			}
		      addr = addr + 64;
		    }
		}
	    }	  
      }
    
    
    if(pref_evaluator->total_segment >= segment_size)
      //|| pref_evaluator->total_segment_l2 >= segment_size)
      {
        
        bool acc_high1, acc_medium1, acc_low1, late1, bandw_high1;
        bool acc_high2, acc_medium2, acc_low2, late2, bandw_high2;
        
        acc_high1 = acc_medium1 = acc_low1 = late1 = bandw_high1 = false;
        acc_high2 = acc_medium2 = acc_low2 = late2 = bandw_high2 = false;
        
        double pref_accuracy1
          = (double)pref_evaluator->useful_prefs1
          /(double)pref_evaluator->total_prefs1;
        
        /*
        fprintf(out,"The number of useful L1 prefetches %d, total prefetches %d accuracy %.4f\n",
                (unsigned int)pref_evaluator->useful_prefs1,
                (unsigned int)pref_evaluator->total_prefs1,pref_accuracy1);
                
        fprintf(out,"The number of late L1 prefetches %d, useful prefetches %d late percent %.4f\n",
                (unsigned int)pref_evaluator->late_prefs1,(unsigned int)pref_evaluator->useful_prefs1,
                (double)pref_evaluator->late_prefs1/(double)pref_evaluator->useful_prefs1);
        */
        
	if(pref_accuracy1 > useful_thresh_high1)
          acc_high1 = true;
        else if(pref_accuracy1 > useful_thresh_low1)
          acc_medium1 = true;
        else
          acc_low1 = true;

	//fprintf(out,"The value of acc_high1 %d acc_medium1 %d\n",(int)acc_high1,(int)acc_medium1);
        
        if(double(pref_evaluator->late_prefs1)
           /double(pref_evaluator->useful_prefs1) > late_thresh1)
          late1 = true;
	
	if(double(pref_evaluator->queuelim_l1numcycles)
	   /double(pref_evaluator->total_segment) > bandwidth_thresh1)
	  bandw_high1 = true;

        //if(double(pref_evaluator->pref_demand_misses1)/
        //   (double)(pref_evaluator->total_demand_misses1) > poll_thresh1)
        //  poll1 = true;
	
	//fprintf(out,"The value of the expression being evaluated for more aggressive is %d\n",
	//(int)((acc_high1 || acc_medium1) && late1));
			
        if((acc_high1 || acc_medium1) && late1 && !bandw_high1) //Become more aggressive
          {	
	    pref_evaluator->changeaggr_count++;
            if(pref_evaluator->pref_distance1 < 64)
              {		
                pref_evaluator->pref_distance1 *= 2;		
              }

            if(pref_evaluator->pref_degree1 < 4 && pref_evaluator->pref_distance1 >= 16)
	      {
		pref_evaluator->pref_degree1 *= 2;
	      }
          }
        else if((acc_high1 || acc_medium1) && !late1) //No change
          {
            
          }
        else if(!(acc_high1 ||acc_medium1) && late1) //Become less aggressive
          {
	    pref_evaluator->changeaggr_count++;
            if(pref_evaluator->pref_distance1 > 4)
              {
                pref_evaluator->pref_distance1 /= 2;
	      }	              
	    if(pref_evaluator->pref_degree1 > 1)
	      {
		pref_evaluator->pref_degree1 /= 2;
	      }
          }
        else if(!(acc_high1 ||acc_medium1) && !late1) // No change
          {
            
          }
        
        double pref_accuracy2 
          = (double)pref_evaluator->useful_prefs2
          /(double)pref_evaluator->total_prefs2;

        
         //fprintf(out,"The total number of L2 prefetches which resulted from L1 are %d\n",
         //        (unsigned int)pref_evaluator->total_prefs2_from1);

        if(pref_accuracy2 > useful_thresh_high2)
          acc_high2 = true;
        else if(pref_accuracy2 > useful_thresh_low2)
          acc_medium2 = true;
        else
          acc_low2 = true;
        
	//fprintf(out,"The value of acc_high2 %d acc_medium2 %d\n",(int)acc_high2,(int)acc_medium2);

        if(double(pref_evaluator->late_prefs2)
           /double(pref_evaluator->useful_prefs2) > late_thresh2)
          late2 = true;

	//fprintf(out,"The value of late2 %d\n",(int)late2);
		
        //if(double(pref_evaluator->pref_demand_misses2)/
        //   (double)(pref_evaluator->total_demand_misses2) > poll_thresh2)
        //  poll2 = true;

	if(double(pref_evaluator->queuelim_l2numcycles)
	   /double(pref_evaluator->total_segment) > bandwidth_thresh2)
	  bandw_high2 = true;		

        if((acc_high2 ||acc_medium2) && late2 && !bandw_high2) //Become more aggressive
          {
	    pref_evaluator->changeaggr_count++;
            if(pref_evaluator->pref_distance2 < 64)
              {
                pref_evaluator->pref_distance2 *= 2;
              }

            if(pref_evaluator->pref_degree2 < 4 && pref_evaluator->pref_distance2 >= 16)
	      {
		pref_evaluator->pref_degree2 *= 2;
	      }
          }
        else if((acc_high2 ||acc_medium2) && !late2) //No change
          {
            
          }
        else if(!(acc_high2 ||acc_medium2) && late2) //Become less aggressive
          {
	    pref_evaluator->changeaggr_count++;
	    if(pref_evaluator->pref_distance2 > 4)
              {
                pref_evaluator->pref_distance2 /= 2;
	      }	              
	    if(pref_evaluator->pref_degree2 > 1)
	      {
		pref_evaluator->pref_degree2 /= 2;
	      }            
          }
        else if(!(acc_high2 ||acc_medium2) && !late2) // No change
          {
            
          }
	
        pref_evaluator->total_segment = 0;
	pref_evaluator->total_segment_l2 = 0;
        
        pref_evaluator->useful_prefs1 = 0;
        pref_evaluator->total_prefs1 = 0;
        pref_evaluator->total_demand_misses1 = 0;
        pref_evaluator->pref_demand_misses1 = 0;
        pref_evaluator->late_prefs1 = 0;

	pref_evaluator->queuelim_l1count = 0;
	pref_evaluator->queuelim_l1numcycles = 0;
        
        pref_evaluator->useful_prefs2 = 0;
        pref_evaluator->total_prefs2 = 0;
        pref_evaluator->total_prefs2_from1 = 0;
        pref_evaluator->total_demand_misses2 = 0;
        pref_evaluator->pref_demand_misses2 = 0;
        pref_evaluator->late_prefs2 = 0;

	pref_evaluator->queuelim_l2count = 0;
	pref_evaluator->queuelim_l2numcycles = 0;
      }    
}
