Permalink
Browse files

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'.
  • Loading branch information...
1 parent 6b1b84a commit ee4285f93455f33fcaf558775d9f996ed7784fd9 @klickverbot klickverbot committed Sep 3, 2012
Showing with 173 additions and 172 deletions.
  1. +11 −2 gen/dvalue.cpp
  2. +1 −2 gen/functions.cpp
  3. +112 −116 gen/llvmhelpers.cpp
  4. +9 −0 gen/llvmhelpers.h
  5. +14 −49 gen/nested.cpp
  6. +24 −0 gen/toir.cpp
  7. +0 −1 ir/irvar.cpp
  8. +2 −2 ir/irvar.h
View
@@ -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,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);
}
/////////////////////////////////////////////////////////////////////////////////////////////////
View
@@ -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
View
@@ -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() <<
View
@@ -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
////////////////////////////////////////////
Oops, something went wrong.

0 comments on commit ee4285f

Please sign in to comment.