/*--------------------------------------------------------------------*/
/*--- Callgrind                                                    ---*/
/*---                                                 ct_context.c ---*/
/*--------------------------------------------------------------------*/

/*
   This file is part of Callgrind, a Valgrind tool for call tracing.

   Copyright (C) 2002-2017, Josef Weidendorfer (Josef.Weidendorfer@gmx.de)

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

   This program 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 this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
   02111-1307, USA.

   The GNU General Public License is contained in the file COPYING.
*/

#include "global.h"


/*------------------------------------------------------------*/
/*--- Context operations                                   ---*/
/*------------------------------------------------------------*/

#define N_FNSTACK_INITIAL_ENTRIES 500
#define N_CXT_INITIAL_ENTRIES 2537

fn_stack CLG_(current_fn_stack);

void CLG_(init_fn_stack)(fn_stack* s)
{
  CLG_ASSERT(s != 0);

  s->size   = N_FNSTACK_INITIAL_ENTRIES;   
  s->bottom = (fn_node**) CLG_MALLOC("cl.context.ifs.1",
                                     s->size * sizeof(fn_node*));
  s->top    = s->bottom;
  s->bottom[0] = 0;
}

void CLG_(copy_current_fn_stack)(fn_stack* dst)
{
  CLG_ASSERT(dst != 0);

  dst->size   = CLG_(current_fn_stack).size;
  dst->bottom = CLG_(current_fn_stack).bottom;
  dst->top    = CLG_(current_fn_stack).top;
}

void CLG_(set_current_fn_stack)(fn_stack* s)
{
  CLG_ASSERT(s != 0);

  CLG_(current_fn_stack).size   = s->size;
  CLG_(current_fn_stack).bottom = s->bottom;
  CLG_(current_fn_stack).top    = s->top;
}

static cxt_hash cxts;

void CLG_(init_cxt_table)()
{
   Int i;
   
   cxts.size    = N_CXT_INITIAL_ENTRIES;
   cxts.entries = 0;
   cxts.table   = (Context**) CLG_MALLOC("cl.context.ict.1",
                                         cxts.size * sizeof(Context*));

   for (i = 0; i < cxts.size; i++)
     cxts.table[i] = 0;
}

/* double size of cxt table  */
static void resize_cxt_table(void)
{
    UInt i, new_size, conflicts1 = 0, conflicts2 = 0;
    Context **new_table, *curr, *next;
    UInt new_idx;

    new_size  = 2* cxts.size +3;
    new_table = (Context**) CLG_MALLOC("cl.context.rct.1",
                                       new_size * sizeof(Context*));

    for (i = 0; i < new_size; i++)
      new_table[i] = NULL;

    for (i = 0; i < cxts.size; i++) {
        if (cxts.table[i] == NULL) continue;

        curr = cxts.table[i];
        while (NULL != curr) {
            next = curr->next;

            new_idx = (UInt) (curr->hash % new_size);

            curr->next = new_table[new_idx];
            new_table[new_idx] = curr;
            if (curr->next) {
                conflicts1++;
                if (curr->next->next)
                    conflicts2++;
            }

            curr = next;
        }
    }

    VG_(free)(cxts.table);


    CLG_DEBUG(0, "Resize Context Hash: %u => %u (entries %u, conflicts %u/%u)\n",
             cxts.size, new_size,
             cxts.entries, conflicts1, conflicts2);

    cxts.size  = new_size;
    cxts.table = new_table;
    CLG_(stat).cxt_hash_resizes++;
}

__inline__
static UWord cxt_hash_val(fn_node** fn, UInt size)
{
    UWord hash = 0;
    UInt count = size;
    while(*fn != 0) {
        hash = (hash<<7) + (hash>>25) + (UWord)(*fn);
        fn--;
        count--;
        if (count==0) break;
    }
    return hash;
}

__inline__
static Bool is_cxt(UWord hash, fn_node** fn, Context* cxt)
{
    int count;
    fn_node** cxt_fn;

    if (hash != cxt->hash) return False;

    count = cxt->size;
    cxt_fn = &(cxt->fn[0]);
    while((*fn != 0) && (count>0)) {
        if (*cxt_fn != *fn) return False;
        fn--;
        cxt_fn++;
        count--;
    }
    return True;
}

/**
 * Allocate new Context structure
 */
static Context* new_cxt(fn_node** fn)
{
    Context* cxt;
    UInt idx, offset;
    UWord hash;
    int size, recs;
    fn_node* top_fn;

    CLG_ASSERT(fn);
    top_fn = *fn;
    if (top_fn == 0) return 0;

    size = top_fn->separate_callers +1;
    recs = top_fn->separate_recursions;
    if (recs<1) recs=1;

    /* check fill degree of context hash table and resize if needed (>80%) */
    cxts.entries++;
    if (10 * cxts.entries / cxts.size > 8)
        resize_cxt_table();

    cxt = (Context*) CLG_MALLOC("cl.context.nc.1",
                                sizeof(Context)+sizeof(fn_node*)*size);

    // hash value calculation similar to cxt_hash_val(), but additionally
    // copying function pointers in one run
    hash = 0;
    offset = 0;
    while(*fn != 0) {
        hash = (hash<<7) + (hash>>25) + (UWord)(*fn);
	cxt->fn[offset] = *fn;
        offset++;
        fn--;
        if (offset >= size) break;
    }
    if (offset < size) size = offset;

    cxt->size        = size;
    cxt->base_number = CLG_(stat).context_counter;
    cxt->hash        = hash;

    CLG_(stat).context_counter += recs;
    CLG_(stat).distinct_contexts++;

    /* insert into Context hash table */
    idx = (UInt) (hash % cxts.size);
    cxt->next = cxts.table[idx];
    cxts.table[idx] = cxt;

#if CLG_ENABLE_DEBUG
    CLG_DEBUGIF(3) {
      VG_(printf)("  new_cxt ox%p: ", cxt);
      CLG_(print_cxt)(12, cxt, 0);
    }
#endif

    return cxt;
}

/* get the Context structure for current context */
Context* CLG_(get_cxt)(fn_node** fn)
{
    Context* cxt;
    UInt size, idx;
    UWord hash;

    CLG_ASSERT(fn != 0);
    if (*fn == 0) return 0;
    size = (*fn)->separate_callers+1;
    if (size<=0) { size = -size+1; }

    CLG_DEBUG(5, "+ get_cxt(fn '%s'): size %u\n",
                (*fn)->name, size);

    hash = cxt_hash_val(fn, size);

    if ( ((cxt = (*fn)->last_cxt) != 0) && is_cxt(hash, fn, cxt)) {
        CLG_DEBUG(5, "- get_cxt: %p\n", cxt);
        return cxt;
    }

    CLG_(stat).cxt_lru_misses++;

    idx = (UInt) (hash % cxts.size);
    cxt = cxts.table[idx];

    while(cxt) {
        if (is_cxt(hash,fn,cxt)) break;
        cxt = cxt->next;
    }

    if (!cxt)
        cxt = new_cxt(fn);

    (*fn)->last_cxt = cxt;

    CLG_DEBUG(5, "- get_cxt: %p\n", cxt);

    return cxt;
}


/**
 * Change execution context by calling a new function from current context
 * Pushing 0x0 specifies a marker for a signal handler entry
 */
void CLG_(push_cxt)(fn_node* fn)
{
  call_stack* cs = &CLG_(current_call_stack);
  Int fn_entries;

  CLG_DEBUG(5, "+ push_cxt(fn '%s'): old ctx %d\n", 
	    fn ? fn->name : "0x0",
	    CLG_(current_state).cxt ?
	    (Int)CLG_(current_state).cxt->base_number : -1);

  /* save old context on stack (even if not changed at all!) */
  CLG_ASSERT(cs->sp < cs->size);
  CLG_ASSERT(cs->entry[cs->sp].cxt == 0);
  cs->entry[cs->sp].cxt = CLG_(current_state).cxt;
  cs->entry[cs->sp].fn_sp = CLG_(current_fn_stack).top - CLG_(current_fn_stack).bottom;

  if (fn && (*(CLG_(current_fn_stack).top) == fn)) return;
  if (fn && (fn->group>0) &&
      ((*(CLG_(current_fn_stack).top))->group == fn->group)) return;

  /* resizing needed ? */
  fn_entries = CLG_(current_fn_stack).top - CLG_(current_fn_stack).bottom;
  if (fn_entries == CLG_(current_fn_stack).size-1) {
    UInt new_size = CLG_(current_fn_stack).size *2;
    fn_node** new_array = (fn_node**) CLG_MALLOC("cl.context.pc.1",
						 new_size * sizeof(fn_node*));
    int i;
    for(i=0;i<CLG_(current_fn_stack).size;i++)
      new_array[i] = CLG_(current_fn_stack).bottom[i];
    VG_(free)(CLG_(current_fn_stack).bottom);
    CLG_(current_fn_stack).top = new_array + fn_entries;
    CLG_(current_fn_stack).bottom = new_array;

    CLG_DEBUG(0, "Resize Context Stack: %u => %u (pushing '%s')\n", 
	     CLG_(current_fn_stack).size, new_size,
	     fn ? fn->name : "0x0");

    CLG_(current_fn_stack).size = new_size;
  }

  if (fn && (*(CLG_(current_fn_stack).top) == 0)) {
    UInt *pactive;

    /* this is first function: increment its active count */
    pactive = CLG_(get_fn_entry)(fn->number);
    (*pactive)++;
  }

  CLG_(current_fn_stack).top++;
  *(CLG_(current_fn_stack).top) = fn;
  CLG_(current_state).cxt = CLG_(get_cxt)(CLG_(current_fn_stack).top);

  CLG_DEBUG(5, "- push_cxt(fn '%s'): new cxt %d, fn_sp %ld\n",
	    fn ? fn->name : "0x0",
	    CLG_(current_state).cxt ?
	    (Int)CLG_(current_state).cxt->base_number : -1,
	    CLG_(current_fn_stack).top - CLG_(current_fn_stack).bottom + 0L);
}