/*

  Copyright (C) 2000, 2001 Silicon Graphics, Inc.  All Rights Reserved.

   Path64 is free software; you can redistribute it and/or modify it
   under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2, or (at your option)
   any later version.

   Path64 is distributed in the hope that it will be useful, but WITHOUT
   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
   or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
   License for more details.

   You should have received a copy of the GNU General Public License
   along with Path64; see the file COPYING.  If not, write to the Free
   Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
   02110-1301, USA.

   Special thanks goes to SGI for their continued support to open source

*/


/***********************************************************************
 *
 * This file provides three cache related classes.
 *
 * Exported type:
 *
 *  CACHE_PARAMETERS: store the parameters of the cache(s)
 *  
 * Exported function
 *
 *  void Initialize ()
 *      Initialize the cache data parameters
 *
 *  INT LineSize (INT level)
 *      Return the cache line size for the specified level (1 or 2)
 *
 *  INT64 EffSize (INT level)
 *      Return the effective cache size for the specified level
 *
 *  INT Levels ()
 *      Return the number of levels in the cache
 *
 *  BOOL Level1_Really_Level2 ()
 *      Return TRUE if L1 was disabled for prefetching or for Mhd.
 *
 * Exported type:
 *
 * PF_VOLUME:        the volume of data accessed by a loop
 *
 * Exported functions
 *
 *  PF_VOLUME ()
 *      constructor, initialize to 0
 *
 *  PF_VOLUME (const PF_VOLUME& vol)
 *      constructor, initialize to supplied volume
 *
 *  PF_VOLUME (INT vol_1, INT vol_2)
 *      constructor, initialize to supplied values
 *
 *  PF_VOLUME&    operator *= (INT lines)
 *      multiply the values by the supplied number of lines
 *
 *  PF_VOLUME&    operator += (const PF_VOLUME& vol)
 *      add the supplied volume to this volume
 *
 *  BOOL Localized ()
 *      return TRUE if the volume is localized in either level of the cache
 *
 *  void Print (FILE* fp)
 *      print the volume to FILE*
 *
 *
 * Exported type:
 *
 *  PF_LOCLOOP:       based on the volume of data, is a loop localized or not
 *
 * Exported functions
 *
 *  PF_LOCLOOP ()
 *      constructor, initialize to not-localized
 *
 *  PF_LOCLOOP* operator= (const PF_LOCLOOP& locloop)
 *      copy given locloop into this, return this
 *
 *  BOOL Update (mINT16 depth, PF_VOLUME vol)
 *      Called with the depth and volume of the current loop on the
 *      incoming locloop of the outer loops.
 *      Update the locloop based on the supplied vol -- i.e. if the volume
 *      didn't fit in the cache before, but does for this loop, store
 *      it's depth. Do this for both L1 and L2 caches as appropriate.
 *      Return TRUE if this is the outermost loop to become localized
 *      in the furthest cache level (typically L2).
 *      
 *  BOOL Localized ()
 *      Return true if localized in any cache level, false otherwise
 *
 *  BOOL Localized_1L ()
 *      Return true if localized in L1
 *
 *  BOOL Localized_2L ()
 *      Return true if localized in L2
 *
 *  mINT16 Loop_1L ()
 *      Return depth of outermost loop that is localized in L1, -1 otherwise
 *
 *  mINT16 Loop_2L ()
 *      Return depth of outermost loop that is localized in L2, -1 otherwise
 *
 *  void Print (FILE* fp)
 *      Print to FILE*
 *
 *
 ***********************************************************************/

#ifndef pf_cache_INCLUDED
#define pf_cache_INCLUDED

#include "config_targ.h"
#include "config_cache.h"
#include "config_lno.h"
#include "wn.h"
#include "pf_common.h"

class CACHE_PARAMETERS
{
  CACHE_PARAMETERS (const CACHE_PARAMETERS&);
  CACHE_PARAMETERS* operator= (const CACHE_PARAMETERS&);
  MHD_LEVEL*    _mhdptr[MHD_MAX_LEVELS];
  INT           _num_levels;
public:
  CACHE_PARAMETERS (void) {}
  ~CACHE_PARAMETERS (void) {}
  void Initialize () {
    INT i;
    _num_levels = 0;
    for (i=Mhd.First(); i!=-1; i=Mhd.Next(i)) {
      MHD_LEVEL* mhdp = &Mhd.L[i];
      if ( mhdp->Valid()
	&& (mhdp->Type == MHD_TYPE_CACHE)
	&& mhdp->Prefetch_Level )
      {
        /* prefetching on for this cache level, so store it */
        _mhdptr[_num_levels++] = mhdp;
      }
    }
    VB_PRINT (
      printf ("Cache specs (%d levels): Target Processor = %s\n", 
              _num_levels, Targ_Name(Target));
      for (i=0; i<_num_levels; i++) {
        MHD_LEVEL* mhdp = _mhdptr[i];
        printf (" Level %3d cache: sz %7"PRId64", eff_sz %7"PRId64" (cf=%2d), ln_sz %4d\n",
                i+1, mhdp->Size, LNO_Prefetch_Cache_Factor*(mhdp->Effective_Size),
                LNO_Prefetch_Cache_Factor, mhdp->Line_Size);
      });
    PF_PRINT(
      fprintf (TFile, "Cache specs (%d levels): Target Processor = %s\n",
               _num_levels, Targ_Name(Target)); 
      for (i=0; i<_num_levels; i++) {
        MHD_LEVEL* mhdp = _mhdptr[i];
        fprintf (TFile, " Level %3d cache: sz %7"PRId64", eff_sz %7"PRId64" (cf=%2d), ln_sz %4d\n",
                i+1, mhdp->Size, LNO_Prefetch_Cache_Factor*(mhdp->Effective_Size),
                 LNO_Prefetch_Cache_Factor, mhdp->Line_Size);
      });
  }
  INT LineSize (INT level) {
    if ((level == 0) || (level > _num_levels)) {
      /* DevWarn ("Requesting line size at cache level %d", level); */
      return 0;
    }
    return _mhdptr[level-1]->Line_Size;
  }
  INT64 EffSize (INT level) {
    if ((level == 0) || (level > _num_levels)) {
      /* DevWarn ("Requesting effective size of cache level %d", level); */
      return 0;
    }
    return (LNO_Prefetch_Cache_Factor*(_mhdptr[level-1]->Effective_Size));
  }
  INT Levels () { return _num_levels; }
  BOOL Level1_Really_Level2 () {
    // L1 disabled for prefetching
    if (Mhd.L[0].Prefetch_Level == FALSE) return TRUE;
    // L1 disabled for Mhd
    if (Mhd.First() != 0) return TRUE;
    return FALSE;
  }
  void Print (FILE* fp) {
   INT i;
   fprintf (fp, "Cache has %d levels\n", _num_levels);
   for (i=0; i<_num_levels; i++) {
    _mhdptr[i]->Print (fp);
   }
  } 
};
extern CACHE_PARAMETERS Cache;

class PF_VOLUME {
  PF_VOLUME* operator= (const PF_VOLUME&);
public:
  UINT32    vol_1L;
  UINT32    vol_2L;
  PF_VOLUME () {
    vol_1L = 0;
    vol_2L = 0;
  }
  PF_VOLUME (const PF_VOLUME& vol) {
    vol_1L = vol.vol_1L;
    vol_2L = vol.vol_2L;
  }
  PF_VOLUME (INT vol_1, INT vol_2) {
    vol_1L = vol_1;
    vol_2L = vol_2;
  }
  ~PF_VOLUME (void) {}
  PF_VOLUME&    operator *= (INT lines) {
    vol_1L *= lines;
    vol_2L *= lines;
    return *this;
  }
  PF_VOLUME&    operator += (const PF_VOLUME& vol) {
    vol_1L += vol.vol_1L;
    vol_2L += vol.vol_2L;
    return *this;
  }
  BOOL Localized () {
    if (vol_1L <= Cache.EffSize(1)) return TRUE;
    if ((Cache.EffSize(2) > 0) && (vol_2L <= Cache.EffSize(2)))
      return TRUE;
    return FALSE;
  }
  BOOL Localized_1L () { return (vol_1L <= Cache.EffSize(1)); }
  BOOL Localized_2L () { return (vol_2L <= Cache.EffSize(2)); }
  void Print (FILE* fp) {
    fprintf (fp, "Vol: L1 %6d bytes", vol_1L);
    if (Cache.Levels()>1)
      fprintf (fp, ", L2 %6d bytes", vol_2L);
    fprintf (fp, "\n");
  }
};

class PF_LOCLOOP {
  mINT16    _loop_1L;    // depth of loop localized in 1st-level cache, else -1
  mINT16    _loop_2L;    // depth of loop localized in 2nd-level cache, else -1
  mINT16    _confidence_1L; // confidence of that loop
  mINT16    _confidence_2L; // ditto
  BOOL      _while_temporal_1L;    // temporal locality coz inside a while-loop
  BOOL      _while_temporal_2L;    // temporal locality coz inside a while-loop
public:
  PF_LOCLOOP () {
    _loop_1L = _loop_2L = -1;
    _confidence_1L = _confidence_2L = 3;
    _while_temporal_1L = _while_temporal_2L = FALSE;
  }
  PF_LOCLOOP* operator= (const PF_LOCLOOP& locloop) {
    _loop_1L = locloop._loop_1L;
    _loop_2L = locloop._loop_2L;
    _confidence_1L = locloop._confidence_1L;
    _confidence_2L = locloop._confidence_2L;
    _while_temporal_1L = locloop._while_temporal_1L;
    _while_temporal_2L = locloop._while_temporal_2L;
    return this;
  }
  /* Return TRUE if the outermost cache level has only now become
   * localized, FALSE otherwise.
   */
  BOOL Update (mINT16 depth, PF_VOLUME vol, mINT16 confidence) {
    BOOL updated = FALSE;
    switch (Cache.Levels()) {
    case 1:
      if ((_loop_1L == -1) && (vol.vol_1L < Cache.EffSize(1))) {
        _loop_1L = depth;
        _confidence_1L = confidence;
        updated = TRUE;
      }
      break;
    case 2:
      // if it fits in L1, must also fit in L2
      Is_True ((_loop_1L == -1) || (_loop_2L != -1),
               ("Funny loop -- localized in L1 but not in L2\n"));
      if ((_loop_1L == -1) && (vol.vol_1L < Cache.EffSize(1))) {
        _loop_1L = depth;
        _confidence_1L = confidence;
      }
      if ((_loop_2L == -1) && (vol.vol_2L < Cache.EffSize(2))) {
        _loop_2L = depth;
        _confidence_2L = confidence;
        updated = TRUE;
      }
      break;
    default:
      Cache.Print(stdout);
      FmtAssert (0, ("Cache has (%d) more than 2 levels\n", Cache.Levels()));
    }
    return updated;
  }
  BOOL Localized () {
    return ((_loop_2L != -1) || (_loop_1L != -1));
  }
  BOOL Localized_1L () { return (_loop_1L != -1); }
  BOOL Localized_2L () { return (_loop_2L != -1); }
  mINT16 Loop_1L ()    const { return _loop_1L; }
  mINT16 Loop_2L ()    const { return _loop_2L; }
  mINT16 Confidence_1L () const { return _confidence_1L; }
  mINT16 Confidence_2L () const { return _confidence_2L; }
  BOOL While_Temporal_1L () const { return _while_temporal_1L; }
  BOOL While_Temporal_2L () const { return _while_temporal_2L; }
  void Set_While_Temporal_1L () { _while_temporal_1L = TRUE; }
  void Set_While_Temporal_2L () { _while_temporal_2L = TRUE; }
  void Print (FILE* fp) {
    fprintf (fp, "Localized loops: _1L --> %d (conf: %d%s)",
             _loop_1L, _confidence_1L,
             (_while_temporal_1L?" while-temporal":""));
    if (Cache.Levels()>1)
      fprintf (fp, ", _2L --> %d (conf: %d%s)", _loop_2L, _confidence_2L,
               (_while_temporal_2L?" while-temporal":""));
    fprintf (fp, "\n");
  }
};

#endif // pf_cache_INCLUDED
