comparison gen/nested.cpp @ 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
comparison
equal deleted inserted replaced
1208:2a37f4745ddd 1209:8699c450a1a0
20 /// a pointer to its context. (linked-list style) 20 /// a pointer to its context. (linked-list style)
21 // FIXME: implement 21 // FIXME: implement
22 // TODO: Functions without any variables accessed by nested functions, but 22 // TODO: Functions without any variables accessed by nested functions, but
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 NCStruct, 26 NCStruct,
26 27
27 /// Context is an array of pointers to nested contexts. Each function with variables 28 /// Context is a list of pointers to structs. Each function with variables
28 /// accessed by nested functions puts them in a struct, and appends a pointer to that 29 /// accessed by nested functions puts them in a struct, and appends a
29 /// struct to the array. 30 /// pointer to that struct to it's local copy of the list.
30 // FIXME: implement
31 NCHybrid 31 NCHybrid
32 }; 32 };
33 33
34 static cl::opt<NestedCtxType> nestedCtx("nested-ctx", 34 static cl::opt<NestedCtxType> nestedCtx("nested-ctx",
35 cl::desc("How to construct a nested function's context:"), 35 cl::desc("How to construct a nested function's context:"),
36 cl::ZeroOrMore, 36 cl::ZeroOrMore,
37 cl::values( 37 cl::values(
38 clEnumValN(NCArray, "array", "Array of pointers to variables (including multi-level)"), 38 clEnumValN(NCArray, "array", "Array of pointers to variables (including multi-level)"),
39 //clEnumValN(NCStruct, "struct", "Struct of variables (with multi-level via linked list)"), 39 //clEnumValN(NCStruct, "struct", "Struct of variables (with multi-level via linked list)"),
40 //clEnumValN(NCHybrid, "hybrid", "Array of pointers to structs of variables"), 40 clEnumValN(NCHybrid, "hybrid", "List of pointers to structs of variables, one per level."),
41 clEnumValEnd), 41 clEnumValEnd),
42 cl::init(NCArray)); 42 cl::init(NCHybrid));
43 43
44 44
45 /****************************************************************************************/ 45 /****************************************************************************************/
46 /*//////////////////////////////////////////////////////////////////////////////////////// 46 /*////////////////////////////////////////////////////////////////////////////////////////
47 // NESTED VARIABLE HELPERS 47 // NESTED VARIABLE HELPERS
48 ////////////////////////////////////////////////////////////////////////////////////////*/ 48 ////////////////////////////////////////////////////////////////////////////////////////*/
49 49
50 static FuncDeclaration* getParentFunc(Dsymbol* sym) {
51 Dsymbol* parent = sym->parent;
52 assert(parent);
53 while (parent && !parent->isFuncDeclaration())
54 parent = parent->parent;
55
56 return (parent ? parent->isFuncDeclaration() : NULL);
57 }
58
50 DValue* DtoNestedVariable(Loc loc, Type* astype, VarDeclaration* vd) 59 DValue* DtoNestedVariable(Loc loc, Type* astype, VarDeclaration* vd)
51 { 60 {
61 Logger::println("DtoNestedVariable for %s @ %s", vd->toChars(), loc.toChars());
62 LOG_SCOPE;
63
52 //////////////////////////////////// 64 ////////////////////////////////////
53 // Locate context value 65 // Locate context value
54 66
55 Dsymbol* vdparent = vd->toParent2(); 67 Dsymbol* vdparent = vd->toParent2();
56 assert(vdparent); 68 assert(vdparent);
87 val = DtoLoad(val); 99 val = DtoLoad(val);
88 assert(vd->ir.irLocal->value); 100 assert(vd->ir.irLocal->value);
89 val = DtoBitCast(val, vd->ir.irLocal->value->getType(), vd->toChars()); 101 val = DtoBitCast(val, vd->ir.irLocal->value->getType(), vd->toChars());
90 return new DVarValue(astype, vd, val); 102 return new DVarValue(astype, vd, val);
91 } 103 }
104 else if (nestedCtx == NCHybrid) {
105 FuncDeclaration *parentfunc = getParentFunc(vd);
106 assert(parentfunc && "No parent function for nested variable?");
107
108 LLValue* val = DtoBitCast(ctx, LLPointerType::getUnqual(parentfunc->ir.irFunc->framesType));
109 val = DtoGEPi(val, 0, vd->ir.irLocal->nestedDepth);
110 val = DtoLoad(val, (std::string(".frame.") + parentfunc->toChars()).c_str());
111 val = DtoGEPi(val, 0, vd->ir.irLocal->nestedIndex, vd->toChars());
112 if (vd->ir.irLocal->byref)
113 val = DtoLoad(val);
114 return new DVarValue(astype, vd, val);
115 }
92 else { 116 else {
93 assert(0 && "Not implemented yet"); 117 assert(0 && "Not implemented yet");
94 } 118 }
95 } 119 }
96 120
97 void DtoNestedInit(VarDeclaration* vd) 121 void DtoNestedInit(VarDeclaration* vd)
98 { 122 {
123 Logger::println("DtoNestedInit for %s", vd->toChars());
124 LOG_SCOPE
125
126 LLValue* nestedVar = gIR->func()->decl->ir.irFunc->nestedVar;
127
99 if (nestedCtx == NCArray) { 128 if (nestedCtx == NCArray) {
100 // alloca as usual if no value already 129 // alloca as usual if no value already
101 if (!vd->ir.irLocal->value) 130 if (!vd->ir.irLocal->value)
102 vd->ir.irLocal->value = DtoAlloca(DtoType(vd->type), vd->toChars()); 131 vd->ir.irLocal->value = DtoAlloca(DtoType(vd->type), vd->toChars());
103 132
104 // store the address into the nested vars array 133 // store the address into the nested vars array
105 assert(vd->ir.irLocal->nestedIndex >= 0); 134 assert(vd->ir.irLocal->nestedIndex >= 0);
106 LLValue* gep = DtoGEPi(gIR->func()->decl->ir.irFunc->nestedVar, 0, vd->ir.irLocal->nestedIndex); 135 LLValue* gep = DtoGEPi(nestedVar, 0, vd->ir.irLocal->nestedIndex);
107 136
108 assert(isaPointer(vd->ir.irLocal->value)); 137 assert(isaPointer(vd->ir.irLocal->value));
109 LLValue* val = DtoBitCast(vd->ir.irLocal->value, getVoidPtrType()); 138 LLValue* val = DtoBitCast(vd->ir.irLocal->value, getVoidPtrType());
110 139
111 DtoStore(val, gep); 140 DtoStore(val, gep);
141 }
142 else if (nestedCtx == NCHybrid) {
143 assert(vd->ir.irLocal->value && "Nested variable without storage?");
144 if (!vd->isParameter() && (vd->isRef() || vd->isOut())) {
145 Logger::println("Initializing non-parameter byref value");
146 LLValue* framep = DtoGEPi(nestedVar, 0, vd->ir.irLocal->nestedDepth);
147
148 FuncDeclaration *parentfunc = getParentFunc(vd);
149 assert(parentfunc && "No parent function for nested variable?");
150 LLValue* frame = DtoLoad(framep, (std::string(".frame.") + parentfunc->toChars()).c_str());
151
152 LLValue* slot = DtoGEPi(frame, 0, vd->ir.irLocal->nestedIndex);
153 DtoStore(vd->ir.irLocal->value, slot);
154 } else {
155 // Already initialized in DtoCreateNestedContext
156 }
112 } 157 }
113 else { 158 else {
114 assert(0 && "Not implemented yet"); 159 assert(0 && "Not implemented yet");
115 } 160 }
116 } 161 }
143 return getNullPtr(getVoidPtrType()); 188 return getNullPtr(getVoidPtrType());
144 } 189 }
145 } 190 }
146 191
147 void DtoCreateNestedContext(FuncDeclaration* fd) { 192 void DtoCreateNestedContext(FuncDeclaration* fd) {
193 Logger::println("DtoCreateNestedContext for %s", fd->toChars());
194 LOG_SCOPE
195
148 if (nestedCtx == NCArray) { 196 if (nestedCtx == NCArray) {
149 // construct nested variables array 197 // construct nested variables array
150 if (!fd->nestedVars.empty()) 198 if (!fd->nestedVars.empty())
151 { 199 {
152 Logger::println("has nested frame"); 200 Logger::println("has nested frame");
226 Logger::println("nested var: %s", vd->toChars()); 274 Logger::println("nested var: %s", vd->toChars());
227 } 275 }
228 276
229 vd->ir.irLocal->nestedIndex = idx++; 277 vd->ir.irLocal->nestedIndex = idx++;
230 } 278 }
231 279 }
232 // fixup nested result variable 280 }
233 #if DMDV2 281 else if (nestedCtx == NCHybrid) {
234 if (fd->vresult && fd->vresult->nestedrefs.dim) 282 // construct nested variables array
235 #else 283 if (!fd->nestedVars.empty())
236 if (fd->vresult && fd->vresult->nestedref) 284 {
237 #endif 285 Logger::println("has nested frame");
238 { 286 // start with adding all enclosing parent frames until a static parent is reached
239 Logger::println("nested vresult value: %s", fd->vresult->toChars()); 287 typedef std::vector<const LLType*> TypeVec;
240 LLValue* gep = DtoGEPi(nestedVars, 0, fd->vresult->ir.irLocal->nestedIndex); 288 TypeVec frametypes;
241 LLValue* val = DtoBitCast(fd->vresult->ir.irLocal->value, getVoidPtrType()); 289 if (!fd->isStatic()) {
242 DtoStore(val, gep); 290 Dsymbol* par = fd->toParent2();
291 while (par) {
292 if (FuncDeclaration* parfd = par->isFuncDeclaration()) {
293 // skip functions without nested parameters
294 if (!parfd->nestedVars.empty()) {
295 // Copy the types of parent function frames.
296 const LLStructType* parft = parfd->ir.irFunc->framesType;
297 frametypes.insert(frametypes.begin(), parft->element_begin(), parft->element_end());
298 break; // That's all the info needed.
299 }
300 } else if (ClassDeclaration* parcd = par->isClassDeclaration()) {
301 // skip
302 } else {
303 break;
304 }
305 par = par->toParent2();
306 }
307 }
308 unsigned depth = frametypes.size();
309
310 // Construct a struct for the direct nested variables of this function, and update their indices to match.
311 // TODO: optimize ordering for minimal space usage?
312 TypeVec types;
313 for (std::set<VarDeclaration*>::iterator i=fd->nestedVars.begin(); i!=fd->nestedVars.end(); ++i)
314 {
315 VarDeclaration* vd = *i;
316 if (!vd->ir.irLocal)
317 vd->ir.irLocal = new IrLocal(vd);
318
319 vd->ir.irLocal->nestedDepth = depth;
320 vd->ir.irLocal->nestedIndex = types.size();
321 if (vd->isParameter()) {
322 // Parameters already have storage associated with them (to handle byref etc.),
323 // so handle specially for now by storing a pointer instead of a value.
324 assert(vd->ir.irLocal->value);
325 // FIXME: don't do this for normal parameters?
326 types.push_back(vd->ir.irLocal->value->getType());
327 } else if (vd->isRef() || vd->isOut()) {
328 // Foreach variables can also be by reference, for instance.
329 types.push_back(DtoType(vd->type->pointerTo()));
330 } else {
331 types.push_back(DtoType(vd->type));
332 }
333 if (Logger::enabled()) {
334 Logger::println("Nested var: %s", vd->toChars());
335 Logger::cout() << "of type: " << *types.back() << '\n';
336 }
337 }
338 // Append current frame type to frame type list
339 const LLType* frameType = LLStructType::get(types);
340 frametypes.push_back(LLPointerType::getUnqual(frameType));
341
342 // make struct type for nested frame list
343 const LLStructType* nestedVarsTy = LLStructType::get(frametypes);
344
345 // Store type in IrFunction
346 IrFunction* irfunction = fd->ir.irFunc;
347 irfunction->framesType = nestedVarsTy;
348
349 // alloca it
350 // FIXME: For D2, this should be a gc_malloc (or similar) call, not alloca
351 LLValue* nestedVars = DtoAlloca(nestedVarsTy, ".frame_list");
352
353 // copy parent frames into beginning
354 if (depth != 0)
355 {
356 LLValue* src = irfunction->nestArg;
357 if (!src)
358 {
359 assert(irfunction->thisArg);
360 assert(fd->isMember2());
361 LLValue* thisval = DtoLoad(irfunction->thisArg);
362 ClassDeclaration* cd = fd->isMember2()->isClassDeclaration();
363 assert(cd);
364 assert(cd->vthis);
365 Logger::println("Indexing to 'this'");
366 src = DtoLoad(DtoGEPi(thisval, 0, cd->vthis->ir.irField->index, ".vthis"));
367 }
368 src = DtoBitCast(src, getVoidPtrType());
369 LLValue* dst = DtoBitCast(nestedVars, getVoidPtrType());
370 DtoMemCpy(dst, src, DtoConstSize_t(depth * PTRSIZE));
371 }
372
373 // Create frame for current function and append to frames list
374 LLValue* frame = DtoAlloca(frameType, ".frame");
375 // store current frame in list
376 DtoStore(frame, DtoGEPi(nestedVars, 0, depth));
377
378 // store context in IrFunction
379 irfunction->nestedVar = nestedVars;
380
381 // go through all nested vars and assign addresses where possible.
382 for (std::set<VarDeclaration*>::iterator i=fd->nestedVars.begin(); i!=fd->nestedVars.end(); ++i)
383 {
384 VarDeclaration* vd = *i;
385
386 LLValue* gep = DtoGEPi(frame, 0, vd->ir.irLocal->nestedIndex, vd->toChars());
387 if (vd->isParameter()) {
388 Logger::println("nested param: %s", vd->toChars());
389 DtoStore(vd->ir.irLocal->value, gep);
390 vd->ir.irLocal->byref = true;
391 } else if (vd->isRef() || vd->isOut()) {
392 // This slot is initialized in DtoNestedInit, to handle things like byref foreach variables
393 // which move around in memory.
394 vd->ir.irLocal->byref = true;
395 } else {
396 Logger::println("nested var: %s", vd->toChars());
397 if (vd->ir.irLocal->value)
398 Logger::cout() << "Pre-existing value: " << *vd->ir.irLocal->value << '\n';
399 assert(!vd->ir.irLocal->value);
400 vd->ir.irLocal->value = gep;
401 vd->ir.irLocal->byref = false;
402 }
243 } 403 }
244 } 404 }
245 } 405 }
246 else { 406 else {
247 assert(0 && "Not implemented yet"); 407 assert(0 && "Not implemented yet");