
#include "atlas_mmtesttime.h"

static int Mylcm(const int M, const int N)
/*
 * Returns least common multiple (LCM) of two positive integers M & N by
 * computing greatest common divisor (GCD) and using the property that
 * M*N = GCD*LCM.
 */
{
   register int tmp, max, min, gcd=0;

   if (M != N)
   {
      if (M > N) { max = M; min = N; }
      else { max = N; min = M; }
      if (min > 0)  /* undefined for negative numbers */
      {
         do  /* while (min) */
         {
            if ( !(min & 1) ) /* min is even */
            {
               if ( !(max & 1) ) /* max is also even */
               {
                  do
                  {
                     min >>= 1;
                     max >>= 1;
                     gcd++;
                     if (min & 1) goto MinIsOdd;
                  }
                  while ( !(max & 1) );
               }
               do min >>=1 ; while ( !(min & 1) );
            }
/*
 *          Once min is odd, halve max until it too is odd.  Then, use
 *          property that gcd(max, min) = gcd(max, (max-min)/2)
 *          for odd max & min
 */
MinIsOdd:
            if (min != 1)
            {
               do  /* while (max >= min */
               {
                  max -= (max & 1) ? min : 0;
                  max >>= 1;
               }
               while (max >= min);
            }
            else return( (M*N) / (1<<gcd) );
            tmp = max;
            max = min;
            min = tmp;
         }
         while(tmp);
      }
      return( (M*N) / (max<<gcd) );
   }
   else return(M);
}

void FillInSSEGenString
(
   ATL_mmnode_t *mmp,           /* ptr to MMNODE to generate */
   char pre,                    /* precision/type prefix */
   int M, int N, int K,         /* problem dimensions */
   int ldc                      /* ldc to use */
)
/*
 * RETURNS: string holding the make call that will generate the kernel
 * specified by mmp
 */
{
   char ln[2048];
   int i;

   i = sprintf(ln, "make %cgensseK mu=%d nu=%d ku=%d",
               pre, mmp->mu, mmp->nu, mmp->ku);
   i += sprintf(ln+i, " MB=%d NB=%d, KB=%d",
                FLAG_IS_SET(mmp->flag, MMF_MRUNTIME) ? 0 : M,
                FLAG_IS_SET(mmp->flag, MMF_NRUNTIME) ? 0 : N,
                FLAG_IS_SET(mmp->flag, MMF_KRUNTIME) ? 0 : K);
   i += sprintf(ln+i, " CASES/%cssegenf=%s", pre, mmp->rout);
   if (FLAG_IS_SET(mmp->flag, MMF_LDISKB))
      i += sprintf(ln+i, " lda=%d ldb=%d\n", K, K);
/*
 * Need to modify generator so some flag (maybe lda=-2?) means that
 * lda/ldb are runtime constants, but lda == ldb --> saves registers
 */
   else if (FLAG_IS_SET(mmp->flag, MMF_LDAB))
      i += sprintf(ln+i, " lda=0 ldb=0");
   else
      i += sprintf(ln+i, " lda=0 ldb=0");
   i += sprintf(ln+i, " ldc=0", ldc);
   i += sprintf(ln+i, " ssegenflags=\"");
   if (FLAG_IS_SET(mmp->flag, MMF_LDFLOAT))
      i += sprintf(ln+i, "-treatLoadsAsFloat 1");
   if (FLAG_IS_SET(mmp->flag, MMF_STFLOAT))
      i += sprintf(ln+i, " -treatStoresAsFloat 1");
   if (FLAG_IS_SET(mmp->flag, MMF_PFACOLS))
      i += sprintf(ln+i, " -prefetchAcols 1");
   if (FLAG_IS_SET(mmp->flag, MMF_PFABLK))
      i += sprintf(ln+i, " -prefetchABlock 1");
/*
 * I think you need to change your name to prefetchBCols.  You prefetch
 * nu columns of B, not rows, right?  For now, I use the name you presently
 * have
 */

   if (FLAG_IS_SET(mmp->flag, MMF_PFBCOLS))
      i += sprintf(ln+i, " -prefetchBRows 1");
/*
 * You need an option that prefetches C at top of loop
 */
   if (FLAG_IS_SET(mmp->flag, MMF_PFCELTS))
      i += sprintf(ln+i, " -prefetchCelts 1");
   i += sprintf(ln+i, "\"");
   fprintf(stderr, "\n\nGENSTRING = %s\n", ln);
   if (mmp->genstr)
      free(mmp->genstr);
   mmp->genstr = DupString(ln);
}

void FillInMMNode(ATL_mmnode_t *mmp, int ID, char pre, int M, int N, int K,
                  int mu, int nu, int ku, int lda, int ldb, int ldc,
                  int LDFLOAT, int STFLOAT,
                  int PFAcols, int PFAblk, int PFBcols)
/*
 * Take mmgen_sse's flags and produce fill in the standard ATL_mmnode_t
 * structure pointed to by mmp.  We assume that it doesn't have any
 */
{
   char ln[256];
   int i;

   if (mmp->auth)
      free(mmp->auth);
   if (mmp->rout)
      free(mmp->rout);
   if (mmp->comp)
      free(mmp->comp);
   if (mmp->cflags)
      free(mmp->cflags);
   if (mmp->str)
      free(mmp->str);
   if (mmp->genstr)
      free(mmp->genstr);
   mmp->auth = mmp->rout = mmp->comp = mmp->cflags = mmp->str =
               mmp->genstr = NULL;
   mmp->mu = mu;
   mmp->nu = nu;
   mmp->ku = ku;
   mmp->ID = ID;
   if (K > 0)
   {
      mmp->kbmin = 0;
      mmp->kbmax = 2000;
   }
   mmp->SSE = 3;
   mmp->lat = 1;
   mmp->muladd = 0;
   mmp->fftch = mmp->iftch = mmp->nftch = mmp->clean = 0;
   mmp->TA = AtlasTrans;
   mmp->TB = AtlasNoTrans;
   mmp->asmbits = 0;
   mmp->auth = DupString("Zalkin & Whaley");
   sprintf(ln, "%cgenmm_sse.c", pre);
   mmp->rout = DupString(ln);
/*
 * Need to figure out what to do if gcc isn't normal compiler, but ignore
 * for now and don't set comp & cflags
 */
   mmp->comp = mmp->cflags = NULL;
   mmp->str = mmp->genstr = NULL;
   i = 0;
   SET_FLAG(i, MMF_MRUNTIME, (M == 0));
   SET_FLAG(i, MMF_NRUNTIME, (N == 0));
   SET_FLAG(i, MMF_KRUNTIME, (K == 0));
   if (K > 0)
   {
      SET_FLAG(i, MMF_KUISKB, (K == ku));
      SET_FLAG(i, MMF_LDISKB, (K == lda && K == ldb));
   }
   SET_FLAG(i, MMF_LDAB, (lda == ldb));
   SET_FLAG(i, MMF_PFACOLS, PFAcols);
   SET_FLAG(i, MMF_PFABLK, PFAblk);
   SET_FLAG(i, MMF_PFBCOLS, PFBcols);
   mmp->flag = i;
}

ATL_mmnode_t *mmksearch_sse
(
   char pre     /* precision/type specifier, one of [s,d,c,z] */
)
/*
 * This routine will eventually be called by my master search, so put
 * all the logic in this routine and its children, and have an essentially
 * empty main() routine.
 * RETURNS: best found case
 */
{
#ifndef ATL_SSE3  /* can your generator work with SSE2 only? */
   return(NULL)
#else

   int mu, nu, ku, i, nb, inb, L1Elts, maxNB;
   int ID=10000;
   double maxmf=0.0, mf;
   int bestNB=0;
   ATL_mmnode_t *mmb;

   #ifdef ATL_GAS_x8632
      mu = 2; nu = 2;
   #else
      mu = 4; nu = 2;
   #endif
/*
 * Presently, just find best NB assuming above mu/nu & ku=NB is good
 */
   L1Elts = GetL1CacheSize() / pre2size(pre);
/*
 * Find maximum NB w/o call sqrt and bringing in math library
 */
   for (maxNB=16; maxNB*maxNB < L1Elts; maxNB += 4);
   if (maxNB > L1Elts)
     maxNB -= 4;
   if (maxNB < 16)
     maxNB = 16;
   mmb = GetMMNode();
   for (nb=16; nb <= maxNB; nb += maxNB)
   {
      FillInMMNode(mmb, ID++, pre, nb, nb, nb, mu, nu, nb, nb, nb,
                   0, 0, 0, 0, 0, 0);
      FillInSSEGenString(mmb, pre, nb, nb, nb, nb+8);
      #ifdef DEBUG
         assert(!MMKernelFailsTest(pre, nb, nb, nb, 0, mmb));
         assert(!MMKernelFailsTest(pre, nb, nb, nb, 1, mmb));
         assert(!MMKernelFailsTest(pre, nb, nb, nb, 2, mmb));
      #endif
      mf = TimeMMKernel(1, 0, mmb, pre, nb, nb, nb, nb, nb, nb+8, 1, -1, -1);
      if (mf > maxmf)
      {
         maxmf = mf;
         bestNB = nb;
      }
   }
   fprintf(stdout, "BEST NB=%d, MFLOP=%.2f\n", bestNB, maxmf);
   assert(!MMKernelFailsTest(pre, nb, nb, nb, 0, mmb));
   assert(!MMKernelFailsTest(pre, nb, nb, nb, 1, mmb));
   assert(!MMKernelFailsTest(pre, nb, nb, nb, 2, mmb));
   mmb->mbB = mmb->nbB = mmb->kbB = nb;
   mmb->mflop[0] = maxmf;
   return(mmb);
#endif
}

main(int nargs, char **args)
{
   ATL_mmnode_t *mmp;
   mmp = mmksearch_sse('d');
   KillMMNode(mmp);
   exit(0);
}
