// j_PcMap.h -- bi-directional vPC to nPC mappings
//
// Author: Ian.Piumarta@INRIA.Fr
//
// Last edited: Tue Jan  4 19:12:08 2000 by piumarta (Ian Piumarta) on pingu


#ifndef _j_PcMap_h
#define _j_PcMap_h


#include "archdep.h"

#include "Cache.h"


// symmetric mapping between virtual and native PC values.
//
// operator new called with argument giving the largest anticipated
// number of entries (the number of bytecodes in the method would be
// a fail-proof guess ;-).

class PcMap
{
  struct PcMapping
  {
    int   vPC;
    insn *nPC;
  };

public:
  int size;

protected:
  friend class NativeMethod;
  PcMapping map[1];	// should be [0] but ANSI compilers can't cope

public:
  inline void *operator new(size_t lbs, size_t nEntries)
    {
      extern Cache *mapCache;
      return mapCache->reserve(lbs + nEntries * sizeof(PcMapping));
    }

public:
  PcMap(void) : size(0) {}

  ~PcMap(void) { fatal("this cannot happen"); }

  // indicate that there will be no more entries made in the map.
  // MUST be called when the map is finished, to finialise allocation.

  inline void finalise(void) const
    {
#     ifndef NDEBUG
      void *base= 
#     endif
      mapCache->allocate(sizeof(PcMap)-sizeof(map) + size * sizeof(PcMapping));
      assert(base == (void *)this);
    }

  // add an entry to the map, associating vPC with nPC

  inline void notePC(int vPC, insn *nPC) {
    //printf("notePC: %d -> %p\n", vPC, nPC);
    assert(size == 0 || map[size - 1].vPC < vPC);   // monotonically increasing
    assert(size == 0 || map[size - 1].nPC <= nPC);  // non-decreasing
    map[size].vPC= vPC;
    map[size].nPC= nPC;
    ++size;
  }

  inline void checkPC(int vPC, insn *nPC, int index) {
    if (map[index].vPC != vPC) fatal("phase error!  vPC %d != %d", map[index].vPC, vPC);
    if (map[index].nPC != nPC) fatal("phase error!  nPC %p != %p", map[index].nPC, nPC);
  }

  // answer the vPC corresponding to the given nPC

  inline size_t n2vPC(insn *nPC) const {
    assert(size > 0);
    register int low= 0;
    register int high= size - 1;
    register int index;
    while ((index= (high + low) >> 1), (low <= high))
      if (map[index].nPC < nPC) low= index + 1; else high= index - 1;
    if ((low < size) && (map[low].nPC == nPC)) return map[low].vPC;
    fatal("nPC=%p not found in map", nPC);
    return 0;
  }

  // answer the nPC corresponding to the given vPC

  inline insn *v2nPC(int vPC) const {
    assert(size > 0);
    register int low= 0;
    register int high= size - 1;
    register int index;
    while ((index= (high + low) >> 1), (low <= high))
      if (map[index].vPC < vPC) low= index + 1; else high= index - 1;
    if ((low < size) && (map[low].vPC == vPC)) return map[low].nPC;
    fatal("vPC=%d not found in map", vPC);
    return 0;
  }

  // linear search probably faster for block entry points
  //#warning: FIX THIS
  // Should test the size of the map and punt to binary search if
  // larger than some predetermined, hand-waving, rabbit-out-of-a-hat,
  // arbitrary kind of value.  Better still, run a loop in j3
  // initialisation to determine the break-even point (measured in
  // bogoprobes, of course ;-).
  inline insn *v2nPClinear(int vPC) const
    {
      assert(size > 0);
      register int limit= size;
      register int index= 0;
      // gcov says 75% of probes hit on FIRST entry: rewriting this from the
      // original "for" gained 3% in Interpreter>>translate
      do
	if (map[index].vPC == vPC)
	  return map[index].nPC;
      while (++index != limit);
      fatal("vPC=%d not found in map", vPC);
      return 0;
    }

  inline bool includesVPC(int vPC) const {
    assert(size > 0);
    register int low= 0;
    register int high= size - 1;
    register int index;
    while ((index= (high + low) >> 1), (low <= high))
      if (map[index].vPC < vPC) low= index + 1; else high= index - 1;
    if ((low < size) && (map[low].vPC == vPC)) return true;
    return false;
  }

  inline bool includesNPC(insn *nPC) const {
    assert(size > 0);
    register int low= 0;
    register int high= size - 1;
    register int index;
    while ((index= (high + low) >> 1), (low <= high))
      if (map[index].nPC < nPC) low= index + 1; else high= index - 1;
    if ((low < size) && (map[low].nPC == nPC)) return true;
    return false;
  }

}; // class PcMap


#endif // _j_PcMap_h
