static void DtoCreateNestedContextType(FuncDeclaration* fd) { Logger::println("DtoCreateNestedContextType for %s", fd->toChars()); LOG_SCOPE DtoDeclareFunction(fd); if (fd->ir.irFunc->nestedContextCreated) return; fd->ir.irFunc->nestedContextCreated = true; if (fd->nestedVars.empty()) { // fill nestedVars size_t nnest = fd->closureVars.dim; for (size_t i = 0; i < nnest; ++i) { VarDeclaration* vd = static_cast<VarDeclaration*>(fd->closureVars.data[i]); fd->nestedVars.insert(vd); } } // 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 LLStructType* innerFrameType = NULL; unsigned depth = -1; if (!fd->isStatic()) { if (FuncDeclaration* parfd = getParentFunc(fd, true)) { // Make sure the parent has already been analyzed. DtoCreateNestedContextType(parfd); innerFrameType = parfd->ir.irFunc->frameType; if (innerFrameType) depth = parfd->ir.irFunc->depth; } } fd->ir.irFunc->depth = ++depth; Logger::cout() << "Function " << fd->toChars() << " has depth " << depth << '\n'; typedef std::vector<LLType*> TypeVec; TypeVec types; if (depth != 0) { assert(innerFrameType); // Add frame pointer types for all but last frame if (depth > 1) { for (unsigned i = 0; i < (depth - 1); ++i) { types.push_back(innerFrameType->getElementType(i)); } } // Add frame pointer type for last frame types.push_back(LLPointerType::getUnqual(innerFrameType)); } if (Logger::enabled() && depth != 0) { Logger::println("Frame types: "); LOG_SCOPE; for (TypeVec::iterator i = types.begin(); i != types.end(); ++i) Logger::cout() << **i << '\n'; } // Add the direct nested variables of this function, and update their indices to match. // TODO: optimize ordering for minimal space usage? 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); vd->ir.irLocal->nestedIndex = types.size(); vd->ir.irLocal->nestedDepth = depth; if (vd->isParameter()) { // Parameters will have storage associated with them (to handle byref etc.), // so handle those cases specially by storing a pointer instead of a value. const IrParameter* irparam = vd->ir.irParam; const bool refout = vd->storage_class & (STCref | STCout); const bool lazy = vd->storage_class & STClazy; const bool byref = irparam->arg->byref; const bool isVthisPtr = irparam->isVthis && !byref; if (!(refout || (byref && !lazy)) || isVthisPtr) { // This will be copied to the nesting frame. if (lazy) types.push_back(irparam->value->getType()->getContainedType(0)); else types.push_back(DtoType(vd->type)); } else { types.push_back(irparam->value->getType()); } } else if (isSpecialRefVar(vd)) { types.push_back(DtoType(vd->type->pointerTo())); } else { types.push_back(DtoType(vd->type)); } if (Logger::enabled()) { Logger::cout() << "Nested var '" << vd->toChars() << "' of type " << *types.back() << "\n"; } } LLStructType* frameType = LLStructType::create(gIR->context(), types, std::string("nest.") + fd->toChars()); Logger::cout() << "frameType = " << *frameType << '\n'; // Store type in IrFunction fd->ir.irFunc->frameType = frameType; } else if (FuncDeclaration* parFunc = getParentFunc(fd, true)) { // Propagate context arg properties if the context arg is passed on unmodified. DtoCreateNestedContextType(parFunc); fd->ir.irFunc->frameType = parFunc->ir.irFunc->frameType; fd->ir.irFunc->depth = parFunc->ir.irFunc->depth; } }
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"); } }
void DtoDefineFunction(FuncDeclaration* fd) { IF_LOG Logger::println("DtoDefineFunction(%s): %s", fd->toPrettyChars(), fd->loc.toChars()); LOG_SCOPE; if (fd->ir.isDefined()) return; if ((fd->type && fd->type->ty == Terror) || (fd->type && fd->type->ty == Tfunction && static_cast<TypeFunction *>(fd->type)->next == NULL) || (fd->type && fd->type->ty == Tfunction && static_cast<TypeFunction *>(fd->type)->next->ty == Terror)) { IF_LOG Logger::println("Ignoring; has error type, no return type or returns error type"); fd->ir.setDefined(); return; } if (fd->semanticRun == PASSsemanticdone) { /* What happened is this function failed semantic3() with errors, * but the errors were gagged. * Try to reproduce those errors, and then fail. */ error(fd->loc, "errors compiling function %s", fd->toPrettyChars()); fd->ir.setDefined(); return; } DtoResolveFunction(fd); if (fd->isUnitTestDeclaration() && !global.params.useUnitTests) { IF_LOG Logger::println("No code generation for unit test declaration %s", fd->toChars()); fd->ir.setDefined(); return; } // Skip array ops implemented in druntime if (fd->isArrayOp && isDruntimeArrayOp(fd)) { IF_LOG Logger::println("No code generation for array op %s implemented in druntime", fd->toChars()); fd->ir.setDefined(); return; } // Check whether the frontend knows that the function is already defined // in some other module (see DMD's FuncDeclaration::toObjFile). for (FuncDeclaration *f = fd; f; ) { if (!f->isInstantiated() && f->inNonRoot()) { IF_LOG Logger::println("Skipping '%s'.", fd->toPrettyChars()); // TODO: Emit as available_externally for inlining purposes instead // (see #673). fd->ir.setDefined(); return; } if (f->isNested()) f = f->toParent2()->isFuncDeclaration(); else break; } DtoDeclareFunction(fd); assert(fd->ir.isDeclared()); // DtoResolveFunction might also set the defined flag for functions we // should not touch. if (fd->ir.isDefined()) return; fd->ir.setDefined(); // We cannot emit nested functions with parents that have not gone through // semantic analysis. This can happen as DMD leaks some template instances // from constraints into the module member list. DMD gets away with being // sloppy as functions in template contraints obviously never need to access // data from the template function itself, but it would still mess up our // nested context creation code. FuncDeclaration* parent = fd; while ((parent = getParentFunc(parent, true))) { if (parent->semanticRun != PASSsemantic3done || parent->semantic3Errors) { IF_LOG Logger::println("Ignoring nested function with unanalyzed parent."); return; } } assert(fd->semanticRun == PASSsemantic3done); assert(fd->ident != Id::empty); if (fd->isUnitTestDeclaration()) { gIR->unitTests.push_back(fd); } else if (fd->isSharedStaticCtorDeclaration()) { gIR->sharedCtors.push_back(fd); } else if (StaticDtorDeclaration *dtorDecl = fd->isSharedStaticDtorDeclaration()) { gIR->sharedDtors.push_front(fd); if (dtorDecl->vgate) gIR->sharedGates.push_front(dtorDecl->vgate); } else if (fd->isStaticCtorDeclaration()) { gIR->ctors.push_back(fd); } else if (StaticDtorDeclaration *dtorDecl = fd->isStaticDtorDeclaration()) { gIR->dtors.push_front(fd); if (dtorDecl->vgate) gIR->gates.push_front(dtorDecl->vgate); } // if this function is naked, we take over right away! no standard processing! if (fd->naked) { DtoDefineNakedFunction(fd); return; } IrFunction *irFunc = getIrFunc(fd); IrFuncTy &irFty = irFunc->irFty; // debug info irFunc->diSubprogram = gIR->DBuilder.EmitSubProgram(fd); Type* t = fd->type->toBasetype(); TypeFunction* f = static_cast<TypeFunction*>(t); // assert(f->ctype); llvm::Function* func = irFunc->func; // is there a body? if (fd->fbody == NULL) return; IF_LOG Logger::println("Doing function body for: %s", fd->toChars()); gIR->functions.push_back(irFunc); if (fd->isMain()) gIR->emitMain = true; func->setLinkage(lowerFuncLinkage(fd)); // On x86_64, always set 'uwtable' for System V ABI compatibility. // TODO: Find a better place for this. // TODO: Is this required for Win64 as well? if (global.params.targetTriple.getArch() == llvm::Triple::x86_64) { func->addFnAttr(LDC_ATTRIBUTE(UWTable)); } #if LDC_LLVM_VER >= 303 if (opts::sanitize != opts::None) { // Set the required sanitizer attribute. if (opts::sanitize == opts::AddressSanitizer) { func->addFnAttr(LDC_ATTRIBUTE(SanitizeAddress)); } if (opts::sanitize == opts::MemorySanitizer) { func->addFnAttr(LDC_ATTRIBUTE(SanitizeMemory)); } if (opts::sanitize == opts::ThreadSanitizer) { func->addFnAttr(LDC_ATTRIBUTE(SanitizeThread)); } } #endif llvm::BasicBlock* beginbb = llvm::BasicBlock::Create(gIR->context(), "", func); llvm::BasicBlock* endbb = llvm::BasicBlock::Create(gIR->context(), "endentry", func); //assert(gIR->scopes.empty()); gIR->scopes.push_back(IRScope(beginbb, endbb)); // create alloca point // this gets erased when the function is complete, so alignment etc does not matter at all llvm::Instruction* allocaPoint = new llvm::AllocaInst(LLType::getInt32Ty(gIR->context()), "alloca point", beginbb); irFunc->allocapoint = allocaPoint; // debug info - after all allocas, but before any llvm.dbg.declare etc gIR->DBuilder.EmitFuncStart(fd); // this hack makes sure the frame pointer elimination optimization is disabled. // this this eliminates a bunch of inline asm related issues. if (fd->hasReturnExp & 8) // has inline asm { // emit a call to llvm_eh_unwind_init LLFunction* hack = GET_INTRINSIC_DECL(eh_unwind_init); gIR->ir->CreateCall(hack, ""); } // give the 'this' argument storage and debug info if (irFty.arg_this) { LLValue* thisvar = irFunc->thisArg; assert(thisvar); LLValue* thismem = thisvar; if (!irFty.arg_this->byref) { thismem = DtoRawAlloca(thisvar->getType(), 0, "this"); // FIXME: align? DtoStore(thisvar, thismem); irFunc->thisArg = thismem; } assert(getIrParameter(fd->vthis)->value == thisvar); getIrParameter(fd->vthis)->value = thismem; gIR->DBuilder.EmitLocalVariable(thismem, fd->vthis); } // give the 'nestArg' storage if (irFty.arg_nest) { LLValue *nestArg = irFunc->nestArg; LLValue *val = DtoRawAlloca(nestArg->getType(), 0, "nestedFrame"); DtoStore(nestArg, val); irFunc->nestArg = val; } // give arguments storage // and debug info if (fd->parameters) { size_t n = irFty.args.size(); assert(n == fd->parameters->dim); for (size_t i=0; i < n; ++i) { Dsymbol* argsym = static_cast<Dsymbol*>(fd->parameters->data[i]); VarDeclaration* vd = argsym->isVarDeclaration(); assert(vd); IrParameter* irparam = getIrParameter(vd); assert(irparam); bool refout = vd->storage_class & (STCref | STCout); bool lazy = vd->storage_class & STClazy; if (!refout && (!irparam->arg->byref || lazy)) { // alloca a stack slot for this first class value arg LLValue* mem = DtoAlloca(irparam->arg->type, vd->ident->toChars()); // let the abi transform the argument back first DImValue arg_dval(vd->type, irparam->value); irFty.getParam(vd->type, i, &arg_dval, mem); // set the arg var value to the alloca irparam->value = mem; } if (global.params.symdebug && !(isaArgument(irparam->value) && isaArgument(irparam->value)->hasByValAttr()) && !refout) gIR->DBuilder.EmitLocalVariable(irparam->value, vd); } } FuncGen fg; irFunc->gen = &fg; DtoCreateNestedContext(fd); if (fd->vresult && ! fd->vresult->nestedrefs.dim // FIXME: not sure here :/ ) { DtoVarDeclaration(fd->vresult); } // D varargs: prepare _argptr and _arguments if (f->linkage == LINKd && f->varargs == 1) { // allocate _argptr (of type core.stdc.stdarg.va_list) LLValue* argptrmem = DtoAlloca(Type::tvalist, "_argptr_mem"); irFunc->_argptr = argptrmem; // initialize _argptr with a call to the va_start intrinsic LLValue* vaStartArg = gABI->prepareVaStart(argptrmem); llvm::CallInst::Create(GET_INTRINSIC_DECL(vastart), vaStartArg, "", gIR->scopebb()); // copy _arguments to a memory location LLType* argumentsType = irFunc->_arguments->getType(); LLValue* argumentsmem = DtoRawAlloca(argumentsType, 0, "_arguments_mem"); new llvm::StoreInst(irFunc->_arguments, argumentsmem, gIR->scopebb()); irFunc->_arguments = argumentsmem; } // output function body codegenFunction(fd->fbody, gIR); irFunc->gen = 0; llvm::BasicBlock* bb = gIR->scopebb(); if (pred_begin(bb) == pred_end(bb) && bb != &bb->getParent()->getEntryBlock()) { // This block is trivially unreachable, so just delete it. // (This is a common case because it happens when 'return' // is the last statement in a function) bb->eraseFromParent(); } else if (!gIR->scopereturned()) { // llvm requires all basic blocks to end with a TerminatorInst but DMD does not put a return statement // in automatically, so we do it here. // pass the previous block into this block gIR->DBuilder.EmitFuncEnd(fd); if (func->getReturnType() == LLType::getVoidTy(gIR->context())) { llvm::ReturnInst::Create(gIR->context(), gIR->scopebb()); } else if (!fd->isMain()) { AsmBlockStatement* asmb = fd->fbody->endsWithAsm(); if (asmb) { assert(asmb->abiret); llvm::ReturnInst::Create(gIR->context(), asmb->abiret, bb); } else { llvm::ReturnInst::Create(gIR->context(), llvm::UndefValue::get(func->getReturnType()), bb); } } else llvm::ReturnInst::Create(gIR->context(), LLConstant::getNullValue(func->getReturnType()), bb); } // erase alloca point if (allocaPoint->getParent()) allocaPoint->eraseFromParent(); allocaPoint = 0; gIR->func()->allocapoint = 0; gIR->scopes.pop_back(); // get rid of the endentry block, it's never used assert(!func->getBasicBlockList().empty()); func->getBasicBlockList().pop_back(); gIR->functions.pop_back(); }
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 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 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); } } } 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; } }
void DtoResolveFunction(FuncDeclaration* fdecl) { if ((!global.params.useUnitTests || !fdecl->type) && fdecl->isUnitTestDeclaration()) { IF_LOG Logger::println("Ignoring unittest %s", fdecl->toPrettyChars()); return; // ignore declaration completely } if (fdecl->ir.isResolved()) return; fdecl->ir.setResolved(); Type *type = fdecl->type; // If errors occurred compiling it, such as bugzilla 6118 if (type && type->ty == Tfunction) { Type *next = static_cast<TypeFunction *>(type)->next; if (!next || next->ty == Terror) return; } //printf("resolve function: %s\n", fdecl->toPrettyChars()); if (fdecl->parent) if (TemplateInstance* tinst = fdecl->parent->isTemplateInstance()) { if (TemplateDeclaration* tempdecl = tinst->tempdecl->isTemplateDeclaration()) { if (tempdecl->llvmInternal == LLVMva_arg) { Logger::println("magic va_arg found"); fdecl->llvmInternal = LLVMva_arg; fdecl->ir.setDefined(); return; // this gets mapped to an instruction so a declaration makes no sence } else if (tempdecl->llvmInternal == LLVMva_start) { Logger::println("magic va_start found"); fdecl->llvmInternal = LLVMva_start; } else if (tempdecl->llvmInternal == LLVMintrinsic) { Logger::println("overloaded intrinsic found"); assert(fdecl->llvmInternal == LLVMintrinsic); assert(fdecl->mangleOverride); } else if (tempdecl->llvmInternal == LLVMinline_asm) { Logger::println("magic inline asm found"); TypeFunction* tf = static_cast<TypeFunction*>(fdecl->type); if (tf->varargs != 1 || (fdecl->parameters && fdecl->parameters->dim != 0)) { tempdecl->error("invalid __asm declaration, must be a D style variadic with no explicit parameters"); fatal(); } fdecl->llvmInternal = LLVMinline_asm; fdecl->ir.setDefined(); return; // this gets mapped to a special inline asm call, no point in going on. } else if (tempdecl->llvmInternal == LLVMinline_ir) { Logger::println("magic inline ir found"); fdecl->llvmInternal = LLVMinline_ir; fdecl->linkage = LINKc; Type* type = fdecl->type; assert(type->ty == Tfunction); static_cast<TypeFunction*>(type)->linkage = LINKc; DtoFunctionType(fdecl); DtoDeclareFunction(fdecl); fdecl->ir.setDefined(); return; } } } DtoFunctionType(fdecl); IF_LOG Logger::println("DtoResolveFunction(%s): %s", fdecl->toPrettyChars(), fdecl->loc.toChars()); LOG_SCOPE; // queue declaration unless the function is abstract without body if (!fdecl->isAbstract() || fdecl->fbody) { DtoDeclareFunction(fdecl); } }