#include "gen/llvm.h" #include "llvm/Target/TargetMachineRegistry.h" #include "mars.h" #include "init.h" #include "id.h" #include "expression.h" #include "gen/tollvm.h" #include "gen/llvmhelpers.h" #include "gen/irstate.h" #include "gen/runtime.h" #include "gen/logger.h" #include "gen/arrays.h" #include "gen/dvalue.h" #include "gen/complex.h" #include "gen/classes.h" #include "gen/functions.h" #include "gen/typeinf.h" #include "gen/todebug.h" /****************************************************************************************/ /*//////////////////////////////////////////////////////////////////////////////////////// // DYNAMIC MEMORY HELPERS ////////////////////////////////////////////////////////////////////////////////////////*/ LLValue* DtoNew(Type* newtype) { // get runtime function llvm::Function* fn = LLVM_D_GetRuntimeFunction(gIR->module, "_d_allocmemoryT"); // get type info LLConstant* ti = DtoTypeInfoOf(newtype); assert(isaPointer(ti)); // call runtime allocator LLValue* mem = gIR->CreateCallOrInvoke(fn, ti, ".gc_mem")->get(); // cast return DtoBitCast(mem, getPtrToType(DtoType(newtype)), ".gc_mem"); } void DtoDeleteMemory(LLValue* ptr) { // get runtime function llvm::Function* fn = LLVM_D_GetRuntimeFunction(gIR->module, "_d_delmemory"); // build args LLSmallVector arg; arg.push_back(DtoBitCast(ptr, getVoidPtrType(), ".tmp")); // call gIR->CreateCallOrInvoke(fn, arg.begin(), arg.end()); } void DtoDeleteClass(LLValue* inst) { // get runtime function llvm::Function* fn = LLVM_D_GetRuntimeFunction(gIR->module, "_d_delclass"); // build args LLSmallVector arg; arg.push_back(DtoBitCast(inst, fn->getFunctionType()->getParamType(0), ".tmp")); // call gIR->CreateCallOrInvoke(fn, arg.begin(), arg.end()); } void DtoDeleteInterface(LLValue* inst) { // get runtime function llvm::Function* fn = LLVM_D_GetRuntimeFunction(gIR->module, "_d_delinterface"); // build args LLSmallVector arg; arg.push_back(DtoBitCast(inst, fn->getFunctionType()->getParamType(0), ".tmp")); // call gIR->CreateCallOrInvoke(fn, arg.begin(), arg.end()); } void DtoDeleteArray(DValue* arr) { // get runtime function llvm::Function* fn = LLVM_D_GetRuntimeFunction(gIR->module, "_d_delarray"); // build args LLSmallVector arg; arg.push_back(DtoArrayLen(arr)); arg.push_back(DtoBitCast(DtoArrayPtr(arr), getVoidPtrType(), ".tmp")); // call gIR->CreateCallOrInvoke(fn, arg.begin(), arg.end()); } /****************************************************************************************/ /*//////////////////////////////////////////////////////////////////////////////////////// // ASSERT HELPER ////////////////////////////////////////////////////////////////////////////////////////*/ void DtoAssert(Loc* loc, DValue* msg) { std::vector args; LLConstant* c; // func const char* fname = msg ? "_d_assert_msg" : "_d_assert"; llvm::Function* fn = LLVM_D_GetRuntimeFunction(gIR->module, fname); // param attrs llvm::PAListPtr palist; int idx = 1; // FIXME: every assert creates a global for the filename !!! c = DtoConstString(loc->filename); // msg param if (msg) { if (DSliceValue* s = msg->isSlice()) { llvm::AllocaInst* alloc = gIR->func()->msgArg; if (!alloc) { alloc = new llvm::AllocaInst(c->getType(), ".assertmsg", gIR->topallocapoint()); DtoSetArray(alloc, DtoArrayLen(s), DtoArrayPtr(s)); gIR->func()->msgArg = alloc; } args.push_back(alloc); } else { args.push_back(msg->getRVal()); } palist = palist.addAttr(idx++, llvm::ParamAttr::ByVal); } // file param llvm::AllocaInst* alloc = gIR->func()->srcfileArg; if (!alloc) { alloc = new llvm::AllocaInst(c->getType(), ".srcfile", gIR->topallocapoint()); gIR->func()->srcfileArg = alloc; } LLValue* ptr = DtoGEPi(alloc, 0,0, "tmp"); DtoStore(c->getOperand(0), ptr); ptr = DtoGEPi(alloc, 0,1, "tmp"); DtoStore(c->getOperand(1), ptr); args.push_back(alloc); palist = palist.addAttr(idx++, llvm::ParamAttr::ByVal); // line param c = DtoConstUint(loc->linnum); args.push_back(c); // call CallOrInvoke* call = gIR->CreateCallOrInvoke(fn, args.begin(), args.end()); call->setParamAttrs(palist); // after assert is always unreachable gIR->ir->CreateUnreachable(); } /****************************************************************************************/ /*//////////////////////////////////////////////////////////////////////////////////////// // LABEL HELPER ////////////////////////////////////////////////////////////////////////////////////////*/ LabelStatement* DtoLabelStatement(Identifier* ident) { FuncDeclaration* fd = gIR->func()->decl; FuncDeclaration::LabelMap::iterator iter = fd->labmap.find(ident->toChars()); if (iter == fd->labmap.end()) { if (fd->returnLabel && fd->returnLabel->ident->equals(ident)) { assert(fd->returnLabel->statement); return fd->returnLabel->statement; } return NULL; } return iter->second; } /****************************************************************************************/ /*//////////////////////////////////////////////////////////////////////////////////////// // GOTO HELPER ////////////////////////////////////////////////////////////////////////////////////////*/ void DtoGoto(Loc* loc, Identifier* target, EnclosingHandler* enclosinghandler, TryFinallyStatement* sourcetf) { assert(!gIR->scopereturned()); LabelStatement* lblstmt = DtoLabelStatement(target); if(!lblstmt) { error(*loc, "the label %s does not exist", target->toChars()); fatal(); } // if the target label is inside inline asm, error if(lblstmt->asmLabel) { error(*loc, "cannot goto to label %s inside an inline asm block", target->toChars()); fatal(); } // find target basic block std::string labelname = gIR->func()->getScopedLabelName(target->toChars()); llvm::BasicBlock*& targetBB = gIR->func()->labelToBB[labelname]; if (targetBB == NULL) targetBB = llvm::BasicBlock::Create("label", gIR->topfunc()); // find finallys between goto and label EnclosingHandler* endfinally = enclosinghandler; while(endfinally != NULL && endfinally != lblstmt->enclosinghandler) { endfinally = endfinally->getEnclosing(); } // error if didn't find tf statement of label if(endfinally != lblstmt->enclosinghandler) error(*loc, "cannot goto into try block"); // goto into finally blocks is forbidden by the spec // though it should not be problematic to implement if(lblstmt->tf != sourcetf) { error(*loc, "spec disallows goto into finally block"); fatal(); } // emit code for finallys between goto and label DtoEnclosingHandlers(enclosinghandler, endfinally); llvm::BranchInst::Create(targetBB, gIR->scopebb()); } /****************************************************************************************/ /*//////////////////////////////////////////////////////////////////////////////////////// // TRY-FINALLY, VOLATILE AND SYNCHRONIZED HELPER ////////////////////////////////////////////////////////////////////////////////////////*/ void EnclosingSynchro::emitCode(IRState * p) { if (s->exp) DtoLeaveMonitor(s->llsync); else DtoLeaveCritical(s->llsync); } EnclosingHandler* EnclosingSynchro::getEnclosing() { return s->enclosinghandler; } //////////////////////////////////////////////////////////////////////////////////////// void EnclosingVolatile::emitCode(IRState * p) { // store-load barrier DtoMemoryBarrier(false, false, true, false); } EnclosingHandler* EnclosingVolatile::getEnclosing() { return v->enclosinghandler; } //////////////////////////////////////////////////////////////////////////////////////// void EnclosingTryFinally::emitCode(IRState * p) { assert(tf->finalbody); tf->finalbody->toIR(p); } EnclosingHandler* EnclosingTryFinally::getEnclosing() { return tf->enclosinghandler; } //////////////////////////////////////////////////////////////////////////////////////// void DtoEnclosingHandlers(EnclosingHandler* start, EnclosingHandler* end) { // verify that end encloses start EnclosingHandler* endfinally = start; while(endfinally != NULL && endfinally != end) { endfinally = endfinally->getEnclosing(); } assert(endfinally == end); // // emit code for finallys between start and end // // since the labelstatements possibly inside are private // and might already exist push a label scope gIR->func()->pushUniqueLabelScope("enclosing"); EnclosingHandler* tf = start; while(tf != end) { tf->emitCode(gIR); tf = tf->getEnclosing(); } gIR->func()->popLabelScope(); } /****************************************************************************************/ /*//////////////////////////////////////////////////////////////////////////////////////// // SYNCHRONIZED SECTION HELPERS ////////////////////////////////////////////////////////////////////////////////////////*/ void DtoEnterCritical(LLValue* g) { LLFunction* fn = LLVM_D_GetRuntimeFunction(gIR->module, "_d_criticalenter"); gIR->CreateCallOrInvoke(fn, g); } void DtoLeaveCritical(LLValue* g) { LLFunction* fn = LLVM_D_GetRuntimeFunction(gIR->module, "_d_criticalexit"); gIR->CreateCallOrInvoke(fn, g); } void DtoEnterMonitor(LLValue* v) { LLFunction* fn = LLVM_D_GetRuntimeFunction(gIR->module, "_d_monitorenter"); v = DtoBitCast(v, fn->getFunctionType()->getParamType(0)); gIR->CreateCallOrInvoke(fn, v); } void DtoLeaveMonitor(LLValue* v) { LLFunction* fn = LLVM_D_GetRuntimeFunction(gIR->module, "_d_monitorexit"); v = DtoBitCast(v, fn->getFunctionType()->getParamType(0)); gIR->CreateCallOrInvoke(fn, v); } /****************************************************************************************/ /*//////////////////////////////////////////////////////////////////////////////////////// // NESTED VARIABLE HELPERS ////////////////////////////////////////////////////////////////////////////////////////*/ static const LLType* get_next_frame_ptr_type(Dsymbol* sc) { assert(sc->isFuncDeclaration() || sc->isClassDeclaration()); Dsymbol* p = sc->toParent2(); if (!p->isFuncDeclaration() && !p->isClassDeclaration()) Logger::println("unexpected parent symbol found while resolving frame pointer - '%s' kind: '%s'", p->toChars(), p->kind()); assert(p->isFuncDeclaration() || p->isClassDeclaration()); if (FuncDeclaration* fd = p->isFuncDeclaration()) { LLValue* v = fd->ir.irFunc->nestedVar; assert(v); return v->getType(); } else if (ClassDeclaration* cd = p->isClassDeclaration()) { return DtoType(cd->type); } else { Logger::println("symbol: '%s' kind: '%s'", sc->toChars(), sc->kind()); assert(0); } } ////////////////////////////////////////////////////////////////////////////////////////// static LLValue* get_frame_ptr_impl(FuncDeclaration* func, Dsymbol* sc, LLValue* v) { LOG_SCOPE; if (sc == func) { return v; } else if (FuncDeclaration* fd = sc->isFuncDeclaration()) { Logger::println("scope is function: %s", fd->toChars()); if (fd->toParent2() == func) { if (!func->ir.irFunc->nestedVar) return NULL; return DtoBitCast(v, func->ir.irFunc->nestedVar->getType()); } v = DtoBitCast(v, get_next_frame_ptr_type(fd)); Logger::cout() << "v = " << *v << '\n'; if (fd->toParent2()->isFuncDeclaration()) { v = DtoGEPi(v, 0,0, "tmp"); v = DtoLoad(v); } else if (ClassDeclaration* cd = fd->toParent2()->isClassDeclaration()) { v = DtoGEPi(v,0,2+cd->vthis->ir.irField->index,"tmp"); v = DtoLoad(v); } else { assert(0); } return get_frame_ptr_impl(func, fd->toParent2(), v); } else if (ClassDeclaration* cd = sc->isClassDeclaration()) { Logger::println("scope is class: %s", cd->toChars()); return get_frame_ptr_impl(func, cd->toParent2(), v); } else { Logger::println("symbol: '%s'", sc->toPrettyChars()); assert(0); } } ////////////////////////////////////////////////////////////////////////////////////////// static LLValue* get_frame_ptr(FuncDeclaration* func) { Logger::println("Resolving context pointer for nested function: '%s'", func->toPrettyChars()); LOG_SCOPE; IrFunction* irfunc = gIR->func(); // in the right scope already if (func == irfunc->decl) return irfunc->decl->ir.irFunc->nestedVar; // use the 'this' pointer LLValue* ptr = irfunc->decl->ir.irFunc->thisVar; assert(ptr); // return the fully resolved frame pointer ptr = get_frame_ptr_impl(func, irfunc->decl, ptr); if (ptr) Logger::cout() << "Found context!" << *ptr; else Logger::cout() << "NULL context!\n"; return ptr; } ////////////////////////////////////////////////////////////////////////////////////////// LLValue* DtoNestedContext(FuncDeclaration* func) { // resolve frame ptr LLValue* ptr = get_frame_ptr(func); Logger::cout() << "Nested context ptr = "; if (ptr) Logger::cout() << *ptr; else Logger::cout() << "NULL"; Logger::cout() << '\n'; return ptr; } ////////////////////////////////////////////////////////////////////////////////////////// static void print_frame_worker(VarDeclaration* vd, Dsymbol* par) { if (vd->toParent2() == par) { Logger::println("found: '%s' kind: '%s'", par->toChars(), par->kind()); return; } Logger::println("diving into: '%s' kind: '%s'", par->toChars(), par->kind()); LOG_SCOPE; print_frame_worker(vd, par->toParent2()); } ////////////////////////////////////////////////////////////////////////////////////////// static void print_nested_frame_list(VarDeclaration* vd, Dsymbol* par) { Logger::println("Frame pointer list for nested var: '%s'", vd->toPrettyChars()); LOG_SCOPE; if (vd->toParent2() != par) print_frame_worker(vd, par); else Logger::println("Found at level 0"); Logger::println("Done"); } ////////////////////////////////////////////////////////////////////////////////////////// LLValue* DtoNestedVariable(VarDeclaration* vd) { // log the frame list IrFunction* irfunc = gIR->func(); if (Logger::enabled()) print_nested_frame_list(vd, irfunc->decl); // resolve frame ptr FuncDeclaration* func = vd->toParent2()->isFuncDeclaration(); assert(func); LLValue* ptr = DtoNestedContext(func); assert(ptr && "nested var, but no context"); // if the nested var is a this pointer it's a class member and not a magic struct // so we're done here! // this happens since 1.033 for some reason... always correct ? if (vd->ident == Id::This) { return ptr; } // handle a "normal" nested variable // we must cast here to be sure. nested classes just have a void* ptr = DtoBitCast(ptr, func->ir.irFunc->nestedVar->getType()); // index nested var and load (if necessary) LLValue* v = DtoGEPi(ptr, 0, vd->ir.irLocal->nestedIndex, "tmp"); // references must be loaded, for normal variables this IS already the variable storage!!! if (vd->isParameter() && (vd->isRef() || vd->isOut() || DtoIsPassedByRef(vd->type))) v = DtoLoad(v); // log and return Logger::cout() << "Nested var ptr = " << *v << '\n'; return v; } /****************************************************************************************/ /*//////////////////////////////////////////////////////////////////////////////////////// // ASSIGNMENT HELPER (store this in that) ////////////////////////////////////////////////////////////////////////////////////////*/ void DtoAssign(Loc& loc, DValue* lhs, DValue* rhs) { Logger::cout() << "DtoAssign(...);\n"; LOG_SCOPE; Type* t = DtoDType(lhs->getType()); Type* t2 = DtoDType(rhs->getType()); if (t->ty == Tstruct) { if (!t->equals(t2)) { // TODO: fix this, use 'rhs' for something DtoAggrZeroInit(lhs->getLVal()); } else if (!rhs->inPlace()) { DtoAggrCopy(lhs->getLVal(), rhs->getRVal()); } } else if (t->ty == Tarray) { // lhs is slice if (DSliceValue* s = lhs->isSlice()) { if (DSliceValue* s2 = rhs->isSlice()) { DtoArrayCopySlices(s, s2); } else if (t->next->toBasetype()->equals(t2)) { DtoArrayInit(loc, s, rhs); } else { DtoArrayCopyToSlice(s, rhs); } } // rhs is slice else if (DSliceValue* s = rhs->isSlice()) { assert(s->getType()->toBasetype() == lhs->getType()->toBasetype()); DtoSetArray(lhs->getLVal(),DtoArrayLen(s),DtoArrayPtr(s)); } // null else if (rhs->isNull()) { DtoSetArrayToNull(lhs->getLVal()); } // reference assignment else { DtoArrayAssign(lhs->getLVal(), rhs->getRVal()); } } else if (t->ty == Tsarray) { if (DtoType(lhs->getType()) == DtoType(rhs->getType())) { DtoStaticArrayCopy(lhs->getLVal(), rhs->getRVal()); } else { DtoArrayInit(loc, lhs, rhs); } } else if (t->ty == Tdelegate) { if (rhs->isNull()) DtoAggrZeroInit(lhs->getLVal()); else if (!rhs->inPlace()) { LLValue* l = lhs->getLVal(); LLValue* r = rhs->getRVal(); Logger::cout() << "assign\nlhs: " << *l << "rhs: " << *r << '\n'; DtoAggrCopy(l, r); } } else if (t->ty == Tclass) { assert(t2->ty == Tclass); // assignment to this in constructor special case if (lhs->isThis()) { LLValue* tmp = rhs->getRVal(); FuncDeclaration* fdecl = gIR->func()->decl; // respecify the this param if (!llvm::isa(fdecl->ir.irFunc->thisVar)) fdecl->ir.irFunc->thisVar = new llvm::AllocaInst(tmp->getType(), "newthis", gIR->topallocapoint()); DtoStore(tmp, fdecl->ir.irFunc->thisVar); } // regular class ref -> class ref assignment else { DtoStore(rhs->getRVal(), lhs->getLVal()); } } else if (t->iscomplex()) { assert(!lhs->isComplex()); LLValue* dst; if (DLRValue* lr = lhs->isLRValue()) { dst = lr->getLVal(); rhs = DtoCastComplex(loc, rhs, lr->getLType()); } else { dst = lhs->getRVal(); } if (DComplexValue* cx = rhs->isComplex()) DtoComplexSet(dst, cx->re, cx->im); else DtoComplexAssign(dst, rhs->getRVal()); } else { LLValue* l = lhs->getLVal(); LLValue* r = rhs->getRVal(); Logger::cout() << "assign\nlhs: " << *l << "rhs: " << *r << '\n'; const LLType* lit = l->getType()->getContainedType(0); if (r->getType() != lit) { // handle lvalue cast assignments if (DLRValue* lr = lhs->isLRValue()) { Logger::println("lvalue cast!"); r = DtoCast(loc, rhs, lr->getLType())->getRVal(); } else { r = DtoCast(loc, rhs, lhs->getType())->getRVal(); } Logger::cout() << "really assign\nlhs: " << *l << "rhs: " << *r << '\n'; assert(r->getType() == l->getType()->getContainedType(0)); } gIR->ir->CreateStore(r, l); } } /****************************************************************************************/ /*//////////////////////////////////////////////////////////////////////////////////////// // NULL VALUE HELPER ////////////////////////////////////////////////////////////////////////////////////////*/ DValue* DtoNullValue(Type* type) { Type* basetype = type->toBasetype(); TY basety = basetype->ty; const LLType* lltype = DtoType(basetype); // complex, needs to be first since complex are also floating if (basetype->iscomplex()) { const LLType* basefp = DtoComplexBaseType(basetype); return new DComplexValue(type, LLConstant::getNullValue(basefp), LLConstant::getNullValue(basefp)); } // integer, floating, pointer and class have no special representation else if (basetype->isintegral() || basetype->isfloating() || basety == Tpointer || basety == Tclass) { return new DConstValue(type, LLConstant::getNullValue(lltype)); } // dynamic array else if (basety == Tarray) { LLValue* len = DtoConstSize_t(0); LLValue* ptr = getNullPtr(getPtrToType(DtoType(basetype->next))); return new DSliceValue(type, len, ptr); } // delegate else if (basety == Tdelegate) { return new DNullValue(type, LLConstant::getNullValue(lltype)); } // unknown std::cout << "unsupported: null value for " << type->toChars() << '\n'; assert(0); return 0; } /****************************************************************************************/ /*//////////////////////////////////////////////////////////////////////////////////////// // CASTING HELPERS ////////////////////////////////////////////////////////////////////////////////////////*/ DValue* DtoCastInt(Loc& loc, DValue* val, Type* _to) { const LLType* tolltype = DtoType(_to); Type* to = DtoDType(_to); Type* from = DtoDType(val->getType()); assert(from->isintegral()); size_t fromsz = from->size(); size_t tosz = to->size(); LLValue* rval = val->getRVal(); if (rval->getType() == tolltype) { return new DImValue(_to, rval); } if (to->isintegral()) { if (fromsz < tosz) { Logger::cout() << "cast to: " << *tolltype << '\n'; if (from->isunsigned() || from->ty == Tbool) { rval = new llvm::ZExtInst(rval, tolltype, "tmp", gIR->scopebb()); } else { rval = new llvm::SExtInst(rval, tolltype, "tmp", gIR->scopebb()); } } else if (fromsz > tosz) { rval = new llvm::TruncInst(rval, tolltype, "tmp", gIR->scopebb()); } else { rval = DtoBitCast(rval, tolltype); } } else if (to->iscomplex()) { return DtoComplex(loc, to, val); } else if (to->isfloating()) { if (from->isunsigned()) { rval = new llvm::UIToFPInst(rval, tolltype, "tmp", gIR->scopebb()); } else { rval = new llvm::SIToFPInst(rval, tolltype, "tmp", gIR->scopebb()); } } else if (to->ty == Tpointer) { Logger::cout() << "cast pointer: " << *tolltype << '\n'; rval = gIR->ir->CreateIntToPtr(rval, tolltype, "tmp"); } else { error(loc, "invalid cast from '%s' to '%s'", val->getType()->toChars(), _to->toChars()); fatal(); } return new DImValue(_to, rval); } DValue* DtoCastPtr(Loc& loc, DValue* val, Type* to) { const LLType* tolltype = DtoType(to); Type* totype = DtoDType(to); Type* fromtype = DtoDType(val->getType()); assert(fromtype->ty == Tpointer || fromtype->ty == Tfunction); LLValue* rval; if (totype->ty == Tpointer || totype->ty == Tclass) { LLValue* src = val->getRVal(); Logger::cout() << "src: " << *src << "to type: " << *tolltype << '\n'; rval = DtoBitCast(src, tolltype); } else if (totype->isintegral()) { rval = new llvm::PtrToIntInst(val->getRVal(), tolltype, "tmp", gIR->scopebb()); } else { error(loc, "invalid cast from '%s' to '%s'", val->getType()->toChars(), to->toChars()); fatal(); } return new DImValue(to, rval); } DValue* DtoCastFloat(Loc& loc, DValue* val, Type* to) { if (val->getType() == to) return val; const LLType* tolltype = DtoType(to); Type* totype = DtoDType(to); Type* fromtype = DtoDType(val->getType()); assert(fromtype->isfloating()); size_t fromsz = fromtype->size(); size_t tosz = totype->size(); LLValue* rval; if (totype->iscomplex()) { return DtoComplex(loc, to, val); } else if (totype->isfloating()) { if ((fromtype->ty == Tfloat80 || fromtype->ty == Tfloat64) && (totype->ty == Tfloat80 || totype->ty == Tfloat64)) { rval = val->getRVal(); } else if ((fromtype->ty == Timaginary80 || fromtype->ty == Timaginary64) && (totype->ty == Timaginary80 || totype->ty == Timaginary64)) { rval = val->getRVal(); } else if (fromsz < tosz) { rval = new llvm::FPExtInst(val->getRVal(), tolltype, "tmp", gIR->scopebb()); } else if (fromsz > tosz) { rval = new llvm::FPTruncInst(val->getRVal(), tolltype, "tmp", gIR->scopebb()); } else { error(loc, "invalid cast from '%s' to '%s'", val->getType()->toChars(), to->toChars()); fatal(); } } else if (totype->isintegral()) { if (totype->isunsigned()) { rval = new llvm::FPToUIInst(val->getRVal(), tolltype, "tmp", gIR->scopebb()); } else { rval = new llvm::FPToSIInst(val->getRVal(), tolltype, "tmp", gIR->scopebb()); } } else { error(loc, "invalid cast from '%s' to '%s'", val->getType()->toChars(), to->toChars()); fatal(); } return new DImValue(to, rval); } DValue* DtoCastDelegate(Loc& loc, DValue* val, Type* to) { LLValue* res = 0; to = to->toBasetype(); if (to->ty == Tdelegate) { const LLType* toll = getPtrToType(DtoType(to)); res = DtoBitCast(val->getRVal(), toll); } else { error(loc, "invalid cast from '%s' to '%s'", val->getType()->toChars(), to->toChars()); fatal(); } return new DImValue(to, res); } DValue* DtoCast(Loc& loc, DValue* val, Type* to) { Type* fromtype = DtoDType(val->getType()); Logger::println("Casting from '%s' to '%s'", fromtype->toChars(), to->toChars()); if (fromtype->isintegral()) { return DtoCastInt(loc, val, to); } else if (fromtype->iscomplex()) { return DtoCastComplex(loc, val, to); } else if (fromtype->isfloating()) { return DtoCastFloat(loc, val, to); } else if (fromtype->ty == Tclass) { return DtoCastClass(val, to); } else if (fromtype->ty == Tarray || fromtype->ty == Tsarray) { return DtoCastArray(loc, val, to); } else if (fromtype->ty == Tpointer || fromtype->ty == Tfunction) { return DtoCastPtr(loc, val, to); } else if (fromtype->ty == Tdelegate) { return DtoCastDelegate(loc, val, to); } else { error(loc, "invalid cast from '%s' to '%s'", val->getType()->toChars(), to->toChars()); fatal(); } } /****************************************************************************************/ /*//////////////////////////////////////////////////////////////////////////////////////// // TEMPLATE HELPERS ////////////////////////////////////////////////////////////////////////////////////////*/ bool DtoIsTemplateInstance(Dsymbol* s) { if (!s) return false; if (s->isTemplateInstance() && !s->isTemplateMixin()) return true; else if (s->parent) return DtoIsTemplateInstance(s->parent); return false; } /****************************************************************************************/ /*//////////////////////////////////////////////////////////////////////////////////////// // LAZY STATIC INIT HELPER ////////////////////////////////////////////////////////////////////////////////////////*/ void DtoLazyStaticInit(bool istempl, LLValue* gvar, Initializer* init, Type* t) { // create a flag to make sure initialization only happens once llvm::GlobalValue::LinkageTypes gflaglink = istempl ? llvm::GlobalValue::WeakLinkage : llvm::GlobalValue::InternalLinkage; std::string gflagname(gvar->getName()); gflagname.append("__initflag"); llvm::GlobalVariable* gflag = new llvm::GlobalVariable(LLType::Int1Ty,false,gflaglink,DtoConstBool(false),gflagname,gIR->module); // check flag and do init if not already done llvm::BasicBlock* oldend = gIR->scopeend(); llvm::BasicBlock* initbb = llvm::BasicBlock::Create("ifnotinit",gIR->topfunc(),oldend); llvm::BasicBlock* endinitbb = llvm::BasicBlock::Create("ifnotinitend",gIR->topfunc(),oldend); LLValue* cond = gIR->ir->CreateICmpEQ(gIR->ir->CreateLoad(gflag,"tmp"),DtoConstBool(false)); gIR->ir->CreateCondBr(cond, initbb, endinitbb); gIR->scope() = IRScope(initbb,endinitbb); DValue* ie = DtoInitializer(init); if (!ie->inPlace()) { DValue* dst = new DVarValue(t, gvar, true); DtoAssign(init->loc, dst, ie); } gIR->ir->CreateStore(DtoConstBool(true), gflag); gIR->ir->CreateBr(endinitbb); gIR->scope() = IRScope(endinitbb,oldend); } /****************************************************************************************/ /*//////////////////////////////////////////////////////////////////////////////////////// // PROCESSING QUEUE HELPERS ////////////////////////////////////////////////////////////////////////////////////////*/ void DtoResolveDsymbol(Dsymbol* dsym) { if (StructDeclaration* sd = dsym->isStructDeclaration()) { DtoResolveStruct(sd); } else if (ClassDeclaration* cd = dsym->isClassDeclaration()) { DtoResolveClass(cd); } else if (FuncDeclaration* fd = dsym->isFuncDeclaration()) { DtoResolveFunction(fd); } else if (TypeInfoDeclaration* fd = dsym->isTypeInfoDeclaration()) { DtoResolveTypeInfo(fd); } else { error(dsym->loc, "unsupported dsymbol: %s", dsym->toChars()); assert(0 && "unsupported dsymbol for DtoResolveDsymbol"); } } ////////////////////////////////////////////////////////////////////////////////////////// void DtoDeclareDsymbol(Dsymbol* dsym) { if (StructDeclaration* sd = dsym->isStructDeclaration()) { DtoDeclareStruct(sd); } else if (ClassDeclaration* cd = dsym->isClassDeclaration()) { DtoDeclareClass(cd); } else if (FuncDeclaration* fd = dsym->isFuncDeclaration()) { DtoDeclareFunction(fd); } else if (TypeInfoDeclaration* fd = dsym->isTypeInfoDeclaration()) { DtoDeclareTypeInfo(fd); } else { error(dsym->loc, "unsupported dsymbol: %s", dsym->toChars()); assert(0 && "unsupported dsymbol for DtoDeclareDsymbol"); } } ////////////////////////////////////////////////////////////////////////////////////////// void DtoConstInitDsymbol(Dsymbol* dsym) { if (StructDeclaration* sd = dsym->isStructDeclaration()) { DtoConstInitStruct(sd); } else if (ClassDeclaration* cd = dsym->isClassDeclaration()) { DtoConstInitClass(cd); } else if (TypeInfoDeclaration* fd = dsym->isTypeInfoDeclaration()) { DtoConstInitTypeInfo(fd); } else if (VarDeclaration* vd = dsym->isVarDeclaration()) { DtoConstInitGlobal(vd); } else { error(dsym->loc, "unsupported dsymbol: %s", dsym->toChars()); assert(0 && "unsupported dsymbol for DtoConstInitDsymbol"); } } ////////////////////////////////////////////////////////////////////////////////////////// void DtoDefineDsymbol(Dsymbol* dsym) { if (StructDeclaration* sd = dsym->isStructDeclaration()) { DtoDefineStruct(sd); } else if (ClassDeclaration* cd = dsym->isClassDeclaration()) { DtoDefineClass(cd); } else if (FuncDeclaration* fd = dsym->isFuncDeclaration()) { DtoDefineFunc(fd); } else if (TypeInfoDeclaration* fd = dsym->isTypeInfoDeclaration()) { DtoDefineTypeInfo(fd); } else { error(dsym->loc, "unsupported dsymbol: %s", dsym->toChars()); assert(0 && "unsupported dsymbol for DtoDefineDsymbol"); } } ////////////////////////////////////////////////////////////////////////////////////////// void DtoConstInitGlobal(VarDeclaration* vd) { if (vd->ir.initialized) return; vd->ir.initialized = gIR->dmodule; Logger::println("* DtoConstInitGlobal(%s)", vd->toChars()); LOG_SCOPE; bool emitRTstaticInit = false; LLConstant* _init = 0; if (vd->parent && vd->parent->isFuncDeclaration() && vd->init && vd->init->isExpInitializer()) { _init = DtoConstInitializer(vd->type, NULL); emitRTstaticInit = true; } else { _init = DtoConstInitializer(vd->type, vd->init); } const LLType* _type = DtoType(vd->type); Type* t = DtoDType(vd->type); //Logger::cout() << "initializer: " << *_init << '\n'; if (_type != _init->getType()) { Logger::cout() << "got type '" << *_init->getType() << "' expected '" << *_type << "'\n"; // zero initalizer if (_init->isNullValue()) _init = llvm::Constant::getNullValue(_type); // pointer to global constant (struct.init) else if (llvm::isa(_init)) { assert(_init->getType()->getContainedType(0) == _type); llvm::GlobalVariable* gv = llvm::cast(_init); assert(t->ty == Tstruct); TypeStruct* ts = (TypeStruct*)t; assert(ts->sym->ir.irStruct->constInit); _init = ts->sym->ir.irStruct->constInit; } // array single value init else if (isaArray(_type)) { _init = DtoConstStaticArray(_type, _init); } else { Logger::cout() << "Unexpected initializer type: " << *_type << '\n'; //assert(0); } } bool istempl = false; if ((vd->storage_class & STCcomdat) || (vd->parent && DtoIsTemplateInstance(vd->parent))) { istempl = true; } if (_init && _init->getType() != _type) _type = _init->getType(); llvm::cast(vd->ir.irGlobal->type.get())->refineAbstractTypeTo(_type); _type = vd->ir.irGlobal->type.get(); //_type->dump(); assert(!_type->isAbstract()); llvm::GlobalVariable* gvar = llvm::cast(vd->ir.irGlobal->value); if (!(vd->storage_class & STCextern) && (vd->getModule() == gIR->dmodule || istempl)) { gvar->setInitializer(_init); // do debug info if (global.params.symdebug) { LLGlobalVariable* gv = DtoDwarfGlobalVariable(gvar, vd); // keep a reference so GDCE doesn't delete it ! gIR->usedArray.push_back(llvm::ConstantExpr::getBitCast(gv, getVoidPtrType())); } } if (emitRTstaticInit) DtoLazyStaticInit(istempl, gvar, vd->init, t); } ////////////////////////////////////////////////////////////////////////////////////////// void DtoEmptyResolveList() { //Logger::println("DtoEmptyResolveList()"); Dsymbol* dsym; while (!gIR->resolveList.empty()) { dsym = gIR->resolveList.front(); gIR->resolveList.pop_front(); DtoResolveDsymbol(dsym); } } ////////////////////////////////////////////////////////////////////////////////////////// void DtoEmptyDeclareList() { //Logger::println("DtoEmptyDeclareList()"); Dsymbol* dsym; while (!gIR->declareList.empty()) { dsym = gIR->declareList.front(); gIR->declareList.pop_front(); DtoDeclareDsymbol(dsym); } } ////////////////////////////////////////////////////////////////////////////////////////// void DtoEmptyConstInitList() { //Logger::println("DtoEmptyConstInitList()"); Dsymbol* dsym; while (!gIR->constInitList.empty()) { dsym = gIR->constInitList.front(); gIR->constInitList.pop_front(); DtoConstInitDsymbol(dsym); } } ////////////////////////////////////////////////////////////////////////////////////////// void DtoEmptyDefineList() { //Logger::println("DtoEmptyDefineList()"); Dsymbol* dsym; while (!gIR->defineList.empty()) { dsym = gIR->defineList.front(); gIR->defineList.pop_front(); DtoDefineDsymbol(dsym); } } ////////////////////////////////////////////////////////////////////////////////////////// void DtoEmptyAllLists() { for(;;) { Dsymbol* dsym; if (!gIR->resolveList.empty()) { dsym = gIR->resolveList.front(); gIR->resolveList.pop_front(); DtoResolveDsymbol(dsym); } else if (!gIR->declareList.empty()) { dsym = gIR->declareList.front(); gIR->declareList.pop_front(); DtoDeclareDsymbol(dsym); } else if (!gIR->constInitList.empty()) { dsym = gIR->constInitList.front(); gIR->constInitList.pop_front(); DtoConstInitDsymbol(dsym); } else if (!gIR->defineList.empty()) { dsym = gIR->defineList.front(); gIR->defineList.pop_front(); DtoDefineDsymbol(dsym); } else { break; } } } ////////////////////////////////////////////////////////////////////////////////////////// void DtoForceDeclareDsymbol(Dsymbol* dsym) { if (dsym->ir.declared) return; Logger::println("DtoForceDeclareDsymbol(%s)", dsym->toPrettyChars()); LOG_SCOPE; DtoResolveDsymbol(dsym); DtoEmptyResolveList(); DtoDeclareDsymbol(dsym); } ////////////////////////////////////////////////////////////////////////////////////////// void DtoForceConstInitDsymbol(Dsymbol* dsym) { if (dsym->ir.initialized) return; Logger::println("DtoForceConstInitDsymbol(%s)", dsym->toPrettyChars()); LOG_SCOPE; DtoResolveDsymbol(dsym); DtoEmptyResolveList(); DtoEmptyDeclareList(); DtoConstInitDsymbol(dsym); } ////////////////////////////////////////////////////////////////////////////////////////// void DtoForceDefineDsymbol(Dsymbol* dsym) { if (dsym->ir.defined) return; Logger::println("DtoForceDefineDsymbol(%s)", dsym->toPrettyChars()); LOG_SCOPE; DtoResolveDsymbol(dsym); DtoEmptyResolveList(); DtoEmptyDeclareList(); DtoEmptyConstInitList(); DtoDefineDsymbol(dsym); } /****************************************************************************************/ /*//////////////////////////////////////////////////////////////////////////////////////// // INITIALIZER HELPERS ////////////////////////////////////////////////////////////////////////////////////////*/ LLConstant* DtoConstInitializer(Type* type, Initializer* init) { LLConstant* _init = 0; // may return zero if (!init) { Logger::println("const default initializer for %s", type->toChars()); if(type->ty == Tsarray) { Logger::println("type is a static array, building constant array initializer"); TypeSArray* arrtype = (TypeSArray*)type; Type* elemtype = type->next; integer_t arraydim; arraydim = arrtype->dim->toInteger(); std::vector inits(arraydim, elemtype->defaultInit()->toConstElem(gIR)); const LLArrayType* arrty = LLArrayType::get(DtoType(elemtype),arraydim); _init = LLConstantArray::get(arrty, inits); } else _init = type->defaultInit()->toConstElem(gIR); } else if (ExpInitializer* ex = init->isExpInitializer()) { Logger::println("const expression initializer"); _init = ex->exp->toConstElem(gIR); } else if (StructInitializer* si = init->isStructInitializer()) { Logger::println("const struct initializer"); _init = DtoConstStructInitializer(si); } else if (ArrayInitializer* ai = init->isArrayInitializer()) { Logger::println("const array initializer"); _init = DtoConstArrayInitializer(ai); } else if (init->isVoidInitializer()) { Logger::println("const void initializer"); const LLType* ty = DtoType(type); _init = llvm::Constant::getNullValue(ty); } else { Logger::println("unsupported const initializer: %s", init->toChars()); } return _init; } ////////////////////////////////////////////////////////////////////////////////////////// LLConstant* DtoConstFieldInitializer(Type* t, Initializer* init) { Logger::println("DtoConstFieldInitializer"); LOG_SCOPE; const LLType* _type = DtoType(t); LLConstant* _init = DtoConstInitializer(t, init); assert(_init); if (_type != _init->getType()) { Logger::cout() << "field init is: " << *_init << " type should be " << *_type << '\n'; if (t->ty == Tsarray) { const LLArrayType* arrty = isaArray(_type); uint64_t n = arrty->getNumElements(); std::vector vals(n,_init); _init = llvm::ConstantArray::get(arrty, vals); } else if (t->ty == Tarray) { assert(isaStruct(_type)); _init = llvm::ConstantAggregateZero::get(_type); } else if (t->ty == Tstruct) { const LLStructType* structty = isaStruct(_type); TypeStruct* ts = (TypeStruct*)t; assert(ts); assert(ts->sym); assert(ts->sym->ir.irStruct->constInit); _init = ts->sym->ir.irStruct->constInit; } else if (t->ty == Tclass) { _init = llvm::Constant::getNullValue(_type); } else { Logger::println("failed for type %s", t->toChars()); assert(0); } } return _init; } ////////////////////////////////////////////////////////////////////////////////////////// DValue* DtoInitializer(Initializer* init) { if (!init) return 0; else if (ExpInitializer* ex = init->isExpInitializer()) { Logger::println("expression initializer"); assert(ex->exp); return ex->exp->toElem(gIR); } else if (init->isVoidInitializer()) { // do nothing } else { Logger::println("unsupported initializer: %s", init->toChars()); assert(0); } return 0; } ////////////////////////////////////////////////////////////////////////////////////////// void DtoAnnotation(const char* str) { std::string s("CODE: "); s.append(str); char* p = &s[0]; while (*p) { if (*p == '"') *p = '\''; ++p; } // create a noop with the code as the result name! gIR->ir->CreateAnd(DtoConstSize_t(0),DtoConstSize_t(0),s.c_str()); } ////////////////////////////////////////////////////////////////////////////////////////// LLConstant* DtoTypeInfoOf(Type* type, bool base) { type = type->merge(); // needed.. getTypeInfo does the same type->getTypeInfo(NULL); TypeInfoDeclaration* tidecl = type->vtinfo; assert(tidecl); DtoForceDeclareDsymbol(tidecl); assert(tidecl->ir.irGlobal != NULL); LLConstant* c = isaConstant(tidecl->ir.irGlobal->value); assert(c != NULL); if (base) return llvm::ConstantExpr::getBitCast(c, DtoType(Type::typeinfo->type)); return c; } ////////////////////////////////////////////////////////////////////////////////////////// void findDefaultTarget() { std::string err_str; const llvm::TargetMachineRegistry::entry* e = llvm::TargetMachineRegistry::getClosestTargetForJIT(err_str); if (e == 0) { error("Failed to find a default target machine: %s", err_str.c_str()); fatal(); } else { global.params.llvmArch = const_cast(e->Name); } } ////////////////////////////////////////////////////////////////////////////////////////// LLValue* DtoBoolean(Loc& loc, DValue* dval) { Type* dtype = dval->getType()->toBasetype(); TY ty = dtype->ty; // integer if (dtype->isintegral()) { LLValue* val = dval->getRVal(); if (val->getType() == LLType::Int1Ty) return val; else { LLValue* zero = LLConstantInt::get(val->getType(), 0, false); return gIR->ir->CreateICmpNE(val, zero, "tmp"); } } // complex else if (dtype->iscomplex()) { return DtoComplexEquals(loc, TOKnotequal, dval, DtoNullValue(dtype)); } // floating point else if (dtype->isfloating()) { LLValue* val = dval->getRVal(); LLValue* zero = LLConstant::getNullValue(val->getType()); return gIR->ir->CreateFCmpONE(val, zero, "tmp"); } // pointer/class else if (ty == Tpointer || ty == Tclass) { LLValue* val = dval->getRVal(); LLValue* zero = LLConstant::getNullValue(val->getType()); return gIR->ir->CreateICmpNE(val, zero, "tmp"); } // dynamic array else if (ty == Tarray) { // return (arr.length != 0) return gIR->ir->CreateICmpNE(DtoArrayLen(dval), DtoConstSize_t(0), "tmp"); } // delegate else if (ty == Tdelegate) { // return (dg !is null) return DtoDelegateEquals(TOKnotequal, dval->getRVal(), NULL); } // unknown std::cout << "unsupported -> bool : " << dtype->toChars() << '\n'; assert(0); return 0; }