changeset 1209:8699c450a1a0

Implement -nested-ctx=hybrid
author Frits van Bommel <fvbommel wxs.nl>
date Sun, 12 Apr 2009 20:23:00 +0200
parents 2a37f4745ddd
children 3d4581761b4c
files gen/functions.cpp gen/nested.cpp ir/irfunction.cpp ir/irfunction.h ir/irvar.h
diffstat 5 files changed, 194 insertions(+), 25 deletions(-) [+]
line wrap: on
line diff
--- 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)
     {
--- 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<const LLType*> 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<VarDeclaration*>::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<VarDeclaration*>::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;
+                }
             }
         }
     }
--- 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;
--- 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;
--- 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;
 };