Wiki Roadmap Timeline Tickets New Ticket Source Search Help / Guide About Trac Login

root/gen/nested.cpp

Revision 1569:755abafbf25d, 22.4 kB (checked in by Benjamin Kramer <benny.kra@gmail.com>, 3 years ago)

Push the context through StructType::get.

Requires LLVM >= 78258. Also remove old #if's.

Line 
1 #include "gen/nested.h"
2
3 #include "gen/dvalue.h"
4 #include "gen/irstate.h"
5 #include "gen/llvmhelpers.h"
6 #include "gen/logger.h"
7 #include "gen/tollvm.h"
8 #include "gen/functions.h"
9
10 #include "llvm/Support/CommandLine.h"
11 namespace cl = llvm::cl;
12
13 /// What the context pointer for a nested function looks like
14 enum NestedCtxType {
15     /// Context is void*[] of pointers to variables.
16     /// Variables from higher levels are at the front.
17     NCArray,
18    
19     /// Context is a struct containing variables belonging to the parent function.
20     /// If the parent function itself has a parent function, one of the members is
21     /// a pointer to its context. (linked-list style)
22     // FIXME: implement
23     // TODO: Functions without any variables accessed by nested functions, but
24     //       with a parent whose variables are accessed, can use the parent's
25     //       context.
26     // NOTE: This is what DMD seems to do.
27     NCStruct,
28    
29     /// Context is a list of pointers to structs of variables, followed by the
30     /// variables of the inner-most function with variables accessed by nested
31     /// functions. The initial pointers point to similar structs for enclosing
32     /// functions.
33     /// Only functions whose variables are accessed by nested functions create
34     /// new frames, others just pass on what got passed in.
35     NCHybrid
36 };
37
38 static cl::opt<NestedCtxType> nestedCtx("nested-ctx",
39     cl::desc("How to construct a nested function's context:"),
40     cl::ZeroOrMore,
41     cl::values(
42         clEnumValN(NCArray,  "array",  "Array of pointers to variables (including multi-level)"),
43         //clEnumValN(NCStruct, "struct", "Struct of variables (with multi-level via linked list)"),
44         clEnumValN(NCHybrid, "hybrid", "List of pointers to structs of variables, one per level."),
45         clEnumValEnd),
46     cl::init(NCHybrid));
47
48
49 /****************************************************************************************/
50 /*////////////////////////////////////////////////////////////////////////////////////////
51 // NESTED VARIABLE HELPERS
52 ////////////////////////////////////////////////////////////////////////////////////////*/
53
54 static FuncDeclaration* getParentFunc(Dsymbol* sym, bool stopOnStatic) {
55     if (!sym)
56         return NULL;
57     Dsymbol* parent = sym->parent;
58     assert(parent);
59     while (parent && !parent->isFuncDeclaration()) {
60         if (stopOnStatic) {
61             Declaration* decl = sym->isDeclaration();
62             if (decl && decl->isStatic())
63                 return NULL;
64         }
65         parent = parent->parent;
66     }
67    
68     return (parent ? parent->isFuncDeclaration() : NULL);
69 }
70
71 DValue* DtoNestedVariable(Loc loc, Type* astype, VarDeclaration* vd)
72 {
73     Logger::println("DtoNestedVariable for %s @ %s", vd->toChars(), loc.toChars());
74     LOG_SCOPE;
75    
76     ////////////////////////////////////
77     // Locate context value
78    
79     Dsymbol* vdparent = vd->toParent2();
80     assert(vdparent);
81    
82     IrFunction* irfunc = gIR->func();
83    
84     // is the nested variable in this scope?
85     if (vdparent == irfunc->decl)
86     {
87         LLValue* val = vd->ir.getIrValue();
88         return new DVarValue(astype, vd, val);
89     }
90    
91     // get the nested context
92     LLValue* ctx = 0;
93     if (irfunc->decl->isMember2())
94     {
95         ClassDeclaration* cd = irfunc->decl->isMember2()->isClassDeclaration();
96         LLValue* val = DtoLoad(irfunc->thisArg);
97         ctx = DtoLoad(DtoGEPi(val, 0,cd->vthis->ir.irField->index, ".vthis"));
98     }
99     else if (irfunc->nestedVar)
100         ctx = irfunc->nestedVar;
101     else
102         ctx = irfunc->nestArg;
103     assert(ctx);
104    
105     assert(vd->ir.irLocal);
106    
107     ////////////////////////////////////
108     // Extract variable from nested context
109    
110     if (nestedCtx == NCArray) {
111         LLValue* val = DtoBitCast(ctx, getPtrToType(getVoidPtrType()));
112         val = DtoGEPi1(val, vd->ir.irLocal->nestedIndex);
113         val = DtoAlignedLoad(val);
114         assert(vd->ir.irLocal->value);
115         val = DtoBitCast(val, vd->ir.irLocal->value->getType(), vd->toChars());
116         return new DVarValue(astype, vd, val);
117     }
118     else if (nestedCtx == NCHybrid) {
119         LLValue* val = DtoBitCast(ctx, LLPointerType::getUnqual(irfunc->frameType));
120         Logger::cout() << "Context: " << *val << '\n';
121         Logger::cout() << "of type: " << *val->getType() << '\n';
122        
123         unsigned vardepth = vd->ir.irLocal->nestedDepth;
124         unsigned funcdepth = irfunc->depth;
125        
126         Logger::cout() << "Variable: " << vd->toChars() << '\n';
127         Logger::cout() << "Variable depth: " << vardepth << '\n';
128         Logger::cout() << "Function: " << irfunc->decl->toChars() << '\n';
129         Logger::cout() << "Function depth: " << funcdepth << '\n';
130        
131         if (vardepth == funcdepth) {
132             // This is not always handled above because functions without
133             // variables accessed by nested functions don't create new frames.
134             Logger::println("Same depth");
135         } else {
136             // Load frame pointer and index that...
137             Logger::println("Lower depth");
138             val = DtoGEPi(val, 0, vd->ir.irLocal->nestedDepth);
139             Logger::cout() << "Frame index: " << *val << '\n';
140             val = DtoAlignedLoad(val, (std::string(".frame.") + vdparent->toChars()).c_str());
141             Logger::cout() << "Frame: " << *val << '\n';
142         }
143         val = DtoGEPi(val, 0, vd->ir.irLocal->nestedIndex, vd->toChars());
144         Logger::cout() << "Addr: " << *val << '\n';
145         Logger::cout() << "of type: " << *val->getType() << '\n';
146         if (vd->ir.irLocal->byref) {
147             val = DtoAlignedLoad(val);
148             Logger::cout() << "Was byref, now: " << *val << '\n';
149             Logger::cout() << "of type: " << *val->getType() << '\n';
150         }
151        
152         return new DVarValue(astype, vd, val);
153     }
154     else {
155         assert(0 && "Not implemented yet");
156     }
157 }
158
159 void DtoNestedInit(VarDeclaration* vd)
160 {
161     Logger::println("DtoNestedInit for %s", vd->toChars());
162     LOG_SCOPE
163    
164     IrFunction* irfunc = gIR->func()->decl->ir.irFunc;
165     LLValue* nestedVar = irfunc->nestedVar;
166    
167     if (nestedCtx == NCArray) {
168         // alloca as usual if no value already
169         if (!vd->ir.irLocal->value)
170             vd->ir.irLocal->value = DtoAlloca(vd->type, vd->toChars());
171        
172         // store the address into the nested vars array
173         assert(vd->ir.irLocal->nestedIndex >= 0);
174         LLValue* gep = DtoGEPi(nestedVar, 0, vd->ir.irLocal->nestedIndex);
175        
176         assert(isaPointer(vd->ir.irLocal->value));
177         LLValue* val = DtoBitCast(vd->ir.irLocal->value, getVoidPtrType());
178        
179         DtoAlignedStore(val, gep);
180     }
181     else if (nestedCtx == NCHybrid) {
182         assert(vd->ir.irLocal->value && "Nested variable without storage?");
183        
184         if (!vd->isParameter() && (vd->isRef() || vd->isOut())) {
185             unsigned vardepth = vd->ir.irLocal->nestedDepth;
186            
187             LLValue* val = NULL;
188             // Retrieve frame pointer
189             if (vardepth == irfunc->depth) {
190                 val = nestedVar;
191             } else {
192                 FuncDeclaration *parentfunc = getParentFunc(vd, true);
193                 assert(parentfunc && "No parent function for nested variable?");
194                
195                 val = DtoGEPi(nestedVar, 0, vardepth);
196                 val = DtoAlignedLoad(val, (std::string(".frame.") + parentfunc->toChars()).c_str());
197             }
198             val = DtoGEPi(val, 0, vd->ir.irLocal->nestedIndex, vd->toChars());
199             DtoAlignedStore(vd->ir.irLocal->value, val);
200         } else {
201             // Already initialized in DtoCreateNestedContext
202         }
203     }
204     else {
205         assert(0 && "Not implemented yet");
206     }
207 }
208
209 LLValue* DtoNestedContext(Loc loc, Dsymbol* sym)
210 {
211     Logger::println("DtoNestedContext for %s", sym->toPrettyChars());
212     LOG_SCOPE;
213
214     IrFunction* irfunc = gIR->func();
215     bool fromParent = true;
216
217     LLValue* val;
218     // if this func has its own vars that are accessed by nested funcs
219     // use its own context
220     if (irfunc->nestedVar) {
221         val = irfunc->nestedVar;
222         fromParent = false;
223     }
224     // otherwise, it may have gotten a context from the caller
225     else if (irfunc->nestArg)
226         val = irfunc->nestArg;
227     // or just have a this argument
228     else if (irfunc->thisArg)
229     {
230         ClassDeclaration* cd = irfunc->decl->isMember2()->isClassDeclaration();
231         if (!cd || !cd->vthis)
232             return llvm::UndefValue::get(getVoidPtrType());
233         val = DtoLoad(irfunc->thisArg);
234         val = DtoLoad(DtoGEPi(val, 0,cd->vthis->ir.irField->index, ".vthis"));
235     }
236     else
237     {
238         return llvm::UndefValue::get(getVoidPtrType());
239     }
240     if (nestedCtx == NCHybrid) {
241         if (FuncDeclaration* symfd = sym->isFuncDeclaration()) {
242             // Make sure we've had a chance to analyze nested context usage
243             DtoDefineFunction(symfd);
244            
245             // if this is for a function that doesn't access variables from
246             // enclosing scopes, it doesn't matter what we pass.
247             // Tell LLVM about it by passing an 'undef'.
248             if (symfd && symfd->ir.irFunc->depth == -1)
249                 return llvm::UndefValue::get(getVoidPtrType());
250            
251             // If sym is a nested function, and it's parent context is different than the
252             // one we got, adjust it.
253             if (FuncDeclaration* fd = getParentFunc(symfd, true)) {
254                 Logger::println("For nested function, parent is %s", fd->toChars());
255                 FuncDeclaration* ctxfd = irfunc->decl;
256                 Logger::println("Current function is %s", ctxfd->toChars());
257                 if (fromParent) {
258                     ctxfd = getParentFunc(ctxfd, true);
259                     assert(ctxfd && "Context from outer function, but no outer function?");
260                 }
261                 Logger::println("Context is from %s", ctxfd->toChars());
262                
263                 unsigned neededDepth = fd->ir.irFunc->depth;
264                 unsigned ctxDepth = ctxfd->ir.irFunc->depth;
265                
266                 Logger::cout() << "Needed depth: " << neededDepth << '\n';
267                 Logger::cout() << "Context depth: " << ctxDepth << '\n';
268                
269                 if (neededDepth >= ctxDepth) {
270                     assert(neededDepth <= ctxDepth + 1 && "How are we going more than one nesting level up?");
271                     // fd needs the same context as we do, so all is well
272                     Logger::println("Calling sibling function or directly nested function");
273                 } else {
274                     val = DtoBitCast(val, LLPointerType::getUnqual(ctxfd->ir.irFunc->frameType));
275                     val = DtoGEPi(val, 0, neededDepth);
276                     val = DtoAlignedLoad(val, (std::string(".frame.") + fd->toChars()).c_str());
277                 }
278             }
279         }
280     }
281     Logger::cout() << "result = " << *val << '\n';
282     Logger::cout() << "of type " << *val->getType() << '\n';
283     return val;
284 }
285
286 void DtoCreateNestedContext(FuncDeclaration* fd) {
287     Logger::println("DtoCreateNestedContext for %s", fd->toChars());
288     LOG_SCOPE
289    
290     if (nestedCtx == NCArray) {
291         // construct nested variables array
292         if (!fd->nestedVars.empty())
293         {
294             Logger::println("has nested frame");
295             // start with adding all enclosing parent frames until a static parent is reached
296             int nparelems = 0;
297             if (!fd->isStatic())
298             {
299                 Dsymbol* par = fd->toParent2();
300                 while (par)
301                 {
302                     if (FuncDeclaration* parfd = par->isFuncDeclaration())
303                     {
304                         nparelems += parfd->nestedVars.size();
305                         // stop at first static
306                         if (parfd->isStatic())
307                             break;
308                     }
309                     else if (ClassDeclaration* parcd = par->isClassDeclaration())
310                     {
311                         // nothing needed
312                     }
313                     else
314                     {
315                         break;
316                     }
317
318                     par = par->toParent2();
319                 }
320             }
321             int nelems = fd->nestedVars.size() + nparelems;
322            
323             // make array type for nested vars
324             const LLType* nestedVarsTy = LLArrayType::get(getVoidPtrType(), nelems);
325        
326             // alloca it
327             // FIXME align ?
328             LLValue* nestedVars = DtoRawAlloca(nestedVarsTy, 0, ".nested_vars");
329            
330             IrFunction* irfunction = fd->ir.irFunc;
331            
332             // copy parent frame into beginning
333             if (nparelems)
334             {
335                 LLValue* src = irfunction->nestArg;
336                 if (!src)
337                 {
338                     assert(irfunction->thisArg);
339                     assert(fd->isMember2());
340                     LLValue* thisval = DtoLoad(irfunction->thisArg);
341                     ClassDeclaration* cd = fd->isMember2()->isClassDeclaration();
342                     assert(cd);
343                     assert(cd->vthis);
344                     src = DtoLoad(DtoGEPi(thisval, 0,cd->vthis->ir.irField->index, ".vthis"));
345                 }
346                 DtoMemCpy(nestedVars, src, DtoConstSize_t(nparelems*PTRSIZE),
347                     getABITypeAlign(getVoidPtrType()));
348             }
349            
350             // store in IrFunction
351             irfunction->nestedVar = nestedVars;
352            
353             // go through all nested vars and assign indices
354             int idx = nparelems;
355             for (std::set<VarDeclaration*>::iterator i=fd->nestedVars.begin(); i!=fd->nestedVars.end(); ++i)
356             {
357                 VarDeclaration* vd = *i;
358                 if (!vd->ir.irLocal)
359                     vd->ir.irLocal = new IrLocal(vd);
360
361                 if (vd->isParameter())
362                 {
363                     Logger::println("nested param: %s", vd->toChars());
364                     LLValue* gep = DtoGEPi(nestedVars, 0, idx);
365                     LLValue* val = DtoBitCast(vd->ir.irLocal->value, getVoidPtrType());
366                     DtoAlignedStore(val, gep);
367                 }
368                 else
369                 {
370                     Logger::println("nested var:   %s", vd->toChars());
371                 }
372
373                 vd->ir.irLocal->nestedIndex = idx++;
374             }
375         }
376     }
377     else if (nestedCtx == NCHybrid) {
378         // construct nested variables array
379         if (!fd->nestedVars.empty())
380         {
381             Logger::println("has nested frame");
382             // start with adding all enclosing parent frames until a static parent is reached
383            
384             const LLStructType* innerFrameType = NULL;
385             unsigned depth = -1;
386             if (!fd->isStatic()) {
387                 if (FuncDeclaration* parfd = getParentFunc(fd, true)) {
388                     innerFrameType = parfd->ir.irFunc->frameType;
389                     if (innerFrameType)
390                         depth = parfd->ir.irFunc->depth;
391                 }
392             }
393             fd->ir.irFunc->depth = ++depth;
394            
395             Logger::cout() << "Function " << fd->toChars() << " has depth " << depth << '\n';
396            
397             typedef std::vector<const LLType*> TypeVec;
398             TypeVec types;
399             if (depth != 0) {
400                 assert(innerFrameType);
401                 // Add frame pointer types for all but last frame
402                 if (depth > 1) {
403                     for (unsigned i = 0; i < (depth - 1); ++i) {
404                         types.push_back(innerFrameType->getElementType(i));
405                     }
406                 }
407                 // Add frame pointer type for last frame
408                 types.push_back(LLPointerType::getUnqual(innerFrameType));
409             }
410            
411             if (Logger::enabled()) {
412                 Logger::println("Frame types: ");
413                 LOG_SCOPE;
414                 for (TypeVec::iterator i = types.begin(); i != types.end(); ++i)
415                     Logger::cout() << **i << '\n';
416             }
417            
418             // Add the direct nested variables of this function, and update their indices to match.
419             // TODO: optimize ordering for minimal space usage?
420             for (std::set<VarDeclaration*>::iterator i=fd->nestedVars.begin(); i!=fd->nestedVars.end(); ++i)
421             {
422                 VarDeclaration* vd = *i;
423                 if (!vd->ir.irLocal)
424                     vd->ir.irLocal = new IrLocal(vd);
425                
426                 vd->ir.irLocal->nestedIndex = types.size();
427                 vd->ir.irLocal->nestedDepth = depth;
428                 if (vd->isParameter()) {
429                     // Parameters already have storage associated with them (to handle byref etc.),
430                     // so handle those cases specially by storing a pointer instead of a value.
431                     assert(vd->ir.irLocal->value);
432                     LLValue* value = vd->ir.irLocal->value;
433                     const LLType* type = value->getType();
434                     if (llvm::isa<llvm::AllocaInst>(value->getUnderlyingObject()))
435                         // This will be copied to the nesting frame.
436                         type = type->getContainedType(0);
437                     types.push_back(type);
438                 } else if (vd->isRef() || vd->isOut()) {
439                     // Foreach variables can also be by reference, for instance.
440                     types.push_back(DtoType(vd->type->pointerTo()));
441                 } else {
442                     types.push_back(DtoType(vd->type));
443                 }
444                 if (Logger::enabled()) {
445                     Logger::println("Nested var: %s", vd->toChars());
446                     Logger::cout() << "of type: " << *types.back() << '\n';
447                 }
448             }
449            
450             const LLStructType* frameType = LLStructType::get(gIR->context(), types);
451             gIR->module->addTypeName(std::string("nest.") + fd->toChars(), frameType);
452            
453             Logger::cout() << "frameType = " << *frameType << '\n';
454            
455             // Store type in IrFunction
456             IrFunction* irfunction = fd->ir.irFunc;
457             irfunction->frameType = frameType;
458            
459             // Create frame for current function and append to frames list
460             // FIXME: For D2, this should be a gc_malloc (or similar) call, not alloca
461             //        (Note that it'd also require more aggressive copying of
462             //        by-value parameters instead of just alloca'd ones)
463             // FIXME: alignment ?
464             LLValue* frame = DtoRawAlloca(frameType, 0, ".frame");
465            
466             // copy parent frames into beginning
467             if (depth != 0) {
468                 LLValue* src = irfunction->nestArg;
469                 if (!src) {
470                     assert(irfunction->thisArg);
471                     assert(fd->isMember2());
472                     LLValue* thisval = DtoLoad(irfunction->thisArg);
473                     ClassDeclaration* cd = fd->isMember2()->isClassDeclaration();
474                     assert(cd);
475                     assert(cd->vthis);
476                     Logger::println("Indexing to 'this'");
477                     src = DtoLoad(DtoGEPi(thisval, 0, cd->vthis->ir.irField->index, ".vthis"));
478                 }
479                 if (depth > 1) {
480                     src = DtoBitCast(src, getVoidPtrType());
481                     LLValue* dst = DtoBitCast(frame, getVoidPtrType());
482                     DtoMemCpy(dst, src, DtoConstSize_t((depth-1) * PTRSIZE),
483                         getABITypeAlign(getVoidPtrType()));
484                 }
485                 // Copy nestArg into framelist; the outer frame is not in the list of pointers
486                 src = DtoBitCast(src, types[depth-1]);
487                 LLValue* gep = DtoGEPi(frame, 0, depth-1);
488                 DtoAlignedStore(src, gep);
489             }
490            
491             // store context in IrFunction
492             irfunction->nestedVar = frame;
493            
494             // go through all nested vars and assign addresses where possible.
495             for (std::set<VarDeclaration*>::iterator i=fd->nestedVars.begin(); i!=fd->nestedVars.end(); ++i)
496             {
497                 VarDeclaration* vd = *i;
498                
499                 LLValue* gep = DtoGEPi(frame, 0, vd->ir.irLocal->nestedIndex, vd->toChars());
500                 if (vd->isParameter()) {
501                     Logger::println("nested param: %s", vd->toChars());
502                     LOG_SCOPE
503                     LLValue* value = vd->ir.irLocal->value;
504                     if (llvm::isa<llvm::AllocaInst>(value->getUnderlyingObject())) {
505                         Logger::println("Copying to nested frame");
506                         // The parameter value is an alloca'd stack slot.
507                         // Copy to the nesting frame and leave the alloca for
508                         // the optimizers to clean up.
509                         DtoStore(DtoLoad(value), gep);
510                         gep->takeName(value);
511                         vd->ir.irLocal->value = gep;
512                         vd->ir.irLocal->byref = false;
513                     } else {
514                         Logger::println("Adding pointer to nested frame");
515                         // The parameter value is something else, such as a
516                         // passed-in pointer (for 'ref' or 'out' parameters) or
517                         // a pointer arg with byval attribute.
518                         // Store the address into the frame and set the byref flag.
519                         DtoAlignedStore(vd->ir.irLocal->value, gep);
520                         vd->ir.irLocal->byref = true;
521                     }
522                 } else if (vd->isRef() || vd->isOut()) {
523                     // This slot is initialized in DtoNestedInit, to handle things like byref foreach variables
524                     // which move around in memory.
525                     vd->ir.irLocal->byref = true;
526                 } else {
527                     Logger::println("nested var:   %s", vd->toChars());
528                     if (vd->ir.irLocal->value)
529                         Logger::cout() << "Pre-existing value: " << *vd->ir.irLocal->value << '\n';
530                     assert(!vd->ir.irLocal->value);
531                     vd->ir.irLocal->value = gep;
532                     vd->ir.irLocal->byref = false;
533                 }
534             }
535         } else if (FuncDeclaration* parFunc = getParentFunc(fd, true)) {
536             // Propagate context arg properties if the context arg is passed on unmodified.
537             fd->ir.irFunc->frameType = parFunc->ir.irFunc->frameType;
538             fd->ir.irFunc->depth = parFunc->ir.irFunc->depth;
539         }
540     }
541     else {
542         assert(0 && "Not implemented yet");
543     }
544 }
Note: See TracBrowser for help on using the browser.
Copyright © 2008, LDC Development Team.