diff --git a/gen/dvalue.cpp b/gen/dvalue.cpp index cf0a4bdd4e..e0057654da 100644 --- a/gen/dvalue.cpp +++ b/gen/dvalue.cpp @@ -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); } ///////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/gen/functions.cpp b/gen/functions.cpp index 287464dc3f..5e540c2174 100644 --- a/gen/functions.cpp +++ b/gen/functions.cpp @@ -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 diff --git a/gen/llvmhelpers.cpp b/gen/llvmhelpers.cpp index 15283c7778..de90a17f6f 100644 --- a/gen/llvmhelpers.cpp +++ b/gen/llvmhelpers.cpp @@ -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(ei->exp); + if (ae->e2->op == TOKcall) { + CallExp *ce = static_cast(ae->e2); + TypeFunction *tf = static_cast(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(ei->exp); - if (ae->e2->op == TOKcall) { - CallExp *ce = static_cast(ae->e2); - TypeFunction *tf = static_cast(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() << diff --git a/gen/llvmhelpers.h b/gen/llvmhelpers.h index ab80da921b..1fd85a0735 100644 --- a/gen/llvmhelpers.h +++ b/gen/llvmhelpers.h @@ -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 //////////////////////////////////////////// diff --git a/gen/nested.cpp b/gen/nested.cpp index 1e4213abd8..7688d6dab7 100644 --- a/gen/nested.cpp +++ b/gen/nested.cpp @@ -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::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) { diff --git a/gen/toir.cpp b/gen/toir.cpp index 6064b17df9..c348dd1061 100644 --- a/gen/toir.cpp +++ b/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); diff --git a/ir/irvar.cpp b/ir/irvar.cpp index f41f6acb22..e338f12359 100644 --- a/ir/irvar.cpp +++ b/ir/irvar.cpp @@ -31,7 +31,6 @@ IrGlobal::IrGlobal(VarDeclaration* v): IrVar(v) IrLocal::IrLocal(VarDeclaration* v) : IrVar(v) { nestedIndex = -1; - byref = false; } ////////////////////////////////////////////////////////////////////////////// diff --git a/ir/irvar.h b/ir/irvar.h index 3d66cc39cd..680c112be0 100644 --- a/ir/irvar.h +++ b/ir/irvar.h @@ -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; };