/* piw-write-barriers.h - write barriers for PIW
 *
 * author(s): Tom Lord
 ****************************************************************
 * Copyright (C) 1998 UUNET Technologies, Inc.
 *
 * See the file "COPYING.PIW" for further information
 * about the copyright status of this work.
 */

#ifndef INCLUDE__PIW__PIW_WRITE_BARRIERS_H
#define INCLUDE__PIW__PIW_WRITE_BARRIERS_H

#include "hackerlab/piw-malloc/write-barrier-breakpoints.h"
#include "hackerlab/piw-malloc/watchpt-breakpoints.h"
#include "hackerlab/piw-malloc/piw-malloc.h"
#include "hackerlab/piw-malloc/write-barriers.h"
#include "hackerlab/piw-malloc/logging.h"

#if !defined(__GNUC__)
#  if !defined(PIW_BARRIERS_NOT_INLINE)
#    define __inline__
#  endif
#  define __attribute__(X)
#  define PIW_ATTRIBUTE(X)
#endif

#if defined(PIW_NO_ATTRIBUTES)
#  define PIW_ATTRIBUTE(X)
#else
#  define PIW_ATTRIBUTE(X)	__attribute__(X)
#endif

#if !defined(PIW_EXTERN_INLINE)
#  define PIW_EXTERN_INLINE extern __inline__
#endif


/*c
 * PIW_EXTERN_INLINE void * __attribute__((no_write_barrier))
 * piw_write_checking_barrier (void * addr, int length);
 *
 * Examine the locations of an imminent write for any that
 * might be in the malloc arena, but not in an allocated region.
 * If any are found, log an error and call a noop function, returning
 * its return value.
 *
 * If no stray writes are found, return `addr'.
 */
PIW_EXTERN_INLINE void * PIW_ATTRIBUTE((no_write_barrier))
piw_write_checking_barrier (void * addr, int length)
{
  if ((addr < piw_malloc_heap_start) || (addr >= piw_malloc_heap_end) || !piw_malloc_arena_tags)
    {
      /* The write is not even in the malloc arena
       * or tag bits are not being kept.
       */
      return addr;
    }
  else
    {
      int x;	
      int n_bits;
      int first_bit;
    
      /* The write is in the malloc arena.  Will it touch any
       * region that is not marked writable?
       */
      n_bits = ((length + piw_bytes_per_tag_bit - 1)
		/ piw_bytes_per_tag_bit);

      first_bit = (((char *)addr - (char *)piw_tagged_heap_start)
		   / piw_bytes_per_tag_bit);

      /* Find the first 0 bit among the tags, if any:
       */
      x = bitset_ffc_range (piw_malloc_arena_tags, first_bit, first_bit + n_bits);

      if (x >= 0)
	{	  
	  piw_log_bad_write (addr + x * piw_bytes_per_tag_bit, length);
	  return piw_write_barrier_breakpoint (addr, length);
	}
      else
	return addr;
    }
}


PIW_EXTERN_INLINE void * PIW_ATTRIBUTE((no_write_barrier))
piw_write_logging_barrier (void * addr, int length)
{
  if ((addr < piw_malloc_heap_start) || (addr >= piw_malloc_heap_end))
    {
      /* The write is not even in the malloc arena.
       */
      return addr;
    }
  else
    {
      piw_log_wm (addr, length);
      return addr;
    }
}


PIW_EXTERN_INLINE void * PIW_ATTRIBUTE((no_write_barrier))
piw_write_watchpoint_barrier (void * addr, int length)
{
  if (   ((addr + length) >= piw_watchpt[0].start)
      && (addr < piw_watchpt[0].end))
    return piw_watchpt0_breakpoint (addr, length);
  else if (   ((addr + length) >= piw_watchpt[1].start)
	   && (addr < piw_watchpt[1].end))
    return piw_watchpt1_breakpoint (addr, length);
  else
    return addr;
}


#ifndef PIW_NO_UDWB
PIW_EXTERN_INLINE void * PIW_ATTRIBUTE((no_write_barrier)) __attribute__((unused))
__user_supplied_write_barrier (void * addr, int length)
{
  void * answer;

  answer = addr;
#ifdef PIW_LOG_WRITES
  answer = piw_write_logging_barrier (answer, length);
#endif
#ifdef PIW_CHECK_WRITES
  answer = piw_write_checking_barrier (answer, length);
#endif
#ifdef PIW_WATCHPOINTS
  answer = piw_write_watchpoint_barrier (answer, length);
#endif
  return answer;
}
#endif



/* automatically generated __STDC__ prototypes */
#endif  /* INCLUDE__PIW__PIW_WRITE_BARRIERS_H */
