mirror of
https://github.com/ldc-developers/ldc.git
synced 2025-05-02 16:11:08 +03:00
Properly handle DMD-internal "reference variables".
Previously, we just had a hack to make ref foreach statements work. This commit enables them to work in other cases as well, like the implicit __result variable for functions with out-contracts (which is such a magic ref variable for ref-returning functions). Fixes DMD testcase 'testcontracts'.
This commit is contained in:
parent
6b1b84a28d
commit
ee4285f934
8 changed files with 173 additions and 172 deletions
|
@ -16,6 +16,8 @@ DVarValue::DVarValue(Type* t, VarDeclaration* vd, LLValue* llvmValue)
|
|||
: DValue(t), var(vd), val(llvmValue)
|
||||
{
|
||||
assert(isaPointer(llvmValue));
|
||||
assert(!isSpecialRefVar(vd) ||
|
||||
isaPointer(isaPointer(llvmValue)->getElementType()));
|
||||
}
|
||||
|
||||
DVarValue::DVarValue(Type* t, LLValue* llvmValue)
|
||||
|
@ -27,6 +29,8 @@ DVarValue::DVarValue(Type* t, LLValue* llvmValue)
|
|||
LLValue* DVarValue::getLVal()
|
||||
{
|
||||
assert(val);
|
||||
if (var && isSpecialRefVar(var))
|
||||
return DtoLoad(val);
|
||||
return val;
|
||||
}
|
||||
|
||||
|
@ -34,9 +38,14 @@ LLValue* DVarValue::getRVal()
|
|||
{
|
||||
assert(val);
|
||||
Type* bt = type->toBasetype();
|
||||
|
||||
LLValue* tmp = val;
|
||||
if (var && isSpecialRefVar(var))
|
||||
tmp = DtoLoad(tmp);
|
||||
|
||||
if (DtoIsPassedByRef(bt))
|
||||
return val;
|
||||
return DtoLoad(val);
|
||||
return tmp;
|
||||
return DtoLoad(tmp);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -815,8 +815,7 @@ void DtoDefineFunction(FuncDeclaration* fd)
|
|||
{
|
||||
DtoNestedInit(fd->vresult);
|
||||
} else if (fd->vresult) {
|
||||
fd->vresult->ir.irLocal = new IrLocal(fd->vresult);
|
||||
fd->vresult->ir.irLocal->value = DtoAlloca(fd->vresult->type, fd->vresult->toChars());
|
||||
DtoVarDeclaration(fd->vresult);
|
||||
}
|
||||
|
||||
// copy _argptr and _arguments to a memory location
|
||||
|
|
|
@ -1013,6 +1013,110 @@ void DtoConstInitGlobal(VarDeclaration* vd)
|
|||
/*////////////////////////////////////////////////////////////////////////////////////////
|
||||
// DECLARATION EXP HELPER
|
||||
////////////////////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
// TODO: Merge with DtoRawVarDeclaration!
|
||||
void DtoVarDeclaration(VarDeclaration* vd)
|
||||
{
|
||||
assert(!vd->isDataseg() && "Statics/globals are handled in DtoDeclarationExp.");
|
||||
assert(!vd->aliassym && "Aliases are handled in DtoDeclarationExp.");
|
||||
|
||||
Logger::println("vdtype = %s", vd->type->toChars());
|
||||
|
||||
#if DMDV2
|
||||
if (vd->nestedrefs.dim)
|
||||
#else
|
||||
if (vd->nestedref)
|
||||
#endif
|
||||
{
|
||||
Logger::println("has nestedref set (referenced by nested function/delegate)");
|
||||
assert(vd->ir.irLocal);
|
||||
DtoNestedInit(vd);
|
||||
}
|
||||
else if(vd->ir.irLocal)
|
||||
{
|
||||
// Nothing to do if it has already been allocated.
|
||||
}
|
||||
#if DMDV2
|
||||
/* Named Return Value Optimization (NRVO):
|
||||
T f(){
|
||||
T ret; // &ret == hidden pointer
|
||||
ret = ...
|
||||
return ret; // NRVO.
|
||||
}
|
||||
*/
|
||||
else if (gIR->func()->retArg && gIR->func()->decl->nrvo_can && gIR->func()->decl->nrvo_var == vd) {
|
||||
assert(!isSpecialRefVar(vd) && "Can this happen?");
|
||||
vd->ir.irLocal = new IrLocal(vd);
|
||||
vd->ir.irLocal->value = gIR->func()->retArg;
|
||||
}
|
||||
#endif
|
||||
// normal stack variable, allocate storage on the stack if it has not already been done
|
||||
else {
|
||||
vd->ir.irLocal = new IrLocal(vd);
|
||||
|
||||
#if DMDV2
|
||||
/* NRVO again:
|
||||
T t = f(); // t's memory address is taken hidden pointer
|
||||
*/
|
||||
ExpInitializer *ei = 0;
|
||||
if (vd->type->toBasetype()->ty == Tstruct && vd->init &&
|
||||
!!(ei = vd->init->isExpInitializer()))
|
||||
{
|
||||
if (ei->exp->op == TOKconstruct) {
|
||||
AssignExp *ae = static_cast<AssignExp*>(ei->exp);
|
||||
if (ae->e2->op == TOKcall) {
|
||||
CallExp *ce = static_cast<CallExp *>(ae->e2);
|
||||
TypeFunction *tf = static_cast<TypeFunction *>(ce->e1->type->toBasetype());
|
||||
if (tf->ty == Tfunction && tf->fty.arg_sret) {
|
||||
LLValue* const val = ce->toElem(gIR)->getLVal();
|
||||
if (isSpecialRefVar(vd))
|
||||
{
|
||||
vd->ir.irLocal->value = DtoAlloca(
|
||||
vd->type->pointerTo(), vd->toChars());
|
||||
DtoStore(val, vd->ir.irLocal->value);
|
||||
}
|
||||
else
|
||||
{
|
||||
vd->ir.irLocal->value = val;
|
||||
}
|
||||
goto Lexit;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
Type* type = isSpecialRefVar(vd) ? vd->type->pointerTo() : vd->type;
|
||||
LLType* lltype = DtoType(type);
|
||||
|
||||
llvm::Value* allocainst;
|
||||
if(gTargetData->getTypeSizeInBits(lltype) == 0)
|
||||
allocainst = llvm::ConstantPointerNull::get(getPtrToType(lltype));
|
||||
else
|
||||
allocainst = DtoAlloca(type, vd->toChars());
|
||||
|
||||
vd->ir.irLocal->value = allocainst;
|
||||
|
||||
DtoDwarfLocalVariable(allocainst, vd);
|
||||
}
|
||||
|
||||
if (Logger::enabled())
|
||||
Logger::cout() << "llvm value for decl: " << *vd->ir.irLocal->value << '\n';
|
||||
|
||||
DtoInitializer(vd->ir.irLocal->value, vd->init); // TODO: Remove altogether?
|
||||
|
||||
#if DMDV2
|
||||
Lexit:
|
||||
/* Mark the point of construction of a variable that needs to be destructed.
|
||||
*/
|
||||
if (vd->edtor && !vd->noscope)
|
||||
{
|
||||
// Put vd on list of things needing destruction
|
||||
gIR->varsInScope().push_back(vd);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
DValue* DtoDeclarationExp(Dsymbol* declaration)
|
||||
{
|
||||
Logger::print("DtoDeclarationExp: %s\n", declaration->toChars());
|
||||
|
@ -1036,123 +1140,8 @@ DValue* DtoDeclarationExp(Dsymbol* declaration)
|
|||
}
|
||||
else
|
||||
{
|
||||
if (global.params.llvmAnnotate)
|
||||
DtoAnnotation(declaration->toChars());
|
||||
|
||||
Logger::println("vdtype = %s", vd->type->toChars());
|
||||
|
||||
// ref vardecls are generated when DMD lowers foreach to a for statement,
|
||||
// and this is a hack to support them for this case only
|
||||
if(vd->isRef())
|
||||
{
|
||||
if (!vd->ir.irLocal)
|
||||
vd->ir.irLocal = new IrLocal(vd);
|
||||
|
||||
ExpInitializer* ex = vd->init->isExpInitializer();
|
||||
assert(ex && "ref vars must have expression initializer");
|
||||
assert(ex->exp);
|
||||
AssignExp* as = ex->exp->isAssignExp();
|
||||
assert(as && "ref vars must be initialized by an assign exp");
|
||||
DValue *val = as->e2->toElem(gIR);
|
||||
if (val->isLVal())
|
||||
{
|
||||
vd->ir.irLocal->value = val->getLVal();
|
||||
}
|
||||
else
|
||||
{
|
||||
LLValue *newVal = DtoAlloca(val->type);
|
||||
DtoStore(val->getRVal(), newVal);
|
||||
vd->ir.irLocal->value = newVal;
|
||||
}
|
||||
}
|
||||
|
||||
// referenced by nested delegate?
|
||||
#if DMDV2
|
||||
if (vd->nestedrefs.dim) {
|
||||
#else
|
||||
if (vd->nestedref) {
|
||||
#endif
|
||||
Logger::println("has nestedref set");
|
||||
assert(vd->ir.irLocal);
|
||||
DtoNestedInit(vd);
|
||||
// is it already allocated?
|
||||
} else if(vd->ir.irLocal) {
|
||||
// nothing to do...
|
||||
}
|
||||
#if DMDV2
|
||||
/* Named Return Value Optimization (NRVO):
|
||||
T f(){
|
||||
T ret; // &ret == hidden pointer
|
||||
ret = ...
|
||||
return ret; // NRVO.
|
||||
}
|
||||
*/
|
||||
else if (gIR->func()->retArg && gIR->func()->decl->nrvo_can && gIR->func()->decl->nrvo_var == vd) {
|
||||
vd->ir.irLocal = new IrLocal(vd);
|
||||
vd->ir.irLocal->value = gIR->func()->retArg;
|
||||
}
|
||||
#endif
|
||||
// normal stack variable, allocate storage on the stack if it has not already been done
|
||||
else if(!vd->isRef()) {
|
||||
vd->ir.irLocal = new IrLocal(vd);
|
||||
|
||||
#if DMDV2
|
||||
/* NRVO again:
|
||||
T t = f(); // t's memory address is taken hidden pointer
|
||||
*/
|
||||
ExpInitializer *ei = 0;
|
||||
if (vd->type->toBasetype()->ty == Tstruct && vd->init &&
|
||||
!!(ei = vd->init->isExpInitializer()))
|
||||
{
|
||||
if (ei->exp->op == TOKconstruct) {
|
||||
AssignExp *ae = static_cast<AssignExp*>(ei->exp);
|
||||
if (ae->e2->op == TOKcall) {
|
||||
CallExp *ce = static_cast<CallExp *>(ae->e2);
|
||||
TypeFunction *tf = static_cast<TypeFunction *>(ce->e1->type->toBasetype());
|
||||
if (tf->ty == Tfunction && tf->fty.arg_sret) {
|
||||
vd->ir.irLocal->value = ce->toElem(gIR)->getLVal();
|
||||
goto Lexit;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
LLType* lltype = DtoType(vd->type);
|
||||
|
||||
llvm::Value* allocainst;
|
||||
if(gTargetData->getTypeSizeInBits(lltype) == 0)
|
||||
allocainst = llvm::ConstantPointerNull::get(getPtrToType(lltype));
|
||||
else
|
||||
allocainst = DtoAlloca(vd->type, vd->toChars());
|
||||
|
||||
//allocainst->setAlignment(vd->type->alignsize()); // TODO
|
||||
vd->ir.irLocal->value = allocainst;
|
||||
|
||||
DtoDwarfLocalVariable(allocainst, vd);
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(vd->ir.irLocal->value);
|
||||
}
|
||||
|
||||
if (Logger::enabled())
|
||||
Logger::cout() << "llvm value for decl: " << *vd->ir.irLocal->value << '\n';
|
||||
if (!vd->isRef())
|
||||
DtoInitializer(vd->ir.irLocal->value, vd->init); // TODO: Remove altogether?
|
||||
|
||||
#if DMDV2
|
||||
Lexit:
|
||||
/* Mark the point of construction of a variable that needs to be destructed.
|
||||
*/
|
||||
if (vd->edtor && !vd->noscope)
|
||||
{
|
||||
// Put vd on list of things needing destruction
|
||||
gIR->varsInScope().push_back(vd);
|
||||
}
|
||||
#endif
|
||||
DtoVarDeclaration(vd);
|
||||
}
|
||||
|
||||
return new DVarValue(vd->type, vd, vd->ir.getIrValue());
|
||||
}
|
||||
// struct declaration
|
||||
|
@ -1921,6 +1910,13 @@ void callPostblit(Loc &loc, Expression *exp, LLValue *val)
|
|||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool isSpecialRefVar(VarDeclaration* vd)
|
||||
{
|
||||
return (vd->storage_class & STCref) && (vd->storage_class & STCforeach);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void printLabelName(std::ostream& target, const char* func_mangle, const char* label_name)
|
||||
{
|
||||
target << gTargetMachine->getMCAsmInfo()->getPrivateGlobalPrefix() <<
|
||||
|
|
|
@ -98,6 +98,7 @@ void DtoResolveDsymbol(Dsymbol* dsym);
|
|||
void DtoConstInitGlobal(VarDeclaration* vd);
|
||||
|
||||
// declaration inside a declarationexp
|
||||
void DtoVarDeclaration(VarDeclaration* var);
|
||||
DValue* DtoDeclarationExp(Dsymbol* declaration);
|
||||
LLValue* DtoRawVarDeclaration(VarDeclaration* var, LLValue* addr = 0);
|
||||
|
||||
|
@ -158,6 +159,14 @@ LLValue* makeLValue(Loc& loc, DValue* value);
|
|||
void callPostblit(Loc &loc, Expression *exp, LLValue *val);
|
||||
#endif
|
||||
|
||||
/// Returns whether the given variable is a DMD-internal "ref variable".
|
||||
///
|
||||
/// D doesn't have reference variables (the ref keyword is only usable in
|
||||
/// function signatures and foreach headers), but the DMD frontend internally
|
||||
/// creates them in cases like lowering a ref foreach to a for loop or the
|
||||
/// implicit __result variable for ref-return functions with out contracts.
|
||||
bool isSpecialRefVar(VarDeclaration* vd);
|
||||
|
||||
////////////////////////////////////////////
|
||||
// gen/tocall.cpp stuff below
|
||||
////////////////////////////////////////////
|
||||
|
|
|
@ -211,7 +211,7 @@ DValue* DtoNestedVariable(Loc loc, Type* astype, VarDeclaration* vd, bool byref)
|
|||
val = DtoGEPi(val, 0, vd->ir.irLocal->nestedIndex, vd->toChars());
|
||||
Logger::cout() << "Addr: " << *val << '\n';
|
||||
Logger::cout() << "of type: " << *val->getType() << '\n';
|
||||
if (vd->ir.irLocal->byref || byref) {
|
||||
if (byref || (vd->isParameter() && vd->ir.irParam->arg->byref)) {
|
||||
val = DtoAlignedLoad(val);
|
||||
//dwarfOpDeref(dwarfAddr);
|
||||
Logger::cout() << "Was byref, now: " << *val << '\n';
|
||||
|
@ -251,27 +251,7 @@ void DtoNestedInit(VarDeclaration* vd)
|
|||
DtoAlignedStore(val, gep);
|
||||
}
|
||||
else if (nestedCtx == NCHybrid) {
|
||||
assert(vd->ir.irLocal->value && "Nested variable without storage?");
|
||||
|
||||
if (!vd->isParameter() && (vd->isRef() || vd->isOut())) {
|
||||
unsigned vardepth = vd->ir.irLocal->nestedDepth;
|
||||
|
||||
LLValue* val = NULL;
|
||||
// Retrieve frame pointer
|
||||
if (vardepth == irfunc->depth) {
|
||||
val = nestedVar;
|
||||
} else {
|
||||
FuncDeclaration *parentfunc = getParentFunc(vd, true);
|
||||
assert(parentfunc && "No parent function for nested variable?");
|
||||
|
||||
val = DtoGEPi(nestedVar, 0, vardepth);
|
||||
val = DtoAlignedLoad(val, (std::string(".frame.") + parentfunc->toChars()).c_str());
|
||||
}
|
||||
val = DtoGEPi(val, 0, vd->ir.irLocal->nestedIndex, vd->toChars());
|
||||
storeVariable(vd, val);
|
||||
} else {
|
||||
// Already initialized in DtoCreateNestedContext
|
||||
}
|
||||
// Already initialized in DtoCreateNestedContext.
|
||||
}
|
||||
else {
|
||||
assert(0 && "Not implemented yet");
|
||||
|
@ -492,18 +472,13 @@ static void DtoCreateNestedContextType(FuncDeclaration* fd) {
|
|||
type = type->getContainedType(0);
|
||||
else
|
||||
type = DtoType(vd->type);
|
||||
vd->ir.irParam->byref = false;
|
||||
} else {
|
||||
vd->ir.irParam->byref = true;
|
||||
}
|
||||
types.push_back(type);
|
||||
} else if (vd->isRef() || vd->isOut()) {
|
||||
// Foreach variables can also be by reference, for instance.
|
||||
} else if (isSpecialRefVar(vd)) {
|
||||
types.push_back(DtoType(vd->type->pointerTo()));
|
||||
vd->ir.irLocal->byref = true;
|
||||
} else {
|
||||
types.push_back(DtoType(vd->type));
|
||||
vd->ir.irLocal->byref = false;
|
||||
}
|
||||
if (Logger::enabled()) {
|
||||
Logger::println("Nested var: %s", vd->toChars());
|
||||
|
@ -692,36 +667,26 @@ void DtoCreateNestedContext(FuncDeclaration* fd) {
|
|||
if (vd->isParameter()) {
|
||||
Logger::println("nested param: %s", vd->toChars());
|
||||
LOG_SCOPE
|
||||
LLValue* value = vd->ir.irLocal->value;
|
||||
if (llvm::isa<llvm::AllocaInst>(llvm::GetUnderlyingObject(value))) {
|
||||
IrParameter* parm = vd->ir.irParam;
|
||||
|
||||
if (parm->arg->byref)
|
||||
{
|
||||
storeVariable(vd, gep);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger::println("Copying to nested frame");
|
||||
// The parameter value is an alloca'd stack slot.
|
||||
// Copy to the nesting frame and leave the alloca for
|
||||
// the optimizers to clean up.
|
||||
assert(!vd->ir.irLocal->byref);
|
||||
DtoStore(DtoLoad(value), gep);
|
||||
gep->takeName(value);
|
||||
vd->ir.irLocal->value = gep;
|
||||
} else {
|
||||
Logger::println("Adding pointer to nested frame");
|
||||
// The parameter value is something else, such as a
|
||||
// passed-in pointer (for 'ref' or 'out' parameters) or
|
||||
// a pointer arg with byval attribute.
|
||||
// Store the address into the frame.
|
||||
assert(vd->ir.irLocal->byref);
|
||||
storeVariable(vd, gep);
|
||||
DtoStore(DtoLoad(parm->value), gep);
|
||||
gep->takeName(parm->value);
|
||||
parm->value = gep;
|
||||
}
|
||||
} else if (vd->isRef() || vd->isOut()) {
|
||||
// This slot is initialized in DtoNestedInit, to handle things like byref foreach variables
|
||||
// which move around in memory.
|
||||
assert(vd->ir.irLocal->byref);
|
||||
} else {
|
||||
Logger::println("nested var: %s", vd->toChars());
|
||||
if (vd->ir.irLocal->value)
|
||||
Logger::cout() << "Pre-existing value: " << *vd->ir.irLocal->value << '\n';
|
||||
assert(!vd->ir.irLocal->value);
|
||||
vd->ir.irLocal->value = gep;
|
||||
assert(!vd->ir.irLocal->byref);
|
||||
}
|
||||
|
||||
if (global.params.symdebug) {
|
||||
|
|
24
gen/toir.cpp
24
gen/toir.cpp
|
@ -583,6 +583,30 @@ DValue* AssignExp::toElem(IRState* p)
|
|||
return newlen;
|
||||
}
|
||||
|
||||
// Can't just override ConstructExp::toElem because not all TOKconstruct
|
||||
// operations are actually instances of ConstructExp... Long live the DMD
|
||||
// coding style!
|
||||
if (op == TOKconstruct)
|
||||
{
|
||||
if (e1->op == TOKvar)
|
||||
{
|
||||
VarExp* ve = (VarExp*)e1;
|
||||
if (ve->var->storage_class & STCref)
|
||||
{
|
||||
// Note that the variable value is accessed directly (instead
|
||||
// of via getLValue(), which would perform a load from the
|
||||
// uninitialized location), and that rhs is stored as an l-value!
|
||||
|
||||
IrLocal* const local = ve->var->ir.irLocal;
|
||||
assert(local && "ref var must be local and already initialized");
|
||||
|
||||
DValue* rhs = e2->toElem(p);
|
||||
DtoStore(rhs->getLVal(), local->value);
|
||||
return rhs;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Logger::println("performing normal assignment");
|
||||
|
||||
DValue* l = e1->toElem(p);
|
||||
|
|
|
@ -31,7 +31,6 @@ IrGlobal::IrGlobal(VarDeclaration* v): IrVar(v)
|
|||
IrLocal::IrLocal(VarDeclaration* v) : IrVar(v)
|
||||
{
|
||||
nestedIndex = -1;
|
||||
byref = false;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -28,8 +28,8 @@ struct IrLocal : IrVar
|
|||
{
|
||||
IrLocal(VarDeclaration* v);
|
||||
|
||||
bool byref; // Not used for -nested-ctx=array
|
||||
int nestedDepth; // ditto
|
||||
// Used for hybrid nested context creation.
|
||||
int nestedDepth;
|
||||
int nestedIndex;
|
||||
};
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue