# HG changeset patch # User Frits van Bommel # Date 1239567735 -7200 # Node ID 50dc0db06238168a41b0a7155fbbfc7d35fa9484 # Parent 3d4581761b4c92d936cabae1658afb5487118e7d# Parent e45984519be7af112fd4dbd66976333f1495b434 Merge diff -r e45984519be7 -r 50dc0db06238 gen/classes.cpp --- a/gen/classes.cpp Sun Apr 12 19:56:03 2009 +0200 +++ b/gen/classes.cpp Sun Apr 12 22:22:15 2009 +0200 @@ -15,6 +15,7 @@ #include "gen/functions.h" #include "gen/runtime.h" #include "gen/dvalue.h" +#include "gen/nested.h" #include "ir/irstruct.h" @@ -1330,7 +1331,7 @@ // index vtbl funcval = DtoGEPi(funcval, 0, fdecl->vtblIndex, fdecl->toChars()); // load funcptr - funcval = DtoLoad(funcval); + funcval = DtoAlignedLoad(funcval); if (Logger::enabled()) Logger::cout() << "funcval: " << *funcval << '\n'; diff -r e45984519be7 -r 50dc0db06238 gen/dvalue.h --- a/gen/dvalue.h Sun Apr 12 19:56:03 2009 +0200 +++ b/gen/dvalue.h Sun Apr 12 22:22:15 2009 +0200 @@ -38,8 +38,8 @@ Type*& getType() { assert(type); return type; } - virtual LLValue* getLVal() { assert(0); return 0; } - virtual LLValue* getRVal() { assert(0); return 0; } + virtual llvm::Value* getLVal() { assert(0); return 0; } + virtual llvm::Value* getRVal() { assert(0); return 0; } virtual bool isLVal() { return false; } @@ -60,11 +60,11 @@ // immediate d-value struct DImValue : DValue { - LLValue* val; + llvm::Value* val; - DImValue(Type* t, LLValue* v) : DValue(t), val(v) { } + DImValue(Type* t, llvm::Value* v) : DValue(t), val(v) { } - virtual LLValue* getRVal() { assert(val); return val; } + virtual llvm::Value* getRVal() { assert(val); return val; } virtual DImValue* isIm() { return this; } }; @@ -72,11 +72,11 @@ // constant d-value struct DConstValue : DValue { - LLConstant* c; + llvm::Constant* c; - DConstValue(Type* t, LLConstant* con) : DValue(t), c(con) {} + DConstValue(Type* t, llvm::Constant* con) : DValue(t), c(con) {} - virtual LLValue* getRVal(); + virtual llvm::Value* getRVal(); virtual DConstValue* isConst() { return this; } }; @@ -84,7 +84,7 @@ // null d-value struct DNullValue : DConstValue { - DNullValue(Type* t, LLConstant* con) : DConstValue(t,con) {} + DNullValue(Type* t, llvm::Constant* con) : DConstValue(t,con) {} virtual DNullValue* isNull() { return this; } }; @@ -92,14 +92,14 @@ struct DVarValue : DValue { VarDeclaration* var; - LLValue* val; + llvm::Value* val; - DVarValue(Type* t, VarDeclaration* vd, LLValue* llvmValue); - DVarValue(Type* t, LLValue* llvmValue); + DVarValue(Type* t, VarDeclaration* vd, llvm::Value* llvmValue); + DVarValue(Type* t, llvm::Value* llvmValue); virtual bool isLVal() { return true; } - virtual LLValue* getLVal(); - virtual LLValue* getRVal(); + virtual llvm::Value* getLVal(); + virtual llvm::Value* getRVal(); virtual DVarValue* isVar() { return this; } }; @@ -107,19 +107,19 @@ // field d-value struct DFieldValue : DVarValue { - DFieldValue(Type* t, LLValue* llvmValue) : DVarValue(t, llvmValue) {} + DFieldValue(Type* t, llvm::Value* llvmValue) : DVarValue(t, llvmValue) {} virtual DFieldValue* isField() { return this; } }; // slice d-value struct DSliceValue : DValue { - LLValue* len; - LLValue* ptr; + llvm::Value* len; + llvm::Value* ptr; - DSliceValue(Type* t, LLValue* l, LLValue* p) : DValue(t), len(l), ptr(p) {} + DSliceValue(Type* t, llvm::Value* l, llvm::Value* p) : DValue(t), len(l), ptr(p) {} - virtual LLValue* getRVal(); + virtual llvm::Value* getRVal(); virtual DSliceValue* isSlice() { return this; } }; @@ -128,12 +128,12 @@ struct DFuncValue : DValue { FuncDeclaration* func; - LLValue* val; - LLValue* vthis; + llvm::Value* val; + llvm::Value* vthis; - DFuncValue(FuncDeclaration* fd, LLValue* v, LLValue* vt = 0); + DFuncValue(FuncDeclaration* fd, llvm::Value* v, llvm::Value* vt = 0); - virtual LLValue* getRVal(); + virtual llvm::Value* getRVal(); virtual DFuncValue* isFunc() { return this; } }; diff -r e45984519be7 -r 50dc0db06238 gen/functions.cpp --- a/gen/functions.cpp Sun Apr 12 19:56:03 2009 +0200 +++ b/gen/functions.cpp Sun Apr 12 22:22:15 2009 +0200 @@ -21,6 +21,7 @@ #include "gen/classes.h" #include "gen/dvalue.h" #include "gen/abi.h" +#include "gen/nested.h" using namespace llvm::Attribute; @@ -672,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) @@ -775,100 +769,20 @@ fd->nestedVars.insert(fd->vresult); } - // construct nested variables array - if (!fd->nestedVars.empty()) + DtoCreateNestedContext(fd); + +#if DMDV2 + if (fd->vresult && fd->vresult->nestedrefs.dim) +#else + if (fd->vresult && fd->vresult->nestedref) +#endif { - Logger::println("has nested frame"); - // start with adding all enclosing parent frames until a static parent is reached - int nparelems = 0; - if (!fd->isStatic()) - { - Dsymbol* par = fd->toParent2(); - while (par) - { - if (FuncDeclaration* parfd = par->isFuncDeclaration()) - { - nparelems += parfd->nestedVars.size(); - // stop at first static - if (parfd->isStatic()) - break; - } - else if (ClassDeclaration* parcd = par->isClassDeclaration()) - { - // nothing needed - } - else - { - break; - } - - par = par->toParent2(); - } - } - int nelems = fd->nestedVars.size() + nparelems; - - // make array type for nested vars - const LLType* nestedVarsTy = LLArrayType::get(getVoidPtrType(), nelems); + 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()); + } - // alloca it - LLValue* nestedVars = DtoAlloca(nestedVarsTy, ".nested_vars"); - - // copy parent frame into beginning - if (nparelems) - { - 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); - src = DtoLoad(DtoGEPi(thisval, 0,cd->vthis->ir.irField->index, ".vthis")); - } - DtoMemCpy(nestedVars, src, DtoConstSize_t(nparelems*PTRSIZE)); - } - - // store in IrFunction - irfunction->nestedVar = nestedVars; - - // go through all nested vars and assign indices - int idx = nparelems; - for (std::set::iterator i=fd->nestedVars.begin(); i!=fd->nestedVars.end(); ++i) - { - VarDeclaration* vd = *i; - if (!vd->ir.irLocal) - vd->ir.irLocal = new IrLocal(vd); - - if (vd->isParameter()) - { - Logger::println("nested param: %s", vd->toChars()); - LLValue* gep = DtoGEPi(nestedVars, 0, idx); - LLValue* val = DtoBitCast(vd->ir.irLocal->value, getVoidPtrType()); - DtoStore(val, gep); - } - else - { - Logger::println("nested var: %s", vd->toChars()); - } - - 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 - 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); - } - } - // copy _argptr and _arguments to a memory location if (f->linkage == LINKd && f->varargs == 1) { diff -r e45984519be7 -r 50dc0db06238 gen/llvmhelpers.cpp --- a/gen/llvmhelpers.cpp Sun Apr 12 19:56:03 2009 +0200 +++ b/gen/llvmhelpers.cpp Sun Apr 12 22:22:15 2009 +0200 @@ -1,3 +1,4 @@ +#include "gen/llvmhelpers.h" #include "gen/llvm.h" #include "llvm/Target/TargetMachineRegistry.h" @@ -9,7 +10,6 @@ #include "module.h" #include "gen/tollvm.h" -#include "gen/llvmhelpers.h" #include "gen/irstate.h" #include "gen/runtime.h" #include "gen/logger.h" @@ -21,6 +21,7 @@ #include "gen/typeinf.h" #include "gen/todebug.h" #include "gen/cl_options.h" +#include "gen/nested.h" #include "ir/irmodule.h" #include @@ -313,75 +314,6 @@ /****************************************************************************************/ /*//////////////////////////////////////////////////////////////////////////////////////// -// NESTED VARIABLE HELPERS -////////////////////////////////////////////////////////////////////////////////////////*/ - -DValue* DtoNestedVariable(Loc loc, Type* astype, VarDeclaration* vd) -{ - Dsymbol* vdparent = vd->toParent2(); - assert(vdparent); - - IrFunction* irfunc = gIR->func(); - - // is the nested variable in this scope? - if (vdparent == irfunc->decl) - { - LLValue* val = vd->ir.getIrValue(); - return new DVarValue(astype, vd, val); - } - - // get it from the nested context - LLValue* ctx = 0; - if (irfunc->decl->isMember2()) - { - ClassDeclaration* cd = irfunc->decl->isMember2()->isClassDeclaration(); - LLValue* val = DtoLoad(irfunc->thisArg); - ctx = DtoLoad(DtoGEPi(val, 0,cd->vthis->ir.irField->index, ".vthis")); - } - else - ctx = irfunc->nestArg; - assert(ctx); - - assert(vd->ir.irLocal); - LLValue* val = DtoBitCast(ctx, getPtrToType(getVoidPtrType())); - val = DtoGEPi1(val, vd->ir.irLocal->nestedIndex); - val = DtoLoad(val); - assert(vd->ir.irLocal->value); - val = DtoBitCast(val, vd->ir.irLocal->value->getType(), vd->toChars()); - return new DVarValue(astype, vd, val); -} - -LLValue* DtoNestedContext(Loc loc, Dsymbol* sym) -{ - Logger::println("DtoNestedContext for %s", sym->toPrettyChars()); - LOG_SCOPE; - - IrFunction* irfunc = gIR->func(); - - // if this func has its own vars that are accessed by nested funcs - // use its own context - if (irfunc->nestedVar) - return irfunc->nestedVar; - // otherwise, it may have gotten a context from the caller - else if (irfunc->nestArg) - return irfunc->nestArg; - // or just have a this argument - else if (irfunc->thisArg) - { - ClassDeclaration* cd = irfunc->decl->isMember2()->isClassDeclaration(); - if (!cd || !cd->vthis) - return getNullPtr(getVoidPtrType()); - LLValue* val = DtoLoad(irfunc->thisArg); - return DtoLoad(DtoGEPi(val, 0,cd->vthis->ir.irField->index, ".vthis")); - } - else - { - return getNullPtr(getVoidPtrType()); - } -} - -/****************************************************************************************/ -/*//////////////////////////////////////////////////////////////////////////////////////// // ASSIGNMENT HELPER (store this in that) ////////////////////////////////////////////////////////////////////////////////////////*/ @@ -946,22 +878,7 @@ Logger::println("has nestedref set"); assert(vd->ir.irLocal); - // alloca as usual if no value already - if (!vd->ir.irLocal->value) - { - vd->ir.irLocal->value = DtoAlloca(DtoType(vd->type), vd->toChars()); - } - - // 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); - - assert(isaPointer(vd->ir.irLocal->value)); - LLValue* val = DtoBitCast(vd->ir.irLocal->value, getVoidPtrType()); - - DtoStore(val, gep); - + DtoNestedInit(vd); } // normal stack variable, allocate storage on the stack if it has not already been done else if(!vd->ir.irLocal) { @@ -1110,12 +1027,7 @@ else assert(!addr || addr == var->ir.irLocal->value); - // store the address into the nested vars array - assert(var->ir.irLocal->nestedIndex >= 0); - LLValue* gep = DtoGEPi(gIR->func()->decl->ir.irFunc->nestedVar, 0, var->ir.irLocal->nestedIndex); - assert(isaPointer(var->ir.irLocal->value)); - LLValue* val = DtoBitCast(var->ir.irLocal->value, getVoidPtrType()); - DtoStore(val, gep); + DtoNestedInit(var); } // normal local variable else diff -r e45984519be7 -r 50dc0db06238 gen/llvmhelpers.h --- a/gen/llvmhelpers.h Sun Apr 12 19:56:03 2009 +0200 +++ b/gen/llvmhelpers.h Sun Apr 12 22:22:15 2009 +0200 @@ -2,7 +2,10 @@ #define LDC_GEN_LLVMHELPERS_H #include "gen/llvm.h" +#include "gen/dvalue.h" + #include "statement.h" +#include "mtype.h" // this is used for tracking try-finally, synchronized and volatile scopes struct EnclosingHandler @@ -64,15 +67,6 @@ /// Leaves a monitor lock. void DtoLeaveMonitor(LLValue* v); -// nested variable and context helpers - -/// Gets the context value for a call to a nested function or newing a nested -/// class with arbitrary nesting. -LLValue* DtoNestedContext(Loc loc, Dsymbol* sym); - -/// Gets the DValue of a nested variable with arbitrary nesting. -DValue* DtoNestedVariable(Loc loc, Type* astype, VarDeclaration* vd); - // basic operations void DtoAssign(Loc& loc, DValue* lhs, DValue* rhs); diff -r e45984519be7 -r 50dc0db06238 gen/nested.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gen/nested.cpp Sun Apr 12 22:22:15 2009 +0200 @@ -0,0 +1,411 @@ +#include "gen/nested.h" + +#include "gen/dvalue.h" +#include "gen/irstate.h" +#include "gen/llvmhelpers.h" +#include "gen/logger.h" +#include "gen/tollvm.h" + +#include "llvm/Support/CommandLine.h" +namespace cl = llvm::cl; + +/// What the context pointer for a nested function looks like +enum NestedCtxType { + /// Context is void*[] of pointers to variables. + /// Variables from higher levels are at the front. + NCArray, + + /// Context is a struct containing variables belonging to the parent function. + /// If the parent function itself has a parent function, one of the members is + /// a pointer to its context. (linked-list style) + // FIXME: implement + // 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 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 +}; + +static cl::opt nestedCtx("nested-ctx", + cl::desc("How to construct a nested function's context:"), + cl::ZeroOrMore, + 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", "List of pointers to structs of variables, one per level."), + clEnumValEnd), + cl::init(NCHybrid)); + + +/****************************************************************************************/ +/*//////////////////////////////////////////////////////////////////////////////////////// +// 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 + + Dsymbol* vdparent = vd->toParent2(); + assert(vdparent); + + IrFunction* irfunc = gIR->func(); + + // is the nested variable in this scope? + if (vdparent == irfunc->decl) + { + LLValue* val = vd->ir.getIrValue(); + return new DVarValue(astype, vd, val); + } + + // get the nested context + LLValue* ctx = 0; + if (irfunc->decl->isMember2()) + { + ClassDeclaration* cd = irfunc->decl->isMember2()->isClassDeclaration(); + LLValue* val = DtoLoad(irfunc->thisArg); + ctx = DtoLoad(DtoGEPi(val, 0,cd->vthis->ir.irField->index, ".vthis")); + } + else + ctx = irfunc->nestArg; + assert(ctx); + + assert(vd->ir.irLocal); + + //////////////////////////////////// + // Extract variable from nested context + + if (nestedCtx == NCArray) { + LLValue* val = DtoBitCast(ctx, getPtrToType(getVoidPtrType())); + val = DtoGEPi1(val, vd->ir.irLocal->nestedIndex); + val = DtoAlignedLoad(val); + assert(vd->ir.irLocal->value); + 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 = DtoAlignedLoad(val, (std::string(".frame.") + parentfunc->toChars()).c_str()); + val = DtoGEPi(val, 0, vd->ir.irLocal->nestedIndex, vd->toChars()); + if (vd->ir.irLocal->byref) + val = DtoAlignedLoad(val); + return new DVarValue(astype, vd, val); + } + else { + assert(0 && "Not implemented yet"); + } +} + +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) + vd->ir.irLocal->value = DtoAlloca(DtoType(vd->type), vd->toChars()); + + // store the address into the nested vars array + assert(vd->ir.irLocal->nestedIndex >= 0); + LLValue* gep = DtoGEPi(nestedVar, 0, vd->ir.irLocal->nestedIndex); + + assert(isaPointer(vd->ir.irLocal->value)); + LLValue* val = DtoBitCast(vd->ir.irLocal->value, getVoidPtrType()); + + DtoAlignedStore(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 = DtoAlignedLoad(framep, (std::string(".frame.") + parentfunc->toChars()).c_str()); + + LLValue* slot = DtoGEPi(frame, 0, vd->ir.irLocal->nestedIndex); + DtoAlignedStore(vd->ir.irLocal->value, slot); + } else { + // Already initialized in DtoCreateNestedContext + } + } + else { + assert(0 && "Not implemented yet"); + } +} + +LLValue* DtoNestedContext(Loc loc, Dsymbol* sym) +{ + Logger::println("DtoNestedContext for %s", sym->toPrettyChars()); + LOG_SCOPE; + + IrFunction* irfunc = gIR->func(); + + // if this func has its own vars that are accessed by nested funcs + // use its own context + if (irfunc->nestedVar) + return irfunc->nestedVar; + // otherwise, it may have gotten a context from the caller + else if (irfunc->nestArg) + return irfunc->nestArg; + // or just have a this argument + else if (irfunc->thisArg) + { + ClassDeclaration* cd = irfunc->decl->isMember2()->isClassDeclaration(); + if (!cd || !cd->vthis) + return getNullPtr(getVoidPtrType()); + LLValue* val = DtoLoad(irfunc->thisArg); + return DtoLoad(DtoGEPi(val, 0,cd->vthis->ir.irField->index, ".vthis")); + } + else + { + return getNullPtr(getVoidPtrType()); + } +} + +void DtoCreateNestedContext(FuncDeclaration* fd) { + Logger::println("DtoCreateNestedContext for %s", fd->toChars()); + LOG_SCOPE + + if (nestedCtx == NCArray) { + // 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 + int nparelems = 0; + if (!fd->isStatic()) + { + Dsymbol* par = fd->toParent2(); + while (par) + { + if (FuncDeclaration* parfd = par->isFuncDeclaration()) + { + nparelems += parfd->nestedVars.size(); + // stop at first static + if (parfd->isStatic()) + break; + } + else if (ClassDeclaration* parcd = par->isClassDeclaration()) + { + // nothing needed + } + else + { + break; + } + + par = par->toParent2(); + } + } + int nelems = fd->nestedVars.size() + nparelems; + + // make array type for nested vars + const LLType* nestedVarsTy = LLArrayType::get(getVoidPtrType(), nelems); + + // alloca it + LLValue* nestedVars = DtoAlloca(nestedVarsTy, ".nested_vars"); + + IrFunction* irfunction = fd->ir.irFunc; + + // copy parent frame into beginning + if (nparelems) + { + 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); + src = DtoLoad(DtoGEPi(thisval, 0,cd->vthis->ir.irField->index, ".vthis")); + } + DtoMemCpy(nestedVars, src, DtoConstSize_t(nparelems*PTRSIZE), + getABITypeAlign(getVoidPtrType())); + } + + // store in IrFunction + irfunction->nestedVar = nestedVars; + + // go through all nested vars and assign indices + int idx = nparelems; + for (std::set::iterator i=fd->nestedVars.begin(); i!=fd->nestedVars.end(); ++i) + { + VarDeclaration* vd = *i; + if (!vd->ir.irLocal) + vd->ir.irLocal = new IrLocal(vd); + + if (vd->isParameter()) + { + Logger::println("nested param: %s", vd->toChars()); + LLValue* gep = DtoGEPi(nestedVars, 0, idx); + LLValue* val = DtoBitCast(vd->ir.irLocal->value, getVoidPtrType()); + DtoAlignedStore(val, gep); + } + else + { + Logger::println("nested var: %s", vd->toChars()); + } + + vd->ir.irLocal->nestedIndex = idx++; + } + } + } + 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) + { + 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), + getABITypeAlign(getVoidPtrType())); + } + + // Create frame for current function and append to frames list + LLValue* frame = DtoAlloca(frameType, ".frame"); + // store current frame in list + DtoAlignedStore(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()); + DtoAlignedStore(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; + } + } + } + } + else { + assert(0 && "Not implemented yet"); + } +} diff -r e45984519be7 -r 50dc0db06238 gen/nested.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gen/nested.h Sun Apr 12 22:22:15 2009 +0200 @@ -0,0 +1,25 @@ +#ifndef LDC_GEN_NESTED_H +#define LDC_GEN_NESTED_H + +#include "declaration.h" +#include "mtype.h" +#include "gen/dvalue.h" + +/////////////////////////////////////////////////////////// +// Nested variable and context helpers +/////////////////////////////////////////////////////////// + +/// Creates the context value for a nested function. +void DtoCreateNestedContext(FuncDeclaration* fd); + +/// Allocate space for variable accessed from nested function. +void DtoNestedInit(VarDeclaration* vd); + +/// Gets the context value for a call to a nested function or newing a nested +/// class with arbitrary nesting. +llvm::Value* DtoNestedContext(Loc loc, Dsymbol* sym); + +/// Gets the DValue of a nested variable with arbitrary nesting. +DValue* DtoNestedVariable(Loc loc, Type* astype, VarDeclaration* vd); + +#endif diff -r e45984519be7 -r 50dc0db06238 gen/tocall.cpp --- a/gen/tocall.cpp Sun Apr 12 19:56:03 2009 +0200 +++ b/gen/tocall.cpp Sun Apr 12 22:22:15 2009 +0200 @@ -9,6 +9,7 @@ #include "gen/dvalue.h" #include "gen/functions.h" #include "gen/abi.h" +#include "gen/nested.h" #include "gen/logger.h" diff -r e45984519be7 -r 50dc0db06238 gen/toir.cpp --- a/gen/toir.cpp Sun Apr 12 19:56:03 2009 +0200 +++ b/gen/toir.cpp Sun Apr 12 22:22:15 2009 +0200 @@ -36,6 +36,7 @@ #include "gen/aa.h" #include "gen/functions.h" #include "gen/todebug.h" +#include "gen/nested.h" #include "llvm/Support/ManagedStatic.h" diff -r e45984519be7 -r 50dc0db06238 gen/tollvm.cpp --- a/gen/tollvm.cpp Sun Apr 12 19:56:03 2009 +0200 +++ b/gen/tollvm.cpp Sun Apr 12 22:22:15 2009 +0200 @@ -426,7 +426,7 @@ ////////////////////////////////////////////////////////////////////////////////////////// -void DtoMemCpy(LLValue* dst, LLValue* src, LLValue* nbytes) +void DtoMemCpy(LLValue* dst, LLValue* src, LLValue* nbytes, unsigned align) { dst = DtoBitCast(dst,getVoidPtrType()); src = DtoBitCast(src,getVoidPtrType()); @@ -435,7 +435,7 @@ llvm::Function* fn = llvm::Intrinsic::getDeclaration(gIR->module, llvm::Intrinsic::memcpy, &intTy, 1); - gIR->ir->CreateCall4(fn, dst, src, nbytes, DtoConstUint(0), ""); + gIR->ir->CreateCall4(fn, dst, src, nbytes, DtoConstUint(align), ""); } ////////////////////////////////////////////////////////////////////////////////////////// @@ -573,11 +573,20 @@ { // if (Logger::enabled()) // Logger::cout() << "loading " << *src << '\n'; - LLValue* ld = gIR->ir->CreateLoad(src, name ? name : "tmp"); + llvm::LoadInst* ld = gIR->ir->CreateLoad(src, name ? name : "tmp"); //ld->setVolatile(gIR->func()->inVolatile); return ld; } +// Like DtoLoad, but the pointer is guaranteed to be aligned appropriately for the type. +LLValue* DtoAlignedLoad(LLValue* src, const char* name) +{ + llvm::LoadInst* ld = gIR->ir->CreateLoad(src, name ? name : "tmp"); + ld->setAlignment(getABITypeAlign(ld->getType())); + return ld; +} + + void DtoStore(LLValue* src, LLValue* dst) { // if (Logger::enabled()) @@ -586,6 +595,13 @@ //st->setVolatile(gIR->func()->inVolatile); } +// Like DtoStore, but the pointer is guaranteed to be aligned appropriately for the type. +void DtoAlignedStore(LLValue* src, LLValue* dst) +{ + llvm::StoreInst* st = gIR->ir->CreateStore(src,dst); + st->setAlignment(getABITypeAlign(src->getType())); +} + ////////////////////////////////////////////////////////////////////////////////////////// LLValue* DtoBitCast(LLValue* v, const LLType* t, const char* name) diff -r e45984519be7 -r 50dc0db06238 gen/tollvm.h --- a/gen/tollvm.h Sun Apr 12 19:56:03 2009 +0200 +++ b/gen/tollvm.h Sun Apr 12 22:22:15 2009 +0200 @@ -63,7 +63,9 @@ // llvm wrappers LLValue* DtoLoad(LLValue* src, const char* name=0); +LLValue* DtoAlignedLoad(LLValue* src, const char* name=0); void DtoStore(LLValue* src, LLValue* dst); +void DtoAlignedStore(LLValue* src, LLValue* dst); LLValue* DtoBitCast(LLValue* v, const LLType* t, const char* name=0); LLConstant* DtoBitCast(LLConstant* v, const LLType* t); @@ -117,8 +119,9 @@ * @param dst Destination memory. * @param src Source memory. * @param nbytes Number of bytes to copy. + * @param align The minimum alignment of the source and destination memory. */ -void DtoMemCpy(LLValue* dst, LLValue* src, LLValue* nbytes); +void DtoMemCpy(LLValue* dst, LLValue* src, LLValue* nbytes, unsigned align = 0); /** * Generates a call to C memcmp. diff -r e45984519be7 -r 50dc0db06238 ir/irfunction.cpp --- a/ir/irfunction.cpp Sun Apr 12 19:56:03 2009 +0200 +++ b/ir/irfunction.cpp Sun Apr 12 22:22:15 2009 +0200 @@ -111,6 +111,7 @@ nestArg = NULL; nestedVar = NULL; + framesType = NULL; _arguments = NULL; _argptr = NULL; diff -r e45984519be7 -r 50dc0db06238 ir/irfunction.h --- a/ir/irfunction.h Sun Apr 12 19:56:03 2009 +0200 +++ b/ir/irfunction.h Sun Apr 12 22:22:15 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 e45984519be7 -r 50dc0db06238 ir/irvar.h --- a/ir/irvar.h Sun Apr 12 19:56:03 2009 +0200 +++ b/ir/irvar.h Sun Apr 12 22:22:15 2009 +0200 @@ -26,6 +26,8 @@ { IrLocal(VarDeclaration* v); + bool byref; // Not used for -nested-ctx=array + int nestedDepth; // ditto int nestedIndex; };