void AsmStatement_toNakedIR(AsmStatement *stmt, IRState *irs) { IF_LOG Logger::println("AsmStatement::toNakedIR(): %s", stmt->loc.toChars()); LOG_SCOPE; // is there code? if (!stmt->asmcode) return; AsmCode * code = static_cast<AsmCode *>(stmt->asmcode); // build asm stmt replace_func_name(irs, code->insnTemplate); irs->nakedAsm << "\t" << code->insnTemplate << std::endl; }
void AsmStatement::toNakedIR(IRState *p) { Logger::println("AsmStatement::toNakedIR(): %s", loc.toChars()); LOG_SCOPE; // is there code? if (!asmcode) return; AsmCode * code = (AsmCode *) asmcode; // build asm stmt replace_func_name(p, code->insnTemplate); p->nakedAsm << "\t" << code->insnTemplate << std::endl; }
void AsmStatement_toIR(AsmStatement *stmt, IRState * irs) { IF_LOG Logger::println("AsmStatement::toIR(): %s", stmt->loc.toChars()); LOG_SCOPE; // sanity check assert(irs->func()->decl->hasReturnExp & 8); // get asm block IRAsmBlock* asmblock = irs->asmBlock; assert(asmblock); // debug info gIR->DBuilder.EmitStopPoint(stmt->loc.linnum); if (!stmt->asmcode) return; static std::string i_cns = "i"; static std::string p_cns = "i"; static std::string m_cns = "*m"; static std::string mw_cns = "=*m"; static std::string mrw_cns = "+*m"; static std::string memory_name = "memory"; AsmCode *code = static_cast<AsmCode *>(stmt->asmcode); std::vector<LLValue*> input_values; std::vector<std::string> input_constraints; std::vector<LLValue*> output_values; std::vector<std::string> output_constraints; std::vector<std::string> clobbers; // FIXME //#define HOST_WIDE_INT long //HOST_WIDE_INT var_frame_offset; // "frame_offset" is a macro bool clobbers_mem = code->clobbersMemory; int input_idx = 0; int n_outputs = 0; int arg_map[10]; assert(code->args.size() <= 10); std::vector<AsmArg>::iterator arg = code->args.begin(); for (unsigned i = 0; i < code->args.size(); i++, ++arg) { bool is_input = true; LLValue* arg_val = 0; std::string cns; switch (arg->type) { case Arg_Integer: arg_val = toElem(arg->expr)->getRVal(); do_integer: cns = i_cns; break; case Arg_Pointer: assert(arg->expr->op == TOKvar); arg_val = toElem(arg->expr)->getRVal(); cns = p_cns; break; case Arg_Memory: arg_val = toElem(arg->expr)->getRVal(); switch (arg->mode) { case Mode_Input: cns = m_cns; break; case Mode_Output: cns = mw_cns; is_input = false; break; case Mode_Update: cns = mrw_cns; is_input = false; break; default: llvm_unreachable("Unknown inline asm reference mode."); break; } break; case Arg_FrameRelative: // FIXME llvm_unreachable("Arg_FrameRelative not supported."); /* if (arg->expr->op == TOKvar) arg_val = ((VarExp *) arg->expr)->var->toSymbol()->Stree; else assert(0); if ( getFrameRelativeValue(arg_val, & var_frame_offset) ) { // arg_val = irs->integerConstant(var_frame_offset); cns = i_cns; } else { this->error("%s", "argument not frame relative"); return; } if (arg->mode != Mode_Input) clobbers_mem = true; break;*/ case Arg_LocalSize: // FIXME llvm_unreachable("Arg_LocalSize not supported."); /* var_frame_offset = cfun->x_frame_offset; if (var_frame_offset < 0) var_frame_offset = - var_frame_offset; arg_val = irs->integerConstant( var_frame_offset );*/ goto do_integer; default: llvm_unreachable("Unknown inline asm reference type."); } if (is_input) { arg_map[i] = --input_idx; input_values.push_back(arg_val); input_constraints.push_back(cns); } else { arg_map[i] = n_outputs++; output_values.push_back(arg_val); output_constraints.push_back(cns); } } // Telling GCC that callee-saved registers are clobbered makes it preserve // those registers. This changes the stack from what a naked function // expects. // FIXME // if (! irs->func->naked) { assert(asmparser); for (size_t i = 0; i < code->regs.size(); i++) { if (code->regs[i]) { clobbers.push_back(asmparser->getRegName(i)); } } if (clobbers_mem) clobbers.push_back(memory_name); // } // Remap argument numbers for (unsigned i = 0; i < code->args.size(); i++) { if (arg_map[i] < 0) arg_map[i] = -arg_map[i] - 1 + n_outputs; } bool pct = false; std::string::iterator p = code->insnTemplate.begin(), q = code->insnTemplate.end(); //printf("start: %.*s\n", code->insnTemplateLen, code->insnTemplate); while (p < q) { if (pct) { if (*p >= '0' && *p <= '9') { // %% doesn't check against nargs *p = '0' + arg_map[*p - '0']; pct = false; } else if (*p == '$') { pct = false; } //assert(*p == '%');// could be 'a', etc. so forget it.. } else if (*p == '$') pct = true; ++p; } typedef std::vector<std::string>::iterator It; IF_LOG { Logger::cout() << "final asm: " << code->insnTemplate << '\n'; std::ostringstream ss; ss << "GCC-style output constraints: {"; for (It i = output_constraints.begin(), e = output_constraints.end(); i != e; ++i) { ss << " " << *i; } ss << " }"; Logger::println("%s", ss.str().c_str()); ss.str(""); ss << "GCC-style input constraints: {"; for (It i = input_constraints.begin(), e = input_constraints.end(); i != e; ++i) { ss << " " << *i; } ss << " }"; Logger::println("%s", ss.str().c_str()); ss.str(""); ss << "GCC-style clobbers: {"; for (It i = clobbers.begin(), e = clobbers.end(); i != e; ++i) { ss << " " << *i; } ss << " }"; Logger::println("%s", ss.str().c_str()); } // rewrite GCC-style constraints to LLVM-style constraints std::string llvmOutConstraints; std::string llvmInConstraints; int n = 0; for(It i = output_constraints.begin(), e = output_constraints.end(); i != e; ++i, ++n) { // rewrite update constraint to in and out constraints if((*i)[0] == '+') { assert(*i == mrw_cns && "What else are we updating except memory?"); /* LLVM doesn't support updating operands, so split into an input * and an output operand. */ // Change update operand to pure output operand. *i = mw_cns; // Add input operand with same value, with original as "matching output". std::ostringstream ss; ss << '*' << (n + asmblock->outputcount); // Must be at the back; unused operands before used ones screw up numbering. input_constraints.push_back(ss.str()); input_values.push_back(output_values[n]); } llvmOutConstraints += *i; llvmOutConstraints += ","; } asmblock->outputcount += n; for(It i = input_constraints.begin(), e = input_constraints.end(); i != e; ++i) { llvmInConstraints += *i; llvmInConstraints += ","; } std::string clobstr; for(It i = clobbers.begin(), e = clobbers.end(); i != e; ++i) { clobstr = "~{" + *i + "},"; asmblock->clobs.insert(clobstr); } IF_LOG { typedef std::vector<LLValue*>::iterator It; { Logger::println("Output values:"); LOG_SCOPE size_t i = 0; for (It I = output_values.begin(), E = output_values.end(); I != E; ++I) { Logger::cout() << "Out " << i++ << " = " << **I << '\n'; } } { Logger::println("Input values:"); LOG_SCOPE size_t i = 0; for (It I = input_values.begin(), E = input_values.end(); I != E; ++I) { Logger::cout() << "In " << i++ << " = " << **I << '\n'; } } } // excessive commas are removed later... replace_func_name(irs, code->insnTemplate); // push asm statement IRAsmStmt* asmStmt = new IRAsmStmt; asmStmt->code = code->insnTemplate; asmStmt->out_c = llvmOutConstraints; asmStmt->in_c = llvmInConstraints; asmStmt->out.insert(asmStmt->out.begin(), output_values.begin(), output_values.end()); asmStmt->in.insert(asmStmt->in.begin(), input_values.begin(), input_values.end()); asmStmt->isBranchToLabel = stmt->isBranchToLabel; asmblock->s.push_back(asmStmt); }