Mercurial > projects > ldc
diff gen/structs.cpp @ 797:340acf1535d0
Removed KDevelop3 project files, CMake can generate them just fine!
Fixed function literals in static initializers.
Changed alignment of delegates from 2*PTRSIZE to just PTRSIZE.
Changed errors to go to stderr instead of stdout.
Fairly major rewriting of struct/union/class handling, STILL A BIT BUGGY !!!
author | Tomas Lindquist Olsen <tomas.l.olsen@gmail.com> |
---|---|
date | Sat, 29 Nov 2008 21:25:43 +0100 |
parents | 6e7a4c3b64d2 |
children | d14e4594c7d7 |
line wrap: on
line diff
--- a/gen/structs.cpp Sat Nov 29 12:28:10 2008 +0100 +++ b/gen/structs.cpp Sat Nov 29 21:25:43 2008 +0100 @@ -18,36 +18,279 @@ #include "ir/irstruct.h" ////////////////////////////////////////////////////////////////////////////////////////// +void addZeros(std::vector<llvm::Constant*>& inits, unsigned pos, unsigned offset); // defined in irstruct.cpp + +// pair of var and its init +typedef std::pair<VarDeclaration*,Initializer*> VarInitPair; + +// comparison func for qsort +static int varinit_offset_cmp_func(const void* p1, const void* p2) +{ + VarDeclaration* v1 = ((VarInitPair*)p1)->first; + VarDeclaration* v2 = ((VarInitPair*)p2)->first; + if (v1->offset < v2->offset) + return -1; + else if (v1->offset > v2->offset) + return 1; + else + return 0; +} + +/* +this uses a simple algorithm to build the correct constant + +(1) first sort the explicit initializers by offset... well, DMD doesn't :) + +(2) if there is NO space before the next explicit initializeer, goto (9) +(3) find the next default initializer that fits before it, if NOT found goto (7) +(4) insert zero padding up to the next default initializer +(5) insert the next default initializer +(6) goto (2) + +(7) insert zero padding up to the next explicit initializer + +(9) insert the next explicit initializer +(10) goto (2) + +(11) done + +(next can be the end too) + +*/ + +// return the next default initializer to use or null +static VarDeclaration* nextDefault(IrStruct* irstruct, size_t& idx, size_t pos, size_t offset) +{ + IrStruct::VarDeclVector& defaults = irstruct->defVars; + size_t ndefaults = defaults.size(); + + // for each valid index + while(idx < ndefaults) + { + VarDeclaration* v = defaults[idx]; + + // skip defaults before pos + if (v->offset < pos) + { + idx++; + continue; + } + + // this var default fits + if (v->offset >= pos && v->offset + v->type->size() <= offset) + return v; + + // not usable + break; + } + + // not usable + return NULL; +} + LLConstant* DtoConstStructInitializer(StructInitializer* si) { Logger::println("DtoConstStructInitializer: %s", si->toChars()); LOG_SCOPE; + // get TypeStruct assert(si->ad); TypeStruct* ts = (TypeStruct*)si->ad->type; - DtoResolveDsymbol(si->ad); + // force constant initialization of the symbol + DtoForceConstInitDsymbol(si->ad); + // get formal type const llvm::StructType* structtype = isaStruct(ts->ir.type->get()); + // log it if (Logger::enabled()) Logger::cout() << "llvm struct type: " << *structtype << '\n'; + // sanity check + assert(si->value.dim > 0); assert(si->value.dim == si->vars.dim); - std::vector<DUnionIdx> inits; - for (int i = 0; i < si->value.dim; ++i) + // vector of final initializer constants + std::vector<LLConstant*> inits; + + // get the ir struct + IrStruct* irstruct = si->ad->ir.irStruct; + + // get default fields + IrStruct::VarDeclVector& defaults = irstruct->defVars; + size_t ndefaults = defaults.size(); + + // make sure si->vars is sorted by offset + std::vector<VarInitPair> vars; + size_t nvars = si->vars.dim; + vars.resize(nvars); + + // fill pair vector + for (size_t i = 0; i < nvars; i++) { + VarDeclaration* var = (VarDeclaration*)si->vars.data[i]; Initializer* ini = (Initializer*)si->value.data[i]; + assert(var); assert(ini); - VarDeclaration* vd = (VarDeclaration*)si->vars.data[i]; - assert(vd); - LLConstant* v = DtoConstInitializer(vd->loc, vd->type, ini); - inits.push_back(DUnionIdx(vd->ir.irField->index, vd->ir.irField->indexOffset, v)); + vars[i] = std::make_pair(var, ini); + } + // sort it + qsort(&vars[0], nvars, sizeof(VarInitPair), &varinit_offset_cmp_func); + + // check integrity + // and do error checking, since the frontend does verify static struct initializers + size_t lastoffset = 0; + size_t lastsize = 0; + bool overlap = false; + for (size_t i=0; i < nvars; i++) + { + // next explicit init var + VarDeclaration* var = vars[i].first; + Logger::println("var = %s : +%u", var->toChars(), var->offset); + + // I would have thought this to be a frontend check + for (size_t j=i+1; j<nvars; j++) + { + if (j == i) + continue; + VarDeclaration* var2 = vars[j].first; + if (var2->offset >= var->offset && var2->offset < var->offset + var->type->size()) + { + fprintf(stdmsg, "Error: %s: initializer '%s' overlaps with '%s'\n", si->loc.toChars(), var->toChars(), var2->toChars()); + overlap = true; + } + } + + // update offsets + lastoffset = var->offset; + lastsize = var->type->size(); + } + + // error handling, report all overlaps before aborting + if (overlap) + { + error("%s: overlapping union initializers", si->loc.toChars()); } - DtoConstInitStruct((StructDeclaration*)si->ad); - return si->ad->ir.irStruct->dunion->getConst(inits); + // go through each explicit initalizer, falling back to defaults or zeros when necessary + lastoffset = 0; + lastsize = 0; + + size_t j=0; // defaults + + for (size_t i=0; i < nvars; i++) + { + // get var and init + VarDeclaration* var = vars[i].first; + Initializer* ini = vars[i].second; + + size_t offset = var->offset; + size_t size = var->type->size(); + + // if there is space before the next explicit initializer +Lpadding: + size_t pos = lastoffset+lastsize; + if (offset > pos) + { + // find the the next default initializer that fits in this space + VarDeclaration* nextdef = nextDefault(irstruct, j, lastoffset+lastsize, offset); + + // found + if (nextdef) + { + // need zeros before the default + if (nextdef->offset > pos) + { + Logger::println("inserting %lu byte padding at %lu", nextdef->offset - pos, pos); + addZeros(inits, pos, nextdef->offset); + } + + // do the default + Logger::println("adding default field: %s : +%u", nextdef->toChars(), nextdef->offset); + LLConstant* c = nextdef->ir.irField->constInit; + inits.push_back(c); + + // update offsets + lastoffset = nextdef->offset; + lastsize = nextdef->type->size(); + + // check if more defaults would fit + goto Lpadding; + } + // not found, pad with zeros + else + { + Logger::println("inserting %lu byte padding at %lu", offset - pos, pos); + addZeros(inits, pos, offset); + // offsets are updated by the explicit initializer + } + } + + // insert next explicit + Logger::println("adding explicit field: %s : +%lu", var->toChars(), offset); + LOG_SCOPE; + LLConstant* c = DtoConstInitializer(var->loc, var->type, ini); + inits.push_back(c); + + lastoffset = offset; + lastsize = size; + } + + // there might still be padding after the last one, make sure that is defaulted/zeroed as well + size_t structsize = getABITypeSize(structtype); + + // if there is space before the next explicit initializer + // FIXME: this should be handled in the loop above as well +Lpadding2: + size_t pos = lastoffset+lastsize; + if (structsize > pos) + { + // find the the next default initializer that fits in this space + VarDeclaration* nextdef = nextDefault(irstruct, j, lastoffset+lastsize, structsize); + + // found + if (nextdef) + { + // need zeros before the default + if (nextdef->offset > pos) + { + Logger::println("inserting %lu byte padding at %lu", nextdef->offset - pos, pos); + addZeros(inits, pos, nextdef->offset); + } + + // do the default + Logger::println("adding default field: %s : +%u", nextdef->toChars(), nextdef->offset); + LLConstant* c = nextdef->ir.irField->constInit; + inits.push_back(c); + + // update offsets + lastoffset = nextdef->offset; + lastsize = nextdef->type->size(); + + // check if more defaults would fit + goto Lpadding2; + } + // not found, pad with zeros + else + { + Logger::println("inserting %lu byte padding at %lu", structsize - pos, pos); + addZeros(inits, pos, structsize); + lastoffset = pos; + lastsize = structsize - pos; + } + } + + assert(lastoffset+lastsize == structsize); + + // make the constant struct + LLConstant* c = LLConstantStruct::get(inits, si->ad->ir.irStruct->packed); + if (Logger::enabled()) + { + Logger::cout() << "constant struct initializer: " << *c << '\n'; + } + assert(getABITypeSize(c->getType()) == structsize); + return c; } ////////////////////////////////////////////////////////////////////////////////////////// @@ -61,17 +304,26 @@ IrField* field = vd->ir.irField; assert(field); - unsigned idx = field->index; - unsigned off = field->indexOffset; + // get the start pointer + const LLType* st = getPtrToType(DtoType(sd->type)); - const LLType* st = getPtrToType(DtoType(sd->type)); + // cast to the formal struct type src = DtoBitCast(src, st); - LLValue* val = DtoGEPi(src, 0,idx); - val = DtoBitCast(val, getPtrToType(DtoType(vd->type))); + // gep to the index + LLValue* val = DtoGEPi(src, 0, field->index); - if (off) - val = DtoGEPi1(val, off); + // do we need to offset further? (union area) + if (field->unionOffset) + { + // cast to void* + val = DtoBitCast(val, getVoidPtrType()); + // offset + val = DtoGEPi1(val, field->unionOffset); + } + + // cast it to the right type + val = DtoBitCast(val, getPtrToType(DtoType(vd->type))); if (Logger::enabled()) Logger::cout() << "value: " << *val << '\n'; @@ -79,46 +331,46 @@ return val; } -////////////////////////////////////////////////////////////////////////////////////////// - void DtoResolveStruct(StructDeclaration* sd) { + // don't do anything if already been here if (sd->ir.resolved) return; + // make sure above works :P sd->ir.resolved = true; - Logger::println("DtoResolveStruct(%s): %s", sd->toChars(), sd->loc.toChars()); + // log what we're doing + Logger::println("Resolving struct type: %s (%s)", sd->toChars(), sd->locToChars()); LOG_SCOPE; - TypeStruct* ts = (TypeStruct*)sd->type->toBasetype(); + // get the DMD TypeStruct + TypeStruct* ts = (TypeStruct*)sd->type; - // this struct is a forward declaration + // create the IrStruct + IrStruct* irstruct = new IrStruct(sd); + sd->ir.irStruct = irstruct; + + // create the type + ts->ir.type = new LLPATypeHolder(llvm::OpaqueType::get()); + + // handle forward declaration structs (opaques) // didn't even know D had those ... if (sd->sizeok != 1) { - sd->ir.irStruct = new IrStruct(ts); - ts->ir.type = new llvm::PATypeHolder(llvm::OpaqueType::get()); + // nothing more to do return; } - bool ispacked = (ts->alignsize() == 1); - - // create the IrStruct - IrStruct* irstruct = new IrStruct(ts); - sd->ir.irStruct = irstruct; + // make this struct current gIR->structs.push_back(irstruct); - // add fields - Array* fields = &sd->fields; - for (int k=0; k < fields->dim; k++) - { - VarDeclaration* v = (VarDeclaration*)fields->data[k]; - Logger::println("Adding field: %s %s", v->type->toChars(), v->toChars()); - // init fields, used to happen in VarDeclaration::toObjFile - irstruct->addField(v); - } + // get some info + bool ispacked = (ts->alignsize() == 1); + bool isunion = sd->isUnionDeclaration(); + // set irstruct info irstruct->packed = ispacked; + // defined in this module? bool thisModule = false; if (sd->getModule() == gIR->dmodule) thisModule = true; @@ -127,21 +379,29 @@ Array* arr = sd->members; for (int k=0; k < arr->dim; k++) { Dsymbol* s = (Dsymbol*)arr->data[k]; - if (FuncDeclaration* fd = s->isFuncDeclaration()) { - if (thisModule || (fd->prot() != PROTprivate)) { - fd->toObjFile(0); // TODO: multiobj - } - } - else if (s->isAttribDeclaration() || - s->isVarDeclaration() || - s->isTemplateMixin()) { - s->toObjFile(0); // TODO: multiobj - } - else { - Logger::println("Ignoring dsymbol '%s' in this->members of kind '%s'", s->toPrettyChars(), s->kind()); - } + s->toObjFile(0); } + const LLType* ST = irstruct->build(); + +#if 0 + std::cout << sd->kind() << ' ' << sd->toPrettyChars() << " type: " << *ST << '\n'; + + // add fields + for (int k=0; k < fields->dim; k++) + { + VarDeclaration* v = (VarDeclaration*)fields->data[k]; + printf(" field: %s %s\n", v->type->toChars(), v->toChars()); + printf(" index: %u offset: %u\n", v->ir.irField->index, v->ir.irField->unionOffset); + } + + unsigned llvmSize = (unsigned)getABITypeSize(ST); + unsigned dmdSize = (unsigned)sd->type->size(); + printf(" llvm size: %u dmd size: %u\n", llvmSize, dmdSize); + assert(llvmSize == dmdSize); + +#endif + /*for (int k=0; k < sd->members->dim; k++) { Dsymbol* dsym = (Dsymbol*)(sd->members->data[k]); dsym->toObjFile(); @@ -149,98 +409,13 @@ Logger::println("doing struct fields"); - const llvm::StructType* structtype = 0; - std::vector<const LLType*> fieldtypes; + // refine abstract types for stuff like: struct S{S* next;} + llvm::cast<llvm::OpaqueType>(ts->ir.type->get())->refineAbstractTypeTo(ST); + ST = ts->ir.type->get(); - if (irstruct->offsets.empty()) - { - Logger::println("has no fields"); - fieldtypes.push_back(LLType::Int8Ty); - structtype = llvm::StructType::get(fieldtypes, ispacked); - } - else - { - Logger::println("has fields"); - unsigned prevsize = (unsigned)-1; - unsigned lastoffset = (unsigned)-1; - const LLType* fieldtype = NULL; - VarDeclaration* fieldinit = NULL; - size_t fieldpad = 0; - int idx = 0; - for (IrStruct::OffsetMap::iterator i=irstruct->offsets.begin(); i!=irstruct->offsets.end(); ++i) { - // first iteration - if (lastoffset == (unsigned)-1) { - lastoffset = i->first; - assert(lastoffset == 0); - fieldtype = i->second.type; - fieldinit = i->second.var; - prevsize = fieldinit->type->size(); - i->second.var->ir.irField->index = idx; - } - // colliding offset? - else if (lastoffset == i->first) { - size_t s = i->second.var->type->size(); - if (s > prevsize) { - fieldpad += s - prevsize; - prevsize = s; - } - sd->ir.irStruct->hasUnions = true; - i->second.var->ir.irField->index = idx; - } - // intersecting offset? - else if (i->first < (lastoffset + prevsize)) { - size_t s = i->second.var->type->size(); - assert((i->first + s) <= (lastoffset + prevsize)); // this holds because all types are aligned to their size - sd->ir.irStruct->hasUnions = true; - i->second.var->ir.irField->index = idx; - i->second.var->ir.irField->indexOffset = (i->first - lastoffset) / s; - } - // fresh offset - else { - // commit the field - fieldtypes.push_back(fieldtype); - irstruct->defaultFields.push_back(fieldinit); - if (fieldpad) { - fieldtypes.push_back(llvm::ArrayType::get(LLType::Int8Ty, fieldpad)); - irstruct->defaultFields.push_back(NULL); - idx++; - } - - idx++; - - // start new - lastoffset = i->first; - fieldtype = i->second.type; - fieldinit = i->second.var; - prevsize = fieldinit->type->size(); - i->second.var->ir.irField->index = idx; - fieldpad = 0; - } - } - fieldtypes.push_back(fieldtype); - irstruct->defaultFields.push_back(fieldinit); - if (fieldpad) { - fieldtypes.push_back(llvm::ArrayType::get(LLType::Int8Ty, fieldpad)); - irstruct->defaultFields.push_back(NULL); - } - - Logger::println("creating struct type"); - structtype = llvm::StructType::get(fieldtypes, ispacked); - } - - // refine abstract types for stuff like: struct S{S* next;} - if (irstruct->recty != 0) - { - llvm::PATypeHolder& pa = irstruct->recty; - llvm::cast<llvm::OpaqueType>(pa.get())->refineAbstractTypeTo(structtype); - structtype = isaStruct(pa.get()); - } - - assert(ts->ir.type == 0); - ts->ir.type = new llvm::PATypeHolder(structtype); - + // name type if (sd->parent->isModule()) { - gIR->module->addTypeName(sd->mangle(),structtype); + gIR->module->addTypeName(sd->mangle(),ST); } gIR->structs.pop_back(); @@ -265,7 +440,7 @@ initname.append("6__initZ"); llvm::GlobalValue::LinkageTypes _linkage = DtoExternalLinkage(sd); - llvm::GlobalVariable* initvar = new llvm::GlobalVariable(ts->ir.type->get(), true, _linkage, NULL, initname, gIR->module); + llvm::GlobalVariable* initvar = new llvm::GlobalVariable(sd->ir.irStruct->initOpaque.get(), true, _linkage, NULL, initname, gIR->module); sd->ir.irStruct->init = initvar; gIR->constInitList.push_back(sd); @@ -286,59 +461,33 @@ IrStruct* irstruct = sd->ir.irStruct; gIR->structs.push_back(irstruct); - // 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; - LLConstant* finit = DtoConstFieldInitializer(so->var->loc, so->var->type, so->var->init); - so->init = finit; - so->var->ir.irField->constInit = finit; - } - const llvm::StructType* structtype = isaStruct(sd->type->ir.type->get()); - // go through the field inits and build the default initializer - std::vector<LLConstant*> fieldinits_ll; - size_t nfi = irstruct->defaultFields.size(); - for (size_t i=0; i<nfi; ++i) { - LLConstant* c; - if (irstruct->defaultFields[i] != NULL) { - c = irstruct->defaultFields[i]->ir.irField->constInit; - assert(c); - } - else { - const llvm::ArrayType* arrty = isaArray(structtype->getElementType(i)); - std::vector<LLConstant*> vals(arrty->getNumElements(), llvm::ConstantInt::get(LLType::Int8Ty, 0, false)); - c = llvm::ConstantArray::get(arrty, vals); - } - fieldinits_ll.push_back(c); + // make sure each offset knows its default initializer + Array* fields = &sd->fields; + for (int k=0; k < fields->dim; k++) + { + VarDeclaration* v = (VarDeclaration*)fields->data[k]; + LLConstant* finit = DtoConstFieldInitializer(v->loc, v->type, v->init); + v->ir.irField->constInit = finit; } - // generate the union mapper - sd->ir.irStruct->dunion = new DUnion(); // uses gIR->topstruct() - // always generate the constant initalizer - if (!sd->zeroInit) { + if (sd->zeroInit) + { + Logger::println("Zero initialized"); + irstruct->constInit = llvm::ConstantAggregateZero::get(structtype); + } + else + { Logger::println("Not zero initialized"); - #if 0 - //assert(tk == gIR->gIR->topstruct()().size()); - #ifndef LLVMD_NO_LOGGER - Logger::cout() << "struct type: " << *structtype << '\n'; - for (size_t k=0; k<fieldinits_ll.size(); ++k) { - Logger::cout() << "Type:" << '\n'; - Logger::cout() << *fieldinits_ll[k]->getType() << '\n'; - Logger::cout() << "Value:" << '\n'; - Logger::cout() << *fieldinits_ll[k] << '\n'; - } - Logger::cout() << "Initializer printed" << '\n'; - #endif - #endif - sd->ir.irStruct->constInit = llvm::ConstantStruct::get(structtype,fieldinits_ll); + + LLConstant* c = irstruct->buildDefaultConstInit(); + irstruct->constInit = c; } - else { - Logger::println("Zero initialized"); - sd->ir.irStruct->constInit = llvm::ConstantAggregateZero::get(structtype); - } + + // refine __initZ global type to the one of the initializer + llvm::cast<llvm::OpaqueType>(irstruct->initOpaque.get())->refineAbstractTypeTo(irstruct->constInit->getType()); gIR->structs.pop_back(); @@ -385,163 +534,3 @@ LLValue* val = DtoMemCmp(lhs->getRVal(), rhs->getRVal(), DtoConstSize_t(sz)); return gIR->ir->CreateICmp(cmpop, val, LLConstantInt::get(val->getType(), 0, false), "tmp"); } - -////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////// D UNION HELPER CLASS //////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////// - -DUnion::DUnion() -{ - DUnionField* f = NULL; - IrStruct* topstruct = gIR->topstruct(); - bool unions = false; - for (IrStruct::OffsetMap::iterator i=topstruct->offsets.begin(); i!=topstruct->offsets.end(); ++i) - { - unsigned o = i->first; - IrStruct::Offset* so = &i->second; - const LLType* ft = so->init->getType(); - size_t sz = getABITypeSize(ft); - if (f == NULL) { // new field - fields.push_back(DUnionField()); - f = &fields.back(); - f->size = sz; - f->offset = o; - f->init = so->init; - f->initsize = sz; - f->types.push_back(ft); - } - else if (o == f->offset) { // same offset - if (sz > f->size) - f->size = sz; - f->types.push_back(ft); - unions = true; - } - else if (o < f->offset+f->size) { - assert((o+sz) <= (f->offset+f->size)); - unions = true; - } - else { - fields.push_back(DUnionField()); - f = &fields.back(); - f->size = sz; - f->offset = o; - f->init = so->init; - f->initsize = sz; - f->types.push_back(ft); - } - } - - ispacked = topstruct->packed; - - /*{ - LOG_SCOPE; - Logger::println("******** DUnion BEGIN"); - size_t n = fields.size(); - for (size_t i=0; i<n; ++i) { - Logger::cout()<<"field #"<<i<<" offset: "<<fields[i].offset<<" size: "<<fields[i].size<<'('<<fields[i].initsize<<")\n"; - LOG_SCOPE; - size_t nt = fields[i].types.size(); - for (size_t j=0; j<nt; ++j) { - Logger::cout()<<*fields[i].types[j]<<'\n'; - } - } - Logger::println("******** DUnion END"); - }*/ -} - -static void push_nulls(size_t nbytes, std::vector<LLConstant*>& out) -{ - assert(nbytes > 0); - std::vector<LLConstant*> i(nbytes, llvm::ConstantInt::get(LLType::Int8Ty, 0, false)); - out.push_back(llvm::ConstantArray::get(llvm::ArrayType::get(LLType::Int8Ty, nbytes), i)); -} - -LLConstant* DUnion::getConst(std::vector<DUnionIdx>& in) -{ - std::sort(in.begin(), in.end()); - std::vector<LLConstant*> out; - - size_t nin = in.size(); - size_t nfields = fields.size(); - - size_t fi = 0; - size_t last = 0; - size_t ii = 0; - size_t os = 0; - - for(;;) - { - if (fi == nfields) break; - - bool nextSame = (ii+1 < nin) && (in[ii+1].idx == fi); - - if (ii < nin && fi == in[ii].idx) - { - size_t s = getABITypeSize(in[ii].c->getType()); - if (in[ii].idx == last) - { - size_t nos = in[ii].idxos * s; - if (nos && nos-os) { - assert(nos >= os); - push_nulls(nos-os, out); - } - os = nos + s; - } - else - { - os = s; - } - out.push_back(in[ii].c); - ii++; - if (!nextSame) - { - if (os < fields[fi].size) - push_nulls(fields[fi].size - os, out); - os = 0; - last = fi++; - } - continue; - } - - // default initialize if necessary - if (ii == nin || fi < in[ii].idx) - { - DUnionField& f = fields[fi]; - out.push_back(f.init); - if (f.initsize < f.size) - push_nulls(f.size - f.initsize, out); - last = fi++; - os = 0; - continue; - } - } - - std::vector<const LLType*> tys; - size_t nout = out.size(); - for (size_t i=0; i<nout; ++i) - tys.push_back(out[i]->getType()); - - const llvm::StructType* st = llvm::StructType::get(tys, ispacked); - return llvm::ConstantStruct::get(st, out); -} - - - - - - - - - - - - - - - - - - - - -