//==============================================================================
//
// d/wilson_d510/cr_asm_alt.cc
//
//------------------------------------------------------------------------------
//
// Copyright (C) 2007 Thomas Streuer
//
// This file is part of BQCD -- Berlin Quantum ChromoDynamics program
//
// BQCD 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 3 of the License, or
// (at your option) any later version.
//
// BQCD 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 BQCD.  If not, see <http://www.gnu.org/licenses/>.
//
//------------------------------------------------------------------------------
#include <stdlib.h>
#include <stdio.h>

#include "defs.h"
#include "neighb.h"

#define NO_LOAD_PSI_
#define NO_LOAD_U_
#define NO_LOAD_PSI0_
#define NO_STORE_RES_
#define NO_LOAD_NB_
#define NO_PREFETCH_

#define NO_SYNC_
#define NO_UPD_STATE_
#define NO_BND_PROJ_
#define NO_FAR_LOAD_
#define NO_FAR_PREF_
#define FAR_PRELOAD_
#define NO_NEAR_PREF_
#define NO_PREF_PSI0_
#define NO_PREF_STORE_
#define NO_PREF_U_


#define DEBUG_PREF_
#define DEBUG_PREF_LOAD_U_
#define DEBUG_PREF_PREF_U_
#define DEBUG_PREF_LOAD_P_
#define DEBUG_PREF_PREF_P_N_
#define DEBUG_PREF_PREF_P_F_

#define NOP_LOOP_

#define SHMEM_COPY
#define SHMEM_COPY_PUT
#define FASTBCOPY_

#define IEEE_

#define n_ins_upd_load_regs 7

bool single;
bool dagger;
char prec_str;
char d_string[30];
char* sub_name="w_dirac";
const int mod_bnd[4]={MOD_BND_X, MOD_BND_Y, MOD_BND_Z, MOD_BND_T};
const int mod_num[4]={MOD_NUM_X, MOD_NUM_Y, MOD_NUM_Z, MOD_NUM_T};

const int n_regs_load_psi=2;
const int n_regs_load_U=4;
const int n_regs_load_psi0=4;
const int n_regs_store_res=4;

const int n_regs_inp=6;
const int n_regs_loc=84;
const int n_regs_rot=8;
#ifdef SHMEM_COPY
const int n_regs_out=4;
#else
const int n_regs_out=0;
#endif

const int n_float_preserve=20;

const int st_size=16*n_float_preserve;


//input registers
const int inp_reg_base_U=32;
const int inp_reg_base_psi_inp=33;
const int inp_reg_base_res=34;
const int inp_reg_x=35;
const int inp_reg_cnt=36;
const int inp_reg_w_data=37;
//const int inp_reg_local_mod=37;

int reg_base_U[4], reg_base_psi_inp, reg_base_res, reg_x, reg_cnt, reg_local_mod;
int reg_U[2][9]; // always 2 consecutive regs. for re/im
int reg_psi[2][6];
int reg_psi_n[6];
int reg_res_h[6];
int reg_res[12];
int reg_kappa;
int reg_mod_bnd[2];

int reg_load_psi[n_regs_load_psi];
int reg_load_psi0[n_regs_load_psi0];
int reg_load_U[n_regs_load_U];
int reg_store_res[n_regs_store_res];
int reg_mod_offs[2][4];
int reg_nb[2][4], reg_nb_n, reg_nb_pref, reg_nb_pref_n;
int reg_load_nb, reg_pref_loc_nb, reg_pref_rem_nb, reg_tmp_load_psi,
  reg_tmp_load_U, reg_tmp, reg_base_psi,
  reg_tmp_pref_psi, reg_tmp_pref_U, reg_local_mod_mask,
  reg_load_g, reg_load_l, reg_load_n, reg_load_lU,
  reg_pref_n_nb, reg_pref_n_g, reg_pref_n_l, reg_pref_n_n, reg_pref_near,
  reg_pref_f_nb, reg_pref_f_g, reg_pref_f_l, reg_pref_f_n, reg_pref_far,
  reg_pref_U, reg_pref_lU, reg_preload, reg_w_data;
int reg_nb_bnd[2][4];
int reg_nb_bnd_pref[2][4];
int curr_reg_idx=1;
int pred_first, pred_last, pred_load_rem, pred_pref_near, pred_pref_far, pred_spin_proj[2], pred_sync, pred_step2, pred_inc_state, pred_pref_state,
  pred_no_far_pref;
int reg_scratch, reg_save_preds, reg_save_lc, reg_save_pfs, reg_save_b;
int reg_my_state, reg_my_state_addr, reg_pref_state;

#ifdef DEBUG_PREF
int reg_debug_pref;
bool debuf_pref_print=false;
int debug_pref_n;
#endif

const int pref_dist_near=1;
const int pref_dist_far=4;
const int pref_dist_state=6;

const int slot_i=0;
const int slot_f=1;
const int slot_m=2;
const int slot_b=3;

bool main_loop;

const int perm2[2][2]=
{{0,1},{1,0}};
const int perm3[6][3]=
{{0,1,2},{1,2,0},{2,0,1},{0,2,1},{2,1,0},{1,0,2}};
const int templ_mmf=0;
const int templ_mfi=1;
const int templ_mii=2;
const int templ_mfb=3;
const int templ3[4][4][4][2]= // {templ, perm}
{{{{-1, -1}, { 1,  3}, {-1, -1}, {-1, -1}},
  {{-1, -1}, {-1, -1}, { 1,  4}, {-1, -1}},
  {{-1, -1}, { 1,  1}, {-1, -1}, {-1, -1}},
  {{-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}}},
 {{{ 1,  0}, {-1, -1}, { 1,  2}, {-1, -1}},
  {{-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}},
  {{ 1,  5}, {-1, -1}, { 0,  1}, {-1, -1}},
  {{-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}}},
 {{{ 1,  5}, { 1,  3}, {-1, -1}, {-1, -1}},
  {{ 1,  0}, {-1, -1}, { 0,  3}, {-1, -1}},
  {{-1, -1}, { 0,  0}, {-1, -1}, {-1, -1}},
  {{-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}}},
 {{{-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}},
  {{-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}},
  {{-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}},
  {{-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}}}};
const int templ2[4][4][2]=
{{{ 2,  0}, { 1,  1}, { 2,  1}, {-1, -1}},
 {{ 1,  0}, {-1, -1}, { 1,  1}, { 3,  0}},
 {{ 2,  0}, { 1,  0}, {-1, -1}, {-1, -1}},
 {{-1, -1}, { 3,  1}, {-1, -1}, {-1, -1}}};



const int P2o_table[4][2]={{3,2},{3,2},{2,3},{0,0}};
const int P2r_table[4]={1,0,1,0};                      // 0 : real, 1 : imag 
const int P2sr_table[4][2]={{1,1},{0,1},{1,0},{0,0}};  // 0 : +   , 1 : - (re)
const int P2si_table[4][2]={{0,0},{0,1},{0,1},{0,0}};  // 0 : +   , 1 : - (im)


//              mu    hs
//                 fw   r/i
const int fsp_i[3][2][2][2][2]=
{{{{{0, 7}, {1, 6}}, {{2, 5}, {3, 4}}},
  {{{0, 7}, {1, 6}}, {{2, 5}, {3, 4}}}},
 {{{{0, 6}, {1, 7}}, {{2, 4}, {3, 5}}},
  {{{0, 6}, {1, 7}}, {{2, 4}, {3, 5}}}},
 {{{{0, 5}, {1, 4}}, {{2, 7}, {3, 6}}},
  {{{0, 5}, {1, 4}}, {{2, 7}, {3, 6}}}}};
const int fsp_s[3][2][2][2][2]=
{{{{{0, 0}, {0, 1}}, {{0, 0}, {0, 1}}},
  {{{0, 1}, {0, 0}}, {{0, 1}, {0, 0}}}},
 {{{{0, 1}, {0, 1}}, {{0, 0}, {0, 0}}},
  {{{0, 0}, {0, 0}}, {{0, 1}, {0, 1}}}},
 {{{{0, 0}, {0, 1}}, {{0, 1}, {0, 0}}},
  {{{0, 1}, {0, 0}}, {{0, 0}, {0, 1}}}}};

//             mu spin-2
//                   r/i ->spin, r/i, sign
const int hsp_i[3][2][2][3]=
{{{{1, 1, 1}, {1, 0, 0}}, {{0, 1, 1}, {0, 0, 0}}},
 {{{1, 0, 0}, {1, 1, 0}}, {{0, 0, 1}, {0, 1, 1}}},
 {{{0, 1, 1}, {0, 0, 0}}, {{1, 1, 0}, {1, 0, 1}}}};
const int hsp_s[3][2][2]=
{{{1, 0}, {1, 0}},
 {{0, 0}, {1, 1}},
 {{1, 0}, {0, 1}}};


void get_next_mu_f(int mu, int f, int& n_mu, int& n_f)
{
  if (mu!=3)
  {
    n_mu=mu+1;
    n_f=f;
  }
  else
  {
    n_mu=0;
    n_f=1-f;
  }
}

void get_prev_mu_f(int mu, int f, int& p_mu, int& p_f)
{
  if (mu!=0)
  {
    p_mu=mu-1;
    p_f=f;
  }
  else
  {
    p_mu=3;
    p_f=1-f;
  }
}



void fma(int d, int s1, int s2, int s3, bool add, int pred=-1)
{
#ifdef IEEE
  if (pred>=0)
    printf("(p%i)\tf%sma.d\tf%i = f%i, f%i, f%i\n", pred, add? "" : "n",
	   d, s1, s2, s3);
  else
    printf("\tf%sma.d\tf%i = f%i, f%i, f%i\n", add? "" : "n",
	   d, s1, s2, s3);
#else
  if (pred>=0)
    printf("(p%i)\tf%sma\tf%i = f%i, f%i, f%i\n", pred, add? "" : "n",
	   d, s1, s2, s3);
  else
    printf("\tf%sma\tf%i = f%i, f%i, f%i\n", add? "" : "n",
	   d, s1, s2, s3);
#endif
}

void load_f_pair(int dest, int src, bool inc=false, int pred=-1)
{
  if (pred>=0)
    printf("(p%i)", pred);
  if (inc)
    printf("\tldfp%c\tf%i, f%i = [r%i], %i\n",
	   prec_str, dest, dest+1, src, 16);
  else
    printf("\tldfp%c\tf%i, f%i = [r%i]\n",
	   prec_str, dest, dest+1, src);
}

void store_f(int src, int dest, int inc=0, int pred=-1)
{
  if (pred>=0)
    printf("(p%i)", pred);
  if (inc)
    printf("\tstf%c.nta\t[r%i] = f%i, %i\n",
	   prec_str, dest, src, 8*inc);
  else
    printf("\tstf%c.nta\t[r%i] = f%i\n",
	   prec_str, dest, src);
}

void ld4(int dest, int reg, int inc)
{
  printf("\tld4\tr%i = [r%i], %i\n", dest, reg, inc);
}

void ld8(int dest, int reg, int inc, int pred=-1)
{
  if (pred==-1)
    printf("\tld8\tr%i = [r%i], %i\n", dest, reg, inc);
  else
    printf("(p%i)\tld8\tr%i = [r%i], %i\n", pred, dest, reg, inc);
}

void extr_bnd(int dest, int fw, int dir)
{
  printf("\textr.u\tr%i = r%i, %i, %i\n", dest, reg_mod_bnd[fw], 16*dir, 16);
}
void prefetch(int reg, int inc, bool excl=false, int pred=-1)
{
  char pred_str[10];
  char inc_str[10];
  if (pred==-1)
    pred_str[0]=0;
  else
    sprintf(pred_str, "(p%i)", pred);
  if (inc==0)
    inc_str[0]=0;
  else
    sprintf(inc_str, ", %i", inc);
  if (excl)
    printf("%s\tlfetch.fault.excl.nt1\t[r%i]%s\n", pred_str, reg, inc_str);
  else
    printf("%s\tlfetch.fault.nt1\t[r%i]%s\n", pred_str, reg, inc_str);
}


class bundle_slot
{
public:
  bundle_slot();
  virtual void print();
  virtual int slot_type();
  virtual void debug();
};

class U_mult_slot: public bundle_slot
{
  int n, mu, fw;
public:
  U_mult_slot(int n_, int fw_, int mu_);
  virtual void print();
  virtual int slot_type();
};

class psi_pr1_slot: public bundle_slot
{
  int n, mu, fw;
public:
  psi_pr1_slot(int n_, int fw_, int mu_);
  virtual void print();
  virtual int slot_type();
};

class psi_pr2_slot: public bundle_slot
{
  int n, mu, fw;
public:
  psi_pr2_slot(int n_, int fw_, int mu_);
  virtual void print();
  virtual int slot_type();
};

class U_load_slot: public bundle_slot
{
  int n, mu, fw;
public:
  U_load_slot(int n_, int fw_, int mu_);
  virtual void print();
  virtual int slot_type();
  virtual void debug();
};


class psi_load_slot: public bundle_slot
{
  int n, mu, fw;
public:
  psi_load_slot(int n_, int fw_, int mu_);
  virtual void print();
  virtual void debug();
  virtual int slot_type();
};

class psi0_load_slot: public bundle_slot
{
  int n, mu, fw;
public:
  psi0_load_slot(int n_, int fw_, int mu_);
  virtual void print();
  virtual int slot_type();
};

class res_store_slot: public bundle_slot
{
  int n, mu, fw;
public:
  res_store_slot(int n_, int fw_, int mu_);
  virtual void print();
  virtual int slot_type();
};


class upd_pred_slot: public bundle_slot
{
  int n, mu, fw;
public:
  upd_pred_slot(int n_, int fw_, int mu_);
  virtual void print();
  virtual int slot_type();
};

class upd_load_reg_slot: public bundle_slot
{
  int n, mu, fw, tp, l_reg_nb, l_reg_n, l_reg_l, l_reg_g, l_reg_dest, l_pred;
  int r_nb[2][4];
public:
  upd_load_reg_slot(int n_, int fw_, int mu_, int t);
  virtual void print();
  virtual void debug();
  virtual int slot_type();
};

class upd_load_reg_slot2: public bundle_slot
{
  int n, mu, fw, tp, l_reg_n, l_reg_l, l_reg_dest;
public:
  upd_load_reg_slot2(int n_, int fw_, int mu_, int t);
  virtual void print();
  virtual int slot_type();
  virtual void debug();
};

// class upd_load_reg_U_slot: public bundle_slot
// {
//   int n, mu, fw;
// public:
//   upd_load_reg_U_slot(int n_, int fw_, int mu_);
//   virtual void print();
//   virtual int slot_type();
// };

class upd_pref_reg_psi_slot: public bundle_slot
{
  int n, mu, fw;
public:
  upd_pref_reg_psi_slot(int n_, int fw_, int mu_);
  virtual void print();
  virtual int slot_type();
};

class upd_pref_reg_U_slot: public bundle_slot
{
  int n, mu, fw;
public:
  upd_pref_reg_U_slot(int n_, int fw_, int mu_);
  virtual void print();
  virtual int slot_type();
};

class branch_slot: public bundle_slot
{
  int n, mu, fw;
public:
  branch_slot(int n_, int fw_, int mu_);
  virtual void print();
  virtual int slot_type();
};


class pref_state_slot: public bundle_slot
{
  int n, mu, fw;
public:
  pref_state_slot(int n_, int fw_, int mu_);
  virtual void print();
  virtual int slot_type();
};



class bundle
{
public:
  bool         stop;
  int          n_occ;
  bundle_slot* slot[4];
  bundle();
  void print();
  void set_stop();
  void add(bundle_slot* s);
  void debug();
};

void bundle_slot::print()
{
}

bundle_slot::bundle_slot()
{
}

int bundle_slot::slot_type()
{
  return 0;
}

bundle::bundle()
{
  slot[0]=0;
  slot[1]=0;
  slot[2]=0;
  slot[3]=0;
  stop=false;
  n_occ=0;
}

void bundle::add(bundle_slot* s)
{
//   if (n_occ==3)
//   {
//     printf("bundle already full\n");
//      exit(1);
//    }
  if (n_occ<4)
    slot[n_occ++]=s;
}


void nop_print(int t)
{
  if (t==slot_m)
    printf("\tnop.m 0\n");
  else if (t==slot_i)
    printf("\tnop.i 0\n");
  else if (t==slot_f)
    printf("\tnop.f 0\n");
  else if (t==slot_b)
    printf("\tnop.b 0\n");
  else
  {
    fprintf(stderr, "unknown slottype: %i\n", t);
    exit(1);
  }
}

void bundle::print()
{
  printf("{\n");
  if (n_occ==1)
#ifdef NOP_LOOP
    nop_print(slot[0]->slot_type());
#else
    slot[0]->print();
#endif
  else if (n_occ==2)
  {
    int templ=templ2[slot[0]->slot_type()][slot[1]->slot_type()][0];
    int perm=templ2[slot[0]->slot_type()][slot[1]->slot_type()][1];
    if (templ<0 || perm<0)
    {
      printf("error:t2 %i %i\n", slot[0]->slot_type(),
	     slot[1]->slot_type());
      exit(1);
    }
    if (templ==templ_mfi)
      printf(".mfi\n");
    if (templ==templ_mmf)
      printf(".mmf\n");
    if (templ==templ_mii)
      printf(".mii\n");
    if (templ==templ_mfb)
      printf(".mfb\n");
#ifdef NOP_LOOP
    nop_print(slot[perm2[perm][0]]->slot_type());
//     nop_print(slot[perm2[perm][1]]->slot_type());
#else
    slot[perm2[perm][0]]->print();
    slot[perm2[perm][1]]->print();	     
#endif
  }
  else if (n_occ==3)
  {
    int templ=templ3[slot[0]->slot_type()][slot[1]->slot_type()]
      [slot[2]->slot_type()][0];
    int perm=templ3[slot[0]->slot_type()][slot[1]->slot_type()]
      [slot[2]->slot_type()][1];
    if (templ<0 || perm<0)
    {
      printf("error:t3 %i %i %i\n", slot[0]->slot_type(),
	     slot[1]->slot_type(), slot[2]->slot_type());
      exit(1);
    }
    if (templ==templ_mfi)
      printf(".mfi\n");
    if (templ==templ_mmf)
      printf(".mmf\n");
#ifdef NOP_LOOP
    nop_print(slot[perm3[perm][0]]->slot_type());
//     nop_print(slot[perm3[perm][1]]->slot_type());
//     nop_print(slot[perm3[perm][2]]->slot_type());
#else
    slot[perm3[perm][0]]->print();
    slot[perm3[perm][1]]->print();
    slot[perm3[perm][2]]->print();
#endif
  }
  else
  {
    printf("bundle full\n");
    for (int i=0; i<n_occ; i++)
    {
      printf("\t%i\t", i);
      slot[i]->print();
    }
  }
  printf("}%s\n", stop? ";;" : "");
}

void bundle::debug()
{
  for (int i=0; i<n_occ; i++)
    slot[i]->debug();
}



void bundle::set_stop()
{
  stop=true;
}

void bundle_slot::debug()
{
}

U_mult_slot::U_mult_slot(int n_, int fw_, int mu_)
{
  n=n_;
  mu=mu_;
  fw=fw_;
}



void U_mult_slot::print()
{
  int n_group=n/12;
  int target_n=n%12;
  int target_hsp=(target_n/2)%2;
  int n12=n_group%2;
  int col=n_group/2;
  int target_n2=target_n/2;
  int row=target_n/4;
  int re=target_n%2;
  int reg_dest=reg_res_h[target_n/2]+re;
  if (mu==3)
  {
    if ((fw==0 && !dagger) || (fw==1 && dagger)) //XXX
//    if ((fw==0))
      reg_dest=reg_res[2+target_n2+2*(target_n2/2)]+re;
    else
      reg_dest=reg_res[target_n2+2*(target_n2/2)]+re;
    //printf("D %i f%i t%i I%i\n", reg_dest, fw, target_n, target_n%4+8*target_n/4);
  }
  int reg_src_U=reg_U[curr_reg_idx][row+3*col];
  if (fw==1)
    reg_src_U=reg_U[curr_reg_idx][col+3*row];
  if (n12==1)
    reg_src_U++;
  int reg_src_psi=reg_psi[curr_reg_idx][2*col+target_hsp];
printf("//row %i t %i\n", row, target_hsp);
//  if (fw==1)
//    reg_src_psi=reg_psi[curr_reg_idx][3*row+target_hsp];
  if ( (re==0 && n12==1) || (re==1 && n12==0) )
    reg_src_psi++;
  bool add;
  if (fw==0)
    add=!(re==0 && n12==1);
  else
    add=!(re==1 && n12==1);
  if (mu==3)
    add=!add;
  int reg_acc;
  if (mu!=3 && n_group==0)
    reg_acc=0;
  else
    reg_acc=reg_dest;

  fma(reg_dest, reg_src_U, reg_src_psi, reg_acc, !add);
}

int U_mult_slot::slot_type()
{
  return slot_f;
}

psi_load_slot::psi_load_slot(int n_, int fw_, int mu_)
{
  n=n_;
  mu=mu_;
  fw=fw_;
}

void psi_load_slot::print()
{
  int l_reg=n%n_regs_load_psi;
  int n_load=n/n_regs_load_psi;
  bool load=true;
  bool inc_reg=false;
  int t_n=n_load;
  int a;
  bool inc;
  int target;
  bool predic=false;
  bool spinpred=false;
  if (mu!=3)
  {
    int todo=12;
    for (int i=0; i<l_reg; i++)
    {
      int d=(todo+n_regs_load_psi-i-1)/(n_regs_load_psi-i);
      t_n+=d;
      todo-=d;
    }
    int spin=t_n%4;
    int col=t_n/4;
    if (spin<=1)
      target=reg_psi[1-curr_reg_idx][2*col+spin];
    else
    {
      target=reg_psi_n[2*col+spin-2];
      spinpred=true;
    }
    inc=n+n_regs_load_psi<12;
  }
  else
  {
    if (n_regs_load_psi==4)
    {
      if (n>=6)
	load=false;
      int todo=6;
      for (int i=0; i<l_reg; i++)
      {
	int d=(todo+n_regs_load_psi-i-1)/(n_regs_load_psi-i);
	t_n+=d;
	todo-=d;
      }
      int spin=t_n%2;
      int col=t_n/2;
      target=reg_psi[1-curr_reg_idx][2*col+spin];
      inc=n+n_regs_load_psi<6;
    }
    else if (n_regs_load_psi==2)
    {
      l_reg=n%2;
      inc=true;
      if (n==0)
	target=0;
      if (n==1)
	target=2;
      if (n==2)
	target=1;
      if (n==3)
	target=3;
      if (n==8)
	target=4;
      if (n==9)
	target=5;
      if (n==2 || n==3)
	inc=false;
      if (n==4 || n==5 || n==6 || n==7)
      {
	load=false;
	inc_reg=true;
      }
      
      if (n==4)
	a=3*16;
      if (n==5)
	a=2*16;
      if (n==6)
	a=4*16;
      if (n==7)
	a=2*16;
      if (n==6 || n==7)
	predic=true;
      else
	predic=false;
      if (load)
	target=reg_psi[1-curr_reg_idx][target];
    }
    else
    {
      printf("error load psi\n");
      exit(1);
    }

  }
  int pred=-1;
  if (spinpred)
    pred=pred_spin_proj[1-curr_reg_idx];
  else
    if (mu==0 && fw==0)
      pred=pred_last+1;
#ifndef NO_LOAD_PSI
  printf("/* LL %i*/\n", n);
  if (load)
    load_f_pair(target, reg_load_psi[l_reg], inc, pred);
  if (inc_reg)
    if (predic)
      printf("(p%i)\tadds\tr%i = %i, r%i\n", pred_spin_proj[1-curr_reg_idx],
	     reg_load_psi[l_reg], a, reg_load_psi[l_reg]);
    else
      printf("\tadds\tr%i = %i, r%i\n", reg_load_psi[l_reg], a,
	     reg_load_psi[l_reg]);
#else
//   if (mu=2 && fw==0)
//     load_f_pair(target, reg_load_psi[l_reg], inc, pred);
#endif
}


void psi_load_slot::debug()
{
#ifdef DEBUG_PREF_LOAD_P
  int l_reg=n%n_regs_load_psi;
  int n_load=n/n_regs_load_psi;

  int t_n=n_load;
  bool inc;
  int target;
  if (mu!=3)
  {
    int todo=12;
    for (int i=0; i<l_reg; i++)
    {
      int d=(todo+n_regs_load_psi-i-1)/(n_regs_load_psi-i);
      t_n+=d;
      todo-=d;
    }
    int spin=t_n%4;
    int col=t_n/4;
    if (spin<=1)
      target=reg_psi[1-curr_reg_idx][2*col+spin];
    else
      target=reg_psi_n[2*col+spin-2];
    inc=n+n_regs_load_psi<12;
  }
  else
  {
    if (n_regs_load_psi<3)
    {
      printf("error load psi\n");
      exit(1);
    }
    int todo=6;
    for (int i=0; i<l_reg; i++)
    {
      int d=(todo+n_regs_load_psi-i-1)/(n_regs_load_psi-i);
      t_n+=d;
      todo-=d;
    }
    int spin=t_n%2;
    int col=t_n/2;
    target=reg_psi[1-curr_reg_idx][2*col+spin];
    inc=n+n_regs_load_psi<6;
  }
  int pred=-1;
  if (mu==0 && fw==0)
    pred=pred_last+1;
#ifndef NO_LOAD_PSI
  printf("\tst8\t[r%i] = r%i, %i\n;;\n",
   	 reg_debug_pref, reg_load_psi[l_reg], 8);
#endif
#endif
}

int psi_load_slot::slot_type()
{
  return slot_m;
}

psi0_load_slot::psi0_load_slot(int n_, int fw_, int mu_)
{
  n=n_;
  mu=mu_;
  fw=fw_;
}

void psi0_load_slot::print()
{
  if (n<12)
  {
    int l_reg=n%n_regs_load_psi0;
    int n_load=n/n_regs_load_psi0;
    int t_n=n_load;
    bool inc;
    int target;
    int todo=12;
    for (int i=0; i<l_reg; i++)
    {
      int d=(todo+n_regs_load_psi0-i-1)/(n_regs_load_psi0-i);
      t_n+=d;
      todo-=d;
    }
    int spin=t_n%4;
    int col=t_n/4;
    target=reg_res[4*col+spin];
    inc=n+n_regs_load_psi0<12;
#ifndef NO_LOAD_PSI0
    load_f_pair(target, reg_load_psi0[l_reg], inc);
#endif
  }
  else if (n-12<n_regs_load_psi0)
  {
    n-=12;
    int lr=12/n_regs_load_psi0;
    if (n<(12-n_regs_load_psi0*lr))
      lr++;
    lr--;
    int inc=(12-lr)*16;
//     if (n==0 || n==3)
//       inc+=192*(pref_dist_near-1);

    printf("\tadds\tr%i = %i, r%i\n", reg_load_psi0[n], inc,
	   reg_load_psi0[n]);
  }
  else
  {
    n-=12+n_regs_load_psi0;
    if (n_regs_load_psi0!=4)
    {
      printf("error 41484\n");
      exit(1);
    }
//     int inc=(1-pref_dist_near)*192;
    int inc=0;
#ifndef NO_PREF_PSI0
    if (n==0)
    {
      printf("/* prefetch psi0 1 */\n");
      prefetch(reg_load_psi0[0], inc);
    }
    if (n==1)
    {
      printf("/* prefetch psi0 2 */\n");
      prefetch(reg_load_psi0[3], inc);
    }
#endif
//     if (n==2)
//       prefetch(reg_load_psi0[2], inc+16);
//     if (n==3)
//       prefetch(reg_load_psi0[3], inc+16);
    
//     if (n==4)
//       prefetch(reg_load_psi0[0], -16);
//     if (n==5)
//       prefetch(reg_load_psi0[1], -16);
//     if (n==6)
//       prefetch(reg_load_psi0[2], -16);
//     if (n==7)
//       prefetch(reg_load_psi0[3], -16);


  }
}

int psi0_load_slot::slot_type()
{
  return slot_m;
}

res_store_slot::res_store_slot(int n_, int fw_, int mu_)
{
  n=n_;
  mu=mu_;
  fw=fw_;
}

void res_store_slot::print()
{
  if (n<24)
  {
    int l_reg=n%n_regs_store_res;
    int n_load=n/n_regs_store_res;

    int t_n=n_load;
    int inc;
    int src;
    int todo=24;
    for (int i=0; i<l_reg; i++)
    {
      int d=(todo+n_regs_store_res-i-1)/(n_regs_store_res-i);
      t_n+=d;
      todo-=d;
    }
    int spin=(t_n/2)%4;
    int col=t_n/8;
    int re=t_n%2;
    src=reg_res[4*col+spin]+re;
    if (n+n_regs_store_res<24)
      inc=1;
    else
    {
      inc=1+24-24/n_regs_store_res;
//       if (l_reg==0 || l_reg==3)
//  	inc+=24*(pref_dist_near-1);
    }
#ifndef NO_STORE_RES
    store_f(src, reg_store_res[l_reg], inc, pred_first+1);
#endif
  }
  else
  {
    n-=24;
    if (n_regs_store_res!=4)
    {
      printf("error 73445\n");
      exit(1);
    }
#ifndef NO_PREF_STORE
//     int inc=(1-pref_dist_near)*192;    
    int inc=0;
    if (n==0)
    {
      printf("/* prefetch store 1*/\n");
      prefetch(reg_store_res[0], inc, true);
    }
    if (n==1)
    {
      printf("/* prefetch store 2*/\n");
      prefetch(reg_store_res[3], inc, true);
    }
#endif
  }
}

int res_store_slot::slot_type()
{
  return slot_m;
}

// upd_nb_slot::upd_nb_slot(int n_, int fw_, int mu_)
// {
//   n=n_;
//   mu=mu_;
//   fw=fw_;
// }

// void upd_nb_slot::print()
// {
//   int fw_n, mu_n;
//   get_next_mu_f(mu, fw, mu_n, fw_n);

//   int dist=(pref_dist_near-1)*8*4;
//   if (n==0)
//     printf("\tmov\tr%i = r%i\n", reg_nb[fw][mu], reg_nb_n);
//   if (n==1)
//     printf("\tmov\tr%i = r%i\n", reg_nb_pref, reg_nb_pref_n);
// #ifndef NO_LOAD_NB
//   if (n==2)
//     ld4(reg_nb_n, reg_load_nb, dist);
//   if (n==3)
//     ld4(reg_nb_pref_n, reg_load_nb, 4-dist);
// #endif

// }

// int upd_nb_slot::slot_type()
// {
//   return slot_m;
// }

upd_pred_slot::upd_pred_slot(int n_, int fw_, int mu_)
{
  n=n_;
  mu=mu_;
  fw=fw_;
}

void upd_pred_slot::print()
{
  if (n==0)
    if (MOD_BND>0)
      printf("(p%i)\tcmp.eq\tp%i, p%i = 0, r%i\n", pred_step2, pred_last, pred_last+1, reg_cnt);
    else
      printf("\tcmp.eq\tp%i, p%i = 0, r%i\n", pred_last, pred_last+1, reg_cnt);
  if (n==1)
    if (MOD_BND>0)
      printf("(p%i)\tadds\tr%i = -1, r%i\n", pred_step2, reg_cnt, reg_cnt);
    else
      printf("\tadds\tr%i = -1, r%i\n", reg_cnt, reg_cnt);
  if (n==2)
    printf("\tcmp.ne\tp%i, p%i = r0, r0\n", pred_first, pred_first+1);
  if (n==3)
    printf("\tadds\tr%i = 1, r%i\n", reg_x, reg_x);


#ifndef NO_UPD_STATE
  if (n==100)
    printf("(p%i)\tcmp.eq.unc p%i, p%i = IPF_DIRAC_SYNC_T2-1, r%i\n", pred_step2, pred_inc_state, 0, reg_cnt);
  if (n==101)
    printf("(p%i)\tadds\tr%i = %i, r%i\n;;\n", pred_inc_state, reg_my_state, 1, reg_my_state);
  if (n==102)
    printf("(p%i)\tst8.rel\t[r%i] = r%i\n", pred_inc_state, reg_my_state_addr, reg_my_state);
  if (n==200)
    printf("(p%i)\tcmp.eq\tp%i, p%i = r%i, r%i\n", pred_inc_state, 
 	   pred_no_far_pref, 0, 0, 0);
#endif


}

int upd_pred_slot::slot_type()
{
  if (n==102)
    return slot_m;
  return slot_i;
}


upd_load_reg_slot::upd_load_reg_slot(int n_, int fw_, int mu_, int t)
{
  n=n_;
  mu=mu_;
  fw=fw_;
  tp=t;
  if (t==0) //load
  {
    l_reg_nb=reg_load_nb;
    l_reg_g=reg_load_g;
    l_reg_l=reg_load_l;
    l_reg_n=reg_load_n;
    l_reg_dest=reg_load_psi[0];
    l_pred=pred_load_rem;
    for (int i=0; i<8; i++)
      r_nb[0][i]=reg_nb_bnd[0][i];
  }
  else if (t==1) // near prefetch
  {
    l_reg_nb=reg_pref_n_nb;
    l_reg_g=reg_pref_n_g;
    l_reg_l=reg_pref_n_l;
    l_reg_n=reg_pref_n_n;
    l_reg_dest=reg_pref_near;
    l_pred=pred_pref_near;
    for (int i=0; i<8; i++)
      r_nb[0][i]=0;
  }
  else if (t==2) // far prefetch
  {
    l_reg_nb=reg_pref_f_nb;
    l_reg_g=reg_pref_f_g;
    l_reg_l=reg_pref_f_l;
    l_reg_n=reg_pref_f_n;
    l_reg_dest=reg_pref_far;
    l_pred=pred_pref_far;
    for (int i=0; i<8; i++)
      r_nb[0][i]=reg_nb_bnd_pref[0][i];
  }

}

void upd_load_reg_slot::print()
{
  if (n==0)
  {
    printf("/* --> U */\n");
    if (fw==0)
      ld4(l_reg_n, l_reg_nb, 4);
    else
      ld8(l_reg_n, l_reg_nb, 8);

  }
  else if (n==1)
    printf("\tdep.z\tr%i = r%i, %i, MOD_COORD_LEN\n", l_reg_l, l_reg_n, 6);
  else if (n==2)
    printf("\tand\tr%i = r%i, r%i\n", l_reg_g, l_reg_n, reg_local_mod_mask);
  else if (n==3)
    printf("\tshladd\tr%i = r%i, %i, r%i\n", l_reg_l, l_reg_l, 1, l_reg_l);
  else if (n==4)
    printf("\tcmp.eq\tp%i, p%i = r%i, r%i\n",
	   l_pred+1, l_pred, l_reg_g, reg_local_mod);
//     printf("\tcmp.eq\tp%i, p%i = r%i, r%i\n",
// 	   l_pred+1, l_pred, l_reg_g, l_reg_g);
  else if (n==5)
    printf("\tadd\tr%i = r%i, r%i\n", l_reg_dest, l_reg_l, reg_base_psi);
  else if (n==6)
//     printf("(p%i)\tst8\t[r0] = r1\n", l_pred); //crash



#ifdef NO_FAR_LOAD
    printf("(p%i)\tmov\tr%i = r%i\n", l_pred, l_reg_dest, reg_base_psi);
#else
    printf("(p%i)\tmov\tr%i = r%i\n", l_pred, l_reg_dest, r_nb[fw][mu]);
#endif
  else if (n==7)
    if (mu==3 && ((fw==0 && !dagger) || (fw==1 && dagger))) //XXX
      printf("(p%i)\tadds\tr%i = %i, r%i\n", l_pred+1, l_reg_dest, 2*16,
	     l_reg_dest);

  if (tp==0)
  {
    if (n>=100)
    {
      if (n_regs_load_psi!=2)
      {
	printf("error 66\n");
	exit(2);
      }
      int off=mu==3? 2*16 : 2*16;
      if (n==100)
 	if (mu==3)
	  printf("\tadds\tr%i = %i, r%i\n", reg_load_psi[1], 2*16, reg_load_psi[0]);
 	else
 	  printf("\tadds\tr%i = %i, r%i\n", reg_load_psi[1], 4*16, reg_load_psi[0]);
      if (n==101)
	  printf("(p%i)\tcmp.eq.unc\t p%i, p%i = r%i, r%i\n", l_pred+1, pred_spin_proj[curr_reg_idx],
	   0, 0, 0);
      if (n==102)
	  printf("(p%i)\tadds\tr%i = %i, r%i\n", l_pred, r_nb[fw][mu], 16*6, r_nb[fw][mu]);
      if (n==103)
	printf("(p%i)\tadds\tr%i = %i, r%i\n", l_pred+1,
	       reg_load_psi[1], off, reg_load_psi[1]);
      if (n==104)
	if (mu==0 && fw==0)
	  printf("(p%i)\tcmp.eq\t p%i, p%i = r%i, r%i\n", pred_last, 0, pred_spin_proj[curr_reg_idx],
		 0, 0);
    }
  }
  else if (tp==1)
  {
    if (n==100)
    {
      printf("/* near prefetch 1 */\n");
#ifndef NO_NEAR_PREF
      prefetch(l_reg_dest, 128, false, l_pred+1);
#endif
    }
    else if (n==101)
    {
      printf("/* near prefetch 2 */\n");
#ifndef NO_NEAR_PREF
      prefetch(l_reg_dest, 0, false, l_pred+1);
#endif
    }
  }
  else if (tp==2)
  {
    if (n==100)
    {
      printf("/* far prefetch 1 */\n");
#ifndef NO_FAR_PREF
      printf("(p%i)\tcmp.eq\tp%i, p%i = r%i, r%i\n", pred_no_far_pref, 0, l_pred, 0, 0);
#endif
    }
    else if (n==101)
    {
      printf("/* far prefetch 2 */\n");
//       prefetch(l_reg_dest, 0, false, l_pred);
#ifndef NO_FAR_PREF
#ifndef FAR_PRELOAD
      prefetch(r_nb[fw][mu], 6*16, false, l_pred);
#else
      printf("/* preload */\n");
      ld8(reg_preload, r_nb[fw][mu], 6*16, l_pred);
#endif
#endif
    }
  }
}

void upd_load_reg_slot::debug()
{
#ifdef DEBUG_PREF_PREF_P_N
  if (tp==1)
  {
    if (n==100 || n==101)
    {
      printf("(p%i)\tst8\t[r%i] = r%i, %i\n;;\n",
	     l_pred+1, 
	     reg_debug_pref, l_reg_dest, 8);
      printf("//<< DEBUG pref P N\n");
    }
  }
#endif
#ifdef DEBUG_PREF_PREF_P_F
  if (tp==2)
  {
    if (n==100 || n==101)
    {
      printf("(p%i)\tst8\t[r%i] = r%i, %i\n;;\n",
	     l_pred, 
	     reg_debug_pref, l_reg_dest, 8);
      printf("//<< DEBUG pref P F\n");
    }
  }
#endif
}

int upd_load_reg_slot::slot_type()
{
  if (n==0)
    return slot_m;
//   if (n==6)
//     return slot_m;
  else
    if (tp>=1 && n>=100)
      return slot_m;
    else
      return slot_i;
}

upd_load_reg_slot2::upd_load_reg_slot2(int n_, int fw_, int mu_, int t)
{
  n=n_;
  mu=mu_;
  fw=fw_;
  tp=t;
  if (t==0) //load
  {
    l_reg_l=reg_load_lU;
    l_reg_n=reg_load_n;
    l_reg_dest=reg_load_U[0];
  }
  else if (t==1) // near prefetch
  {
    l_reg_l=reg_pref_lU;
    l_reg_n=reg_pref_n_n;
    l_reg_dest=reg_pref_U;
  }

}


void upd_load_reg_slot2::print()
{
  if (fw==1)
    if (n==0)
    {
      printf("\textr\tr%i = r%i, %i, %i\n", l_reg_l, l_reg_n, 32, 32);
      return;
    }
    else if (n<100)
      n--;
  int offs=144*mu;
  offs=0;
  if (tp==1 && fw==0)
    offs+=144*pref_dist_near;
  if (fw==1)
    offs=0;
  if (n==0)
    printf("\tshladd\tr%i = r%i, %i, r%i\n", l_reg_l, fw==0? reg_x : l_reg_l, 
	   3, fw==0? reg_x : l_reg_l);
  if (n==1)
    printf("\tshl\tr%i = r%i, %i\n", l_reg_l, l_reg_l, fw==0? 4 : 4);
  if (n==2)
     printf("\tadd\tr%i = r%i, r%i\n", l_reg_l, l_reg_l, reg_base_U[mu]);
//    printf("\tadd\tr%i = r%i, r%i\n", l_reg_l, 0, reg_base_U[0]);
  if (n==3)
  {
    printf("/* UU1  */\n");
    printf("\tadds\tr%i = %i, r%i\n", l_reg_dest, offs, l_reg_l);
}
  if (tp==0) //load
  {
    if (n>=100)
    {
      int r=n-100+1;

      int todo=9;
      int t_n=0; 
      for (int i=0; i<r; i++)
      {
	int d=(todo+n_regs_load_U-i-1)/(n_regs_load_U-i);
	t_n+=d;
	todo-=d;
      }
      int off=t_n*16;
      printf("//--> updU %i %i\n", n, r);
      printf("\tadds\tr%i = %i, r%i\n",
	     reg_load_U[r], off, reg_load_U[0]);
    }
  }
  else if (tp==1) //prefetch
  {
    if (n==100)
    {
      printf("/* prefetch U 1 */\n");
#ifndef NO_PREF_U
      prefetch(l_reg_dest, 128, false);
#endif 
    }
    else if (n==101)
    {
      printf("/* prefetch U 2 */\n");
#ifndef NO_PREF_U
      prefetch(l_reg_dest, 0, false);
#endif
    }
  }
}

int upd_load_reg_slot2::slot_type()
{
  if (n<100)
    return slot_i;
  else
    if (tp>=1 && n>=100)
      return slot_m;
    else
      return slot_i;
}

void upd_load_reg_slot2::debug()
{
#ifdef DEBUG_PREF_PREF_U
  if (tp==1)
  {
    if (n==100 || n==101)
    {
      printf("\tst8\t[r%i] = r%i, %i\n;;\n",
	     reg_debug_pref, l_reg_dest, 8);
      printf("//<< DEBUG pref U\n");
    }
  }
#endif
}



// upd_load_reg_U_slot::upd_load_reg_U_slot(int n_, int fw_, int mu_)
// {
//   n=n_;
//   mu=mu_;
//   fw=fw_;
// }

// void upd_load_reg_U_slot::print()
// {
//   int shift_a=3;
//   int shift=6;
// printf("//--> updU f %i m %i n %i\n", fw, mu, n);
//   switch(n)
//   {
//   case 0 :
//   {
//     if (fw==0)
//       printf("\tshladd\tr%i = r%i, %i, r%i\t//\n",
//            reg_tmp_load_U, reg_x, shift_a, reg_x);
//     else
//       printf("\tshladd\tr%i = r%i, %i, r%i\n",
//            reg_tmp_load_U, reg_nb[fw][mu], shift_a, reg_nb[fw][mu]);
//     break;
//   }
//   case 1 :
//   {
// //if (fw==1)
//     printf("\tshl\tr%i = r%i, %i\n",
// 	   reg_tmp_load_U, reg_tmp_load_U, shift);
//     break;
//   }
//   case 2 :
//   {
//     printf("\tadd\tr%i = r%i, r%i\n",
// 	   reg_tmp_load_U, reg_tmp_load_U, reg_base_U);
//     break;
//   }
//   case 3 :
//   {
//     printf("\tadds\tr%i = %i, r%i\n",
// 	   reg_tmp_load_U, mu*144, reg_tmp_load_U);
//     break;
//   }
//   }
//   if (n>3)
//   {
//     int r=n-4;

//     int todo=9;
//     int t_n=0; 
//     for (int i=0; i<r; i++)
//     {
//       int d=(todo+n_regs_load_U-i-1)/(n_regs_load_U-i);
//       t_n+=d;
//       todo-=d;
//     }
//     int off=t_n*16;
// //printf("//--> updU\n");
//     printf("\tadds\tr%i = %i, r%i\n",
// 	   reg_load_U[r], off, reg_tmp_load_U);
//   }
    
// }

// int upd_load_reg_U_slot::slot_type()
// {
//   return slot_i;
// }







psi_pr1_slot::psi_pr1_slot(int n_, int fw_, int mu_)
{
  n=n_;
  mu=mu_;
  fw=fw_;
}

void psi_pr1_slot::print()
{
  int col=n/4;
  int h=(n%4)/2;
  int dest=n/2;
  int dest_re=n%2;
  int src=P2o_table[mu][h];
  int src_r=(P2r_table[mu]+dest_re)%2;
  int sign;
  if (dest_re==0)
    sign=P2sr_table[mu][h];
  else
    sign=P2si_table[mu][h];
  if (fw==0)
    sign=1-sign;
  if (dagger)
    sign=1-sign;
  int reg_dest=reg_psi[1-curr_reg_idx][dest]+dest_re;
  int reg_src=reg_psi_n[(src-2)+2*col]+src_r;
  if (mu!=3)
    fma(reg_dest, reg_src, 1, reg_dest, sign==0, pred_spin_proj[1-curr_reg_idx]);
  else  
    fma(reg_dest, reg_dest, 1, reg_dest, true, pred_spin_proj[1-curr_reg_idx]);
}

int psi_pr1_slot::slot_type()
{
  return slot_f;
}

psi_pr2_slot::psi_pr2_slot(int n_, int fw_, int mu_)
{
  n=n_;
  mu=mu_;
  fw=fw_;
}

void psi_pr2_slot::print()
{
  int dest_n=n/2;
  int dest_re=n%2;
  int col=dest_n/4;
  int spin=dest_n%4;
  int src_n, src_re, sign;
  if (spin<=1)
  {
    src_n=2*col+spin;
    src_re=dest_re;
    sign=1;
  }
  else
  {
    src_n=2*col+hsp_i[mu][spin-2][dest_re][0];
    src_re=hsp_i[mu][spin-2][dest_re][1];
    sign=1-hsp_i[mu][spin-2][dest_re][2];
    if (fw==1)
      sign=1-sign;
    if (dagger)
      sign=1-sign;
  }
  if (fw==0 && mu==0)
    fma(reg_res[dest_n]+dest_re, reg_res_h[src_n]+src_re, 1, 
	0, sign==0);
  else
    fma(reg_res[dest_n]+dest_re, reg_res_h[src_n]+src_re, 1, 
	reg_res[dest_n]+dest_re, sign==0);
}


int psi_pr2_slot::slot_type()
{
  return slot_f;
}

U_load_slot::U_load_slot(int n_, int fw_, int mu_)
{
  n=n_;
  mu=mu_;
  fw=fw_;
}

void U_load_slot::print()
{
  int l_reg=n%n_regs_load_U;
  int n_load=n/n_regs_load_U;
  
  int target=n_load;
  int todo=9;
  for (int i=0; i<l_reg; i++)
  {
    int d=(todo+n_regs_load_U-i-1)/(n_regs_load_U-i);
    target+=d;
    todo-=d;
  }
  int pred=-1;
  if (mu==0 && fw==0)
    pred=pred_last+1;
#ifndef NO_LOAD_U
  load_f_pair(reg_U[1-curr_reg_idx][target], reg_load_U[l_reg],
 	      n+n_regs_load_U<9, pred);
#else
//   if (fw==0)
//     load_f_pair(reg_U[1-curr_reg_idx][target], reg_load_U[l_reg],
// 		n+n_regs_load_U<9, pred);

#endif
}

void U_load_slot::debug()
{
#ifdef DEBUG_PREF_LOAD_U
  printf("// DEBUG load U>>\n");
  int l_reg=n%n_regs_load_U;
  int n_load=n/n_regs_load_U;
  
  int target=n_load;
  int todo=9;
  for (int i=0; i<l_reg; i++)
  {
    int d=(todo+n_regs_load_U-i-1)/(n_regs_load_U-i);
    target+=d;
    todo-=d;
  }
  int pred=-1;
  if (mu==0 && fw==0)
    pred=pred_last+1;
  printf("\tst8\t[r%i] = r%i, %i\n;;\n",
   	 reg_debug_pref, reg_load_U[l_reg], 8);
  printf("//<< DEBUG load U\n");
#endif
}

int U_load_slot::slot_type()
{
  return slot_m;
}



branch_slot::branch_slot(int n_, int fw_, int mu_)
{
  n=n_;
  mu=mu_;
  fw=fw_;
}

void branch_slot::print()
{
  printf("\tbr.cloop.sptk.many\tloop_%s%s\n", sub_name, d_string);
}

int branch_slot::slot_type()
{
  return slot_b;
}


pref_state_slot::pref_state_slot(int n_, int fw_, int mu_)
{
  n=n_;
  fw=fw_;
  mu=mu_;
}

void pref_state_slot::print()
{
#ifndef NO_UPD_STATE
  if (n==0)
    printf("\tcmp.eq p%i, p%i = %i, r%i\n", pred_pref_state, 0, pref_dist_state+1, reg_cnt); // reg_cnt not yet decremented here
  if (n==100)
    printf("(p%i)\tadd\tr%i = r%i, r%i\n;;\n", pred_pref_state, reg_pref_state, reg_mod_offs[fw][mu], reg_my_state_addr);
  if (n==101)
    prefetch(reg_pref_state, 0, false, pred_pref_state);
#endif
}

int pref_state_slot::slot_type()
{
  if (n==101)
    return slot_m;
  return slot_i;
}




const int max_n_bundles=128;
bundle* bn[max_n_bundles];

void init_reg_n()
{
  int n=2;
  for (int i=0; i<18; i++)
  {
    reg_U[0][i]=n;
    printf("// U %i %i\tf%i\n", i/9, i%9, n);
    n+=2;
  }
  for (int i=0; i<12; i++)
  {
    reg_psi[0][i]=n;
    printf("// psi %i %i\tf%i\n", i/6, i%6, n);
    n+=2;
  }
  for (int i=0; i<6; i++)
  {
    reg_psi_n[i]=n;
    printf("// psi+ %i\tf%i\n", i%6, n);
    n+=2;
  }
  for (int i=0; i<6; i++)
  {
    reg_res_h[i]=n;
    printf("// res_ %i\tf%i\n", i%6, n);
    n+=2;
  }
  for (int i=0; i<12; i++)
  {
    reg_res[i]=n;
    printf("// res %i\tf%i\n", i, n);
    n+=2;
  }
  
  reg_kappa=n++;

  reg_save_lc=8;
  reg_save_pfs=9;

  n=14;
  for (int mu=0; mu<4; mu++)
    reg_base_U[mu]=n++;
  reg_base_psi_inp=n++;
  reg_base_res=n++;
  reg_x=n++;
  reg_cnt=n++;
  reg_local_mod=n++;

  
  reg_load_psi0[0]=reg_base_psi_inp;
  for (int i=1; i<n_regs_load_psi0; i++)
    reg_load_psi0[i]=n++;



  reg_load_nb=n++;
  reg_load_g=n++;
  reg_load_l=n++;
  reg_load_n=n++;

  printf("/* n1 %i */\n", n);
  if (n>32)
  {
    printf("error r5\n");
    exit(1);
  }
  n=32;
// these (32-39, rotating) must not be used before the main loop starts
  reg_tmp_load_psi=n++;
  reg_tmp_load_U=n++;
  reg_tmp_pref_psi=n++;
  reg_tmp_pref_U=n++;
  reg_load_lU=n++;


//------------------

  n=40;


  printf("/* lU %i */\n", n);
  for (int i=0; i<n_regs_load_U; i++)
    reg_load_U[i]=n++;
  for (int i=0; i<n_regs_load_psi; i++)
    reg_load_psi[i]=n++;

  

  reg_store_res[0]=reg_base_res;
  for (int i=1; i<n_regs_store_res; i++)
    reg_store_res[i]=n++;
  for (int i=0; i<8; i++)
    reg_nb[0][i]=n++;
  printf("/* nmo %i */\n", n);
  for (int i=0; i<8; i++)
    reg_mod_offs[0][i]=n++;
  reg_my_state=n++;
  reg_my_state_addr=n++;
  reg_base_psi=n++;
  reg_local_mod_mask=n++;
  reg_tmp=n++;
  reg_nb_n=n++;
  reg_nb_pref=n++;
  reg_nb_pref_n=n++;
  reg_pref_n_nb=n++;
  reg_pref_n_g=n++;
  reg_pref_n_l=n++;
  reg_pref_n_n=n++;
  reg_pref_near=n++;
  reg_pref_f_nb=n++;
  reg_pref_f_g=n++; 
  reg_pref_f_l=n++; 
  reg_pref_f_n=n++;
  reg_pref_far=n++;
  reg_pref_U=n++;
  reg_pref_lU=n++;
  reg_pref_state=n++;
  reg_save_preds=n++;
  reg_save_b=n++;
  reg_w_data=n++;
  reg_mod_bnd[0]=n++;
  reg_mod_bnd[1]=n++;
  for (int i=0; i<8; i++)
    reg_nb_bnd[0][i]=n++;
  for (int i=0; i<8; i++)
    reg_nb_bnd_pref[0][i]=n++;
  reg_scratch=n;
  n+=8;
#ifdef DEBUG_PREF
  reg_debug_pref=n++;
#endif
#ifdef FAR_PRELOAD
  reg_preload=n;
  n+=2;
#endif
  printf("//max reg: %i\n", n);
  if (n>=32+n_regs_loc)
  {
    printf("Using too many gprs\n");
    exit(1);
  }
  pred_first=6;
  pred_last=8;
  pred_load_rem=10;
  pred_pref_near=12;
  pred_pref_far=14;
  pred_spin_proj[0]=16;
  pred_spin_proj[1]=17;
  pred_sync=18;
  pred_step2=19;
  pred_inc_state=20;
  pred_pref_state=21;
  pred_no_far_pref=22;
}

void bnd_loop_load(int mu, int fw, int n, bool epil, int c_offs=0)
{
  int l_reg=n%2==0 ? 37 : 39;
  int order=n/2 + (n%2)*6;
  int dest_re=76-4*order+c_offs;
  int dest_im=125-4*order+c_offs;
  if (dest_im==128)
    dest_im=32;
  if (epil)
    printf("(p%i)\tldfpd\tf%i, f%i = [r%i], %i\n", 20, dest_re, dest_im, l_reg, 16);
  else
    printf("\tldfpd\tf%i, f%i = [r%i], %i\n", dest_re, dest_im, l_reg, 16);
}

void bnd_loop_store(int n, int* store_regs)
{
  int order=n/2 + (n%2)*6;
  int s_reg=store_regs[n%2];
  int src=16+n;
  printf("\tstfd\t[r%i] = f%i, %i\n", s_reg, src, 16);
}

void bnd_loop_add(int mu, int fw, int n)
{
  int re_im=n%2; // 0=re, i=im
  int n2=n/2;
  int col=n2/2;
  int spin=n2%2;
  int spin2, re_im2, sign; //sign: +=0, -=1
  if (mu==0)
  {
    spin2=1-spin;
    re_im2=1-re_im;
    sign=re_im;
    if (fw==1)
      sign=1-sign;
  }
  if (mu==1)
  {
    spin2=1-spin;
    re_im2=re_im;
    sign=1-spin;
    if (fw==1)
      sign=1-sign;
  }
  if (mu==2)
  {
    spin2=spin;
    re_im2=1-re_im;
    sign=re_im;
    if (spin==1)
      sign=1-sign;
    if (fw==1)
      sign=1-sign;
  }
  if (dagger)
    sign=1-sign;

  if (mu==3)
  {
    spin2=spin;
    re_im2=re_im;
    sign=0;
  }
  int reg1=79-4*spin-16*col+49*re_im;
  int reg2=71-4*spin2-16*col+49*re_im2;
  if (reg1==128)
    reg1=32;
  if (reg2==128)
    reg2=32;
  int dest=16+n;
  if (mu==3) 
    if ((fw==0 && !dagger) || (fw==1 && dagger))
      reg1=reg2;
    else
      reg2=reg1;
#ifdef IEEE
  if (sign==0)
    printf("\tfadd.d\tf%i = f%i, f%i\n", dest, reg1, reg2);
  else if (sign==1)
    printf("\tfsub.d\tf%i = f%i, f%i\n", dest, reg1, reg2);
#else
  if (sign==0)
    printf("\tfadd\tf%i = f%i, f%i\n", dest, reg1, reg2);
  else if (sign==1)
    printf("\tfsub\tf%i = f%i, f%i\n", dest, reg1, reg2);
#endif
}

void bnd_proj_loop(int mu, int fw, int reg_b_list, int* store_regs, bool prol=false, bool epil=false)
{
  int n_load=0;
  int n_store=0;
  int n_add=0;
  printf("bnd_proj_loop_%i_%i%s%s:\n", mu, fw, prol? "_prol" : "", d_string);
  printf("{\n.mmi\n");
  bnd_loop_load(mu, fw, n_load++, epil);
  if (!prol)
    bnd_loop_store(n_store++, store_regs);
  printf("%s\tshladd\tr%i = r%i, %i, r%i\n", epil? "(p18)" : "", 34, 34, 1, 34);
  printf("}\n{.mmi\n");
  bnd_loop_load(mu, fw, n_load++, epil);
  if (!prol)
    bnd_loop_store(n_store++, store_regs);
  printf("%s\tadd\tr%i = r%i, r%i\n", epil? "(p19)" : "", 36, 35, reg_base_psi_inp);
  printf("};;\n");

  printf("{\n.mmf\n");
  bnd_loop_load(mu, fw, n_load++, epil);
  if (!prol)
    bnd_loop_store(n_store++, store_regs);
  bnd_loop_add(mu, fw, n_add++);
  printf("}\n{\n.mmf\n");
  bnd_loop_load(mu, fw, n_load++, epil);
  if (!prol)
    bnd_loop_store(n_store++, store_regs);
  bnd_loop_add(mu, fw, n_add++);
  printf("};;\n");

  printf("{\n.mmf\n");
  printf("\tld4\tr%i = [r%i], %i\n", 32, reg_b_list, 4);
  if (!prol)
    bnd_loop_store(n_store++, store_regs);
  bnd_loop_add(mu, fw, n_add++);
  printf("}\n{\n.mfi\n");
  if (!prol)
    bnd_loop_store(n_store++, store_regs);
  bnd_loop_add(mu, fw, n_add++);
  printf("%s\tshl\tr%i = r%i, %i\n", epil? "(p18)" : "", 34, 34, 6); //PREC
  printf("};;\n");

  for (int i=3; i<=5; i++)
  {
    printf("{\n.mmf\n");
    bnd_loop_load(mu, fw, n_load++, epil);
    if (!prol)
      bnd_loop_store(n_store++, store_regs);
    bnd_loop_add(mu, fw, n_add++);
    printf("}\n{\n.mmf\n");
    bnd_loop_load(mu, fw, n_load++, epil);
    if (!prol)
      bnd_loop_store(n_store++, store_regs);
    bnd_loop_add(mu, fw, n_add++);
    printf("};;\n");
  }
  printf("{\n.mfi\n");
  bnd_loop_load(mu, fw, n_load++, epil);
  bnd_loop_add(mu, fw, n_add++);
  printf("%s\tadds\tr%i = %i, r%i\n", epil? "(p19)" : "", 38, 6*16, 36);
  printf("}\n{\n.mfb\n");
  bnd_loop_load(mu, fw, n_load++, epil);
  bnd_loop_add(mu, fw, n_add++);
  printf("\tbr.ctop.sptk.many\tbnd_proj_loop_%i_%i%s%s\n", mu, fw, prol? "_prol" : "", d_string);
  printf("};;\n");
}

void start_code()
{
  printf("#include \"defs.h\"\n\n");
  printf("\t.global\t%s%s_#\n", sub_name, d_string);
  printf("\t.proc\t%s%s_#\n", sub_name, d_string);
  printf("%s%s_:\n", sub_name, d_string);
  printf(".prologue\n");
  printf("\talloc\tr%i = ar.pfs, %i, %i, %i, %i\n", 
	 reg_save_pfs, n_regs_inp, n_regs_loc, n_regs_out, n_regs_rot);
  printf("\tmov\tr%i = ar.lc\n", reg_save_lc);
  printf("\tmov\tr%i = b0\n", reg_save_b);
  printf("\tadds\tr12 = %i, r12\n", -st_size);
  printf(";;\n");
// save floating regs
  printf("\tmov\tloc0=r12\n;;\n");
  for (int i=2; i<=5; i++)
    printf("\tstf.spill\t[loc0]=f%i, 16\n;;\n", i);
  for (int i=16; i<=31; i++)
    printf("\tstf.spill\t[loc0]=f%i, 16\n;;\n", i);
// save predicates
  printf("\tmov\tr%i = pr\n", reg_save_preds);
  printf(".body\n");

#ifdef SHMEM_COPY
// save returnpointer
//   printf("\tmov\tr%i = b%i\n;;\n", reg_scratch, 0);
// //  printf("\tmov.ret.sptk\tb%i = r%i\n;;\n", 1, reg_scratch);
//   printf("\tmov\tb%i = r%i\n;;\n", branch_scratch_reg, reg_scratch);
#endif

// move input data into "fixed" (non-rotating) registers
  printf("\tmov\tr%i = r%i\n", reg_w_data, inp_reg_w_data);
  printf("\tmov\tr%i = r%i\n", reg_base_U[0], inp_reg_base_U);
  printf("\tmov\tr%i = r%i\n", reg_base_psi_inp, inp_reg_base_psi_inp);
  printf("\tmov\tr%i = r%i\n", reg_base_res, inp_reg_base_res);
//X  printf("\tmov\tr%i = r%i\n", reg_x, inp_reg_x);

  printf("\tshladd\tr%i = r%i, %i, r%i\n;;\n", inp_reg_x, inp_reg_x, 3, inp_reg_x);
  printf("\tshl\tr%i = r%i, %i\n;;\n", inp_reg_x, inp_reg_x, 5);


  printf("\tmov\tr%i = %i\n;;\n", reg_x, 0);
  printf("\tadd\tr%i = r%i, r%i\n;;\n", reg_base_U[1], reg_base_U[0], inp_reg_x);
  printf("\tadd\tr%i = r%i, r%i\n;;\n", reg_base_U[2], reg_base_U[1], inp_reg_x);
  printf("\tadd\tr%i = r%i, r%i\n", reg_base_U[3], reg_base_U[2], inp_reg_x);

  printf("\tmov\tr%i = r%i\n", reg_cnt, inp_reg_cnt);


//  printf("\tmov\tr%i = r%i\n;;\n", reg_local_mod, inp_reg_local_mod);
  printf("/* abc */\n\n");
  printf("\tadd\tr%i = %i, r%i\n;;\n", reg_scratch,
	 (char*) &(w_dirac_data[0].cpu_num)-(char*) &(w_dirac_data[0]), reg_w_data);
  printf("\tld4\tr%i = [r%i]\n;;\n", reg_local_mod, reg_scratch);
  printf("\tadd\tr%i = %i, r%i\n;;\n", reg_scratch,
	 (char*) &(w_dirac_data[0].boundary)-(char*) &(w_dirac_data[0]), reg_w_data);
  printf("\tld8\tr%i = [r%i], 8\n;;\n", reg_mod_bnd[0], reg_scratch);
  printf("\tld8\tr%i = [r%i]\n;;\n", reg_mod_bnd[1], reg_scratch);


  printf("\tshladd\tr%i = r%i, %i, r%i\n;;\n", reg_scratch, reg_x, 3, reg_x);
  printf("\tshl\tr%i = r%i, %i\n;;\n", reg_scratch, reg_scratch, 6);
  printf("\tsub\tr%i = r%i, r%i\n", reg_base_U[0], reg_base_U[0], reg_scratch);
//   printf("\tmov\tr%i = in%i\n;;\n", reg_store_res[0], 2);


  printf("\tshladd\tr%i = r%i, %i, r%i\n;;\n", reg_scratch, reg_x, 1, reg_x);
  printf("\tshl\tr%i = r%i, %i\n;;\n", reg_scratch, reg_scratch, 6);
  printf("\tsub\tr%i = r%i, r%i\n", reg_base_psi, reg_base_psi_inp, reg_scratch);

  printf("\tdep.z\tr%i = %i, MOD_COORD_LEN, 32-MOD_COORD_LEN\n", reg_local_mod_mask, -1);
  printf("\tshl\tr%i = r%i, %i\n", reg_scratch+1, reg_local_mod, 3);


//  printf("\taddl\tr%i = @gprel(neighb_U_loc), gp\n;;\n", reg_tmp);

   printf("\tadd\tr%i = %i, r%i\n;;\n", reg_tmp,
 	 (char*) &(w_dirac_data[0].neighb_U_loc)-(char*) &(w_dirac_data[0]), reg_w_data);
//    printf("\tld8\tr%i = [r%i]\n;;\n", reg_tmp, reg_tmp);


//  printf("\tadd\tr%i = r%i, r%i\n;;\n", reg_tmp, reg_tmp, reg_scratch+1);
  printf("\tld8\tr%i = [r%i]\n;;\n", reg_load_nb, reg_tmp);
  printf("\tadds\tr%i = %i, r%i\n", reg_pref_n_nb, pref_dist_near*48, reg_load_nb);
  printf("\tadds\tr%i = %i, r%i\n", reg_pref_f_nb, pref_dist_far*48, reg_load_nb);
  printf("\tshl\tr%i = r%i, %i\n;;\n", reg_scratch+1, reg_local_mod, 6);



   printf("\taddl\tr%i = @gprel(neighb_mod_offs), gp\n;;\n", reg_scratch);
//    printf("\tadd\tr%i = r%i, r%i\n;;\n", reg_scratch, reg_scratch, reg_scratch+1);

  printf("\tshl\tr%i = r%i, %i\n", reg_scratch+1, reg_local_mod, 3);

#ifndef SHMEM_COPY
  for (int i=0; i<8; i++)
  {
    printf("\tld8\tr%i = [r%i]\n", reg_nb_bnd[0][i], reg_scratch);
    printf("\tld8\tr%i = [r%i], %i\n;;\n", reg_mod_offs[0][i], reg_scratch, 8);
  }
  printf("\taddl\tr%i = @gprel(lattice_boundary), gp\n;;\n", reg_scratch);
#else
  for (int i=0; i<8; i++)
  {
    printf("\tmov\tr%i = %i\n", reg_nb_bnd[0][i], 0);
    printf("\tld8\tr%i = [r%i], %i\n;;\n", reg_mod_offs[0][i], reg_scratch, 8);
  }
  printf("\taddl\tr%i = @gprel(lattice_boundary_local), gp\n;;\n", reg_scratch);
#endif
  printf("\tadd\tr%i = r%i, r%i\n;;\n", 2, reg_scratch, reg_scratch+1);
  printf("\tld8\tr%i = [r%i]\n;;\n", 2, 2); //keep lattice boundary in reg. 2





#ifndef SHMEM_COPY
#error NOT IMPLEMENTED
#endif

  printf("/* PPP */\n");

  printf("\tmov\tr%i = r%i\n", reg_nb_bnd[0][0], 2);

   extr_bnd(reg_scratch+4, 0, 0);
   printf("\n;;\n");
   printf("\tshladd\tr%i = r%i, %i, r%i\n;;\n", reg_scratch+4, reg_scratch+4, 1, reg_scratch+4);
   printf("\tshl\tr%i = r%i, %i\n;;\n", reg_scratch+4,  reg_scratch+4, 5);

   printf("\tadd\tr%i = r%i, r%i\n", reg_nb_bnd[0][1], reg_nb_bnd[0][0], reg_scratch+4);

   extr_bnd(reg_scratch+4, 0, 1);
   printf("\n;;\n");
   printf("\tshladd\tr%i = r%i, %i, r%i\n;;\n", reg_scratch+4, reg_scratch+4, 1, reg_scratch+4);
   printf("\tshl\tr%i = r%i, %i\n;;\n", reg_scratch+4,  reg_scratch+4, 5);

   printf("\tadd\tr%i = r%i, r%i\n", reg_nb_bnd[0][2], reg_nb_bnd[0][1], reg_scratch+4);

   extr_bnd(reg_scratch+4, 0, 2);
   printf("\n;;\n");
   printf("\tshladd\tr%i = r%i, %i, r%i\n;;\n", reg_scratch+4, reg_scratch+4, 1, reg_scratch+4);
   printf("\tshl\tr%i = r%i, %i\n;;\n", reg_scratch+4,  reg_scratch+4, 5);

   printf("\tadd\tr%i = r%i, r%i\n", reg_nb_bnd[0][3], reg_nb_bnd[0][2], reg_scratch+4);

   extr_bnd(reg_scratch+4, 0, 3);
   printf("\n;;\n");
   printf("\tshladd\tr%i = r%i, %i, r%i\n;;\n", reg_scratch+4, reg_scratch+4, 1, reg_scratch+4);
   printf("\tshl\tr%i = r%i, %i\n;;\n", reg_scratch+4,  reg_scratch+4, 5);

   printf("\tadd\tr%i = r%i, r%i\n", reg_nb_bnd[1][0], reg_nb_bnd[0][3], reg_scratch+4);

   extr_bnd(reg_scratch+4, 1, 0);
   printf("\n;;\n");
   printf("\tshladd\tr%i = r%i, %i, r%i\n;;\n", reg_scratch+4, reg_scratch+4, 1, reg_scratch+4);
   printf("\tshl\tr%i = r%i, %i\n;;\n", reg_scratch+4,  reg_scratch+4, 5);

   printf("\tadd\tr%i = r%i, r%i\n", reg_nb_bnd[1][1], reg_nb_bnd[1][0], reg_scratch+4);


   extr_bnd(reg_scratch+4, 1, 1);
   printf("\n;;\n");
   printf("\tshladd\tr%i = r%i, %i, r%i\n;;\n", reg_scratch+4, reg_scratch+4, 1, reg_scratch+4);
   printf("\tshl\tr%i = r%i, %i\n;;\n", reg_scratch+4,  reg_scratch+4, 5);

   printf("\tadd\tr%i = r%i, r%i\n", reg_nb_bnd[1][2], reg_nb_bnd[1][1], reg_scratch+4);

   extr_bnd(reg_scratch+4, 1, 2);
   printf("\n;;\n");
   printf("\tshladd\tr%i = r%i, %i, r%i\n;;\n", reg_scratch+4, reg_scratch+4, 1, reg_scratch+4);
   printf("\tshl\tr%i = r%i, %i\n;;\n", reg_scratch+4,  reg_scratch+4, 5);

   printf("\tadd\tr%i = r%i, r%i\n;;\n", reg_nb_bnd[1][3], reg_nb_bnd[1][2], reg_scratch+4);



//    printf("\tadd\tr%i = %i, r%i\n;;\n", reg_scratch,
//  	 (char*) &(w_dirac_data[0].test)-(char*) &(w_dirac_data[0]), reg_w_data);
//     printf("\tst8\t[r%i] = r%i, 8\n;;\n", reg_scratch, reg_nb_bnd[0][0]);
//     printf("\tst8\t[r%i] = r%i, 8\n;;\n", reg_scratch, reg_nb_bnd[0][1]);
//     printf("\tst8\t[r%i] = r%i, 8\n;;\n", reg_scratch, reg_nb_bnd[0][2]);
//     printf("\tst8\t[r%i] = r%i, 8\n;;\n", reg_scratch, reg_nb_bnd[0][3]);
//     printf("\tst8\t[r%i] = r%i, 8\n;;\n", reg_scratch, reg_nb_bnd[0][4]);
//     printf("\tst8\t[r%i] = r%i, 8\n;;\n", reg_scratch, reg_nb_bnd[0][5]);
//     printf("\tst8\t[r%i] = r%i, 8\n;;\n", reg_scratch, reg_nb_bnd[0][6]);
//     printf("\tst8\t[r%i] = r%i, 8\n;;\n", reg_scratch, reg_nb_bnd[0][7]);

//     printf("br      ababa\n");

//    printf("\n;;\nmov r%i=r%i\n;;\n", reg_nb_bnd[0][0], reg_nb_bnd[0][3]);
//  printf("\n;;\nmov r%i=r%i\n;;\n", reg_nb_bnd[0][1], reg_nb_bnd[0][3]);
//  printf("\n;;\nmov r%i=r%i\n;;\n", reg_nb_bnd[0][2], reg_nb_bnd[0][3]);
//  printf("\n;;\nmov r%i=r%i\n;;\n", reg_nb_bnd[0][3], reg_nb_bnd[0][3]);
//  printf("\n;;\nmov r%i=r%i\n;;\n", reg_nb_bnd[1][0], reg_nb_bnd[0][3]);
// printf("\n;;\nmov r%i=r%i\n;;\n", reg_nb_bnd[1][1], reg_nb_bnd[0][3]);
//  printf("\n;;\nmov r%i=r%i\n;;\n", reg_nb_bnd[1][2], reg_nb_bnd[0][3]);
//  printf("\n;;\nmov r%i=r%i\n;;\n", reg_nb_bnd[1][3], reg_nb_bnd[0][3]);

//    printf("mov r2 = 0\n;;\n");
//    printf("mov r3 = r%i\n;;\n", reg_nb_bnd[0][2]);
//    printf("st8 [r2]= r3\n;;\n");


//XXXXX
//    printf("\taddl\tr%i = 0, r%i\n;;\n", 3, 2);
//    printf("\tadd\tr%i = r%i, r%i\n", reg_nb_bnd[0][0], reg_nb_bnd[0][0], 3);

//     printf("\taddl\tr%i = 16*6*(MOD_BND_X), r%i\n;;\n", 3, 2);
//     printf("\tadd\tr%i = r%i, r%i\n", reg_nb_bnd[0][1], reg_nb_bnd[0][1], 3);

//    printf("\taddl\tr%i = 16*6*(MOD_BND_X+MOD_BND_Y), r%i\n;;\n", 3, 2);
//    printf("\tadd\tr%i = r%i, r%i\n", reg_nb_bnd[0][2], reg_nb_bnd[0][2], 3);

//   printf("\taddl\tr%i = 16*6*(MOD_BND_X+MOD_BND_Y+MOD_BND_Z), r%i\n;;\n", 3, 2);
//   printf("\tadd\tr%i = r%i, r%i\n", reg_nb_bnd[0][3], reg_nb_bnd[0][3], 3);

//   printf("\taddl\tr%i = 16*6*(MOD_BNDH), r%i\n;;\n", 3, 2);
//   printf("\tadd\tr%i = r%i, r%i\n", reg_nb_bnd[1][0], reg_nb_bnd[1][0], 3);

//   printf("\taddl\tr%i = 16*6*(MOD_BNDH+MOD_BND_X), r%i\n;;\n", 3, 2);
//   printf("\tadd\tr%i = r%i, r%i\n", reg_nb_bnd[1][1], reg_nb_bnd[1][1], 3);

//   printf("\taddl\tr%i = 16*6*(MOD_BNDH+MOD_BND_X+MOD_BND_Y), r%i\n;;\n", 3, 2);
//   printf("\tadd\tr%i = r%i, r%i\n", reg_nb_bnd[1][2], reg_nb_bnd[1][2], 3);

//   printf("\taddl\tr%i = 16*6*(MOD_BNDH+MOD_BND_X+MOD_BND_Y+MOD_BND_Z), r%i\n;;\n", 3, 2);
//   printf("\tadd\tr%i = r%i, r%i\n;;\n", reg_nb_bnd[1][3], reg_nb_bnd[1][3], 3);


  for (int i=0; i<8; i++)
    printf("\tmov\tr%i = r%i\n", reg_nb_bnd_pref[0][i], reg_nb_bnd[0][i]);

#ifdef SHMEM_COPY
  printf("\taddl\tr%i = @gprel(lattice_boundary), gp\n;;\n", reg_scratch);
  printf("\tadd\tr%i = r%i, r%i\n;;\n", 2, reg_scratch, reg_scratch+1);
  printf("\tld8\tr%i = [r%i]\n;;\n", 2, 2); //keep lattice boundary in reg. 2
#endif

#ifndef NO_UPD_STATE
  printf("\taddl\tr%i = @gprel(ipf_dirac_state), gp\n", reg_my_state_addr);
#endif
  printf("\taddl\tr%i = @gprel(mod_mem_offset), gp\n;;\n", reg_scratch);
  printf("\tadd\tr%i = r%i, r%i\n;;\n", reg_scratch, reg_scratch+1, reg_scratch);
#ifndef NO_UPD_STATE
  printf("\tld8\tr%i = [r%i]\n", reg_my_state_addr, reg_my_state_addr);
#endif
  printf("\tld8\tr%i = [r%i]\n;;\n", reg_scratch, reg_scratch);
#ifndef NO_UPD_STATE
  printf("\tadd\tr%i = r%i, r%i\n;;\n", reg_my_state_addr, reg_my_state_addr, reg_scratch);
  printf("\tld8\tr%i = [r%i]\n", reg_my_state, reg_my_state_addr);
#endif


#ifdef DEBUG_PREF
  printf("\taddl\tr%i = @gprel(DEBUG_PREFETCH_MEM), gp\n;;\n", reg_debug_pref);
  printf("\tld8\tr%i = [r%i]\n;;\n", reg_debug_pref, reg_debug_pref);
#endif

  for (int l_reg=1; l_reg<n_regs_store_res; l_reg++)
  {
    int t_n=0;
    int todo=24;
    for (int i=0; i<l_reg; i++)
    {
      int d=(todo+n_regs_store_res-i-1)/(n_regs_store_res-i);
      t_n+=d;
      todo-=d;
    }
    printf("\tadds\tr%i = %i, r%i\n", reg_store_res[l_reg], t_n*8,
	   reg_store_res[0]);

  }
  
  printf("\tshl\tr%i = r%i, %i\n;;\n", reg_scratch, reg_x, 4);
  printf("\tshladd\tr%i = r%i, %i, r%i\n;;\n", reg_scratch, reg_scratch, 1, reg_scratch);
  printf("\tadd\tr%i = r%i, r%i\n", reg_load_nb, reg_load_nb, reg_scratch);



  int reg_load[2], reg_store[2];
  reg_load[0]=reg_pref_n_g;
  reg_load[1]=reg_pref_n_l;
  reg_store[0]=reg_pref_n_n;
  reg_store[1]=reg_pref_near;
  int reg_lat_bnd=2;
  int reg_proj_inp=reg_pref_f_g;





// synchronization
#ifndef NO_SYNC
  printf("/* sync 1 */\n");
  for (int fw=0; fw<2; fw++)
    for (int mu=0; mu<4; mu++)
      if (mod_bnd[mu]>0 && (fw==0 || mod_num[mu]>2)) // if mod_num[mu]=2, fw==bw
      {
	printf("\tadd\tr%i = r%i, r%i\n;;\n", reg_scratch+2, reg_mod_offs[fw][mu], reg_my_state_addr);
	printf("sync_loop1_%i_%i%s:\n", mu, fw, d_string);
	printf("\tld8.acq\tr%i = [r%i]\n;;\n", reg_scratch+3, reg_scratch+2);
	printf("\tsub\tr%i = r%i, r%i\n;;\n", reg_scratch+3, reg_scratch+3, reg_my_state);
	printf("\tcmp.gt p%i, p%i = %i, r%i\n;;\n", pred_sync, 0, 0, reg_scratch+3);
	printf("\(p%i)\tbr.spnt.many\tsync_loop1_%i_%i%s\n", pred_sync, mu, fw, d_string);
      }
#endif

#ifndef NO_BND_PROJ
  if (MOD_BND>0)
  {
// start boundary projection
    printf("/* start boundary projection*/\nstart_bnd_p%s:\n", d_string);
    printf("\tshl\tr%i = r%i, %i\n", reg_scratch+1, reg_local_mod, 3);

   printf("\tadd\tr%i = %i, r%i\n;;\n", reg_scratch,
 	 (char*) &(w_dirac_data[0].bnd_list)-(char*) &(w_dirac_data[0]), reg_w_data);
    printf("\tld8\tr%i = [r%i]\n", reg_scratch, reg_scratch);   // reg_scratch contains bnd_list
    printf("\tadds\tr%i = %i, r%i\n", reg_store[0], 0, reg_lat_bnd);
    printf("\tadds\tr%i = %i, r%i\n;;\n", reg_store[1], 8, reg_lat_bnd);


// initialization:

// seven iterations prologue:
// three loads:

    for (int s=0; s<3; s++)
    {
      printf("\tld4\tr%i = [r%i], %i\n;;\n", 32, reg_scratch, 4);
      printf("\tshladd\tr%i = r%i, %i, r%i\n;;\n", 32, 32, 1, 32);
      printf("\tshl\tr%i = r%i, %i\n;;\n", 32, 32, 6); //PREC
      printf("\tadd\tr%i = r%i, r%i\n;;\n", 37, 32, reg_base_psi_inp);
      printf("\tadds\tr%i = %i, r%i\n;;\n", 39, 6*16, 37);
      for (int n=0; n<12; n++)
	bnd_loop_load(0, 0, n, false, 3-s);
    }
// four initialization steps:
//1. 
    printf("\tld4\tr%i = [r%i], %i\n;;\n", 32, reg_scratch, 4);
    printf("\tshladd\tr%i = r%i, %i, r%i\n;;\n", 32, 32, 1, 32);
    printf("\tshl\tr%i = r%i, %i\n;;\n", 32, 32, 6); //PREC
    printf("\tadd\tr%i = r%i, r%i\n;;\n", 37, 32, reg_base_psi_inp);
    printf("\tadds\tr%i = %i, r%i\n;;\n", 39, 6*16, 37);
//2.
    printf("\tld4\tr%i = [r%i], %i\n;;\n", 32, reg_scratch, 4);
    printf("\tshladd\tr%i = r%i, %i, r%i\n;;\n", 32, 32, 1, 32);
    printf("\tshl\tr%i = r%i, %i\n;;\n", 35, 32, 6); //PREC
//3.
    printf("\tld4\tr%i = [r%i], %i\n;;\n", 34, reg_scratch, 4);
//4.
    printf("\tld4\tr%i = [r%i], %i\n;;\n", 33, reg_scratch, 4);




    bool first_step=true;
    int last_mu=-1;
    for (int mu=0; mu<4; mu++)
      if (mod_bnd[mu]>0)
	last_mu=mu;
 
    for (int fw=0; fw<2; fw++)
      for (int mu=0; mu<4; mu++)
      {
	int bnd_size_diff=0;
	bool last_step=(mu==last_mu) && (fw==1);
	if (mod_bnd[mu]>0)
	{
	  if (first_step)
	  {
	    printf("\tmov\tr%i = %i\n;;\n", reg_scratch+1, 0);
	    printf("\tmov\tar.lc=r%i\n", reg_scratch+1);
	    printf("\tmov\tr%i = %i\n;;\n", reg_scratch+1, 1);
	    printf("\tmov\tar.ec=r%i\n", reg_scratch+1);
	    bnd_proj_loop(mu, fw, reg_scratch, reg_store, true);
	    bnd_size_diff=-1;
	  }
	  int d=0;
	  if (last_step)
	  {
	    printf("\tmov\tpr.rot = 0xfffffffffff\n");
	    d=7;
	  }
	  extr_bnd(reg_scratch+4, fw, mu);
	  printf("\n;;\n");
	  printf("\tadd\tr%i = %i, r%i\n;;\n", reg_scratch+1, -1-d+bnd_size_diff, reg_scratch+4);
	  printf("\tmov\tar.lc=r%i\n", reg_scratch+1);
	  printf("\tmov\tr%i = %i\n;;\n", reg_scratch+1, 1+d);
	  printf("\tmov\tar.ec=r%i\n", reg_scratch+1);
	  bnd_proj_loop(mu, fw, reg_scratch, reg_store, false, last_step);
	  first_step=false;
	}
      }





// store last data elements:
    for (int i=0; i<12; i++)
      bnd_loop_store(i, reg_store);



    printf("bnd_loop_done%s:\n/* end boundary projection*/\n", d_string);
// end boundary projection

  }
#endif




// copy boundaries via shmem_put

#if (defined SHMEM_COPY && defined SHMEM_COPY_PUT)

  int sp_bytes=8*26+128*16;

  printf("\tadds\tr%i = %i, r%i\n;;\n", 12, -sp_bytes, 12);
  printf("\tadds\tr%i = %i, r%i\n;;\n", reg_scratch, 16, 12);
  for (int i=2; i<4; i++)
    printf("\tst8\t[r%i] = r%i, %i\n;;\n", reg_scratch, i, 8);
  for (int i=8; i<12; i++)
    printf("\tst8\t[r%i] = r%i, %i\n;;\n", reg_scratch, i, 8);
  for (int i=14; i<32; i++)
    printf("\tst8\t[r%i] = r%i, %i\n;;\n", reg_scratch, i, 8);
  for (int i=6; i<=15; i++)
    printf("\tstf.spill\t[r%i]=f%i, 16\n;;\n", reg_scratch, i);
  for (int i=32; i<=127; i++)
    printf("\tstf.spill\t[r%i]=f%i, 16\n;;\n", reg_scratch, i);


  printf("\tmov\tr%i = r%i\n", reg_scratch+1, 1);


  printf("\tshl\tr%i = r%i, %i\n", reg_scratch, reg_local_mod, 3);
  printf("\taddl\tr%i = @gprel(lattice_boundary), gp\n;;\n", reg_scratch+2);
  printf("\tadd\tr%i = r%i, r%i\n;;\n", reg_scratch+2, reg_scratch+2, reg_scratch);



#ifndef SHMEM_COPY_PUT
  printf("\tbr.call.sptk.many\tb%i = shmem_barrier_all\n;;\n", 0);
  printf("\tmov\tr%i = r%i\n;;\n", 1, reg_scratch+1);
#endif




   printf("\tsub\tr%i = r%i, r%i\n;;\n", reg_scratch+6, reg_nb_bnd[1][1], reg_nb_bnd[1][0]);
   printf("\tsub\tr%i = r%i, r%i\n;;\n", reg_scratch+6, reg_scratch+6, reg_nb_bnd[0][1]);
   printf("\tadd\tr%i = r%i, r%i\n", reg_scratch+6, reg_scratch+6, reg_nb_bnd[0][0]);





   printf("\tmov\tr%i = %i\n", reg_scratch+3, 0);





#if MOD_BND_X>0
// 0fw
#ifndef FASTBCOPY



   extr_bnd(reg_scratch+5, 0, 0);
   printf("\n;;\n");
   printf("\tshladd\tr%i = r%i, %i, r%i\n;;\n", reg_scratch+5, reg_scratch+5,
	  1, reg_scratch+5);



   printf("{\n.mmi\n");
   printf("\tld8\tr%i = [r%i]\n", 3, reg_scratch+2); // lattice_boundary[rank] in r3
   printf("\taddl\tr%i = @gprel(neighb_mod), gp\n", 2);
   printf("\tmov\tout%i = r%i\n", 0, reg_nb_bnd[0][0]);
   printf("};;\n");

   printf("{\n.mmi\n");
   printf("\tadds\tr%i = %i, r%i\n", 2, 32, 2);
   printf("\tadd\tout%i = r%i, r%i\n", 1, 3, reg_scratch+3); // --> out1: src
   printf("\tshl\tout%i = r%i, %i\n;;\n", 2, reg_scratch+5, 2);// out2 = # doubles
   printf("};;\n");

   printf("\tld8\tout%i = [r%i]\n;;\n", 3, 2); // out3=source pe
   printf("\tbr.call.sptk.many\tb%i = shmem_double_put\n;;\n", 0);
   printf("\tmov\tr%i = r%i\n;;\n", 1, reg_scratch+1);

#else
#error FASTBCOPY not implemented
#endif

   extr_bnd(reg_scratch+4, 0, 0);
   printf("\n;;\n");
   printf("\tshladd\tr%i = r%i, %i, r%i\n;;\n", reg_scratch+4, reg_scratch+4,
	  1, reg_scratch+4);
   printf("\tshl\tr%i = r%i, %i\n;;\n", 
	  reg_scratch+4, reg_scratch+4, 5);
   printf("\tadd\tr%i = r%i, r%i\n", reg_scratch+3, reg_scratch+3, reg_scratch+4);


#endif

#if MOD_BND_Y>0
// 1fw
#ifndef FASTBCOPY



   extr_bnd(reg_scratch+5, 0, 1);
   printf("\n;;\n");
   printf("\tshladd\tr%i = r%i, %i, r%i\n;;\n", reg_scratch+5, reg_scratch+5,
	  1, reg_scratch+5);

  printf("{\n.mmi\n");
  printf("\tld8\tr%i = [r%i]\n", 3, reg_scratch+2); // lattice_boundary[rank] in r3
  printf("\taddl\tr%i = @gprel(neighb_mod), gp\n", 2);
  printf("\tadd\tout%i = r%i, r%i\n", 0, reg_nb_bnd[0][1], reg_scratch+6);
  printf("};;\n");

  printf("{\n.mmi\n");
  printf("\tadds\tr%i = %i, r%i\n", 2, 40, 2);
  printf("\tadd\tout%i = r%i, r%i\n", 1, 3, reg_scratch+3); // --> out1: src
   printf("\tshl\tout%i = r%i, %i\n;;\n", 2, reg_scratch+5, 2);// out2 = # doubles
  printf("};;\n");

  printf("\tld8\tout%i = [r%i]\n;;\n", 3, 2); // out3=source pe
  printf("\tbr.call.sptk.many\tb%i = shmem_double_put\n;;\n", 0);

  printf("\tmov\tr%i = r%i\n;;\n", 1, reg_scratch+1);
#else
#error FASTBCOPY not implemented
#endif

   extr_bnd(reg_scratch+4, 0, 1);
   printf("\n;;\n");
   printf("\tshladd\tr%i = r%i, %i, r%i\n;;\n", reg_scratch+4, reg_scratch+4,
	  1, reg_scratch+4);
   printf("\tshl\tr%i = r%i, %i\n;;\n", 
	  reg_scratch+4, reg_scratch+4, 5);
  printf("\tadd\tr%i = r%i, r%i\n", reg_scratch+3, reg_scratch+3, reg_scratch+4);


#endif



#if MOD_BND_Z>0
//2fw
#ifndef FASTBCOPY
   extr_bnd(reg_scratch+5, 0, 2);
   printf("\n;;\n");
   printf("\tshladd\tr%i = r%i, %i, r%i\n;;\n", reg_scratch+5, reg_scratch+5,
	  1, reg_scratch+5);

  printf("{\n.mmi\n");
  printf("\tld8\tr%i = [r%i]\n", 3, reg_scratch+2); // lattice_boundary[rank] in r3
  printf("\taddl\tr%i = @gprel(neighb_mod), gp\n", 2);
  printf("\tadd\tout%i = r%i, r%i\n", 0, reg_nb_bnd[0][2], reg_scratch+6);
  printf("};;\n");

  printf("{\n.mmi\n");
  printf("\tadds\tr%i = %i, r%i\n", 2, 48, 2);
  printf("\tadd\tout%i = r%i, r%i\n", 1, 3, reg_scratch+3); // --> out0: target
   printf("\tshl\tout%i = r%i, %i\n;;\n", 2, reg_scratch+5, 2);// out2 = # doubles
  printf("};;\n");

  printf("\tld8\tout%i = [r%i]\n;;\n", 3, 2); // out3=source pe
  printf("\tbr.call.sptk.many\tb%i = shmem_double_put\n;;\n", 0);
  printf("\tmov\tr%i = r%i\n;;\n", 1, reg_scratch+1);
#else
#error FASTBCOPY not implemented
#endif


   extr_bnd(reg_scratch+4, 0, 2);
   printf("\n;;\n");
   printf("\tshladd\tr%i = r%i, %i, r%i\n;;\n", reg_scratch+4, reg_scratch+4,
	  1, reg_scratch+4);
   printf("\tshl\tr%i = r%i, %i\n;;\n", 
	  reg_scratch+4, reg_scratch+4, 5);
  printf("\tadd\tr%i = r%i, r%i\n", reg_scratch+3, reg_scratch+3, reg_scratch+4);

#endif

#if MOD_BND_T>0
//3fw
#ifndef FASTBCOPY
   extr_bnd(reg_scratch+5, 0, 3);
   printf("\n;;\n");
   printf("\tshladd\tr%i = r%i, %i, r%i\n;;\n", reg_scratch+5, reg_scratch+5,
	  1, reg_scratch+5);



  printf("{\n.mmi\n");
  printf("\tld8\tr%i = [r%i]\n", 3, reg_scratch+2); // lattice_boundary[rank] in r3
  printf("\taddl\tr%i = @gprel(neighb_mod), gp\n", 2);
  printf("\tadd\tout%i = r%i, r%i\n", 0, reg_nb_bnd[0][3], reg_scratch+6);
  printf("};;\n");

  printf("{\n.mmi\n");
  printf("\tadds\tr%i = %i, r%i\n", 2, 56, 2);
  printf("\tadd\tout%i = r%i, r%i\n", 1, 3, reg_scratch+3); // --> out1: source
   printf("\tshl\tout%i = r%i, %i\n;;\n", 2, reg_scratch+5, 2);// out2 = # doubles
  printf("};;\n");

  printf("\tld8\tout%i = [r%i]\n;;\n", 3, 2); // out3=source pe
  printf("\tbr.call.sptk.many\tb%i = shmem_double_put\n;;\n", 0);
  printf("\tmov\tr%i = r%i\n;;\n", 1, reg_scratch+1);
#else
#error FASTBCOPY not implemented
#endif

   extr_bnd(reg_scratch+4, 0, 3);
   printf("\n;;\n");
   printf("\tshladd\tr%i = r%i, %i, r%i\n;;\n", reg_scratch+4, reg_scratch+4,
	  1, reg_scratch+4);
   printf("\tshl\tr%i = r%i, %i\n;;\n", 
	  reg_scratch+4, reg_scratch+4, 5);
  printf("\tadd\tr%i = r%i, r%i\n", reg_scratch+3, reg_scratch+3, reg_scratch+4);


#endif




#if MOD_BND_X>0
//0bw
#ifndef FASTBCOPY
   extr_bnd(reg_scratch+5, 1, 0);
   printf("\n;;\n");
   printf("\tshladd\tr%i = r%i, %i, r%i\n;;\n", reg_scratch+5, reg_scratch+5,
	  1, reg_scratch+5);

  printf("{\n.mmi\n");
  printf("\tld8\tr%i = [r%i]\n", 3, reg_scratch+2); // lattice_boundary[rank] in r3
  printf("\taddl\tr%i = @gprel(neighb_mod), gp\n", 2);
  printf("\tmov\tout%i = r%i\n", 0, reg_nb_bnd[1][0]);
  printf("};;\n");

  printf("{\n.mmi\n");
  printf("\tadds\tr%i = %i, r%i\n", 2, 0, 2);
  printf("\tadd\tout%i = r%i, r%i\n", 1, 3, reg_scratch+3); // --> out1: source
   printf("\tshl\tout%i = r%i, %i\n;;\n", 2, reg_scratch+5, 2);// out2 = # doubles
  printf("};;\n");

  printf("\tld8\tout%i = [r%i]\n;;\n", 3, 2); // out3=source pe
  printf("\tbr.call.sptk.many\tb%i = shmem_double_put\n;;\n", 0);
  printf("\tmov\tr%i = r%i\n;;\n", 1, reg_scratch+1);
#else
#error FASTBCOPY not implemented
#endif
   extr_bnd(reg_scratch+4, 1, 0);
   printf("\n;;\n");
   printf("\tshladd\tr%i = r%i, %i, r%i\n;;\n", reg_scratch+4, reg_scratch+4,
	  1, reg_scratch+4);
   printf("\tshl\tr%i = r%i, %i\n;;\n", 
	  reg_scratch+4, reg_scratch+4, 5);
  printf("\tadd\tr%i = r%i, r%i\n", reg_scratch+3, reg_scratch+3, reg_scratch+4);
#endif



#if MOD_BND_Y>0
//1bw
#ifndef FASTBCOPY
   extr_bnd(reg_scratch+5, 1, 1);
   printf("\n;;\n");
   printf("\tshladd\tr%i = r%i, %i, r%i\n;;\n", reg_scratch+5, reg_scratch+5,
	  1, reg_scratch+5);

  printf("{\n.mmi\n");
  printf("\tld8\tr%i = [r%i]\n", 3, reg_scratch+2); // lattice_boundary[rank] in r3
  printf("\taddl\tr%i = @gprel(neighb_mod), gp\n", 2);
  printf("\tmov\tout%i = r%i\n", 0, reg_nb_bnd[1][1]);
  printf("};;\n");

  printf("{\n.mmi\n");
  printf("\tadds\tr%i = %i, r%i\n", 2, 8, 2);
  printf("\tadd\tout%i = r%i, r%i\n", 1, 3, reg_scratch+3); // --> out1: source
   printf("\tshl\tout%i = r%i, %i\n;;\n", 2, reg_scratch+5, 2);// out2 = # doubles
  printf("};;\n");

  printf("\tld8\tout%i = [r%i]\n;;\n", 3, 2); // out3=source pe

  printf("\tbr.call.sptk.many\tb%i = shmem_double_put\n;;\n", 0);
  printf("\tmov\tr%i = r%i\n;;\n", 1, reg_scratch+1);
#else
#error FASTBCOPY not implemented
#endif
   extr_bnd(reg_scratch+4, 1, 1);
   printf("\n;;\n");
   printf("\tshladd\tr%i = r%i, %i, r%i\n;;\n", reg_scratch+4, reg_scratch+4,
	  1, reg_scratch+4);
   printf("\tshl\tr%i = r%i, %i\n;;\n", 
	  reg_scratch+4, reg_scratch+4, 5);
   printf("\tadd\tr%i = r%i, r%i\n", reg_scratch+3, reg_scratch+3, reg_scratch+4);
#endif



#if MOD_BND_Z>0
//2bw
#ifndef FASTBCOPY
   extr_bnd(reg_scratch+5, 1, 2);
   printf("\n;;\n");
   printf("\tshladd\tr%i = r%i, %i, r%i\n;;\n", reg_scratch+5, reg_scratch+5,
	  1, reg_scratch+5);

  printf("{\n.mmi\n");
  printf("\tld8\tr%i = [r%i]\n", 3, reg_scratch+2); // lattice_boundary[rank] in r3
  printf("\taddl\tr%i = @gprel(neighb_mod), gp\n", 2);
  printf("\tmov\tout%i = r%i\n", 0, reg_nb_bnd[1][2]);
  printf("};;\n");

  printf("{\n.mmi\n");
  printf("\tadds\tr%i = %i, r%i\n", 2, 16, 2);
  printf("\tadd\tout%i = r%i, r%i\n", 1, 3, reg_scratch+3); // --> out1: source
   printf("\tshl\tout%i = r%i, %i\n;;\n", 2, reg_scratch+5, 2);// out2 = # doubles
  printf("};;\n");

  printf("\tld8\tout%i = [r%i]\n;;\n", 3, 2); // out3=source pe
  printf("\tbr.call.sptk.many\tb%i = shmem_double_put\n;;\n", 0);
  printf("\tmov\tr%i = r%i\n;;\n", 1, reg_scratch+1);
#else
#error FASTBCOPY not implemented
#endif
  extr_bnd(reg_scratch+4, 1, 2);
   printf("\n;;\n");
   printf("\tshladd\tr%i = r%i, %i, r%i\n;;\n", reg_scratch+4, reg_scratch+4,
	  1, reg_scratch+4);
   printf("\tshl\tr%i = r%i, %i\n;;\n", 
	  reg_scratch+4, reg_scratch+4, 5);
  printf("\tadd\tr%i = r%i, r%i\n", reg_scratch+3, reg_scratch+3, reg_scratch+4);
#endif



#if MOD_BND_T>0
//3bw
#ifndef FASTBCOPY
  extr_bnd(reg_scratch+5, 1, 3);
   printf("\n;;\n");
   printf("\tshladd\tr%i = r%i, %i, r%i\n;;\n", reg_scratch+5, reg_scratch+5,
	  1, reg_scratch+5);

  printf("{\n.mmi\n");
  printf("\tld8\tr%i = [r%i]\n", 3, reg_scratch+2); // lattice_boundary[rank] in r3
  printf("\taddl\tr%i = @gprel(neighb_mod), gp\n", 2);
  printf("\tmov\tout%i = r%i\n", 0, reg_nb_bnd[1][3]);
  printf("};;\n");

  printf("{\n.mmi\n");
  printf("\tadds\tr%i = %i, r%i\n", 2, 24, 2);
  printf("\tadd\tout%i = r%i, r%i\n", 1, 3, reg_scratch+3); // --> out1: source
  printf("\tshl\tout%i = r%i, %i\n;;\n", 2, reg_scratch+5, 2);// out2 = # doubles
  printf("};;\n");

  printf("\tld8\tout%i = [r%i]\n;;\n", 3, 2); // out3=source pe
  printf("\tbr.call.sptk.many\tb%i = shmem_double_put\n;;\n", 0);
  printf("\tmov\tr%i = r%i\n;;\n", 1, reg_scratch+1);
#else
#error FASTBCOPY not implemented
#endif
#endif



#ifdef SHMEM_COPY_PUT
  printf("\tbr.call.sptk.many\tb%i = shmem_barrier_all\n;;\n", 0);
  printf("\tmov\tr%i = r%i\n;;\n", 1, reg_scratch+1);
#endif



  printf("\tadds\tr%i = %i, r%i\n;;\n", reg_scratch, 16, 12);
  for (int i=2; i<4; i++)
    printf("\tld8\tr%i = [r%i], %i\n;;\n", i, reg_scratch, 8);
  for (int i=8; i<12; i++)
    printf("\tld8\tr%i = [r%i], %i\n;;\n", i, reg_scratch, 8);
  for (int i=14; i<32; i++)
    printf("\tld8\tr%i = [r%i], %i\n;;\n", i, reg_scratch, 8);

  for (int i=6; i<=15; i++)
    printf("\tldf.fill\tf%i=[r%i], 16\n;;\n", i, reg_scratch);
  for (int i=32; i<=127; i++)
    printf("\tldf.fill\tf%i=[r%i], 16\n;;\n", i, reg_scratch);

  printf("\tadds\tr%i = %i, r%i\n;;\n", 12, sp_bytes, 12);

#endif




// increment state counter
#ifndef NO_UPD_STATE
  if (MOD_BND>0)
  {
    printf("\tadds\tr%i = %i, r%i\n;;\n", reg_my_state, 1, reg_my_state);
    printf("\tst8.rel\t[r%i] = r%i\n", reg_my_state_addr, reg_my_state);
  }
#endif




  printf("\tshl\tr%i = r%i, MOD_COORD_LEN\n", reg_local_mod, reg_local_mod);

  if (MOD_BND>0)
    printf("\tadds\tr%i=-1-IPF_DIRAC_SYNC_T1, r%i\n", reg_cnt, reg_cnt);
  else
    printf("\tadds\tr%i=-1, r%i\n", reg_cnt, reg_cnt);
  if (MOD_BND>0)
  {
    printf("\tmov\tr%i = %s\n", reg_scratch, "IPF_DIRAC_SYNC_T1-1");
    printf("\tcmp.ne\tp%i, p%i = r0, r0\n", pred_step2, 0);
    printf("\tcmp.ne\tp%i, p%i = r0, r0\n", pred_no_far_pref, 0);
  }
  printf("\tcmp.eq\tp%i, p%i = r0, r0\n", pred_first, pred_first+1);
  printf("\tcmp.ne\tp%i, p%i = r0, r0\n", pred_last, pred_last+1);
  if (MOD_BND>0)
    printf(";;\n\tmov\tar.lc=r%i\n", reg_scratch);
  else
    printf(";;\n\tmov\tar.lc=r%i\n", reg_cnt);











  printf("\n;;\n");


  const int n_bn=100;
  bundle* bn[n_bn];
  for (int i=0; i<n_bn; i++)
    bn[i]=new bundle;


  int l_max=n_ins_upd_load_regs+3+n_regs_load_psi;
  for (int i=0; i<n_ins_upd_load_regs; i++)
  {
    bn[i]->add(new upd_load_reg_slot(i, 0, 0, 0));
     bn[i]->set_stop();
  }
  for (int i=0; i<n_regs_load_psi+3; i++)
    bn[n_ins_upd_load_regs+i]->add(new upd_load_reg_slot(100+i, 0, 0, 0));
  bn[n_ins_upd_load_regs+1]->set_stop();
  

   for (int i=0; i<l_max; i++)
     bn[i]->print();
  for (int i=0; i<l_max; i++)
  {
    delete bn[i];
    bn[i]=new bundle;
  }

  l_max=7;
  for (int i=0; i<4; i++)
  {
    bn[i]->add(new upd_load_reg_slot2(i, 0, 0, 0));
    bn[i]->set_stop();
  }
  for (int i=0; i<3; i++)
    bn[4+i]->add(new upd_load_reg_slot2(100+i, 0, 0, 0));
  

   for (int i=0; i<l_max; i++)
     bn[i]->print();

  for (int i=0; i<l_max; i++)
  {
    delete bn[i];
    bn[i]=new bundle;
  }






  for (int l_reg=1; l_reg<n_regs_load_psi0; l_reg++)
  {
    int t_n=0;
    int todo=12;
    for (int i=0; i<l_reg; i++)
    {
      int d=(todo+n_regs_load_psi0-i-1)/(n_regs_load_psi0-i);
      t_n+=d;
      todo-=d;
    }
    printf("\tadds\tr%i = %i, r%i\n", reg_load_psi0[l_reg], t_n*16,
	   reg_load_psi0[0]);

  }


  curr_reg_idx=1-curr_reg_idx;      

  l_max=24;
  for (int i=0; i<l_max; i++)
  {
    delete bn[i];
    bn[i]=new bundle;
  }


  for (int i=0; i<12; i++)
    bn[i]->add(new psi_load_slot(i, 0, 0));
  for (int i=n_regs_load_psi-1; i<12; i+=n_regs_load_psi)
    bn[i]->set_stop();

  for (int i=0; i<9; i++)
    bn[12+i]->add(new U_load_slot(i, 0, 0));

  for (int i=n_regs_load_U-1; i<9; i+=n_regs_load_U)
    bn[12+i]->set_stop();
  for (int i=0; i<12; i++)
    bn[12+i]->add(new psi_pr1_slot(i, 0, 0));
  bn[23]->set_stop();


  for (int i=0; i<24; i++)
    bn[i]->print();

  for (int i=0; i<n_bn; i++)
  {
    delete bn[i];
    bn[i]=new bundle;
  }



  l_max=n_ins_upd_load_regs+2+n_regs_load_psi;
  for (int i=0; i<n_ins_upd_load_regs; i++)
  {
    bn[i]->add(new upd_load_reg_slot(i, 0, 1, 0));
    bn[i]->set_stop();
  }
  for (int i=0; i<n_regs_load_psi+2; i++)
    bn[n_ins_upd_load_regs+i]->add(new upd_load_reg_slot(100+i, 0, 1, 0));
  bn[n_ins_upd_load_regs+n_regs_load_psi]->set_stop();

   for (int i=0; i<l_max; i++)
     bn[i]->print();
  for (int i=0; i<l_max; i++)
  {
    delete bn[i];
    bn[i]=new bundle;
  }

  l_max=7;
  for (int i=0; i<4; i++)
  {
    bn[i]->add(new upd_load_reg_slot2(i, 0, 1, 0));
    bn[i]->set_stop();
  }
  for (int i=0; i<3; i++)
    bn[4+i]->add(new upd_load_reg_slot2(100+i, 0, 1, 0));
  

   for (int i=0; i<l_max; i++)
     bn[i]->print();
  for (int i=0; i<l_max; i++)
  {
    delete bn[i];
    bn[i]=new bundle;
  }




#ifdef NO_LOAD_U
  printf(";;\n");
  for (int i=0; i<18; i++)
    fma(reg_U[0][i], 0, 0, 0, true);
  for (int i=0; i<18; i++)
    fma(reg_U[0][i]+1, 0, 0, 0, true);
  printf(";;\n");
#endif
#ifdef NO_LOAD_PSI
  printf(";;\n");
  for (int i=0; i<12; i++)
    fma(reg_psi[0][i], 0, 0, 0, true);
  for (int i=0; i<12; i++)
    fma(reg_psi[0][i]+1, 0, 0, 0, true);
  for (int i=0; i<6; i++)
    fma(reg_psi_n[i], 0, 0, 0, true);
  for (int i=0; i<6; i++)
    fma(reg_psi_n[i]+1, 0, 0, 0, true);
  printf(";;\n");
#endif
#if IPF_DIRAC_SYNC_T1==0
    printf("\tbr.cond.sptk.many\tend_loop%s\n", d_string);
#endif

  printf("\tloop_%s%s:\n", sub_name, d_string);

}

void end_code()
{

    printf("\tend_loop%s:\n", d_string);
  if (MOD_BND>0)
  {
    printf("(p%i)\tbr.cond.sptk.many\ts2_finished%s\n", pred_step2, d_string);
    printf("\tcmp.eq\tp%i, p%i = r0, r0\n", pred_step2, 0);
    printf("\tmov\tar.lc=r%i\n", reg_cnt);

// synchronization
#ifndef NO_SYNC
  printf("/* sync 2 */\n");

    for (int fw=0; fw<2; fw++)
      for (int mu=0; mu<4; mu++)
	if (mod_bnd[mu]>0 && (fw==0 || mod_num[mu]>2)) // if mod_num[mu]=2, fw==bw
	{
	  printf("\tadd\tr%i = r%i, r%i\n;;\n", reg_scratch+2, reg_mod_offs[fw][mu], reg_my_state_addr);
	  printf("sync_loop2_%i_%i%s:\n", mu, fw, d_string);
	  printf("\tld8.acq\tr%i = [r%i]\n;;\n", reg_scratch+3, reg_scratch+2);
	  printf("\tsub\tr%i = r%i, r%i\n;;\n", reg_scratch+3, reg_scratch+3, reg_my_state);
	  printf("\tcmp.gt p%i, p%i = %i, r%i\n;;\n", pred_sync, 0, 0, reg_scratch+3);
	  printf("\(p%i)\tbr.spnt.many\tsync_loop2_%i_%i%s\n", pred_sync, mu, fw, d_string);
	}
#endif
















// copy boundaries via shmem_get



#if (defined SHMEM_COPY && ! (defined SHMEM_COPY_PUT))
#error NOT IMPLEMENTED


  printf("\tadds\tr%i = %i, r%i\n;;\n", 12, -1072, 12);
  printf("\tadds\tr%i = %i, r%i\n;;\n", reg_scratch, 16, 12);
  for (int i=2; i<4; i++)
    printf("\tst8\t[r%i] = r%i, %i\n;;\n", reg_scratch, i, 8);
  for (int i=8; i<12; i++)
    printf("\tst8\t[r%i] = r%i, %i\n;;\n", reg_scratch, i, 8);
  for (int i=14; i<32; i++)
    printf("\tst8\t[r%i] = r%i, %i\n;;\n", reg_scratch, i, 8);

  for (int i=6; i<16; i++)
    printf("\tstfd\t[r%i] = f%i, %i\n;;\n", reg_scratch, i, 8);
  for (int i=32; i<128; i++)
    printf("\tstfd\t[r%i] = f%i, %i\n;;\n", reg_scratch, i, 8);

  printf("\tmov\tr%i = pr\n;;\n", reg_scratch+2);
  printf("\tst8\t[r%i] = r%i, %i\n;;\n", reg_scratch, reg_scratch+2, 8);
  printf("\tmov\tr%i = b0\n;;\n", reg_scratch+2);
  printf("\tst8\t[r%i] = r%i, %i\n;;\n", reg_scratch, reg_scratch+2, 8);


  printf("\tmov\tr%i = r%i\n", reg_scratch+1, 1);






  printf("\tshr\tr%i = r%i, MOD_COORD_LEN-%i\n", reg_scratch, reg_local_mod, 3);
  printf("\taddl\tr%i = @gprel(lattice_boundary), gp\n;;\n", reg_scratch+2);
  printf("\tadd\tr%i = r%i, r%i\n;;\n", reg_scratch+2, reg_scratch+2, reg_scratch);





#if MOD_BND_X>0
// 0fw
  printf("{\n.mmi\n");
  printf("\tld8\tr%i = [r%i]\n", 3, reg_scratch+2); // lattice_boundary[rank] in r3
  printf("\taddl\tr%i = @gprel(neighb_mod), gp\n", 2);
  printf("\tmov\tout%i = r%i\n", 0, reg_nb_bnd[0][0]);
  printf("};;\n");

  printf("{\n.mmi\n");
  printf("\tadds\tr%i = %i, r%i\n", 2, 0, 2);
  printf("\taddl\tout%i = 0, r%i\n", 1, 3); // --> out1
  printf("\taddl\tout%i = 12*MOD_BND_X, r%i\n", 2, 0); // out2 = # doubles
  printf("};;\n");

  printf("\tld8\tout%i = [r%i]\n;;\n", 3, 2); // out3=source pe
  printf("\tbr.call.sptk.many\tb%i = shmem_double_get\n;;\n", 0);
  printf("\tmov\tr%i = r%i\n;;\n", 1, reg_scratch+1);
#endif

#if MOD_BND_Y>0
// 1fw
  printf("{\n.mmi\n");
  printf("\tld8\tr%i = [r%i]\n", 3, reg_scratch+2); // lattice_boundary[rank] in r3
  printf("\taddl\tr%i = @gprel(neighb_mod), gp\n", 2);
  printf("\tmov\tout%i = r%i\n", 0, reg_nb_bnd[0][1]);
  printf("};;\n");

  printf("{\n.mmi\n");
  printf("\tadds\tr%i = %i, r%i\n", 2, 8, 2);
  printf("\taddl\tout%i = 8*12*MOD_BND_X, r%i\n", 1, 3); // --> out1
  printf("\taddl\tout%i = 12*MOD_BND_Y, r%i\n", 2, 0); // out2 = # doubles
  printf("};;\n");

  printf("\tld8\tout%i = [r%i]\n;;\n", 3, 2); // out3=source pe
  printf("\tbr.call.sptk.many\tb%i = shmem_double_get\n;;\n", 0);

  printf("\tmov\tr%i = r%i\n;;\n", 1, reg_scratch+1);
#endif

#if MOD_BND_Z>0
//2fw
  printf("{\n.mmi\n");
  printf("\tld8\tr%i = [r%i]\n", 3, reg_scratch+2); // lattice_boundary[rank] in r3
  printf("\taddl\tr%i = @gprel(neighb_mod), gp\n", 2);
  printf("\tmov\tout%i = r%i\n", 0, reg_nb_bnd[0][2]);
  printf("};;\n");

  printf("{\n.mmi\n");
  printf("\tadds\tr%i = %i, r%i\n", 2, 16, 2);
  printf("\taddl\tout%i = 8*12*(MOD_BND_X+MOD_BND_Y), r%i\n", 1, 3); // --> out1
  printf("\taddl\tout%i = 12*MOD_BND_Z, r%i\n", 2, 0); // out2 = # doubles
  printf("};;\n");

  printf("\tld8\tout%i = [r%i]\n;;\n", 3, 2); // out3=source pe
  printf("\tbr.call.sptk.many\tb%i = shmem_double_get\n;;\n", 0);
  printf("\tmov\tr%i = r%i\n;;\n", 1, reg_scratch+1);
#endif

#if MOD_BND_T>0
//3fw
  printf("{\n.mmi\n");
  printf("\tld8\tr%i = [r%i]\n", 3, reg_scratch+2); // lattice_boundary[rank] in r3
  printf("\taddl\tr%i = @gprel(neighb_mod), gp\n", 2);
  printf("\tmov\tout%i = r%i\n", 0, reg_nb_bnd[0][3]);
  printf("};;\n");

  printf("{\n.mmi\n");
  printf("\tadds\tr%i = %i, r%i\n", 2, 24, 2);
  printf("\taddl\tout%i = 8*12*(MOD_BND_X+MOD_BND_Y+MOD_BND_Z), r%i\n", 1, 3); // --> out1
  printf("\taddl\tout%i = 12*MOD_BND_T, r%i\n", 2, 0); // out2 = # doubles
  printf("};;\n");

  printf("\tld8\tout%i = [r%i]\n;;\n", 3, 2); // out3=source pe
  printf("\tbr.call.sptk.many\tb%i = shmem_double_get\n;;\n", 0);
  printf("\tmov\tr%i = r%i\n;;\n", 1, reg_scratch+1);
#endif


#if MOD_BND_X>0
//0bw
  printf("{\n.mmi\n");
  printf("\tld8\tr%i = [r%i]\n", 3, reg_scratch+2); // lattice_boundary[rank] in r3
  printf("\taddl\tr%i = @gprel(neighb_mod), gp\n", 2);
  printf("\tmov\tout%i = r%i\n", 0, reg_nb_bnd[1][0]);
  printf("};;\n");

  printf("{\n.mmi\n");
  printf("\tadds\tr%i = %i, r%i\n", 2, 32, 2);
  printf("\taddl\tout%i = 8*12*(MOD_BNDH), r%i\n", 1, 3); // --> out1
  printf("\taddl\tout%i = 12*MOD_BND_X, r%i\n", 2, 0); // out2 = # doubles
  printf("};;\n");

  printf("\tld8\tout%i = [r%i]\n;;\n", 3, 2); // out3=source pe
  printf("\tbr.call.sptk.many\tb%i = shmem_double_get\n;;\n", 0);
  printf("\tmov\tr%i = r%i\n;;\n", 1, reg_scratch+1);
#endif

#if MOD_BND_Y>0
//1bw
  printf("{\n.mmi\n");
  printf("\tld8\tr%i = [r%i]\n", 3, reg_scratch+2); // lattice_boundary[rank] in r3
  printf("\taddl\tr%i = @gprel(neighb_mod), gp\n", 2);
  printf("\tmov\tout%i = r%i\n", 0, reg_nb_bnd[1][1]);
  printf("};;\n");

  printf("{\n.mmi\n");
  printf("\tadds\tr%i = %i, r%i\n", 2, 40, 2);
  printf("\taddl\tout%i = 8*12*(MOD_BNDH+MOD_BND_X), r%i\n", 1, 3); // --> out1
  printf("\taddl\tout%i = 12*MOD_BND_Y, r%i\n", 2, 0); // out2 = # doubles
  printf("};;\n");

  printf("\tld8\tout%i = [r%i]\n;;\n", 3, 2); // out3=source pe
  printf("\tbr.call.sptk.many\tb%i = shmem_double_get\n;;\n", 0);
  printf("\tmov\tr%i = r%i\n;;\n", 1, reg_scratch+1);
#endif

#if MOD_BND_Z>0
//2bw
  printf("{\n.mmi\n");
  printf("\tld8\tr%i = [r%i]\n", 3, reg_scratch+2); // lattice_boundary[rank] in r3
  printf("\taddl\tr%i = @gprel(neighb_mod), gp\n", 2);
  printf("\tmov\tout%i = r%i\n", 0, reg_nb_bnd[1][2]);
  printf("};;\n");

  printf("{\n.mmi\n");
  printf("\tadds\tr%i = %i, r%i\n", 2, 48, 2);
  printf("\taddl\tout%i = 8*12*(MOD_BNDH+MOD_BND_X+MOD_BND_Y), r%i\n", 1, 3); // --> out1
  printf("\taddl\tout%i = 12*MOD_BND_Z, r%i\n", 2, 0); // out2 = # doubles
  printf("};;\n");

  printf("\tld8\tout%i = [r%i]\n;;\n", 3, 2); // out3=source pe
  printf("\tbr.call.sptk.many\tb%i = shmem_double_get\n;;\n", 0);
  printf("\tmov\tr%i = r%i\n;;\n", 1, reg_scratch+1);
#endif

#if MOD_BND_T>0
//3bw
  printf("{\n.mmi\n");
  printf("\tld8\tr%i = [r%i]\n", 3, reg_scratch+2); // lattice_boundary[rank] in r3
  printf("\taddl\tr%i = @gprel(neighb_mod), gp\n", 2);
  printf("\tmov\tout%i = r%i\n", 0, reg_nb_bnd[1][3]);
  printf("};;\n");

  printf("{\n.mmi\n");
  printf("\tadds\tr%i = %i, r%i\n", 2, 56, 2);
  printf("\taddl\tout%i = 8*12*(MOD_BNDH+MOD_BND_X+MOD_BND_Y+MOD_BND_Z), r%i\n", 1, 3); // --> out1
  printf("\taddl\tout%i = 12*MOD_BND_T, r%i\n", 2, 0); // out2 = # doubles
  printf("};;\n");

  printf("\tld8\tout%i = [r%i]\n;;\n", 3, 2); // out3=source pe
  printf("\tbr.call.sptk.many\tb%i = shmem_double_get\n;;\n", 0);
  printf("\tmov\tr%i = r%i\n;;\n", 1, reg_scratch+1);
#endif






  printf("\tadds\tr%i = %i, r%i\n;;\n", reg_scratch, 16, 12);
  for (int i=2; i<4; i++)
    printf("\tld8\tr%i = [r%i], %i\n;;\n", i, reg_scratch, 8);
  for (int i=8; i<12; i++)
    printf("\tld8\tr%i = [r%i], %i\n;;\n", i, reg_scratch, 8);
  for (int i=14; i<32; i++)
    printf("\tld8\tr%i = [r%i], %i\n;;\n", i, reg_scratch, 8);




  for (int i=6; i<16; i++)
    printf("\tldfd\tf%i = [r%i], %i\n;;\n", i, reg_scratch, 8);
  for (int i=32; i<128; i++)
    printf("\tldfd\tf%i = [r%i], %i\n;;\n", i, reg_scratch, 8);


  printf("\tld8\tr%i = [r%i], %i\n;;\n", reg_scratch+2, reg_scratch, 8);
  printf("\tmov\tpr = r%i, 0x1ffff\n", reg_scratch+2);
  printf("\tld8\tr%i = [r%i], %i\n;;\n", reg_scratch+2, reg_scratch, 8);
  printf("\tmov\tb0 = r%i\n", reg_scratch+2);


  printf("\tadds\tr%i = %i, r%i\n;;\n", 12, 1072, 12);

#endif












    printf("\tbr.sptk.many\tloop_%s%s\n", sub_name, d_string);
    printf("s2_finished%s:\n", d_string);


#if MOD_NUM>1
    printf("\tadds\tr%i = %i, r%i\n;;\n", reg_my_state, 1, reg_my_state);
    printf("\tst8.rel\t[r%i] = r%i\n", reg_my_state_addr, reg_my_state);
#endif

  }
  const int n_bn=24;
  int l_max=24;
  bundle* bn[n_bn];
  for (int i=0; i<n_bn; i++)
    bn[i]=new bundle;


#ifndef NO_STORE_RES
  for (int i=0; i<24; i++)
    bn[i]->add(new res_store_slot(i, 0, 0));
  for (int i=n_regs_store_res-1; i<24; i+=n_regs_store_res)
    bn[i]->set_stop();

  for (int i=0; i<l_max; i++)
    bn[i]->print();

  for (int i=0; i<n_bn; i++)
    delete bn[i];
#endif







  printf("\tmov\tloc0=r12\n;;\n");

//restore predicates
  printf("\tmov\tpr = r%i, 0x1ffff\n", reg_save_preds);

// restore floating regs
  for (int i=2; i<=5; i++)
    printf("\tldf.fill\tf%i=[loc0], 16\n;;\n", i);
  for (int i=16; i<=31; i++)
    printf("\tldf.fill\tf%i=[loc0], 16\n;;\n", i);

  printf("\tadds\tr12 = %i, r12\n", st_size);
  printf("\tmov\tb0 = r%i\n", reg_save_b);
  printf("\tmov\tar.lc = r%i\n", reg_save_lc);
  printf("\tmov\tar.pfs = r%i\n;;\n", reg_save_pfs);

  printf("\tbr.ret.sptk.many\tb0\n");

// #ifndef SHMEM_COPY
//   printf("\tbr.ret.sptk.many\tb0\n");
// #else
//   printf("\tbr.ret.sptk.many\tb%i\n", branch_scratch_reg);
// #endif
  printf(".endp\n");
}

void cr_asm(bool dagger_, bool single_)
{
  dagger=dagger_;
  single=single_;
  prec_str=single? 's' : 'd';
  sprintf(d_string, "_%c_%c", dagger? 'd':'n', prec_str);
  main_loop=false;
  start_code();
  int b_count=0;
  main_loop=true;
  for (int f=0; f<2; f++)
    for (int mu=0; mu<4; mu++)
    {
      int mu_n, mu_nn, mu_p, f_n, f_nn, f_p, n_bn;
      curr_reg_idx=1-curr_reg_idx;      
      get_next_mu_f(mu, f, mu_n, f_n);
      get_next_mu_f(mu_n, f_n, mu_nn, f_nn);
      get_prev_mu_f(mu, f, mu_p, f_p);
      int n_load_psi=mu==2? 10 : 12;
      int pos_load_psi=0;
      int pos_load_U=n_load_psi;
      int pos_load_psi0=0;
      bool load_psi0=mu==0 && f==0;
      bool load_psi02=mu==1 && f==0;
      bool store_res=mu==0 && f==0;
      bool store_res2=mu==2 && f==0;
      if (load_psi0)
      {
	pos_load_psi+=12;
	pos_load_U+=12;
      }
      int pos_store_res;
      if (store_res)
      {
	pos_store_res=0;
	pos_load_psi+=24;
	pos_load_U+=24;
	pos_load_psi0+=24;
      }
      else
      {
	pos_load_psi+=2;
	pos_load_U+=2;
	pos_load_psi0+=2;
      }

      if (store_res2)
	pos_store_res=32;
      if (load_psi02)
	pos_load_psi0=34;
      n_bn=72;
      bool pr1=mu!=2;
      pr1=true;
      bool pr2=mu!=0;
      int pos_pr1=72;
      if (pr2)
	pos_pr1+=12;
      int pos_pr2=0;
      if (pr1)
	n_bn+=12;
      if (pr2)
	n_bn+=24;

      for (int i=0; i<n_bn; i++)
	bn[i]=new bundle;

      if (load_psi0)
      {
	for (int i=0; i<12+n_regs_load_psi0; i++)
	  bn[pos_load_psi0+i]->add(new psi0_load_slot(i, f, mu));
	for (int i=n_regs_load_psi0-1; i<12+n_regs_load_psi0;
	     i+=n_regs_load_psi0)
	  bn[pos_load_psi0+i]->set_stop();
      }


      if (load_psi02)
  	for (int i=0; i<2; i++)
  	  bn[pos_load_psi0+i]->add(new psi0_load_slot(
  				     12+n_regs_load_psi0+i, f, mu));
//      bn[pos_load_psi0+n_regs_load_psi0+3]->set_stop();

      if (store_res)
      {
	for (int i=0; i<24; i++)
	  bn[pos_store_res+i]->add(new res_store_slot(i, f, mu));
	for (int i=n_regs_store_res-1; i<24; i+=n_regs_store_res)
	  bn[pos_store_res+i]->set_stop();
      }

      if (store_res2)
  	for (int i=0; i<2; i++)
  	  bn[pos_store_res+i]->add(new res_store_slot(24+i, f, mu));


      for (int i=0; i<n_load_psi; i++)
  	bn[pos_load_psi+i]->add(new psi_load_slot(i, f_n, mu_n));
      for (int i=n_regs_load_psi-1; i<n_load_psi; i+=n_regs_load_psi)
	bn[pos_load_psi+i]->set_stop();
      




      for (int i=0; i<9; i++)
  	bn[pos_load_U+i]->add(new U_load_slot(i, f_n, mu_n));
      for (int i=n_regs_load_psi-1; i<9; i+=n_regs_load_psi)
	bn[pos_load_U+i]->set_stop();



      if (mu==0 && f==0)
      {
	bn[3]->add(new upd_pred_slot(0, f, mu));
	bn[33]->add(new upd_pred_slot(1, f, mu));

	if (MOD_BND>0)
	{
	  bn[41]->add(new upd_pred_slot(100, f, mu));
	  bn[43]->add(new upd_pred_slot(101, f, mu));
	  bn[49]->add(new upd_pred_slot(102, f, mu));
	  bn[53]->add(new upd_pred_slot(200, f, mu));
	  bn[15]->add(new pref_state_slot(0, f, mu));
	}

      }


      if ((mod_bnd[mu]>0) && (mod_num[mu]>2 || f==0))
      {
	bn[21]->add(new pref_state_slot(100, f, mu));
	bn[69]->add(new pref_state_slot(101, f, mu));
      }

      if (mu==3 && f==0)
	bn[33]->add(new upd_pred_slot(2, f, mu));
      if (mu==0 && f==1)
	bn[33]->add(new upd_pred_slot(3, f, mu)); //inc x



      bn[0]->add(new upd_load_reg_slot(0, f_nn, mu_nn, 0)); // load instruction
      for (int i=0; i<4; i++)
	bn[10+i]->add(new upd_load_reg_slot(i+1, f_nn, mu_nn, 0));
      bn[11]->set_stop();
      if (pos_load_psi+n_load_psi>49)
      {
	fprintf(stderr, "error 5786\n");
	exit(1);
      }
      bn[50]->add(new upd_load_reg_slot(5, f_nn, mu_nn, 0));
      bn[51]->set_stop();
      bn[52]->add(new upd_load_reg_slot(6, f_nn, mu_nn, 0));
      bn[53]->set_stop();

      if (mu_nn==3 && ((f_nn==0 && !dagger) || (f_nn==1 && dagger))) //XXX
      {
	bn[55]->add(new upd_load_reg_slot(7, f_nn, mu_nn, 0));
	bn[55]->set_stop();
      }
      for (int i=0; i<n_regs_load_psi+2; i++)
	bn[58+i]->add(new upd_load_reg_slot(100+i, f_nn, mu_nn, 0));
      if (f_nn==0 && mu_nn==0)
	bn[58+4]->add(new upd_load_reg_slot(100+4, f_nn, mu_nn, 0));
      bn[61]->set_stop();


      bn[14]->add(new upd_load_reg_slot2(0, f_nn, mu_nn, 0));
      if (f_nn==1)
      {
	bn[15]->set_stop();
	bn[16]->add(new upd_load_reg_slot2(1, f_nn, mu_nn, 0));
      }
      bn[17]->set_stop();
      int i_o=f_nn==0? 0 : 1;
      bn[18]->add(new upd_load_reg_slot2(1+i_o, f_nn, mu_nn, 0));
      bn[19]->set_stop();
      bn[20]->add(new upd_load_reg_slot2(2+i_o, f_nn, mu_nn, 0));
      if (pos_load_U+10>58)
      {
	fprintf(stderr, "error 98576\n");
	exit(1);
      }
      bn[59]->add(new upd_load_reg_slot2(3+i_o, f_nn, mu_nn, 0));
      for (int i=0; i<3; i++)
	bn[60+i]->add(new upd_load_reg_slot2(100+i, f_nn, mu_nn, 0));

//near prefetch spinor
       bn[1]->add(new upd_load_reg_slot(0, f, mu, 1)); // load instruction
       for (int i=0; i<4; i++)
 	bn[22+i]->add(new upd_load_reg_slot(i+1, f, mu, 1));
       bn[23]->set_stop();
       bn[40]->add(new upd_load_reg_slot(5, f, mu, 1));
       bn[41]->set_stop();
       bn[42]->add(new upd_load_reg_slot(6, f, mu, 1));
       bn[43]->set_stop();

       int p_time=44;
       if (mu==0 && f==0)
	 p_time=62;
        bn[p_time]->add(new upd_load_reg_slot(100, f, mu, 1));
        bn[p_time+1]->set_stop();
        bn[p_time+2]->add(new upd_load_reg_slot(101, f, mu, 1));
        bn[p_time+3]->set_stop();
// prefetch U



       bn[26]->add(new upd_load_reg_slot2(0, f, mu, 1));
       if (f==1)
       {
 	bn[27]->set_stop();
 	bn[28]->add(new upd_load_reg_slot2(1, f, mu, 1));
       }
       bn[29]->set_stop();
       i_o=f==0? 0 : 1;
       bn[30]->add(new upd_load_reg_slot2(1+i_o, f, mu, 1));
       bn[31]->set_stop();
       bn[32]->add(new upd_load_reg_slot2(2+i_o, f, mu, 1));
       bn[33]->set_stop();
       bn[34]->add(new upd_load_reg_slot2(3+i_o, f, mu, 1));


       bn[p_time+4]->add(new upd_load_reg_slot2(100, f, mu, 1));
       bn[p_time+5]->set_stop();
       bn[p_time+6]->add(new upd_load_reg_slot2(101, f, mu, 1));
       bn[p_time+7]->set_stop();


//far prefetch spinor
       int l_pos;
       if (mu==0 && f==0)
 	 l_pos=2;
       else
 	 l_pos=pos_load_U+9;
       bn[l_pos]->add(new upd_load_reg_slot(0, f, mu, 2)); // load instruction
       if (l_pos>30)
       {
	 fprintf(stderr, "l_pos>30\n");
	 exit(1);
       }
        for (int i=0; i<4; i++)
 	 bn[44+i]->add(new upd_load_reg_slot(i+1, f, mu, 2));
        bn[45]->set_stop();
        bn[48]->add(new upd_load_reg_slot(5, f, mu, 2));
        bn[49]->set_stop();
        bn[51]->add(new upd_load_reg_slot(6, f, mu, 2));
        bn[51]->set_stop();

        p_time=62;
        bn[p_time+1]->add(new upd_load_reg_slot(100, f, mu, 2));
        bn[p_time+1]->set_stop();
        bn[p_time+3]->add(new upd_load_reg_slot(101, f, mu, 2));
        bn[p_time+3]->set_stop();




// floating computations
      for (int i=0; i<72; i++)
      {
	int pos=i;
	if (pr2 && (pos>=pos_pr2))
	  pos+=24;
	if (pr1 && (pos>=pos_pr1))
	  pos+=12;
	bn[pos]->add(new U_mult_slot(i, f, mu));
	if (i%12==11)
	  bn[pos]->set_stop();
      }
      if (pr1)
      {
	for (int i=0; i<12; i++)
	  bn[i+pos_pr1]->add(new psi_pr1_slot(i, f_n, mu_n));
	bn[pos_pr1+11]->set_stop();
      }
      if (pr2)
      {
	for (int i=0; i<24; i++)
	  bn[i+pos_pr2]->add(new psi_pr2_slot(i, f_p, mu_p));
	bn[pos_pr2+23]->set_stop();
      }
      int b_pos=71;
      if (pr1)
	b_pos+=12;
      if (pr2)
	b_pos+=24;
      if (f==1 && mu==3)
	bn[b_pos]->add(new branch_slot(0, f, mu));

      for (int i=0; i<n_bn; i++)
      {
	printf("//bundle %i mu %i f %i i %i\n", b_count++, mu, f, i);
#ifdef DEBUG_PREF
	bn[i]->debug();
#endif
	bn[i]->print();
      }
      for (int i=0; i<n_bn; i++)
	delete bn[i];
//final branch
    }
  main_loop=false;

  end_code();

}


int main()
{
  init_reg_n();
  cr_asm(false, false);
  cr_asm(true, false);
}
