view ir/irstruct.cpp @ 1117:4c20fcc4252b

Fun with parameter attributes: For several of the "synthetic" parameters added to D functions, we can apply noalias and nocapture. They are sret parameters, 'nest' pointers passed to nested functions, and _argptr: Nocapture: - Sret and nest are nocapture because they don't represent D-level variables, and thus the callee can't (validly) obtain a pointer to them, let alone keep it around after it returns. - _argptr is nocapture because although the callee has access to it as a pointer, that pointer is invalidated when it returns. All three are noalias because they're function-local variables - Sret and _argptr are noalias because they're freshly alloca'd memory only used for a single function call that's not allowed to keep an aliasing pointer to it around (since the parameter is nocapture). - 'Nest' is noalias because the callee only ever has access to one such pointer per parent function, and every parent function has a different one. This commit also ensures attributes set on sret, _arguments and _argptr are propagated to calls to such functions. It also adds one exception to the general rule that attributes on function types should propagate to calls: the type of a delegate's function pointer has a 'nest' parameter, but this can either be a true 'nest' (for delegates to nested functions) or a 'this' (for delegates to member functions). Since 'this' is neither noalias nor nocapture, and there's generally no way to tell which one it is, we remove these attributes at the call site if the callee is a delegate.
author Frits van Bommel <fvbommel wxs.nl>
date Sat, 14 Mar 2009 22:15:31 +0100
parents 1714836f2c0b
children 79758fd2f48a
line wrap: on
line source

#include "gen/llvm.h"

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

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

IrInterface::IrInterface(BaseClass* b)
:   vtblInitTy(llvm::OpaqueType::get())
{
    base = b;
    decl = b->base;
    vtblInit = NULL;
    vtbl = NULL;
    infoTy = NULL;
    infoInit = NULL;
    info = NULL;

    index = 0;
}

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

IrStruct::IrStruct(AggregateDeclaration* aggr)
:   initOpaque(llvm::OpaqueType::get()),
    classInfoOpaque(llvm::OpaqueType::get()),
    vtblTy(llvm::OpaqueType::get()),
    vtblInitTy(llvm::OpaqueType::get()),
    diCompositeType(NULL)
{
    aggrdecl = aggr;
    defaultFound = false;
    anon = NULL;
    index = 0;

    type = aggr->type;
    defined = false;
    constinited = false;

    interfaceInfos = NULL;
    vtbl = NULL;
    constVtbl = NULL;

    init = NULL;
    constInit = NULL;

    classInfo = NULL;
    constClassInfo = NULL;
    classInfoDeclared = false;
    classInfoDefined = false;

    packed = false;
}

IrStruct::~IrStruct()
{
}

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

void IrStruct::pushAnon(bool isunion)
{
    anon = new Anon(isunion, anon);
}

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

void IrStruct::popAnon()
{
    assert(anon);

    const LLType* BT;

    // get the anon type
    if (anon->isunion)
    {
        // get biggest type in block
        const LLType* biggest = getBiggestType(&anon->types[0], anon->types.size());
        std::vector<const LLType*> vec(1, biggest);
        BT = LLStructType::get(vec, aggrdecl->ir.irStruct->packed);
    }
    else
    {
        // build a struct from the types
        BT = LLStructType::get(anon->types, aggrdecl->ir.irStruct->packed);
    }

    // pop anon
    Anon* tmp = anon;
    anon = anon->parent;
    delete tmp;

    // is there a parent anon?
    if (anon)
    {
        // make sure type gets pushed in the anon, not the main
        anon->types.push_back(BT);
        // index is only manipulated at the top level, anons use raw offsets
    }
    // no parent anon, finally add to aggrdecl
    else
    {
        types.push_back(BT);
        // only advance to next position if main is not a union
        if (!aggrdecl->isUnionDeclaration())
        {
            index++;
        }
    }
}

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

void IrStruct::addVar(VarDeclaration * var)
{
    TypeVector* tvec = &types;
    if (anon)
    {
        // make sure type gets pushed in the anon, not the main
        tvec = &anon->types;

        // set but don't advance index
        var->ir.irField->index = index;

        // set offset in bytes from start of anon block
        var->ir.irField->unionOffset = var->offset - var->offset2;
    }
    else if (aggrdecl->isUnionDeclaration())
    {
        // set but don't advance index
        var->ir.irField->index = index;
    }
    else
    {
        // set and advance index
        var->ir.irField->index = index++;
    }

    // add type
    tvec->push_back(DtoType(var->type));

    // add var
    varDecls.push_back(var);
}

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

const LLType* IrStruct::build()
{
    // if types is empty, add a byte
    if (types.empty())
    {
        types.push_back(LLType::Int8Ty);
    }

    // union type
    if (aggrdecl->isUnionDeclaration())
    {
        const LLType* biggest = getBiggestType(&types[0], types.size());
        std::vector<const LLType*> vec(1, biggest);
        return LLStructType::get(vec, aggrdecl->ir.irStruct->packed);
    }
    // struct/class type
    else
    {
        return LLStructType::get(types, aggrdecl->ir.irStruct->packed);
    }
}

void addZeros(std::vector<const llvm::Type*>& inits, size_t pos, size_t offset)
{
    assert(offset > pos);
    size_t diff = offset - pos;

    size_t sz;

    do
    {
        if (pos%8 == 0 && diff >= 8)
            sz = 8;
        else if (pos%4 == 0 && diff >= 4)
            sz = 4;
        else if (pos%2 == 0 && diff >= 2)
            sz = 2;
        else // if (pos % 1 == 0)
            sz = 1;
        inits.push_back(LLIntegerType::get(sz*8));
        pos += sz;
        diff -= sz;
    } while (pos < offset);

    assert(pos == offset);
}

void addZeros(std::vector<llvm::Constant*>& inits, size_t pos, size_t offset)
{
    assert(offset > pos);
    size_t diff = offset - pos;

    size_t sz;

    do
    {
        if (pos%8 == 0 && diff >= 8)
            sz = 8;
        else if (pos%4 == 0 && diff >= 4)
            sz = 4;
        else if (pos%2 == 0 && diff >= 2)
            sz = 2;
        else // if (pos % 1 == 0)
            sz = 1;
        inits.push_back(LLConstant::getNullValue(LLIntegerType::get(sz*8)));
        pos += sz;
        diff -= sz;
    } while (pos < offset);

    assert(pos == offset);
}

// FIXME: body is exact copy of above
void addZeros(std::vector<llvm::Value*>& inits, size_t pos, size_t offset)
{
    assert(offset > pos);
    size_t diff = offset - pos;

    size_t sz;

    do
    {
        if (pos%8 == 0 && diff >= 8)
            sz = 8;
        else if (pos%4 == 0 && diff >= 4)
            sz = 4;
        else if (pos%2 == 0 && diff >= 2)
            sz = 2;
        else // if (pos % 1 == 0)
            sz = 1;
        inits.push_back(LLConstant::getNullValue(LLIntegerType::get(sz*8)));
        pos += sz;
        diff -= sz;
    } while (pos < offset);

    assert(pos == offset);
}

void IrStruct::buildDefaultConstInit(std::vector<llvm::Constant*>& inits)
{
    assert(!defaultFound);
    defaultFound = true;

    const llvm::StructType* structtype = isaStruct(aggrdecl->type->ir.type->get());
    Logger::cout() << "struct type: " << *structtype << '\n';

    size_t lastoffset = 0;
    size_t lastsize = 0;

    {
        Logger::println("Find the default fields");
        LOG_SCOPE;

        // go through all vars and find the ones that contribute to the default
        size_t nvars = varDecls.size();
        for (size_t i=0; i<nvars; i++)
        {
            VarDeclaration* var = varDecls[i];

            Logger::println("field %s %s = %s : +%u", var->type->toChars(), var->toChars(), var->init ? var->init->toChars() : var->type->defaultInit(var->loc)->toChars(), var->offset);

            // only add vars that don't overlap
            size_t offset = var->offset;
            size_t size = var->type->size();
            if (offset >= lastoffset+lastsize)
            {
                Logger::println("  added");
                lastoffset = offset;
                lastsize = size;
                defVars.push_back(var);
            }
        }
    }

    {
        Logger::println("Build the default initializer");
        LOG_SCOPE;

        lastoffset = 0;
        lastsize = 0;

        // go through the default vars and build the default constant initializer
        // adding zeros along the way to live up to alignment expectations
        size_t nvars = defVars.size();
        for (size_t i=0; i<nvars; i++)
        {
            VarDeclaration* var = defVars[i];

            Logger::println("field %s %s = %s : +%u", var->type->toChars(), var->toChars(), var->init ? var->init->toChars() : var->type->defaultInit(var->loc)->toChars(), var->offset);

            // get offset and size
            size_t offset = var->offset;
            size_t size = var->type->size();

            // is there space in between last last offset and this one?
            // if so, fill it with zeros
            if (offset > lastoffset+lastsize)
            {
                size_t pos = lastoffset + lastsize;
                addZeros(inits, pos, offset);
            }

            // add the field
            // lazily default initialize
            if (!var->ir.irField->constInit)
                var->ir.irField->constInit = DtoConstInitializer(var->loc, var->type, var->init);
            inits.push_back(var->ir.irField->constInit);

            lastoffset = offset;
            lastsize = var->type->size();
        }

        // there might still be padding after the last one, make sure that is zeroed as well
        // is there space in between last last offset and this one?
        size_t structsize = getTypePaddedSize(structtype);

        if (structsize > lastoffset+lastsize)
        {
            size_t pos = lastoffset + lastsize;
            addZeros(inits, pos, structsize);
        }
    }
}

LLConstant* IrStruct::buildDefaultConstInit()
{
    // doesn't work for classes, they add stuff before and maybe after data fields
    assert(!aggrdecl->isClassDeclaration());

    // initializer llvm constant list
    std::vector<LLConstant*> inits;

    // just start with an empty list
    buildDefaultConstInit(inits);

    // build the constant
    // note that the type matches the initializer, not the aggregate in cases with unions
    LLConstant* c = LLConstantStruct::get(inits, aggrdecl->ir.irStruct->packed);
    Logger::cout() << "llvm constant: " << *c << '\n';
//     assert(0);
    return c;
}