// Minimal libcxxabi shim for the W65816 backend.
//
// Provides just enough of the Itanium C++ ABI to make `dynamic_cast`
// link and run for the inheritance shapes the smoke covers (single,
// multi, virtual-base diamond).  Does NOT provide exception support
// (`__cxa_throw`, `_Unwind_*`, personality routine) — that needs a
// stack unwinder driven by DWARF .eh_frame, which we don't carry on
// this target.  Compile your C++ with `clang++ -fno-exceptions` (RTTI
// can stay on).
//
// Layout used by clang's RTTI emit on a 16-bit target:
//   __class_type_info    : { vptr,  name }                   (4 bytes)
//   __si_class_type_info : { vptr,  name, base_typeinfo }    (6 bytes)
//   __vmi_class_type_info: { vptr,  name, flags, count,
//                            { base_typeinfo, offset_flags } * count }
//                            with base entry = 2 + 4 = 6 bytes
//
// Vtable layout:
//   { offset_to_top:i16, typeinfo_ptr:i16, vfunc[0..]:i16 ... }
// The vptr stored in an object points at vfunc[0] (i.e. base+4).  So:
//   typeinfo = *(TypeInfo **)(vptr - 2)
//   offset_to_top = *(int16_t *)(vptr - 4)
//
// offset_flags in vmi base entries (Itanium ABI):
//   bit 0 (0x01) = virtual base
//   bit 1 (0x02) = public base
//   bits >> 8    = signed offset (bytes for non-virtual; vtable offset
//                  for virtual — read i16 at *vptr + that offset)

#include <stdint.h>
#include <stddef.h>

extern void free(void *);

typedef struct TypeInfo {
    const void *vptr;
    const char *name;
} TypeInfo;

typedef struct SiTypeInfo {
    TypeInfo        base;
    const TypeInfo *baseType;
} SiTypeInfo;

typedef struct VmiBase {
    const TypeInfo *baseType;
    int32_t         offsetFlags;
} VmiBase;

typedef struct VmiTypeInfo {
    TypeInfo        base;
    uint16_t        flags;
    uint16_t        baseCount;
    VmiBase         bases[1];          // baseCount entries
} VmiTypeInfo;

// Forward decls.
static void *findBaseInObject(void *obj, const TypeInfo *cur, const TypeInfo *target);

// The three typeinfo class vtables.  Their *addresses* are what
// __dynamic_cast compares against to discriminate the three layouts —
// their contents are never executed.  Six bytes each (offset_to_top
// + typeinfo + at least one slot so &sym+4 remains in-bounds).
const void *abiClassTypeInfoVtable[3] __asm__("_ZTVN10__cxxabiv117__class_type_infoE")     = { 0, 0, 0 };
const void *abiSiClassTypeInfoVtable[3] __asm__("_ZTVN10__cxxabiv120__si_class_type_infoE") = { 0, 0, 0 };
const void *abiVmiClassTypeInfoVtable[3] __asm__("_ZTVN10__cxxabiv121__vmi_class_type_infoE") = { 0, 0, 0 };

// Itanium ABI: void *__dynamic_cast(const void *src,
//                                    const TypeInfo *staticSrcType,
//                                    const TypeInfo *dstType,
//                                    ptrdiff_t src2dstHint);
//
// staticSrcType is unused in our search (we walk from the most-derived
// type instead); src2dstHint could short-circuit some cases but we
// ignore it for simplicity.
void *abiDynamicCast(const void *src,
                     const TypeInfo *staticSrcType,
                     const TypeInfo *dstType,
                     int32_t src2dstHint) __asm__("__dynamic_cast");
void *abiDynamicCast(const void *src,
                     const TypeInfo *staticSrcType,
                     const TypeInfo *dstType,
                     int32_t src2dstHint) {
    (void)staticSrcType;
    (void)src2dstHint;
    if (!src) {
        return 0;
    }
    // Itanium ABI: vptr points to the first virtual function slot.
    // The two entries IMMEDIATELY BEFORE the vptr are (in order):
    //   [-2 ptrs] offset-to-top (signed integer-sized)
    //   [-1 ptr ] RTTI (TypeInfo *)
    // Under ptr16 a pointer is 2 bytes → RTTI at vptr-2, offset at -4.
    // Under ptr32 a pointer is 4 bytes → RTTI at vptr-4, offset at -8.
    // (offset-to-top is still a 16-bit signed int regardless — only the
    // SLOT it occupies grows with pointer size.)
    const int PTR_SZ = (int)sizeof(void *);
    const void *vptr = *(const void * const *)src;
    const TypeInfo *mostDerivedType =
        *(const TypeInfo * const *)((const char *)vptr - PTR_SZ);
    int16_t offsetToTop =
        *(const int16_t *)((const char *)vptr - 2 * PTR_SZ);
    void *mostDerived = (char *)src + offsetToTop;
    return findBaseInObject(mostDerived, mostDerivedType, dstType);
}

static void *findBaseInObject(void *obj, const TypeInfo *cur, const TypeInfo *target) {
    if (cur == target) {
        return obj;
    }
    const void *kindVtable = cur->vptr;
    if (kindVtable == &abiSiClassTypeInfoVtable[2]) {
        const SiTypeInfo *si = (const SiTypeInfo *)cur;
        return findBaseInObject(obj, si->baseType, target);
    }
    if (kindVtable == &abiVmiClassTypeInfoVtable[2]) {
        const VmiTypeInfo *vmi = (const VmiTypeInfo *)cur;
        for (uint16_t i = 0; i < vmi->baseCount; i++) {
            const VmiBase *b = &vmi->bases[i];
            int32_t off = (int32_t)b->offsetFlags >> 8;
            void *baseObj;
            if ((b->offsetFlags & 0x01) != 0) {
                // Virtual base: offset lives in the object's vtable.
                const void *vp = *(const void * const *)obj;
                int16_t vbaseOff = *(const int16_t *)((const char *)vp + (int16_t)off);
                baseObj = (char *)obj + vbaseOff;
            } else {
                baseObj = (char *)obj + (int16_t)off;
            }
            void *r = findBaseInObject(baseObj, b->baseType, target);
            if (r) {
                return r;
            }
        }
    }
    return 0;
}

// operator delete(void *, unsigned int) — sized-delete form clang
// emits for virtual destructors on a 16-bit target.  Route to free.
void abiOperatorDelete(void *p, unsigned int sz) __asm__("_ZdlPvj");
void abiOperatorDelete(void *p, unsigned int sz) {
    (void)sz;
    free(p);
}

// operator delete(void *, unsigned long) — same as above but with the
// long-typed size hint that clang emits under p:32:16 (size_t = unsigned
// long).  Same implementation, different mangled name (m = unsigned long).
void abiOperatorDeleteLong(void *p, unsigned long sz) __asm__("_ZdlPvm");
void abiOperatorDeleteLong(void *p, unsigned long sz) {
    (void)sz;
    free(p);
}

// Plain operator delete(void *) — for non-virtual delete sites.
void abiOperatorDeletePv(void *p) __asm__("_ZdlPv");
void abiOperatorDeletePv(void *p) {
    free(p);
}

// __cxa_pure_virtual — called if a pure virtual is somehow invoked
// (typically a bug).  Spin so it's catchable in a debugger.
void abiPureVirtual(void) __asm__("__cxa_pure_virtual");
void abiPureVirtual(void) {
    while (1) {
    }
}

// Fundamental typeinfo objects.  The Itanium ABI lists a fixed roster
// of typeinfo objects for built-in types; libcxxabi normally provides
// them.  We supply the ones likely to be thrown.  Each is a TypeInfo
// (vptr + name) where the vptr is the fundamental-type-info vtable
// address.  Since our personality compares typeinfo pointers directly
// (no hierarchy walk for fundamental types), the contents only need
// to be stable addresses.
const void *abiFundTypeInfoVtable[3] __asm__("_ZTVN10__cxxabiv123__fundamental_type_infoE") = { 0, 0, 0 };

// Helper macro to declare one fundamental typeinfo + its name string.
#define FUND_TI(MANGLED, NAMECHAR, NAMESTR)                                    \
    static const char abiTSname_##NAMECHAR[] = NAMESTR;                        \
    const TypeInfo MANGLED __asm__(#MANGLED) = {                               \
        &abiFundTypeInfoVtable[2], abiTSname_##NAMECHAR                        \
    }

// Itanium mangling for built-in types: i=int, j=unsigned int, c=char,
// h=unsigned char, s=short, t=unsigned short, l=long, m=unsigned long,
// x=long long, y=unsigned long long, b=bool, v=void, Pv=void*.
FUND_TI(_ZTIi, i, "i");
FUND_TI(_ZTIj, j, "j");
FUND_TI(_ZTIc, c, "c");
FUND_TI(_ZTIh, h, "h");
FUND_TI(_ZTIs, s, "s");
FUND_TI(_ZTIt, t, "t");
FUND_TI(_ZTIl, l, "l");
FUND_TI(_ZTIm, m, "m");
FUND_TI(_ZTIb, b, "b");
FUND_TI(_ZTIv, v, "v");

// Pointer-to-void typeinfo.  Used when throwing void* (or any pointer
// caught as void*).  Itanium classifies it as __pointer_type_info.
const void *abiPtrTypeInfoVtable[3] __asm__("_ZTVN10__cxxabiv119__pointer_type_infoE") = { 0, 0, 0 };
static const char abiTSname_Pv[] = "Pv";
const TypeInfo _ZTIPv __asm__("_ZTIPv") = { &abiPtrTypeInfoVtable[2], abiTSname_Pv };


// ---- operator new / operator new[] ---------------------------------------
//
// On this target size_t is `unsigned long`, so clang emits `_Znwm` /
// `_Znam` (the `m`-suffix manglings).  We provide both `m` and `j`
// variants defensively so changing the size_t typedef won't break linkage.
//
// Failure model: malloc() returns NULL on OOM.  Standard says
// `operator new` should throw `std::bad_alloc`, but that drags the
// entire exception runtime + a typeinfo for bad_alloc into every C++
// program that just allocates.  Embedded convention is to halt instead.
// A spin loop here is deterministic + dependency-free; programs that
// want graceful failure should use the nothrow form `new (std::nothrow) T`
// (which clang inlines and doesn't reach these symbols).

extern void *malloc(unsigned long);
extern void free(void *);


static void *abiNewImpl(unsigned long sz) {
    void *p = malloc(sz);
    if (!p) {
        for (;;) {
            // halt-on-OOM
        }
    }
    return p;
}


void *abiOperatorNew(unsigned long sz) __asm__("_Znwm");
void *abiOperatorNew(unsigned long sz) {
    return abiNewImpl(sz);
}


void *abiOperatorNewJ(unsigned int sz) __asm__("_Znwj");
void *abiOperatorNewJ(unsigned int sz) {
    return abiNewImpl((unsigned long)sz);
}


// `new T[N]` — the compiler may prepend an array-size cookie itself;
// the size we receive already accounts for it, so we just allocate.
void *abiOperatorNewArr(unsigned long sz) __asm__("_Znam");
void *abiOperatorNewArr(unsigned long sz) {
    return abiNewImpl(sz);
}


void *abiOperatorNewArrJ(unsigned int sz) __asm__("_Znaj");
void *abiOperatorNewArrJ(unsigned int sz) {
    return abiNewImpl((unsigned long)sz);
}


// ---- operator delete[] (unsized + sized) ---------------------------------
// Companion to the existing operator delete (sized + unsized) above.

void abiOperatorDeleteArr(void *p) __asm__("_ZdaPv");
void abiOperatorDeleteArr(void *p) {
    if (p) {
        free(p);
    }
}


void abiOperatorDeleteArrLong(void *p, unsigned long sz) __asm__("_ZdaPvm");
void abiOperatorDeleteArrLong(void *p, unsigned long sz) {
    (void)sz;
    if (p) {
        free(p);
    }
}


void abiOperatorDeleteArrInt(void *p, unsigned int sz) __asm__("_ZdaPvj");
void abiOperatorDeleteArrInt(void *p, unsigned int sz) {
    (void)sz;
    if (p) {
        free(p);
    }
}


// `__dso_handle` is a per-shared-library cookie used by Itanium ABI to
// scope atexit registrations to a specific DSO (so unloading a library
// runs only its dtors).  We don't have shared libraries — a single
// static executable — but clang emits `__cxa_atexit(fn, arg, &__dso_handle)`
// for every global with a non-trivial dtor, and the linker would fail
// without the symbol.  Any unique address works; the value is never
// dereferenced.  Place in BSS so it costs only one storage slot.
void *__dso_handle;


// ---- __cxa_atexit --------------------------------------------------------
//
// Itanium ABI: `int __cxa_atexit(void (*fn)(void*), void *arg, void *dso)`.
// Programs with any non-trivial global dtor reach this at program startup
// to register the dtor.  The registrations are walked in LIFO order by
// `__run_cxa_atexit()` — each crt0 variant calls it after `main()`
// returns and before halt/QUIT, so static-storage-duration objects with
// non-trivial destructors actually run their dtors.

#define ABI_ATEXIT_CAP 32
typedef struct {
    void (*fn)(void *);
    void  *arg;
} AbiAtexitEntry;
static AbiAtexitEntry abiAtexitTab[ABI_ATEXIT_CAP];
static unsigned int   abiAtexitCount = 0;


int abiCxaAtexit(void (*fn)(void *), void *arg, void *dso) __asm__("__cxa_atexit");
int abiCxaAtexit(void (*fn)(void *), void *arg, void *dso) {
    (void)dso;
    if (abiAtexitCount >= ABI_ATEXIT_CAP) {
        return -1;
    }
    abiAtexitTab[abiAtexitCount].fn  = fn;
    abiAtexitTab[abiAtexitCount].arg = arg;
    abiAtexitCount++;
    return 0;
}


// Walk the registered dtors in LIFO order.  Itanium ABI says exit-time
// dtors run in reverse order of registration, which is also reverse
// order of construction for typical programs (init_array walks first
// to last; ctors register dtors as they complete; LIFO unwind matches).
//
// Re-entrancy: if a dtor itself registers another atexit entry, the
// new entry must run before the remaining older ones.  We loop on
// abiAtexitCount and drop the count BEFORE calling, so a dtor's
// __cxa_atexit() lands at the slot we just freed and the outer loop
// picks it up on the next iteration.
// `retain` + `used` is the LTO survival policy (Phase 1.11): the only
// callers of __run_cxa_atexit live in crt0*.s (asm `jsl __run_cxa_atexit`
// after main() returns).  LTO's IR view doesn't see those references —
// so without retain/used the body looks dead and LTO can strip it,
// leaving crt0 to JSL into the weak-no-op fallback in libgcc.s and
// global C++ dtors never run.  No-op in non-LTO builds.
void abiRunCxaAtexit(void) __asm__("__run_cxa_atexit") __attribute__((retain, used));
void abiRunCxaAtexit(void) {
    while (abiAtexitCount > 0) {
        abiAtexitCount--;
        AbiAtexitEntry e = abiAtexitTab[abiAtexitCount];
        e.fn(e.arg);
    }
}


// ---- __cxa_guard_acquire / release / abort -------------------------------
//
// Itanium ABI: a 64-bit guard variable per static local with a non-trivial
// constructor.  Byte 0 is the "init complete" flag; byte 1 is the
// "in-progress" flag (used by the threaded variant for waiter handling).
// Single-threaded: check byte 0, mark byte 1 while running the ctor,
// flip byte 0 on success or rollback byte 1 on early-exit.
//
// (The full 64-bit guard storage exists in user code as a __guard_var
// symbol; we only touch the first two bytes here.)

int abiCxaGuardAcquire(unsigned char *g) __asm__("__cxa_guard_acquire");
int abiCxaGuardAcquire(unsigned char *g) {
    if (g[0]) {
        return 0;
    }
    g[1] = 1;
    return 1;
}


void abiCxaGuardRelease(unsigned char *g) __asm__("__cxa_guard_release");
void abiCxaGuardRelease(unsigned char *g) {
    g[0] = 1;
    g[1] = 0;
}


void abiCxaGuardAbort(unsigned char *g) __asm__("__cxa_guard_abort");
void abiCxaGuardAbort(unsigned char *g) {
    g[1] = 0;
}
