/*@Internals

# The Run Time System

Ajhc is very minimalist in that it does not have a precompiled run time system,
but rather generates what is needed as part of the compilation process.
However, back ends do have specific run-time representations of data, which can
be affected by things like the choice of garbage collector. The following
describes the general layout for the C based back-ends, but compiler options
such as garbage collection method or whether we do full program analysis, will
affect which features are used and whether certain optimized layouts are
possible.

Unboxed values directly translate to values in the target language, an unboxed
Int will translate directly into an 'int' as an argument and an unboxed pointer
will be a raw pointer. Unboxed values have no special interpretation and are
_not_ followed by the garbage collector. If the target language does not
support a feature such as multiple return values, it will have to be simulated.
It would not be wrong to think of Grin code that only deals with unboxed values
to be isomorphic to C-- or C augmented with multiple return values.

Boxed values have a standard representation and can be followed. Unlike some
other implementation, being boxed does not imply the object is located on the
heap. It may be on the stack, heap, or even embedded within the smart pointer
itself. Being boxed only means that the object may be represented by a smart
pointer, which may or may not actually be a pointer in the traditional sense.

A boxed value in Ajhc is represented by a 'smart pointer' of c type sptr_t. a
smart pointer is the size of a native pointer, but can take on different roles
depending on a pair of tag bits, called the ptype.

smart pointers take on a general form as follows:

    -------------------------
    |    payload        | GL|
    -------------------------

      G - if set, then the garbage collector should not treat value as a pointer to be followed
      L - lazy, this bit being set means the value is potentially not in WHNF

A sptr_t on its own in the wild can only take on one of the following forms:

    -------------------------
    |    whnf raw value | 10|
    -------------------------

    -------------------------
    |    whnf location  | 00|
    -------------------------

WHNF stands for 'Weak Head Normal Form' and means that the value is not a
suspended function and hence not a pointer to a thunk. It may be directly
examined and need not be evaluated. wptr_t is an alias for sptr_t that is
guarenteed to be of one of the above forms. It is used to improve safety for
when we can statically know that a value is WHNF and hence we can skip the
expensive 'eval'.

The difference between the raw value and the whnf location is that the first
contains uninterpreted bits, while the second is a pointer to a location on the
heap or stack and hence the garbage collector should follow it. The format of
the memory pointed to by the whnf location is unspecified and dependent on the
actual type being represented.

Partial (unsaturated) applications are normal WHNF values. Saturated
applications which may be 'eval'ed and updated are called thunks and must not
be pointed to by WHNF pointers. Their representation follows.

    -------------------------
    |   lazy location   | 01|
    -------------------------

A lazy location points to either a thunk, or a redirection to a WHNF value. A
lazy location is always a pointer to an allocated block of memory which always
begins with a restricted smart pointer. This restricted smart pointer is represented by
the C type alias 'fptr_t'. fptr_t's only occur as the first entry in a lazy
location, they never are passed around as objects in their own right.

A fptr_t may be a whnf value or a code pointer. If a fptr_t is a whnf value (of one of
the two forms given above) then it is called a redirection, the lazy location should be
treated exactly as if it were the whnf given. This is used to redirect an evaluated
thunk to its computed value.

A fptr_t may also be a 'code pointer' in which case the lazy location is called
a thunk. A code pointer is a pointer to executable machine code that evaluates
a closure and returns a wptr_t, the returned wptr_t is then generally written
over the code pointer, turning the thunk into a redirection. It is the
responsibility of the code pointed to to perform this redirection.

    -------------------------
    |    code pointer   | 11|
    -------------------------
    |     data ...          |

When debugging, the special code pointer BLACK_HOLE is also sometimes stored in
a fptr_t to detect certain run-time errors.

Note that unlike other implementations, a fptr_t may _not_ be another lazy
location. you can not have chained redirections, a redirection is always a
redirection to a whnf value.

    sptr_t - a tagged smart pointer, may contain a whnf value or a lazy location.
    wptr_t - a tagged smart pointer that contains a whnf value (either raw or a location)
    fptr_t - a tagged smart pointer, may contain a whnf value indicating a redirection, or a code pointer indicating a thunk.

*/

#include "jhc_rts_header.h"

#if _JHC_GC == _JHC_GC_JGC
typedef wptr_t (*eval_fn)(gc_t gc,arena_t arena,node_t *node) A_STD;
#else
typedef wptr_t (*eval_fn)(node_t *node) A_STD;
#endif

// like eval but you know the target is in WHNF or is a already evaluated indirection
static inline wptr_t A_STD A_UNUSED  A_HOT
follow(sptr_t s)
{
        assert(jhc_valid_lazy(s));
        if(IS_LAZY(s)) {
                sptr_t h = (sptr_t)(GETHEAD(FROM_SPTR(s)));
                assert(!IS_LAZY(h));
                return (wptr_t)h;
        }
        return (wptr_t)s;
}

wptr_t A_STD A_UNUSED  A_HOT
#if _JHC_GC == _JHC_GC_JGC
eval(gc_t gc,arena_t arena,sptr_t s)
#else
eval(sptr_t s)
#endif
{
        assert(jhc_valid_lazy(s));
        if(IS_LAZY(s)) {
                assert(GET_PTYPE(s) == P_LAZY);
                void *ds = FROM_SPTR(s);
                sptr_t h = (sptr_t)(GETHEAD(ds));
                assert((fptr_t)h != BLACK_HOLE);
                if(IS_LAZY(h)) {
                        eval_fn fn = (eval_fn)FROM_SPTR(h);
                        assert(GET_PTYPE(h) == P_FUNC);
#if _JHC_DEBUG
                        GETHEAD(ds) = BLACK_HOLE;
#endif
                        fn = (eval_fn)SET_THUMB_BIT(fn);
#if _JHC_GC == _JHC_GC_JGC
                        wptr_t r = (*fn)(gc,arena,NODEP(ds));
#else
                        wptr_t r = (*fn)(NODEP(ds));
#endif
#if _JHC_DEBUG
                        assert(GETHEAD(ds) != BLACK_HOLE);
#endif
                        return r;
                }
                return (wptr_t)h;
        }
        assert(jhc_valid_whnf((wptr_t)s));
        return (wptr_t)s;
}

#if _JHC_STANDALONE
int
main(int argc, char *argv[])
{
        hs_init(&argc,&argv);
        if (jhc_setjmp(&jhc_uncaught))
                jhc_error("Uncaught Exception");
        else
                _amain();
        hs_exit();
        return 0;
}
#endif
