Mercurial > projects > ldc
view ir/irstruct.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 | dd135ff697fa |
children | 65505c9d70f5 |
line wrap: on
line source
#include "gen/llvm.h" #include "mtype.h" #include "aggregate.h" #include "declaration.h" #include "init.h" #include "gen/irstate.h" #include "gen/tollvm.h" #include "gen/logger.h" #include "gen/llvmhelpers.h" #include "gen/utils.h" #include "ir/irstruct.h" #include "ir/irtypeclass.h" #include <algorithm> ////////////////////////////////////////////////////////////////////////////// IrStruct::IrStruct(AggregateDeclaration* aggr) : diCompositeType(NULL), init_pa(llvm::OpaqueType::get()) { aggrdecl = aggr; type = aggr->type; packed = (type->ty == Tstruct) ? type->alignsize() == 1 : 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() { 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( init_pa.get(), true, _linkage, NULL, initname, gIR->module); return init; } ////////////////////////////////////////////////////////////////////////////// llvm::Constant * IrStruct::getDefaultInit() { if (constInit) return constInit; if (type->ty == Tstruct) { constInit = createStructDefaultInitializer(); } else { constInit = createClassDefaultInitializer(); } llvm::OpaqueType* o = llvm::cast<llvm::OpaqueType>(init_pa.get()); o->refineAbstractTypeTo(constInit->getType()); return constInit; } ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// // helper function that returns the static default initializer of a variable LLConstant* get_default_initializer(VarDeclaration* vd, Initializer* init) { if (init) { return DtoConstInitializer(init->loc, vd->type, init); } else if (vd->init) { return DtoConstInitializer(vd->init->loc, vd->type, vd->init); } else { return DtoConstExpInit(vd->loc, vd->type, vd->type->defaultInit(vd->loc)); } } // helper function that adds zero bytes to a vector of constants size_t add_zeros(std::vector<llvm::Constant*>& constants, size_t diff) { size_t n = constants.size(); while (diff) { 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; } // Matches the way the type is built in IrTypeStruct // maybe look at unifying the interface. LLConstant * IrStruct::createStructDefaultInitializer() { 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"); IrTypeStruct* ts = type->irtype->isStruct(); assert(ts); // start at offset zero size_t offset = 0; // vector of constants std::vector<llvm::Constant*> constants; // go through fields IrTypeAggr::iterator it; for (it = ts->def_begin(); it != ts->def_end(); ++it) { VarDeclaration* vd = *it; assert(vd->offset >= offset && "default fields not sorted by offset"); 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) { alignedoffset = realignOffset(alignedoffset, vd->type); } // 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 0 IF_LOG Logger::cout() << "final default initializer: " << *definit << std::endl; #endif return definit; } ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// // yet another rewrite of the notorious StructInitializer. typedef std::pair<VarDeclaration*, llvm::Constant*> VCPair; bool struct_init_data_sort(const VCPair& a, const VCPair& b) { return (a.first && b.first) ? a.first->offset < b.first->offset : false; } // this time a bit more inspired by the DMD code. LLConstant * IrStruct::createStructInitializer(StructInitializer * si) { IF_LOG Logger::println("Building StructInitializer of type %s", si->ad->toPrettyChars()); LOG_SCOPE; // sanity check assert(si->ad == aggrdecl && "struct type mismatch"); assert(si->vars.dim == si->value.dim && "inconsistent StructInitializer"); // array of things to build llvm::SmallVector<VCPair, 16> data(aggrdecl->fields.dim); // 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]) { datamap[i] = j; break; } } } // fill in explicit initializers n = si->vars.dim; for (size_t i = 0; i < n; i++) { VarDeclaration* vd = (VarDeclaration*)si->vars.data[i]; Initializer* ini = (Initializer*)si->value.data[i]; Loc loc = ini ? ini->loc : si->loc; size_t idx = datamap[i]; // check for duplicate initialization if (data[idx].first != NULL) { Loc l = ini ? ini->loc : si->loc; error(l, "duplicate initialization of %s", vd->toChars()); continue; } // check for overlapping initialization for (size_t j = 0; j < i; j++) { size_t idx2 = datamap[j]; assert(data[idx2].first); VarDeclarationIter it(aggrdecl->fields, idx2); unsigned f_begin = it->offset; unsigned f_end = f_begin + it->type->size(); if (vd->offset >= f_end || (vd->offset + vd->type->size()) <= f_begin) continue; error(loc, "initializer for %s overlaps previous initialization of %s", vd->toChars(), it->toChars()); } IF_LOG Logger::println("Explicit initializer: %s @+%u", vd->toChars(), vd->offset); LOG_SCOPE; data[idx].first = vd; data[idx].second = get_default_initializer(vd, ini); } // fill in implicit initializers n = data.size(); for (size_t i = 0; i < n; i++) { VarDeclaration* vd = data[i].first; if (vd) continue; vd = (VarDeclaration*)aggrdecl->fields.data[i]; unsigned vd_begin = vd->offset; unsigned vd_end = vd_begin + vd->type->size(); // make sure it doesn't overlap any explicit initializers. VarDeclarationIter it(aggrdecl->fields); bool overlaps = false; size_t j = 0; for (; it.more(); it.next(), j++) { if (i == j || !data[j].first) continue; unsigned f_begin = it->offset; unsigned f_end = f_begin + it->type->size(); if (vd_begin >= f_end || vd_end <= f_begin) continue; overlaps = true; break; } // add if no overlap found if (!overlaps) { IF_LOG Logger::println("Implicit initializer: %s @+%u", vd->toChars(), vd->offset); LOG_SCOPE; data[i].first = vd; data[i].second = get_default_initializer(vd, NULL); } } // stop if there were errors if (global.errors) { fatal(); } // sort data array by offset std::sort(data.begin(), data.end(), struct_init_data_sort); // build array of constants and make sure explicit zero padding is inserted when necessary. size_t offset = 0; std::vector<llvm::Constant*> constants; constants.reserve(n); for (size_t i = 0; i < n; i++) { VarDeclaration* vd = data[i].first; if (vd == NULL) continue; // get next aligned offset for this field size_t alignedoffset = offset; if (!packed) { alignedoffset = realignOffset(alignedoffset, vd->type); } // insert explicit padding? if (alignedoffset < vd->offset) { size_t diff = vd->offset - alignedoffset; IF_LOG Logger::println("adding %zu bytes zero padding", diff); add_zeros(constants, diff); } IF_LOG Logger::println("adding field %s", vd->toChars()); constants.push_back(data[i].second); offset = vd->offset + vd->type->size(); } // tail padding? if (offset < aggrdecl->structsize) { size_t diff = aggrdecl->structsize - offset; IF_LOG Logger::println("adding %zu bytes zero padding", diff); add_zeros(constants, diff); } // 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; }