Skip to content

Commit

Permalink
Properly handle DMD-internal "reference variables".
Browse files Browse the repository at this point in the history
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'.
  • Loading branch information
dnadlinger committed Sep 7, 2012
1 parent 6b1b84a commit ee4285f
Show file tree
Hide file tree
Showing 8 changed files with 173 additions and 172 deletions.
13 changes: 11 additions & 2 deletions gen/dvalue.cpp
Expand Up @@ -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)
Expand All @@ -27,16 +29,23 @@ DVarValue::DVarValue(Type* t, LLValue* llvmValue)
LLValue* DVarValue::getLVal()
{
assert(val);
if (var && isSpecialRefVar(var))
return DtoLoad(val);
return val;
}

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);
}

/////////////////////////////////////////////////////////////////////////////////////////////////
Expand Down
3 changes: 1 addition & 2 deletions gen/functions.cpp
Expand Up @@ -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
Expand Down
228 changes: 112 additions & 116 deletions gen/llvmhelpers.cpp
Expand Up @@ -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());
Expand All @@ -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
Expand Down Expand Up @@ -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() <<
Expand Down
9 changes: 9 additions & 0 deletions gen/llvmhelpers.h
Expand Up @@ -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);

Expand Down Expand Up @@ -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
////////////////////////////////////////////
Expand Down

0 comments on commit ee4285f

Please sign in to comment.