# HG changeset patch # User Frits van Bommel # Date 1239560580 -7200 # Node ID 8699c450a1a0eb292a41cf0fb618f104c6bbcbf5 # Parent 2a37f4745ddd2b0cae97dd48c72a571080c1b813 Implement -nested-ctx=hybrid diff -r 2a37f4745ddd -r 8699c450a1a0 gen/functions.cpp --- a/gen/functions.cpp Sun Apr 12 16:22:21 2009 +0200 +++ b/gen/functions.cpp Sun Apr 12 20:23:00 2009 +0200 @@ -673,13 +673,6 @@ // debug info - after all allocas, but before any llvm.dbg.declare etc if (global.params.symdebug) DtoDwarfFuncStart(fd); - // need result variable? - if (fd->vresult) { - Logger::println("vresult value"); - fd->vresult->ir.irLocal = new IrLocal(fd->vresult); - fd->vresult->ir.irLocal->value = DtoAlloca(DtoType(fd->vresult->type), "function_vresult"); - } - // this hack makes sure the frame pointer elimination optimization is disabled. // this this eliminates a bunch of inline asm related issues. if (fd->inlineAsm) @@ -778,6 +771,18 @@ DtoCreateNestedContext(fd); +#if DMDV2 + if (fd->vresult && fd->vresult->nestedrefs.dim) +#else + if (fd->vresult && fd->vresult->nestedref) +#endif + { + DtoNestedInit(fd->vresult); + } else if (fd->vresult) { + fd->vresult->ir.irLocal = new IrLocal(fd->vresult); + fd->vresult->ir.irLocal->value = DtoAlloca(DtoType(fd->vresult->type), fd->vresult->toChars()); + } + // copy _argptr and _arguments to a memory location if (f->linkage == LINKd && f->varargs == 1) { diff -r 2a37f4745ddd -r 8699c450a1a0 gen/nested.cpp --- a/gen/nested.cpp Sun Apr 12 16:22:21 2009 +0200 +++ b/gen/nested.cpp Sun Apr 12 20:23:00 2009 +0200 @@ -22,12 +22,12 @@ // TODO: Functions without any variables accessed by nested functions, but // with a parent whose variables are accessed, can use the parent's // context. + // NOTE: This is what DMD seems to do. NCStruct, - /// Context is an array of pointers to nested contexts. Each function with variables - /// accessed by nested functions puts them in a struct, and appends a pointer to that - /// struct to the array. - // FIXME: implement + /// Context is a list of pointers to structs. Each function with variables + /// accessed by nested functions puts them in a struct, and appends a + /// pointer to that struct to it's local copy of the list. NCHybrid }; @@ -37,9 +37,9 @@ cl::values( clEnumValN(NCArray, "array", "Array of pointers to variables (including multi-level)"), //clEnumValN(NCStruct, "struct", "Struct of variables (with multi-level via linked list)"), - //clEnumValN(NCHybrid, "hybrid", "Array of pointers to structs of variables"), + clEnumValN(NCHybrid, "hybrid", "List of pointers to structs of variables, one per level."), clEnumValEnd), - cl::init(NCArray)); + cl::init(NCHybrid)); /****************************************************************************************/ @@ -47,8 +47,20 @@ // NESTED VARIABLE HELPERS ////////////////////////////////////////////////////////////////////////////////////////*/ +static FuncDeclaration* getParentFunc(Dsymbol* sym) { + Dsymbol* parent = sym->parent; + assert(parent); + while (parent && !parent->isFuncDeclaration()) + parent = parent->parent; + + return (parent ? parent->isFuncDeclaration() : NULL); +} + DValue* DtoNestedVariable(Loc loc, Type* astype, VarDeclaration* vd) { + Logger::println("DtoNestedVariable for %s @ %s", vd->toChars(), loc.toChars()); + LOG_SCOPE; + //////////////////////////////////// // Locate context value @@ -89,6 +101,18 @@ val = DtoBitCast(val, vd->ir.irLocal->value->getType(), vd->toChars()); return new DVarValue(astype, vd, val); } + else if (nestedCtx == NCHybrid) { + FuncDeclaration *parentfunc = getParentFunc(vd); + assert(parentfunc && "No parent function for nested variable?"); + + LLValue* val = DtoBitCast(ctx, LLPointerType::getUnqual(parentfunc->ir.irFunc->framesType)); + val = DtoGEPi(val, 0, vd->ir.irLocal->nestedDepth); + val = DtoLoad(val, (std::string(".frame.") + parentfunc->toChars()).c_str()); + val = DtoGEPi(val, 0, vd->ir.irLocal->nestedIndex, vd->toChars()); + if (vd->ir.irLocal->byref) + val = DtoLoad(val); + return new DVarValue(astype, vd, val); + } else { assert(0 && "Not implemented yet"); } @@ -96,6 +120,11 @@ void DtoNestedInit(VarDeclaration* vd) { + Logger::println("DtoNestedInit for %s", vd->toChars()); + LOG_SCOPE + + LLValue* nestedVar = gIR->func()->decl->ir.irFunc->nestedVar; + if (nestedCtx == NCArray) { // alloca as usual if no value already if (!vd->ir.irLocal->value) @@ -103,13 +132,29 @@ // store the address into the nested vars array assert(vd->ir.irLocal->nestedIndex >= 0); - LLValue* gep = DtoGEPi(gIR->func()->decl->ir.irFunc->nestedVar, 0, vd->ir.irLocal->nestedIndex); + LLValue* gep = DtoGEPi(nestedVar, 0, vd->ir.irLocal->nestedIndex); assert(isaPointer(vd->ir.irLocal->value)); LLValue* val = DtoBitCast(vd->ir.irLocal->value, getVoidPtrType()); DtoStore(val, gep); } + else if (nestedCtx == NCHybrid) { + assert(vd->ir.irLocal->value && "Nested variable without storage?"); + if (!vd->isParameter() && (vd->isRef() || vd->isOut())) { + Logger::println("Initializing non-parameter byref value"); + LLValue* framep = DtoGEPi(nestedVar, 0, vd->ir.irLocal->nestedDepth); + + FuncDeclaration *parentfunc = getParentFunc(vd); + assert(parentfunc && "No parent function for nested variable?"); + LLValue* frame = DtoLoad(framep, (std::string(".frame.") + parentfunc->toChars()).c_str()); + + LLValue* slot = DtoGEPi(frame, 0, vd->ir.irLocal->nestedIndex); + DtoStore(vd->ir.irLocal->value, slot); + } else { + // Already initialized in DtoCreateNestedContext + } + } else { assert(0 && "Not implemented yet"); } @@ -145,6 +190,9 @@ } void DtoCreateNestedContext(FuncDeclaration* fd) { + Logger::println("DtoCreateNestedContext for %s", fd->toChars()); + LOG_SCOPE + if (nestedCtx == NCArray) { // construct nested variables array if (!fd->nestedVars.empty()) @@ -228,18 +276,130 @@ vd->ir.irLocal->nestedIndex = idx++; } - - // fixup nested result variable - #if DMDV2 - if (fd->vresult && fd->vresult->nestedrefs.dim) - #else - if (fd->vresult && fd->vresult->nestedref) - #endif + } + } + else if (nestedCtx == NCHybrid) { + // construct nested variables array + if (!fd->nestedVars.empty()) + { + Logger::println("has nested frame"); + // start with adding all enclosing parent frames until a static parent is reached + typedef std::vector TypeVec; + TypeVec frametypes; + if (!fd->isStatic()) { + Dsymbol* par = fd->toParent2(); + while (par) { + if (FuncDeclaration* parfd = par->isFuncDeclaration()) { + // skip functions without nested parameters + if (!parfd->nestedVars.empty()) { + // Copy the types of parent function frames. + const LLStructType* parft = parfd->ir.irFunc->framesType; + frametypes.insert(frametypes.begin(), parft->element_begin(), parft->element_end()); + break; // That's all the info needed. + } + } else if (ClassDeclaration* parcd = par->isClassDeclaration()) { + // skip + } else { + break; + } + par = par->toParent2(); + } + } + unsigned depth = frametypes.size(); + + // Construct a struct for the direct nested variables of this function, and update their indices to match. + // TODO: optimize ordering for minimal space usage? + TypeVec types; + for (std::set::iterator i=fd->nestedVars.begin(); i!=fd->nestedVars.end(); ++i) { - Logger::println("nested vresult value: %s", fd->vresult->toChars()); - LLValue* gep = DtoGEPi(nestedVars, 0, fd->vresult->ir.irLocal->nestedIndex); - LLValue* val = DtoBitCast(fd->vresult->ir.irLocal->value, getVoidPtrType()); - DtoStore(val, gep); + VarDeclaration* vd = *i; + if (!vd->ir.irLocal) + vd->ir.irLocal = new IrLocal(vd); + + vd->ir.irLocal->nestedDepth = depth; + vd->ir.irLocal->nestedIndex = types.size(); + if (vd->isParameter()) { + // Parameters already have storage associated with them (to handle byref etc.), + // so handle specially for now by storing a pointer instead of a value. + assert(vd->ir.irLocal->value); + // FIXME: don't do this for normal parameters? + types.push_back(vd->ir.irLocal->value->getType()); + } else if (vd->isRef() || vd->isOut()) { + // Foreach variables can also be by reference, for instance. + types.push_back(DtoType(vd->type->pointerTo())); + } else { + types.push_back(DtoType(vd->type)); + } + if (Logger::enabled()) { + Logger::println("Nested var: %s", vd->toChars()); + Logger::cout() << "of type: " << *types.back() << '\n'; + } + } + // Append current frame type to frame type list + const LLType* frameType = LLStructType::get(types); + frametypes.push_back(LLPointerType::getUnqual(frameType)); + + // make struct type for nested frame list + const LLStructType* nestedVarsTy = LLStructType::get(frametypes); + + // Store type in IrFunction + IrFunction* irfunction = fd->ir.irFunc; + irfunction->framesType = nestedVarsTy; + + // alloca it + // FIXME: For D2, this should be a gc_malloc (or similar) call, not alloca + LLValue* nestedVars = DtoAlloca(nestedVarsTy, ".frame_list"); + + // copy parent frames into beginning + if (depth != 0) + { + LLValue* src = irfunction->nestArg; + if (!src) + { + assert(irfunction->thisArg); + assert(fd->isMember2()); + LLValue* thisval = DtoLoad(irfunction->thisArg); + ClassDeclaration* cd = fd->isMember2()->isClassDeclaration(); + assert(cd); + assert(cd->vthis); + Logger::println("Indexing to 'this'"); + src = DtoLoad(DtoGEPi(thisval, 0, cd->vthis->ir.irField->index, ".vthis")); + } + src = DtoBitCast(src, getVoidPtrType()); + LLValue* dst = DtoBitCast(nestedVars, getVoidPtrType()); + DtoMemCpy(dst, src, DtoConstSize_t(depth * PTRSIZE)); + } + + // Create frame for current function and append to frames list + LLValue* frame = DtoAlloca(frameType, ".frame"); + // store current frame in list + DtoStore(frame, DtoGEPi(nestedVars, 0, depth)); + + // store context in IrFunction + irfunction->nestedVar = nestedVars; + + // go through all nested vars and assign addresses where possible. + for (std::set::iterator i=fd->nestedVars.begin(); i!=fd->nestedVars.end(); ++i) + { + VarDeclaration* vd = *i; + + LLValue* gep = DtoGEPi(frame, 0, vd->ir.irLocal->nestedIndex, vd->toChars()); + if (vd->isParameter()) { + Logger::println("nested param: %s", vd->toChars()); + DtoStore(vd->ir.irLocal->value, gep); + vd->ir.irLocal->byref = true; + } else if (vd->isRef() || vd->isOut()) { + // This slot is initialized in DtoNestedInit, to handle things like byref foreach variables + // which move around in memory. + vd->ir.irLocal->byref = true; + } 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; + vd->ir.irLocal->byref = false; + } } } } diff -r 2a37f4745ddd -r 8699c450a1a0 ir/irfunction.cpp --- a/ir/irfunction.cpp Sun Apr 12 16:22:21 2009 +0200 +++ b/ir/irfunction.cpp Sun Apr 12 20:23:00 2009 +0200 @@ -111,6 +111,7 @@ nestArg = NULL; nestedVar = NULL; + framesType = NULL; _arguments = NULL; _argptr = NULL; diff -r 2a37f4745ddd -r 8699c450a1a0 ir/irfunction.h --- a/ir/irfunction.h Sun Apr 12 16:22:21 2009 +0200 +++ b/ir/irfunction.h Sun Apr 12 20:23:00 2009 +0200 @@ -45,6 +45,7 @@ llvm::Value* nestArg; // nested function 'this' arg llvm::Value* nestedVar; // nested var alloca + const llvm::StructType* framesType; // type of nested context (not for -nested-ctx=array) llvm::Value* _arguments; llvm::Value* _argptr; diff -r 2a37f4745ddd -r 8699c450a1a0 ir/irvar.h --- a/ir/irvar.h Sun Apr 12 16:22:21 2009 +0200 +++ b/ir/irvar.h Sun Apr 12 20:23:00 2009 +0200 @@ -26,6 +26,8 @@ { IrLocal(VarDeclaration* v); + bool byref; // Not used for -nested-ctx=array + int nestedDepth; // ditto int nestedIndex; };