view gen/classes.cpp @ 109:5ab8e92611f9 trunk

[svn r113] Added initial support for associative arrays (AAs). Fixed some problems with the string runtime support functions. Fixed initialization of array of structs. Fixed slice assignment where LHS is slice but RHS is dynamic array. Fixed problems with result of assignment expressions. Fixed foreach problems with key type mismatches.
author lindquist
date Wed, 21 Nov 2007 04:13:15 +0100
parents 5b5194b25f33
children e8da7856a260
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/arrays.h"
#include "gen/logger.h"
#include "gen/classes.h"
#include "gen/functions.h"

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

static void LLVM_AddBaseClassData(BaseClasses* bcs)
{
    // add base class data members first
    for (int j=0; j<bcs->dim; j++)
    {
        BaseClass* bc = (BaseClass*)(bcs->data[j]);
        assert(bc);
        Logger::println("Adding base class members of %s", bc->base->toChars());
        LOG_SCOPE;

        LLVM_AddBaseClassData(&bc->base->baseclasses);
        for (int k=0; k < bc->base->members->dim; k++) {
            Dsymbol* dsym = (Dsymbol*)(bc->base->members->data[k]);
            if (dsym->isVarDeclaration())
            {
                dsym->toObjFile();
            }
        }
    }
}

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

void DtoResolveClass(ClassDeclaration* cd)
{
    if (cd->llvmResolved) return;
    cd->llvmResolved = true;

    // first resolve the base class
    if (cd->baseClass) {
        DtoResolveClass(cd->baseClass);
    }
    // resolve typeinfo
    //DtoResolveClass(ClassDeclaration::typeinfo);
    // resolve classinfo
    //DtoResolveClass(ClassDeclaration::classinfo);

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

    assert(cd->type->ty == Tclass);
    TypeClass* ts = (TypeClass*)cd->type;

    assert(!cd->llvmIRStruct);
    IRStruct* irstruct = new IRStruct(ts);
    cd->llvmIRStruct = irstruct;

    gIR->structs.push_back(irstruct);
    gIR->classes.push_back(cd);

    // add vtable
    ts->llvmVtblType = new llvm::PATypeHolder(llvm::OpaqueType::get());
    const llvm::Type* vtabty = llvm::PointerType::get(ts->llvmVtblType->get());

    std::vector<const llvm::Type*> fieldtypes;
    fieldtypes.push_back(vtabty);

    // base classes first
    LLVM_AddBaseClassData(&cd->baseclasses);

    // then add own members
    for (int k=0; k < cd->members->dim; k++) {
        Dsymbol* dsym = (Dsymbol*)(cd->members->data[k]);
        dsym->toObjFile();
    }

    // add field types
    for (IRStruct::OffsetMap::iterator i=irstruct->offsets.begin(); i!=irstruct->offsets.end(); ++i) {
        fieldtypes.push_back(i->second.type);
    }

    const llvm::StructType* structtype = llvm::StructType::get(fieldtypes);
    // refine abstract types for stuff like: class C {C next;}
    assert(irstruct->recty != 0);

    llvm::PATypeHolder& spa = irstruct->recty;
    llvm::cast<llvm::OpaqueType>(spa.get())->refineAbstractTypeTo(structtype);
    structtype = isaStruct(spa.get());

    if (!ts->llvmType)
        ts->llvmType = new llvm::PATypeHolder(structtype);
    else
        *ts->llvmType = structtype;

    bool needs_definition = false;
    if (cd->parent->isModule()) {
        gIR->module->addTypeName(cd->mangle(), ts->llvmType->get());
        needs_definition = (cd->getModule() == gIR->dmodule);
    }
    else {
        assert(0 && "class parent is not a module");
    }

    // generate vtable
    llvm::GlobalVariable* svtblVar = 0;
    std::vector<const llvm::Type*> sinits_ty;

    for (int k=0; k < cd->vtbl.dim; k++)
    {
        Dsymbol* dsym = (Dsymbol*)cd->vtbl.data[k];
        assert(dsym);
        //Logger::cout() << "vtblsym: " << dsym->toChars() << '\n';

        if (FuncDeclaration* fd = dsym->isFuncDeclaration()) {
            DtoResolveFunction(fd);
            assert(fd->type->ty == Tfunction);
            TypeFunction* tf = (TypeFunction*)fd->type;
            const llvm::Type* fpty = llvm::PointerType::get(tf->llvmType->get());
            sinits_ty.push_back(fpty);
        }
        else if (ClassDeclaration* cd = dsym->isClassDeclaration()) {
            //Logger::println("*** ClassDeclaration in vtable: %s", cd->toChars());
            const llvm::Type* cinfoty;
            if (cd != ClassDeclaration::classinfo) {
                cd = ClassDeclaration::classinfo;
                DtoResolveClass(cd);
                cinfoty = cd->type->llvmType->get();
            }
            else {
                cinfoty = ts->llvmType->get();
            }
            const llvm::Type* cty = llvm::PointerType::get(cd->type->llvmType->get());
            sinits_ty.push_back(cty);
        }
        else
        assert(0);
    }

    assert(!sinits_ty.empty());
    const llvm::StructType* svtbl_ty = llvm::StructType::get(sinits_ty);

    std::string styname(cd->mangle());
    styname.append("__vtblType");
    gIR->module->addTypeName(styname, svtbl_ty);

    // refine for final vtable type
    llvm::cast<llvm::OpaqueType>(ts->llvmVtblType->get())->refineAbstractTypeTo(svtbl_ty);

    gIR->classes.pop_back();
    gIR->structs.pop_back();

    gIR->declareList.push_back(cd);
}

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

void DtoDeclareClass(ClassDeclaration* cd)
{
    if (cd->llvmDeclared) return;
    cd->llvmDeclared = true;

    Logger::println("DtoDeclareClass(%s)", cd->toPrettyChars());
    LOG_SCOPE;

    assert(cd->type->ty == Tclass);
    TypeClass* ts = (TypeClass*)cd->type;

    assert(cd->llvmIRStruct);
    IRStruct* irstruct = cd->llvmIRStruct;

    gIR->structs.push_back(irstruct);
    gIR->classes.push_back(cd);

    bool needs_definition = false;
    if (cd->parent->isModule()) {
        needs_definition = (cd->getModule() == gIR->dmodule);
    }

    // vtable
    std::string varname("_D");
    varname.append(cd->mangle());
    varname.append("6__vtblZ");

    std::string styname(cd->mangle());
    styname.append("__vtblTy");

    llvm::GlobalValue::LinkageTypes _linkage = llvm::GlobalValue::ExternalLinkage;

    const llvm::StructType* svtbl_ty = isaStruct(ts->llvmVtblType->get());
    cd->llvmVtbl = new llvm::GlobalVariable(svtbl_ty, true, _linkage, 0, varname, gIR->module);

    // init
    std::string initname("_D");
    initname.append(cd->mangle());
    initname.append("6__initZ");

    llvm::GlobalVariable* initvar = new llvm::GlobalVariable(ts->llvmType->get(), true, _linkage, NULL, initname, gIR->module);
    ts->llvmInit = initvar;

    gIR->classes.pop_back();
    gIR->structs.pop_back();

    gIR->constInitList.push_back(cd);
    if (needs_definition)
        gIR->defineList.push_back(cd);

    // classinfo
    DtoDeclareClassInfo(cd);

    // typeinfo
    if (cd->parent->isModule() && cd->getModule() == gIR->dmodule)
        cd->type->getTypeInfo(NULL);
}

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

void DtoConstInitClass(ClassDeclaration* cd)
{
    if (cd->llvmInitialized) return;
    cd->llvmInitialized = true;

    Logger::println("DtoConstInitClass(%s)", cd->toPrettyChars());
    LOG_SCOPE;

    IRStruct* irstruct = cd->llvmIRStruct;
    gIR->structs.push_back(irstruct);
    gIR->classes.push_back(cd);

    // make sure each offset knows its default initializer
    for (IRStruct::OffsetMap::iterator i=irstruct->offsets.begin(); i!=irstruct->offsets.end(); ++i)
    {
        IRStruct::Offset* so = &i->second;
        llvm::Constant* finit = DtoConstFieldInitializer(so->var->type, so->var->init);
        so->init = finit;
        so->var->llvmConstInit = finit;
    }

    // fill out fieldtypes/inits
    std::vector<llvm::Constant*> fieldinits;

    // first field is always the vtable
    assert(cd->llvmVtbl != 0);
    fieldinits.push_back(cd->llvmVtbl);

    // rest
    for (IRStruct::OffsetMap::iterator i=irstruct->offsets.begin(); i!=irstruct->offsets.end(); ++i) {
        Logger::println("adding fieldinit for: %s", i->second.var->toChars());
        fieldinits.push_back(i->second.init);
    }

    // get the struct (class) type
    assert(cd->type->ty == Tclass);
    TypeClass* ts = (TypeClass*)cd->type;
    const llvm::StructType* structtype = isaStruct(ts->llvmType->get());

    // generate initializer
    /*Logger::cout() << cd->toPrettyChars() << " | " << *structtype << '\n';

    for(size_t i=0; i<structtype->getNumElements(); ++i) {
        Logger::cout() << "s#" << i << " = " << *structtype->getElementType(i) << '\n';
    }

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

    llvm::Constant* _init = llvm::ConstantStruct::get(structtype, fieldinits);
    assert(_init);
    cd->llvmInitZ = _init;

    // generate vtable initializer
    std::vector<llvm::Constant*> sinits;

    for (int k=0; k < cd->vtbl.dim; k++)
    {
        Dsymbol* dsym = (Dsymbol*)cd->vtbl.data[k];
        assert(dsym);
        //Logger::cout() << "vtblsym: " << dsym->toChars() << '\n';

        if (FuncDeclaration* fd = dsym->isFuncDeclaration()) {
            DtoForceDeclareDsymbol(fd);
            assert(fd->llvmValue);
            llvm::Constant* c = llvm::cast<llvm::Constant>(fd->llvmValue);
            sinits.push_back(c);
        }
        else if (ClassDeclaration* cd2 = dsym->isClassDeclaration()) {
            assert(cd->llvmClass);
            llvm::Constant* c = cd->llvmClass;
            sinits.push_back(c);
        }
        else
        assert(0);
    }

    const llvm::StructType* svtbl_ty = isaStruct(ts->llvmVtblType->get());

    /*for (size_t i=0; i< sinits.size(); ++i)
    {
        Logger::cout() << "field[" << i << "] = " << *svtbl_ty->getElementType(i) << '\n';
        Logger::cout() << "init [" << i << "] = " << *sinits[i]->getType() << '\n';
        assert(svtbl_ty->getElementType(i) == sinits[i]->getType());
    }*/

    llvm::Constant* cvtblInit = llvm::ConstantStruct::get(svtbl_ty, sinits);
    cd->llvmConstVtbl = llvm::cast<llvm::ConstantStruct>(cvtblInit);

    gIR->classes.pop_back();
    gIR->structs.pop_back();
}

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

void DtoDefineClass(ClassDeclaration* cd)
{
    if (cd->llvmDefined) return;
    cd->llvmDefined = true;

    Logger::println("DtoDefineClass(%s)", cd->toPrettyChars());
    LOG_SCOPE;

    // get the struct (class) type
    assert(cd->type->ty == Tclass);
    TypeClass* ts = (TypeClass*)cd->type;

    bool def = false;
    if (cd->parent->isModule() && cd->getModule() == gIR->dmodule) {
        ts->llvmInit->setInitializer(cd->llvmInitZ);
        cd->llvmVtbl->setInitializer(cd->llvmConstVtbl);
        def = true;
    }

    // generate classinfo
    if (def) DtoDefineClassInfo(cd);
}

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

void DtoCallClassDtors(TypeClass* tc, llvm::Value* instance)
{
    Array* arr = &tc->sym->dtors;
    for (size_t i=0; i<arr->dim; i++)
    {
        FuncDeclaration* fd = (FuncDeclaration*)arr->data[i];
        assert(fd->llvmValue);
        new llvm::CallInst(fd->llvmValue, instance, "", gIR->scopebb());
    }
}

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

void DtoInitClass(TypeClass* tc, llvm::Value* dst)
{
    assert(gIR);

    assert(tc->llvmType);
    uint64_t size_t_size = gTargetData->getTypeSize(DtoSize_t());
    uint64_t n = gTargetData->getTypeSize(tc->llvmType->get()) - size_t_size;

    // set vtable field
    llvm::Value* vtblvar = DtoGEPi(dst,0,0,"tmp",gIR->scopebb());
    assert(tc->sym->llvmVtbl);
    new llvm::StoreInst(tc->sym->llvmVtbl, vtblvar, gIR->scopebb());

    // copy the static initializer
    if (n > 0) {
        assert(tc->llvmInit);
        assert(dst->getType() == tc->llvmInit->getType());

        llvm::Type* arrty = llvm::PointerType::get(llvm::Type::Int8Ty);

        llvm::Value* dstarr = new llvm::BitCastInst(dst,arrty,"tmp",gIR->scopebb());
        dstarr = DtoGEPi(dstarr,size_t_size,"tmp",gIR->scopebb());

        llvm::Value* srcarr = new llvm::BitCastInst(tc->llvmInit,arrty,"tmp",gIR->scopebb());
        srcarr = DtoGEPi(srcarr,size_t_size,"tmp",gIR->scopebb());

        llvm::Function* fn = LLVM_DeclareMemCpy32();
        std::vector<llvm::Value*> llargs;
        llargs.resize(4);
        llargs[0] = dstarr;
        llargs[1] = srcarr;
        llargs[2] = llvm::ConstantInt::get(llvm::Type::Int32Ty, n, false);
        llargs[3] = llvm::ConstantInt::get(llvm::Type::Int32Ty, 0, false);

        new llvm::CallInst(fn, llargs.begin(), llargs.end(), "", gIR->scopebb());
    }
}

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

void DtoDeclareClassInfo(ClassDeclaration* cd)
{
    if (cd->llvmClassDeclared) return;
    cd->llvmClassDeclared = true;

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

    ClassDeclaration* cinfo = ClassDeclaration::classinfo;
    DtoResolveClass(cinfo);

    std::string gname("_D");
    gname.append(cd->mangle());
    gname.append("7__ClassZ");

    const llvm::Type* st = cinfo->type->llvmType->get();

    cd->llvmClass = new llvm::GlobalVariable(st, true, llvm::GlobalValue::ExternalLinkage, NULL, gname, gIR->module);
}

void 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;
//        }

    if (cd->llvmClassDefined) return;
    cd->llvmClassDefined = true;

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

    assert(cd->type->ty == Tclass);
    assert(cd->llvmClass);
    assert(cd->llvmInitZ);
    assert(cd->llvmVtbl);
    assert(cd->llvmConstVtbl);

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

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

    ClassDeclaration* cinfo = ClassDeclaration::classinfo;
    DtoForceConstInitDsymbol(cinfo);
    assert(cinfo->llvmInitZ);

    llvm::Constant* c;

    // own vtable
    c = cinfo->llvmInitZ->getOperand(0);
    assert(c);
    inits.push_back(c);

    // monitor
    // TODO no monitors yet

    // byte[] init
    const llvm::Type* byteptrty = llvm::PointerType::get(llvm::Type::Int8Ty);
    c = llvm::ConstantExpr::getBitCast(cdty->llvmInit, byteptrty);
    assert(!cd->llvmInitZ->getType()->isAbstract());
    size_t initsz = gTargetData->getTypeSize(cd->llvmInitZ->getType());
    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
    const llvm::Type* byteptrptrty = llvm::PointerType::get(byteptrty);
    assert(!cd->llvmVtbl->getType()->isAbstract());
    c = llvm::ConstantExpr::getBitCast(cd->llvmVtbl, byteptrptrty);
    assert(!cd->llvmConstVtbl->getType()->isAbstract());
    size_t vtblsz = gTargetData->getTypeSize(cd->llvmConstVtbl->getType());
    c = DtoConstSlice(DtoConstSize_t(vtblsz), c);
    inits.push_back(c);

    // interfaces array
    // TODO
    c = cinfo->llvmInitZ->getOperand(4);
    inits.push_back(c);

    // base classinfo
    if (cd->baseClass) {
        DtoDeclareClassInfo(cd->baseClass);
        c = cd->baseClass->llvmClass;
        assert(c);
        inits.push_back(c);
    }
    else {
        // null
        c = cinfo->llvmInitZ->getOperand(5);
        inits.push_back(c);
    }

    // destructor
    // TODO
    c = cinfo->llvmInitZ->getOperand(6);
    inits.push_back(c);

    // invariant
    // TODO
    c = cinfo->llvmInitZ->getOperand(7);
    inits.push_back(c);

    // uint flags, adapted from original dmd code
    uint flags = 0;
    //flags |= 4; // has offTi
    //flags |= isCOMclass(); // IUnknown
    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];
        //printf("sm = %s %s\n", sm->kind(), sm->toChars());
        if (sm->hasPointers())
            goto L2;
        }
    }
    }
    flags |= 2;         // no pointers
L2:
    c = DtoConstUint(flags);
    inits.push_back(c);

    // allocator
    // TODO
    c = cinfo->llvmInitZ->getOperand(9);
    inits.push_back(c);

    // offset typeinfo
    // TODO
    c = cinfo->llvmInitZ->getOperand(10);
    inits.push_back(c);

    // default constructor
    // TODO
    c = cinfo->llvmInitZ->getOperand(11);
    inits.push_back(c);

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

    // build the initializer
    const llvm::StructType* st = isaStruct(cinfo->llvmInitZ->getType());
    llvm::Constant* finalinit = llvm::ConstantStruct::get(st, inits);
    //Logger::cout() << "built the classinfo initializer:\n" << *finalinit <<'\n';

    cd->llvmClassZ = finalinit;
    cd->llvmClass->setInitializer(finalinit);
}