Mercurial > projects > ldc
comparison gen/nested.cpp @ 1212:df2227fdc860
For the outermost function needing a context frame, use the address of that
frame as the nest argument instead of the address of a single-element list
containing only that frame address.
This saves some stack space and reduces memory accesses.
author | Frits van Bommel <fvbommel wxs.nl> |
---|---|
date | Mon, 13 Apr 2009 04:09:08 +0200 |
parents | 3d4581761b4c |
children | 9430d4959ab4 |
comparison
equal
deleted
inserted
replaced
1211:50dc0db06238 | 1212:df2227fdc860 |
---|---|
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. Each function with variables |
29 /// accessed by nested functions puts them in a struct, and appends a | 29 /// accessed by nested functions puts them in a struct, and appends a |
30 /// pointer to that struct to it's local copy of the list. | 30 /// pointer to that struct to it's local copy of the list. |
31 /// As an additional optimization, if the list has length one it's not | |
32 /// generated; the only element is used directly instead. | |
31 NCHybrid | 33 NCHybrid |
32 }; | 34 }; |
33 | 35 |
34 static cl::opt<NestedCtxType> nestedCtx("nested-ctx", | 36 static cl::opt<NestedCtxType> nestedCtx("nested-ctx", |
35 cl::desc("How to construct a nested function's context:"), | 37 cl::desc("How to construct a nested function's context:"), |
100 assert(vd->ir.irLocal->value); | 102 assert(vd->ir.irLocal->value); |
101 val = DtoBitCast(val, vd->ir.irLocal->value->getType(), vd->toChars()); | 103 val = DtoBitCast(val, vd->ir.irLocal->value->getType(), vd->toChars()); |
102 return new DVarValue(astype, vd, val); | 104 return new DVarValue(astype, vd, val); |
103 } | 105 } |
104 else if (nestedCtx == NCHybrid) { | 106 else if (nestedCtx == NCHybrid) { |
105 FuncDeclaration *parentfunc = getParentFunc(vd); | 107 FuncDeclaration* parentfunc = getParentFunc(irfunc->decl); |
106 assert(parentfunc && "No parent function for nested variable?"); | 108 assert(parentfunc && "No parent function for nested function?"); |
109 Logger::println("Parent function: %s", parentfunc->toChars()); | |
107 | 110 |
108 LLValue* val = DtoBitCast(ctx, LLPointerType::getUnqual(parentfunc->ir.irFunc->framesType)); | 111 LLValue* val = DtoBitCast(ctx, LLPointerType::getUnqual(parentfunc->ir.irFunc->framesType)); |
109 val = DtoGEPi(val, 0, vd->ir.irLocal->nestedDepth); | 112 Logger::cout() << "Context: " << *val << '\n'; |
110 val = DtoAlignedLoad(val, (std::string(".frame.") + parentfunc->toChars()).c_str()); | 113 |
114 if (!parentfunc->ir.irFunc->elidedCtxList) { | |
115 val = DtoGEPi(val, 0, vd->ir.irLocal->nestedDepth); | |
116 val = DtoAlignedLoad(val, (std::string(".frame.") + vdparent->toChars()).c_str()); | |
117 } | |
111 val = DtoGEPi(val, 0, vd->ir.irLocal->nestedIndex, vd->toChars()); | 118 val = DtoGEPi(val, 0, vd->ir.irLocal->nestedIndex, vd->toChars()); |
112 if (vd->ir.irLocal->byref) | 119 if (vd->ir.irLocal->byref) |
113 val = DtoAlignedLoad(val); | 120 val = DtoAlignedLoad(val); |
114 return new DVarValue(astype, vd, val); | 121 return new DVarValue(astype, vd, val); |
115 } | 122 } |
121 void DtoNestedInit(VarDeclaration* vd) | 128 void DtoNestedInit(VarDeclaration* vd) |
122 { | 129 { |
123 Logger::println("DtoNestedInit for %s", vd->toChars()); | 130 Logger::println("DtoNestedInit for %s", vd->toChars()); |
124 LOG_SCOPE | 131 LOG_SCOPE |
125 | 132 |
126 LLValue* nestedVar = gIR->func()->decl->ir.irFunc->nestedVar; | 133 IrFunction* irfunc = gIR->func()->decl->ir.irFunc; |
134 LLValue* nestedVar = irfunc->nestedVar; | |
127 | 135 |
128 if (nestedCtx == NCArray) { | 136 if (nestedCtx == NCArray) { |
129 // alloca as usual if no value already | 137 // alloca as usual if no value already |
130 if (!vd->ir.irLocal->value) | 138 if (!vd->ir.irLocal->value) |
131 vd->ir.irLocal->value = DtoAlloca(DtoType(vd->type), vd->toChars()); | 139 vd->ir.irLocal->value = DtoAlloca(DtoType(vd->type), vd->toChars()); |
141 } | 149 } |
142 else if (nestedCtx == NCHybrid) { | 150 else if (nestedCtx == NCHybrid) { |
143 assert(vd->ir.irLocal->value && "Nested variable without storage?"); | 151 assert(vd->ir.irLocal->value && "Nested variable without storage?"); |
144 if (!vd->isParameter() && (vd->isRef() || vd->isOut())) { | 152 if (!vd->isParameter() && (vd->isRef() || vd->isOut())) { |
145 Logger::println("Initializing non-parameter byref value"); | 153 Logger::println("Initializing non-parameter byref value"); |
146 LLValue* framep = DtoGEPi(nestedVar, 0, vd->ir.irLocal->nestedDepth); | 154 LLValue* frame; |
147 | 155 if (!irfunc->elidedCtxList) { |
148 FuncDeclaration *parentfunc = getParentFunc(vd); | 156 LLValue* framep = DtoGEPi(nestedVar, 0, vd->ir.irLocal->nestedDepth); |
149 assert(parentfunc && "No parent function for nested variable?"); | 157 |
150 LLValue* frame = DtoAlignedLoad(framep, (std::string(".frame.") + parentfunc->toChars()).c_str()); | 158 FuncDeclaration *parentfunc = getParentFunc(vd); |
151 | 159 assert(parentfunc && "No parent function for nested variable?"); |
160 frame = DtoAlignedLoad(framep, (std::string(".frame.") + parentfunc->toChars()).c_str()); | |
161 } else { | |
162 frame = nestedVar; | |
163 } | |
152 LLValue* slot = DtoGEPi(frame, 0, vd->ir.irLocal->nestedIndex); | 164 LLValue* slot = DtoGEPi(frame, 0, vd->ir.irLocal->nestedIndex); |
153 DtoAlignedStore(vd->ir.irLocal->value, slot); | 165 DtoAlignedStore(vd->ir.irLocal->value, slot); |
154 } else { | 166 } else { |
155 // Already initialized in DtoCreateNestedContext | 167 // Already initialized in DtoCreateNestedContext |
156 } | 168 } |
291 Dsymbol* par = fd->toParent2(); | 303 Dsymbol* par = fd->toParent2(); |
292 while (par) { | 304 while (par) { |
293 if (FuncDeclaration* parfd = par->isFuncDeclaration()) { | 305 if (FuncDeclaration* parfd = par->isFuncDeclaration()) { |
294 // skip functions without nested parameters | 306 // skip functions without nested parameters |
295 if (!parfd->nestedVars.empty()) { | 307 if (!parfd->nestedVars.empty()) { |
296 // Copy the types of parent function frames. | |
297 const LLStructType* parft = parfd->ir.irFunc->framesType; | 308 const LLStructType* parft = parfd->ir.irFunc->framesType; |
298 frametypes.insert(frametypes.begin(), parft->element_begin(), parft->element_end()); | 309 if (parfd->ir.irFunc->elidedCtxList) { |
310 // This is the outermost function with a nested context. | |
311 // Its context is not a list of frames, but just the frame itself. | |
312 frametypes.push_back(LLPointerType::getUnqual(parft)); | |
313 } else { | |
314 // Copy the types of parent function frames. | |
315 frametypes.insert(frametypes.begin(), parft->element_begin(), parft->element_end()); | |
316 } | |
299 break; // That's all the info needed. | 317 break; // That's all the info needed. |
300 } | 318 } |
301 } else if (ClassDeclaration* parcd = par->isClassDeclaration()) { | 319 } else if (ClassDeclaration* parcd = par->isClassDeclaration()) { |
302 // skip | 320 // skip |
303 } else { | 321 } else { |
305 } | 323 } |
306 par = par->toParent2(); | 324 par = par->toParent2(); |
307 } | 325 } |
308 } | 326 } |
309 unsigned depth = frametypes.size(); | 327 unsigned depth = frametypes.size(); |
328 | |
329 if (Logger::enabled()) { | |
330 Logger::println("Frame types: "); | |
331 LOG_SCOPE; | |
332 for (TypeVec::iterator i=frametypes.begin(); i!=frametypes.end(); ++i) | |
333 Logger::cout() << **i << '\n'; | |
334 } | |
310 | 335 |
311 // Construct a struct for the direct nested variables of this function, and update their indices to match. | 336 // Construct a struct for the direct nested variables of this function, and update their indices to match. |
312 // TODO: optimize ordering for minimal space usage? | 337 // TODO: optimize ordering for minimal space usage? |
313 TypeVec types; | 338 TypeVec types; |
314 for (std::set<VarDeclaration*>::iterator i=fd->nestedVars.begin(); i!=fd->nestedVars.end(); ++i) | 339 for (std::set<VarDeclaration*>::iterator i=fd->nestedVars.begin(); i!=fd->nestedVars.end(); ++i) |
334 if (Logger::enabled()) { | 359 if (Logger::enabled()) { |
335 Logger::println("Nested var: %s", vd->toChars()); | 360 Logger::println("Nested var: %s", vd->toChars()); |
336 Logger::cout() << "of type: " << *types.back() << '\n'; | 361 Logger::cout() << "of type: " << *types.back() << '\n'; |
337 } | 362 } |
338 } | 363 } |
364 | |
339 // Append current frame type to frame type list | 365 // Append current frame type to frame type list |
340 const LLType* frameType = LLStructType::get(types); | 366 const LLStructType* frameType = LLStructType::get(types); |
341 frametypes.push_back(LLPointerType::getUnqual(frameType)); | 367 const LLStructType* nestedVarsTy = NULL; |
342 | 368 if (!frametypes.empty()) { |
343 // make struct type for nested frame list | 369 assert(depth > 0); |
344 const LLStructType* nestedVarsTy = LLStructType::get(frametypes); | 370 frametypes.push_back(LLPointerType::getUnqual(frameType)); |
371 | |
372 // make struct type for nested frame list | |
373 nestedVarsTy = LLStructType::get(frametypes); | |
374 } else { | |
375 assert(depth == 0); | |
376 // For the outer function, just use the frame as the context | |
377 // instead of alloca'ing a single-element framelist and passing | |
378 // a pointer to that. | |
379 nestedVarsTy = frameType; | |
380 fd->ir.irFunc->elidedCtxList = true; | |
381 } | |
382 | |
383 Logger::cout() << "nestedVarsTy = " << *nestedVarsTy << '\n'; | |
345 | 384 |
346 // Store type in IrFunction | 385 // Store type in IrFunction |
347 IrFunction* irfunction = fd->ir.irFunc; | 386 IrFunction* irfunction = fd->ir.irFunc; |
348 irfunction->framesType = nestedVarsTy; | 387 irfunction->framesType = nestedVarsTy; |
349 | 388 |
350 // alloca it | 389 LLValue* nestedVars = NULL; |
390 | |
391 // Create frame for current function and append to frames list | |
351 // FIXME: For D2, this should be a gc_malloc (or similar) call, not alloca | 392 // FIXME: For D2, this should be a gc_malloc (or similar) call, not alloca |
352 LLValue* nestedVars = DtoAlloca(nestedVarsTy, ".frame_list"); | 393 LLValue* frame = DtoAlloca(frameType, ".frame"); |
353 | 394 |
354 // copy parent frames into beginning | 395 // copy parent frames into beginning |
355 if (depth != 0) | 396 if (depth != 0) |
356 { | 397 { |
398 // alloca frame list first | |
399 nestedVars = DtoAlloca(nestedVarsTy, ".frame_list"); | |
400 | |
357 LLValue* src = irfunction->nestArg; | 401 LLValue* src = irfunction->nestArg; |
358 if (!src) | 402 if (!src) |
359 { | 403 { |
360 assert(irfunction->thisArg); | 404 assert(irfunction->thisArg); |
361 assert(fd->isMember2()); | 405 assert(fd->isMember2()); |
364 assert(cd); | 408 assert(cd); |
365 assert(cd->vthis); | 409 assert(cd->vthis); |
366 Logger::println("Indexing to 'this'"); | 410 Logger::println("Indexing to 'this'"); |
367 src = DtoLoad(DtoGEPi(thisval, 0, cd->vthis->ir.irField->index, ".vthis")); | 411 src = DtoLoad(DtoGEPi(thisval, 0, cd->vthis->ir.irField->index, ".vthis")); |
368 } | 412 } |
369 src = DtoBitCast(src, getVoidPtrType()); | 413 if (depth == 1) { |
370 LLValue* dst = DtoBitCast(nestedVars, getVoidPtrType()); | 414 // Just copy nestArg into framelist; the outer frame is not a list of pointers |
371 DtoMemCpy(dst, src, DtoConstSize_t(depth * PTRSIZE), | 415 // but a direct pointer. |
372 getABITypeAlign(getVoidPtrType())); | 416 src = DtoBitCast(src, frametypes[0]); |
373 } | 417 LLValue* gep = DtoGEPi(nestedVars, 0, 0); |
374 | 418 DtoAlignedStore(src, gep); |
375 // Create frame for current function and append to frames list | 419 } else { |
376 LLValue* frame = DtoAlloca(frameType, ".frame"); | 420 src = DtoBitCast(src, getVoidPtrType()); |
377 // store current frame in list | 421 LLValue* dst = DtoBitCast(nestedVars, getVoidPtrType()); |
378 DtoAlignedStore(frame, DtoGEPi(nestedVars, 0, depth)); | 422 DtoMemCpy(dst, src, DtoConstSize_t(depth * PTRSIZE), |
423 getABITypeAlign(getVoidPtrType())); | |
424 } | |
425 // store current frame in list | |
426 DtoAlignedStore(frame, DtoGEPi(nestedVars, 0, depth)); | |
427 } else { | |
428 // Use frame as context directly | |
429 nestedVars = frame; | |
430 } | |
379 | 431 |
380 // store context in IrFunction | 432 // store context in IrFunction |
381 irfunction->nestedVar = nestedVars; | 433 irfunction->nestedVar = nestedVars; |
382 | 434 |
383 // go through all nested vars and assign addresses where possible. | 435 // go through all nested vars and assign addresses where possible. |
401 assert(!vd->ir.irLocal->value); | 453 assert(!vd->ir.irLocal->value); |
402 vd->ir.irLocal->value = gep; | 454 vd->ir.irLocal->value = gep; |
403 vd->ir.irLocal->byref = false; | 455 vd->ir.irLocal->byref = false; |
404 } | 456 } |
405 } | 457 } |
458 } else if (FuncDeclaration* parFunc = getParentFunc(fd)) { | |
459 // Propagate context arg properties if the context arg is passed on unmodified. | |
460 fd->ir.irFunc->framesType = parFunc->ir.irFunc->framesType; | |
461 fd->ir.irFunc->elidedCtxList = parFunc->ir.irFunc->elidedCtxList; | |
406 } | 462 } |
407 } | 463 } |
408 else { | 464 else { |
409 assert(0 && "Not implemented yet"); | 465 assert(0 && "Not implemented yet"); |
410 } | 466 } |