packages feed

ddc-code-0.4.2.1: sea/runtime/Runtime.h

#pragma once

// Interface to the DDC runtime.
//   This is imported by generated modules and defines the types and macros
//   that those modules uses.
//
//   Everything should also be static-inlined, so we can run programs without
//   needing to link against external code. Primops that are implemented with
//   manifest object code should be imported separately.
//   
#include <stdint.h>
#include <stdlib.h>


// -- Types -------------------------------------------------------------------
// Boolean type.
typedef int     bool_t;

// An unsigned natural number.
//   Used for object sizes and field counts.
//   Big enough to represent the number of allocatable bytes.
typedef size_t   nat_t;

// Define int_t to make things look consistent.
typedef int      int_t;

// Generic address type.
//   #ifdef because Cygwin already defines it.
#ifndef __addr_t_defined
typedef uint8_t* addr_t;
#endif

// A constructor tag.
typedef uint32_t tag_t;

// A UTF8 string.
typedef char     string_t;


// -- Object Format -----------------------------------------------------------
//
//  Object: TAG2 TAG1 TAG0 FORMAT ... 
//   byte    3    2    1     0          (in MSB order)
//
//  All heap objects start with a 32-bit word containg the tag of the object,
//  and a format field in the least-significant byte.
//
//  Format Field
//  ~~~~~~~~~~~~
//
//  bit 7  6  5  4  3  2  1  0
//      -- arg ---  -- obj ---
//      X  X  X  X  X  X  0  0  -- Forward / Broken-Heart
//      X  X  X  X  a  X  X  X  -- Anchor flag
//      0  0  0  1  a  0  0  1  -- Thunk
//      0  0  1  0  a  0  0  1  -- DataBoxed
//      0  0  1  1  a  0  0  1  -- DataRaw
//      0  1  0  0  a  0  0  1  -- DataMixed
//      0  1  0  1  a  0  0  1  -- SuspIndir
//      -- size --  a  0  1  1  -- DataRawSmall
//
//  * GC Forwarding / Broken-Heart pointers.
//    During garbage collection, after the GC copies an object to the
//    "to-space" its header in the "from-space" is overwritten with a pointer
//    to where the "to-space" version of the object is.
//
//    We can identify these pointers because their lowest 2 bits are always 00.
//    This is because objects in the heap are always 4-byte aligned.
//
//    For all other values of the format field, we ensure the lowest two bits
//    are not 00.
//
//  * Anchor flag
//    If bit 3 in the format field is set then the GC is not permitted to move
//    the object. This is useful when the object has been allocated by malloc
//    and exists outside the DDC runtime's garbage collected heap.
//
//  * Data{Boxed, Mixed, Raw, RawSmall}
//    There are four data object formats:
//     DataBoxed:    A boxed object containing pointers to more heap objects.
//     DataMixed:    Some heap pointers, and some raw data.
//     DataRaw:      Contains raw data and no pointers.
//     DataRawSmall: Contains raw data where the size is small enough to 
//                   encode directly in the format field.
//
//    The -obj- (object mode) portion of the format field can be used to
//    determine if the object is a forwarding pointer, has a fixed value for
//    its format field, or is a DataRS object.
//
//
//  Note: 64-bit architectures
//  ~~~~~~~~~~~~~~~~~~~~~~~~~~~
//  The various object formats always contain an even number of 32-bit words
//  in the header portion, before the payload. This ensures that the payload
//  is 8-byte aligned, which is needed for architecures that cannot load
//  misaligned double precision floats (Float64).


// The object types.
enum _ObjType 
{       _ObjTypeUnknown,
        _ObjTypeForward,
        _ObjTypeThunk,
        _ObjTypeDataBoxed,
        _ObjTypeDataRaw,
        _ObjTypeDataMixed,
        _ObjTypeSuspIndir,
        _ObjTypeDataRawSmall
};


// Whether the object is:
//      a forwarding pointer, has a fixed format,
//      or is a DataRawSmall object that has its payload size encoded in format
//      field as well.
enum _ObjMode
{       _ObjModeForward         = 0x00,
        _ObjModeFixed           = 0x01,
        _ObjModeDataRawSmall    = 0x03
};

// Use this mask to select the object mode portion of the format field.
#define _MaskObjMode            0x03


// If the object has a fixed format field (ie, has _ObjModeFixed)
//      then we can determine the format of the rest of the object by masking
//      the format field with the following mask and testing against this enum.
enum _ObjFixed
{       _ObjFixedThunk          = 0x11,
        _ObjFixedDataBoxed      = 0x21,
        _ObjFixedDataRaw        = 0x31,
        _ObjFixedDataMixed      = 0x41,
        _ObjFixedSuspIndir      = 0x51,
        _ObjFixedMapped         = 0x71
};

#define _MaskObjFixed           0xf7


// Optional flags in the format field.
enum _ObjFlag
{        _ObjFlagAnchored        = 0x08
};

#define _MaskObjAnchored        (~_ObjFlagAnchored)


// -- Object Structures -------------------------------------------------------
// Object
// A General Object.
//   All objects contain the tag and format field as the first 32-bit word.
//   The following is a supertype of the others.
typedef struct 
{        uint32_t  tagFormat;
} Obj;


// A Boxed Data Object.
//   The payload contains pointers to other heap objects.
typedef struct 
{       uint32_t  tagFormat;    // Constructor tag and format field.
        uint32_t  arity;        // Arity of the data constructor.
                                //  (The number of pointers in the payload)
        Obj*      payload[];    
} DataBoxed;


// A Mixed Data Object.
//   The payload contains some pointers followed by raw data.
typedef struct 
{       uint32_t  tagFormat;
        uint32_t  padding;      // Padding to ensure payload is 8 byte aligned.
        uint32_t  size;         // Size of the whole object, in bytes.
        uint32_t  ptrCount;     // Number of pointers at the start of the payload.
        Obj*      payload[];    // Contains ptrCount pointers, then raw data.
} DataMixed;


// ----------------------------------------------------------------------------
// A Raw Data Object.
//   A raw data object does not contain heap pointers that need to be traced
//   by the garbage collector.
typedef struct 
{       uint32_t  tagFormat;    // Constructor tag and format field.
        uint32_t  size;         // Size of the whole object, in bytes.
        uint8_t   payload[];    // Raw data that does not contain heap pointers.
} DataRaw;

static inline uint8_t* _payloadRaw(Obj* obj)
{
        return ((uint8_t*)obj) + 8;
}


// A Small Raw object.
//   The object size is encoded as part of format field.
//    This saves us from needing to include a separate arity field.
typedef struct 
{       uint32_t  tagFormat;    // Constructor tag and format field.
        uint8_t   payload[];    // Raw data that does not contain heap pointers.
} DataRawSmall;


// -- Object Utils ------------------------------------------------------------
// Get the constructor tag of an object.
static inline 
uint32_t _tag (Obj* obj)
{       return obj ->tagFormat >> 8;
}       

// Get the format field of an object.
static inline 
uint8_t  _format (Obj* obj)
{       return (uint8_t)(obj ->tagFormat & 0x0f);
}