comparison gen/nested.cpp @ 1216:033f18ec1371

Unify %.frames_list and %.frame into a single data structure, generalizing r1212 to all frames instead of just the outer-most one.
author Frits van Bommel <fvbommel wxs.nl>
date Wed, 15 Apr 2009 20:59:19 +0200
parents 9430d4959ab4
children 7977096f0e49
comparison
equal deleted inserted replaced
1215:08f87d8cd101 1216:033f18ec1371
23 // with a parent whose variables are accessed, can use the parent's 23 // with a parent whose variables are accessed, can use the parent's
24 // context. 24 // context.
25 // NOTE: This is what DMD seems to do. 25 // NOTE: This is what DMD seems to do.
26 NCStruct, 26 NCStruct,
27 27
28 /// Context is a list of pointers to structs. Each function with variables 28 /// Context is a list of pointers to structs of variables, followed by the
29 /// accessed by nested functions puts them in a struct, and appends a 29 /// variables of the inner-most function with variables accessed by nested
30 /// pointer to that struct to it's local copy of the list. 30 /// functions. The initial pointers point to similar structs for enclosing
31 /// As an additional optimization, if the list has length one it's not 31 /// functions.
32 /// generated; the only element is used directly instead. 32 /// Only functions whose variables are accessed by nested functions create
33 /// new frames, others just pass on what got passed in.
33 NCHybrid 34 NCHybrid
34 }; 35 };
35 36
36 static cl::opt<NestedCtxType> nestedCtx("nested-ctx", 37 static cl::opt<NestedCtxType> nestedCtx("nested-ctx",
37 cl::desc("How to construct a nested function's context:"), 38 cl::desc("How to construct a nested function's context:"),
47 /****************************************************************************************/ 48 /****************************************************************************************/
48 /*//////////////////////////////////////////////////////////////////////////////////////// 49 /*////////////////////////////////////////////////////////////////////////////////////////
49 // NESTED VARIABLE HELPERS 50 // NESTED VARIABLE HELPERS
50 ////////////////////////////////////////////////////////////////////////////////////////*/ 51 ////////////////////////////////////////////////////////////////////////////////////////*/
51 52
52 static FuncDeclaration* getParentFunc(Dsymbol* sym) { 53 static FuncDeclaration* getParentFunc(Dsymbol* sym, bool stopOnStatic) {
53 if (!sym) 54 if (!sym)
54 return NULL; 55 return NULL;
55 Dsymbol* parent = sym->parent; 56 Dsymbol* parent = sym->parent;
56 assert(parent); 57 assert(parent);
57 while (parent && !parent->isFuncDeclaration()) 58 while (parent && !parent->isFuncDeclaration()) {
59 if (stopOnStatic) {
60 Declaration* decl = sym->isDeclaration();
61 if (decl && decl->isStatic())
62 return NULL;
63 }
58 parent = parent->parent; 64 parent = parent->parent;
65 }
59 66
60 return (parent ? parent->isFuncDeclaration() : NULL); 67 return (parent ? parent->isFuncDeclaration() : NULL);
61 } 68 }
62 69
63 DValue* DtoNestedVariable(Loc loc, Type* astype, VarDeclaration* vd) 70 DValue* DtoNestedVariable(Loc loc, Type* astype, VarDeclaration* vd)
86 { 93 {
87 ClassDeclaration* cd = irfunc->decl->isMember2()->isClassDeclaration(); 94 ClassDeclaration* cd = irfunc->decl->isMember2()->isClassDeclaration();
88 LLValue* val = DtoLoad(irfunc->thisArg); 95 LLValue* val = DtoLoad(irfunc->thisArg);
89 ctx = DtoLoad(DtoGEPi(val, 0,cd->vthis->ir.irField->index, ".vthis")); 96 ctx = DtoLoad(DtoGEPi(val, 0,cd->vthis->ir.irField->index, ".vthis"));
90 } 97 }
98 else if (irfunc->nestedVar)
99 ctx = irfunc->nestedVar;
91 else 100 else
92 ctx = irfunc->nestArg; 101 ctx = irfunc->nestArg;
93 assert(ctx); 102 assert(ctx);
94 103
95 assert(vd->ir.irLocal); 104 assert(vd->ir.irLocal);
104 assert(vd->ir.irLocal->value); 113 assert(vd->ir.irLocal->value);
105 val = DtoBitCast(val, vd->ir.irLocal->value->getType(), vd->toChars()); 114 val = DtoBitCast(val, vd->ir.irLocal->value->getType(), vd->toChars());
106 return new DVarValue(astype, vd, val); 115 return new DVarValue(astype, vd, val);
107 } 116 }
108 else if (nestedCtx == NCHybrid) { 117 else if (nestedCtx == NCHybrid) {
109 FuncDeclaration* parentfunc = getParentFunc(irfunc->decl); 118 LLValue* val = DtoBitCast(ctx, LLPointerType::getUnqual(irfunc->frameType));
110 assert(parentfunc && "No parent function for nested function?");
111 Logger::println("Parent function: %s", parentfunc->toChars());
112
113 LLValue* val = DtoBitCast(ctx, LLPointerType::getUnqual(parentfunc->ir.irFunc->framesType));
114 Logger::cout() << "Context: " << *val << '\n'; 119 Logger::cout() << "Context: " << *val << '\n';
115 120 Logger::cout() << "of type: " << *val->getType() << '\n';
116 if (!parentfunc->ir.irFunc->elidedCtxList) { 121
122 unsigned vardepth = vd->ir.irLocal->nestedDepth;
123 unsigned funcdepth = irfunc->depth;
124
125 Logger::cout() << "Variable: " << vd->toChars() << '\n';
126 Logger::cout() << "Variable depth: " << vardepth << '\n';
127 Logger::cout() << "Function: " << irfunc->decl->toChars() << '\n';
128 Logger::cout() << "Function depth: " << funcdepth << '\n';
129
130 if (vardepth == funcdepth) {
131 // This is not always handled above because functions without
132 // variables accessed by nested functions don't create new frames.
133 Logger::println("Same depth");
134 } else {
135 // Load frame pointer and index that...
136 Logger::println("Lower depth");
117 val = DtoGEPi(val, 0, vd->ir.irLocal->nestedDepth); 137 val = DtoGEPi(val, 0, vd->ir.irLocal->nestedDepth);
138 Logger::cout() << "Frame index: " << *val << '\n';
118 val = DtoAlignedLoad(val, (std::string(".frame.") + vdparent->toChars()).c_str()); 139 val = DtoAlignedLoad(val, (std::string(".frame.") + vdparent->toChars()).c_str());
140 Logger::cout() << "Frame: " << *val << '\n';
119 } 141 }
120 val = DtoGEPi(val, 0, vd->ir.irLocal->nestedIndex, vd->toChars()); 142 val = DtoGEPi(val, 0, vd->ir.irLocal->nestedIndex, vd->toChars());
121 if (vd->ir.irLocal->byref) 143 Logger::cout() << "Addr: " << *val << '\n';
144 Logger::cout() << "of type: " << *val->getType() << '\n';
145 if (vd->ir.irLocal->byref) {
122 val = DtoAlignedLoad(val); 146 val = DtoAlignedLoad(val);
147 Logger::cout() << "Was byref, now: " << *val << '\n';
148 Logger::cout() << "of type: " << *val->getType() << '\n';
149 }
150
123 return new DVarValue(astype, vd, val); 151 return new DVarValue(astype, vd, val);
124 } 152 }
125 else { 153 else {
126 assert(0 && "Not implemented yet"); 154 assert(0 && "Not implemented yet");
127 } 155 }
149 177
150 DtoAlignedStore(val, gep); 178 DtoAlignedStore(val, gep);
151 } 179 }
152 else if (nestedCtx == NCHybrid) { 180 else if (nestedCtx == NCHybrid) {
153 assert(vd->ir.irLocal->value && "Nested variable without storage?"); 181 assert(vd->ir.irLocal->value && "Nested variable without storage?");
182
154 if (!vd->isParameter() && (vd->isRef() || vd->isOut())) { 183 if (!vd->isParameter() && (vd->isRef() || vd->isOut())) {
155 Logger::println("Initializing non-parameter byref value"); 184 unsigned vardepth = vd->ir.irLocal->nestedDepth;
156 LLValue* frame; 185
157 if (!irfunc->elidedCtxList) { 186 LLValue* val = NULL;
158 LLValue* framep = DtoGEPi(nestedVar, 0, vd->ir.irLocal->nestedDepth); 187 // Retrieve frame pointer
188 if (vardepth == irfunc->depth) {
189 val = nestedVar;
190 } else {
191 FuncDeclaration *parentfunc = getParentFunc(vd, true);
192 assert(parentfunc && "No parent function for nested variable?");
159 193
160 FuncDeclaration *parentfunc = getParentFunc(vd); 194 val = DtoGEPi(val, 0, vardepth);
161 assert(parentfunc && "No parent function for nested variable?"); 195 val = DtoAlignedLoad(val, (std::string(".frame.") + parentfunc->toChars()).c_str());
162 frame = DtoAlignedLoad(framep, (std::string(".frame.") + parentfunc->toChars()).c_str()); 196 }
163 } else { 197 val = DtoGEPi(val, 0, vd->ir.irLocal->nestedIndex, vd->toChars());
164 frame = nestedVar; 198 DtoAlignedStore(vd->ir.irLocal->value, val);
165 }
166 LLValue* slot = DtoGEPi(frame, 0, vd->ir.irLocal->nestedIndex);
167 DtoAlignedStore(vd->ir.irLocal->value, slot);
168 } else { 199 } else {
169 // Already initialized in DtoCreateNestedContext 200 // Already initialized in DtoCreateNestedContext
170 } 201 }
171 } 202 }
172 else { 203 else {
204 else 235 else
205 { 236 {
206 return getNullPtr(getVoidPtrType()); 237 return getNullPtr(getVoidPtrType());
207 } 238 }
208 if (nestedCtx == NCHybrid) { 239 if (nestedCtx == NCHybrid) {
209 // If sym is a nested function, and its parent elided the context list but the 240 // If sym is a nested function, and it's parent context is different than the
210 // context we got didn't, we need to index to the first frame. 241 // one we got, adjust it.
211 if (FuncDeclaration* fd = getParentFunc(sym->isFuncDeclaration())) { 242
243 if (FuncDeclaration* fd = getParentFunc(sym->isFuncDeclaration(), true)) {
212 Logger::println("For nested function, parent is %s", fd->toChars()); 244 Logger::println("For nested function, parent is %s", fd->toChars());
213 FuncDeclaration* ctxfd = irfunc->decl; 245 FuncDeclaration* ctxfd = irfunc->decl;
214 Logger::println("Current function is %s", ctxfd->toChars()); 246 Logger::println("Current function is %s", ctxfd->toChars());
215 if (fromParent) { 247 if (fromParent) {
216 ctxfd = getParentFunc(ctxfd); 248 ctxfd = getParentFunc(ctxfd, true);
217 assert(ctxfd && "Context from outer function, but no outer function?"); 249 assert(ctxfd && "Context from outer function, but no outer function?");
218 } 250 }
219 Logger::println("Context is from %s", ctxfd->toChars()); 251 Logger::println("Context is from %s", ctxfd->toChars());
220 if (fd->ir.irFunc->elidedCtxList && !ctxfd->ir.irFunc->elidedCtxList) { 252
221 Logger::println("Adjusting to remove context frame list", ctxfd->toChars()); 253 unsigned neededDepth = fd->ir.irFunc->depth;
222 val = DtoBitCast(val, LLPointerType::getUnqual(ctxfd->ir.irFunc->framesType)); 254 unsigned ctxDepth = ctxfd->ir.irFunc->depth;
223 val = DtoGEPi(val, 0, 0); 255
256 Logger::cout() << "Needed depth: " << neededDepth << '\n';
257 Logger::cout() << "Context depth: " << ctxDepth << '\n';
258
259 if (neededDepth >= ctxDepth) {
260 assert(neededDepth <= ctxDepth + 1 && "How are we going more than one nesting level up?");
261 // fd needs the same context as we do, so all is well
262 Logger::println("Calling sibling function or directly nested function");
263 } else {
264 val = DtoBitCast(val, LLPointerType::getUnqual(ctxfd->ir.irFunc->frameType));
265 val = DtoGEPi(val, 0, neededDepth);
224 val = DtoAlignedLoad(val, (std::string(".frame.") + fd->toChars()).c_str()); 266 val = DtoAlignedLoad(val, (std::string(".frame.") + fd->toChars()).c_str());
225 } 267 }
226 } 268 }
227 } 269 }
228 Logger::cout() << "result = " << *val << '\n'; 270 Logger::cout() << "result = " << *val << '\n';
324 // construct nested variables array 366 // construct nested variables array
325 if (!fd->nestedVars.empty()) 367 if (!fd->nestedVars.empty())
326 { 368 {
327 Logger::println("has nested frame"); 369 Logger::println("has nested frame");
328 // start with adding all enclosing parent frames until a static parent is reached 370 // start with adding all enclosing parent frames until a static parent is reached
371
372 const LLStructType* innerFrameType = NULL;
373 unsigned depth = 0;
374 if (!fd->isStatic()) {
375 if (FuncDeclaration* parfd = getParentFunc(fd, true)) {
376 innerFrameType = parfd->ir.irFunc->frameType;
377 if (innerFrameType)
378 depth = parfd->ir.irFunc->depth + 1;
379 }
380 }
381 fd->ir.irFunc->depth = depth;
382
383 Logger::cout() << "Function " << fd->toChars() << " has depth " << depth << '\n';
384
329 typedef std::vector<const LLType*> TypeVec; 385 typedef std::vector<const LLType*> TypeVec;
330 TypeVec frametypes; 386 TypeVec types;
331 if (!fd->isStatic()) { 387 if (depth != 0) {
332 Dsymbol* par = fd->toParent2(); 388 assert(innerFrameType);
333 while (par) { 389 // Add frame pointer types for all but last frame
334 if (FuncDeclaration* parfd = par->isFuncDeclaration()) { 390 if (depth > 1) {
335 // skip functions without nested parameters 391 for (unsigned i = 0; i < (depth - 1); ++i) {
336 if (!parfd->nestedVars.empty()) { 392 types.push_back(innerFrameType->getElementType(i));
337 const LLStructType* parft = parfd->ir.irFunc->framesType;
338 if (parfd->ir.irFunc->elidedCtxList) {
339 // This is the outermost function with a nested context.
340 // Its context is not a list of frames, but just the frame itself.
341 frametypes.push_back(LLPointerType::getUnqual(parft));
342 } else {
343 // Copy the types of parent function frames.
344 frametypes.insert(frametypes.begin(), parft->element_begin(), parft->element_end());
345 }
346 break; // That's all the info needed.
347 }
348 } else if (ClassDeclaration* parcd = par->isClassDeclaration()) {
349 // skip
350 } else {
351 break;
352 } 393 }
353 par = par->toParent2(); 394 }
354 } 395 // Add frame pointer type for last frame
355 } 396 types.push_back(LLPointerType::getUnqual(innerFrameType));
356 unsigned depth = frametypes.size(); 397 }
357 398
358 if (Logger::enabled()) { 399 if (Logger::enabled()) {
359 Logger::println("Frame types: "); 400 Logger::println("Frame types: ");
360 LOG_SCOPE; 401 LOG_SCOPE;
361 for (TypeVec::iterator i=frametypes.begin(); i!=frametypes.end(); ++i) 402 for (TypeVec::iterator i = types.begin(); i != types.end(); ++i)
362 Logger::cout() << **i << '\n'; 403 Logger::cout() << **i << '\n';
363 } 404 }
364 405
365 // Construct a struct for the direct nested variables of this function, and update their indices to match. 406 // Add the direct nested variables of this function, and update their indices to match.
366 // TODO: optimize ordering for minimal space usage? 407 // TODO: optimize ordering for minimal space usage?
367 TypeVec types;
368 for (std::set<VarDeclaration*>::iterator i=fd->nestedVars.begin(); i!=fd->nestedVars.end(); ++i) 408 for (std::set<VarDeclaration*>::iterator i=fd->nestedVars.begin(); i!=fd->nestedVars.end(); ++i)
369 { 409 {
370 VarDeclaration* vd = *i; 410 VarDeclaration* vd = *i;
371 if (!vd->ir.irLocal) 411 if (!vd->ir.irLocal)
372 vd->ir.irLocal = new IrLocal(vd); 412 vd->ir.irLocal = new IrLocal(vd);
373 413
414 vd->ir.irLocal->nestedIndex = types.size();
374 vd->ir.irLocal->nestedDepth = depth; 415 vd->ir.irLocal->nestedDepth = depth;
375 vd->ir.irLocal->nestedIndex = types.size();
376 if (vd->isParameter()) { 416 if (vd->isParameter()) {
377 // Parameters already have storage associated with them (to handle byref etc.), 417 // Parameters already have storage associated with them (to handle byref etc.),
378 // so handle specially for now by storing a pointer instead of a value. 418 // so handle specially for now by storing a pointer instead of a value.
379 assert(vd->ir.irLocal->value); 419 assert(vd->ir.irLocal->value);
380 // FIXME: don't do this for normal parameters? 420 // FIXME: don't do this for normal parameters?
391 } 431 }
392 } 432 }
393 433
394 // Append current frame type to frame type list 434 // Append current frame type to frame type list
395 const LLStructType* frameType = LLStructType::get(types); 435 const LLStructType* frameType = LLStructType::get(types);
396 const LLStructType* nestedVarsTy = NULL; 436
397 if (!frametypes.empty()) { 437 Logger::cout() << "frameType = " << *frameType << '\n';
398 assert(depth > 0);
399 frametypes.push_back(LLPointerType::getUnqual(frameType));
400
401 // make struct type for nested frame list
402 nestedVarsTy = LLStructType::get(frametypes);
403 } else {
404 assert(depth == 0);
405 // For the outer function, just use the frame as the context
406 // instead of alloca'ing a single-element framelist and passing
407 // a pointer to that.
408 nestedVarsTy = frameType;
409 fd->ir.irFunc->elidedCtxList = true;
410 }
411
412 Logger::cout() << "nestedVarsTy = " << *nestedVarsTy << '\n';
413 438
414 // Store type in IrFunction 439 // Store type in IrFunction
415 IrFunction* irfunction = fd->ir.irFunc; 440 IrFunction* irfunction = fd->ir.irFunc;
416 irfunction->framesType = nestedVarsTy; 441 irfunction->frameType = frameType;
417
418 LLValue* nestedVars = NULL;
419 442
420 // Create frame for current function and append to frames list 443 // Create frame for current function and append to frames list
421 // FIXME: For D2, this should be a gc_malloc (or similar) call, not alloca 444 // FIXME: For D2, this should be a gc_malloc (or similar) call, not alloca
422 LLValue* frame = DtoAlloca(frameType, ".frame"); 445 LLValue* frame = DtoAlloca(frameType, ".frame");
423 446
424 // copy parent frames into beginning 447 // copy parent frames into beginning
425 if (depth != 0) 448 if (depth != 0) {
426 {
427 // alloca frame list first
428 nestedVars = DtoAlloca(nestedVarsTy, ".frame_list");
429
430 LLValue* src = irfunction->nestArg; 449 LLValue* src = irfunction->nestArg;
431 if (!src) 450 if (!src) {
432 {
433 assert(irfunction->thisArg); 451 assert(irfunction->thisArg);
434 assert(fd->isMember2()); 452 assert(fd->isMember2());
435 LLValue* thisval = DtoLoad(irfunction->thisArg); 453 LLValue* thisval = DtoLoad(irfunction->thisArg);
436 ClassDeclaration* cd = fd->isMember2()->isClassDeclaration(); 454 ClassDeclaration* cd = fd->isMember2()->isClassDeclaration();
437 assert(cd); 455 assert(cd);
438 assert(cd->vthis); 456 assert(cd->vthis);
439 Logger::println("Indexing to 'this'"); 457 Logger::println("Indexing to 'this'");
440 src = DtoLoad(DtoGEPi(thisval, 0, cd->vthis->ir.irField->index, ".vthis")); 458 src = DtoLoad(DtoGEPi(thisval, 0, cd->vthis->ir.irField->index, ".vthis"));
441 } 459 }
442 if (depth == 1) { 460 if (depth > 1) {
443 // Just copy nestArg into framelist; the outer frame is not a list of pointers
444 // but a direct pointer.
445 src = DtoBitCast(src, frametypes[0]);
446 LLValue* gep = DtoGEPi(nestedVars, 0, 0);
447 DtoAlignedStore(src, gep);
448 } else {
449 src = DtoBitCast(src, getVoidPtrType()); 461 src = DtoBitCast(src, getVoidPtrType());
450 LLValue* dst = DtoBitCast(nestedVars, getVoidPtrType()); 462 LLValue* dst = DtoBitCast(frame, getVoidPtrType());
451 DtoMemCpy(dst, src, DtoConstSize_t(depth * PTRSIZE), 463 DtoMemCpy(dst, src, DtoConstSize_t((depth-1) * PTRSIZE),
452 getABITypeAlign(getVoidPtrType())); 464 getABITypeAlign(getVoidPtrType()));
453 } 465 }
454 // store current frame in list 466 // Copy nestArg into framelist; the outer frame is not in the list of pointers
455 DtoAlignedStore(frame, DtoGEPi(nestedVars, 0, depth)); 467 src = DtoBitCast(src, types[depth-1]);
456 } else { 468 LLValue* gep = DtoGEPi(frame, 0, depth-1);
457 // Use frame as context directly 469 DtoAlignedStore(src, gep);
458 nestedVars = frame;
459 } 470 }
460 471
461 // store context in IrFunction 472 // store context in IrFunction
462 irfunction->nestedVar = nestedVars; 473 irfunction->nestedVar = frame;
463 474
464 // go through all nested vars and assign addresses where possible. 475 // go through all nested vars and assign addresses where possible.
465 for (std::set<VarDeclaration*>::iterator i=fd->nestedVars.begin(); i!=fd->nestedVars.end(); ++i) 476 for (std::set<VarDeclaration*>::iterator i=fd->nestedVars.begin(); i!=fd->nestedVars.end(); ++i)
466 { 477 {
467 VarDeclaration* vd = *i; 478 VarDeclaration* vd = *i;
482 assert(!vd->ir.irLocal->value); 493 assert(!vd->ir.irLocal->value);
483 vd->ir.irLocal->value = gep; 494 vd->ir.irLocal->value = gep;
484 vd->ir.irLocal->byref = false; 495 vd->ir.irLocal->byref = false;
485 } 496 }
486 } 497 }
487 } else if (FuncDeclaration* parFunc = getParentFunc(fd)) { 498 } else if (FuncDeclaration* parFunc = getParentFunc(fd, true)) {
488 // Propagate context arg properties if the context arg is passed on unmodified. 499 // Propagate context arg properties if the context arg is passed on unmodified.
489 fd->ir.irFunc->framesType = parFunc->ir.irFunc->framesType; 500 fd->ir.irFunc->frameType = parFunc->ir.irFunc->frameType;
490 fd->ir.irFunc->elidedCtxList = parFunc->ir.irFunc->elidedCtxList; 501 fd->ir.irFunc->depth = parFunc->ir.irFunc->depth;
491 } 502 }
492 } 503 }
493 else { 504 else {
494 assert(0 && "Not implemented yet"); 505 assert(0 && "Not implemented yet");
495 } 506 }