std::string getIRMangledModuleInfoSymbolName(Module *module) { OutBuffer mangledName; mangledName.writestring("_D"); mangleToBuffer(module, &mangledName); mangledName.writestring("12__ModuleInfoZ"); return getIRMangledVarName(mangledName.peekString(), LINKd); }
std::string getIRMangledInterfaceInfosSymbolName(ClassDeclaration *cd) { OutBuffer mangledName; mangledName.writestring("_D"); mangleToBuffer(cd, &mangledName); mangledName.writestring("16__interfaceInfosZ"); return getIRMangledVarName(mangledName.peekString(), LINKd); }
std::string getIRMangledName(VarDeclaration *vd) { OutBuffer mangleBuf; mangleToBuffer(vd, &mangleBuf); // TODO: is hashing of variable names necessary? // TODO: Cache the result? return getIRMangledVarName(mangleBuf.peekString(), vd->linkage); }
/** * Replaces <<func>> with the name of the currently codegen'd function. * * This kludge is required to handle labels correctly, as the instruction * strings for jumps, … are generated during semantic3, but attribute inference * might change the function type (and hence the mangled name) right at the end * of semantic3. */ static void replace_func_name(IRState *p, std::string &insnt) { static const std::string needle("<<func>>"); OutBuffer mangleBuf; mangleToBuffer(p->func()->decl, &mangleBuf); size_t pos; while (std::string::npos != (pos = insnt.find(needle))) { // This will only happen for few instructions, and only once for those. insnt.replace(pos, needle.size(), mangleBuf.peekString()); } }
void CompoundAsmStatement_toIR(CompoundAsmStatement *stmt, IRState *p) { IF_LOG Logger::println("CompoundAsmStatement::toIR(): %s", stmt->loc.toChars()); LOG_SCOPE; // disable inlining by default if (!p->func()->decl->allowInlining) { p->func()->setNeverInline(); } // create asm block structure assert(!p->asmBlock); auto asmblock = new IRAsmBlock(stmt); assert(asmblock); p->asmBlock = asmblock; // do asm statements for (unsigned i = 0; i < stmt->statements->dim; i++) { if (Statement *s = (*stmt->statements)[i]) { Statement_toIR(s, p); } } // build forwarder for in-asm branches to external labels // this additional asm code sets the __llvm_jump_target variable // to a unique value that will identify the jump target in // a post-asm switch // maps each goto destination to its special value std::map<LabelDsymbol *, int> gotoToVal; // location of the special value determining the goto label // will be set if post-asm dispatcher block is needed LLValue *jump_target = nullptr; { FuncDeclaration *fd = gIR->func()->decl; OutBuffer mangleBuf; mangleToBuffer(fd, &mangleBuf); const char *fdmangle = mangleBuf.peekString(); // we use a simple static counter to make sure the new end labels are unique static size_t uniqueLabelsId = 0; std::ostringstream asmGotoEndLabel; printLabelName(asmGotoEndLabel, fdmangle, "_llvm_asm_end"); asmGotoEndLabel << uniqueLabelsId++; // initialize the setter statement we're going to build auto outSetterStmt = new IRAsmStmt; std::string asmGotoEnd = "\n\tjmp " + asmGotoEndLabel.str() + "\n"; std::ostringstream code; code << asmGotoEnd; int n_goto = 1; size_t n = asmblock->s.size(); for (size_t i = 0; i < n; ++i) { IRAsmStmt *a = asmblock->s[i]; // skip non-branch statements if (!a->isBranchToLabel) { continue; } // if internal, no special handling is necessary, skip std::vector<Identifier *>::const_iterator it, end; end = asmblock->internalLabels.end(); bool skip = false; for (auto il : asmblock->internalLabels) { if (il->equals(a->isBranchToLabel->ident)) { skip = true; } } if (skip) { continue; } // if we already set things up for this branch target, skip if (gotoToVal.find(a->isBranchToLabel) != gotoToVal.end()) { continue; } // record that the jump needs to be handled in the post-asm dispatcher gotoToVal[a->isBranchToLabel] = n_goto; // provide an in-asm target for the branch and set value IF_LOG Logger::println( "statement '%s' references outer label '%s': creating forwarder", a->code.c_str(), a->isBranchToLabel->ident->toChars()); printLabelName(code, fdmangle, a->isBranchToLabel->ident->toChars()); code << ":\n\t"; code << "movl $<<in" << n_goto << ">>, $<<out0>>\n"; // FIXME: Store the value -> label mapping somewhere, so it can be // referenced later outSetterStmt->in.push_back(DtoConstUint(n_goto)); outSetterStmt->in_c += "i,"; code << asmGotoEnd; ++n_goto; } if (code.str() != asmGotoEnd) { // finalize code outSetterStmt->code = code.str(); outSetterStmt->code += asmGotoEndLabel.str() + ":\n"; // create storage for and initialize the temporary jump_target = DtoAllocaDump(DtoConstUint(0), 0, "__llvm_jump_target"); // setup variable for output from asm outSetterStmt->out_c = "=*m,"; outSetterStmt->out.push_back(jump_target); asmblock->s.push_back(outSetterStmt); } else { delete outSetterStmt; } } // build a fall-off-end-properly asm statement FuncDeclaration *thisfunc = p->func()->decl; bool useabiret = false; p->asmBlock->asmBlock->abiret = nullptr; if (thisfunc->fbody->endsWithAsm() == stmt && thisfunc->type->nextOf()->ty != Tvoid) { // there can't be goto forwarders in this case assert(gotoToVal.empty()); emitABIReturnAsmStmt(asmblock, stmt->loc, thisfunc); useabiret = true; } // build asm block std::vector<LLValue *> outargs; std::vector<LLValue *> inargs; std::vector<LLType *> outtypes; std::vector<LLType *> intypes; std::string out_c; std::string in_c; std::string clobbers; std::string code; size_t asmIdx = asmblock->retn; Logger::println("do outputs"); size_t n = asmblock->s.size(); for (size_t i = 0; i < n; ++i) { IRAsmStmt *a = asmblock->s[i]; assert(a); size_t onn = a->out.size(); for (size_t j = 0; j < onn; ++j) { outargs.push_back(a->out[j]); outtypes.push_back(a->out[j]->getType()); } if (!a->out_c.empty()) { out_c += a->out_c; } remap_outargs(a->code, onn + a->in.size(), asmIdx); asmIdx += onn; } Logger::println("do inputs"); for (size_t i = 0; i < n; ++i) { IRAsmStmt *a = asmblock->s[i]; assert(a); size_t inn = a->in.size(); for (size_t j = 0; j < inn; ++j) { inargs.push_back(a->in[j]); intypes.push_back(a->in[j]->getType()); } if (!a->in_c.empty()) { in_c += a->in_c; } remap_inargs(a->code, inn + a->out.size(), asmIdx); asmIdx += inn; if (!code.empty()) { code += "\n\t"; } code += a->code; } asmblock->s.clear(); // append inputs out_c += in_c; // append clobbers for (const auto &c : asmblock->clobs) { out_c += c; } // remove excessive comma if (!out_c.empty()) { out_c.resize(out_c.size() - 1); } IF_LOG { Logger::println("code = \"%s\"", code.c_str()); Logger::println("constraints = \"%s\"", out_c.c_str()); } // build return types LLType *retty; if (asmblock->retn) { retty = asmblock->retty; } else { retty = llvm::Type::getVoidTy(gIR->context()); } // build argument types std::vector<LLType *> types; types.insert(types.end(), outtypes.begin(), outtypes.end()); types.insert(types.end(), intypes.begin(), intypes.end()); llvm::FunctionType *fty = llvm::FunctionType::get(retty, types, false); IF_LOG Logger::cout() << "function type = " << *fty << '\n'; std::vector<LLValue *> args; args.insert(args.end(), outargs.begin(), outargs.end()); args.insert(args.end(), inargs.begin(), inargs.end()); IF_LOG { Logger::cout() << "Arguments:" << '\n'; Logger::indent(); size_t i = 0; for (auto arg : args) { Stream cout = Logger::cout(); cout << '$' << i << " ==> " << *arg; if (!llvm::isa<llvm::Instruction>(arg) && !llvm::isa<LLGlobalValue>(arg)) { cout << '\n'; } ++i; } Logger::undent(); } llvm::InlineAsm *ia = llvm::InlineAsm::get(fty, code, out_c, true); llvm::CallInst *call = p->ir->CreateCall( ia, args, retty == LLType::getVoidTy(gIR->context()) ? "" : "asm"); IF_LOG Logger::cout() << "Complete asm statement: " << *call << '\n'; // capture abi return value if (useabiret) { IRAsmBlock *block = p->asmBlock; if (block->retfixup) { block->asmBlock->abiret = (*block->retfixup)(p->ir, call); } else if (p->asmBlock->retemu) { block->asmBlock->abiret = DtoLoad(block->asmBlock->abiret); } else { block->asmBlock->abiret = call; } } p->asmBlock = nullptr; // if asm contained external branches, emit goto forwarder code if (!gotoToVal.empty()) { assert(jump_target); // make new blocks llvm::BasicBlock *bb = p->insertBB("afterasmgotoforwarder"); llvm::LoadInst *val = p->ir->CreateLoad(jump_target, "__llvm_jump_target_value"); llvm::SwitchInst *sw = p->ir->CreateSwitch(val, bb, gotoToVal.size()); // add all cases for (const auto &pair : gotoToVal) { llvm::BasicBlock *casebb = p->insertBBBefore(bb, "case"); sw->addCase(LLConstantInt::get(llvm::IntegerType::get(gIR->context(), 32), pair.second), casebb); p->scope() = IRScope(casebb); DtoGoto(stmt->loc, pair.first); } p->scope() = IRScope(bb); } }
void TryCatchScope::emitCatchBodies(IRState &irs, llvm::Value *ehPtrSlot) { assert(catchBlocks.empty()); auto &PGO = irs.funcGen().pgo; const auto entryCount = PGO.setCurrentStmt(stmt); struct CBPrototype { Type *t; llvm::BasicBlock *catchBB; uint64_t catchCount; uint64_t uncaughtCount; }; llvm::SmallVector<CBPrototype, 8> cbPrototypes; cbPrototypes.reserve(stmt->catches->dim); for (auto c : *stmt->catches) { auto catchBB = irs.insertBBBefore(endbb, llvm::Twine("catch.") + c->type->toChars()); irs.scope() = IRScope(catchBB); irs.DBuilder.EmitBlockStart(c->loc); PGO.emitCounterIncrement(c); bool isCPPclass = false; if (auto lp = c->langPlugin()) // CALYPSO lp->codegen()->toBeginCatch(irs, c); else { const auto cd = c->type->toBasetype()->isClassHandle(); isCPPclass = cd->isCPPclass(); const auto enterCatchFn = getRuntimeFunction( Loc(), irs.module, isCPPclass ? "__cxa_begin_catch" : "_d_eh_enter_catch"); const auto ptr = DtoLoad(ehPtrSlot); const auto throwableObj = irs.ir->CreateCall(enterCatchFn, ptr); // For catches that use the Throwable object, create storage for it. // We will set it in the code that branches from the landing pads // (there might be more than one) to catchBB. if (c->var) { // This will alloca if we haven't already and take care of nested refs // if there are any. DtoDeclarationExp(c->var); // Copy the exception reference over from the _d_eh_enter_catch return // value. DtoStore(DtoBitCast(throwableObj, DtoType(c->var->type)), getIrLocal(c->var)->value); } } // Emit handler, if there is one. The handler is zero, for instance, // when building 'catch { debug foo(); }' in non-debug mode. if (isCPPclass) { // from DMD: /* C++ catches need to end with call to __cxa_end_catch(). * Create: * try { handler } finally { __cxa_end_catch(); } * Note that this is worst case code because it always sets up an * exception handler. At some point should try to do better. */ FuncDeclaration *fdend = FuncDeclaration::genCfunc(nullptr, Type::tvoid, "__cxa_end_catch"); Expression *efunc = VarExp::create(Loc(), fdend); Expression *ecall = CallExp::create(Loc(), efunc); ecall->type = Type::tvoid; Statement *call = ExpStatement::create(Loc(), ecall); Statement *stmt = c->handler ? TryFinallyStatement::create(Loc(), c->handler, call) : call; Statement_toIR(stmt, &irs); } else { if (c->handler) Statement_toIR(c->handler, &irs); } if (!irs.scopereturned()) { // CALYPSO FIXME: _cxa_end_catch won't be called if it has already returned if (auto lp = c->langPlugin()) lp->codegen()->toEndCatch(irs, c); irs.ir->CreateBr(endbb); } irs.DBuilder.EmitBlockEnd(); // PGO information, currently unused auto catchCount = PGO.getRegionCount(c); // uncaughtCount is handled in a separate pass below cbPrototypes.push_back({c->type->toBasetype(), catchBB, catchCount, 0}); // CALYPSO } // Total number of uncaught exceptions is equal to the execution count at // the start of the try block minus the one after the continuation. // uncaughtCount keeps track of the exception type mismatch count while // iterating through the catch block prototypes in reversed order. auto uncaughtCount = entryCount - PGO.getRegionCount(stmt); for (auto it = cbPrototypes.rbegin(), end = cbPrototypes.rend(); it != end; ++it) { it->uncaughtCount = uncaughtCount; // Add this catch block's match count to the uncaughtCount, because these // failed to match the remaining (lexically preceding) catch blocks. uncaughtCount += it->catchCount; } catchBlocks.reserve(stmt->catches->dim); auto c_it = stmt->catches->begin(); // CALYPSO for (const auto &p : cbPrototypes) { auto branchWeights = PGO.createProfileWeights(p.catchCount, p.uncaughtCount); LLGlobalVariable *ci; if (auto lp = (*c_it)->langPlugin()) // CALYPSO ci = lp->codegen()->toCatchScopeType(irs, p.t); else { ClassDeclaration *cd = p.t->isClassHandle(); DtoResolveClass(cd); if (cd->isCPPclass()) { const char *name = Target::cppTypeInfoMangle(cd); auto cpp_ti = getOrCreateGlobal( cd->loc, irs.module, getVoidPtrType(), /*isConstant=*/true, LLGlobalValue::ExternalLinkage, /*init=*/nullptr, name); // Wrap std::type_info pointers inside a __cpp_type_info_ptr class instance so that // the personality routine may differentiate C++ catch clauses from D ones. OutBuffer mangleBuf; mangleBuf.writestring("_D"); mangleToBuffer(cd, &mangleBuf); mangleBuf.printf("%d%s", 18, "_cpp_type_info_ptr"); const auto wrapperMangle = getIRMangledVarName(mangleBuf.peekString(), LINKd); RTTIBuilder b(ClassDeclaration::cpp_type_info_ptr); b.push(cpp_ti); auto wrapperType = llvm::cast<llvm::StructType>( static_cast<IrTypeClass*>(ClassDeclaration::cpp_type_info_ptr->type->ctype)->getMemoryLLType()); auto wrapperInit = b.get_constant(wrapperType); ci = getOrCreateGlobal( cd->loc, irs.module, wrapperType, /*isConstant=*/true, LLGlobalValue::LinkOnceODRLinkage, wrapperInit, wrapperMangle); } else { ci = getIrAggr(cd)->getClassInfoSymbol(); } } catchBlocks.push_back({ci, p.catchBB, branchWeights}); c_it++; } }