//===-- toir.cpp ----------------------------------------------------------===// // // LDC – the LLVM D compiler // // This file is distributed under the BSD-style LDC license. See the LICENSE // file for details. // //===----------------------------------------------------------------------===// #include "attrib.h" #include "enum.h" #include "gen/aa.h" #include "gen/abi.h" #include "gen/arrays.h" #include "gen/classes.h" #include "gen/complex.h" #include "gen/coverage.h" #include "gen/dvalue.h" #include "gen/functions.h" #include "gen/inlineir.h" #include "gen/irstate.h" #include "gen/llvm.h" #include "gen/llvmhelpers.h" #include "gen/logger.h" #include "gen/nested.h" #include "gen/optimizer.h" #include "gen/pragma.h" #include "gen/runtime.h" #include "gen/structs.h" #include "gen/tollvm.h" #include "gen/typinf.h" #include "gen/warnings.h" #include "hdrgen.h" #include "id.h" #include "init.h" #include "ir/irfunction.h" #include "ir/irtypeclass.h" #include "ir/irtypestruct.h" #include "ldcbindings.h" #include "module.h" #include "mtype.h" #include "port.h" #include "rmem.h" #include "template.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/ManagedStatic.h" #include #include #include #include // Needs other includes. #include "ctfe.h" llvm::cl::opt checkPrintf( "check-printf-calls", llvm::cl::desc("Validate printf call format strings against arguments"), llvm::cl::ZeroOrMore); bool walkPostorder(Expression *e, StoppableVisitor *v); extern LLConstant *get_default_initializer(VarDeclaration *vd); //////////////////////////////////////////////////////////////////////////////// dinteger_t undoStrideMul(Loc &loc, Type *t, dinteger_t offset) { assert(t->ty == Tpointer); d_uns64 elemSize = t->nextOf()->size(loc); assert((offset % elemSize) == 0 && "Expected offset by an integer amount of elements"); return offset / elemSize; } //////////////////////////////////////////////////////////////////////////////// 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; } //////////////////////////////////////////////////////////////////////////////// static void write_struct_literal(Loc loc, LLValue *mem, StructDeclaration *sd, Expressions *elements) { // ready elements data assert(elements && "struct literal has null elements"); const size_t nexprs = elements->dim; Expression **exprs = reinterpret_cast(elements->data); // might be reset to an actual i8* value so only a single bitcast is emitted. LLValue *voidptr = mem; unsigned offset = 0; // go through fields const size_t nfields = sd->fields.dim; for (size_t index = 0; index < nfields; ++index) { VarDeclaration *vd = sd->fields[index]; // get initializer expression Expression *expr = (index < nexprs) ? exprs[index] : nullptr; if (vd->overlapped && !expr) { // In case of an union (overlapped field), we can't simply use the default // initializer. // Consider the type union U7727A1 { int i; double d; } and // the declaration U7727A1 u = { d: 1.225 }; // The loop will first visit variable i and then d. Since d has an // explicit initializer, we must use this one. We should therefore skip // union fields // with no explicit initializer. IF_LOG Logger::println( "skipping overlapped field without init expr: %s %s (+%u)", vd->type->toChars(), vd->toChars(), vd->offset); continue; } // 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); LOG_SCOPE // get initializer DValue *val; std::unique_ptr cv; if (expr) { IF_LOG Logger::println("expr %llu = %s", static_cast(index), expr->toChars()); val = toElem(expr); } else if (vd == sd->vthis) { IF_LOG Logger::println("initializing vthis"); LOG_SCOPE val = new DImValue( vd->type, DtoBitCast(DtoNestedContext(loc, sd), DtoType(vd->type))); } else { if (vd->_init && vd->_init->isVoidInitializer()) { continue; } IF_LOG Logger::println("using default initializer"); LOG_SCOPE cv.reset(new DConstValue(vd->type, get_default_initializer(vd))); val = cv.get(); } // get a pointer to this field assert(!isSpecialRefVar(vd) && "Code not expected to handle special ref " "vars, although it can easily be made to."); DLValue field(vd->type, DtoIndexAggregate(mem, sd, vd)); // store the initializer there DtoAssign(loc, &field, val, TOKconstruct, true); if (expr && expr->isLvalue()) { callPostblit(loc, expr, DtoLVal(&field)); } // Also zero out padding bytes counted as being part of the type in DMD // but not in LLVM; e.g. real/x86_fp80. int implicitPadding = vd->type->size() - gDataLayout->getTypeStoreSize(DtoType(vd->type)); assert(implicitPadding >= 0); if (implicitPadding > 0) { IF_LOG Logger::println("zeroing %d padding bytes", implicitPadding); voidptr = write_zeroes(voidptr, offset - implicitPadding, offset); } } // initialize trailing padding if (sd->structsize != offset) { voidptr = write_zeroes(voidptr, offset, sd->structsize); } } namespace { void pushVarDtorCleanup(IRState *p, VarDeclaration *vd) { llvm::BasicBlock *beginBB = llvm::BasicBlock::Create( p->context(), llvm::Twine("dtor.") + vd->toChars(), p->topfunc()); // TODO: Clean this up with push/pop insertion point methods. IRScope oldScope = p->scope(); p->scope() = IRScope(beginBB); toElemDtor(vd->edtor); p->func()->scopes->pushCleanup(beginBB, p->scopebb()); p->scope() = oldScope; } } //////////////////////////////////////////////////////////////////////////////// // Tries to find the proper lvalue subexpression of an assign/binassign // expression. // Returns null if none is found. static Expression *findLvalueExp(Expression *e) { class FindLvalueVisitor : public Visitor { public: Expression *result; FindLvalueVisitor() : result(nullptr) {} void visit(Expression *e) LLVM_OVERRIDE {} #define FORWARD(TYPE) \ void visit(TYPE *e) LLVM_OVERRIDE { e->e1->accept(this); } FORWARD(AssignExp) FORWARD(BinAssignExp) FORWARD(CastExp) #undef FORWARD #define IMPLEMENT(TYPE) \ void visit(TYPE *e) LLVM_OVERRIDE { result = e; } IMPLEMENT(VarExp) IMPLEMENT(CallExp) IMPLEMENT(PtrExp) IMPLEMENT(DotVarExp) IMPLEMENT(IndexExp) IMPLEMENT(CommaExp) #undef IMPLEMENT }; FindLvalueVisitor v; e->accept(&v); return v.result; } // Evaluates an lvalue expression e and prevents further // evaluations as long as e->cachedLvalue isn't reset to null. static DValue *toElemAndCacheLvalue(Expression *e) { DValue *value = toElem(e); e->cachedLvalue = DtoLVal(value); return value; } // Evaluates e and, if tryGetLvalue is true, returns the // (casted) nested lvalue if one is found. // Otherwise simply returns the expression's result. DValue *toElem(Expression *e, bool tryGetLvalue) { if (!tryGetLvalue) { return toElem(e); } Expression *lvalExp = findLvalueExp(e); // may be null Expression *nestedLvalExp = (lvalExp == e ? nullptr : lvalExp); DValue *nestedLval = nullptr; if (nestedLvalExp) { IF_LOG Logger::println("Caching l-value of %s => %s", e->toChars(), nestedLvalExp->toChars()); LOG_SCOPE; nestedLval = toElemAndCacheLvalue(nestedLvalExp); } DValue *value = toElem(e); if (nestedLvalExp) { nestedLvalExp->cachedLvalue = nullptr; } return !nestedLval ? value : DtoCast(e->loc, nestedLval, e->type); } //////////////////////////////////////////////////////////////////////////////// class ToElemVisitor : public Visitor { IRState *p; bool destructTemporaries; CleanupCursor initialCleanupScope; DValue *result; public: ToElemVisitor(IRState *p_, bool destructTemporaries_) : p(p_), destructTemporaries(destructTemporaries_), result(nullptr) { initialCleanupScope = p->func()->scopes->currentCleanupScope(); } DValue *getResult() { if (destructTemporaries && p->func()->scopes->currentCleanupScope() != initialCleanupScope) { // We might share the CFG edges through the below cleanup blocks with // other paths (e.g. exception unwinding) where the result value has not // been constructed. At runtime, the branches will be chosen such that the // end bb (which will likely go on to access the value) is never executed // in those other cases, but we need to make sure that the SSA is also // well-formed statically (i.e. all instructions dominate their uses). // Thus, dump the result to a temporary stack slot (created in the entry // bb) if it is not guaranteed to dominate the end bb after possibly // adding more control flow. if (result && result->type->ty != Tvoid && !result->definedInFuncEntryBB()) { if (result->isLVal()) { LLValue *copy = DtoAlloca(result->type); DtoMemCpy(copy, DtoLVal(result)); result = new DLValue(result->type, copy); } else { LLValue *copy = DtoAllocaDump(result); result = new DLValue(result->type, copy); } } llvm::BasicBlock *endbb = llvm::BasicBlock::Create( p->context(), "toElem.success", p->topfunc()); p->func()->scopes->runCleanups(initialCleanupScope, endbb); p->func()->scopes->popCleanups(initialCleanupScope); p->scope() = IRScope(endbb); destructTemporaries = false; } return result; } ////////////////////////////////////////////////////////////////////////////// // Import all functions from class Visitor using Visitor::visit; ////////////////////////////////////////////////////////////////////////////// void visit(DeclarationExp *e) override { IF_LOG Logger::print("DeclarationExp::toElem: %s | T=%s\n", e->toChars(), e->type ? e->type->toChars() : "(null)"); LOG_SCOPE; auto &PGO = gIR->func()->pgo; PGO.setCurrentStmt(e); result = DtoDeclarationExp(e->declaration); if (auto vd = e->declaration->isVarDeclaration()) { if (!vd->isDataseg() && vd->needsScopeDtor()) { pushVarDtorCleanup(p, vd); } } } ////////////////////////////////////////////////////////////////////////////// void visit(VarExp *e) override { IF_LOG Logger::print("VarExp::toElem: %s @ %s\n", e->toChars(), e->type->toChars()); LOG_SCOPE; assert(e->var); if (e->cachedLvalue) { LLValue *V = e->cachedLvalue; result = new DLValue(e->type, V); return; } if (auto fd = e->var->isFuncLiteralDeclaration()) { genFuncLiteral(fd, nullptr); } if (auto em = e->var->isEnumMember()) { IF_LOG Logger::println("Create temporary for enum member"); // Return the value of the enum member instead of trying to take its // address (impossible because we don't emit them as variables) // In most cases, the front-end constfolds a VarExp of an EnumMember, // leaving the AST free of EnumMembers. However in rare cases, // EnumMembers remain and thus we have to deal with them here. // See DMD issues 16022 and 16100. result = toElem(em->value(), p); return; } result = DtoSymbolAddress(e->loc, e->type, e->var); } ////////////////////////////////////////////////////////////////////////////// void visit(IntegerExp *e) override { IF_LOG Logger::print("IntegerExp::toElem: %s @ %s\n", e->toChars(), e->type->toChars()); LOG_SCOPE; LLConstant *c = toConstElem(e, p); result = new DConstValue(e->type, c); } ////////////////////////////////////////////////////////////////////////////// void visit(RealExp *e) override { IF_LOG Logger::print("RealExp::toElem: %s @ %s\n", e->toChars(), e->type->toChars()); LOG_SCOPE; LLConstant *c = toConstElem(e, p); result = new DConstValue(e->type, c); } ////////////////////////////////////////////////////////////////////////////// void visit(NullExp *e) override { IF_LOG Logger::print("NullExp::toElem(type=%s): %s\n", e->type->toChars(), e->toChars()); LOG_SCOPE; LLConstant *c = toConstElem(e, p); result = new DNullValue(e->type, c); } ////////////////////////////////////////////////////////////////////////////// void visit(ComplexExp *e) override { IF_LOG Logger::print("ComplexExp::toElem(): %s @ %s\n", e->toChars(), e->type->toChars()); LOG_SCOPE; LLConstant *c = toConstElem(e, p); LLValue *res; if (c->isNullValue()) { switch (e->type->toBasetype()->ty) { default: llvm_unreachable("Unexpected complex floating point type"); case Tcomplex32: c = DtoConstFP(Type::tfloat32, ldouble(0)); break; case Tcomplex64: c = DtoConstFP(Type::tfloat64, ldouble(0)); break; case Tcomplex80: c = DtoConstFP(Type::tfloat80, ldouble(0)); break; } res = DtoAggrPair(DtoType(e->type), c, c); } else { res = DtoAggrPair(DtoType(e->type), c->getOperand(0), c->getOperand(1)); } result = new DImValue(e->type, res); } ////////////////////////////////////////////////////////////////////////////// void visit(StringExp *e) override { IF_LOG Logger::print("StringExp::toElem: %s @ %s\n", e->toChars(), e->type->toChars()); LOG_SCOPE; Type *dtype = e->type->toBasetype(); Type *cty = dtype->nextOf()->toBasetype(); LLType *ct = DtoMemType(cty); llvm::StringMap *stringLiteralCache = stringLiteralCacheForType(cty); LLConstant *_init = buildStringLiteralConstant(e, true); const auto at = _init->getType(); llvm::StringRef key(e->toChars()); llvm::GlobalVariable *gvar = (stringLiteralCache->find(key) == stringLiteralCache->end()) ? nullptr : (*stringLiteralCache)[key]; if (gvar == nullptr) { llvm::GlobalValue::LinkageTypes _linkage = llvm::GlobalValue::PrivateLinkage; IF_LOG { Logger::cout() << "type: " << *at << '\n'; Logger::cout() << "init: " << *_init << '\n'; } gvar = new llvm::GlobalVariable(gIR->module, at, true, _linkage, _init, ".str"); #if LDC_LLVM_VER >= 309 gvar->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global); #else gvar->setUnnamedAddr(true); #endif (*stringLiteralCache)[key] = gvar; } llvm::ConstantInt *zero = LLConstantInt::get(LLType::getInt32Ty(gIR->context()), 0, false); LLConstant *idxs[2] = {zero, zero}; #if LDC_LLVM_VER >= 307 LLConstant *arrptr = llvm::ConstantExpr::getGetElementPtr( isaPointer(gvar)->getElementType(), gvar, idxs, true); #else LLConstant *arrptr = llvm::ConstantExpr::getGetElementPtr(gvar, idxs, true); #endif if (dtype->ty == Tarray) { LLConstant *clen = LLConstantInt::get(DtoSize_t(), e->numberOfCodeUnits(), false); result = new DImValue(e->type, DtoConstSlice(clen, arrptr, dtype)); } else if (dtype->ty == Tsarray) { LLType *dstType = getPtrToType(LLArrayType::get(ct, e->numberOfCodeUnits())); LLValue *emem = (gvar->getType() == dstType) ? gvar : DtoBitCast(gvar, dstType); result = new DLValue(e->type, emem); } else if (dtype->ty == Tpointer) { result = new DImValue(e->type, arrptr); } else { llvm_unreachable("Unknown type for StringExp."); } } ////////////////////////////////////////////////////////////////////////////// void visit(AssignExp *e) override { IF_LOG Logger::print("AssignExp::toElem: %s | (%s)(%s = %s)\n", e->toChars(), e->type->toChars(), e->e1->type->toChars(), e->e2->type ? e->e2->type->toChars() : nullptr); LOG_SCOPE; if (e->e1->op == TOKarraylength) { Logger::println("performing array.length assignment"); ArrayLengthExp *ale = static_cast(e->e1); DLValue arrval(ale->e1->type, DtoLVal(ale->e1)); DValue *newlen = toElem(e->e2); DSliceValue *slice = DtoResizeDynArray(e->loc, arrval.type, &arrval, DtoRVal(newlen)); DtoAssign(e->loc, &arrval, slice); result = newlen; return; } // Initialization of ref variable? // Can't just override ConstructExp::toElem because not all TOKconstruct // operations are actually instances of ConstructExp... Long live the DMD // coding style! if (e->memset & referenceInit) { assert(e->op == TOKconstruct || e->op == TOKblit); assert(e->e1->op == TOKvar); Declaration *d = static_cast(e->e1)->var; if (d->storage_class & (STCref | STCout)) { Logger::println("performing ref variable initialization"); // Note that the variable value is accessed directly (instead // of via getLVal(), which would perform a load from the // uninitialized location), and that rhs is stored as an l-value! DSpecialRefValue *lhs = toElem(e->e1)->isSpecialRef(); assert(lhs); result = toElem(e->e2); // We shouldn't really need makeLValue() here, but the 2.063 // frontend generates ref variables initialized from function // calls. DtoStore(makeLValue(e->loc, result), lhs->getRefStorage()); return; } } if (e->e1->op == TOKslice) { // Check if this is an initialization of a static array with an array // literal that the frontend has foolishly rewritten into an // assignment of a dynamic array literal to a slice. Logger::println("performing static array literal assignment"); SliceExp *const se = static_cast(e->e1); Type *const t2 = e->e2->type->toBasetype(); Type *const ta = se->e1->type->toBasetype(); if (se->lwr == nullptr && ta->ty == Tsarray && e->e2->op == TOKarrayliteral && e->op == TOKconstruct && // DMD Bugzilla 11238: avoid aliasing issue t2->nextOf()->mutableOf()->implicitConvTo(ta->nextOf())) { ArrayLiteralExp *const ale = static_cast(e->e2); initializeArrayLiteral(p, ale, DtoLVal(se->e1)); result = toElem(e->e1); return; } } DValue *l = toElem(e->e1, true); // Direct construction by right-hand-side call via sret? // E.g., `T v = foo();` if the callee `T foo()` uses sret. // In this case, pass `&v` as hidden sret argument, i.e., let `foo()` // construct the return value directly into the lhs lvalue. if (l->isLVal() && e->op == TOKconstruct) { Type *const lhsBasetype = l->type->toBasetype(); Expression *rhs = e->e2; if (rhs->type->toBasetype() == lhsBasetype) { // Skip over rhs casts only emitted because of differing static array // constness. See runnable.sdtor.test10094. if (rhs->op == TOKcast && lhsBasetype->ty == Tsarray) { Expression *castSource = static_cast(rhs)->e1; Type *rhsElem = castSource->type->toBasetype()->nextOf(); if (rhsElem) { Type *l = lhsBasetype->nextOf()->arrayOf()->immutableOf(); Type *r = rhsElem->arrayOf()->immutableOf(); if (l->equals(r)) rhs = castSource; } } if (rhs->op == TOKcall) { auto ce = static_cast(rhs); if (DtoIsReturnInArg(ce)) { call(p, ce, DtoLVal(l)); result = l; return; } } } } DValue *r = toElem(e->e2); if (e->e1->type->toBasetype()->ty == Tstruct && e->e2->op == TOKint64) { Logger::println("performing aggregate zero initialization"); assert(e->e2->toInteger() == 0); DtoMemSetZero(DtoLVal(l)); TypeStruct *ts = static_cast(e->e1->type); if (ts->sym->isNested() && ts->sym->vthis) { DtoResolveNestedContext(e->loc, ts->sym, DtoLVal(l)); } // Return value should be irrelevant. result = r; return; } // This matches the logic in AssignExp::semantic. // TODO: Should be cached in the frontend to avoid issues with the code // getting out of sync? bool lvalueElem = false; if ((e->e2->op == TOKslice && static_cast(e->e2)->e1->isLvalue()) || (e->e2->op == TOKcast && static_cast(e->e2)->e1->isLvalue()) || (e->e2->op != TOKslice && e->e2->isLvalue())) { lvalueElem = true; } Logger::println("performing normal assignment (rhs has lvalue elems = %d)", lvalueElem); DtoAssign(e->loc, l, r, e->op, !lvalueElem); result = l; } ////////////////////////////////////////////////////////////////////////////// template static DValue *binAssign(BinAssignExp *e) { Loc loc = e->loc; // find the lhs' lvalue expression Expression *lvalExp = findLvalueExp(e->e1); if (!lvalExp) { e->error("expression %s does not mask any l-value", e->e1->toChars()); fatal(); } // pre-evaluate and cache the lvalue subexpression DValue *lval = nullptr; { IF_LOG Logger::println("Caching l-value of %s => %s", e->toChars(), lvalExp->toChars()); LOG_SCOPE; lval = toElemAndCacheLvalue(lvalExp); } // evaluate the underlying binary expression Expression *lhsForBinExp = (useLvalForBinExpLhs ? lvalExp : e->e1); BinExp *binExp = bindD::create(loc, lhsForBinExp, e->e2); binExp->type = lhsForBinExp->type; DValue *result = toElem(binExp); lvalExp->cachedLvalue = nullptr; // assign the (casted) result to lval DValue *assignedResult = DtoCast(loc, result, lval->type); DtoAssign(loc, lval, assignedResult); // return the (casted) result return e->type == lval->type ? lval : DtoCast(loc, lval, e->type); } #define BIN_ASSIGN(Op, useLvalForBinExpLhs) \ void visit(Op##AssignExp *e) override { \ IF_LOG Logger::print(#Op "AssignExp::toElem: %s @ %s\n", e->toChars(), \ e->type->toChars()); \ LOG_SCOPE; \ result = binAssign(e); \ } BIN_ASSIGN(Add, false) BIN_ASSIGN(Min, false) BIN_ASSIGN(Mul, false) BIN_ASSIGN(Div, false) BIN_ASSIGN(Mod, false) BIN_ASSIGN(And, false) BIN_ASSIGN(Or, false) BIN_ASSIGN(Xor, false) BIN_ASSIGN(Shl, true) BIN_ASSIGN(Shr, true) BIN_ASSIGN(Ushr, true) #undef BIN_ASSIGN ////////////////////////////////////////////////////////////////////////////// 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(); } } /// Tries to remove a MulExp by a constant value of baseSize from e. Returns /// NULL if not possible. Expression *extractNoStrideInc(Expression *e, d_uns64 baseSize, bool &negate) { MulExp *mul; while (true) { if (e->op == TOKneg) { negate = !negate; e = static_cast(e)->e1; continue; } if (e->op == TOKmul) { mul = static_cast(e); break; } return nullptr; } if (!mul->e2->isConst()) { return nullptr; } dinteger_t stride = mul->e2->toInteger(); if (stride != baseSize) { return nullptr; } return mul->e1; } DValue *emitPointerOffset(IRState *p, Loc loc, DValue *base, Expression *offset, bool negateOffset, Type *resultType) { // The operand emitted by the frontend is in units of bytes, and not // pointer elements. We try to undo this before resorting to // temporarily bitcasting the pointer to i8. llvm::Value *noStrideInc = nullptr; if (offset->isConst()) { dinteger_t byteOffset = offset->toInteger(); if (byteOffset == 0) { Logger::println("offset is zero"); return base; } noStrideInc = DtoConstSize_t(undoStrideMul(loc, base->type, byteOffset)); } else if (Expression *inc = extractNoStrideInc( offset, base->type->nextOf()->size(loc), negateOffset)) { noStrideInc = DtoRVal(inc); } if (noStrideInc) { if (negateOffset) { noStrideInc = p->ir->CreateNeg(noStrideInc); } return new DImValue(base->type, DtoGEP1(DtoRVal(base), noStrideInc, false)); } // This might not actually be generated by the frontend, just to be // safe. llvm::Value *inc = DtoRVal(offset); if (negateOffset) { inc = p->ir->CreateNeg(inc); } llvm::Value *bytePtr = DtoBitCast(DtoRVal(base), getVoidPtrType()); DValue *result = new DImValue(Type::tvoidptr, DtoGEP1(bytePtr, inc, false)); return DtoCast(loc, result, resultType); } void visit(AddExp *e) override { IF_LOG Logger::print("AddExp::toElem: %s @ %s\n", e->toChars(), e->type->toChars()); LOG_SCOPE; auto &PGO = gIR->func()->pgo; PGO.setCurrentStmt(e); DRValue *l = toElem(e->e1)->getRVal(); Type *t = e->type->toBasetype(); Type *e1type = e->e1->type->toBasetype(); Type *e2type = e->e2->type->toBasetype(); errorOnIllegalArrayOp(e, e->e1, e->e2); if (e1type != e2type && e1type->ty == Tpointer && e2type->isintegral()) { Logger::println("Adding integer to pointer"); result = emitPointerOffset(p, e->loc, l, e->e2, false, e->type); } else if (t->iscomplex()) { result = DtoComplexAdd(e->loc, e->type, l, toElem(e->e2)->getRVal()); } else { result = DtoBinAdd(l, toElem(e->e2)->getRVal()); } } void visit(MinExp *e) override { IF_LOG Logger::print("MinExp::toElem: %s @ %s\n", e->toChars(), e->type->toChars()); LOG_SCOPE; auto &PGO = gIR->func()->pgo; PGO.setCurrentStmt(e); DRValue *l = toElem(e->e1)->getRVal(); Type *t = e->type->toBasetype(); Type *t1 = e->e1->type->toBasetype(); Type *t2 = e->e2->type->toBasetype(); errorOnIllegalArrayOp(e, e->e1, e->e2); if (t1->ty == Tpointer && t2->ty == Tpointer) { LLValue *lv = DtoRVal(l); LLValue *rv = DtoRVal(e->e2); IF_LOG Logger::cout() << "lv: " << *lv << " rv: " << *rv << '\n'; lv = p->ir->CreatePtrToInt(lv, DtoSize_t()); rv = p->ir->CreatePtrToInt(rv, DtoSize_t()); LLValue *diff = p->ir->CreateSub(lv, rv); if (diff->getType() != DtoType(e->type)) { diff = p->ir->CreateIntToPtr(diff, DtoType(e->type)); } result = new DImValue(e->type, diff); } else if (t1->ty == Tpointer && t2->isintegral()) { Logger::println("Subtracting integer from pointer"); result = emitPointerOffset(p, e->loc, l, e->e2, true, e->type); } else if (t->iscomplex()) { result = DtoComplexSub(e->loc, e->type, l, toElem(e->e2)->getRVal()); } else { result = DtoBinSub(l, toElem(e->e2)->getRVal()); } } ////////////////////////////////////////////////////////////////////////////// void visit(MulExp *e) override { IF_LOG Logger::print("MulExp::toElem: %s @ %s\n", e->toChars(), e->type->toChars()); LOG_SCOPE; auto &PGO = gIR->func()->pgo; PGO.setCurrentStmt(e); DRValue *l = toElem(e->e1)->getRVal(); DRValue *r = toElem(e->e2)->getRVal(); errorOnIllegalArrayOp(e, e->e1, e->e2); if (e->type->iscomplex()) { result = DtoComplexMul(e->loc, e->type, l, r); } else { result = DtoBinMul(e->type, l, r); } } ////////////////////////////////////////////////////////////////////////////// void visit(DivExp *e) override { IF_LOG Logger::print("DivExp::toElem: %s @ %s\n", e->toChars(), e->type->toChars()); LOG_SCOPE; auto &PGO = gIR->func()->pgo; PGO.setCurrentStmt(e); DRValue *l = toElem(e->e1)->getRVal(); DRValue *r = toElem(e->e2)->getRVal(); errorOnIllegalArrayOp(e, e->e1, e->e2); if (e->type->iscomplex()) { result = DtoComplexDiv(e->loc, e->type, l, r); } else { result = DtoBinDiv(e->type, l, r); } } ////////////////////////////////////////////////////////////////////////////// void visit(ModExp *e) override { IF_LOG Logger::print("ModExp::toElem: %s @ %s\n", e->toChars(), e->type->toChars()); LOG_SCOPE; auto &PGO = gIR->func()->pgo; PGO.setCurrentStmt(e); DRValue *l = toElem(e->e1)->getRVal(); DRValue *r = toElem(e->e2)->getRVal(); errorOnIllegalArrayOp(e, e->e1, e->e2); if (e->type->iscomplex()) { result = DtoComplexRem(e->loc, e->type, l, r); } else { result = DtoBinRem(e->type, l, r); } } ////////////////////////////////////////////////////////////////////////////// static DValue *call(IRState *p, CallExp *e, LLValue *retvar = nullptr) { IF_LOG Logger::print("CallExp::toElem: %s @ %s\n", e->toChars(), e->type->toChars()); LOG_SCOPE; auto &PGO = gIR->func()->pgo; PGO.setCurrentStmt(e); if (e->cachedLvalue) { LLValue *V = e->cachedLvalue; return new DLValue(e->type, V); } // handle magic inline asm if (e->e1->op == TOKvar) { VarExp *ve = static_cast(e->e1); if (FuncDeclaration *fd = ve->var->isFuncDeclaration()) { if (fd->llvmInternal == LLVMinline_asm) { return DtoInlineAsmExpr(e->loc, fd, e->arguments); } if (fd->llvmInternal == LLVMinline_ir) { return DtoInlineIRExpr(e->loc, fd, e->arguments); } } } // Check if we are about to construct a just declared temporary. DMD // unfortunately rewrites this as // MyStruct(myArgs) => (MyStruct tmp; tmp).this(myArgs), // which would lead us to invoke the dtor even if the ctor throws. To // work around this, we hold on to the cleanup and push it only after // making the function call. // // The correct fix for this (DMD issue 13095) would have been to adapt // the AST, but we are stuck with this as DMD also patched over it with // a similar hack. VarDeclaration *delayedDtorVar = nullptr; Expression *delayedDtorExp = nullptr; if (e->f && e->f->isCtorDeclaration() && e->e1->op == TOKdotvar) { DotVarExp *dve = static_cast(e->e1); if (dve->e1->op == TOKcomma) { CommaExp *ce = static_cast(dve->e1); if (ce->e1->op == TOKdeclaration && ce->e2->op == TOKvar) { VarExp *ve = static_cast(ce->e2); if (VarDeclaration *vd = ve->var->isVarDeclaration()) { if (vd->needsScopeDtor()) { Logger::println("Delaying edtor"); delayedDtorVar = vd; delayedDtorExp = vd->edtor; vd->edtor = nullptr; } } } } } // get the callee value DValue *fnval; if (e->directcall) { // TODO: Do this as an extra parameter to DotVarExp implementation. assert(e->e1->op == TOKdotvar); DotVarExp *dve = static_cast(e->e1); FuncDeclaration *fdecl = dve->var->isFuncDeclaration(); assert(fdecl); DtoResolveFunction(fdecl); fnval = new DFuncValue(fdecl, getIrFunc(fdecl)->func, DtoRVal(dve->e1)); } else { fnval = toElem(e->e1); } // 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(e->loc, (*e->arguments)[0], e->arguments->dim); } } DValue *result = nullptr; if (DtoLowerMagicIntrinsic(p, fndecl, e, result)) return result; } DValue *result = DtoCallFunction(e->loc, e->type, fnval, e->arguments, retvar); if (delayedDtorVar) { delayedDtorVar->edtor = delayedDtorExp; pushVarDtorCleanup(p, delayedDtorVar); } return result; } void visit(CallExp *e) override { result = call(p, e); } ////////////////////////////////////////////////////////////////////////////// void visit(CastExp *e) override { IF_LOG Logger::print("CastExp::toElem: %s @ %s\n", e->toChars(), e->type->toChars()); LOG_SCOPE; auto &PGO = gIR->func()->pgo; PGO.setCurrentStmt(e); // get the value to cast DValue *u = toElem(e->e1); // handle cast to void (usually created by frontend to avoid "has no effect" // error) if (e->to == Type::tvoid) { result = new DConstValue(Type::tvoid, llvm::UndefValue::get(DtoMemType(Type::tvoid))); return; } // cast it to the 'to' type, if necessary result = u; if (!e->to->equals(e->e1->type)) { result = DtoCast(e->loc, u, e->to); } // paint the type, if necessary if (!e->type->equals(e->to)) { result = DtoPaintType(e->loc, result, e->type); } } ////////////////////////////////////////////////////////////////////////////// void visit(SymOffExp *e) override { IF_LOG Logger::print("SymOffExp::toElem: %s @ %s\n", e->toChars(), e->type->toChars()); LOG_SCOPE; auto &PGO = gIR->func()->pgo; PGO.setCurrentStmt(e); DValue *base = DtoSymbolAddress(e->loc, e->var->type, e->var); // This weird setup is required to be able to handle both variables as // well as functions and TypeInfo references (which are not a DLValue // as well due to the level-of-indirection hack in Type::getTypeInfo that // is unfortunately required by the frontend). llvm::Value *baseValue; if (base->isLVal()) { baseValue = DtoLVal(base); } else { baseValue = DtoRVal(base); } assert(isaPointer(baseValue)); llvm::Value *offsetValue; Type *offsetType; if (e->offset == 0) { offsetValue = baseValue; offsetType = base->type->pointerTo(); } else { uint64_t elemSize = gDataLayout->getTypeAllocSize( baseValue->getType()->getContainedType(0)); if (e->offset % elemSize == 0) { // We can turn this into a "nice" GEP. offsetValue = DtoGEPi1(baseValue, e->offset / elemSize); offsetType = base->type->pointerTo(); } else { // Offset isn't a multiple of base type size, just cast to i8* and // apply the byte offset. offsetValue = DtoGEPi1(DtoBitCast(baseValue, getVoidPtrType()), e->offset); offsetType = Type::tvoidptr; } } // Casts are also "optimized into" SymOffExp by the frontend. result = DtoCast(e->loc, new DImValue(offsetType, offsetValue), e->type); } ////////////////////////////////////////////////////////////////////////////// void visit(AddrExp *e) override { IF_LOG Logger::println("AddrExp::toElem: %s @ %s", e->toChars(), e->type->toChars()); LOG_SCOPE; auto &PGO = gIR->func()->pgo; PGO.setCurrentStmt(e); // The address of a StructLiteralExp can in fact be a global variable, check // for that instead of re-codegening the literal. if (e->e1->op == TOKstructliteral) { // lvalue literal must be a global, hence we can just use // toConstElem on the AddrExp to get the address. LLConstant *addr = toConstElem(e, p); IF_LOG Logger::cout() << "returning address of struct literal global: " << addr << '\n'; result = new DImValue(e->type, DtoBitCast(addr, DtoType(e->type))); return; } DValue *v = toElem(e->e1, true); if (DFuncValue *fv = v->isFunc()) { Logger::println("is func"); // Logger::println("FuncDeclaration"); FuncDeclaration *fd = fv->func; assert(fd); DtoResolveFunction(fd); result = new DFuncValue(fd, getIrFunc(fd)->func); return; } if (v->isIm()) { Logger::println("is immediate"); result = v; return; } 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 = DtoLVal(v); } else { assert(v->isSlice()); lval = DtoAllocaDump(v, ".tmp_slice_storage"); } IF_LOG Logger::cout() << "lval: " << *lval << '\n'; result = new DImValue(e->type, DtoBitCast(lval, DtoType(e->type))); } ////////////////////////////////////////////////////////////////////////////// void visit(PtrExp *e) override { IF_LOG Logger::println("PtrExp::toElem: %s @ %s", e->toChars(), e->type->toChars()); LOG_SCOPE; auto &PGO = gIR->func()->pgo; PGO.setCurrentStmt(e); // function pointers are special if (e->type->toBasetype()->ty == Tfunction) { assert(!e->cachedLvalue); DValue *dv = toElem(e->e1); if (DFuncValue *dfv = dv->isFunc()) { result = new DFuncValue(e->type, dfv->func, DtoRVal(dfv)); } else { result = new DImValue(e->type, DtoRVal(dv)); } return; } // get the rvalue and return it as an lvalue LLValue *V; if (e->cachedLvalue) { V = e->cachedLvalue; } else { V = DtoRVal(e->e1); } // The frontend emits dereferences of class/interfaces types to access the // first member, which is the .classinfo property. Type *origType = e->e1->type->toBasetype(); if (origType->ty == Tclass) { TypeClass *ct = static_cast(origType); Type *resultType; if (ct->sym->isInterfaceDeclaration()) { // For interfaces, the first entry in the vtbl is actually a pointer // to an Interface instance, which has the type info as its first // member, so we have to add an extra layer of indirection. resultType = Type::typeinfointerface->type->pointerTo(); } else { resultType = Type::typeinfoclass->type; } V = DtoBitCast(V, DtoType(resultType->pointerTo()->pointerTo())); } result = new DLValue(e->type, V); } ////////////////////////////////////////////////////////////////////////////// void visit(DotVarExp *e) override { IF_LOG Logger::print("DotVarExp::toElem: %s @ %s\n", e->toChars(), e->type->toChars()); LOG_SCOPE; auto &PGO = gIR->func()->pgo; PGO.setCurrentStmt(e); if (e->cachedLvalue) { Logger::println("using cached lvalue"); LLValue *V = e->cachedLvalue; assert(!isSpecialRefVar(e->var->isVarDeclaration()) && "Code not expected to handle special ref vars, although it can " "easily be made to."); result = new DLValue(e->type, V); return; } DValue *l = toElem(e->e1); Type *e1type = e->e1->type->toBasetype(); // Logger::println("e1type=%s", e1type->toChars()); // Logger::cout() << *DtoType(e1type) << '\n'; if (VarDeclaration *vd = e->var->isVarDeclaration()) { LLValue *arrptr; // indexing struct pointer if (e1type->ty == Tpointer) { assert(e1type->nextOf()->ty == Tstruct); TypeStruct *ts = static_cast(e1type->nextOf()); arrptr = DtoIndexAggregate(DtoRVal(l), ts->sym, vd); } // indexing normal struct else if (e1type->ty == Tstruct) { TypeStruct *ts = static_cast(e1type); arrptr = DtoIndexAggregate(DtoLVal(l), ts->sym, vd); } // indexing class else if (e1type->ty == Tclass) { TypeClass *tc = static_cast(e1type); arrptr = DtoIndexAggregate(DtoRVal(l), tc->sym, vd); } else { llvm_unreachable("Unknown DotVarExp type for VarDeclaration."); } // Logger::cout() << "mem: " << *arrptr << '\n'; result = new DLValue(e->type, arrptr); } else if (FuncDeclaration *fdecl = e->var->isFuncDeclaration()) { DtoResolveFunction(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 // isFinalFunc is not necessarily true. // Also, private/package methods are always non-virtual. const bool nonFinal = !fdecl->isFinalFunc() && (fdecl->isAbstract() || fdecl->isVirtual()) && fdecl->prot().kind != PROTprivate && fdecl->prot().kind != PROTpackage; // Get the actual function value to call. LLValue *funcval = nullptr; if (nonFinal) { funcval = DtoVirtualFunctionPointer(l, fdecl, e->toChars()); } else { funcval = getIrFunc(fdecl)->func; } assert(funcval); LLValue *vthis = (DtoIsInMemoryOnly(l->type) ? DtoLVal(l) : DtoRVal(l)); result = new DFuncValue(fdecl, funcval, vthis); } else { llvm_unreachable("Unknown target for VarDeclaration."); } } ////////////////////////////////////////////////////////////////////////////// void visit(ThisExp *e) override { IF_LOG Logger::print("ThisExp::toElem: %s @ %s\n", e->toChars(), e->type->toChars()); LOG_SCOPE; auto &PGO = gIR->func()->pgo; PGO.setCurrentStmt(e); // special cases: `this(int) { this(); }` and `this(int) { super(); }` if (!e->var) { Logger::println("this exp without var declaration"); result = new DLValue(e->type, p->func()->thisArg); return; } const auto vd = e->var->isVarDeclaration(); assert(vd); assert(!isSpecialRefVar(vd) && "Code not expected to handle special ref " "vars, although it can easily be made to."); LLValue *v; const auto ident = p->func()->decl->ident; if (ident == Id::ensure || ident == Id::require) { Logger::println("contract this exp"); v = DtoBitCast(p->func()->nestArg, DtoType(e->type)->getPointerTo()); } else if (vd->toParent2() != p->func()->decl) { Logger::println("nested this exp"); result = DtoNestedVariable(e->loc, e->type, vd, e->type->ty == Tstruct); return; } else { Logger::println("normal this exp"); v = p->func()->thisArg; } result = new DLValue(e->type, v); } ////////////////////////////////////////////////////////////////////////////// void visit(IndexExp *e) override { IF_LOG Logger::print("IndexExp::toElem: %s @ %s\n", e->toChars(), e->type->toChars()); LOG_SCOPE; auto &PGO = gIR->func()->pgo; PGO.setCurrentStmt(e); if (e->cachedLvalue) { LLValue *V = e->cachedLvalue; result = new DLValue(e->type, V); return; } DValue *l = toElem(e->e1); Type *e1type = e->e1->type->toBasetype(); p->arrays.push_back(l); // if $ is used it must be an array so this is fine. DValue *r = toElem(e->e2); p->arrays.pop_back(); LLValue *arrptr = nullptr; if (e1type->ty == Tpointer) { arrptr = DtoGEP1(DtoRVal(l), DtoRVal(r), false); } else if (e1type->ty == Tsarray) { if (p->emitArrayBoundsChecks() && !e->indexIsInBounds) { DtoIndexBoundsCheck(e->loc, l, r); } arrptr = DtoGEP(DtoLVal(l), DtoConstUint(0), DtoRVal(r), e->indexIsInBounds); } else if (e1type->ty == Tarray) { if (p->emitArrayBoundsChecks() && !e->indexIsInBounds) { DtoIndexBoundsCheck(e->loc, l, r); } arrptr = DtoGEP1(DtoArrayPtr(l), DtoRVal(r), e->indexIsInBounds); } else if (e1type->ty == Taarray) { result = DtoAAIndex(e->loc, e->type, l, r, e->modifiable); return; } else { IF_LOG Logger::println("e1type: %s", e1type->toChars()); llvm_unreachable("Unknown IndexExp target."); } result = new DLValue(e->type, arrptr); } ////////////////////////////////////////////////////////////////////////////// void visit(SliceExp *e) override { IF_LOG Logger::print("SliceExp::toElem: %s @ %s\n", e->toChars(), e->type->toChars()); LOG_SCOPE; auto &PGO = gIR->func()->pgo; PGO.setCurrentStmt(e); // value being sliced LLValue *elen = nullptr; LLValue *eptr; DValue *v = toElem(e->e1); Type *etype = e->e1->type->toBasetype(); if (etype->ty == Tpointer) { // pointer slicing assert(e->lwr); eptr = DtoRVal(v); } else { // array slice eptr = DtoArrayPtr(v); } // has lower bound, pointer needs adjustment if (e->lwr) { // must have upper bound too then assert(e->upr); // get bounds (make sure $ works) // The lower bound expression must be fully evaluated to an RVal before // evaluating the upper bound expression, because the lower bound // expression might change value after evaluating the upper bound, e.g. in // a statement like this: `auto a1 = values[offset .. offset += 2];` p->arrays.push_back(v); LLValue *vlo = DtoRVal(e->lwr); LLValue *vup = DtoRVal(e->upr); p->arrays.pop_back(); const bool needCheckUpper = (etype->ty != Tpointer) && !e->upperIsInBounds; const bool needCheckLower = !e->lowerIsLessThanUpper; if (p->emitArrayBoundsChecks() && (needCheckUpper || needCheckLower)) { llvm::BasicBlock *failbb = llvm::BasicBlock::Create(p->context(), "bounds.fail", p->topfunc()); llvm::BasicBlock *okbb = llvm::BasicBlock::Create(p->context(), "bounds.ok", p->topfunc()); llvm::Value *okCond = nullptr; if (needCheckUpper) { okCond = p->ir->CreateICmp(llvm::ICmpInst::ICMP_ULE, vup, DtoArrayLen(v), "bounds.cmp.lo"); } if (needCheckLower) { llvm::Value *cmp = p->ir->CreateICmp(llvm::ICmpInst::ICMP_ULE, vlo, vup, "bounds.cmp.up"); if (okCond) { okCond = p->ir->CreateAnd(okCond, cmp); } else { okCond = cmp; } } p->ir->CreateCondBr(okCond, okbb, failbb); p->scope() = IRScope(failbb); DtoBoundsCheckFailCall(p, e->loc); p->scope() = IRScope(okbb); } // offset by lower eptr = DtoGEP1(eptr, vlo, !needCheckLower, "lowerbound"); // adjust length elen = p->ir->CreateSub(vup, vlo); } // no bounds or full slice -> just convert to slice else { assert(e->e1->type->toBasetype()->ty != Tpointer); // if the slicee 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())); } } // The frontend generates a SliceExp of static array type when assigning a // fixed-width slice to a static array. if (e->type->toBasetype()->ty == Tsarray) { LLValue *v = DtoBitCast(eptr, DtoType(e->type->pointerTo())); result = new DLValue(e->type, v); return; } if (!elen) { elen = DtoArrayLen(v); } result = new DSliceValue(e->type, elen, eptr); } ////////////////////////////////////////////////////////////////////////////// void visit(CmpExp *e) override { IF_LOG Logger::print("CmpExp::toElem: %s @ %s\n", e->toChars(), e->type->toChars()); LOG_SCOPE; auto &PGO = gIR->func()->pgo; PGO.setCurrentStmt(e); DValue *l = toElem(e->e1); DValue *r = toElem(e->e2); Type *t = e->e1->type->toBasetype(); LLValue *eval = nullptr; if (t->isintegral() || t->ty == Tpointer || t->ty == Tnull) { llvm::ICmpInst::Predicate icmpPred; tokToICmpPred(e->op, isLLVMUnsigned(t), &icmpPred, &eval); if (!eval) { LLValue *a = DtoRVal(l); LLValue *b = DtoRVal(r); IF_LOG { 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(icmpPred, a, b); } } else if (t->isfloating()) { llvm::FCmpInst::Predicate cmpop; switch (e->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: llvm_unreachable("Unsupported floating point comparison operator."); } eval = p->ir->CreateFCmp(cmpop, DtoRVal(l), DtoRVal(r)); } else if (t->ty == Tsarray || t->ty == Tarray) { Logger::println("static or dynamic array"); eval = DtoArrayCompare(e->loc, e->op, l, r); } else if (t->ty == Taarray) { eval = LLConstantInt::getFalse(gIR->context()); } else if (t->ty == Tdelegate) { llvm::ICmpInst::Predicate icmpPred; tokToICmpPred(e->op, isLLVMUnsigned(t), &icmpPred, &eval); if (!eval) { // First compare the function pointers, then the context ones. This is // what DMD does. llvm::Value *lhs = DtoRVal(l); llvm::Value *rhs = DtoRVal(r); llvm::BasicBlock *fptreq = llvm::BasicBlock::Create(gIR->context(), "fptreq", gIR->topfunc()); llvm::BasicBlock *fptrneq = llvm::BasicBlock::Create(gIR->context(), "fptrneq", gIR->topfunc()); llvm::BasicBlock *dgcmpend = llvm::BasicBlock::Create( gIR->context(), "dgcmpend", gIR->topfunc()); llvm::Value *lfptr = p->ir->CreateExtractValue(lhs, 1, ".lfptr"); llvm::Value *rfptr = p->ir->CreateExtractValue(rhs, 1, ".rfptr"); llvm::Value *fptreqcmp = p->ir->CreateICmp(llvm::ICmpInst::ICMP_EQ, lfptr, rfptr, ".fptreqcmp"); llvm::BranchInst::Create(fptreq, fptrneq, fptreqcmp, p->scopebb()); p->scope() = IRScope(fptreq); llvm::Value *lctx = p->ir->CreateExtractValue(lhs, 0, ".lctx"); llvm::Value *rctx = p->ir->CreateExtractValue(rhs, 0, ".rctx"); llvm::Value *ctxcmp = p->ir->CreateICmp(icmpPred, lctx, rctx, ".ctxcmp"); llvm::BranchInst::Create(dgcmpend, p->scopebb()); p->scope() = IRScope(fptrneq); llvm::Value *fptrcmp = p->ir->CreateICmp(icmpPred, lfptr, rfptr, ".fptrcmp"); llvm::BranchInst::Create(dgcmpend, p->scopebb()); p->scope() = IRScope(dgcmpend); llvm::PHINode *phi = p->ir->CreatePHI(ctxcmp->getType(), 2, ".dgcmp"); phi->addIncoming(ctxcmp, fptreq); phi->addIncoming(fptrcmp, fptrneq); eval = phi; } } else { llvm_unreachable("Unsupported CmpExp type"); } result = new DImValue(e->type, eval); } ////////////////////////////////////////////////////////////////////////////// void visit(EqualExp *e) override { IF_LOG Logger::print("EqualExp::toElem: %s @ %s\n", e->toChars(), e->type->toChars()); LOG_SCOPE; auto &PGO = gIR->func()->pgo; PGO.setCurrentStmt(e); DValue *l = toElem(e->e1); DValue *r = toElem(e->e2); Type *t = e->e1->type->toBasetype(); LLValue *eval = nullptr; // 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 (e->op) { case TOKequal: cmpop = llvm::ICmpInst::ICMP_EQ; break; case TOKnotequal: cmpop = llvm::ICmpInst::ICMP_NE; break; default: llvm_unreachable("Unsupported integral type equality comparison."); } LLValue *lv = DtoRVal(l); LLValue *rv = DtoRVal(r); if (rv->getType() != lv->getType()) { rv = DtoBitCast(rv, lv->getType()); } IF_LOG { Logger::cout() << "lv: " << *lv << '\n'; Logger::cout() << "rv: " << *rv << '\n'; } eval = p->ir->CreateICmp(cmpop, lv, rv); } else if (t->isfloating()) // includes iscomplex { eval = DtoBinNumericEquals(e->loc, l, r, e->op); } else if (t->ty == Tsarray || t->ty == Tarray) { Logger::println("static or dynamic array"); eval = DtoArrayEquals(e->loc, e->op, l, r); } else if (t->ty == Taarray) { Logger::println("associative array"); eval = DtoAAEquals(e->loc, e->op, l, r); } else if (t->ty == Tdelegate) { Logger::println("delegate"); eval = DtoDelegateEquals(e->op, DtoRVal(l), DtoRVal(r)); } else if (t->ty == Tstruct) { Logger::println("struct"); // when this is reached it means there is no opEquals overload. eval = DtoStructEquals(e->op, l, r); } else { llvm_unreachable("Unsupported EqualExp type."); } result = new DImValue(e->type, eval); } ////////////////////////////////////////////////////////////////////////////// void visit(PostExp *e) override { IF_LOG Logger::print("PostExp::toElem: %s @ %s\n", e->toChars(), e->type->toChars()); LOG_SCOPE; auto &PGO = gIR->func()->pgo; PGO.setCurrentStmt(e); LLValue *const lval = DtoLVal(e->e1); toElem(e->e2); LLValue *val = DtoLoad(lval); LLValue *post = nullptr; Type *e1type = e->e1->type->toBasetype(); Type *e2type = e->e2->type->toBasetype(); if (e1type->isintegral()) { assert(e2type->isintegral()); LLValue *one = LLConstantInt::get(val->getType(), 1, !e2type->isunsigned()); if (e->op == TOKplusplus) { post = llvm::BinaryOperator::CreateAdd(val, one, "", p->scopebb()); } else if (e->op == TOKminusminus) { post = llvm::BinaryOperator::CreateSub(val, one, "", p->scopebb()); } } else if (e1type->ty == Tpointer) { assert(e->e2->op == TOKint64); LLConstant *offset = e->op == TOKplusplus ? DtoConstUint(1) : DtoConstInt(-1); post = DtoGEP1(val, offset, false, "", p->scopebb()); } else if (e1type->isfloating()) { assert(e2type->isfloating()); LLValue *one = DtoConstFP(e1type, ldouble(1.0)); if (e->op == TOKplusplus) { post = llvm::BinaryOperator::CreateFAdd(val, one, "", p->scopebb()); } else if (e->op == TOKminusminus) { post = llvm::BinaryOperator::CreateFSub(val, one, "", p->scopebb()); } } else { llvm_unreachable("Unsupported type for PostExp."); } DtoStore(post, lval); result = new DImValue(e->type, val); } ////////////////////////////////////////////////////////////////////////////// void visit(NewExp *e) override { IF_LOG Logger::print("NewExp::toElem: %s @ %s\n", e->toChars(), e->type->toChars()); LOG_SCOPE; auto &PGO = gIR->func()->pgo; PGO.setCurrentStmt(e); bool isArgprefixHandled = false; assert(e->newtype); Type *ntype = e->newtype->toBasetype(); // new class if (ntype->ty == Tclass) { Logger::println("new class"); result = DtoNewClass(e->loc, static_cast(ntype), e); isArgprefixHandled = true; // by DtoNewClass() } // new dynamic array else if (ntype->ty == Tarray) { IF_LOG Logger::println("new dynamic array: %s", e->newtype->toChars()); assert(e->argprefix == NULL); // get dim assert(e->arguments); assert(e->arguments->dim >= 1); if (e->arguments->dim == 1) { DValue *sz = toElem((*e->arguments)[0]); // allocate & init result = DtoNewDynArray(e->loc, e->newtype, sz, true); } else { size_t ndims = e->arguments->dim; std::vector dims; dims.reserve(ndims); for (size_t i = 0; i < ndims; ++i) { dims.push_back(toElem((*e->arguments)[i])); } result = DtoNewMulDimDynArray(e->loc, e->newtype, &dims[0], ndims); } } // new static array else if (ntype->ty == Tsarray) { llvm_unreachable("Static array new should decay to dynamic array."); } // new struct else if (ntype->ty == Tstruct) { IF_LOG Logger::println("new struct on heap: %s\n", e->newtype->toChars()); TypeStruct *ts = static_cast(ntype); // allocate LLValue *mem = nullptr; if (e->allocator) { // custom allocator DtoResolveFunction(e->allocator); DFuncValue dfn(e->allocator, getIrFunc(e->allocator)->func); DValue *res = DtoCallFunction(e->loc, nullptr, &dfn, e->newargs); mem = DtoBitCast(DtoRVal(res), DtoType(ntype->pointerTo()), ".newstruct_custom"); } else { // default allocator mem = DtoNewStruct(e->loc, ts); } if (!e->member && e->arguments) { IF_LOG Logger::println("Constructing using literal"); write_struct_literal(e->loc, mem, ts->sym, e->arguments); } else { // set nested context if (ts->sym->isNested() && ts->sym->vthis) { DtoResolveNestedContext(e->loc, ts->sym, mem); } // call constructor if (e->member) { // evaluate argprefix if (e->argprefix) { toElemDtor(e->argprefix); isArgprefixHandled = true; } IF_LOG Logger::println("Calling constructor"); assert(e->arguments != NULL); DtoResolveFunction(e->member); DFuncValue dfn(e->member, getIrFunc(e->member)->func, mem); DtoCallFunction(e->loc, ts, &dfn, e->arguments); } } result = new DImValue(e->type, mem); } // new basic type else { IF_LOG Logger::println("basic type on heap: %s\n", e->newtype->toChars()); assert(e->argprefix == NULL); // allocate LLValue *mem = DtoNew(e->loc, e->newtype); DLValue tmpvar(e->newtype, mem); Expression *exp = nullptr; if (!e->arguments || e->arguments->dim == 0) { IF_LOG Logger::println("default initializer\n"); // static arrays never appear here, so using the defaultInit is ok! exp = e->newtype->defaultInit(e->loc); } else { IF_LOG Logger::println("uniform constructor\n"); assert(e->arguments->dim == 1); exp = (*e->arguments)[0]; } DValue *iv = toElem(exp); DtoAssign(e->loc, &tmpvar, iv); // return as pointer-to result = new DImValue(e->type, mem); } assert(e->argprefix == NULL || isArgprefixHandled); } ////////////////////////////////////////////////////////////////////////////// void visit(DeleteExp *e) override { IF_LOG Logger::print("DeleteExp::toElem: %s @ %s\n", e->toChars(), e->type->toChars()); LOG_SCOPE; auto &PGO = gIR->func()->pgo; PGO.setCurrentStmt(e); DValue *dval = toElem(e->e1); Type *et = e->e1->type->toBasetype(); // pointer if (et->ty == Tpointer) { Type *elementType = et->nextOf()->toBasetype(); if (elementType->ty == Tstruct && elementType->needsDestruction()) { DtoDeleteStruct(e->loc, dval); } else { DtoDeleteMemory(e->loc, dval); } } // class else if (et->ty == Tclass) { bool onstack = false; TypeClass *tc = static_cast(et); if (tc->sym->isInterfaceDeclaration()) { DtoDeleteInterface(e->loc, dval); onstack = true; } else if (e->e1->op == TOKvar) { if (auto vd = static_cast(e->e1)->var->isVarDeclaration()) { if (vd->onstack) { DtoFinalizeClass(e->loc, DtoRVal(dval)); onstack = true; } } } if (!onstack) { DtoDeleteClass(e->loc, dval); // sets dval to null } else if (dval->isLVal()) { LLValue *lval = DtoLVal(dval); DtoStore(LLConstant::getNullValue(lval->getType()->getContainedType(0)), lval); } } // dyn array else if (et->ty == Tarray) { DtoDeleteArray(e->loc, dval); if (dval->isLVal()) { DtoSetArrayToNull(DtoLVal(dval)); } } // unknown/invalid else { llvm_unreachable("Unsupported DeleteExp target."); } } ////////////////////////////////////////////////////////////////////////////// void visit(ArrayLengthExp *e) override { IF_LOG Logger::print("ArrayLengthExp::toElem: %s @ %s\n", e->toChars(), e->type->toChars()); LOG_SCOPE; auto &PGO = gIR->func()->pgo; PGO.setCurrentStmt(e); DValue *u = toElem(e->e1); result = new DImValue(e->type, DtoArrayLen(u)); } ////////////////////////////////////////////////////////////////////////////// void visit(AssertExp *e) override { IF_LOG Logger::print("AssertExp::toElem: %s\n", e->toChars()); LOG_SCOPE; auto &PGO = gIR->func()->pgo; PGO.setCurrentStmt(e); // DMD allows syntax like this: // f() == 0 || assert(false) result = new DImValue(e->type, DtoConstBool(false)); if (!global.params.useAssert) { return; } // condition DValue *cond; Type *condty; // special case for dmd generated assert(this); when not in -release mode if (e->e1->op == TOKthis && static_cast(e->e1)->var == nullptr) { LLValue *thisarg = p->func()->thisArg; assert(thisarg && "null thisarg, but we're in assert(this) exp;"); LLValue *thisptr = DtoLoad(thisarg); condty = e->e1->type->toBasetype(); cond = new DImValue(condty, thisptr); } else { cond = toElem(e->e1); condty = e->e1->type->toBasetype(); } // create basic blocks llvm::BasicBlock *passedbb = llvm::BasicBlock::Create(gIR->context(), "assertPassed", p->topfunc()); llvm::BasicBlock *failedbb = llvm::BasicBlock::Create(gIR->context(), "assertFailed", p->topfunc()); // test condition LLValue *condval = DtoRVal(DtoCast(e->loc, cond, Type::tbool)); // branch llvm::BranchInst::Create(passedbb, failedbb, condval, p->scopebb()); // The branch does not need instrumentation for PGO because failedbb // terminates in unreachable, which means that LLVM will automatically // assign branch weights to this branch instruction. // failed: call assert runtime function p->scope() = IRScope(failedbb); /* DMD Bugzilla 8360: If the condition is evaluated to true, * msg is not evaluated at all. So should use toElemDtor() * instead of toElem(). */ DtoAssert(p->func()->decl->getModule(), e->loc, e->msg ? toElemDtor(e->msg) : nullptr); // passed: p->scope() = IRScope(passedbb); FuncDeclaration *invdecl; // class invariants if (global.params.useInvariants && condty->ty == Tclass && !(static_cast(condty)->sym->isInterfaceDeclaration()) && !(static_cast(condty)->sym->isCPPclass())) { Logger::println("calling class invariant"); llvm::Function *fn = getRuntimeFunction( e->loc, gIR->module, gABI->mangleFunctionForLLVM("_D9invariant12_d_invariantFC6ObjectZv", LINKd) .c_str()); LLValue *arg = DtoBitCast(DtoRVal(cond), 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) != nullptr) { Logger::print("calling struct invariant"); DtoResolveFunction(invdecl); DFuncValue invfunc(invdecl, getIrFunc(invdecl)->func, DtoRVal(cond)); DtoCallFunction(e->loc, nullptr, &invfunc, nullptr); } } ////////////////////////////////////////////////////////////////////////////// void visit(NotExp *e) override { IF_LOG Logger::print("NotExp::toElem: %s @ %s\n", e->toChars(), e->type->toChars()); LOG_SCOPE; auto &PGO = gIR->func()->pgo; PGO.setCurrentStmt(e); DValue *u = toElem(e->e1); LLValue *b = DtoRVal(DtoCast(e->loc, u, Type::tbool)); LLConstant *zero = DtoConstBool(false); b = p->ir->CreateICmpEQ(b, zero); result = new DImValue(e->type, b); } ////////////////////////////////////////////////////////////////////////////// void visit(AndAndExp *e) override { IF_LOG Logger::print("AndAndExp::toElem: %s @ %s\n", e->toChars(), e->type->toChars()); LOG_SCOPE; auto &PGO = gIR->func()->pgo; PGO.setCurrentStmt(e); DValue *u = toElem(e->e1); llvm::BasicBlock *andand = llvm::BasicBlock::Create(gIR->context(), "andand", gIR->topfunc()); llvm::BasicBlock *andandend = llvm::BasicBlock::Create(gIR->context(), "andandend", gIR->topfunc()); LLValue *ubool = DtoRVal(DtoCast(e->loc, u, Type::tbool)); llvm::BasicBlock *oldblock = p->scopebb(); auto truecount = PGO.getRegionCount(e); auto falsecount = PGO.getCurrentRegionCount() - truecount; auto branchweights = PGO.createProfileWeights(truecount, falsecount); p->ir->CreateCondBr(ubool, andand, andandend, branchweights); p->scope() = IRScope(andand); PGO.emitCounterIncrement(e); emitCoverageLinecountInc(e->e2->loc); DValue *v = toElemDtor(e->e2); LLValue *vbool = nullptr; if (v && !v->isFunc() && v->type != Type::tvoid) { vbool = DtoRVal(DtoCast(e->loc, v, Type::tbool)); } llvm::BasicBlock *newblock = p->scopebb(); llvm::BranchInst::Create(andandend, p->scopebb()); p->scope() = IRScope(andandend); LLValue *resval = nullptr; 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; } result = new DImValue(e->type, resval); } ////////////////////////////////////////////////////////////////////////////// void visit(OrOrExp *e) override { IF_LOG Logger::print("OrOrExp::toElem: %s @ %s\n", e->toChars(), e->type->toChars()); LOG_SCOPE; auto &PGO = gIR->func()->pgo; PGO.setCurrentStmt(e); DValue *u = toElem(e->e1); llvm::BasicBlock *oror = llvm::BasicBlock::Create(gIR->context(), "oror", gIR->topfunc()); llvm::BasicBlock *ororend = llvm::BasicBlock::Create(gIR->context(), "ororend", gIR->topfunc()); LLValue *ubool = DtoRVal(DtoCast(e->loc, u, Type::tbool)); llvm::BasicBlock *oldblock = p->scopebb(); auto falsecount = PGO.getRegionCount(e); auto truecount = PGO.getCurrentRegionCount() - falsecount; auto branchweights = PGO.createProfileWeights(truecount, falsecount); p->ir->CreateCondBr(ubool, ororend, oror, branchweights); p->scope() = IRScope(oror); PGO.emitCounterIncrement(e); emitCoverageLinecountInc(e->e2->loc); DValue *v = toElemDtor(e->e2); LLValue *vbool = nullptr; if (v && !v->isFunc() && v->type != Type::tvoid) { vbool = DtoRVal(DtoCast(e->loc, v, Type::tbool)); } llvm::BasicBlock *newblock = p->scopebb(); llvm::BranchInst::Create(ororend, p->scopebb()); p->scope() = IRScope(ororend); LLValue *resval = nullptr; 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; } result = new DImValue(e->type, resval); } //////////////////////////////////////////////////////////////////////////////// #define BIN_BLIT_EXP(X, Y) \ void visit(X##Exp *e) override { \ IF_LOG Logger::print("%sExp::toElem: %s @ %s\n", #X, e->toChars(), \ e->type->toChars()); \ LOG_SCOPE; \ DValue *u = toElem(e->e1); \ DValue *v = toElem(e->e2); \ errorOnIllegalArrayOp(e, e->e1, e->e2); \ v = DtoCast(e->loc, v, e->e1->type); \ LLValue *x = llvm::BinaryOperator::Create( \ llvm::Instruction::Y, DtoRVal(u), DtoRVal(v), "", p->scopebb()); \ result = new DImValue(e->type, x); \ } BIN_BLIT_EXP(And, And) BIN_BLIT_EXP(Or, Or) BIN_BLIT_EXP(Xor, Xor) BIN_BLIT_EXP(Shl, Shl) BIN_BLIT_EXP(Ushr, LShr) #undef BIN_BLIT_EXP void visit(ShrExp *e) override { IF_LOG Logger::print("ShrExp::toElem: %s @ %s\n", e->toChars(), e->type->toChars()); LOG_SCOPE; DValue *u = toElem(e->e1); DValue *v = toElem(e->e2); v = DtoCast(e->loc, v, e->e1->type); LLValue *x; if (isLLVMUnsigned(e->e1->type)) { x = p->ir->CreateLShr(DtoRVal(u), DtoRVal(v)); } else { x = p->ir->CreateAShr(DtoRVal(u), DtoRVal(v)); } result = new DImValue(e->type, x); } ////////////////////////////////////////////////////////////////////////////// void visit(HaltExp *e) override { IF_LOG Logger::print("HaltExp::toElem: %s\n", e->toChars()); LOG_SCOPE; #if LDC_LLVM_VER >= 307 p->ir->CreateCall(GET_INTRINSIC_DECL(trap), {}); #else p->ir->CreateCall(GET_INTRINSIC_DECL(trap), ""); #endif p->ir->CreateUnreachable(); // 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 *bb = llvm::BasicBlock::Create(gIR->context(), "afterhalt", p->topfunc()); p->scope() = IRScope(bb); } ////////////////////////////////////////////////////////////////////////////// void visit(DelegateExp *e) override { IF_LOG Logger::print("DelegateExp::toElem: %s @ %s\n", e->toChars(), e->type->toChars()); LOG_SCOPE; if (e->func->isStatic()) { e->error("can't take delegate of static function %s, it does not require " "a context ptr", e->func->toChars()); } LLPointerType *int8ptrty = getPtrToType(LLType::getInt8Ty(gIR->context())); assert(e->type->toBasetype()->ty == Tdelegate); LLType *dgty = DtoType(e->type); DValue *u = toElem(e->e1); LLValue *uval; if (DFuncValue *f = u->isFunc()) { assert(f->func); LLValue *contextptr = DtoNestedContext(e->loc, f->func); uval = DtoBitCast(contextptr, getVoidPtrType()); } else { uval = (DtoIsInMemoryOnly(u->type) ? DtoLVal(u) : DtoRVal(u)); } IF_LOG Logger::cout() << "context = " << *uval << '\n'; LLValue *castcontext = DtoBitCast(uval, int8ptrty); IF_LOG Logger::println("func: '%s'", e->func->toPrettyChars()); LLValue *castfptr; if (e->e1->op != TOKsuper && e->e1->op != TOKdottype && e->func->isVirtual() && !e->func->isFinalFunc()) { castfptr = DtoVirtualFunctionPointer(u, e->func, e->toChars()); } else if (e->func->isAbstract()) { llvm_unreachable("Delegate to abstract method not implemented."); } else if (e->func->toParent()->isInterfaceDeclaration()) { llvm_unreachable("Delegate to interface method not implemented."); } else { DtoResolveFunction(e->func); // We need to actually codegen the function here, as literals are not // added to the module member list. if (e->func->semanticRun == PASSsemantic3done) { Dsymbol *owner = e->func->toParent(); while (!owner->isTemplateInstance() && owner->toParent()) { owner = owner->toParent(); } if (owner->isTemplateInstance() || owner == p->dmodule) { Declaration_codegen(e->func, p); } } castfptr = getIrFunc(e->func)->func; } castfptr = DtoBitCast(castfptr, dgty->getContainedType(1)); result = new DImValue( e->type, DtoAggrPair(DtoType(e->type), castcontext, castfptr, ".dg")); } ////////////////////////////////////////////////////////////////////////////// void visit(IdentityExp *e) override { IF_LOG Logger::print("IdentityExp::toElem: %s @ %s\n", e->toChars(), e->type->toChars()); LOG_SCOPE; DValue *l = toElem(e->e1); DValue *r = toElem(e->e2); Type *t1 = e->e1->type->toBasetype(); // handle dynarray specially if (t1->ty == Tarray) { result = new DImValue(e->type, DtoDynArrayIs(e->op, l, r)); return; } // also structs if (t1->ty == Tstruct) { result = new DImValue(e->type, DtoStructEquals(e->op, l, r)); return; } // FIXME this stuff isn't pretty LLValue *eval = nullptr; if (t1->ty == Tdelegate) { LLValue *lv = DtoRVal(l); LLValue *rv = nullptr; if (!r->isNull()) { rv = DtoRVal(r); assert(lv->getType() == rv->getType()); } eval = DtoDelegateEquals(e->op, lv, rv); } else if (t1->isfloating()) // includes iscomplex { eval = DtoBinNumericEquals(e->loc, l, r, e->op); } else if (t1->ty == Tpointer || t1->ty == Tclass) { LLValue *lv = DtoRVal(l); LLValue *rv = DtoRVal(r); if (lv->getType() != rv->getType()) { if (r->isNull()) { rv = llvm::ConstantPointerNull::get(isaPointer(lv->getType())); } else { rv = DtoBitCast(rv, lv->getType()); } } eval = (e->op == TOKidentity) ? p->ir->CreateICmpEQ(lv, rv) : p->ir->CreateICmpNE(lv, rv); } else if (t1->ty == Tsarray) { LLValue *lptr = DtoLVal(l); LLValue *rptr = DtoLVal(r); assert(lptr->getType() == rptr->getType()); eval = (e->op == TOKidentity) ? p->ir->CreateICmpEQ(lptr, rptr) : p->ir->CreateICmpNE(lptr, rptr); } else { LLValue *lv = DtoRVal(l); LLValue *rv = DtoRVal(r); assert(lv->getType() == rv->getType()); eval = (e->op == TOKidentity) ? p->ir->CreateICmpEQ(lv, rv) : p->ir->CreateICmpNE(lv, rv); } result = new DImValue(e->type, eval); } ////////////////////////////////////////////////////////////////////////////// void visit(CommaExp *e) override { IF_LOG Logger::print("CommaExp::toElem: %s @ %s\n", e->toChars(), e->type->toChars()); LOG_SCOPE; if (e->cachedLvalue) { LLValue *V = e->cachedLvalue; result = new DLValue(e->type, V); return; } toElem(e->e1); result = toElem(e->e2); // Actually, we can get qualifier mismatches in the 2.064 frontend: // assert(e2->type == type); } ////////////////////////////////////////////////////////////////////////////// void visit(CondExp *e) override { IF_LOG Logger::print("CondExp::toElem: %s @ %s\n", e->toChars(), e->type->toChars()); LOG_SCOPE; auto &PGO = gIR->func()->pgo; PGO.setCurrentStmt(e); Type *dtype = e->type->toBasetype(); LLValue *retPtr = nullptr; if (dtype->ty != Tvoid) { // allocate a temporary for pointer to the final result. retPtr = DtoAlloca(dtype->pointerTo(), "condtmp"); } llvm::BasicBlock *condtrue = llvm::BasicBlock::Create(gIR->context(), "condtrue", gIR->topfunc()); llvm::BasicBlock *condfalse = llvm::BasicBlock::Create(gIR->context(), "condfalse", gIR->topfunc()); llvm::BasicBlock *condend = llvm::BasicBlock::Create(gIR->context(), "condend", gIR->topfunc()); DValue *c = toElem(e->econd); LLValue *cond_val = DtoRVal(DtoCast(e->loc, c, Type::tbool)); auto truecount = PGO.getRegionCount(e); auto falsecount = PGO.getCurrentRegionCount() - truecount; auto branchweights = PGO.createProfileWeights(truecount, falsecount); p->ir->CreateCondBr(cond_val, condtrue, condfalse, branchweights); p->scope() = IRScope(condtrue); PGO.emitCounterIncrement(e); DValue *u = toElem(e->e1); if (retPtr) { LLValue *lval = makeLValue(e->loc, u); DtoStore(lval, DtoBitCast(retPtr, lval->getType()->getPointerTo())); } llvm::BranchInst::Create(condend, p->scopebb()); p->scope() = IRScope(condfalse); DValue *v = toElem(e->e2); if (retPtr) { LLValue *lval = makeLValue(e->loc, v); DtoStore(lval, DtoBitCast(retPtr, lval->getType()->getPointerTo())); } llvm::BranchInst::Create(condend, p->scopebb()); p->scope() = IRScope(condend); if (retPtr) { result = new DSpecialRefValue(e->type, retPtr); } else { result = new DConstValue(e->type, getNullValue(DtoMemType(dtype))); } } ////////////////////////////////////////////////////////////////////////////// void visit(ComExp *e) override { IF_LOG Logger::print("ComExp::toElem: %s @ %s\n", e->toChars(), e->type->toChars()); LOG_SCOPE; LLValue *value = DtoRVal(e->e1); LLValue *minusone = LLConstantInt::get(value->getType(), static_cast(-1), true); value = llvm::BinaryOperator::Create(llvm::Instruction::Xor, value, minusone, "", p->scopebb()); result = new DImValue(e->type, value); } ////////////////////////////////////////////////////////////////////////////// void visit(NegExp *e) override { IF_LOG Logger::print("NegExp::toElem: %s @ %s\n", e->toChars(), e->type->toChars()); LOG_SCOPE; DRValue *dval = toElem(e->e1)->getRVal(); if (e->type->iscomplex()) { result = DtoComplexNeg(e->loc, e->type, dval); return; } LLValue *val = DtoRVal(dval); if (e->type->isintegral()) { val = p->ir->CreateNeg(val, "negval"); } else { val = p->ir->CreateFNeg(val, "negval"); } result = new DImValue(e->type, val); } ////////////////////////////////////////////////////////////////////////////// void visit(CatExp *e) override { IF_LOG Logger::print("CatExp::toElem: %s @ %s\n", e->toChars(), e->type->toChars()); LOG_SCOPE; result = DtoCatArrays(e->loc, e->type, e->e1, e->e2); } ////////////////////////////////////////////////////////////////////////////// void visit(CatAssignExp *e) override { IF_LOG Logger::print("CatAssignExp::toElem: %s @ %s\n", e->toChars(), e->type->toChars()); LOG_SCOPE; result = toElem(e->e1); Type *e1type = e->e1->type->toBasetype(); assert(e1type->ty == Tarray); Type *elemtype = e1type->nextOf()->toBasetype(); Type *e2type = e->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(e->loc, result, e->e2); } else { /*if (elemtype->ty == Twchar)*/ // append dchar to wchar[] DtoAppendDCharToUnicodeString(e->loc, result, e->e2); } } else if (e1type->equals(e2type)) { // append array DSliceValue *slice = DtoCatAssignArray(e->loc, result, e->e2); DtoAssign(e->loc, result, slice); } else { // append element DtoCatAssignElement(e->loc, e1type, result, e->e2); } } ////////////////////////////////////////////////////////////////////////////// void genFuncLiteral(FuncLiteralDeclaration *fd, FuncExp *e) { if ((fd->tok == TOKreserved || fd->tok == TOKdelegate) && (e && e->type->ty == Tpointer)) { // This is a lambda that was inferred to be a function literal instead // of a delegate, so set tok here in order to get correct types/mangling. // Horrible hack, but DMD does the same thing. fd->tok = TOKfunction; fd->vthis = nullptr; } if (fd->isNested()) { Logger::println("nested"); } Logger::println("kind = %s", fd->kind()); // We need to actually codegen the function here, as literals are not added // to the module member list. Declaration_codegen(fd, p); if (!isIrFuncCreated(fd)) { // See DtoDefineFunction for reasons why codegen was suppressed. // Instead just declare the function. DtoDeclareFunction(fd); assert(!fd->isNested()); } assert(getIrFunc(fd)->func); } ////////////////////////////////////////////////////////////////////////////// void visit(FuncExp *e) override { IF_LOG Logger::print("FuncExp::toElem: %s @ %s\n", e->toChars(), e->type->toChars()); LOG_SCOPE; FuncLiteralDeclaration *fd = e->fd; assert(fd); genFuncLiteral(fd, e); if (fd->isNested()) { LLType *dgty = DtoType(e->type); LLValue *cval; IrFunction *irfn = p->func(); if (irfn->nestedVar && fd->toParent2() == irfn->decl) { // We check fd->toParent2() because a frame allocated in one // function cannot be used for a delegate created in another // function. Happens with anonymous functions. cval = irfn->nestedVar; } else if (irfn->nestArg) { cval = DtoLoad(irfn->nestArg); } 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, getFieldGEPIndex(ad, ad->vthis), ".vthis")); } } else { cval = getNullPtr(getVoidPtrType()); } cval = DtoBitCast(cval, dgty->getContainedType(0)); LLValue *castfptr = DtoBitCast(getIrFunc(fd)->func, dgty->getContainedType(1)); result = new DImValue(e->type, DtoAggrPair(cval, castfptr, ".func")); } else { result = new DFuncValue(e->type, fd, getIrFunc(fd)->func); } } ////////////////////////////////////////////////////////////////////////////// void visit(ArrayLiteralExp *e) override { IF_LOG Logger::print("ArrayLiteralExp::toElem: %s @ %s\n", e->toChars(), e->type->toChars()); LOG_SCOPE; // D types Type *arrayType = e->type->toBasetype(); Type *elemType = arrayType->nextOf()->toBasetype(); // is dynamic ? bool const dyn = (arrayType->ty == Tarray); // length size_t const len = e->elements->dim; // llvm target type LLType *llType = DtoType(arrayType); IF_LOG 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 = DtoMemType(elemType); LLType *llStoType = LLArrayType::get(llElemType, len); IF_LOG 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... result = new DSliceValue(e->type, DtoConstSize_t(0), getNullPtr(getPtrToType(llElemType))); } else if (dyn) { if (arrayType->isImmutable() && isConstLiteral(e)) { llvm::Constant *init = arrayLiteralToConst(p, e); auto global = new llvm::GlobalVariable( gIR->module, init->getType(), true, llvm::GlobalValue::InternalLinkage, init, ".immutablearray"); result = new DSliceValue(arrayType, DtoConstSize_t(e->elements->dim), DtoBitCast(global, getPtrToType(llElemType))); } else { DSliceValue *dynSlice = DtoNewDynArray( e->loc, arrayType, new DConstValue(Type::tsize_t, DtoConstSize_t(len)), false); initializeArrayLiteral( p, e, DtoBitCast(dynSlice->getPtr(), getPtrToType(llStoType))); result = dynSlice; } } else { llvm::Value *storage = DtoRawAlloca(llStoType, DtoAlignment(e->type), "arrayliteral"); initializeArrayLiteral(p, e, storage); if (arrayType->ty == Tsarray) { result = new DLValue(e->type, storage); } else if (arrayType->ty == Tpointer) { storage = DtoBitCast(storage, llElemType->getPointerTo()); result = new DImValue(e->type, storage); } else { llvm_unreachable("Unexpected array literal type"); } } } ////////////////////////////////////////////////////////////////////////////// void visit(StructLiteralExp *e) override { IF_LOG Logger::print("StructLiteralExp::toElem: %s @ %s\n", e->toChars(), e->type->toChars()); LOG_SCOPE; if (e->useStaticInit) { DtoResolveStruct(e->sd); LLValue *initsym = getIrAggr(e->sd)->getInitSymbol(); initsym = DtoBitCast(initsym, DtoType(e->type->pointerTo())); result = new DLValue(e->type, initsym); return; } if (e->inProgressMemory) { result = new DLValue(e->type, e->inProgressMemory); return; } // make sure the struct is fully resolved DtoResolveStruct(e->sd); // alloca a stack slot e->inProgressMemory = DtoAlloca(e->type, ".structliteral"); // fill the allocated struct literal write_struct_literal(e->loc, e->inProgressMemory, e->sd, e->elements); // return as a var result = new DLValue(e->type, e->inProgressMemory); e->inProgressMemory = nullptr; } ////////////////////////////////////////////////////////////////////////////// void visit(ClassReferenceExp *e) override { IF_LOG Logger::print("ClassReferenceExp::toElem: %s @ %s\n", e->toChars(), e->type->toChars()); LOG_SCOPE; result = new DImValue(e->type, toConstElem(e, p)); } ////////////////////////////////////////////////////////////////////////////// void visit(InExp *e) override { IF_LOG Logger::print("InExp::toElem: %s @ %s\n", e->toChars(), e->type->toChars()); LOG_SCOPE; DValue *key = toElem(e->e1); DValue *aa = toElem(e->e2); result = DtoAAIn(e->loc, e->type, aa, key); } void visit(RemoveExp *e) override { IF_LOG Logger::print("RemoveExp::toElem: %s\n", e->toChars()); LOG_SCOPE; DValue *aa = toElem(e->e1); DValue *key = toElem(e->e2); result = DtoAARemove(e->loc, aa, key); } ////////////////////////////////////////////////////////////////////////////// /// Constructs an array initializer constant with the given constants as its /// elements. If the element types differ (unions, …), an anonymous struct /// literal is emitted (as for array constant initializers). llvm::Constant *arrayConst(std::vector &vals, Type *nominalElemType) { if (vals.size() == 0) { llvm::ArrayType *type = llvm::ArrayType::get(DtoType(nominalElemType), 0); return llvm::ConstantArray::get(type, vals); } llvm::Type *elementType = nullptr; bool differentTypes = false; for (auto v : vals) { if (!elementType) { elementType = v->getType(); } else { differentTypes |= (elementType != v->getType()); } } if (differentTypes) { return llvm::ConstantStruct::getAnon(vals, true); } llvm::ArrayType *t = llvm::ArrayType::get(elementType, vals.size()); return llvm::ConstantArray::get(t, vals); } void visit(AssocArrayLiteralExp *e) override { IF_LOG Logger::print("AssocArrayLiteralExp::toElem: %s @ %s\n", e->toChars(), e->type->toChars()); LOG_SCOPE; assert(e->keys); assert(e->values); assert(e->keys->dim == e->values->dim); Type *basetype = e->type->toBasetype(); Type *aatype = basetype; Type *vtype = aatype->nextOf(); if (!e->keys->dim) { goto LruntimeInit; } if (aatype->ty != Taarray) { // It's the AssociativeArray type. // Turn it back into a TypeAArray vtype = e->values->tdata()[0]->type; aatype = TypeAArray::create(vtype, e->keys->tdata()[0]->type); aatype = aatype->semantic(e->loc, nullptr); } { std::vector keysInits, valuesInits; keysInits.reserve(e->keys->dim); valuesInits.reserve(e->keys->dim); for (size_t i = 0, n = e->keys->dim; i < n; ++i) { Expression *ekey = e->keys->tdata()[i]; Expression *eval = e->values->tdata()[i]; IF_LOG Logger::println("(%llu) aa[%s] = %s", static_cast(i), ekey->toChars(), eval->toChars()); unsigned errors = global.startGagging(); LLConstant *ekeyConst = toConstElem(ekey, p); LLConstant *evalConst = toConstElem(eval, 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 = getRuntimeFunction(e->loc, gIR->module, "_d_assocarrayliteralTX"); LLFunctionType *funcTy = func->getFunctionType(); LLValue *aaTypeInfo = DtoBitCast(DtoTypeInfoOf(stripModifiers(aatype)), DtoType(Type::typeinfoassociativearray->type)); LLConstant *idxs[2] = {DtoConstUint(0), DtoConstUint(0)}; LLConstant *initval = arrayConst(keysInits, indexType); LLConstant *globalstore = new LLGlobalVariable( gIR->module, initval->getType(), false, LLGlobalValue::InternalLinkage, initval, ".aaKeysStorage"); #if LDC_LLVM_VER >= 307 LLConstant *slice = llvm::ConstantExpr::getGetElementPtr( isaPointer(globalstore)->getElementType(), globalstore, idxs, true); #else LLConstant *slice = llvm::ConstantExpr::getGetElementPtr(globalstore, idxs, true); #endif slice = DtoConstSlice(DtoConstSize_t(e->keys->dim), slice); LLValue *keysArray = DtoAggrPaint(slice, funcTy->getParamType(1)); initval = arrayConst(valuesInits, vtype); globalstore = new LLGlobalVariable(gIR->module, initval->getType(), false, LLGlobalValue::InternalLinkage, initval, ".aaValuesStorage"); #if LDC_LLVM_VER >= 307 slice = llvm::ConstantExpr::getGetElementPtr( isaPointer(globalstore)->getElementType(), globalstore, idxs, true); #else slice = llvm::ConstantExpr::getGetElementPtr(globalstore, idxs, true); #endif slice = DtoConstSlice(DtoConstSize_t(e->keys->dim), slice); LLValue *valuesArray = DtoAggrPaint(slice, funcTy->getParamType(2)); LLValue *aa = gIR->CreateCallOrInvoke(func, aaTypeInfo, keysArray, valuesArray, "aa") .getInstruction(); if (basetype->ty != Taarray) { LLValue *tmp = DtoAlloca(e->type, "aaliteral"); DtoStore(aa, DtoGEPi(tmp, 0, 0)); result = new DLValue(e->type, tmp); } else { result = new DImValue(e->type, aa); } return; } LruntimeInit: // it should be possible to avoid the temporary in some cases LLValue *tmp = DtoAllocaDump(LLConstant::getNullValue(DtoType(e->type)), e->type, "aaliteral"); result = new DLValue(e->type, tmp); const size_t n = e->keys->dim; for (size_t i = 0; i < n; ++i) { Expression *ekey = (*e->keys)[i]; Expression *eval = (*e->values)[i]; IF_LOG Logger::println("(%llu) aa[%s] = %s", static_cast(i), ekey->toChars(), eval->toChars()); // index DValue *key = toElem(ekey); DValue *mem = DtoAAIndex(e->loc, vtype, result, key, true); // store DValue *val = toElem(eval); DtoAssign(e->loc, mem, val); } } ////////////////////////////////////////////////////////////////////////////// DValue *toGEP(UnaExp *exp, unsigned index) { // (&a.foo).funcptr is a case where toElem(e1) is genuinely not an l-value. LLValue *val = makeLValue(exp->loc, toElem(exp->e1)); LLValue *v = DtoGEPi(val, 0, index); return new DLValue(exp->type, DtoBitCast(v, DtoPtrToType(exp->type))); } void visit(DelegatePtrExp *e) override { IF_LOG Logger::print("DelegatePtrExp::toElem: %s @ %s\n", e->toChars(), e->type->toChars()); LOG_SCOPE; result = toGEP(e, 0); } void visit(DelegateFuncptrExp *e) override { IF_LOG Logger::print("DelegateFuncptrExp::toElem: %s @ %s\n", e->toChars(), e->type->toChars()); LOG_SCOPE; result = toGEP(e, 1); } ////////////////////////////////////////////////////////////////////////////// void visit(BoolExp *e) override { IF_LOG Logger::print("BoolExp::toElem: %s @ %s\n", e->toChars(), e->type->toChars()); LOG_SCOPE; result = new DImValue( e->type, DtoRVal(DtoCast(e->loc, toElem(e->e1), Type::tbool))); } ////////////////////////////////////////////////////////////////////////////// void visit(DotTypeExp *e) override { IF_LOG Logger::print("DotTypeExp::toElem: %s @ %s\n", e->toChars(), e->type->toChars()); LOG_SCOPE; assert(e->sym->getType()); result = toElem(e->e1); } ////////////////////////////////////////////////////////////////////////////// void visit(TypeExp *e) override { e->error("type %s is not an expression", e->toChars()); // TODO: Improve error handling. DMD just returns some value here and hopes // some more sensible error messages will be triggered. fatal(); } ////////////////////////////////////////////////////////////////////////////// void visit(TupleExp *e) override { IF_LOG Logger::print("TupleExp::toElem() %s\n", e->toChars()); LOG_SCOPE; // If there are any side effects, evaluate them first. if (e->e0) { toElem(e->e0); } std::vector types; types.reserve(e->exps->dim); for (size_t i = 0; i < e->exps->dim; i++) { types.push_back(DtoMemType((*e->exps)[i]->type)); } LLValue *val = DtoRawAlloca(LLStructType::get(gIR->context(), types), 0, ".tuple"); for (size_t i = 0; i < e->exps->dim; i++) { Expression *el = (*e->exps)[i]; DValue *ep = toElem(el); LLValue *gep = DtoGEPi(val, 0, i); if (DtoIsInMemoryOnly(el->type)) { DtoMemCpy(gep, DtoLVal(ep)); } else if (el->type->ty != Tvoid) { DtoStoreZextI8(DtoRVal(ep), gep); } else { DtoStore(LLConstantInt::get(LLType::getInt8Ty(p->context()), 0, false), gep); } } result = new DLValue(e->type, val); } ////////////////////////////////////////////////////////////////////////////// void visit(VectorExp *e) override { IF_LOG Logger::print("VectorExp::toElem() %s\n", e->toChars()); LOG_SCOPE; TypeVector *type = static_cast(e->to->toBasetype()); assert(e->type->ty == Tvector); LLValue *vector = DtoAlloca(e->to); // Array literals are assigned element-wise, other expressions are cast and // splat across the vector elements. This is what DMD does. if (e->e1->op == TOKarrayliteral) { Logger::println("array literal expression"); ArrayLiteralExp *lit = static_cast(e->e1); assert(lit->elements->dim == e->dim && "Array literal vector initializer " "length mismatch, should have been handled in frontend."); for (unsigned int i = 0; i < e->dim; ++i) { DValue *val = toElem(indexArrayLiteral(lit, i)); LLValue *llval = DtoRVal(DtoCast(e->loc, val, type->elementType())); DtoStore(llval, DtoGEPi(vector, 0, i)); } } else { Logger::println("normal (splat) expression"); DValue *val = toElem(e->e1); LLValue *llval = DtoRVal(DtoCast(e->loc, val, type->elementType())); for (unsigned int i = 0; i < e->dim; ++i) { DtoStore(llval, DtoGEPi(vector, 0, i)); } } result = new DLValue(e->to, vector); } ////////////////////////////////////////////////////////////////////////////// void visit(PowExp *e) override { IF_LOG Logger::print("PowExp::toElem() %s\n", e->toChars()); LOG_SCOPE; e->error("must import std.math to use ^^ operator"); result = new DNullValue(e->type, llvm::UndefValue::get(DtoType(e->type))); } ////////////////////////////////////////////////////////////////////////////// void visit(TypeidExp *e) override { if (Type *t = isType(e->obj)) { result = DtoSymbolAddress(e->loc, e->type, getOrCreateTypeInfoDeclaration(t, nullptr)); return; } if (Expression *ex = isExpression(e->obj)) { Type *t = ex->type->toBasetype(); assert(t->ty == Tclass); LLValue *val = DtoRVal(ex); // Get and load vtbl pointer. llvm::Value *vtbl = DtoLoad(DtoGEPi(val, 0, 0)); // TypeInfo ptr is first vtbl entry. llvm::Value *typinf = DtoGEPi(vtbl, 0, 0); Type *resultType = Type::typeinfoclass->type; if (static_cast(t)->sym->isInterfaceDeclaration()) { // For interfaces, the first entry in the vtbl is actually a pointer // to an Interface instance, which has the type info as its first // member, so we have to add an extra layer of indirection. resultType = Type::typeinfointerface->type; typinf = DtoLoad( DtoBitCast(typinf, DtoType(resultType->pointerTo()->pointerTo()))); } result = new DLValue(resultType, typinf); return; } llvm_unreachable("Unknown TypeidExp argument kind"); } //////////////////////////////////////////////////////////////////////////////// #define STUB(x) \ void visit(x *e) override { \ e->error("Internal compiler error: Type " #x " not implemented: %s", \ e->toChars()); \ fatal(); \ } STUB(Expression) STUB(ScopeExp) STUB(SymbolExp) STUB(PowAssignExp) #undef STUB }; //////////////////////////////////////////////////////////////////////////////// DValue *toElem(Expression *e) { ToElemVisitor v(gIR, false); e->accept(&v); return v.getResult(); } DValue *toElemDtor(Expression *e) { ToElemVisitor v(gIR, true); e->accept(&v); return v.getResult(); } // FIXME: Implement & place in right module Symbol *toModuleAssert(Module *m) { return nullptr; } // FIXME: Implement & place in right module Symbol *toModuleUnittest(Module *m) { return nullptr; } // FIXME: Implement & place in right module Symbol *toModuleArray(Module *m) { return nullptr; }