void vaCopy(LLValue* pDest, LLValue* src) { // Analog to va_start, we need to allocate a new __va_list struct on the stack, // fill it with a bitcopy of the source struct... src = DtoLoad(DtoBitCast(src, getValistType()->getPointerTo())); // *(__va_list*)src LLValue* valistmem = DtoAllocaDump(src, 0, "__va_list_mem"); // ... and finally set the passed 'dest' char* pointer to the new struct's address. DtoStore(DtoBitCast(valistmem, getVoidPtrType()), DtoBitCast(pDest, getPtrToType(getVoidPtrType()))); }
LLValue *get(Type *dty, LLValue *v) override { LLValue *address = DtoAllocaDump(v, dty, ".X86_64_C_struct_rewrite_dump"); LLType *type = DtoType(dty); return loadFromMemory(address, type, ".X86_64_C_struct_rewrite_getResult"); }
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); } }
LLValue *getLVal(Type *dty, LLValue *v) override { return DtoAllocaDump(v, dty, ".X86_64_C_struct_rewrite_dump"); }