//these functions are gcc internals that are used liberally in the kernel
extern int __builtin_constant_p(int a);
extern int __builtin_expect(long a, long b);

  
//These are the real readb/writeb replacements. Maybe re-macrofy them, tho inline is the same
static inline void CYC_UTIL_W8(u8 val8, unsigned char ?@nozeroterm fat_ioaddr, int reg) {
  void *ioaddr = fat_ioaddr.base;
  if(ioaddr + reg >= (void*)fat_ioaddr.last_plus_one)
    _throw_arraybounds();
  writeb ((val8), ioaddr + (reg));
}

static inline void CYC_UTIL_W16(u16 val16, unsigned char ?@nozeroterm fat_ioaddr, int reg) {
  void *ioaddr = fat_ioaddr.base;
  if(ioaddr + reg + 1 >= (void*)fat_ioaddr.last_plus_one)
    _throw_arraybounds();
  writew ((val16), ioaddr + (reg));
}

static inline void CYC_UTIL_W32(u32 val32, unsigned char ?@nozeroterm fat_ioaddr, int reg) {
  void *ioaddr = fat_ioaddr.base;
  if(ioaddr + reg + 3 >= (void*)fat_ioaddr.last_plus_one)
    _throw_arraybounds();
  writel ((val32), ioaddr + (reg));
}
  
static inline u8 CYC_UTIL_R8(unsigned char ?@nozeroterm fat_ioaddr, int reg) {
  void *ioaddr = fat_ioaddr.base;
  if(ioaddr + reg >= (void*)fat_ioaddr.last_plus_one)
    _throw_arraybounds();
  return readb (ioaddr + (reg));
}

static inline u16 CYC_UTIL_R16(unsigned char ?@nozeroterm fat_ioaddr, int reg) {
  void *ioaddr = fat_ioaddr.base;
  if(ioaddr + reg + 1 >= (void*)fat_ioaddr.last_plus_one)
    _throw_arraybounds();
  return readw (ioaddr + (reg));
}
  
static inline u32 CYC_UTIL_R32(unsigned char ?@nozeroterm fat_ioaddr, int reg) {
  void *ioaddr = fat_ioaddr.base;
  if(ioaddr + reg + 3 >= (void*)fat_ioaddr.last_plus_one)
    _throw_arraybounds();
  return     ((unsigned long) readl (ioaddr + (reg)));
}


static inline void THIN_CYC_UTIL_W8(u8 val8, u8 *@nozeroterm ioaddr, int reg) {
  writeb ((val8), (void*)ioaddr + (reg));
}

static inline void THIN_CYC_UTIL_W16(u16 val16, u8 *@nozeroterm ioaddr, int reg) {
  writew ((val16), (void*)ioaddr + (reg));
}

static inline void THIN_CYC_UTIL_W32(u32 val32, u8 *@nozeroterm ioaddr, int reg) {
  writel ((val32), (void*)ioaddr + (reg));
}
  
static inline u8 THIN_CYC_UTIL_R8(u8 *@nozeroterm ioaddr, int reg) {
  return readb ((void*)ioaddr + (reg));
}

static inline u16 THIN_CYC_UTIL_R16(u8 *@nozeroterm ioaddr, int reg) {
  return readw ((void*)ioaddr + (reg));
}
  
static inline u32 THIN_CYC_UTIL_R32(u8 *@nozeroterm ioaddr, int reg) {
  return     ((unsigned long) readl ((void*)ioaddr + (reg)));
}

//same as above, but use inb, outb instread
static inline void CYC_UTIL_OUT8(u8 val8, unsigned char ?@nozeroterm fat_ioaddr, int reg) {
  void *ioaddr = fat_ioaddr.base;
  if(ioaddr + reg >= (void*)fat_ioaddr.last_plus_one)
    _throw_arraybounds();
  outb ((val8), (u32)ioaddr + (reg));
}

static inline void CYC_UTIL_OUT16(u16 val16, unsigned char ?@nozeroterm fat_ioaddr, int reg) {
  void *ioaddr = fat_ioaddr.base;
  if(ioaddr + reg + 1 >= (void*)fat_ioaddr.last_plus_one)
    _throw_arraybounds();
  outw ((val16), (u32)ioaddr + (reg));
}

static inline void CYC_UTIL_OUT32(u32 val32, unsigned char ?@nozeroterm fat_ioaddr, int reg) {
  void *ioaddr = fat_ioaddr.base;
  if(ioaddr + reg + 3 >= (void*)fat_ioaddr.last_plus_one)
    _throw_arraybounds();
  outl ((val32), (u32)ioaddr + (reg));
}
  
static inline u8 CYC_UTIL_IN8(unsigned char ?@nozeroterm fat_ioaddr, int reg) {
  void *ioaddr = fat_ioaddr.base;
  if(ioaddr + reg >= (void*)fat_ioaddr.last_plus_one)
    _throw_arraybounds();
  return inb ((u32)ioaddr + (reg));
}

static inline u16 CYC_UTIL_IN16(unsigned char ?@nozeroterm fat_ioaddr, int reg) {
  void *ioaddr = fat_ioaddr.base;
  if(ioaddr + reg + 1 >= (void*)fat_ioaddr.last_plus_one)
    _throw_arraybounds();
  return inw ((u32)ioaddr + (reg));
}
  
static inline u32 CYC_UTIL_IN32(unsigned char ?@nozeroterm fat_ioaddr, int reg) {
  void *ioaddr = fat_ioaddr.base;
  if(ioaddr + reg + 3 >= (void*)fat_ioaddr.last_plus_one)
    _throw_arraybounds();
  return     ((unsigned long) inl ((u32)ioaddr + (reg)));
}



static inline void THIN_CYC_UTIL_OUT8(u8 val8, u8 *@nozeroterm ioaddr, int reg) {
  outb ((val8), (u32)ioaddr + (reg));
}

static inline void THIN_CYC_UTIL_OUT16(u16 val16, u8 *@nozeroterm ioaddr, int reg) {
  outw ((val16), (u32)ioaddr + (reg));
}

static inline void THIN_CYC_UTIL_OUT32(u32 val32, u8 *@nozeroterm ioaddr, int reg) {
  outl ((val32), (u32)ioaddr + (reg));
}
  
static inline u8 THIN_CYC_UTIL_IN8(u8 *@nozeroterm ioaddr, int reg) {
  return inb ((u32)ioaddr + (reg));
}

static inline u16 THIN_CYC_UTIL_IN16(u8 *@nozeroterm ioaddr, int reg) {
  return inw ((u32)ioaddr + (reg));
}
  
static inline u32 THIN_CYC_UTIL_IN32(u8 *@nozeroterm ioaddr, int reg) {
  return     ((unsigned long) inl ((u32)ioaddr + (reg)));
}

//Add requires
static inline u8 _THIN_CYC_UTIL_IN8(u8 *@nozeroterm ioaddr, int reg)
{
  return inb ((u32)ioaddr + (reg));
}


static inline u16 _THIN_CYC_UTIL_IN16(u8 *@nozeroterm ioaddr, int reg)
{
  return inw ((u32)ioaddr + (reg));
}
  
static inline u32 _THIN_CYC_UTIL_IN32(u8 *@nozeroterm ioaddr, int reg)
{
  return ((unsigned long) inl ((u32)ioaddr + (reg)));
}


static inline void _THIN_CYC_UTIL_OUT8(u8 val8, u8 *@nozeroterm ioaddr, int reg) {
  outb ((val8), (u32)ioaddr + (reg));
}

static inline void _THIN_CYC_UTIL_OUT16(u16 val16, u8 *@nozeroterm ioaddr, int reg) {
  outw ((val16), (u32)ioaddr + (reg));
}

static inline void _THIN_CYC_UTIL_OUT32(u32 val32, u8 *@nozeroterm ioaddr, int reg) {
  outl ((val32), (u32)ioaddr + (reg));
}

static inline void* CYC_DO_ARITHMETIC(void *p, u32 size, u32 num) {
  return p + (size * num);
}

static inline void* CYC_DO_CAST(void *p, u32 size) {
  return p;
}
  
//difficulty with pointer arithmetic on dependent type -- &iobuf[i], does not work
//See cyclone-internal discussion on tag_t dependent type 
//these are safe escapes for now
//--  see the requires clauses in the overrides section below
inline u8* add_for_deptype(u8* x, int i) {
  return x+i;
}
  
inline u16* read_u16_from_u8(u8 *x, int offset, int bound) {
  if(offset + 1 >= bound)
    _throw_arraybounds();
  return *((u16*)(x+offset));
}
  
inline u32* read_u32_from_u8(u8 *x, int offset, int bound) {
  if(offset + 3 >= bound)
    _throw_arraybounds();
  return *((u32*)(x+offset));
}
  
inline void write_u16_to_u8(u8 *ioaddr, int offset, int bound, u16 val16) {
  if(offset+1 >= bound)
    _throw_arraybounds();
  writew ((val16), ioaddr + offset);
}

//lazy -- sizeof(void) == sizeof(u8)
static inline void* cycescape_u8_to_void(u8 *a) {
  return a;
}
static char *cycescape_coerce_charstar(const char *a) {
  return a;
}

static char *cycescape_coerce_charstar_noz(char *a) {
  return a;
}

static inline void* malloc_init(unsigned int size) {
  void *a = _cycalloc(size);
  if(a != 0)
    memset(a, 0, size);
  return a;
}

static inline void* umalloc_init(unsigned int size) {
  void *a = _region_malloc(Cyc_Core_unique_region,size);
  if(a != 0)
    memset(a, 0, size);
  return a;
}

static inline void* rmalloc_init(struct _RegionHandle *reg, unsigned int size) {
  void *a = _region_malloc(reg, size);
  if(a != 0)
    memset(a, 0, size);
  return a;
}

extern struct _RegionHandle *Cyc_Core_refcnt_region;

static inline void* rcmalloc_init(unsigned int size) {
  void *a = _region_malloc(Cyc_Core_refcnt_region, size);
  if(a != 0)
    memset(a, 0, size);
  return a;
}

static void *safe_arith(void *base, unsigned int eltsize, unsigned int off) {
  return base + (eltsize*off);
}



//#define ASSERT_BOUND(tvar, t, fat, thin, bnd)  \


#define REGISTER_THREAD_DBG(fn)                                                \
   tls_record_t tlr={0,0,0,0};                                             \
   let tls=_tls_slot{0,0,&tlr};                                            \
   cyc_runtime_register_thread_dbg(&tls, # fn)

#define UNREGISTER_THREAD_DBG(fn)                                          \
   cyc_runtime_unregister_thread_dbg(# fn)


#define REGISTER_THREAD                                                    \
   tls_record_t tlr={0,0,0,0};                                             \
   let tls=_tls_slot{0,0,&tlr};                                            \
   cyc_runtime_register_thread(&tls)

#define UNREGISTER_THREAD                                          \
   cyc_runtime_unregister_thread()

