#include "gen/llvm.h" #include "mtype.h" #include "aggregate.h" #include "init.h" #include "declaration.h" #include "template.h" #include "module.h" #include "statement.h" #include "gen/irstate.h" #include "gen/tollvm.h" #include "gen/runtime.h" #include "gen/arrays.h" #include "gen/logger.h" #include "gen/functions.h" #include "gen/todebug.h" #include "gen/classes.h" const llvm::FunctionType* DtoFunctionType(Type* type, const llvm::Type* thistype, bool ismain) { TypeFunction* f = (TypeFunction*)type; assert(f != 0); if (type->llvmType != NULL) { return llvm::cast(type->llvmType->get()); } bool typesafeVararg = false; if (f->linkage == LINKd && f->varargs == 1) { typesafeVararg = true; } // return value type const llvm::Type* rettype; const llvm::Type* actualRettype; Type* rt = f->next; bool retinptr = false; bool usesthis = false; if (ismain) { rettype = llvm::Type::Int32Ty; actualRettype = rettype; } else { assert(rt); if (DtoIsPassedByRef(rt)) { rettype = llvm::PointerType::get(DtoType(rt)); actualRettype = llvm::Type::VoidTy; f->llvmRetInPtr = retinptr = true; } else { rettype = DtoType(rt); actualRettype = rettype; } } // parameter types std::vector paramvec; if (retinptr) { Logger::cout() << "returning through pointer parameter: " << *rettype << '\n'; paramvec.push_back(rettype); } if (thistype) { paramvec.push_back(thistype); usesthis = true; } if (typesafeVararg) { ClassDeclaration* ti = Type::typeinfo; ti->toObjFile(); DtoConstInitClass(ti); assert(ti->llvmInitZ); std::vector types; types.push_back(DtoSize_t()); types.push_back(llvm::PointerType::get(llvm::PointerType::get(ti->llvmInitZ->getType()))); const llvm::Type* t1 = llvm::StructType::get(types); paramvec.push_back(llvm::PointerType::get(t1)); paramvec.push_back(llvm::PointerType::get(llvm::Type::Int8Ty)); } size_t n = Argument::dim(f->parameters); for (int i=0; i < n; ++i) { Argument* arg = Argument::getNth(f->parameters, i); // ensure scalar Type* argT = DtoDType(arg->type); assert(argT); if ((arg->storageClass & STCref) || (arg->storageClass & STCout)) { //assert(arg->vardecl); //arg->vardecl->refparam = true; } else arg->llvmCopy = true; const llvm::Type* at = DtoType(argT); if (isaStruct(at)) { Logger::println("struct param"); paramvec.push_back(llvm::PointerType::get(at)); } else if (isaArray(at)) { Logger::println("sarray param"); assert(argT->ty == Tsarray); //paramvec.push_back(llvm::PointerType::get(at->getContainedType(0))); paramvec.push_back(llvm::PointerType::get(at)); } else if (llvm::isa(at)) { Logger::println("opaque param"); assert(argT->ty == Tstruct || argT->ty == Tclass); paramvec.push_back(llvm::PointerType::get(at)); } else { if (!arg->llvmCopy) { Logger::println("ref param"); at = llvm::PointerType::get(at); } else { Logger::println("in param"); } paramvec.push_back(at); } } // construct function type bool isvararg = !typesafeVararg && f->varargs; llvm::FunctionType* functype = llvm::FunctionType::get(actualRettype, paramvec, isvararg); f->llvmRetInPtr = retinptr; f->llvmUsesThis = usesthis; if (!f->llvmType) f->llvmType = new llvm::PATypeHolder(functype); else assert(functype == f->llvmType->get()); return functype; } ////////////////////////////////////////////////////////////////////////////////////////// static const llvm::FunctionType* DtoVaFunctionType(FuncDeclaration* fdecl) { TypeFunction* f = (TypeFunction*)fdecl->type; assert(f != 0); const llvm::PointerType* i8pty = llvm::PointerType::get(llvm::Type::Int8Ty); std::vector args; if (fdecl->llvmInternal == LLVMva_start) { args.push_back(i8pty); } else if (fdecl->llvmInternal == LLVMva_intrinsic) { size_t n = Argument::dim(f->parameters); for (size_t i=0; illvmType) f->llvmType = new llvm::PATypeHolder(fty); else assert(fty == f->llvmType->get()); return fty; } ////////////////////////////////////////////////////////////////////////////////////////// const llvm::FunctionType* DtoFunctionType(FuncDeclaration* fdecl) { if ((fdecl->llvmInternal == LLVMva_start) || (fdecl->llvmInternal == LLVMva_intrinsic)) { return DtoVaFunctionType(fdecl); } // type has already been resolved if (fdecl->type->llvmType != 0) { return llvm::cast(fdecl->type->llvmType->get()); } const llvm::Type* thisty = NULL; if (fdecl->needThis()) { if (AggregateDeclaration* ad = fdecl->isMember()) { Logger::print("isMember = this is: %s\n", ad->type->toChars()); thisty = DtoType(ad->type); Logger::cout() << "this llvm type: " << *thisty << '\n'; if (isaStruct(thisty) || thisty == gIR->topstruct()->recty.get()) thisty = llvm::PointerType::get(thisty); } else assert(0); } else if (fdecl->isNested()) { thisty = llvm::PointerType::get(llvm::Type::Int8Ty); } const llvm::FunctionType* functype = DtoFunctionType(fdecl->type, thisty, fdecl->isMain()); return functype; } ////////////////////////////////////////////////////////////////////////////////////////// static llvm::Function* DtoDeclareVaFunction(FuncDeclaration* fdecl) { TypeFunction* f = (TypeFunction*)DtoDType(fdecl->type); const llvm::FunctionType* fty = DtoVaFunctionType(fdecl); llvm::Constant* fn = 0; if (fdecl->llvmInternal == LLVMva_start) { fn = gIR->module->getOrInsertFunction("llvm.va_start", fty); assert(fn); } else if (fdecl->llvmInternal == LLVMva_intrinsic) { fn = gIR->module->getOrInsertFunction(fdecl->llvmInternal1, fty); assert(fn); } else assert(0); llvm::Function* func = llvm::dyn_cast(fn); assert(func); assert(func->isIntrinsic()); fdecl->llvmValue = func; return func; } ////////////////////////////////////////////////////////////////////////////////////////// void DtoDeclareFunction(FuncDeclaration* fdecl) { Logger::println("DtoDeclareFunction(%s)", fdecl->toPrettyChars()); LOG_SCOPE; if (fdecl->llvmRunTimeHack) { Logger::println("runtime hack func chars: %s", fdecl->toChars()); if (!fdecl->llvmValue) fdecl->llvmValue = LLVM_D_GetRuntimeFunction(gIR->module, fdecl->toChars()); return; } if (fdecl->isUnitTestDeclaration()) { Logger::attention("ignoring unittest declaration: %s", fdecl->toChars()); return; } bool declareOnly = false; if (fdecl->parent) { if (TemplateInstance* tinst = fdecl->parent->isTemplateInstance()) { TemplateDeclaration* tempdecl = tinst->tempdecl; if (tempdecl->llvmInternal == LLVMva_start) { Logger::println("magic va_start found"); fdecl->llvmInternal = LLVMva_start; declareOnly = true; } else if (tempdecl->llvmInternal == LLVMva_arg) { Logger::println("magic va_arg found"); fdecl->llvmInternal = LLVMva_arg; return; } } } if (fdecl->llvmTouched) return; fdecl->llvmTouched = true; if (!fdecl->llvmIRFunc) { fdecl->llvmIRFunc = new IRFunction(fdecl); } // mangled name char* mangled_name; if (fdecl->llvmInternal == LLVMintrinsic) mangled_name = fdecl->llvmInternal1; else mangled_name = fdecl->mangle(); // unit test special handling if (fdecl->isUnitTestDeclaration()) { assert(0 && "no unittests yet"); /*const llvm::FunctionType* fnty = llvm::FunctionType::get(llvm::Type::VoidTy, std::vector(), false); // make the function llvm::Function* func = gIR->module->getFunction(mangled_name); if (func == 0) func = new llvm::Function(fnty,llvm::GlobalValue::InternalLinkage,mangled_name,gIR->module); func->setCallingConv(llvm::CallingConv::Fast); fdecl->llvmValue = func; return func; */ } if (fdecl->llvmInternal == LLVMintrinsic && fdecl->fbody) { error("intrinsics cannot have function bodies"); fatal(); } llvm::Function* vafunc = 0; if ((fdecl->llvmInternal == LLVMva_start) || (fdecl->llvmInternal == LLVMva_intrinsic)) { vafunc = DtoDeclareVaFunction(fdecl); } Type* t = DtoDType(fdecl->type); TypeFunction* f = (TypeFunction*)t; // construct function const llvm::FunctionType* functype = DtoFunctionType(fdecl); llvm::Function* func = vafunc ? vafunc : gIR->module->getFunction(mangled_name); if (!func) func = new llvm::Function(functype, DtoLinkage(fdecl->protection, fdecl->storage_class), mangled_name, gIR->module); else assert(func->getFunctionType() == functype); // add func to IRFunc fdecl->llvmIRFunc->func = func; // calling convention if (!vafunc && fdecl->llvmInternal != LLVMintrinsic) func->setCallingConv(DtoCallingConv(f->linkage)); // template instances should have weak linkage if (!vafunc && fdecl->llvmInternal != LLVMintrinsic && fdecl->parent && DtoIsTemplateInstance(fdecl->parent)) func->setLinkage(llvm::GlobalValue::WeakLinkage); fdecl->llvmValue = func; assert(llvm::isa(f->llvmType->get())); if (fdecl->isMain()) { gIR->mainFunc = func; } // name parameters llvm::Function::arg_iterator iarg = func->arg_begin(); int k = 0; if (f->llvmRetInPtr) { iarg->setName("retval"); f->llvmRetArg = iarg; ++iarg; } if (f->llvmUsesThis) { iarg->setName("this"); ++iarg; } int varargs = -1; if (f->linkage == LINKd && f->varargs == 1) varargs = 0; for (; iarg != func->arg_end(); ++iarg) { Argument* arg = Argument::getNth(f->parameters, k++); //arg->llvmValue = iarg; //Logger::println("identifier: '%s' %p\n", arg->ident->toChars(), arg->ident); if (arg && arg->ident != 0) { if (arg->vardecl) { arg->vardecl->llvmValue = iarg; } iarg->setName(arg->ident->toChars()); } else if (!arg && varargs >= 0) { if (varargs == 0) { iarg->setName("_arguments"); fdecl->llvmArguments = iarg; } else if (varargs == 1) { iarg->setName("_argptr"); fdecl->llvmArgPtr = iarg; } else assert(0); varargs++; } else { iarg->setName("unnamed"); } } if (!declareOnly) gIR->defineQueue.push_back(fdecl); Logger::cout() << "func decl: " << *func << '\n'; } ////////////////////////////////////////////////////////////////////////////////////////// // TODO split this monster up void DtoDefineFunc(FuncDeclaration* fd) { // debug info if (global.params.symdebug) { Module* mo = fd->getModule(); if (!mo->llvmCompileUnit) { mo->llvmCompileUnit = DtoDwarfCompileUnit(mo,false); } fd->llvmDwarfSubProgram = DtoDwarfSubProgram(fd, mo->llvmCompileUnit); } Type* t = DtoDType(fd->type); TypeFunction* f = (TypeFunction*)t; assert(f->llvmType); llvm::Function* func = fd->llvmIRFunc->func; const llvm::FunctionType* functype = func->getFunctionType(); // only members of the current module or template instances maybe be defined if (fd->getModule() == gIR->dmodule || DtoIsTemplateInstance(fd->parent)) { fd->llvmDModule = gIR->dmodule; // handle static constructor / destructor if (fd->isStaticCtorDeclaration() || fd->isStaticDtorDeclaration()) { const llvm::ArrayType* sctor_type = llvm::ArrayType::get(llvm::PointerType::get(functype),1); //Logger::cout() << "static ctor type: " << *sctor_type << '\n'; llvm::Constant* sctor_func = llvm::cast(fd->llvmValue); //Logger::cout() << "static ctor func: " << *sctor_func << '\n'; llvm::Constant* sctor_init = llvm::ConstantArray::get(sctor_type,&sctor_func,1); //Logger::cout() << "static ctor init: " << *sctor_init << '\n'; // output the llvm.global_ctors array const char* varname = fd->isStaticCtorDeclaration() ? "_d_module_ctor_array" : "_d_module_dtor_array"; llvm::GlobalVariable* sctor_arr = new llvm::GlobalVariable(sctor_type, false, llvm::GlobalValue::AppendingLinkage, sctor_init, varname, gIR->module); } // function definition if (fd->fbody != 0) { Logger::println("Doing function body for: %s", fd->toChars()); assert(fd->llvmIRFunc); gIR->functions.push_back(fd->llvmIRFunc); // this handling if (f->llvmUsesThis) { Logger::println("uses this"); if (f->llvmRetInPtr) fd->llvmThisVar = ++func->arg_begin(); else fd->llvmThisVar = func->arg_begin(); assert(fd->llvmThisVar != 0); } if (fd->isMain()) gIR->emitMain = true; llvm::BasicBlock* beginbb = new llvm::BasicBlock("entry",func); llvm::BasicBlock* endbb = new llvm::BasicBlock("endentry",func); //assert(gIR->scopes.empty()); gIR->scopes.push_back(IRScope(beginbb, endbb)); // create alloca point f->llvmAllocaPoint = new llvm::BitCastInst(llvm::ConstantInt::get(llvm::Type::Int32Ty,0,false),llvm::Type::Int32Ty,"alloca point",gIR->scopebb()); gIR->func()->allocapoint = f->llvmAllocaPoint; // give arguments storage size_t n = Argument::dim(f->parameters); for (int i=0; i < n; ++i) { Argument* arg = Argument::getNth(f->parameters, i); if (arg && arg->vardecl) { VarDeclaration* vd = arg->vardecl; if (!vd->llvmNeedsStorage || vd->nestedref || vd->isRef() || vd->isOut() || DtoIsPassedByRef(vd->type)) continue; llvm::Value* a = vd->llvmValue; assert(a); std::string s(a->getName()); Logger::println("giving argument '%s' storage", s.c_str()); s.append("_storage"); llvm::Value* v = new llvm::AllocaInst(a->getType(),s,f->llvmAllocaPoint); gIR->ir->CreateStore(a,v); vd->llvmValue = v; } else { Logger::attention("some unknown argument: %s", arg ? arg->toChars() : 0); } } // debug info if (global.params.symdebug) DtoDwarfFuncStart(fd); llvm::Value* parentNested = NULL; if (FuncDeclaration* fd2 = fd->toParent()->isFuncDeclaration()) { parentNested = fd2->llvmNested; } // construct nested variables struct if (!fd->llvmNestedVars.empty() || parentNested) { std::vector nestTypes; int j = 0; if (parentNested) { nestTypes.push_back(parentNested->getType()); j++; } for (std::set::iterator i=fd->llvmNestedVars.begin(); i!=fd->llvmNestedVars.end(); ++i) { VarDeclaration* vd = *i; vd->llvmNestedIndex = j++; if (vd->isParameter()) { assert(vd->llvmValue); nestTypes.push_back(vd->llvmValue->getType()); } else { nestTypes.push_back(DtoType(vd->type)); } } const llvm::StructType* nestSType = llvm::StructType::get(nestTypes); Logger::cout() << "nested var struct has type:" << '\n' << *nestSType; fd->llvmNested = new llvm::AllocaInst(nestSType,"nestedvars",f->llvmAllocaPoint); if (parentNested) { assert(fd->llvmThisVar); llvm::Value* ptr = gIR->ir->CreateBitCast(fd->llvmThisVar, parentNested->getType(), "tmp"); gIR->ir->CreateStore(ptr, DtoGEPi(fd->llvmNested, 0,0, "tmp")); } for (std::set::iterator i=fd->llvmNestedVars.begin(); i!=fd->llvmNestedVars.end(); ++i) { VarDeclaration* vd = *i; if (vd->isParameter()) { gIR->ir->CreateStore(vd->llvmValue, DtoGEPi(fd->llvmNested, 0, vd->llvmNestedIndex, "tmp")); vd->llvmValue = fd->llvmNested; } } } // copy _argptr to a memory location if (f->linkage == LINKd && f->varargs == 1) { llvm::Value* argptrmem = new llvm::AllocaInst(fd->llvmArgPtr->getType(), "_argptrmem", gIR->topallocapoint()); new llvm::StoreInst(fd->llvmArgPtr, argptrmem, gIR->scopebb()); fd->llvmArgPtr = argptrmem; } // output function body fd->fbody->toIR(gIR); // 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. if (!fd->isMain()) { if (!gIR->scopereturned()) { // pass the previous block into this block if (global.params.symdebug) DtoDwarfFuncEnd(fd); if (func->getReturnType() == llvm::Type::VoidTy) { new llvm::ReturnInst(gIR->scopebb()); } else { new llvm::ReturnInst(llvm::UndefValue::get(func->getReturnType()), gIR->scopebb()); } } } // erase alloca point f->llvmAllocaPoint->eraseFromParent(); f->llvmAllocaPoint = 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(); // if the last block is empty now, it must be unreachable or it's a bug somewhere else // would be nice to figure out how to assert that this is correct llvm::BasicBlock* lastbb = &func->getBasicBlockList().back(); if (lastbb->empty()) { if (lastbb->getNumUses() == 0) lastbb->eraseFromParent(); else { new llvm::UnreachableInst(lastbb); /*if (func->getReturnType() == llvm::Type::VoidTy) { new llvm::ReturnInst(lastbb); } else { new llvm::ReturnInst(llvm::UndefValue::get(func->getReturnType()), lastbb); }*/ } } gIR->functions.pop_back(); } } } ////////////////////////////////////////////////////////////////////////////////////////// void DtoMain() { // emit main function llvm style // int main(int argc, char**argv, char**env); assert(gIR != 0); IRState& ir = *gIR; assert(ir.emitMain && ir.mainFunc); // parameter types std::vector pvec; pvec.push_back((const llvm::Type*)llvm::Type::Int32Ty); const llvm::Type* chPtrType = (const llvm::Type*)llvm::PointerType::get(llvm::Type::Int8Ty); pvec.push_back((const llvm::Type*)llvm::PointerType::get(chPtrType)); pvec.push_back((const llvm::Type*)llvm::PointerType::get(chPtrType)); const llvm::Type* rettype = (const llvm::Type*)llvm::Type::Int32Ty; llvm::FunctionType* functype = llvm::FunctionType::get(rettype, pvec, false); llvm::Function* func = new llvm::Function(functype,llvm::GlobalValue::ExternalLinkage,"main",ir.module); llvm::BasicBlock* bb = new llvm::BasicBlock("entry",func); // call static ctors llvm::Function* fn = LLVM_D_GetRuntimeFunction(ir.module,"_d_run_module_ctors"); llvm::Instruction* apt = new llvm::CallInst(fn,"",bb); // call user main function const llvm::FunctionType* mainty = ir.mainFunc->getFunctionType(); llvm::CallInst* call; if (mainty->getNumParams() > 0) { // main with arguments assert(mainty->getNumParams() == 1); std::vector args; llvm::Function* mfn = LLVM_D_GetRuntimeFunction(ir.module,"_d_main_args"); llvm::Function::arg_iterator argi = func->arg_begin(); args.push_back(argi++); args.push_back(argi++); const llvm::Type* at = mainty->getParamType(0)->getContainedType(0); llvm::Value* arr = new llvm::AllocaInst(at->getContainedType(1)->getContainedType(0), func->arg_begin(), "argstorage", apt); llvm::Value* a = new llvm::AllocaInst(at, "argarray", apt); llvm::Value* ptr = DtoGEPi(a,0,0,"tmp",bb); llvm::Value* v = args[0]; if (v->getType() != DtoSize_t()) v = new llvm::ZExtInst(v, DtoSize_t(), "tmp", bb); new llvm::StoreInst(v,ptr,bb); ptr = DtoGEPi(a,0,1,"tmp",bb); new llvm::StoreInst(arr,ptr,bb); args.push_back(a); new llvm::CallInst(mfn, args.begin(), args.end(), "", bb); call = new llvm::CallInst(ir.mainFunc,a,"ret",bb); } else { // main with no arguments call = new llvm::CallInst(ir.mainFunc,"ret",bb); } call->setCallingConv(ir.mainFunc->getCallingConv()); // call static dtors fn = LLVM_D_GetRuntimeFunction(ir.module,"_d_run_module_dtors"); new llvm::CallInst(fn,"",bb); // return new llvm::ReturnInst(call,bb); } //////////////////////////////////////////////////////////////////////////////////////////