// Backend stubs /* DMDFE backend stubs * This file contains the implementations of the backend routines. * For dmdfe these do nothing but print a message saying the module * has been parsed. Substitute your own behaviors for these routimes. */ #include #include #include #include "gen/llvm.h" #include "llvm/Support/CommandLine.h" #include "attrib.h" #include "init.h" #include "mtype.h" #include "template.h" #include "hdrgen.h" #include "port.h" #include "rmem.h" #include "id.h" #include "enum.h" #include "gen/irstate.h" #include "gen/logger.h" #include "gen/tollvm.h" #include "gen/llvmhelpers.h" #include "gen/runtime.h" #include "gen/arrays.h" #include "gen/structs.h" #include "gen/classes.h" #include "gen/typeinf.h" #include "gen/complex.h" #include "gen/dvalue.h" #include "gen/aa.h" #include "gen/functions.h" #include "gen/todebug.h" #include "gen/nested.h" #include "gen/utils.h" #include "gen/warnings.h" #include "gen/optimizer.h" #include "gen/pragma.h" #include "llvm/Support/ManagedStatic.h" llvm::cl::opt checkPrintf("check-printf-calls", llvm::cl::desc("Validate printf call format strings against arguments"), llvm::cl::ZeroOrMore); ////////////////////////////////////////////////////////////////////////////////////////// void Expression::cacheLvalue(IRState* irs) { error("expression %s does not mask any l-value", toChars()); fatal(); } /******************************************* * Evaluate Expression, then call destructors on any temporaries in it. */ DValue *Expression::toElemDtor(IRState *irs) { #if DMDV2 Logger::println("Expression::toElemDtor(): %s", toChars()); LOG_SCOPE size_t starti = irs->varsInScope().size(); DValue *val = toElem(irs); size_t endi = irs->varsInScope().size(); // Add destructors while (endi-- > starti) { VarDeclaration *vd = gIR->varsInScope().back(); gIR->varsInScope().pop_back(); vd->edtor->toElem(gIR); } return val; #else return toElem(irs); #endif } ////////////////////////////////////////////////////////////////////////////////////////// DValue* DeclarationExp::toElem(IRState* p) { Logger::print("DeclarationExp::toElem: %s | T=%s\n", toChars(), type->toChars()); LOG_SCOPE; return DtoDeclarationExp(declaration); } ////////////////////////////////////////////////////////////////////////////////////////// void VarExp::cacheLvalue(IRState* p) { Logger::println("Caching l-value of %s", toChars()); LOG_SCOPE; cachedLvalue = toElem(p)->getLVal(); } DValue* VarExp::toElem(IRState* p) { Logger::print("VarExp::toElem: %s @ %s\n", toChars(), type->toChars()); LOG_SCOPE; assert(var); if (cachedLvalue) { LLValue* V = cachedLvalue; return new DVarValue(type, V); } if (VarDeclaration* vd = var->isVarDeclaration()) { Logger::println("VarDeclaration ' %s ' of type ' %s '", vd->toChars(), vd->type->toChars()); #if DMDV2 /* The magic variable __ctfe is always false at runtime */ if (vd->ident == Id::ctfe) { return new DConstValue(type, DtoConstBool(false)); } #endif // this is an error! must be accessed with DotVarExp if (var->needThis()) { error("need 'this' to access member %s", toChars()); fatal(); } // _arguments if (vd->ident == Id::_arguments && p->func()->_arguments) { Logger::println("Id::_arguments"); LLValue* v = p->func()->_arguments; return new DVarValue(type, vd, v); } // _argptr else if (vd->ident == Id::_argptr && p->func()->_argptr) { Logger::println("Id::_argptr"); LLValue* v = p->func()->_argptr; return new DVarValue(type, vd, v); } // _dollar else if (vd->ident == Id::dollar) { Logger::println("Id::dollar"); LLValue* val = 0; if (vd->ir.isSet() && (val = vd->ir.getIrValue())) { // It must be length of a range return new DVarValue(type, vd, val); } assert(!p->arrays.empty()); val = DtoArrayLen(p->arrays.back()); return new DImValue(type, val); } // classinfo else if (ClassInfoDeclaration* cid = vd->isClassInfoDeclaration()) { Logger::println("ClassInfoDeclaration: %s", cid->cd->toChars()); cid->cd->codegen(Type::sir);; return new DVarValue(type, vd, cid->cd->ir.irStruct->getClassInfoSymbol()); } // typeinfo else if (TypeInfoDeclaration* tid = vd->isTypeInfoDeclaration()) { Logger::println("TypeInfoDeclaration"); tid->codegen(Type::sir); assert(tid->ir.getIrValue()); LLType* vartype = DtoType(type); LLValue* m = tid->ir.getIrValue(); if (m->getType() != getPtrToType(vartype)) m = p->ir->CreateBitCast(m, vartype, "tmp"); return new DImValue(type, m); } // nested variable #if DMDV2 else if (vd->nestedrefs.dim) { #else else if (vd->nestedref) { #endif Logger::println("nested variable"); return DtoNestedVariable(loc, type, vd); } // function parameter else if (vd->isParameter()) { Logger::println("function param"); Logger::println("type: %s", vd->type->toChars()); FuncDeclaration* fd = vd->toParent2()->isFuncDeclaration(); if (fd && fd != p->func()->decl) { Logger::println("nested parameter"); return DtoNestedVariable(loc, type, vd); } else if (vd->storage_class & STClazy) { Logger::println("lazy parameter"); assert(type->ty == Tdelegate); return new DVarValue(type, vd->ir.getIrValue()); } else if (vd->isRef() || vd->isOut() || DtoIsPassedByRef(vd->type) || llvm::isa(vd->ir.getIrValue())) { return new DVarValue(type, vd, vd->ir.getIrValue()); } else if (llvm::isa(vd->ir.getIrValue())) { return new DImValue(type, vd->ir.getIrValue()); } else assert(0); } else { Logger::println("a normal variable"); // take care of forward references of global variables if (vd->isDataseg() || (vd->storage_class & STCextern)) vd->codegen(Type::sir); LLValue* val; if (!vd->ir.isSet() || !(val = vd->ir.getIrValue())) { // FIXME: this error is bad! // We should be VERY careful about adding errors in general, as they have // a tendency to "mask" out the underlying problems ... error("variable %s not resolved", vd->toChars()); if (Logger::enabled()) Logger::cout() << "unresolved variable had type: " << *DtoType(vd->type) << '\n'; fatal(); } if (vd->isDataseg() || (vd->storage_class & STCextern)) val = DtoBitCast(val, DtoType(type->pointerTo())); return new DVarValue(type, vd, val); } } else if (FuncDeclaration* fdecl = var->isFuncDeclaration()) { Logger::println("FuncDeclaration"); LLValue* func = 0; #if DMDV2 fdecl = fdecl->toAliasFunc(); #endif if (fdecl->llvmInternal == LLVMinline_asm) { error("special ldc inline asm is not a normal function"); fatal(); } else if (fdecl->llvmInternal != LLVMva_arg) { fdecl->codegen(Type::sir); func = fdecl->ir.irFunc->func; } return new DFuncValue(fdecl, func); } else if (StaticStructInitDeclaration* sdecl = var->isStaticStructInitDeclaration()) { // this seems to be the static initialiser for structs Type* sdecltype = sdecl->type->toBasetype(); Logger::print("Sym: type=%s\n", sdecltype->toChars()); assert(sdecltype->ty == Tstruct); TypeStruct* ts = static_cast(sdecltype); assert(ts->sym); ts->sym->codegen(Type::sir); LLValue* initsym = ts->sym->ir.irStruct->getInitSymbol(); initsym = DtoBitCast(initsym, DtoType(ts->pointerTo())); return new DVarValue(type, initsym); } else { assert(0 && "Unimplemented VarExp type"); } return 0; } ////////////////////////////////////////////////////////////////////////////////////////// LLConstant* VarExp::toConstElem(IRState* p) { Logger::print("VarExp::toConstElem: %s @ %s\n", toChars(), type->toChars()); LOG_SCOPE; if (StaticStructInitDeclaration* sdecl = var->isStaticStructInitDeclaration()) { // this seems to be the static initialiser for structs Type* sdecltype = sdecl->type->toBasetype(); Logger::print("Sym: type=%s\n", sdecltype->toChars()); assert(sdecltype->ty == Tstruct); TypeStruct* ts = static_cast(sdecltype); ts->sym->codegen(Type::sir); return ts->sym->ir.irStruct->getDefaultInit(); } if (TypeInfoDeclaration* ti = var->isTypeInfoDeclaration()) { LLType* vartype = DtoType(type); LLConstant* m = DtoTypeInfoOf(ti->tinfo, false); if (m->getType() != getPtrToType(vartype)) m = llvm::ConstantExpr::getBitCast(m, vartype); return m; } VarDeclaration* vd = var->isVarDeclaration(); if (vd && vd->isConst() && vd->init) { if (vd->inuse) { error("recursive reference %s", toChars()); return llvm::UndefValue::get(DtoType(type)); } vd->inuse++; LLConstant* ret = DtoConstInitializer(loc, type, vd->init); vd->inuse--; // return the initializer return ret; } // fail error("non-constant expression %s", toChars()); return llvm::UndefValue::get(DtoType(type)); } ////////////////////////////////////////////////////////////////////////////////////////// DValue* IntegerExp::toElem(IRState* p) { Logger::print("IntegerExp::toElem: %s @ %s\n", toChars(), type->toChars()); LOG_SCOPE; LLConstant* c = toConstElem(p); return new DConstValue(type, c); } ////////////////////////////////////////////////////////////////////////////////////////// LLConstant* IntegerExp::toConstElem(IRState* p) { Logger::print("IntegerExp::toConstElem: %s @ %s\n", toChars(), type->toChars()); LOG_SCOPE; LLType* t = DtoType(type); if (isaPointer(t)) { Logger::println("pointer"); LLConstant* i = LLConstantInt::get(DtoSize_t(),(uint64_t)value,false); return llvm::ConstantExpr::getIntToPtr(i, t); } assert(llvm::isa(t)); LLConstant* c = LLConstantInt::get(t,(uint64_t)value,!type->isunsigned()); assert(c); if (Logger::enabled()) Logger::cout() << "value = " << *c << '\n'; return c; } ////////////////////////////////////////////////////////////////////////////////////////// DValue* RealExp::toElem(IRState* p) { Logger::print("RealExp::toElem: %s @ %s\n", toChars(), type->toChars()); LOG_SCOPE; LLConstant* c = toConstElem(p); return new DConstValue(type, c); } ////////////////////////////////////////////////////////////////////////////////////////// LLConstant* RealExp::toConstElem(IRState* p) { Logger::print("RealExp::toConstElem: %s @ %s | %La\n", toChars(), type->toChars(), value); LOG_SCOPE; Type* t = type->toBasetype(); return DtoConstFP(t, value); } ////////////////////////////////////////////////////////////////////////////////////////// DValue* NullExp::toElem(IRState* p) { Logger::print("NullExp::toElem(type=%s): %s\n", type->toChars(),toChars()); LOG_SCOPE; LLConstant* c = toConstElem(p); return new DNullValue(type, c); } ////////////////////////////////////////////////////////////////////////////////////////// LLConstant* NullExp::toConstElem(IRState* p) { Logger::print("NullExp::toConstElem(type=%s): %s\n", type->toChars(),toChars()); LOG_SCOPE; LLType* t = DtoType(type); if (type->ty == Tarray) { assert(isaStruct(t)); return llvm::ConstantAggregateZero::get(t); } else { return LLConstant::getNullValue(t); } assert(0); return NULL; } ////////////////////////////////////////////////////////////////////////////////////////// DValue* ComplexExp::toElem(IRState* p) { Logger::print("ComplexExp::toElem(): %s @ %s\n", toChars(), type->toChars()); LOG_SCOPE; LLConstant* c = toConstElem(p); LLValue* res; if (c->isNullValue()) { Type* t = type->toBasetype(); if (t->ty == Tcomplex32) c = DtoConstFP(Type::tfloat32, ldouble(0)); else if (t->ty == Tcomplex64) c = DtoConstFP(Type::tfloat64, ldouble(0)); else if (t->ty == Tcomplex80) c = DtoConstFP(Type::tfloat80, ldouble(0)); else assert(0); res = DtoAggrPair(DtoType(type), c, c); } else { res = DtoAggrPair(DtoType(type), c->getOperand(0), c->getOperand(1)); } return new DImValue(type, res); } ////////////////////////////////////////////////////////////////////////////////////////// LLConstant* ComplexExp::toConstElem(IRState* p) { Logger::print("ComplexExp::toConstElem(): %s @ %s\n", toChars(), type->toChars()); LOG_SCOPE; return DtoConstComplex(type, value.re, value.im); } ////////////////////////////////////////////////////////////////////////////////////////// template static inline LLConstant* toConstantArray(LLType* ct, LLArrayType* at, T* str, size_t len, bool nullterm = true) { std::vector vals; vals.reserve(len+1); for (size_t i = 0; i < len; ++i) { vals.push_back(LLConstantInt::get(ct, str[i], false)); } if (nullterm) vals.push_back(LLConstantInt::get(ct, 0, false)); return LLConstantArray::get(at, vals); } ////////////////////////////////////////////////////////////////////////////////////////// DValue* StringExp::toElem(IRState* p) { Logger::print("StringExp::toElem: %s @ %s\n", toChars(), type->toChars()); LOG_SCOPE; Type* dtype = type->toBasetype(); Type* cty = dtype->nextOf()->toBasetype(); LLType* ct = DtoTypeNotVoid(cty); //printf("ct = %s\n", type->nextOf()->toChars()); LLArrayType* at = LLArrayType::get(ct,len+1); LLConstant* _init; switch (cty->size()) { default: llvm_unreachable("Unknown char type"); case 1: _init = toConstantArray(ct, at, static_cast(string), len); break; case 2: _init = toConstantArray(ct, at, static_cast(string), len); break; case 4: _init = toConstantArray(ct, at, static_cast(string), len); break; } llvm::GlobalValue::LinkageTypes _linkage = llvm::GlobalValue::InternalLinkage; if (Logger::enabled()) Logger::cout() << "type: " << *at << "\ninit: " << *_init << '\n'; llvm::GlobalVariable* gvar = new llvm::GlobalVariable(*gIR->module,at,true,_linkage,_init,".str"); llvm::ConstantInt* zero = LLConstantInt::get(LLType::getInt32Ty(gIR->context()), 0, false); LLConstant* idxs[2] = { zero, zero }; LLConstant* arrptr = llvm::ConstantExpr::getGetElementPtr(gvar, idxs, true); if (dtype->ty == Tarray) { LLConstant* clen = LLConstantInt::get(DtoSize_t(),len,false); return new DImValue(type, DtoConstSlice(clen, arrptr, dtype)); } else if (dtype->ty == Tsarray) { LLType* dstType = getPtrToType(LLArrayType::get(ct, len)); LLValue* emem = (gvar->getType() == dstType) ? gvar : DtoBitCast(gvar, dstType); return new DVarValue(type, emem); } else if (dtype->ty == Tpointer) { return new DImValue(type, arrptr); } assert(0); return 0; } ////////////////////////////////////////////////////////////////////////////////////////// LLConstant* StringExp::toConstElem(IRState* p) { Logger::print("StringExp::toConstElem: %s @ %s\n", toChars(), type->toChars()); LOG_SCOPE; Type* t = type->toBasetype(); Type* cty = t->nextOf()->toBasetype(); bool nullterm = (t->ty != Tsarray); size_t endlen = nullterm ? len+1 : len; LLType* ct = DtoTypeNotVoid(cty); LLArrayType* at = LLArrayType::get(ct,endlen); LLConstant* _init; switch (cty->size()) { default: llvm_unreachable("Unknown char type"); case 1: _init = toConstantArray(ct, at, static_cast(string), len, nullterm); break; case 2: _init = toConstantArray(ct, at, static_cast(string), len, nullterm); break; case 4: _init = toConstantArray(ct, at, static_cast(string), len, nullterm); break; } if (t->ty == Tsarray) { return _init; } llvm::GlobalValue::LinkageTypes _linkage = llvm::GlobalValue::InternalLinkage; llvm::GlobalVariable* gvar = new llvm::GlobalVariable(*gIR->module,_init->getType(),true,_linkage,_init,".str"); llvm::ConstantInt* zero = LLConstantInt::get(LLType::getInt32Ty(gIR->context()), 0, false); LLConstant* idxs[2] = { zero, zero }; LLConstant* arrptr = llvm::ConstantExpr::getGetElementPtr(gvar, idxs, true); if (t->ty == Tpointer) { return arrptr; } else if (t->ty == Tarray) { LLConstant* clen = LLConstantInt::get(DtoSize_t(),len,false); return DtoConstSlice(clen, arrptr, type); } assert(0); return NULL; } ////////////////////////////////////////////////////////////////////////////////////////// DValue* AssignExp::toElem(IRState* p) { Logger::print("AssignExp::toElem: %s | (%s)(%s = %s)\n", toChars(), type->toChars(), e1->type->toChars(), e2->type ? e2->type->toChars() : 0); LOG_SCOPE; if (e1->op == TOKarraylength) { Logger::println("performing array.length assignment"); ArrayLengthExp *ale = static_cast(e1); DValue* arr = ale->e1->toElem(p); DVarValue arrval(ale->e1->type, arr->getLVal()); DValue* newlen = e2->toElem(p); DSliceValue* slice = DtoResizeDynArray(arrval.getType(), &arrval, newlen->getRVal()); DtoAssign(loc, &arrval, slice); return newlen; } // Can't just override ConstructExp::toElem because not all TOKconstruct // operations are actually instances of ConstructExp... Long live the DMD // coding style! if (op == TOKconstruct) { if (e1->op == TOKvar) { VarExp* ve = (VarExp*)e1; if (ve->var->storage_class & STCref) { Logger::println("performing ref variable initialization"); // Note that the variable value is accessed directly (instead // of via getLValue(), which would perform a load from the // uninitialized location), and that rhs is stored as an l-value! IrLocal* const local = ve->var->ir.irLocal; assert(local && "ref var must be local and already initialized"); DValue* rhs = e2->toElem(p); DtoStore(rhs->getLVal(), local->value); return rhs; } } } DValue* l = e1->toElem(p); DValue* r = e2->toElem(p); if (e1->type->toBasetype()->ty == Tstruct && e2->op == TOKint64) { Logger::println("performing aggregate zero initialization"); assert(e2->toInteger() == 0); DtoAggrZeroInit(l->getLVal()); #if DMDV2 TypeStruct *ts = static_cast(e1->type); if (ts->sym->isNested() && ts->sym->vthis) DtoResolveNestedContext(loc, ts->sym, l->getLVal()); #endif // Return value should be irrelevant. return r; } Logger::println("performing normal assignment"); DtoAssign(loc, l, r, op); if (l->isSlice()) return l; return r; } ////////////////////////////////////////////////////////////////////////////////////////// /// Finds the proper lvalue for a binassign expressions. /// Makes sure the given LHS expression is only evaluated once. static Expression* findLvalue(IRState* irs, Expression* exp) { Expression* e = exp; // skip past any casts while(e->op == TOKcast) e = static_cast(e)->e1; // cache lvalue and return e->cacheLvalue(irs); return e; } #define BIN_ASSIGN(X) \ DValue* X##AssignExp::toElem(IRState* p) \ { \ Logger::print(#X"AssignExp::toElem: %s @ %s\n", toChars(), type->toChars()); \ LOG_SCOPE; \ X##Exp e3(loc, e1, e2); \ e3.type = e1->type; \ DValue* dst = findLvalue(p, e1)->toElem(p); \ DValue* res = e3.toElem(p); \ /* Now that we are done with the expression, clear the cached lvalue. */ \ Expression* e = e1; \ while(e->op == TOKcast) \ e = static_cast(e)->e1; \ e->cachedLvalue = NULL; \ /* Assign the (casted) value and return it. */ \ DValue* stval = DtoCast(loc, res, dst->getType()); \ DtoAssign(loc, dst, stval); \ return DtoCast(loc, res, type); \ } BIN_ASSIGN(Add) BIN_ASSIGN(Min) BIN_ASSIGN(Mul) BIN_ASSIGN(Div) BIN_ASSIGN(Mod) BIN_ASSIGN(And) BIN_ASSIGN(Or) BIN_ASSIGN(Xor) BIN_ASSIGN(Shl) BIN_ASSIGN(Shr) BIN_ASSIGN(Ushr) #undef BIN_ASSIGN ////////////////////////////////////////////////////////////////////////////////////////// static void errorOnIllegalArrayOp(Expression* base, Expression* e1, Expression* e2) { Type* t1 = e1->type->toBasetype(); Type* t2 = e2->type->toBasetype(); // valid array ops would have been transformed by optimize if ((t1->ty == Tarray || t1->ty == Tsarray) && (t2->ty == Tarray || t2->ty == Tsarray) ) { base->error("Array operation %s not recognized", base->toChars()); fatal(); } } ////////////////////////////////////////////////////////////////////////////////////////// LLConstant* AddExp::toConstElem(IRState* p) { // add to pointer if (e1->type->ty == Tpointer && e2->type->isintegral()) { LLConstant *ptr = e1->toConstElem(p); LLConstant *index = e2->toConstElem(p); ptr = llvm::ConstantExpr::getGetElementPtr(ptr, llvm::makeArrayRef(&index, 1)); return ptr; } error("expression '%s' is not a constant", toChars()); fatal(); return NULL; } ////////////////////////////////////////////////////////////////////////////////////////// DValue* AddExp::toElem(IRState* p) { Logger::print("AddExp::toElem: %s @ %s\n", toChars(), type->toChars()); LOG_SCOPE; DValue* l = e1->toElem(p); DValue* r = e2->toElem(p); Type* t = type->toBasetype(); Type* e1type = e1->type->toBasetype(); Type* e2type = e2->type->toBasetype(); errorOnIllegalArrayOp(this, e1, e2); if (e1type != e2type && e1type->ty == Tpointer) { Logger::println("add to pointer"); if (DConstValue* cv = r->isConst()) { if (cv->c->isNullValue()) { Logger::println("is zero"); return new DImValue(type, l->getRVal()); } } LLValue* v = llvm::GetElementPtrInst::Create(l->getRVal(), r->getRVal(), "tmp", p->scopebb()); return new DImValue(type, v); } else if (t->iscomplex()) { return DtoComplexAdd(loc, type, l, r); } else { return DtoBinAdd(l,r); } } ////////////////////////////////////////////////////////////////////////////////////////// LLConstant* MinExp::toConstElem(IRState* p) { if (e1->type->ty == Tpointer && e2->type->isintegral()) { LLConstant *ptr = e1->toConstElem(p); LLConstant *index = e2->toConstElem(p); index = llvm::ConstantExpr::getNeg(index); ptr = llvm::ConstantExpr::getGetElementPtr(ptr, llvm::makeArrayRef(&index, 1)); return ptr; } error("expression '%s' is not a constant", toChars()); fatal(); return NULL; } ////////////////////////////////////////////////////////////////////////////////////////// DValue* MinExp::toElem(IRState* p) { Logger::print("MinExp::toElem: %s @ %s\n", toChars(), type->toChars()); LOG_SCOPE; DValue* l = e1->toElem(p); DValue* r = e2->toElem(p); Type* t = type->toBasetype(); Type* t1 = e1->type->toBasetype(); Type* t2 = e2->type->toBasetype(); errorOnIllegalArrayOp(this, e1, e2); if (t1->ty == Tpointer && t2->ty == Tpointer) { LLValue* lv = l->getRVal(); LLValue* rv = r->getRVal(); if (Logger::enabled()) Logger::cout() << "lv: " << *lv << " rv: " << *rv << '\n'; lv = p->ir->CreatePtrToInt(lv, DtoSize_t(), "tmp"); rv = p->ir->CreatePtrToInt(rv, DtoSize_t(), "tmp"); LLValue* diff = p->ir->CreateSub(lv,rv,"tmp"); if (diff->getType() != DtoType(type)) diff = p->ir->CreateIntToPtr(diff, DtoType(type), "tmp"); return new DImValue(type, diff); } else if (t1->ty == Tpointer) { LLValue* idx = p->ir->CreateNeg(r->getRVal(), "tmp"); LLValue* v = llvm::GetElementPtrInst::Create(l->getRVal(), idx, "tmp", p->scopebb()); return new DImValue(type, v); } else if (t->iscomplex()) { return DtoComplexSub(loc, type, l, r); } else { return DtoBinSub(l,r); } } ////////////////////////////////////////////////////////////////////////////////////////// DValue* MulExp::toElem(IRState* p) { Logger::print("MulExp::toElem: %s @ %s\n", toChars(), type->toChars()); LOG_SCOPE; DValue* l = e1->toElem(p); DValue* r = e2->toElem(p); errorOnIllegalArrayOp(this, e1, e2); if (type->iscomplex()) { return DtoComplexMul(loc, type, l, r); } return DtoBinMul(type, l, r); } ////////////////////////////////////////////////////////////////////////////////////////// DValue* DivExp::toElem(IRState* p) { Logger::print("DivExp::toElem: %s @ %s\n", toChars(), type->toChars()); LOG_SCOPE; DValue* l = e1->toElem(p); DValue* r = e2->toElem(p); errorOnIllegalArrayOp(this, e1, e2); if (type->iscomplex()) { return DtoComplexDiv(loc, type, l, r); } return DtoBinDiv(type, l, r); } ////////////////////////////////////////////////////////////////////////////////////////// DValue* ModExp::toElem(IRState* p) { Logger::print("ModExp::toElem: %s @ %s\n", toChars(), type->toChars()); LOG_SCOPE; DValue* l = e1->toElem(p); DValue* r = e2->toElem(p); errorOnIllegalArrayOp(this, e1, e2); if (type->iscomplex()) { return DtoComplexRem(loc, type, l, r); } return DtoBinRem(type, l, r); } ////////////////////////////////////////////////////////////////////////////////////////// void CallExp::cacheLvalue(IRState* p) { Logger::println("Caching l-value of %s", toChars()); LOG_SCOPE; cachedLvalue = toElem(p)->getLVal(); } ////////////////////////////////////////////////////////////////////////////////////////// DValue* CallExp::toElem(IRState* p) { Logger::print("CallExp::toElem: %s @ %s\n", toChars(), type->toChars()); LOG_SCOPE; if (cachedLvalue) { LLValue* V = cachedLvalue; return new DVarValue(type, V); } // handle magic inline asm if (e1->op == TOKvar) { VarExp* ve = static_cast(e1); if (FuncDeclaration* fd = ve->var->isFuncDeclaration()) { if (fd->llvmInternal == LLVMinline_asm) { return DtoInlineAsmExpr(loc, fd, arguments); } } } // get the callee value DValue* fnval = e1->toElem(p); // get func value if any DFuncValue* dfnval = fnval->isFunc(); // handle magic intrinsics (mapping to instructions) if (dfnval && dfnval->func) { FuncDeclaration* fndecl = dfnval->func; // as requested by bearophile, see if it's a C printf call and that it's valid. if (global.params.warnings && checkPrintf) { if (fndecl->linkage == LINKc && strcmp(fndecl->ident->string, "printf") == 0) { warnInvalidPrintfCall(loc, static_cast(arguments->data[0]), arguments->dim); } } // va_start instruction if (fndecl->llvmInternal == LLVMva_start) { if (arguments->dim != 2) { error("va_start instruction expects 2 arguments"); return NULL; } // llvm doesn't need the second param hence the override Expression* exp = static_cast(arguments->data[0]); LLValue* arg = exp->toElem(p)->getLVal(); #if DMDV2 if (LLValue *argptr = gIR->func()->_argptr) { DtoStore(DtoLoad(argptr), DtoBitCast(arg, getPtrToType(getVoidPtrType()))); return new DImValue(type, arg); } else if (global.params.cpu == ARCHx86_64) { LLValue *va_list = DtoAlloca(exp->type->nextOf()); DtoStore(va_list, arg); va_list = DtoBitCast(va_list, getVoidPtrType()); return new DImValue(type, gIR->ir->CreateCall(GET_INTRINSIC_DECL(vastart), va_list, "")); } else #endif { arg = DtoBitCast(arg, getVoidPtrType()); return new DImValue(type, gIR->ir->CreateCall(GET_INTRINSIC_DECL(vastart), arg, "")); } } #if DMDV2 else if (fndecl->llvmInternal == LLVMva_copy && global.params.cpu == ARCHx86_64) { if (arguments->dim != 2) { error("va_copy instruction expects 2 arguments"); return NULL; } Expression* exp1 = static_cast(arguments->data[0]); Expression* exp2 = static_cast(arguments->data[1]); LLValue* arg1 = exp1->toElem(p)->getLVal(); LLValue* arg2 = exp2->toElem(p)->getLVal(); LLValue *va_list = DtoAlloca(exp1->type->nextOf()); DtoStore(va_list, arg1); DtoStore(DtoLoad(DtoLoad(arg2)), DtoLoad(arg1)); return new DVarValue(type, arg1); } #endif // va_arg instruction else if (fndecl->llvmInternal == LLVMva_arg) { if (arguments->dim != 1) { error("va_arg instruction expects 1 arguments"); return NULL; } return DtoVaArg(loc, type, static_cast(arguments->data[0])); } // C alloca else if (fndecl->llvmInternal == LLVMalloca) { if (arguments->dim != 1) { error("alloca expects 1 arguments"); return NULL; } Expression* exp = static_cast(arguments->data[0]); DValue* expv = exp->toElem(p); if (expv->getType()->toBasetype()->ty != Tint32) expv = DtoCast(loc, expv, Type::tint32); return new DImValue(type, p->ir->CreateAlloca(LLType::getInt8Ty(gIR->context()), expv->getRVal(), ".alloca")); } // shufflevector else if (fndecl->llvmInternal == LLVMshufflevector) { llvm::SmallVector mask; for(int i = 2, n = arguments->dim; i < n; i++){ Expression* exp = static_cast(arguments->data[i]); if(exp->op != TOKint64){ error("Function %s was declared with pragma shufflevector. Because of that all of its arguments except the first two must be integer literals.", fndecl->toChars()); fatal(); } IntegerExp* iexp = static_cast(arguments->data[i]); mask.push_back(iexp->toConstElem(p)); } LLValue* maskVal = llvm::ConstantVector::get(mask); Expression* exp1 = static_cast(arguments->data[0]); Expression* exp2 = static_cast(arguments->data[1]); LLValue* v1 = exp1->toElem(p)->getRVal(); LLValue* v2 = exp2->toElem(p)->getRVal(); return new DImValue(type, p->ir->CreateShuffleVector(v1, v2, maskVal)); } // fence instruction else if (fndecl->llvmInternal == LLVMfence) { if (arguments->dim != 1) { error("fence instruction expects 1 arguments"); return NULL; } gIR->ir->CreateFence(llvm::AtomicOrdering(static_cast(arguments->data[0])->toInteger())); return NULL; // atomic store instruction } else if (fndecl->llvmInternal == LLVMatomic_store) { if (arguments->dim != 3) { error("atomic store instruction expects 3 arguments"); return NULL; } Expression* exp1 = static_cast(arguments->data[0]); Expression* exp2 = static_cast(arguments->data[1]); int atomicOrdering = static_cast(arguments->data[2])->toInteger(); LLValue* val = exp1->toElem(p)->getRVal(); LLValue* ptr = exp2->toElem(p)->getRVal(); llvm::StoreInst* ret = gIR->ir->CreateStore(val, ptr, "tmp"); ret->setAtomic(llvm::AtomicOrdering(atomicOrdering)); ret->setAlignment(getTypeAllocSize(val->getType())); return NULL; // atomic load instruction } else if (fndecl->llvmInternal == LLVMatomic_load) { if (arguments->dim != 2) { error("atomic load instruction expects 2 arguments"); return NULL; } Expression* exp = static_cast(arguments->data[0]); int atomicOrdering = static_cast(arguments->data[1])->toInteger(); LLValue* ptr = exp->toElem(p)->getRVal(); Type* retType = exp->type->nextOf(); llvm::LoadInst* val = gIR->ir->CreateLoad(ptr, "tmp"); val->setAlignment(getTypeAllocSize(val->getType())); val->setAtomic(llvm::AtomicOrdering(atomicOrdering)); return new DImValue(retType, val); // cmpxchg instruction } else if (fndecl->llvmInternal == LLVMatomic_cmp_xchg) { if (arguments->dim != 4) { error("cmpxchg instruction expects 4 arguments"); return NULL; } Expression* exp1 = static_cast(arguments->data[0]); Expression* exp2 = static_cast(arguments->data[1]); Expression* exp3 = static_cast(arguments->data[2]); int atomicOrdering = static_cast(arguments->data[3])->toInteger(); LLValue* ptr = exp1->toElem(p)->getRVal(); LLValue* cmp = exp2->toElem(p)->getRVal(); LLValue* val = exp3->toElem(p)->getRVal(); LLValue* ret = gIR->ir->CreateAtomicCmpXchg(ptr, cmp, val, llvm::AtomicOrdering(atomicOrdering)); return new DImValue(exp3->type, ret); // atomicrmw instruction } else if (fndecl->llvmInternal == LLVMatomic_rmw) { if (arguments->dim != 3) { error("atomic_rmw instruction expects 3 arguments"); return NULL; } static char *ops[] = { "xchg", "add", "sub", "and", "nand", "or", "xor", "max", "min", "umax", "umin", 0 }; int op = 0; for (; ; ++op) { if (ops[op] == 0) { error("unknown atomic_rmw operation %s", fndecl->intrinsicName.c_str()); return NULL; } if (fndecl->intrinsicName == ops[op]) break; } Expression* exp1 = static_cast(arguments->data[0]); Expression* exp2 = static_cast(arguments->data[1]); int atomicOrdering = static_cast(arguments->data[2])->toInteger(); LLValue* ptr = exp1->toElem(p)->getRVal(); LLValue* val = exp2->toElem(p)->getRVal(); LLValue* ret = gIR->ir->CreateAtomicRMW(llvm::AtomicRMWInst::BinOp(op), ptr, val, llvm::AtomicOrdering(atomicOrdering)); return new DImValue(exp2->type, ret); // bitop } else if (fndecl->llvmInternal == LLVMbitop_bt || fndecl->llvmInternal == LLVMbitop_btr || fndecl->llvmInternal == LLVMbitop_btc || fndecl->llvmInternal == LLVMbitop_bts) { if (arguments->dim != 2) { error("bitop intrinsic expects 2 arguments"); return NULL; } Expression* exp1 = static_cast(arguments->data[0]); Expression* exp2 = static_cast(arguments->data[1]); LLValue* ptr = exp1->toElem(p)->getRVal(); LLValue* bitnum = exp2->toElem(p)->getRVal(); // auto q = cast(ubyte*)ptr + (bitnum >> 3); LLValue* q = DtoBitCast(ptr, DtoType(Type::tuns8->pointerTo())); q = DtoGEP1(q, p->ir->CreateLShr(bitnum, 3), "bitop.q"); // auto mask = 1 << (bitnum & 7); LLValue* mask = p->ir->CreateAnd(bitnum, DtoConstSize_t(7), "bitop.tmp"); mask = p->ir->CreateShl(DtoConstSize_t(1), mask, "bitop.mask"); // auto result = (*q & mask) ? -1 : 0; LLValue* val = p->ir->CreateZExt(DtoLoad(q, "bitop.tmp"), DtoSize_t(), "bitop.val"); LLValue* result = p->ir->CreateAnd(val, mask, "bitop.tmp"); result = p->ir->CreateICmpNE(result, DtoConstSize_t(0), "bitop.tmp"); result = p->ir->CreateSelect(result, DtoConstInt(-1), DtoConstInt(0), "bitop.result"); if (fndecl->llvmInternal != LLVMbitop_bt) { llvm::Instruction::BinaryOps op; if (fndecl->llvmInternal == LLVMbitop_btc) { // *q ^= mask; op = llvm::Instruction::Xor; } else if (fndecl->llvmInternal == LLVMbitop_btr) { // *q &= ~mask; mask = p->ir->CreateNot(mask); op = llvm::Instruction::And; } else if (fndecl->llvmInternal == LLVMbitop_bts) { // *q |= mask; op = llvm::Instruction::Or; } else { assert(false); } LLValue *newVal = p->ir->CreateBinOp(op, val, mask, "bitop.new_val"); newVal = p->ir->CreateTrunc(newVal, DtoType(Type::tuns8), "bitop.tmp"); DtoStore(newVal, q); } return new DImValue(type, result); } } return DtoCallFunction(loc, type, fnval, arguments); } ////////////////////////////////////////////////////////////////////////////////////////// DValue* CastExp::toElem(IRState* p) { Logger::print("CastExp::toElem: %s @ %s\n", toChars(), type->toChars()); LOG_SCOPE; // get the value to cast DValue* u = e1->toElem(p); // cast it to the 'to' type, if necessary DValue* v = u; if (!to->equals(e1->type)) v = DtoCast(loc, u, to); // paint the type, if necessary if (!type->equals(to)) v = DtoPaintType(loc, v, type); // return the new rvalue return v; } ////////////////////////////////////////////////////////////////////////////////////////// LLConstant* CastExp::toConstElem(IRState* p) { Logger::print("CastExp::toConstElem: %s @ %s\n", toChars(), type->toChars()); LOG_SCOPE; LLConstant* res; LLType* lltype = DtoType(type); Type* tb = to->toBasetype(); // string literal to dyn array: // reinterpret the string data as an array, calculate the length if (e1->op == TOKstring && tb->ty == Tarray) { /* StringExp *strexp = static_cast(e1); size_t datalen = strexp->sz * strexp->len; Type* eltype = tb->nextOf()->toBasetype(); if (datalen % eltype->size() != 0) { error("the sizes don't line up"); return e1->toConstElem(p); } size_t arrlen = datalen / eltype->size();*/ error("ct cast of string to dynamic array not fully implemented"); return e1->toConstElem(p); } // pointer to pointer else if (tb->ty == Tpointer && e1->type->toBasetype()->ty == Tpointer) { res = llvm::ConstantExpr::getBitCast(e1->toConstElem(p), lltype); } // global variable to pointer else if (tb->ty == Tpointer && e1->op == TOKvar) { VarDeclaration *vd = static_cast(e1)->var->isVarDeclaration(); assert(vd); vd->codegen(Type::sir); LLConstant *value = vd->ir.irGlobal ? isaConstant(vd->ir.irGlobal->value) : 0; if (!value) goto Lerr; Type *type = vd->type->toBasetype(); if (type->ty == Tarray || type->ty == Tdelegate) { LLConstant* idxs[2] = { DtoConstSize_t(0), DtoConstSize_t(1) }; value = llvm::ConstantExpr::getGetElementPtr(value, idxs, true); } return DtoBitCast(value, DtoType(tb)); } else { goto Lerr; } return res; Lerr: error("can not cast %s to %s at compile time", e1->type->toChars(), type->toChars()); if (!global.gag) fatal(); return NULL; } ////////////////////////////////////////////////////////////////////////////////////////// DValue* SymOffExp::toElem(IRState* p) { Logger::print("SymOffExp::toElem: %s @ %s\n", toChars(), type->toChars()); LOG_SCOPE; assert(0 && "SymOffExp::toElem should no longer be called :/"); return 0; } ////////////////////////////////////////////////////////////////////////////////////////// DValue* AddrExp::toElem(IRState* p) { Logger::println("AddrExp::toElem: %s @ %s", toChars(), type->toChars()); LOG_SCOPE; DValue* v = e1->toElem(p); if (v->isField()) { Logger::println("is field"); return v; } else if (DFuncValue* fv = v->isFunc()) { Logger::println("is func"); //Logger::println("FuncDeclaration"); FuncDeclaration* fd = fv->func; assert(fd); fd->codegen(Type::sir); return new DFuncValue(fd, fd->ir.irFunc->func); } else if (v->isIm()) { Logger::println("is immediate"); return v; } Logger::println("is nothing special"); // we special case here, since apparently taking the address of a slice is ok LLValue* lval; if (v->isLVal()) lval = v->getLVal(); else { assert(v->isSlice()); LLValue* rval = v->getRVal(); lval = DtoRawAlloca(rval->getType(), 0, ".tmp_slice_storage"); DtoStore(rval, lval); } if (Logger::enabled()) Logger::cout() << "lval: " << *lval << '\n'; return new DImValue(type, DtoBitCast(lval, DtoType(type))); } LLConstant* AddrExp::toConstElem(IRState* p) { // FIXME: this should probably be generalized more so we don't // need to have a case for each thing we can take the address of // address of global variable if (e1->op == TOKvar) { VarExp* vexp = static_cast(e1); // make sure 'this' isn't needed if (vexp->var->needThis()) { error("need 'this' to access %s", vexp->var->toChars()); fatal(); } // global variable if (VarDeclaration* vd = vexp->var->isVarDeclaration()) { vd->codegen(Type::sir); LLConstant* llc = llvm::dyn_cast(vd->ir.getIrValue()); assert(llc); return DtoBitCast(llc, DtoType(type)); } // static function else if (FuncDeclaration* fd = vexp->var->isFuncDeclaration()) { fd->codegen(Type::sir); IrFunction* irfunc = fd->ir.irFunc; return irfunc->func; } // something else else { // fail goto Lerr; } } // address of indexExp else if (e1->op == TOKindex) { IndexExp* iexp = static_cast(e1); // indexee must be global static array var assert(iexp->e1->op == TOKvar); VarExp* vexp = static_cast(iexp->e1); VarDeclaration* vd = vexp->var->isVarDeclaration(); assert(vd); assert(vd->type->toBasetype()->ty == Tsarray); vd->codegen(Type::sir); assert(vd->ir.irGlobal); // get index LLConstant* index = iexp->e2->toConstElem(p); assert(index->getType() == DtoSize_t()); // gep LLConstant* idxs[2] = { DtoConstSize_t(0), index }; LLConstant *val = isaConstant(vd->ir.irGlobal->value); val = DtoBitCast(val, DtoType(vd->type->pointerTo())); LLConstant* gep = llvm::ConstantExpr::getGetElementPtr(val, idxs, true); // bitcast to requested type assert(type->toBasetype()->ty == Tpointer); return DtoBitCast(gep, DtoType(type)); } else if ( e1->op == TOKstructliteral || e1->op == TOKslice) { error("non-constant expression '%s'", toChars()); fatal(); } // not yet supported else { Lerr: error("constant expression '%s' not yet implemented", toChars()); fatal(); } } ////////////////////////////////////////////////////////////////////////////////////////// void PtrExp::cacheLvalue(IRState* p) { Logger::println("Caching l-value of %s", toChars()); LOG_SCOPE; cachedLvalue = e1->toElem(p)->getRVal(); } DValue* PtrExp::toElem(IRState* p) { Logger::println("PtrExp::toElem: %s @ %s", toChars(), type->toChars()); LOG_SCOPE; // function pointers are special if (type->toBasetype()->ty == Tfunction) { assert(!cachedLvalue); return new DImValue(type, e1->toElem(p)->getRVal()); } // get the rvalue and return it as an lvalue LLValue* V; if (cachedLvalue) { V = cachedLvalue; } else { V = e1->toElem(p)->getRVal(); } return new DVarValue(type, V); } ////////////////////////////////////////////////////////////////////////////////////////// void DotVarExp::cacheLvalue(IRState* p) { Logger::println("Caching l-value of %s", toChars()); LOG_SCOPE; cachedLvalue = toElem(p)->getLVal(); } DValue* DotVarExp::toElem(IRState* p) { Logger::print("DotVarExp::toElem: %s @ %s\n", toChars(), type->toChars()); LOG_SCOPE; if (cachedLvalue) { Logger::println("using cached lvalue"); LLValue *V = cachedLvalue; VarDeclaration* vd = var->isVarDeclaration(); assert(vd); return new DVarValue(type, vd, V); } DValue* l = e1->toElem(p); Type* e1type = e1->type->toBasetype(); //Logger::println("e1type=%s", e1type->toChars()); //Logger::cout() << *DtoType(e1type) << '\n'; if (VarDeclaration* vd = var->isVarDeclaration()) { LLValue* arrptr; // indexing struct pointer if (e1type->ty == Tpointer) { assert(e1type->nextOf()->ty == Tstruct); TypeStruct* ts = static_cast(e1type->nextOf()); arrptr = DtoIndexStruct(l->getRVal(), ts->sym, vd); } // indexing normal struct else if (e1type->ty == Tstruct) { TypeStruct* ts = static_cast(e1type); arrptr = DtoIndexStruct(l->getRVal(), ts->sym, vd); } // indexing class else if (e1type->ty == Tclass) { TypeClass* tc = static_cast(e1type); arrptr = DtoIndexClass(l->getRVal(), tc->sym, vd); } else assert(0); //Logger::cout() << "mem: " << *arrptr << '\n'; return new DVarValue(type, vd, arrptr); } else if (FuncDeclaration* fdecl = var->isFuncDeclaration()) { DtoResolveDsymbol(fdecl); // This is a bit more convoluted than it would need to be, because it // has to take templated interface methods into account, for which // isFinal is not necessarily true. const bool nonFinal = !fdecl->isFinal() && (fdecl->isAbstract() || fdecl->isVirtual()); // If we are calling a non-final interface function, we need to get // the pointer to the underlying object instead of passing the // interface pointer directly. LLValue* passedThis = 0; if (e1type->ty == Tclass) { TypeClass* tc = static_cast(e1type); if (tc->sym->isInterfaceDeclaration() && nonFinal) passedThis = DtoCastInterfaceToObject(l, NULL)->getRVal(); } LLValue* vthis = l->getRVal(); if (!passedThis) passedThis = vthis; // Decide whether this function needs to be looked up in the vtable. // Even virtual functions are looked up directly if super or DotTypeExp // are used, thus we need to walk through the this expression and check. bool vtbllookup = nonFinal; Expression* e = e1; while (e && vtbllookup) { if (e->op == TOKsuper || e->op == TOKdottype) vtbllookup = false; else if (e->op == TOKcast) e = static_cast(e)->e1; else break; } // Get the actual function value to call. LLValue* funcval = 0; if (vtbllookup) { DImValue thisVal(e1type, vthis); funcval = DtoVirtualFunctionPointer(&thisVal, fdecl, toChars()); } else { fdecl->codegen(Type::sir); funcval = fdecl->ir.irFunc->func; } assert(funcval); return new DFuncValue(fdecl, funcval, passedThis); } else { printf("unsupported dotvarexp: %s\n", var->toChars()); } assert(0); return 0; } ////////////////////////////////////////////////////////////////////////////////////////// DValue* ThisExp::toElem(IRState* p) { Logger::print("ThisExp::toElem: %s @ %s\n", toChars(), type->toChars()); LOG_SCOPE; // regular this expr if (VarDeclaration* vd = var->isVarDeclaration()) { LLValue* v; Dsymbol* vdparent = vd->toParent2(); Identifier *ident = p->func()->decl->ident; if (ident == Id::ensure || ident == Id::require) { Logger::println("contract this exp"); v = p->func()->nestArg; v = DtoBitCast(v, DtoType(type)->getPointerTo()); } else if (vdparent != p->func()->decl) { Logger::println("nested this exp"); #if STRUCTTHISREF return DtoNestedVariable(loc, type, vd, type->ty == Tstruct); #else return DtoNestedVariable(loc, type, vd); #endif } else { Logger::println("normal this exp"); v = p->func()->thisArg; } return new DVarValue(type, vd, v); } // anything we're not yet handling ? assert(0 && "no var in ThisExp"); return 0; } ////////////////////////////////////////////////////////////////////////////////////////// void IndexExp::cacheLvalue(IRState* p) { Logger::println("Caching l-value of %s", toChars()); LOG_SCOPE; cachedLvalue = toElem(p)->getLVal(); } DValue* IndexExp::toElem(IRState* p) { Logger::print("IndexExp::toElem: %s @ %s\n", toChars(), type->toChars()); LOG_SCOPE; if (cachedLvalue) { LLValue* V = cachedLvalue; return new DVarValue(type, V); } DValue* l = e1->toElem(p); Type* e1type = e1->type->toBasetype(); p->arrays.push_back(l); // if $ is used it must be an array so this is fine. DValue* r = e2->toElem(p); p->arrays.pop_back(); LLValue* zero = DtoConstUint(0); LLValue* arrptr = 0; if (e1type->ty == Tpointer) { arrptr = DtoGEP1(l->getRVal(),r->getRVal()); } else if (e1type->ty == Tsarray) { if(global.params.useArrayBounds) DtoArrayBoundsCheck(loc, l, r); arrptr = DtoGEP(l->getRVal(), zero, r->getRVal()); } else if (e1type->ty == Tarray) { if(global.params.useArrayBounds) DtoArrayBoundsCheck(loc, l, r); arrptr = DtoArrayPtr(l); arrptr = DtoGEP1(arrptr,r->getRVal()); } else if (e1type->ty == Taarray) { return DtoAAIndex(loc, type, l, r, modifiable); } else { Logger::println("invalid index exp! e1type: %s", e1type->toChars()); assert(0); } return new DVarValue(type, arrptr); } ////////////////////////////////////////////////////////////////////////////////////////// DValue* SliceExp::toElem(IRState* p) { Logger::print("SliceExp::toElem: %s @ %s\n", toChars(), type->toChars()); LOG_SCOPE; // this is the new slicing code, it's different in that a full slice will no longer retain the original pointer. // but this was broken if there *was* no original pointer, ie. a slice of a slice... // now all slices have *both* the 'len' and 'ptr' fields set to != null. // value being sliced LLValue* elen; LLValue* eptr; DValue* e = e1->toElem(p); // handle pointer slicing Type* etype = e1->type->toBasetype(); if (etype->ty == Tpointer) { assert(lwr); eptr = e->getRVal(); } // array slice else { eptr = DtoArrayPtr(e); } // has lower bound, pointer needs adjustment if (lwr) { // must have upper bound too then assert(upr); // get bounds (make sure $ works) p->arrays.push_back(e); DValue* lo = lwr->toElem(p); DValue* up = upr->toElem(p); p->arrays.pop_back(); LLValue* vlo = lo->getRVal(); LLValue* vup = up->getRVal(); #if DMDV2 if(global.params.useArrayBounds) #else if(global.params.useArrayBounds && (etype->ty == Tsarray || etype->ty == Tarray)) #endif DtoArrayBoundsCheck(loc, e, up, lo); // offset by lower eptr = DtoGEP1(eptr, vlo); // adjust length elen = p->ir->CreateSub(vup, vlo, "tmp"); } // no bounds or full slice -> just convert to slice else { assert(e1->type->toBasetype()->ty != Tpointer); // if the sliceee is a static array, we use the length of that as DMD seems // to give contrary inconsistent sizesin some multidimensional static array cases. // (namely default initialization, int[16][16] arr; -> int[256] arr = 0;) if (etype->ty == Tsarray) { TypeSArray* tsa = static_cast(etype); elen = DtoConstSize_t(tsa->dim->toUInteger()); // in this case, we also need to make sure the pointer is cast to the innermost element type eptr = DtoBitCast(eptr, DtoType(tsa->nextOf()->pointerTo())); } // for normal code the actual array length is what we want! else { elen = DtoArrayLen(e); } } return new DSliceValue(type, elen, eptr); } ////////////////////////////////////////////////////////////////////////////////////////// DValue* CmpExp::toElem(IRState* p) { Logger::print("CmpExp::toElem: %s @ %s\n", toChars(), type->toChars()); LOG_SCOPE; DValue* l = e1->toElem(p); DValue* r = e2->toElem(p); Type* t = e1->type->toBasetype(); LLValue* eval = 0; if (t->isintegral() || t->ty == Tpointer || t->ty == Tnull) { llvm::ICmpInst::Predicate cmpop; bool skip = false; bool uns = isLLVMUnsigned(t); switch(op) { case TOKlt: case TOKul: cmpop = uns ? llvm::ICmpInst::ICMP_ULT : llvm::ICmpInst::ICMP_SLT; break; case TOKle: case TOKule: cmpop = uns ? llvm::ICmpInst::ICMP_ULE : llvm::ICmpInst::ICMP_SLE; break; case TOKgt: case TOKug: cmpop = uns ? llvm::ICmpInst::ICMP_UGT : llvm::ICmpInst::ICMP_SGT; break; case TOKge: case TOKuge: cmpop = uns ? llvm::ICmpInst::ICMP_UGE : llvm::ICmpInst::ICMP_SGE; break; case TOKue: cmpop = llvm::ICmpInst::ICMP_EQ; break; case TOKlg: cmpop = llvm::ICmpInst::ICMP_NE; break; case TOKleg: skip = true; eval = LLConstantInt::getTrue(gIR->context()); break; case TOKunord: skip = true; eval = LLConstantInt::getFalse(gIR->context()); break; default: assert(0); } if (!skip) { LLValue* a = l->getRVal(); LLValue* b = r->getRVal(); if (Logger::enabled()) { Logger::cout() << "type 1: " << *a << '\n'; Logger::cout() << "type 2: " << *b << '\n'; } if (a->getType() != b->getType()) b = DtoBitCast(b, a->getType()); eval = p->ir->CreateICmp(cmpop, a, b, "tmp"); } } else if (t->isfloating()) { llvm::FCmpInst::Predicate cmpop; switch(op) { case TOKlt: cmpop = llvm::FCmpInst::FCMP_OLT;break; case TOKle: cmpop = llvm::FCmpInst::FCMP_OLE;break; case TOKgt: cmpop = llvm::FCmpInst::FCMP_OGT;break; case TOKge: cmpop = llvm::FCmpInst::FCMP_OGE;break; case TOKunord: cmpop = llvm::FCmpInst::FCMP_UNO;break; case TOKule: cmpop = llvm::FCmpInst::FCMP_ULE;break; case TOKul: cmpop = llvm::FCmpInst::FCMP_ULT;break; case TOKuge: cmpop = llvm::FCmpInst::FCMP_UGE;break; case TOKug: cmpop = llvm::FCmpInst::FCMP_UGT;break; case TOKue: cmpop = llvm::FCmpInst::FCMP_UEQ;break; case TOKlg: cmpop = llvm::FCmpInst::FCMP_ONE;break; case TOKleg: cmpop = llvm::FCmpInst::FCMP_ORD;break; default: assert(0); } eval = p->ir->CreateFCmp(cmpop, l->getRVal(), r->getRVal(), "tmp"); } else if (t->ty == Tsarray || t->ty == Tarray) { Logger::println("static or dynamic array"); eval = DtoArrayCompare(loc,op,l,r); } else if (t->ty == Taarray) { eval = LLConstantInt::getFalse(gIR->context()); } else { assert(0 && "Unsupported CmpExp type"); } return new DImValue(type, eval); } ////////////////////////////////////////////////////////////////////////////////////////// DValue* EqualExp::toElem(IRState* p) { Logger::print("EqualExp::toElem: %s @ %s\n", toChars(), type->toChars()); LOG_SCOPE; DValue* l = e1->toElem(p); DValue* r = e2->toElem(p); LLValue* lv = l->getRVal(); LLValue* rv = r->getRVal(); Type* t = e1->type->toBasetype(); LLValue* eval = 0; // the Tclass catches interface comparisons, regular // class equality should be rewritten as a.opEquals(b) by this time if (t->isintegral() || t->ty == Tpointer || t->ty == Tclass || t->ty == Tnull) { Logger::println("integral or pointer or interface"); llvm::ICmpInst::Predicate cmpop; switch(op) { case TOKequal: cmpop = llvm::ICmpInst::ICMP_EQ; break; case TOKnotequal: cmpop = llvm::ICmpInst::ICMP_NE; break; default: assert(0); } if (rv->getType() != lv->getType()) { rv = DtoBitCast(rv, lv->getType()); } if (Logger::enabled()) { Logger::cout() << "lv: " << *lv << '\n'; Logger::cout() << "rv: " << *rv << '\n'; } eval = p->ir->CreateICmp(cmpop, lv, rv, "tmp"); } else if (t->isfloating()) // includes iscomplex { eval = DtoBinNumericEquals(loc, l, r, op); } else if (t->ty == Tsarray || t->ty == Tarray) { Logger::println("static or dynamic array"); eval = DtoArrayEquals(loc,op,l,r); } else if (t->ty == Taarray) { Logger::println("associative array"); eval = DtoAAEquals(loc,op,l,r); } else if (t->ty == Tdelegate) { Logger::println("delegate"); eval = DtoDelegateEquals(op,l->getRVal(),r->getRVal()); } else if (t->ty == Tstruct) { Logger::println("struct"); // when this is reached it means there is no opEquals overload. eval = DtoStructEquals(op,l,r); } else { assert(0 && "Unsupported EqualExp type"); } return new DImValue(type, eval); } ////////////////////////////////////////////////////////////////////////////////////////// DValue* PostExp::toElem(IRState* p) { Logger::print("PostExp::toElem: %s @ %s\n", toChars(), type->toChars()); LOG_SCOPE; DValue* l = e1->toElem(p); e2->toElem(p); LLValue* val = l->getRVal(); LLValue* post = 0; Type* e1type = e1->type->toBasetype(); Type* e2type = e2->type->toBasetype(); if (e1type->isintegral()) { assert(e2type->isintegral()); LLValue* one = LLConstantInt::get(val->getType(), 1, !e2type->isunsigned()); if (op == TOKplusplus) { post = llvm::BinaryOperator::CreateAdd(val,one,"tmp",p->scopebb()); } else if (op == TOKminusminus) { post = llvm::BinaryOperator::CreateSub(val,one,"tmp",p->scopebb()); } } else if (e1type->ty == Tpointer) { assert(e2->op == TOKint64); LLConstant* minusone = LLConstantInt::get(DtoSize_t(),static_cast(-1),true); LLConstant* plusone = LLConstantInt::get(DtoSize_t(),static_cast(1),false); LLConstant* whichone = (op == TOKplusplus) ? plusone : minusone; post = llvm::GetElementPtrInst::Create(val, whichone, "tmp", p->scopebb()); } else if (e1type->isfloating()) { assert(e2type->isfloating()); LLValue* one = DtoConstFP(e1type, ldouble(1.0)); if (op == TOKplusplus) { post = llvm::BinaryOperator::CreateFAdd(val,one,"tmp",p->scopebb()); } else if (op == TOKminusminus) { post = llvm::BinaryOperator::CreateFSub(val,one,"tmp",p->scopebb()); } } else assert(post); DtoStore(post,l->getLVal()); return new DImValue(type,val); } ////////////////////////////////////////////////////////////////////////////////////////// DValue* NewExp::toElem(IRState* p) { Logger::print("NewExp::toElem: %s @ %s\n", toChars(), type->toChars()); LOG_SCOPE; assert(newtype); Type* ntype = newtype->toBasetype(); // new class if (ntype->ty == Tclass) { Logger::println("new class"); return DtoNewClass(loc, static_cast(ntype), this); } // new dynamic array else if (ntype->ty == Tarray) { Logger::println("new dynamic array: %s", newtype->toChars()); // get dim assert(arguments); assert(arguments->dim >= 1); if (arguments->dim == 1) { DValue* sz = static_cast(arguments->data[0])->toElem(p); // allocate & init return DtoNewDynArray(loc, newtype, sz, true); } else { size_t ndims = arguments->dim; std::vector dims(ndims); for (size_t i=0; i(arguments->data[i])->toElem(p); return DtoNewMulDimDynArray(loc, newtype, &dims[0], ndims, true); } } // new static array else if (ntype->ty == Tsarray) { assert(0); } // new struct else if (ntype->ty == Tstruct) { Logger::println("new struct on heap: %s\n", newtype->toChars()); // allocate LLValue* mem = 0; #if DMDV2 if (allocator) { // custom allocator allocator->codegen(Type::sir); DFuncValue dfn(allocator, allocator->ir.irFunc->func); DValue* res = DtoCallFunction(loc, NULL, &dfn, newargs); mem = DtoBitCast(res->getRVal(), DtoType(ntype->pointerTo()), ".newstruct_custom"); } else #endif { // default allocator mem = DtoNew(newtype); } // init TypeStruct* ts = static_cast(ntype); if (ts->isZeroInit(ts->sym->loc)) { DtoAggrZeroInit(mem); } else { assert(ts->sym); ts->sym->codegen(Type::sir); DtoAggrCopy(mem, ts->sym->ir.irStruct->getInitSymbol()); } #if DMDV2 if (ts->sym->isNested() && ts->sym->vthis) DtoResolveNestedContext(loc, ts->sym, mem); // call constructor if (member) { Logger::println("Calling constructor"); assert(arguments != NULL); member->codegen(Type::sir); DFuncValue dfn(member, member->ir.irFunc->func, mem); return DtoCallFunction(loc, ts, &dfn, arguments); } #endif return new DImValue(type, mem); } // new basic type else { // allocate LLValue* mem = DtoNew(newtype); DVarValue tmpvar(newtype, mem); // default initialize // static arrays never appear here, so using the defaultInit is ok! Expression* exp = newtype->defaultInit(loc); DValue* iv = exp->toElem(gIR); DtoAssign(loc, &tmpvar, iv); // return as pointer-to return new DImValue(type, mem); } assert(0); } ////////////////////////////////////////////////////////////////////////////////////////// DValue* DeleteExp::toElem(IRState* p) { Logger::print("DeleteExp::toElem: %s @ %s\n", toChars(), type->toChars()); LOG_SCOPE; DValue* dval = e1->toElem(p); Type* et = e1->type->toBasetype(); // simple pointer if (et->ty == Tpointer) { #if DMDV2 DtoDeleteMemory(dval->isLVal() ? dval->getLVal() : makeLValue(loc, dval)); #else LLValue* rval = dval->getRVal(); DtoDeleteMemory(rval); if (dval->isVar()) DtoStore(LLConstant::getNullValue(rval->getType()), dval->getLVal()); #endif } // class else if (et->ty == Tclass) { bool onstack = false; TypeClass* tc = static_cast(et); if (tc->sym->isInterfaceDeclaration()) { #if DMDV2 LLValue *val = dval->getLVal(); #else LLValue *val = dval->getRVal(); #endif DtoDeleteInterface(val); onstack = true; } else if (DVarValue* vv = dval->isVar()) { if (vv->var && vv->var->onstack) { DtoFinalizeClass(dval->getRVal()); onstack = true; } } if (!onstack) { LLValue* rval = dval->getRVal(); DtoDeleteClass(rval); } if (dval->isVar()) { LLValue* lval = dval->getLVal(); DtoStore(LLConstant::getNullValue(lval->getType()->getContainedType(0)), lval); } } // dyn array else if (et->ty == Tarray) { DtoDeleteArray(dval); if (dval->isLVal()) DtoSetArrayToNull(dval->getLVal()); } // unknown/invalid else { assert(0 && "invalid delete"); } // no value to return return NULL; } ////////////////////////////////////////////////////////////////////////////////////////// DValue* ArrayLengthExp::toElem(IRState* p) { Logger::print("ArrayLengthExp::toElem: %s @ %s\n", toChars(), type->toChars()); LOG_SCOPE; DValue* u = e1->toElem(p); return new DImValue(type, DtoArrayLen(u)); } ////////////////////////////////////////////////////////////////////////////////////////// DValue* AssertExp::toElem(IRState* p) { Logger::print("AssertExp::toElem: %s\n", toChars()); LOG_SCOPE; if(!global.params.useAssert) return NULL; // condition DValue* cond; Type* condty; // special case for dmd generated assert(this); when not in -release mode if (e1->op == TOKthis && static_cast(e1)->var == NULL) { LLValue* thisarg = p->func()->thisArg; assert(thisarg && "null thisarg, but we're in assert(this) exp;"); LLValue* thisptr = DtoLoad(p->func()->thisArg); condty = e1->type->toBasetype(); cond = new DImValue(condty, thisptr); } else { cond = e1->toElem(p); condty = e1->type->toBasetype(); } // create basic blocks llvm::BasicBlock* oldend = p->scopeend(); llvm::BasicBlock* assertbb = llvm::BasicBlock::Create(gIR->context(), "assert", p->topfunc(), oldend); llvm::BasicBlock* endbb = llvm::BasicBlock::Create(gIR->context(), "noassert", p->topfunc(), oldend); // test condition LLValue* condval = DtoCast(loc, cond, Type::tbool)->getRVal(); // branch llvm::BranchInst::Create(endbb, assertbb, condval, p->scopebb()); // call assert runtime functions p->scope() = IRScope(assertbb,endbb); DtoAssert(p->func()->decl->getModule(), loc, msg ? msg->toElem(p) : NULL); // rewrite the scope p->scope() = IRScope(endbb,oldend); InvariantDeclaration* invdecl; // class invariants if( global.params.useInvariants && condty->ty == Tclass && !(static_cast(condty)->sym->isInterfaceDeclaration())) { Logger::println("calling class invariant"); llvm::Function* fn = LLVM_D_GetRuntimeFunction(gIR->module, "_d_invariant"); LLValue* arg = DtoBitCast(cond->getRVal(), fn->getFunctionType()->getParamType(0)); gIR->CreateCallOrInvoke(fn, arg); } // struct invariants else if( global.params.useInvariants && condty->ty == Tpointer && condty->nextOf()->ty == Tstruct && (invdecl = static_cast(condty->nextOf())->sym->inv) != NULL) { Logger::print("calling struct invariant"); static_cast(condty->nextOf())->sym->codegen(Type::sir); DFuncValue invfunc(invdecl, invdecl->ir.irFunc->func, cond->getRVal()); DtoCallFunction(loc, NULL, &invfunc, NULL); } // DMD allows syntax like this: // f() == 0 || assert(false) return new DImValue(type, DtoConstBool(false)); } ////////////////////////////////////////////////////////////////////////////////////////// DValue* NotExp::toElem(IRState* p) { Logger::print("NotExp::toElem: %s @ %s\n", toChars(), type->toChars()); LOG_SCOPE; DValue* u = e1->toElem(p); LLValue* b = DtoCast(loc, u, Type::tbool)->getRVal(); LLConstant* zero = DtoConstBool(false); b = p->ir->CreateICmpEQ(b,zero); return new DImValue(type, b); } ////////////////////////////////////////////////////////////////////////////////////////// DValue* AndAndExp::toElem(IRState* p) { Logger::print("AndAndExp::toElem: %s @ %s\n", toChars(), type->toChars()); LOG_SCOPE; DValue* u = e1->toElem(p); llvm::BasicBlock* oldend = p->scopeend(); llvm::BasicBlock* andand = llvm::BasicBlock::Create(gIR->context(), "andand", gIR->topfunc(), oldend); llvm::BasicBlock* andandend = llvm::BasicBlock::Create(gIR->context(), "andandend", gIR->topfunc(), oldend); LLValue* ubool = DtoCast(loc, u, Type::tbool)->getRVal(); llvm::BasicBlock* oldblock = p->scopebb(); llvm::BranchInst::Create(andand,andandend,ubool,p->scopebb()); p->scope() = IRScope(andand, andandend); DValue* v = e2->toElemDtor(p); LLValue* vbool = 0; if (!v->isFunc() && v->getType() != Type::tvoid) { vbool = DtoCast(loc, v, Type::tbool)->getRVal(); } llvm::BasicBlock* newblock = p->scopebb(); llvm::BranchInst::Create(andandend,p->scopebb()); p->scope() = IRScope(andandend, oldend); LLValue* resval = 0; if (ubool == vbool || !vbool) { // No need to create a PHI node. resval = ubool; } else { llvm::PHINode* phi = p->ir->CreatePHI(LLType::getInt1Ty(gIR->context()), 2, "andandval"); // If we jumped over evaluation of the right-hand side, // the result is false. Otherwise it's the value of the right-hand side. phi->addIncoming(LLConstantInt::getFalse(gIR->context()), oldblock); phi->addIncoming(vbool, newblock); resval = phi; } return new DImValue(type, resval); } ////////////////////////////////////////////////////////////////////////////////////////// DValue* OrOrExp::toElem(IRState* p) { Logger::print("OrOrExp::toElem: %s @ %s\n", toChars(), type->toChars()); LOG_SCOPE; DValue* u = e1->toElem(p); llvm::BasicBlock* oldend = p->scopeend(); llvm::BasicBlock* oror = llvm::BasicBlock::Create(gIR->context(), "oror", gIR->topfunc(), oldend); llvm::BasicBlock* ororend = llvm::BasicBlock::Create(gIR->context(), "ororend", gIR->topfunc(), oldend); LLValue* ubool = DtoCast(loc, u, Type::tbool)->getRVal(); llvm::BasicBlock* oldblock = p->scopebb(); llvm::BranchInst::Create(ororend,oror,ubool,p->scopebb()); p->scope() = IRScope(oror, ororend); DValue* v = e2->toElemDtor(p); LLValue* vbool = 0; if (v && !v->isFunc() && v->getType() != Type::tvoid) { vbool = DtoCast(loc, v, Type::tbool)->getRVal(); } llvm::BasicBlock* newblock = p->scopebb(); llvm::BranchInst::Create(ororend,p->scopebb()); p->scope() = IRScope(ororend, oldend); LLValue* resval = 0; if (ubool == vbool || !vbool) { // No need to create a PHI node. resval = ubool; } else { llvm::PHINode* phi = p->ir->CreatePHI(LLType::getInt1Ty(gIR->context()), 2, "ororval"); // If we jumped over evaluation of the right-hand side, // the result is true. Otherwise, it's the value of the right-hand side. phi->addIncoming(LLConstantInt::getTrue(gIR->context()), oldblock); phi->addIncoming(vbool, newblock); resval = phi; } return new DImValue(type, resval); } ////////////////////////////////////////////////////////////////////////////////////////// #define BinBitExp(X,Y) \ DValue* X##Exp::toElem(IRState* p) \ { \ Logger::print("%sExp::toElem: %s @ %s\n", #X, toChars(), type->toChars()); \ LOG_SCOPE; \ DValue* u = e1->toElem(p); \ DValue* v = e2->toElem(p); \ errorOnIllegalArrayOp(this, e1, e2); \ LLValue* x = llvm::BinaryOperator::Create(llvm::Instruction::Y, u->getRVal(), v->getRVal(), "tmp", p->scopebb()); \ return new DImValue(type, x); \ } BinBitExp(And,And); BinBitExp(Or,Or); BinBitExp(Xor,Xor); BinBitExp(Shl,Shl); BinBitExp(Ushr,LShr); DValue* ShrExp::toElem(IRState* p) { Logger::print("ShrExp::toElem: %s @ %s\n", toChars(), type->toChars()); LOG_SCOPE; DValue* u = e1->toElem(p); DValue* v = e2->toElem(p); LLValue* x; if (isLLVMUnsigned(e1->type)) x = p->ir->CreateLShr(u->getRVal(), v->getRVal(), "tmp"); else x = p->ir->CreateAShr(u->getRVal(), v->getRVal(), "tmp"); return new DImValue(type, x); } ////////////////////////////////////////////////////////////////////////////////////////// DValue* HaltExp::toElem(IRState* p) { Logger::print("HaltExp::toElem: %s\n", toChars()); LOG_SCOPE; // FIXME: DMD inserts a trap here... we probably should as well !?! #if 1 DtoAssert(p->func()->decl->getModule(), loc, NULL); #else // call the new (?) trap intrinsic p->ir->CreateCall(GET_INTRINSIC_DECL(trap),""); new llvm::UnreachableInst(p->scopebb()); #endif // this terminated the basicblock, start a new one // this is sensible, since someone might goto behind the assert // and prevents compiler errors if a terminator follows the assert llvm::BasicBlock* oldend = gIR->scopeend(); llvm::BasicBlock* bb = llvm::BasicBlock::Create(gIR->context(), "afterhalt", p->topfunc(), oldend); p->scope() = IRScope(bb,oldend); return 0; } ////////////////////////////////////////////////////////////////////////////////////////// DValue* DelegateExp::toElem(IRState* p) { Logger::print("DelegateExp::toElem: %s @ %s\n", toChars(), type->toChars()); LOG_SCOPE; if(func->isStatic()) error("can't take delegate of static function %s, it does not require a context ptr", func->toChars()); LLPointerType* int8ptrty = getPtrToType(LLType::getInt8Ty(gIR->context())); assert(type->toBasetype()->ty == Tdelegate); LLType* dgty = DtoType(type); DValue* u = e1->toElem(p); LLValue* uval; if (DFuncValue* f = u->isFunc()) { assert(f->func); LLValue* contextptr = DtoNestedContext(loc, f->func); uval = DtoBitCast(contextptr, getVoidPtrType()); } else { DValue* src = u; if (ClassDeclaration* cd = u->getType()->isClassHandle()) { Logger::println("context type is class handle"); if (cd->isInterfaceDeclaration()) { Logger::println("context type is interface"); src = DtoCastInterfaceToObject(u, ClassDeclaration::object->type); } } uval = src->getRVal(); } if (Logger::enabled()) Logger::cout() << "context = " << *uval << '\n'; LLValue* castcontext = DtoBitCast(uval, int8ptrty); Logger::println("func: '%s'", func->toPrettyChars()); LLValue* castfptr; if (e1->op != TOKsuper && e1->op != TOKdottype && func->isVirtual() && !func->isFinal()) castfptr = DtoVirtualFunctionPointer(u, func, toChars()); else if (func->isAbstract()) assert(0 && "TODO delegate to abstract method"); else if (func->toParent()->isInterfaceDeclaration()) assert(0 && "TODO delegate to interface method"); else { func->codegen(Type::sir); castfptr = func->ir.irFunc->func; } castfptr = DtoBitCast(castfptr, dgty->getContainedType(1)); return new DImValue(type, DtoAggrPair(DtoType(type), castcontext, castfptr, ".dg")); } ////////////////////////////////////////////////////////////////////////////////////////// DValue* IdentityExp::toElem(IRState* p) { Logger::print("IdentityExp::toElem: %s @ %s\n", toChars(), type->toChars()); LOG_SCOPE; DValue* l = e1->toElem(p); DValue* r = e2->toElem(p); LLValue* lv = l->getRVal(); LLValue* rv = r->getRVal(); Type* t1 = e1->type->toBasetype(); // handle dynarray specially if (t1->ty == Tarray) return new DImValue(type, DtoDynArrayIs(op,l,r)); // also structs else if (t1->ty == Tstruct) return new DImValue(type, DtoStructEquals(op,l,r)); // FIXME this stuff isn't pretty LLValue* eval = 0; if (t1->ty == Tdelegate) { if (r->isNull()) { rv = NULL; } else { assert(lv->getType() == rv->getType()); } eval = DtoDelegateEquals(op,lv,rv); } else if (t1->isfloating()) // includes iscomplex { eval = DtoBinNumericEquals(loc, l, r, op); } else if (t1->ty == Tpointer || t1->ty == Tclass) { if (lv->getType() != rv->getType()) { if (r->isNull()) rv = llvm::ConstantPointerNull::get(isaPointer(lv->getType())); else rv = DtoBitCast(rv, lv->getType()); } eval = (op == TOKidentity) ? p->ir->CreateICmpEQ(lv,rv,"tmp") : p->ir->CreateICmpNE(lv,rv,"tmp"); } else { assert(lv->getType() == rv->getType()); eval = (op == TOKidentity) ? p->ir->CreateICmpEQ(lv,rv,"tmp") : p->ir->CreateICmpNE(lv,rv,"tmp"); } return new DImValue(type, eval); } ////////////////////////////////////////////////////////////////////////////////////////// DValue* CommaExp::toElem(IRState* p) { Logger::print("CommaExp::toElem: %s @ %s\n", toChars(), type->toChars()); LOG_SCOPE; if (cachedLvalue) { LLValue* V = cachedLvalue; return new DVarValue(type, V); } e1->toElem(p); DValue* v = e2->toElem(p); assert(e2->type == type); return v; } void CommaExp::cacheLvalue(IRState* p) { Logger::println("Caching l-value of %s", toChars()); LOG_SCOPE; cachedLvalue = toElem(p)->getLVal(); } ////////////////////////////////////////////////////////////////////////////////////////// DValue* CondExp::toElem(IRState* p) { Logger::print("CondExp::toElem: %s @ %s\n", toChars(), type->toChars()); LOG_SCOPE; Type* dtype = type->toBasetype(); DValue* dvv; // voids returns will need no storage if (dtype->ty != Tvoid) { // allocate a temporary for the final result. failed to come up with a better way :/ LLValue* resval = DtoAlloca(dtype,"condtmp"); dvv = new DVarValue(type, resval); } else { dvv = new DConstValue(type, getNullValue(DtoTypeNotVoid(dtype))); } llvm::BasicBlock* oldend = p->scopeend(); llvm::BasicBlock* condtrue = llvm::BasicBlock::Create(gIR->context(), "condtrue", gIR->topfunc(), oldend); llvm::BasicBlock* condfalse = llvm::BasicBlock::Create(gIR->context(), "condfalse", gIR->topfunc(), oldend); llvm::BasicBlock* condend = llvm::BasicBlock::Create(gIR->context(), "condend", gIR->topfunc(), oldend); DValue* c = econd->toElem(p); LLValue* cond_val = DtoCast(loc, c, Type::tbool)->getRVal(); llvm::BranchInst::Create(condtrue,condfalse,cond_val,p->scopebb()); p->scope() = IRScope(condtrue, condfalse); DValue* u = e1->toElemDtor(p); if (dtype->ty != Tvoid) DtoAssign(loc, dvv, u); llvm::BranchInst::Create(condend,p->scopebb()); p->scope() = IRScope(condfalse, condend); DValue* v = e2->toElemDtor(p); if (dtype->ty != Tvoid) DtoAssign(loc, dvv, v); llvm::BranchInst::Create(condend,p->scopebb()); p->scope() = IRScope(condend, oldend); return dvv; } ////////////////////////////////////////////////////////////////////////////////////////// DValue* ComExp::toElem(IRState* p) { Logger::print("ComExp::toElem: %s @ %s\n", toChars(), type->toChars()); LOG_SCOPE; DValue* u = e1->toElem(p); LLValue* value = u->getRVal(); LLValue* minusone = LLConstantInt::get(value->getType(), static_cast(-1), true); value = llvm::BinaryOperator::Create(llvm::Instruction::Xor, value, minusone, "tmp", p->scopebb()); return new DImValue(type, value); } ////////////////////////////////////////////////////////////////////////////////////////// DValue* NegExp::toElem(IRState* p) { Logger::print("NegExp::toElem: %s @ %s\n", toChars(), type->toChars()); LOG_SCOPE; DValue* l = e1->toElem(p); if (type->iscomplex()) { return DtoComplexNeg(loc, type, l); } LLValue* val = l->getRVal(); if (type->isintegral()) val = gIR->ir->CreateNeg(val,"negval"); else val = gIR->ir->CreateFNeg(val,"negval"); return new DImValue(type, val); } ////////////////////////////////////////////////////////////////////////////////////////// DValue* CatExp::toElem(IRState* p) { Logger::print("CatExp::toElem: %s @ %s\n", toChars(), type->toChars()); LOG_SCOPE; #if DMDV2 return DtoCatArrays(type, e1, e2); #else bool arrNarr = e1->type->toBasetype() == e2->type->toBasetype(); // array ~ array if (arrNarr) { return DtoCatArrays(type, e1, e2); } // array ~ element // element ~ array else { return DtoCatArrayElement(type, e1, e2); } #endif } ////////////////////////////////////////////////////////////////////////////////////////// DValue* CatAssignExp::toElem(IRState* p) { Logger::print("CatAssignExp::toElem: %s @ %s\n", toChars(), type->toChars()); LOG_SCOPE; DValue* l = e1->toElem(p); Type* e1type = e1->type->toBasetype(); assert(e1type->ty == Tarray); Type* elemtype = e1type->nextOf()->toBasetype(); Type* e2type = e2->type->toBasetype(); if (e1type->ty == Tarray && e2type->ty == Tdchar && (elemtype->ty == Tchar || elemtype->ty == Twchar)) { if (elemtype->ty == Tchar) // append dchar to char[] DtoAppendDCharToString(l, e2); else /*if (elemtype->ty == Twchar)*/ // append dchar to wchar[] DtoAppendDCharToUnicodeString(l, e2); } else if (e1type->equals(e2type)) { // apeend array DSliceValue* slice = DtoCatAssignArray(l,e2); DtoAssign(loc, l, slice); } else { // append element DtoCatAssignElement(loc, e1type, l, e2); } return l; } ////////////////////////////////////////////////////////////////////////////////////////// DValue* FuncExp::toElem(IRState* p) { Logger::print("FuncExp::toElem: %s @ %s\n", toChars(), type->toChars()); LOG_SCOPE; assert(fd); if (fd->isNested()) Logger::println("nested"); Logger::println("kind = %s", fd->kind()); fd->codegen(Type::sir); assert(fd->ir.irFunc->func); if(fd->isNested() && !(fd->tok == TOKreserved && type->ty == Tpointer && fd->vthis)) { LLType* dgty = DtoType(type); LLValue* cval; IrFunction* irfn = p->func(); if (irfn->nestedVar #if DMDV2 // We cannot use a frame allocated in one function // for a delegate created in another function // (that happens with anonymous functions) && fd->toParent2() == irfn->decl #endif ) cval = irfn->nestedVar; else if (irfn->nestArg) cval = DtoLoad(irfn->nestArg); #if DMDV2 // TODO: should we enable that for D1 as well? else if (irfn->thisArg) { AggregateDeclaration* ad = irfn->decl->isMember2(); if (!ad || !ad->vthis) { cval = getNullPtr(getVoidPtrType()); } else { cval = ad->isClassDeclaration() ? DtoLoad(irfn->thisArg) : irfn->thisArg; cval = DtoLoad(DtoGEPi(cval, 0,ad->vthis->ir.irField->index, ".vthis")); } } #endif else cval = getNullPtr(getVoidPtrType()); cval = DtoBitCast(cval, dgty->getContainedType(0)); LLValue* castfptr = DtoBitCast(fd->ir.irFunc->func, dgty->getContainedType(1)); return new DImValue(type, DtoAggrPair(cval, castfptr, ".func")); } else { return new DImValue(type, fd->ir.irFunc->func); } } ////////////////////////////////////////////////////////////////////////////////////////// LLConstant* FuncExp::toConstElem(IRState* p) { Logger::print("FuncExp::toConstElem: %s @ %s\n", toChars(), type->toChars()); LOG_SCOPE; assert(fd); if (fd->tok != TOKfunction && !(fd->tok == TOKreserved && type->ty == Tpointer && fd->vthis)) { assert(fd->tok == TOKdelegate || fd->tok == TOKreserved); error("delegate literals as constant expressions are not yet allowed"); return 0; } fd->codegen(Type::sir); assert(fd->ir.irFunc->func); return fd->ir.irFunc->func; } ////////////////////////////////////////////////////////////////////////////////////////// DValue* ArrayLiteralExp::toElem(IRState* p) { Logger::print("ArrayLiteralExp::toElem: %s @ %s\n", toChars(), type->toChars()); LOG_SCOPE; // D types Type* arrayType = type->toBasetype(); Type* elemType = arrayType->nextOf()->toBasetype(); // is dynamic ? bool dyn = (arrayType->ty == Tarray); // length size_t len = elements->dim; // llvm target type LLType* llType = DtoType(arrayType); if (Logger::enabled()) Logger::cout() << (dyn?"dynamic":"static") << " array literal with length " << len << " of D type: '" << arrayType->toChars() << "' has llvm type: '" << *llType << "'\n"; // llvm storage type LLType* llElemType = DtoTypeNotVoid(elemType); LLType* llStoType = LLArrayType::get(llElemType, len); if (Logger::enabled()) Logger::cout() << "llvm storage type: '" << *llStoType << "'\n"; // don't allocate storage for zero length dynamic array literals if (dyn && len == 0) { // dmd seems to just make them null... return new DSliceValue(type, DtoConstSize_t(0), getNullPtr(getPtrToType(llElemType))); } // dst pointer LLValue* dstMem; DSliceValue* dynSlice = NULL; if(dyn) { dynSlice = DtoNewDynArray(loc, arrayType, new DConstValue(Type::tsize_t, DtoConstSize_t(len)), false); dstMem = dynSlice->ptr; } else dstMem = DtoRawAlloca(llStoType, 0, "arrayliteral"); // store elements for (size_t i=0; i(elements->data[i]); LLValue* elemAddr; if(dyn) elemAddr = DtoGEPi1(dstMem, i, "tmp", p->scopebb()); else elemAddr = DtoGEPi(dstMem,0,i,"tmp",p->scopebb()); // emulate assignment DVarValue* vv = new DVarValue(expr->type, elemAddr); DValue* e = expr->toElem(p); DtoAssign(loc, vv, e); } // return storage directly ? if (!dyn) return new DImValue(type, dstMem); // return slice return dynSlice; } ////////////////////////////////////////////////////////////////////////////////////////// LLConstant* ArrayLiteralExp::toConstElem(IRState* p) { Logger::print("ArrayLiteralExp::toConstElem: %s @ %s\n", toChars(), type->toChars()); LOG_SCOPE; // extract D types Type* bt = type->toBasetype(); Type* elemt = bt->nextOf(); // build llvm array type LLArrayType* arrtype = LLArrayType::get(DtoTypeNotVoid(elemt), elements->dim); // dynamic arrays can occur here as well ... bool dyn = (bt->ty != Tsarray); // build the initializer std::vector vals(elements->dim, NULL); for (unsigned i=0; idim; ++i) { Expression* expr = static_cast(elements->data[i]); vals[i] = expr->toConstElem(p); } // build the constant array initialize LLArrayType *t = elements->dim == 0 ? arrtype : LLArrayType::get(vals.front()->getType(), elements->dim); LLConstant* initval = LLConstantArray::get(t, vals); // if static array, we're done if (!dyn) return initval; // we need to put the initializer in a global, and so we have a pointer to the array // Important: don't make the global constant, since this const initializer might // be used as an initializer for a static T[] - where modifying contents is allowed. LLConstant* globalstore = new LLGlobalVariable(*gIR->module, t, false, LLGlobalValue::InternalLinkage, initval, ".dynarrayStorage"); globalstore = DtoBitCast(globalstore, getPtrToType(arrtype)); #if DMDV2 if (bt->ty == Tpointer) // we need to return pointer to the static array. return globalstore; #endif // build a constant dynamic array reference with the .ptr field pointing into globalstore LLConstant* idxs[2] = { DtoConstUint(0), DtoConstUint(0) }; LLConstant* globalstorePtr = llvm::ConstantExpr::getGetElementPtr(globalstore, idxs, true); return DtoConstSlice(DtoConstSize_t(elements->dim), globalstorePtr); } ////////////////////////////////////////////////////////////////////////////////////////// extern LLConstant* get_default_initializer(VarDeclaration* vd, Initializer* init); static LLValue* write_zeroes(LLValue* mem, unsigned start, unsigned end) { mem = DtoBitCast(mem, getVoidPtrType()); LLValue* gep = DtoGEPi1(mem, start, ".padding"); DtoMemSetZero(gep, DtoConstSize_t(end - start)); return mem; } DValue* StructLiteralExp::toElem(IRState* p) { Logger::print("StructLiteralExp::toElem: %s @ %s\n", toChars(), type->toChars()); LOG_SCOPE; // make sure the struct is fully resolved sd->codegen(Type::sir); // alloca a stack slot LLValue* mem = DtoRawAlloca(DtoType(type), 0, ".structliteral"); // ready elements data assert(elements && "struct literal has null elements"); size_t nexprs = elements->dim; Expression** exprs = (Expression**)elements->data; LLValue* voidptr = mem; unsigned offset = 0; // go through fields ArrayIter it(sd->fields); for (; !it.done(); it.next()) { VarDeclaration* vd = it.get(); // don't re-initialize unions if (vd->offset < offset) { IF_LOG Logger::println("skipping field: %s %s (+%u)", vd->type->toChars(), vd->toChars(), vd->offset); continue; } // initialize any padding so struct comparisons work if (vd->offset != offset) voidptr = write_zeroes(voidptr, offset, vd->offset); offset = vd->offset + vd->type->size(); IF_LOG Logger::println("initializing field: %s %s (+%u)", vd->type->toChars(), vd->toChars(), vd->offset); // get initializer Expression* expr = (it.index < nexprs) ? exprs[it.index] : NULL; IF_LOG Logger::println("expr: %p", expr); DValue* val; DConstValue cv(vd->type, NULL); // Only used in one branch; value is set beforehand if (expr) { IF_LOG Logger::println("expr %zu = %s", it.index, expr->toChars()); val = expr->toElem(gIR); } #if DMDV2 else if (vd == sd->vthis) { IF_LOG Logger::println("initializing vthis"); val = new DImValue(vd->type, DtoBitCast(DtoNestedContext(loc, sd), DtoType(vd->type))); } #endif else { if (vd->init && vd->init->isVoidInitializer()) continue; IF_LOG Logger::println("using default initializer"); cv.c = get_default_initializer(vd, NULL); val = &cv; } // get a pointer to this field DVarValue field(vd->type, vd, DtoIndexStruct(mem, sd, vd)); // store the initializer there DtoAssign(loc, &field, val, TOKconstruct); #if DMDV2 Type *tb = vd->type->toBasetype(); if (tb->ty == Tstruct) { // Call postBlit() StructDeclaration *sd = static_cast(tb)->sym; if (sd->postblit) { FuncDeclaration *fd = sd->postblit; fd->codegen(Type::sir); Expressions args; DFuncValue dfn(fd, fd->ir.irFunc->func, val->getLVal()); DtoCallFunction(loc, Type::basic[Tvoid], &dfn, &args); } } #endif } // initialize trailing padding if (sd->structsize != offset) write_zeroes(voidptr, offset, sd->structsize); // return as a var return new DVarValue(type, mem); } ////////////////////////////////////////////////////////////////////////////////////////// LLConstant* StructLiteralExp::toConstElem(IRState* p) { Logger::print("StructLiteralExp::toConstElem: %s @ %s\n", toChars(), type->toChars()); LOG_SCOPE; // make sure the struct is resolved sd->codegen(Type::sir); // get inits std::vector inits(sd->fields.dim, NULL); size_t nexprs = elements->dim;; Expression** exprs = (Expression**)elements->data; for (size_t i = 0; i < nexprs; i++) if (exprs[i]) inits[i] = exprs[i]->toConstElem(p); // vector of values to build aggregate from std::vector values = DtoStructLiteralValues(sd, inits, true); // we know those values are constants.. cast them std::vector constvals(values.size(), NULL); std::vector types(values.size(), NULL); for (size_t i = 0; i < values.size(); ++i) { constvals[i] = llvm::cast(values[i]); types[i] = values[i]->getType(); } // return constant struct if (!constType) constType = LLStructType::get(gIR->context(), types); else if (constType->isOpaque()) constType->setBody(types); return LLConstantStruct::get(constType, llvm::makeArrayRef(constvals)); } ////////////////////////////////////////////////////////////////////////////////////////// DValue* InExp::toElem(IRState* p) { Logger::print("InExp::toElem: %s @ %s\n", toChars(), type->toChars()); LOG_SCOPE; DValue* key = e1->toElem(p); DValue* aa = e2->toElem(p); return DtoAAIn(loc, type, aa, key); } DValue* RemoveExp::toElem(IRState* p) { Logger::print("RemoveExp::toElem: %s\n", toChars()); LOG_SCOPE; DValue* aa = e1->toElem(p); DValue* key = e2->toElem(p); return DtoAARemove(loc, aa, key); } ////////////////////////////////////////////////////////////////////////////////////////// DValue* AssocArrayLiteralExp::toElem(IRState* p) { Logger::print("AssocArrayLiteralExp::toElem: %s @ %s\n", toChars(), type->toChars()); LOG_SCOPE; assert(keys); assert(values); assert(keys->dim == values->dim); Type* basetype = type->toBasetype(); Type* aatype = basetype; Type* vtype = aatype->nextOf(); #if DMDV2 if (!keys->dim) goto LruntimeInit; if (aatype->ty != Taarray) { // It's the AssociativeArray type. // Turn it back into a TypeAArray vtype = values->tdata()[0]->type; aatype = new TypeAArray(vtype, keys->tdata()[0]->type); aatype = aatype->semantic(loc, NULL); } { std::vector keysInits, valuesInits; for (size_t i = 0, n = keys->dim; i < n; ++i) { Expression* ekey = keys->tdata()[i]; Expression* eval = values->tdata()[i]; Logger::println("(%zu) aa[%s] = %s", i, ekey->toChars(), eval->toChars()); unsigned errors = global.startGagging(); LLConstant *ekeyConst = ekey->toConstElem(p); LLConstant *evalConst = eval->toConstElem(p); if (global.endGagging(errors)) goto LruntimeInit; assert(ekeyConst && evalConst); keysInits.push_back(ekeyConst); valuesInits.push_back(evalConst); } assert(aatype->ty == Taarray); Type* indexType = static_cast(aatype)->index; assert(indexType && vtype); llvm::Function* func = LLVM_D_GetRuntimeFunction(gIR->module, "_d_assocarrayliteralTX"); LLFunctionType* funcTy = func->getFunctionType(); LLValue* aaTypeInfo = DtoTypeInfoOf(stripModifiers(aatype)); LLConstant* idxs[2] = { DtoConstUint(0), DtoConstUint(0) }; LLArrayType* arrtype = LLArrayType::get(DtoType(indexType), keys->dim); LLConstant* initval = LLConstantArray::get(arrtype, keysInits); LLConstant* globalstore = new LLGlobalVariable(*gIR->module, arrtype, false, LLGlobalValue::InternalLinkage, initval, ".aaKeysStorage"); LLConstant* slice = llvm::ConstantExpr::getGetElementPtr(globalstore, idxs, true); slice = DtoConstSlice(DtoConstSize_t(keys->dim), slice); LLValue* keysArray = DtoAggrPaint(slice, funcTy->getParamType(1)); arrtype = LLArrayType::get(DtoType(vtype), values->dim); initval = LLConstantArray::get(arrtype, valuesInits); globalstore = new LLGlobalVariable(*gIR->module, arrtype, false, LLGlobalValue::InternalLinkage, initval, ".aaValuesStorage"); slice = llvm::ConstantExpr::getGetElementPtr(globalstore, idxs, true); slice = DtoConstSlice(DtoConstSize_t(keys->dim), slice); LLValue* valuesArray = DtoAggrPaint(slice, funcTy->getParamType(2)); LLValue* aa = gIR->CreateCallOrInvoke3(func, aaTypeInfo, keysArray, valuesArray, "aa").getInstruction(); if (basetype->ty != Taarray) { LLValue *tmp = DtoAlloca(type, "aaliteral"); DtoStore(aa, DtoGEPi(tmp, 0, 0)); return new DVarValue(type, tmp); } else { return new DImValue(type, aa); } } LruntimeInit: #endif // it should be possible to avoid the temporary in some cases LLValue* tmp = DtoAlloca(type, "aaliteral"); DValue* aa = new DVarValue(type, tmp); DtoStore(LLConstant::getNullValue(DtoType(type)), tmp); const size_t n = keys->dim; for (size_t i=0; i(keys->data[i]); Expression* eval = static_cast(values->data[i]); Logger::println("(%zu) aa[%s] = %s", i, ekey->toChars(), eval->toChars()); // index DValue* key = ekey->toElem(p); DValue* mem = DtoAAIndex(loc, vtype, aa, key, true); // store DValue* val = eval->toElem(p); DtoAssign(loc, mem, val); } return aa; } ////////////////////////////////////////////////////////////////////////////////////////// DValue* GEPExp::toElem(IRState* p) { // (&a.foo).funcptr is a case where e1->toElem is genuinely not an l-value. LLValue* val = makeLValue(loc, e1->toElem(p)); LLValue* v = DtoGEPi(val, 0, index); return new DVarValue(type, DtoBitCast(v, getPtrToType(DtoType(type)))); } ////////////////////////////////////////////////////////////////////////////////////////// DValue* BoolExp::toElem(IRState* p) { return new DImValue(type, DtoCast(loc, e1->toElem(p), Type::tbool)->getRVal()); } ////////////////////////////////////////////////////////////////////////////////////////// DValue* DotTypeExp::toElem(IRState* p) { Type* t = sym->getType(); assert(t); return e1->toElem(p); } ////////////////////////////////////////////////////////////////////////////////////////// DValue* TypeExp::toElem(IRState *p) { error("type %s is not an expression", toChars()); //TODO: Improve error handling. DMD just returns some value here and hopes // some more sensible error messages will be triggered. fatal(); return NULL; } ////////////////////////////////////////////////////////////////////////////////////////// DValue* TupleExp::toElem(IRState *p) { Logger::print("TupleExp::toElem() %s\n", toChars()); std::vector types(exps->dim, NULL); for (size_t i = 0; i < exps->dim; i++) { Expression *el = static_cast(exps->data[i]); types[i] = DtoTypeNotVoid(el->type); } LLValue *val = DtoRawAlloca(LLStructType::get(gIR->context(), types),0, "tuple"); for (size_t i = 0; i < exps->dim; i++) { Expression *el = static_cast(exps->data[i]); DValue* ep = el->toElem(p); LLValue *gep = DtoGEPi(val,0,i); if (el->type->ty == Tstruct) DtoStore(DtoLoad(ep->getRVal()), gep); else if (el->type->ty != Tvoid) DtoStore(ep->getRVal(), gep); else DtoStore(LLConstantInt::get(LLType::getInt8Ty(gIR->context()), 0, false), gep); } return new DImValue(type, val); } ////////////////////////////////////////////////////////////////////////////////////////// #if DMDV2 DValue* VectorExp::toElem(IRState* p) { Logger::print("VectorExp::toElem() %s\n", toChars()); LOG_SCOPE; TypeVector *type = static_cast(to->toBasetype()); assert(type->ty == Tvector); DValue *val = e1->toElem(p); val = DtoCast(loc, val, type->elementType()); LLValue *rval = val->getRVal(); LLValue *vector = DtoAlloca(to); for (int i = 0; i < dim; ++i) { LLValue *elem = DtoGEPi(vector, 0, i); DtoStore(rval, elem); } return new DVarValue(to, vector); } #endif ////////////////////////////////////////////////////////////////////////////////////////// #define STUB(x) DValue *x::toElem(IRState * p) {error("Exp type "#x" not implemented: %s", toChars()); fatal(); return 0; } STUB(Expression); STUB(ScopeExp); #if DMDV2 STUB(SymbolExp); STUB(PowExp); STUB(PowAssignExp); #endif #define CONSTSTUB(x) LLConstant* x::toConstElem(IRState * p) { \ error("expression '%s' is not a constant", toChars()); \ if (!global.gag) \ fatal(); \ return NULL; \ } CONSTSTUB(Expression); CONSTSTUB(GEPExp); CONSTSTUB(SliceExp); CONSTSTUB(IndexExp); CONSTSTUB(AssocArrayLiteralExp); ////////////////////////////////////////////////////////////////////////////////////////// void obj_includelib(const char* lib) { size_t n = strlen(lib)+3; char *arg = static_cast(mem.malloc(n)); strcpy(arg, "-l"); strncat(arg, lib, n); global.params.linkswitches->push(arg); } void backend_init() { // LLVM_D_InitRuntime is done in Module::genLLVMModule // since it requires the semantic pass to be done } void backend_term() { LLVM_D_FreeRuntime(); llvm::llvm_shutdown(); }