diff ir/irstruct.cpp @ 1228:79758fd2f48a

Added Doxygen file. Completely seperated type and symbol generation. Should fix a lot of bugs, but is not yet 100% complete.
author Tomas Lindquist Olsen <tomas.l.olsen gmail.com>
date Wed, 15 Apr 2009 20:06:25 +0200
parents 1714836f2c0b
children 212ec2d9d176
line wrap: on
line diff
--- a/ir/irstruct.cpp	Mon Apr 13 17:42:36 2009 +0200
+++ b/ir/irstruct.cpp	Wed Apr 15 20:06:25 2009 +0200
@@ -5,353 +5,330 @@
 #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"
+#include "gen/utils.h"
 
-IrInterface::IrInterface(BaseClass* b)
-:   vtblInitTy(llvm::OpaqueType::get())
+#include "ir/irstruct.h"
+#include "ir/irtypeclass.h"
+
+//////////////////////////////////////////////////////////////////////////////
+
+IrStruct::IrStruct(AggregateDeclaration* aggr)
+:   diCompositeType(NULL)
+{
+    aggrdecl = aggr;
+
+    type = aggr->type;
+
+    packed = false;
+
+    // above still need to be looked at
+
+    init = NULL;
+    constInit = NULL;
+
+    vtbl = NULL;
+    constVtbl = NULL;
+    classInfo = NULL;
+    constClassInfo = NULL;
+
+    classInterfacesArray = NULL;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+LLGlobalVariable * IrStruct::getInitSymbol()
 {
-    base = b;
-    decl = b->base;
-    vtblInit = NULL;
-    vtbl = NULL;
-    infoTy = NULL;
-    infoInit = NULL;
-    info = NULL;
+    if (init)
+        return init;
+
+    // create the initZ symbol
+    std::string initname("_D");
+    initname.append(aggrdecl->mangle());
+    initname.append("6__initZ");
+
+    llvm::GlobalValue::LinkageTypes _linkage = DtoExternalLinkage(aggrdecl);
+
+    init = new llvm::GlobalVariable(
+        type->irtype->getPA().get(), true, _linkage, NULL, initname, gIR->module);
+
+    return init;
+}
 
-    index = 0;
+//////////////////////////////////////////////////////////////////////////////
+
+llvm::Constant * IrStruct::getDefaultInit()
+{
+    if (constInit)
+        return constInit;
+
+    if (type->ty == Tstruct)
+    {
+        constInit = createStructDefaultInitializer();
+    }
+    else
+    {
+        constInit = createClassDefaultInitializer();
+    }
+
+    return constInit;
 }
 
 //////////////////////////////////////////////////////////////////////////////
 //////////////////////////////////////////////////////////////////////////////
 //////////////////////////////////////////////////////////////////////////////
 
-IrStruct::IrStruct(AggregateDeclaration* aggr)
-:   initOpaque(llvm::OpaqueType::get()),
-    classInfoOpaque(llvm::OpaqueType::get()),
-    vtblTy(llvm::OpaqueType::get()),
-    vtblInitTy(llvm::OpaqueType::get()),
-    diCompositeType(NULL)
+// helper function that returns the static default initializer of a variable
+LLConstant* get_default_initializer(VarDeclaration* vd, Initializer* init)
 {
-    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)
+    if (init)
     {
-        // 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);
+        return DtoConstInitializer(init->loc, vd->type, init);
     }
-
-    // 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
+    else if (vd->init)
     {
-        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;
+        return DtoConstInitializer(vd->init->loc, vd->type, vd->init);
     }
     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);
+        return DtoConstExpInit(vd->loc, vd->type, vd->type->defaultInit(vd->loc));
     }
 }
 
-void addZeros(std::vector<const llvm::Type*>& inits, size_t pos, size_t offset)
+// helper function that adds zero bytes to a vector of constants
+size_t add_zeros(std::vector<llvm::Constant*>& constants, size_t diff)
 {
-    assert(offset > pos);
-    size_t diff = offset - pos;
-
-    size_t sz;
-
-    do
+    size_t n = constants.size();
+    while (diff)
     {
-        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);
+        if (global.params.is64bit && diff % 8 == 0)
+        {
+            constants.push_back(llvm::Constant::getNullValue(llvm::Type::Int64Ty));
+            diff -= 8;
+        }
+        else if (diff % 4 == 0)
+        {
+            constants.push_back(llvm::Constant::getNullValue(llvm::Type::Int32Ty));
+            diff -= 4;
+        }
+        else if (diff % 2 == 0)
+        {
+            constants.push_back(llvm::Constant::getNullValue(llvm::Type::Int16Ty));
+            diff -= 2;
+        }
+        else
+        {
+            constants.push_back(llvm::Constant::getNullValue(llvm::Type::Int8Ty));
+            diff -= 1;
+        }
+    }
+    return constants.size() - n;
 }
 
-// FIXME: body is exact copy of above
-void addZeros(std::vector<llvm::Value*>& inits, size_t pos, size_t offset)
+// Matches the way the type is built in IrTypeStruct
+// maybe look at unifying the interface.
+
+LLConstant * IrStruct::createStructDefaultInitializer()
 {
-    assert(offset > pos);
-    size_t diff = offset - pos;
+    IF_LOG Logger::println("Building default initializer for %s", aggrdecl->toPrettyChars());
+    LOG_SCOPE;
+
+    assert(type->ty == Tstruct && "cannot build struct default initializer for non struct type");
 
-    size_t sz;
+    // start at offset zero
+    size_t offset = 0;
 
-    do
+    // vector of constants
+    std::vector<llvm::Constant*> constants;
+
+    // go through fields
+    ArrayIter<VarDeclaration> it(aggrdecl->fields);
+    for (; !it.done(); it.next())
     {
-        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);
+        VarDeclaration* vd = it.get();
+
+        if (vd->offset < offset)
+        {
+            IF_LOG Logger::println("skipping field: %s %s (+%u)", vd->type->toChars(), vd->toChars(), vd->offset);
+            continue;
+        }
+
+        IF_LOG Logger::println("using field: %s %s (+%u)", vd->type->toChars(), vd->toChars(), vd->offset);
+
+        // get next aligned offset for this field
+        size_t alignedoffset = offset;
+        if (!packed)
+        {
+            size_t alignsize = vd->type->alignsize();
+            alignedoffset = (offset + alignsize - 1) & ~(alignsize - 1);
+        }
 
-    assert(pos == offset);
+        // insert explicit padding?
+        if (alignedoffset < vd->offset)
+        {
+            add_zeros(constants, vd->offset - alignedoffset);
+        }
+
+        // add initializer
+        constants.push_back(get_default_initializer(vd, NULL));
+
+        // advance offset to right past this field
+        offset = vd->offset + vd->type->size();
+    }
+
+    // tail padding?
+    if (offset < aggrdecl->structsize)
+    {
+        add_zeros(constants, aggrdecl->structsize - offset);
+    }
+
+    // build constant struct
+    llvm::Constant* definit = llvm::ConstantStruct::get(constants, packed);
+    IF_LOG Logger::cout() << "final default initializer: " << *definit << std::endl;
+
+    // sanity check
+    assert(definit->getType() == type->irtype->get() &&
+        "default initializer type does not match the default struct type");
+
+    return definit;
 }
 
-void IrStruct::buildDefaultConstInit(std::vector<llvm::Constant*>& inits)
-{
-    assert(!defaultFound);
-    defaultFound = true;
+//////////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////
+
+// yet another rewrite of the notorious StructInitializer.
+
+// this time a bit more inspired by the DMD code.
 
-    const llvm::StructType* structtype = isaStruct(aggrdecl->type->ir.type->get());
-    Logger::cout() << "struct type: " << *structtype << '\n';
+LLConstant * IrStruct::createStructInitializer(StructInitializer * si)
+{
+    IF_LOG Logger::println("Building StructInitializer of type %s", si->ad->toPrettyChars());
+    LOG_SCOPE;
 
-    size_t lastoffset = 0;
-    size_t lastsize = 0;
-
-    {
-        Logger::println("Find the default fields");
-        LOG_SCOPE;
+    // sanity check
+    assert(si->ad == aggrdecl && "struct type mismatch");
+    assert(si->vars.dim == si->value.dim && "inconsistent StructInitializer");
 
-        // 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);
+    // array of things to build
+    typedef std::pair<VarDeclaration*, llvm::Constant*> VCPair;
+    llvm::SmallVector<VCPair, 16> data(aggrdecl->fields.dim);
 
-            // only add vars that don't overlap
-            size_t offset = var->offset;
-            size_t size = var->type->size();
-            if (offset >= lastoffset+lastsize)
+    // start by creating a map from initializer indices to field indices.
+    // I have no fucking clue why dmd represents it like this :/
+    size_t n = si->vars.dim;
+    LLSmallVector<int, 16> datamap(n, 0);
+    for (size_t i = 0; i < n; i++)
+    {
+        for (size_t j = 0; 1; j++)
+        {
+            assert(j < aggrdecl->fields.dim);
+            if (aggrdecl->fields.data[j] == si->vars.data[i])
             {
-                Logger::println("  added");
-                lastoffset = offset;
-                lastsize = size;
-                defVars.push_back(var);
+                datamap[i] = j;
+                break;
             }
         }
     }
 
+    // fill in explicit initializers
+    n = si->vars.dim;
+    for (size_t i = 0; i < n; i++)
     {
-        Logger::println("Build the default initializer");
-        LOG_SCOPE;
+        VarDeclaration* vd = (VarDeclaration*)si->vars.data[i];
+        Initializer* ini = (Initializer*)si->value.data[i];
 
-        lastoffset = 0;
-        lastsize = 0;
+        size_t idx = datamap[i];
 
-        // 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++)
+        if (data[idx].first != NULL)
         {
-            VarDeclaration* var = defVars[i];
+            Loc l = ini ? ini->loc : si->loc;
+            error(l, "duplicate initialization of %s", vd->toChars());
+            continue;
+        }
 
-            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);
+        data[idx].first = vd;
+        data[idx].second = get_default_initializer(vd, ini);
+    }
+
+    // build array of constants and try to fill in default initializers
+    // where there is room.
+    size_t offset = 0;
+    std::vector<llvm::Constant*> constants;
+    constants.reserve(16);
 
-            // get offset and size
-            size_t offset = var->offset;
-            size_t size = var->type->size();
+    n = data.size();
+    for (size_t i = 0; i < n; i++)
+    {
+        VarDeclaration* vd = data[i].first;
 
-            // is there space in between last last offset and this one?
-            // if so, fill it with zeros
-            if (offset > lastoffset+lastsize)
+        // explicitly initialized?
+        if (vd != NULL)
+        {
+            // get next aligned offset for this field
+            size_t alignedoffset = offset;
+            if (!packed)
             {
-                size_t pos = lastoffset + lastsize;
-                addZeros(inits, pos, offset);
+                size_t alignsize = vd->type->alignsize();
+                alignedoffset = (offset + alignsize - 1) & ~(alignsize - 1);
+            }
+
+            // insert explicit padding?
+            if (alignedoffset < vd->offset)
+            {
+                add_zeros(constants, vd->offset - alignedoffset);
             }
 
-            // 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();
+            IF_LOG Logger::println("using field: %s", vd->toChars());
+            constants.push_back(data[i].second);
+            offset = vd->offset + vd->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);
+        // not explicit! try and fit in the default initialization instead
+        // make sure we don't overlap with any following explicity initialized fields
+        else
+        {
+            vd = (VarDeclaration*)aggrdecl->fields.data[i];
 
-        if (structsize > lastoffset+lastsize)
-        {
-            size_t pos = lastoffset + lastsize;
-            addZeros(inits, pos, structsize);
+            // check all the way that we don't overlap, slow but it works!
+            for (size_t j = i+1; j <= n; j++)
+            {
+                if (j == n) // no overlap
+                {
+                    IF_LOG Logger::println("using field default: %s", vd->toChars());
+                    constants.push_back(get_default_initializer(vd, NULL));
+                    offset = vd->offset + vd->type->size();
+                    break;
+                }
+
+                VarDeclaration* vd2 = (VarDeclaration*)aggrdecl->fields.data[j];
+
+                size_t o2 = vd->offset + vd->type->size();
+
+                if (vd2->offset < o2 && data[i].first)
+                    break; // overlaps
+            }
         }
     }
+
+    // tail padding?
+    if (offset < aggrdecl->structsize)
+    {
+        add_zeros(constants, aggrdecl->structsize - offset);
+    }
+
+    // stop if there were errors
+    if (global.errors)
+    {
+        fatal();
+    }
+
+    // build constant
+    assert(!constants.empty());
+    llvm::Constant* c = llvm::ConstantStruct::get(&constants[0], constants.size(), packed);
+    IF_LOG Logger::cout() << "final struct initializer: " << *c << std::endl;
+    return c;
 }
 
-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;
-}