mirror of
https://github.com/ldc-developers/ldc.git
synced 2025-05-04 00:55:49 +03:00

Fixed a problem in definition of ClassInfos where an invalid constant was potentially used. This needs to be looked into proper as it might happen again in the future.
531 lines
16 KiB
C++
531 lines
16 KiB
C++
#include <algorithm>
|
|
|
|
#include "gen/llvm.h"
|
|
|
|
#include "mtype.h"
|
|
#include "aggregate.h"
|
|
#include "init.h"
|
|
#include "declaration.h"
|
|
|
|
#include "gen/irstate.h"
|
|
#include "gen/tollvm.h"
|
|
#include "gen/llvmhelpers.h"
|
|
#include "gen/arrays.h"
|
|
#include "gen/logger.h"
|
|
#include "gen/structs.h"
|
|
#include "gen/dvalue.h"
|
|
|
|
#include "ir/irstruct.h"
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// 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;
|
|
|
|
// 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);
|
|
|
|
// 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);
|
|
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 not 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());
|
|
}
|
|
|
|
// 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);
|
|
if (!nextdef->ir.irField->constInit)
|
|
nextdef->ir.irField->constInit = DtoConstInitializer(nextdef->loc, nextdef->type, nextdef->init);
|
|
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);
|
|
if (!nextdef->ir.irField->constInit)
|
|
nextdef->ir.irField->constInit = DtoConstInitializer(nextdef->loc, nextdef->type, nextdef->init);
|
|
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;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
LLValue* DtoIndexStruct(LLValue* src, StructDeclaration* sd, VarDeclaration* vd)
|
|
{
|
|
Logger::println("indexing struct field %s:", vd->toPrettyChars());
|
|
LOG_SCOPE;
|
|
|
|
// vd must be a field
|
|
IrField* field = vd->ir.irField;
|
|
assert(field);
|
|
|
|
// get the start pointer
|
|
const LLType* st = getPtrToType(DtoType(sd->type));
|
|
|
|
// cast to the formal struct type
|
|
src = DtoBitCast(src, st);
|
|
|
|
// gep to the index
|
|
LLValue* val = DtoGEPi(src, 0, field->index);
|
|
|
|
// 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';
|
|
|
|
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;
|
|
|
|
// log what we're doing
|
|
Logger::println("Resolving struct type: %s (%s)", sd->toChars(), sd->locToChars());
|
|
LOG_SCOPE;
|
|
|
|
// get the DMD TypeStruct
|
|
TypeStruct* ts = (TypeStruct*)sd->type;
|
|
|
|
// 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)
|
|
{
|
|
// nothing more to do
|
|
return;
|
|
}
|
|
|
|
// make this struct current
|
|
gIR->structs.push_back(irstruct);
|
|
|
|
// 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;
|
|
|
|
// methods, fields
|
|
Array* arr = sd->members;
|
|
for (int k=0; k < arr->dim; k++) {
|
|
Dsymbol* s = (Dsymbol*)arr->data[k];
|
|
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();
|
|
}*/
|
|
|
|
Logger::println("doing struct fields");
|
|
|
|
// 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();
|
|
|
|
// name type
|
|
if (sd->parent->isModule()) {
|
|
gIR->module->addTypeName(sd->mangle(),ST);
|
|
}
|
|
|
|
gIR->structs.pop_back();
|
|
|
|
gIR->declareList.push_back(sd);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void DtoDeclareStruct(StructDeclaration* sd)
|
|
{
|
|
if (sd->ir.declared) return;
|
|
sd->ir.declared = true;
|
|
|
|
Logger::println("DtoDeclareStruct(%s): %s", sd->toChars(), sd->loc.toChars());
|
|
LOG_SCOPE;
|
|
|
|
TypeStruct* ts = (TypeStruct*)sd->type->toBasetype();
|
|
|
|
std::string initname("_D");
|
|
initname.append(sd->mangle());
|
|
initname.append("6__initZ");
|
|
|
|
llvm::GlobalValue::LinkageTypes _linkage = DtoExternalLinkage(sd);
|
|
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);
|
|
if (DtoIsTemplateInstance(sd) || sd->getModule() == gIR->dmodule)
|
|
gIR->defineList.push_back(sd);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void DtoConstInitStruct(StructDeclaration* sd)
|
|
{
|
|
if (sd->ir.initialized) return;
|
|
sd->ir.initialized = true;
|
|
|
|
Logger::println("DtoConstInitStruct(%s): %s", sd->toChars(), sd->loc.toChars());
|
|
LOG_SCOPE;
|
|
|
|
IrStruct* irstruct = sd->ir.irStruct;
|
|
gIR->structs.push_back(irstruct);
|
|
|
|
const llvm::StructType* structtype = isaStruct(sd->type->ir.type->get());
|
|
|
|
// always generate the constant initalizer
|
|
assert(!irstruct->constInit);
|
|
if (sd->zeroInit)
|
|
{
|
|
Logger::println("Zero initialized");
|
|
irstruct->constInit = llvm::ConstantAggregateZero::get(structtype);
|
|
}
|
|
else
|
|
{
|
|
Logger::println("Not zero initialized");
|
|
|
|
LLConstant* c = irstruct->buildDefaultConstInit();
|
|
irstruct->constInit = c;
|
|
}
|
|
|
|
// 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();
|
|
|
|
// emit typeinfo
|
|
if (sd->getModule() == gIR->dmodule && sd->llvmInternal != LLVMno_typeinfo)
|
|
DtoTypeInfoOf(sd->type, false);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void DtoDefineStruct(StructDeclaration* sd)
|
|
{
|
|
if (sd->ir.defined) return;
|
|
sd->ir.defined = true;
|
|
|
|
Logger::println("DtoDefineStruct(%s): %s", sd->toChars(), sd->loc.toChars());
|
|
LOG_SCOPE;
|
|
|
|
assert(sd->type->ty == Tstruct);
|
|
TypeStruct* ts = (TypeStruct*)sd->type;
|
|
sd->ir.irStruct->init->setInitializer(sd->ir.irStruct->constInit);
|
|
|
|
sd->ir.DModule = gIR->dmodule;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
//////////////////////////// D STRUCT UTILITIES ////////////////////////////////////
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
LLValue* DtoStructEquals(TOK op, DValue* lhs, DValue* rhs)
|
|
{
|
|
Type* t = lhs->getType()->toBasetype();
|
|
assert(t->ty == Tstruct);
|
|
|
|
// set predicate
|
|
llvm::ICmpInst::Predicate cmpop;
|
|
if (op == TOKequal || op == TOKidentity)
|
|
cmpop = llvm::ICmpInst::ICMP_EQ;
|
|
else
|
|
cmpop = llvm::ICmpInst::ICMP_NE;
|
|
|
|
// call memcmp
|
|
size_t sz = getABITypeSize(DtoType(t));
|
|
LLValue* val = DtoMemCmp(lhs->getRVal(), rhs->getRVal(), DtoConstSize_t(sz));
|
|
return gIR->ir->CreateICmp(cmpop, val, LLConstantInt::get(val->getType(), 0, false), "tmp");
|
|
}
|