void AnonDeclaration::semantic(Scope *sc) { //printf("\tAnonDeclaration::semantic %s %p\n", isunion ? "union" : "struct", this); assert(sc->parent); Dsymbol *parent = sc->parent->pastMixin(); AggregateDeclaration *ad = parent->isAggregateDeclaration(); if (!ad || (!ad->isStructDeclaration() && !ad->isClassDeclaration())) { error("can only be a part of an aggregate"); return; } alignment = sc->structalign; if (decl) { sc = sc->push(); sc->stc &= ~(STCauto | STCscope | STCstatic | STCtls | STCgshared); sc->inunion = isunion; sc->offset = 0; sc->flags = 0; for (size_t i = 0; i < decl->dim; i++) { Dsymbol *s = (*decl)[i]; s->semantic(sc); } sc = sc->pop(); } }
/****************************************** * Return elem that evaluates to the static frame pointer for function fd. * If fd is a member function, the returned expression will compute the value * of fd's 'this' variable. * This routine is critical for implementing nested functions. */ elem *getEthis(Loc loc, IRState *irs, Dsymbol *fd) { elem *ethis; FuncDeclaration *thisfd = irs->getFunc(); Dsymbol *fdparent = fd->toParent2(); Dsymbol *fdp = fdparent; /* These two are compiler generated functions for the in and out contracts, * and are called from an overriding function, not just the one they're * nested inside, so this hack is so they'll pass */ if (fdparent != thisfd && (fd->ident == Id::require || fd->ident == Id::ensure)) { FuncDeclaration *fdthis = thisfd; for (size_t i = 0; ; ) { if (i == fdthis->foverrides.dim) { if (i == 0) break; fdthis = fdthis->foverrides[0]; i = 0; continue; } if (fdthis->foverrides[i] == fdp) { fdparent = thisfd; break; } i++; } } //printf("[%s] getEthis(thisfd = '%s', fd = '%s', fdparent = '%s')\n", loc.toChars(), thisfd->toPrettyChars(), fd->toPrettyChars(), fdparent->toPrettyChars()); if (fdparent == thisfd) { /* Going down one nesting level, i.e. we're calling * a nested function from its enclosing function. */ if (irs->sclosure && !(fd->ident == Id::require || fd->ident == Id::ensure)) { ethis = el_var(irs->sclosure); } else if (irs->sthis) { // We have a 'this' pointer for the current function /* If no variables in the current function's frame are * referenced by nested functions, then we can 'skip' * adding this frame into the linked list of stack * frames. */ if (thisfd->hasNestedFrameRefs()) { /* Local variables are referenced, can't skip. * Address of 'sthis' gives the 'this' for the nested * function */ ethis = el_ptr(irs->sthis); } else { ethis = el_var(irs->sthis); } } else { /* No 'this' pointer for current function, */ if (thisfd->hasNestedFrameRefs()) { /* OPframeptr is an operator that gets the frame pointer * for the current function, i.e. for the x86 it gets * the value of EBP */ ethis = el_long(TYnptr, 0); ethis->Eoper = OPframeptr; } else { /* Use NULL if no references to the current function's frame */ ethis = el_long(TYnptr, 0); } } } else { if (!irs->sthis) // if no frame pointer for this function { fd->error(loc, "is a nested function and cannot be accessed from %s", irs->getFunc()->toPrettyChars()); return el_long(TYnptr, 0); // error recovery } /* Go up a nesting level, i.e. we need to find the 'this' * of an enclosing function. * Our 'enclosing function' may also be an inner class. */ ethis = el_var(irs->sthis); Dsymbol *s = thisfd; while (fd != s) { FuncDeclaration *fdp = s->toParent2()->isFuncDeclaration(); //printf("\ts = '%s'\n", s->toChars()); thisfd = s->isFuncDeclaration(); if (thisfd) { /* Enclosing function is a function. */ // Error should have been caught by front end assert(thisfd->isNested() || thisfd->vthis); } else { /* Enclosed by an aggregate. That means the current * function must be a member function of that aggregate. */ AggregateDeclaration *ad = s->isAggregateDeclaration(); if (!ad) { Lnoframe: irs->getFunc()->error(loc, "cannot get frame pointer to %s", fd->toPrettyChars()); return el_long(TYnptr, 0); // error recovery } ClassDeclaration *cd = ad->isClassDeclaration(); ClassDeclaration *cdx = fd->isClassDeclaration(); if (cd && cdx && cdx->isBaseOf(cd, NULL)) break; StructDeclaration *sd = ad->isStructDeclaration(); if (fd == sd) break; if (!ad->isNested() || !ad->vthis) goto Lnoframe; ethis = el_bin(OPadd, TYnptr, ethis, el_long(TYsize_t, ad->vthis->offset)); ethis = el_una(OPind, TYnptr, ethis); } if (fdparent == s->toParent2()) break; /* Remember that frames for functions that have no * nested references are skipped in the linked list * of frames. */ if (fdp && fdp->hasNestedFrameRefs()) ethis = el_una(OPind, TYnptr, ethis); s = s->toParent2(); assert(s); } } #if 0 printf("ethis:\n"); elem_print(ethis); printf("\n"); #endif return ethis; }
void AnonDeclaration::semantic(Scope *sc) { //printf("\tAnonDeclaration::semantic %s %p\n", isunion ? "union" : "struct", this); Scope *scx = NULL; if (scope) { sc = scope; scx = scope; scope = NULL; } unsigned dprogress_save = Module::dprogress; assert(sc->parent); Dsymbol *parent = sc->parent->pastMixin(); AggregateDeclaration *ad = parent->isAggregateDeclaration(); if (!ad || (!ad->isStructDeclaration() && !ad->isClassDeclaration())) { error("can only be a part of an aggregate"); return; } if (decl) { AnonymousAggregateDeclaration aad; int adisunion; if (sc->anonAgg) { ad = sc->anonAgg; adisunion = sc->inunion; } else adisunion = ad->isUnionDeclaration() != NULL; // printf("\tsc->anonAgg = %p\n", sc->anonAgg); // printf("\tad = %p\n", ad); // printf("\taad = %p\n", &aad); sc = sc->push(); sc->anonAgg = &aad; sc->stc &= ~(STCauto | STCscope | STCstatic | STCtls | STCgshared); sc->inunion = isunion; sc->offset = 0; sc->flags = 0; aad.structalign = sc->structalign; aad.parent = ad; for (unsigned i = 0; i < decl->dim; i++) { Dsymbol *s = (Dsymbol *)decl->data[i]; s->semantic(sc); if (isunion) sc->offset = 0; if (aad.sizeok == 2) { break; } } sc = sc->pop(); // If failed due to forward references, unwind and try again later if (aad.sizeok == 2) { ad->sizeok = 2; //printf("\tsetting ad->sizeok %p to 2\n", ad); if (!sc->anonAgg) { scope = scx ? scx : new Scope(*sc); scope->setNoFree(); scope->module->addDeferredSemantic(this); } Module::dprogress = dprogress_save; //printf("\tforward reference %p\n", this); return; } if (sem == 0) { Module::dprogress++; sem = 1; //printf("\tcompleted %p\n", this); } else ;//printf("\talready completed %p\n", this); // 0 sized structs are set to 1 byte if (aad.structsize == 0) { aad.structsize = 1; aad.alignsize = 1; } // Align size of anonymous aggregate //printf("aad.structalign = %d, aad.alignsize = %d, sc->offset = %d\n", aad.structalign, aad.alignsize, sc->offset); ad->alignmember(aad.structalign, aad.alignsize, &sc->offset); //ad->structsize = sc->offset; //printf("sc->offset = %d\n", sc->offset); // Add members of aad to ad //printf("\tadding members of aad (%p) to '%s'\n", &aad, ad->toChars()); for (unsigned i = 0; i < aad.fields.dim; i++) { VarDeclaration *v = (VarDeclaration *)aad.fields.data[i]; #if IN_LLVM v->offset2 = sc->offset; #endif v->offset += sc->offset; #if IN_LLVM if (!v->anonDecl) v->anonDecl = this; #endif ad->fields.push(v); } // Add size of aad to ad if (adisunion) { if (aad.structsize > ad->structsize) ad->structsize = aad.structsize; sc->offset = 0; } else { ad->structsize = sc->offset + aad.structsize; sc->offset = ad->structsize; } if (ad->alignsize < aad.alignsize) ad->alignsize = aad.alignsize; } }
void visit(CallExp *e) { //printf("CallExp(): %s\n", e->toChars()); /* Check each argument that is * passed as 'return scope'. */ Type *t1 = e->e1->type->toBasetype(); TypeFunction *tf = NULL; TypeDelegate *dg = NULL; if (t1->ty == Tdelegate) { dg = (TypeDelegate *)t1; tf = (TypeFunction *)dg->next; } else if (t1->ty == Tfunction) tf = (TypeFunction *)t1; else return; if (e->arguments && e->arguments->dim) { /* j=1 if _arguments[] is first argument, * skip it because it is not passed by ref */ size_t j = (tf->linkage == LINKd && tf->varargs == 1); for (size_t i = j; i < e->arguments->dim; ++i) { Expression *arg = (*e->arguments)[i]; size_t nparams = Parameter::dim(tf->parameters); if (i - j < nparams && i >= j) { Parameter *p = Parameter::getNth(tf->parameters, i - j); const StorageClass stc = tf->parameterStorageClass(p); if ((stc & (STCscope)) && (stc & STCreturn)) arg->accept(this); else if ((stc & (STCref)) && (stc & STCreturn)) escapeByRef(arg, er); } } } // If 'this' is returned, check it too if (e->e1->op == TOKdotvar && t1->ty == Tfunction) { DotVarExp *dve = (DotVarExp *)e->e1; FuncDeclaration *fd = dve->var->isFuncDeclaration(); AggregateDeclaration *ad = NULL; if (global.params.vsafe && tf->isreturn && fd && (ad = fd->isThis()) != NULL) { if (ad->isClassDeclaration() || tf->isscope) // this is 'return scope' dve->e1->accept(this); else if (ad->isStructDeclaration()) // this is 'return ref' escapeByRef(dve->e1, er); } else if (dve->var->storage_class & STCreturn || tf->isreturn) { if (dve->var->storage_class & STCscope) dve->e1->accept(this); else if (dve->var->storage_class & STCref) escapeByRef(dve->e1, er); } } /* If returning the result of a delegate call, the .ptr * field of the delegate must be checked. */ if (dg) { if (tf->isreturn) e->e1->accept(this); } }
void DtoCreateNestedContext(FuncDeclaration* fd) { Logger::println("DtoCreateNestedContext for %s", fd->toChars()); LOG_SCOPE DtoCreateNestedContextType(fd); // construct nested variables array if (!fd->nestedVars.empty()) { IrFunction* irfunction = fd->ir.irFunc; unsigned depth = irfunction->depth; LLStructType *frameType = irfunction->frameType; // Create frame for current function and append to frames list // FIXME: alignment ? LLValue* frame = 0; if (fd->needsClosure()) frame = DtoGcMalloc(frameType, ".frame"); else frame = DtoRawAlloca(frameType, 0, ".frame"); // 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); AggregateDeclaration* cd = fd->isMember2(); assert(cd); assert(cd->vthis); Logger::println("Indexing to 'this'"); if (cd->isStructDeclaration()) src = DtoExtractValue(thisval, cd->vthis->ir.irField->index, ".vthis"); else src = DtoLoad(DtoGEPi(thisval, 0, cd->vthis->ir.irField->index, ".vthis")); } else { src = DtoLoad(src); } if (depth > 1) { src = DtoBitCast(src, getVoidPtrType()); LLValue* dst = DtoBitCast(frame, getVoidPtrType()); DtoMemCpy(dst, src, DtoConstSize_t((depth-1) * PTRSIZE), getABITypeAlign(getVoidPtrType())); } // Copy nestArg into framelist; the outer frame is not in the list of pointers src = DtoBitCast(src, frameType->getContainedType(depth-1)); LLValue* gep = DtoGEPi(frame, 0, depth-1); DtoAlignedStore(src, gep); } // store context in IrFunction irfunction->nestedVar = frame; // 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()); LOG_SCOPE IrParameter* parm = vd->ir.irParam; if (parm->arg->byref) { storeVariable(vd, gep); } else { Logger::println("Copying to nested frame"); // The parameter value is an alloca'd stack slot. // Copy to the nesting frame and leave the alloca for // the optimizers to clean up. DtoStore(DtoLoad(parm->value), gep); gep->takeName(parm->value); parm->value = gep; } } else { Logger::println("nested var: %s", vd->toChars()); assert(!vd->ir.irLocal->value); vd->ir.irLocal->value = gep; } if (global.params.symdebug) { LLSmallVector<LLValue*, 2> addr; dwarfOpOffset(addr, frameType, vd->ir.irLocal->nestedIndex); DtoDwarfLocalVariable(frame, vd, addr); } } } }
void DtoCreateNestedContext(FuncDeclaration* fd) { Logger::println("DtoCreateNestedContext for %s", fd->toChars()); LOG_SCOPE DtoCreateNestedContextType(fd); if (nestedCtx == NCArray) { // 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 int nparelems = 0; if (!fd->isStatic()) { Dsymbol* par = fd->toParent2(); while (par) { if (FuncDeclaration* parfd = par->isFuncDeclaration()) { nparelems += parfd->nestedVars.size(); // stop at first static if (parfd->isStatic()) break; } else if (par->isClassDeclaration()) { // nothing needed } else { break; } par = par->toParent2(); } } int nelems = fd->nestedVars.size() + nparelems; // make array type for nested vars LLType* nestedVarsTy = LLArrayType::get(getVoidPtrType(), nelems); // alloca it // FIXME align ? LLValue* nestedVars = DtoRawAlloca(nestedVarsTy, 0, ".nested_vars"); IrFunction* irfunction = fd->ir.irFunc; // copy parent frame into beginning if (nparelems) { 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); src = DtoLoad(DtoGEPi(thisval, 0,cd->vthis->ir.irField->index, ".vthis")); } else { src = DtoLoad(src); } DtoMemCpy(nestedVars, src, DtoConstSize_t(nparelems*PTRSIZE), getABITypeAlign(getVoidPtrType())); } // store in IrFunction irfunction->nestedVar = nestedVars; // go through all nested vars and assign indices int idx = nparelems; for (std::set<VarDeclaration*>::iterator i=fd->nestedVars.begin(); i!=fd->nestedVars.end(); ++i) { VarDeclaration* vd = *i; if (!vd->ir.irLocal) vd->ir.irLocal = new IrLocal(vd); if (vd->isParameter()) { Logger::println("nested param: %s", vd->toChars()); LLValue* gep = DtoGEPi(nestedVars, 0, idx); LLValue* val = DtoBitCast(vd->ir.irLocal->value, getVoidPtrType()); DtoAlignedStore(val, gep); } else { Logger::println("nested var: %s", vd->toChars()); } vd->ir.irLocal->nestedIndex = idx++; } } } else if (nestedCtx == NCHybrid) { // construct nested variables array if (!fd->nestedVars.empty()) { IrFunction* irfunction = fd->ir.irFunc; unsigned depth = irfunction->depth; LLStructType *frameType = irfunction->frameType; // Create frame for current function and append to frames list // FIXME: alignment ? LLValue* frame = 0; #if DMDV2 if (fd->needsClosure()) frame = DtoGcMalloc(frameType, ".frame"); else #endif frame = DtoRawAlloca(frameType, 0, ".frame"); // 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); #if DMDV2 AggregateDeclaration* cd = fd->isMember2(); #else ClassDeclaration* cd = fd->isMember2()->isClassDeclaration(); #endif assert(cd); assert(cd->vthis); Logger::println("Indexing to 'this'"); #if DMDV2 if (cd->isStructDeclaration()) src = DtoExtractValue(thisval, cd->vthis->ir.irField->index, ".vthis"); else #endif src = DtoLoad(DtoGEPi(thisval, 0, cd->vthis->ir.irField->index, ".vthis")); } else { src = DtoLoad(src); } if (depth > 1) { src = DtoBitCast(src, getVoidPtrType()); LLValue* dst = DtoBitCast(frame, getVoidPtrType()); DtoMemCpy(dst, src, DtoConstSize_t((depth-1) * PTRSIZE), getABITypeAlign(getVoidPtrType())); } // Copy nestArg into framelist; the outer frame is not in the list of pointers src = DtoBitCast(src, frameType->getContainedType(depth-1)); LLValue* gep = DtoGEPi(frame, 0, depth-1); DtoAlignedStore(src, gep); } // store context in IrFunction irfunction->nestedVar = frame; // 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()); LOG_SCOPE LLValue* value = vd->ir.irLocal->value; if (llvm::isa<llvm::AllocaInst>(llvm::GetUnderlyingObject(value))) { Logger::println("Copying to nested frame"); // The parameter value is an alloca'd stack slot. // Copy to the nesting frame and leave the alloca for // the optimizers to clean up. assert(!vd->ir.irLocal->byref); DtoStore(DtoLoad(value), gep); gep->takeName(value); vd->ir.irLocal->value = gep; } else { Logger::println("Adding pointer to nested frame"); // The parameter value is something else, such as a // passed-in pointer (for 'ref' or 'out' parameters) or // a pointer arg with byval attribute. // Store the address into the frame. assert(vd->ir.irLocal->byref); storeVariable(vd, gep); } } else if (vd->isRef() || vd->isOut()) { // This slot is initialized in DtoNestedInit, to handle things like byref foreach variables // which move around in memory. assert(vd->ir.irLocal->byref); } 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; assert(!vd->ir.irLocal->byref); } if (global.params.symdebug) { LLSmallVector<LLValue*, 2> addr; dwarfOpOffset(addr, frameType, vd->ir.irLocal->nestedIndex); DtoDwarfLocalVariable(frame, vd, addr); } } } else if (FuncDeclaration* parFunc = getParentFunc(fd, true)) { // Propagate context arg properties if the context arg is passed on unmodified. DtoDeclareFunction(parFunc); fd->ir.irFunc->frameType = parFunc->ir.irFunc->frameType; fd->ir.irFunc->depth = parFunc->ir.irFunc->depth; } } else { assert(0 && "Not implemented yet"); } }