void LabelStatement::toNakedIR(IRState *p) { Logger::println("LabelStatement::toNakedIR(): %s", loc.toChars()); LOG_SCOPE; printLabelName(p->nakedAsm, p->func()->decl->mangle(), ident->toChars()); p->nakedAsm << ":"; if (statement) statement->toNakedIR(p); }
void AsmBlockStatement_toIR(AsmBlockStatement *stmt, IRState* p) { IF_LOG Logger::println("AsmBlockStatement::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); IRAsmBlock* asmblock = new IRAsmBlock(stmt); assert(asmblock); p->asmBlock = asmblock; // do asm statements for (unsigned i=0; i < stmt->statements->dim; i++) { Statement* s = static_cast<Statement*>(stmt->statements->data[i]); if (s) { 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 llvm::AllocaInst* jump_target = 0; { FuncDeclaration* fd = gIR->func()->decl; const char* fdmangle = mangle(fd); // 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->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->string); printLabelName(code, fdmangle, a->isBranchToLabel->ident->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() == 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 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); 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(); 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_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 = 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<LabelDsymbol*, 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(stmt->loc, it->first, stmt->enclosingFinally); } p->scope() = IRScope(bb,oldend); } }