void AsmBlockStatement::toIR(IRState* p) { Logger::println("AsmBlockStatement::toIR(): %s", loc.toChars()); LOG_SCOPE; // disable inlining by default if (!p->func()->decl->allowInlining) p->func()->setNeverInline(); // create asm block structure assert(!p->asmBlock); IRAsmBlock* asmblock = new IRAsmBlock(this); assert(asmblock); p->asmBlock = asmblock; // do asm statements for (unsigned i=0; i<statements->dim; i++) { Statement* s = static_cast<Statement*>(statements->data[i]); if (s) { s->toIR(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<Identifier*, int> gotoToVal; // location of the special value determining the goto label // will be set if post-asm dispatcher block is needed llvm::AllocaInst* jump_target = 0; { FuncDeclaration* fd = gIR->func()->decl; const char* fdmangle = fd->mangle(); // 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 IRAsmStmt* 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(it = asmblock->internalLabels.begin(); it != end; ++it) if((*it)->equals(a->isBranchToLabel)) 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 Logger::println("statement '%s' references outer label '%s': creating forwarder", a->code.c_str(), a->isBranchToLabel->string); printLabelName(code, fdmangle, a->isBranchToLabel->string); 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 = DtoAlloca(Type::tint32, "__llvm_jump_target"); gIR->ir->CreateStore(DtoConstUint(0), 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 = NULL; if (thisfunc->fbody->endsWithAsm() == this && thisfunc->type->nextOf()->ty != Tvoid) { // there can't be goto forwarders in this case assert(gotoToVal.empty()); emitABIReturnAsmStmt(asmblock, 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 typedef std::set<std::string>::iterator clobs_it; for (clobs_it i=asmblock->clobs.begin(); i!=asmblock->clobs.end(); ++i) { out_c += *i; } // remove excessive comma if (!out_c.empty()) out_c.resize(out_c.size()-1); 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 (Logger::enabled()) 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 (Logger::enabled()) { Logger::cout() << "Arguments:" << '\n'; Logger::indent(); for (std::vector<LLValue*>::iterator b = args.begin(), i = b, e = args.end(); i != e; ++i) { Stream cout = Logger::cout(); cout << '$' << (i - b) << " ==> " << **i; if (!llvm::isa<llvm::Instruction>(*i) && !llvm::isa<LLGlobalValue>(*i)) cout << '\n'; } 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 (Logger::enabled()) 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 = NULL; // if asm contained external branches, emit goto forwarder code if(!gotoToVal.empty()) { assert(jump_target); // make new blocks llvm::BasicBlock* oldend = gIR->scopeend(); llvm::BasicBlock* bb = llvm::BasicBlock::Create(gIR->context(), "afterasmgotoforwarder", p->topfunc(), oldend); 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 std::map<Identifier*, int>::iterator it, end = gotoToVal.end(); for(it = gotoToVal.begin(); it != end; ++it) { llvm::BasicBlock* casebb = llvm::BasicBlock::Create(gIR->context(), "case", p->topfunc(), bb); sw->addCase(LLConstantInt::get(llvm::IntegerType::get(gIR->context(), 32), it->second), casebb); p->scope() = IRScope(casebb,bb); DtoGoto(loc, it->first, enclosingFinally); } p->scope() = IRScope(bb,oldend); } }
llvm::GlobalVariable * IrAggr::getInterfaceVtbl(BaseClass * b, bool new_instance, size_t interfaces_index) { ClassGlobalMap::iterator it = interfaceVtblMap.find(b->base); if (it != interfaceVtblMap.end()) return it->second; IF_LOG Logger::println("Building vtbl for implementation of interface %s in class %s", b->base->toPrettyChars(), aggrdecl->toPrettyChars()); LOG_SCOPE; ClassDeclaration* cd = aggrdecl->isClassDeclaration(); assert(cd && "not a class aggregate"); FuncDeclarations vtbl_array; b->fillVtbl(cd, &vtbl_array, new_instance); std::vector<llvm::Constant*> constants; constants.reserve(vtbl_array.dim); if (!b->base->isCPPinterface()) { // skip interface info for CPP interfaces // start with the interface info VarDeclarationIter interfaces_idx(ClassDeclaration::classinfo->fields, 3); // index into the interfaces array llvm::Constant* idxs[2] = { DtoConstSize_t(0), DtoConstSize_t(interfaces_index) }; llvm::Constant* c = llvm::ConstantExpr::getGetElementPtr( getInterfaceArraySymbol(), idxs, true); constants.push_back(c); } // add virtual function pointers size_t n = vtbl_array.dim; for (size_t i = b->base->vtblOffset(); i < n; i++) { Dsymbol* dsym = static_cast<Dsymbol*>(vtbl_array.data[i]); if (dsym == NULL) { // FIXME // why is this null? // happens for mini/s.d constants.push_back(getNullValue(getVoidPtrType())); continue; } FuncDeclaration* fd = dsym->isFuncDeclaration(); assert(fd && "vtbl entry not a function"); assert((!fd->isAbstract() || fd->fbody) && "null symbol in interface implementation vtable"); fd->codegen(Type::sir); assert(fd->ir.irFunc && "invalid vtbl function"); LLFunction *fn = fd->ir.irFunc->func; // If the base is a cpp interface, 'this' parameter is a pointer to // the interface not the underlying object as expected. Instead of // the function, we place into the vtable a small wrapper, called thunk, // that casts 'this' to the object and then pass it to the real function. if (b->base->isCPPinterface()) { TypeFunction *f = (TypeFunction*)fd->type->toBasetype(); assert(f->fty.arg_this); // create the thunk function OutBuffer name; name.writestring("Th"); name.printf("%i", b->offset); name.writestring(fd->mangle()); LLFunction *thunk = LLFunction::Create(isaFunction(fn->getType()->getContainedType(0)), DtoLinkage(fd), name.toChars(), gIR->module); // create entry and end blocks llvm::BasicBlock* beginbb = llvm::BasicBlock::Create(gIR->context(), "entry", thunk); llvm::BasicBlock* endbb = llvm::BasicBlock::Create(gIR->context(), "endentry", thunk); gIR->scopes.push_back(IRScope(beginbb, endbb)); // copy the function parameters, so later we can pass them to the real function std::vector<LLValue*> args; llvm::Function::arg_iterator iarg = thunk->arg_begin(); for (; iarg != thunk->arg_end(); ++iarg) args.push_back(iarg); // cast 'this' to Object LLValue* &thisArg = args[(f->fty.arg_sret == 0) ? 0 : 1]; LLType* thisType = thisArg->getType(); thisArg = DtoBitCast(thisArg, getVoidPtrType()); thisArg = DtoGEP1(thisArg, DtoConstInt(-b->offset)); thisArg = DtoBitCast(thisArg, thisType); // call the real vtbl function. LLValue *retVal = gIR->ir->CreateCall(fn, args); // return from the thunk if (thunk->getReturnType() == LLType::getVoidTy(gIR->context())) llvm::ReturnInst::Create(gIR->context(), beginbb); else llvm::ReturnInst::Create(gIR->context(), retVal, beginbb); // clean up gIR->scopes.pop_back(); thunk->getBasicBlockList().pop_back(); fn = thunk; } constants.push_back(fn); } // build the vtbl constant llvm::Constant* vtbl_constant = LLConstantStruct::getAnon(gIR->context(), constants, false); // create the global variable to hold it llvm::GlobalValue::LinkageTypes _linkage = DtoExternalLinkage(aggrdecl); std::string mangle("_D"); mangle.append(cd->mangle()); mangle.append("11__interface"); mangle.append(b->base->mangle()); mangle.append("6__vtblZ"); llvm::GlobalVariable* GV = getOrCreateGlobal(cd->loc, *gIR->module, vtbl_constant->getType(), true, _linkage, vtbl_constant, mangle ); // insert into the vtbl map interfaceVtblMap.insert(std::make_pair(b->base, GV)); return GV; }