view ir/irtypeclass.cpp @ 1479:4f7d50c744ed

Rewrite `StructLiteralExp::toElem` to store individual fields instead of generating a constant to fill the entire struct with a single `store`. This is much more efficient at compile time (fixing #320) and vastly reduces the size of the emitted code. Since LLVM no longer needs to keep the data for all fields in "registers" until the store happens, it should also be more efficient at run time in cases where the fields aren't assigned with constants. There's also some code clean-up by removing duplicated logic.
author Frits van Bommel <fvbommel wxs.nl>
date Sat, 06 Jun 2009 20:16:13 +0200
parents ed70675862c8
children 2292878925f4
line wrap: on
line source

#include "llvm/DerivedTypes.h"

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

#include "gen/irstate.h"
#include "gen/logger.h"
#include "gen/tollvm.h"
#include "gen/utils.h"
#include "gen/llvmhelpers.h"
#include "ir/irtypeclass.h"

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

extern size_t add_zeros(std::vector<const llvm::Type*>& defaultTypes, size_t diff);
extern bool var_offset_sort_cb(const VarDeclaration* v1, const VarDeclaration* v2);

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

IrTypeClass::IrTypeClass(ClassDeclaration* cd)
:   IrTypeAggr(cd),
    cd(cd),
    tc((TypeClass*)cd->type),
    vtbl_pa(llvm::OpaqueType::get())
{
    vtbl_size = cd->vtbl.dim;
    num_interface_vtbls = 0;
}

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

void IrTypeClass::addBaseClassData(
    std::vector< const llvm::Type * > & defaultTypes,
    ClassDeclaration * base,
    size_t & offset,
    size_t & field_index)
{
    if (base->baseClass)
    {
        addBaseClassData(defaultTypes, base->baseClass, offset, field_index);
    }

    // FIXME: merge code with structs in IrTypeAggr

    // mirror the sd->fields array but only fill in contributors
    size_t n = base->fields.dim;
    LLSmallVector<VarDeclaration*, 16> data(n, NULL);
    default_fields.reserve(n);

    // first fill in the fields with explicit initializers
    VarDeclarationIter field_it(base->fields);
    for (; field_it.more(); field_it.next())
    {
        // init is !null for explicit inits
        if (field_it->init != NULL)
        {
            IF_LOG Logger::println("adding explicit initializer for struct field %s",
                field_it->toChars());

            data[field_it.index] = *field_it;

            size_t f_begin = field_it->offset;
            size_t f_end = f_begin + field_it->type->size();

            // make sure there is no overlap
            for (size_t i = 0; i < field_it.index; i++)
            {
                if (data[i] != NULL)
                {
                    VarDeclaration* vd = data[i];
                    size_t v_begin = vd->offset;
                    size_t v_end = v_begin + vd->type->size();

                    if (v_begin >= f_end || v_end <= f_begin)
                        continue;

                    base->error(vd->loc, "has overlapping initialization for %s and %s",
                        field_it->toChars(), vd->toChars());
                }
            }
        }
    }

    if (global.errors)
    {
        fatal();
    }

    // fill in default initializers
    field_it = VarDeclarationIter(base->fields);
    for (;field_it.more(); field_it.next())
    {
        if (data[field_it.index])
            continue;

        size_t f_begin = field_it->offset;
        size_t f_end = f_begin + field_it->type->size();

        // make sure it doesn't overlap anything explicit
        bool overlaps = false;
        for (size_t i = 0; i < n; i++)
        {
            if (data[i])
            {
                size_t v_begin = data[i]->offset;
                size_t v_end = v_begin + data[i]->type->size();

                if (v_begin >= f_end || v_end <= f_begin)
                    continue;

                overlaps = true;
                break;
            }
        }

        // if no overlap was found, add the default initializer
        if (!overlaps)
        {
            IF_LOG Logger::println("adding default initializer for struct field %s",
                field_it->toChars());
            data[field_it.index] = *field_it;
        }
    }

    // ok. now we can build a list of llvm types. and make sure zeros are inserted if necessary.

    // first we sort the list by offset
    std::sort(data.begin(), data.end(), var_offset_sort_cb);

    // add types to list
    for (size_t i = 0; i < n; i++)
    {
        VarDeclaration* vd = data[i];

        if (vd == NULL)
            continue;

        assert(vd->offset >= offset && "it's a bug... most likely DMD bug 2481");

        // add to default field list
        if (cd == base)
            default_fields.push_back(vd);

        // get next aligned offset for this type
        size_t alignedoffset = realignOffset(offset, vd->type);

        // insert explicit padding?
        if (alignedoffset < vd->offset)
        {
            field_index += add_zeros(defaultTypes, vd->offset - alignedoffset);
        }

        // add default type
        defaultTypes.push_back(DtoType(vd->type));

        // advance offset to right past this field
        offset = vd->offset + vd->type->size();

        // create ir field
        vd->aggrIndex = (unsigned)field_index;
        ++field_index;
    }

    // any interface implementations?
    if (base->vtblInterfaces && base->vtblInterfaces->dim > 0)
    {
        bool new_instances = (base == cd);

        ArrayIter<BaseClass> it2(*base->vtblInterfaces);

        VarDeclarationIter interfaces_idx(ClassDeclaration::classinfo->fields, 3);
	Type* first = interfaces_idx->type->nextOf()->pointerTo();

        // align offset
        offset = (offset + PTRSIZE - 1) & ~(PTRSIZE - 1);

        for (; !it2.done(); it2.next())
        {
            BaseClass* b = it2.get();
            IF_LOG Logger::println("Adding interface vtbl for %s", b->base->toPrettyChars());

            Array arr;
            b->fillVtbl(cd, &arr, new_instances);

            const llvm::Type* ivtbl_type = buildVtblType(first, &arr);
            defaultTypes.push_back(llvm::PointerType::get(ivtbl_type, 0));

            offset += PTRSIZE;

            // add to the interface map
            addInterfaceToMap(b->base, field_index);
            field_index++;

            // inc count
            num_interface_vtbls++;
        }
    }

#if 0
    // tail padding?
    if (offset < base->structsize)
    {
        field_index += add_zeros(defaultTypes, base->structsize - offset);
        offset = base->structsize;
    }
#endif
}

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

const llvm::Type* IrTypeClass::buildType()
{
    IF_LOG Logger::println("Building class type %s @ %s", cd->toPrettyChars(), cd->loc.toChars());
    LOG_SCOPE;
    IF_LOG Logger::println("Instance size: %u", cd->structsize);

    // find the fields that contribute to the default initializer.
    // these will define the default type.

    std::vector<const llvm::Type*> defaultTypes;
    defaultTypes.reserve(32);

    // add vtbl
    defaultTypes.push_back(llvm::PointerType::get(vtbl_pa.get(), 0));

    // interfaces are just a vtable
    if (cd->isInterfaceDeclaration())
    {
        num_interface_vtbls = cd->vtblInterfaces ? cd->vtblInterfaces->dim : 0;
    }
    // classes have monitor and fields
    else
    {
        // add monitor
        defaultTypes.push_back(llvm::PointerType::get(llvm::Type::Int8Ty, 0));

        // we start right after the vtbl and monitor
        size_t offset = PTRSIZE * 2;
        size_t field_index = 2;

        // add data members recursively
        addBaseClassData(defaultTypes, cd, offset, field_index);

#if 1
        // tail padding?
        if (offset < cd->structsize)
        {
            field_index += add_zeros(defaultTypes, cd->structsize - offset);
            offset = cd->structsize;
        }
#endif
    }

    // errors are fatal during codegen
    if (global.errors)
        fatal();

    // build the llvm type
    const llvm::Type* st = llvm::StructType::get(defaultTypes, false);

    // refine type
    llvm::cast<llvm::OpaqueType>(pa.get())->refineAbstractTypeTo(st);

    // name type
    Type::sir->getState()->module->addTypeName(cd->toPrettyChars(), pa.get());

    // VTBL

    // build vtbl type
    const llvm::Type* vtblty = buildVtblType(
        ClassDeclaration::classinfo->type,
        &cd->vtbl);

    // refine vtbl pa
    llvm::cast<llvm::OpaqueType>(vtbl_pa.get())->refineAbstractTypeTo(vtblty);

    // name vtbl type
    std::string name(cd->toPrettyChars());
    name.append(".__vtbl");
    Type::sir->getState()->module->addTypeName(name, vtbl_pa.get());

#if 0
    IF_LOG Logger::cout() << "class type: " << *pa.get() << std::endl;
#endif

    return get();
}

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

const llvm::Type* IrTypeClass::buildVtblType(Type* first, Array* vtbl_array)
{
    IF_LOG Logger::println("Building vtbl type for class %s", cd->toPrettyChars());
    LOG_SCOPE;

    std::vector<const llvm::Type*> types;
    types.reserve(vtbl_array->dim);

    // first comes the classinfo
    types.push_back(DtoType(first));

    // then come the functions
    ArrayIter<Dsymbol> it(*vtbl_array);
    it.index = 1;

    for (; !it.done(); it.next())
    {
        Dsymbol* dsym = it.get();
        if (dsym == NULL)
        {
            // FIXME
            // why is this null?
            // happens for mini/s.d
            types.push_back(getVoidPtrType());
            continue;
        }

        FuncDeclaration* fd = dsym->isFuncDeclaration();
        assert(fd && "invalid vtbl entry");

        IF_LOG Logger::println("Adding type of %s", fd->toPrettyChars());

        types.push_back(DtoType(fd->type->pointerTo()));
    }

    // build the vtbl llvm type
    return llvm::StructType::get(types, false);
}

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

const llvm::Type * IrTypeClass::get()
{
    return llvm::PointerType::get(pa.get(), 0);
}

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

size_t IrTypeClass::getInterfaceIndex(ClassDeclaration * inter)
{
    ClassIndexMap::iterator it = interfaceMap.find(inter);
    if (it == interfaceMap.end())
        return ~0;
    return it->second;
}

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

void IrTypeClass::addInterfaceToMap(ClassDeclaration * inter, size_t index)
{
    // don't duplicate work or overwrite indices
    if (interfaceMap.find(inter) != interfaceMap.end())
        return;

    // add this interface
    interfaceMap.insert(std::make_pair(inter, index));

    // add the direct base interfaces recursively - they
    // are accessed through the same index
    if (inter->interfaces_dim > 0)
    {
        BaseClass* b = inter->interfaces[0];
        addInterfaceToMap(b->base, index);
    }
}

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