view gen/classes.cpp @ 1351:8d501abecd24

Initial (but disabled) fix for ticket #294 , the actual part that fixes the bug is in a #if 0 block as I'm afraid it will cause regressions. I'm most likely not going to be around tonight, and maybe not tomorrow as well, so I'm pushing it in case someone wants to run some serious testing/investigate the problem noted in llvmhelpers.cpp : realignOffset .
author Tomas Lindquist Olsen <tomas.l.olsen gmail com>
date Thu, 14 May 2009 17:20:17 +0200
parents 15e9762bb620
children 8026319762be
line wrap: on
line source

#include "gen/llvm.h"

#include "mtype.h"
#include "aggregate.h"
#include "init.h"
#include "declaration.h"

#include "gen/irstate.h"
#include "gen/tollvm.h"
#include "gen/llvmhelpers.h"
#include "gen/arrays.h"
#include "gen/logger.h"
#include "gen/classes.h"
#include "gen/structs.h"
#include "gen/functions.h"
#include "gen/runtime.h"
#include "gen/dvalue.h"
#include "gen/nested.h"
#include "gen/utils.h"

#include "ir/irstruct.h"
#include "ir/irtypeclass.h"

//////////////////////////////////////////////////////////////////////////////////////////

// FIXME: this needs to be cleaned up

void DtoResolveClass(ClassDeclaration* cd)
{
    // make sure the base classes are processed first
    ArrayIter<BaseClass> base_iter(cd->baseclasses);
    while (base_iter.more())
    {
        BaseClass* bc = base_iter.get();
        if (bc)
        {
            bc->base->codegen(Type::sir);
        }
        base_iter.next();
    }

    if (cd->ir.resolved) return;
    cd->ir.resolved = true;

    Logger::println("DtoResolveClass(%s): %s", cd->toPrettyChars(), cd->loc.toChars());
    LOG_SCOPE;

    // make sure type exists
    DtoType(cd->type);

    // create IrStruct
    assert(cd->ir.irStruct == NULL);
    IrStruct* irstruct = new IrStruct(cd);
    cd->ir.irStruct = irstruct;

    // make sure all fields really get their ir field
    ArrayIter<VarDeclaration> it(cd->fields);
    for (; !it.done(); it.next())
    {
        VarDeclaration* vd = it.get();
        if (vd->ir.irField == NULL) {
            new IrField(vd);
        } else {
            IF_LOG Logger::println("class field already exists!!!");
        }
    }

    bool needs_def = mustDefineSymbol(cd);

    // emit the ClassZ symbol
    LLGlobalVariable* ClassZ = irstruct->getClassInfoSymbol();

    // emit the interfaceInfosZ symbol if necessary
    if (cd->vtblInterfaces && cd->vtblInterfaces->dim > 0)
        irstruct->getInterfaceArraySymbol(); // initializer is applied when it's built

    // interface only emit typeinfo and classinfo
    if (cd->isInterfaceDeclaration())
    {
        irstruct->initializeInterface();
    }
    else
    {
        // emit the initZ symbol
        LLGlobalVariable* initZ = irstruct->getInitSymbol();
        // emit the vtblZ symbol
        LLGlobalVariable* vtblZ = irstruct->getVtblSymbol();

        // perform definition
        if (needs_def)
        {
            // set symbol initializers
            initZ->setInitializer(irstruct->getDefaultInit());
            vtblZ->setInitializer(irstruct->getVtblInit());
        }
    }

    // emit members
    if (cd->members)
    {
        ArrayIter<Dsymbol> it(*cd->members);
        while (!it.done())
        {
            Dsymbol* member = it.get();
            if (member)
                member->codegen(Type::sir);
            it.next();
        }
    }

    if (needs_def)
    {
        // emit typeinfo
        DtoTypeInfoOf(cd->type);

        // define classinfo
        ClassZ->setInitializer(irstruct->getClassInfoInit());
    }
}

//////////////////////////////////////////////////////////////////////////////////////////

DValue* DtoNewClass(Loc loc, TypeClass* tc, NewExp* newexp)
{
    // resolve type
    tc->sym->codegen(Type::sir);

    // allocate
    LLValue* mem;
    if (newexp->onstack)
    {
        // FIXME align scope class to its largest member
        mem = DtoRawAlloca(DtoType(tc)->getContainedType(0), 0, ".newclass_alloca");
    }
    // custom allocator
    else if (newexp->allocator)
    {
        newexp->allocator->codegen(Type::sir);
        DFuncValue dfn(newexp->allocator, newexp->allocator->ir.irFunc->func);
        DValue* res = DtoCallFunction(newexp->loc, NULL, &dfn, newexp->newargs);
        mem = DtoBitCast(res->getRVal(), DtoType(tc), ".newclass_custom");
    }
    // default allocator
    else
    {
        llvm::Function* fn = LLVM_D_GetRuntimeFunction(gIR->module, "_d_allocclass");
        LLConstant* ci = DtoBitCast(tc->sym->ir.irStruct->getClassInfoSymbol(), DtoType(ClassDeclaration::classinfo->type));
        mem = gIR->CreateCallOrInvoke(fn, ci, ".newclass_gc_alloc").getInstruction();
        mem = DtoBitCast(mem, DtoType(tc), ".newclass_gc");
    }

    // init
    DtoInitClass(tc, mem);

    // init inner-class outer reference
    if (newexp->thisexp)
    {
        Logger::println("Resolving outer class");
        LOG_SCOPE;
        DValue* thisval = newexp->thisexp->toElem(gIR);
        size_t idx = tc->sym->vthis->ir.irField->index;
        LLValue* src = thisval->getRVal();
        LLValue* dst = DtoGEPi(mem,0,idx,"tmp");
        if (Logger::enabled())
            Logger::cout() << "dst: " << *dst << "\nsrc: " << *src << '\n';
        DtoStore(src, dst);
    }
    // set the context for nested classes
    else if (tc->sym->isNested() && tc->sym->vthis)
    {
        Logger::println("Resolving nested context");
        LOG_SCOPE;

        // get context
        LLValue* nest = DtoNestedContext(loc, tc->sym);

        // store into right location
        size_t idx = tc->sym->vthis->ir.irField->index;
        LLValue* gep = DtoGEPi(mem,0,idx,"tmp");
        DtoStore(DtoBitCast(nest, gep->getType()->getContainedType(0)), gep);
    }

    // call constructor
    if (newexp->member)
    {
        Logger::println("Calling constructor");
        assert(newexp->arguments != NULL);
        newexp->member->codegen(Type::sir);
        DFuncValue dfn(newexp->member, newexp->member->ir.irFunc->func, mem);
        return DtoCallFunction(newexp->loc, tc, &dfn, newexp->arguments);
    }

    // return default constructed class
    return new DImValue(tc, mem);
}

//////////////////////////////////////////////////////////////////////////////////////////

void DtoInitClass(TypeClass* tc, LLValue* dst)
{
    tc->sym->codegen(Type::sir);

    uint64_t n = tc->sym->structsize - PTRSIZE * 2;

    // set vtable field seperately, this might give better optimization
    LLValue* tmp = DtoGEPi(dst,0,0,"vtbl");
    LLValue* val = DtoBitCast(tc->sym->ir.irStruct->getVtblSymbol(), tmp->getType()->getContainedType(0));
    DtoStore(val, tmp);

    // monitor always defaults to zero
    tmp = DtoGEPi(dst,0,1,"monitor");
    val = llvm::Constant::getNullValue(tmp->getType()->getContainedType(0));
    DtoStore(val, tmp);

    // done?
    if (n == 0)
        return;

    // copy the rest from the static initializer
    LLValue* dstarr = DtoGEPi(dst,0,2,"tmp");

    // init symbols might not have valid types
    LLValue* initsym = tc->sym->ir.irStruct->getInitSymbol();
    initsym = DtoBitCast(initsym, DtoType(tc));
    LLValue* srcarr = DtoGEPi(initsym,0,2,"tmp");

    DtoMemCpy(dstarr, srcarr, DtoConstSize_t(n));
}

//////////////////////////////////////////////////////////////////////////////////////////

void DtoFinalizeClass(LLValue* inst)
{
    // get runtime function
    llvm::Function* fn = LLVM_D_GetRuntimeFunction(gIR->module, "_d_callfinalizer");
    // build args
    LLSmallVector<LLValue*,1> arg;
    arg.push_back(DtoBitCast(inst, fn->getFunctionType()->getParamType(0), ".tmp"));
    // call
    gIR->CreateCallOrInvoke(fn, arg.begin(), arg.end(), "");
}

//////////////////////////////////////////////////////////////////////////////////////////

DValue* DtoCastClass(DValue* val, Type* _to)
{
    Logger::println("DtoCastClass(%s, %s)", val->getType()->toChars(), _to->toChars());
    LOG_SCOPE;

    Type* to = _to->toBasetype();

    // class -> pointer
    if (to->ty == Tpointer) {
        Logger::println("to pointer");
        const LLType* tolltype = DtoType(_to);
        LLValue* rval = DtoBitCast(val->getRVal(), tolltype);
        return new DImValue(_to, rval);
    }
    // class -> bool
    else if (to->ty == Tbool) {
        Logger::println("to bool");
        LLValue* llval = val->getRVal();
        LLValue* zero = LLConstant::getNullValue(llval->getType());
        return new DImValue(_to, gIR->ir->CreateICmpNE(llval, zero, "tmp"));
    }

    // must be class/interface
    assert(to->ty == Tclass);
    TypeClass* tc = (TypeClass*)to;

    // from type
    Type* from = val->getType()->toBasetype();
    TypeClass* fc = (TypeClass*)from;

    // x -> interface
    if (InterfaceDeclaration* it = tc->sym->isInterfaceDeclaration()) {
        Logger::println("to interface");
        // interface -> interface
        if (fc->sym->isInterfaceDeclaration()) {
            Logger::println("from interface");
            return DtoDynamicCastInterface(val, _to);
        }
        // class -> interface - static cast
        else if (it->isBaseOf(fc->sym,NULL)) {
            Logger::println("static down cast");

            // get the from class
            ClassDeclaration* cd = fc->sym->isClassDeclaration();
            DtoResolveClass(cd); // add this
            IrStruct* irstruct = cd->ir.irStruct;
            IrTypeClass* typeclass = fc->irtype->isClass();

            // find interface impl
            
            size_t i_index = typeclass->getInterfaceIndex(it);
            assert(i_index != ~0 && "requesting interface that is not implemented by this class");

            // offset pointer
            LLValue* v = val->getRVal();
            LLValue* orig = v;
            v = DtoGEPi(v, 0, i_index);
            const LLType* ifType = DtoType(_to);
            if (Logger::enabled())
            {
                Logger::cout() << "V = " << *v << std::endl;
                Logger::cout() << "T = " << *ifType << std::endl;
            }
            v = DtoBitCast(v, ifType);

            // Check whether the original value was null, and return null if so.
            // Sure we could have jumped over the code above in this case, but
            // it's just a GEP and (maybe) a pointer-to-pointer BitCast, so it
            // should be pretty cheap and perfectly safe even if the original was null.
            LLValue* isNull = gIR->ir->CreateICmpEQ(orig, LLConstant::getNullValue(orig->getType()), ".nullcheck");
            v = gIR->ir->CreateSelect(isNull, LLConstant::getNullValue(ifType), v, ".interface");

            // return r-value
            return new DImValue(_to, v);
        }
        // class -> interface
        else {
            Logger::println("from object");
            return DtoDynamicCastObject(val, _to);
        }
    }
    // x -> class
    else {
        Logger::println("to class");
        int poffset;
        // interface -> class
        if (fc->sym->isInterfaceDeclaration()) {
            Logger::println("interface cast");
            return DtoDynamicCastInterface(val, _to);
        }
        // class -> class - static down cast
        else if (tc->sym->isBaseOf(fc->sym,NULL)) {
            Logger::println("static down cast");
            const LLType* tolltype = DtoType(_to);
            LLValue* rval = DtoBitCast(val->getRVal(), tolltype);
            return new DImValue(_to, rval);
        }
        // class -> class - dynamic up cast
        else {
            Logger::println("dynamic up cast");
            return DtoDynamicCastObject(val, _to);
        }
    }
}

//////////////////////////////////////////////////////////////////////////////////////////

DValue* DtoDynamicCastObject(DValue* val, Type* _to)
{
    // call:
    // Object _d_dynamic_cast(Object o, ClassInfo c)

    ClassDeclaration::object->codegen(Type::sir);
    ClassDeclaration::classinfo->codegen(Type::sir);

    llvm::Function* func = LLVM_D_GetRuntimeFunction(gIR->module, "_d_dynamic_cast");
    const llvm::FunctionType* funcTy = func->getFunctionType();

    std::vector<LLValue*> args;

    // Object o
    LLValue* obj = val->getRVal();
    obj = DtoBitCast(obj, funcTy->getParamType(0));
    assert(funcTy->getParamType(0) == obj->getType());

    // ClassInfo c
    TypeClass* to = (TypeClass*)_to->toBasetype();
    to->sym->codegen(Type::sir);

    LLValue* cinfo = to->sym->ir.irStruct->getClassInfoSymbol();
    // unfortunately this is needed as the implementation of object differs somehow from the declaration
    // this could happen in user code as well :/
    cinfo = DtoBitCast(cinfo, funcTy->getParamType(1));
    assert(funcTy->getParamType(1) == cinfo->getType());

    // call it
    LLValue* ret = gIR->CreateCallOrInvoke2(func, obj, cinfo, "tmp").getInstruction();

    // cast return value
    ret = DtoBitCast(ret, DtoType(_to));

    return new DImValue(_to, ret);
}

//////////////////////////////////////////////////////////////////////////////////////////

DValue* DtoCastInterfaceToObject(DValue* val, Type* to)
{
    // call:
    // Object _d_toObject(void* p)

    llvm::Function* func = LLVM_D_GetRuntimeFunction(gIR->module, "_d_toObject");
    const llvm::FunctionType* funcTy = func->getFunctionType();

    // void* p
    LLValue* tmp = val->getRVal();
    tmp = DtoBitCast(tmp, funcTy->getParamType(0));

    // call it
    LLValue* ret = gIR->CreateCallOrInvoke(func, tmp, "tmp").getInstruction();

    // cast return value
    if (to != NULL)
        ret = DtoBitCast(ret, DtoType(to));
    else
        to = ClassDeclaration::object->type;

    return new DImValue(to, ret);
}

//////////////////////////////////////////////////////////////////////////////////////////

DValue* DtoDynamicCastInterface(DValue* val, Type* _to)
{
    // call:
    // Object _d_interface_cast(void* p, ClassInfo c)

    ClassDeclaration::object->codegen(Type::sir);
    ClassDeclaration::classinfo->codegen(Type::sir);

    llvm::Function* func = LLVM_D_GetRuntimeFunction(gIR->module, "_d_interface_cast");
    const llvm::FunctionType* funcTy = func->getFunctionType();

    std::vector<LLValue*> args;

    // void* p
    LLValue* ptr = val->getRVal();
    ptr = DtoBitCast(ptr, funcTy->getParamType(0));

    // ClassInfo c
    TypeClass* to = (TypeClass*)_to->toBasetype();
    to->sym->codegen(Type::sir);
    LLValue* cinfo = to->sym->ir.irStruct->getClassInfoSymbol();
    // unfortunately this is needed as the implementation of object differs somehow from the declaration
    // this could happen in user code as well :/
    cinfo = DtoBitCast(cinfo, funcTy->getParamType(1));

    // call it
    LLValue* ret = gIR->CreateCallOrInvoke2(func, ptr, cinfo, "tmp").getInstruction();

    // cast return value
    ret = DtoBitCast(ret, DtoType(_to));

    return new DImValue(_to, ret);
}

//////////////////////////////////////////////////////////////////////////////////////////

LLValue* DtoIndexClass(LLValue* src, ClassDeclaration* cd, VarDeclaration* vd)
{
    Logger::println("indexing class field %s:", vd->toPrettyChars());
    LOG_SCOPE;

    if (Logger::enabled())
        Logger::cout() << "src: " << *src << '\n';

    // make sure class is resolved
    DtoResolveClass(cd);

    // vd must be a field
    IrField* field = vd->ir.irField;
    assert(field);

    // get the start pointer
    const LLType* st = DtoType(cd->type);
    // cast to the struct type
    src = DtoBitCast(src, st);

    // gep to the index
    if (Logger::enabled())
    {
        Logger::cout() << "src2: " << *src << '\n';
        Logger::cout() << "index: " << field->index << '\n';
        Logger::cout() << "srctype: " << *src->getType() << '\n';
    }
    LLValue* val = DtoGEPi(src, 0, field->index);

    // do we need to offset further? (union area)
    if (field->unionOffset)
    {
        // cast to void*
        val = DtoBitCast(val, getVoidPtrType());
        // offset
        val = DtoGEPi1(val, field->unionOffset);
    }

    // cast it to the right type
    val = DtoBitCast(val, getPtrToType(DtoType(vd->type)));

    if (Logger::enabled())
        Logger::cout() << "value: " << *val << '\n';

    return val;
}

//////////////////////////////////////////////////////////////////////////////////////////

LLValue* DtoVirtualFunctionPointer(DValue* inst, FuncDeclaration* fdecl)
{
    // sanity checks
    assert(fdecl->isVirtual());
    assert(!fdecl->isFinal());
    assert(fdecl->vtblIndex > 0); // 0 is always ClassInfo/Interface*
    assert(inst->getType()->toBasetype()->ty == Tclass);

    // get instance
    LLValue* vthis = inst->getRVal();
    if (Logger::enabled())
        Logger::cout() << "vthis: " << *vthis << '\n';

    LLValue* funcval = vthis;
    // get the vtbl for objects
    funcval = DtoGEPi(funcval, 0, 0, "tmp");
    // load vtbl ptr
    funcval = DtoLoad(funcval);
    // index vtbl
    funcval = DtoGEPi(funcval, 0, fdecl->vtblIndex, fdecl->toChars());
    // load funcptr
    funcval = DtoAlignedLoad(funcval);

    if (Logger::enabled())
        Logger::cout() << "funcval: " << *funcval << '\n';

    // cast to final funcptr type
    funcval = DtoBitCast(funcval, getPtrToType(DtoType(fdecl->type)));
    if (Logger::enabled())
        Logger::cout() << "funcval casted: " << *funcval << '\n';

    return funcval;
}

//////////////////////////////////////////////////////////////////////////////////////////

#if GENERATE_OFFTI

// build a single element for the OffsetInfo[] of ClassInfo
static LLConstant* build_offti_entry(ClassDeclaration* cd, VarDeclaration* vd)
{
    std::vector<LLConstant*> inits(2);

    // size_t offset;
    //
    assert(vd->ir.irField);
    // grab the offset from llvm and the formal class type
    size_t offset = gTargetData->getStructLayout(isaStruct(cd->type->ir.type->get()))->getElementOffset(vd->ir.irField->index);
    // offset nested struct/union fields
    offset += vd->ir.irField->unionOffset;

    // assert that it matches DMD
    Logger::println("offsets: %lu vs %u", offset, vd->offset);
    assert(offset == vd->offset);

    inits[0] = DtoConstSize_t(offset);

    // TypeInfo ti;
    inits[1] = DtoTypeInfoOf(vd->type, true);

    // done
    return llvm::ConstantStruct::get(inits);
}

static LLConstant* build_offti_array(ClassDeclaration* cd, const LLType* arrayT)
{
    IrStruct* irstruct = cd->ir.irStruct;

    size_t nvars = irstruct->varDecls.size();
    std::vector<LLConstant*> arrayInits(nvars);

    for (size_t i=0; i<nvars; i++)
    {
        arrayInits[i] = build_offti_entry(cd, irstruct->varDecls[i]);
    }

    LLConstant* size = DtoConstSize_t(nvars);
    LLConstant* ptr;

    if (nvars == 0)
        return LLConstant::getNullValue( arrayT );

    // array type
    const llvm::ArrayType* arrTy = llvm::ArrayType::get(arrayInits[0]->getType(), nvars);
    LLConstant* arrInit = llvm::ConstantArray::get(arrTy, arrayInits);

    // mangle
    std::string name(cd->type->vtinfo->toChars());
    name.append("__OffsetTypeInfos");

    // create symbol
    llvm::GlobalVariable* gvar = new llvm::GlobalVariable(arrTy,true,DtoInternalLinkage(cd),arrInit,name,gIR->module);
    ptr = DtoBitCast(gvar, getPtrToType(arrTy->getElementType()));

    return DtoConstSlice(size, ptr);
}

#endif // GENERATE_OFFTI

static LLConstant* build_class_dtor(ClassDeclaration* cd)
{
    FuncDeclaration* dtor = cd->dtor;

    // if no destructor emit a null
    if (!dtor)
        return getNullPtr(getVoidPtrType());

    dtor->codegen(Type::sir);
    return llvm::ConstantExpr::getBitCast(dtor->ir.irFunc->func, getPtrToType(LLType::Int8Ty));
}

static unsigned build_classinfo_flags(ClassDeclaration* cd)
{
    // adapted from original dmd code
    unsigned flags = 0;
    //flags |= isCOMclass(); // IUnknown
    bool hasOffTi = false;
    if (cd->ctor) flags |= 8;
    for (ClassDeclaration *cd2 = cd; cd2; cd2 = cd2->baseClass)
    {
    if (cd2->members)
    {
        for (size_t i = 0; i < cd2->members->dim; i++)
        {
        Dsymbol *sm = (Dsymbol *)cd2->members->data[i];
        if (sm->isVarDeclaration() && !sm->isVarDeclaration()->isDataseg()) // is this enough?
            hasOffTi = true;
        //printf("sm = %s %s\n", sm->kind(), sm->toChars());
        if (sm->hasPointers())
            goto L2;
        }
    }
    }
    flags |= 2;         // no pointers
L2:
    if (hasOffTi)
        flags |= 4;
    return flags;
}

LLConstant* DtoDefineClassInfo(ClassDeclaration* cd)
{
//     The layout is:
//        {
//         void **vptr;
//         monitor_t monitor;
//         byte[] initializer;     // static initialization data
//         char[] name;        // class name
//         void *[] vtbl;
//         Interface[] interfaces;
//         ClassInfo *base;        // base class
//         void *destructor;
//         void *invariant;        // class invariant
//         uint flags;
//         void *deallocator;
//         OffsetTypeInfo[] offTi;
//         void *defaultConstructor;
//        }

    Logger::println("DtoDefineClassInfo(%s)", cd->toChars());
    LOG_SCOPE;

    IrStruct* ir = cd->ir.irStruct;

    assert(cd->type->ty == Tclass);

    TypeClass* cdty = (TypeClass*)cd->type;

    // holds the list of initializers for llvm
    std::vector<LLConstant*> inits;

    ClassDeclaration* cinfo = ClassDeclaration::classinfo;
    cinfo->codegen(Type::sir);

    LLConstant* c;

    const LLType* voidPtr = getVoidPtrType();
    const LLType* voidPtrPtr = getPtrToType(voidPtr);

    // own vtable
    c = cinfo->ir.irStruct->getVtblSymbol();
    inits.push_back(c);

    // monitor
    c = LLConstant::getNullValue(voidPtr);
    inits.push_back(c);

    // byte[] init
    if (cd->isInterfaceDeclaration())
        c = DtoConstSlice(DtoConstSize_t(0), LLConstant::getNullValue(voidPtr));
    else
    {
        c = DtoBitCast(ir->getInitSymbol(), voidPtr);
        //Logger::cout() << *ir->constInit->getType() << std::endl;
        size_t initsz = getTypePaddedSize(ir->getInitSymbol()->getType()->getContainedType(0));
        c = DtoConstSlice(DtoConstSize_t(initsz), c);
    }
    inits.push_back(c);

    // class name
    // from dmd
    char *name = cd->ident->toChars();
    size_t namelen = strlen(name);
    if (!(namelen > 9 && memcmp(name, "TypeInfo_", 9) == 0))
    {
        name = cd->toPrettyChars();
        namelen = strlen(name);
    }
    c = DtoConstString(name);
    inits.push_back(c);

    // vtbl array
    if (cd->isInterfaceDeclaration())
        c = DtoConstSlice(DtoConstSize_t(0), LLConstant::getNullValue(getPtrToType(voidPtr)));
    else {
        c = DtoBitCast(ir->getVtblSymbol(), voidPtrPtr);
        c = DtoConstSlice(DtoConstSize_t(cd->vtbl.dim), c);
    }
    inits.push_back(c);

    // interfaces array
    c = ir->getClassInfoInterfaces();
    inits.push_back(c);

    // base classinfo
    // interfaces never get a base , just the interfaces[]
    if (cd->baseClass && !cd->isInterfaceDeclaration()) {
        c = cd->baseClass->ir.irStruct->getClassInfoSymbol();
        assert(c);
        inits.push_back(c);
    }
    else {
        // null
        c = LLConstant::getNullValue(DtoType(cinfo->type));
        inits.push_back(c);
    }

    // destructor
    if (cd->isInterfaceDeclaration())
        c = LLConstant::getNullValue(voidPtr);
    else
        c = build_class_dtor(cd);
    inits.push_back(c);

    // invariant
    VarDeclaration* invVar = (VarDeclaration*)cinfo->fields.data[6];
    const LLType* invTy = DtoType(invVar->type);
    if (cd->inv)
    {
        cd->inv->codegen(Type::sir);
        c = cd->inv->ir.irFunc->func;
        c = DtoBitCast(c, invTy);
    }
    else
        c = LLConstant::getNullValue(invTy);
    inits.push_back(c);

    // uint flags
    if (cd->isInterfaceDeclaration())
        c = DtoConstUint(0);
    else {
        unsigned flags = build_classinfo_flags(cd);
        c = DtoConstUint(flags);
    }
    inits.push_back(c);

    // deallocator
    if (cd->aggDelete)
    {
        cd->aggDelete->codegen(Type::sir);
        c = cd->aggDelete->ir.irFunc->func;
        c = DtoBitCast(c, voidPtr);
    }
    else
        c = LLConstant::getNullValue(voidPtr);
    inits.push_back(c);

    // offset typeinfo
    VarDeclaration* offTiVar = (VarDeclaration*)cinfo->fields.data[9];
    const LLType* offTiTy = DtoType(offTiVar->type);

#if GENERATE_OFFTI

    if (cd->isInterfaceDeclaration())
        c = LLConstant::getNullValue(offTiTy);
    else
        c = build_offti_array(cd, offTiTy);

#else // GENERATE_OFFTI

    c = LLConstant::getNullValue(offTiTy);

#endif // GENERATE_OFFTI

    inits.push_back(c);

    // default constructor
    if (cd->defaultCtor)
    {
        cd->defaultCtor->codegen(Type::sir);
        c = isaConstant(cd->defaultCtor->ir.irFunc->func);
        c = DtoBitCast(c, voidPtr);
    }
    else
        c = LLConstant::getNullValue(voidPtr);
    inits.push_back(c);

#if DMDV2

    // xgetMembers
    VarDeclaration* xgetVar = (VarDeclaration*)cinfo->fields.data[11];
    const LLType* xgetTy = DtoType(xgetVar->type);

    // FIXME: fill it out!
    inits.push_back( LLConstant::getNullValue(xgetTy) );
#endif

    /*size_t n = inits.size();
    for (size_t i=0; i<n; ++i)
    {
        Logger::cout() << "inits[" << i << "]: " << *inits[i] << '\n';
    }*/

    // build the initializer
    LLConstant* finalinit = llvm::ConstantStruct::get(inits);
    //Logger::cout() << "built the classinfo initializer:\n" << *finalinit <<'\n';
    ir->constClassInfo = finalinit;

    // sanity check
    assert(finalinit->getType() == ir->classInfo->getType()->getContainedType(0) &&
        "__ClassZ initializer does not match the ClassInfo type");
    

    // return initializer
    return finalinit;
}