/****************************************************************************
**  SCALASCA    http://www.scalasca.org/                                   **
*****************************************************************************
**  Copyright (c) 1998-2012                                                **
**  Forschungszentrum Juelich GmbH, Juelich Supercomputing Centre          **
**                                                                         **
**  Copyright (c) 2003-2008                                                **
**  University of Tennessee, Innovative Computing Laboratory               **
**                                                                         **
**  See the file COPYRIGHT in the package base directory for details       **
****************************************************************************/

/*
 *-----------------------------------------------------------------------------
 *
 *  EPIK Library (Event Processing Interface Kit)
 *
 *  - SUN f90 compiler PHAT interface
 * 
 *-----------------------------------------------------------------------------
 */

#include <stdlib.h>
#include <string.h>
#if (defined (ELG_OMPI) || defined (ELG_OMP))
  #include <omp.h>
#endif

#include "epk_conf.h"
#include "epk_comp.h"
#include "elg_error.h"
#include "esd_def.h"
#include "esd_event.h"
#include "esd_run.h"

/*
 *-----------------------------------------------------------------------------
 * Simple hash table to map function names to region identifier
 *-----------------------------------------------------------------------------
 */

typedef struct HN {
  long id;            /* hash code (address of function name) */
  elg_ui4 elgid;      /* associated EPIK region identifier  */
  struct HN* next;
} HashNode;

#define HASH_MAX 1021

static HashNode* htab[HASH_MAX];

static elg_ui4 phat_main_rid = ELG_NO_ID;

/*
 * Stores EPIK region identifier `e' under hash code `h'
 */

static void epk_hash_put(long h, elg_ui4 e) {
  long id = h % HASH_MAX;
  HashNode *add = malloc(sizeof(HashNode));
  add->id = h;
  add->elgid = e;
  add->next = htab[id];
  htab[id] = add;
}

/*
 * Lookup hash code `h'
 * Returns EPIK region identifier if already stored, otherwise ELG_NO_ID
 */

static elg_ui4 epk_hash_get(long h) {
  long id = h % HASH_MAX;
  HashNode *curr = htab[id];
  while ( curr ) {
    if ( curr->id == h ) {
      return curr->elgid;
    }
    curr = curr->next;
  }
  return ELG_NO_ID;
}

/*
 * Register new region with EPIK system
 * `str' is passed in from SUN compiler
 */

static elg_ui4 epk_register_region(char *str) {
  elg_ui4 rid;
  elg_ui4 fid;

  /* -- register region with EPIK and store region identifier -- */
  rid = esd_def_region(str, ELG_NO_ID, ELG_NO_LNO, ELG_NO_LNO, "USR", ELG_FUNCTION);
  epk_hash_put((long) str, rid);
  return rid;
}

/*
 * This function is called at program end
 */

void phat_elg_finalize () { 
  esd_exit(phat_main_rid); /*main*/
  esd_close();
}

void epk_filter_status()
{
  const char* filter_file = epk_get(EPK_FILTER);
  if ( (filter_file != NULL) && strlen(filter_file) ) {
    elg_warning("EPK_FILTER file \"%s\" not supported for Sun Studio compilers", filter_file);
  }
}

/*
 * This function is called at program start
 */

void phat_elg_init ()
{ 
  epk_comp_status = &epk_filter_status;
  epk_comp_finalize = &phat_elg_finalize;
  esd_open();
  phat_main_rid = epk_register_region("main");
  esd_enter(phat_main_rid);
}
#pragma init (phat_elg_init)

/*
 * This function is called at the entry of each function
 * The call is generated by the SUN f90 compilers
 */

void phat_enter(char *str, int *id) {
  /* -- ignore SUN OMP runtime functions -- */
  if ( strchr(str, '$') != NULL ) return;

  /* -- get region identifier -- */
  if ( *id == -1 ) {
    /* -- region entered the first time, register region -- */
#   if defined (ELG_OMPI) || defined (ELG_OMP)
    if (omp_in_parallel()) {
#     pragma omp critical (epk_comp_register_region)
      {
        if ( (*id = epk_hash_get((long) str)) == ELG_NO_ID ) {
          *id = epk_register_region(str);
        }
      }
    } else {
      *id = epk_register_region(str);
    }
#   else
    *id = epk_register_region(str);
#   endif
  }

  /* -- enter region event -- */
  esd_enter(*id);
}


/*
 * This function is called at the exit of each function
 * The call is generated by the SUN F90 compilers
 */

void phat_exit(char *str, int *id) {
  /* -- ignore unidentified regions -- */
  if ( *id == -1 ) return;

  /* -- exit region event -- */
  esd_exit(*id);
}
