Mercurial > projects > ldc
diff gen/functions.cpp @ 100:5071469303d4 trunk
[svn r104] TONS OF FIXES.
Split up declaration, constant initializer gen and definition for globals, structs, classes and functions.
Improved ClassInfo support (not complete), not in vtable yet.
Fixed a bunch of forward reference problems.
Much more. Major commit! :)
author | lindquist |
---|---|
date | Fri, 16 Nov 2007 08:21:47 +0100 |
parents | |
children | 027b8d8b71ec |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gen/functions.cpp Fri Nov 16 08:21:47 2007 +0100 @@ -0,0 +1,662 @@ +#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<llvm::FunctionType>(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<const llvm::Type*> 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<const llvm::Type*> 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<llvm::OpaqueType>(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<const llvm::Type*> 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; i<n; ++i) { + args.push_back(i8pty); + } + } + else + assert(0); + + const llvm::FunctionType* fty = llvm::FunctionType::get(llvm::Type::VoidTy, args, false); + + if (!f->llvmType) + 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<llvm::FunctionType>(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<llvm::Function>(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<const llvm::Type*>(), 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<llvm::FunctionType>(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<llvm::Constant>(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<const llvm::Type*> nestTypes; + int j = 0; + if (parentNested) { + nestTypes.push_back(parentNested->getType()); + j++; + } + for (std::set<VarDeclaration*>::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<VarDeclaration*>::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<const llvm::Type*> 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<llvm::Value*> 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); +} + +//////////////////////////////////////////////////////////////////////////////////////////