StackTransInfo instrStackTransInfo(PC opcode) { static const StackTransInfo::Kind transKind[] = { #define NOV StackTransInfo::Kind::PushPop #define ONE(...) StackTransInfo::Kind::PushPop #define TWO(...) StackTransInfo::Kind::PushPop #define THREE(...) StackTransInfo::Kind::PushPop #define FOUR(...) StackTransInfo::Kind::PushPop #define IDX_A StackTransInfo::Kind::PushPop #define INS_1(...) StackTransInfo::Kind::InsertMid #define INS_2(...) StackTransInfo::Kind::InsertMid #define O(name, imm, pop, push, flags) push, OPCODES #undef NOV #undef ONE #undef TWO #undef THREE #undef FOUR #undef INS_1 #undef INS_2 #undef IDX_A #undef O }; static const int8_t peekPokeType[] = { #define NOV -1 #define ONE(...) -1 #define TWO(...) -1 #define THREE(...) -1 #define FOUR(...) -1 #define INS_1(...) 0 #define INS_2(...) 1 #define IDX_A 0 #define O(name, imm, pop, push, flags) push, OPCODES #undef NOV #undef ONE #undef TWO #undef THREE #undef FOUR #undef INS_2 #undef INS_1 #undef IDX_A #undef O }; StackTransInfo ret; auto const op = peek_op(opcode); ret.kind = transKind[size_t(op)]; switch (ret.kind) { case StackTransInfo::Kind::PushPop: ret.pos = 0; ret.numPushes = instrNumPushes(opcode); ret.numPops = instrNumPops(opcode); return ret; case StackTransInfo::Kind::InsertMid: ret.numPops = 0; ret.numPushes = 0; ret.pos = peekPokeType[size_t(op)]; return ret; } not_reached(); }
void IRTranslator::translateInstr(const NormalizedInstruction& ni) { auto& ht = m_hhbcTrans; ht.setBcOff(ni.source.offset(), ni.endsRegion && !m_hhbcTrans.isInlining()); FTRACE(1, "\n{:-^60}\n", folly::format("Translating {}: {} with stack:\n{}", ni.offset(), ni.toString(), ht.showStack())); // When profiling, we disable type predictions to avoid side exits assert(IMPLIES(mcg->tx().mode() == TransKind::Profile, !ni.outputPredicted)); ht.emitRB(RBTypeBytecodeStart, ni.source, 2); ht.emitIncStat(Stats::Instr_TC, 1, false); auto pc = reinterpret_cast<const Op*>(ni.pc()); for (auto i = 0, num = instrNumPops(pc); i < num; ++i) { auto const type = flavorToType(instrInputFlavor(pc, i)); if (type != Type::Gen) m_hhbcTrans.assertTypeStack(i, type); } if (RuntimeOption::EvalHHIRGenerateAsserts >= 2) { ht.emitDbgAssertRetAddr(); } if (isAlwaysNop(ni.op())) { // Do nothing } else if (instrMustInterp(ni) || ni.interp) { interpretInstr(ni); } else { translateInstrWork(ni); } }
void IRTranslator::translateInstr(const NormalizedInstruction& ni) { auto& ht = m_hhbcTrans; ht.setBcOff(ni.source.offset(), ni.breaksTracelet && !m_hhbcTrans.isInlining()); FTRACE(1, "\n{:-^60}\n", folly::format("Translating {}: {} with stack:\n{}", ni.offset(), ni.toString(), ht.showStack())); // When profiling, we disable type predictions to avoid side exits assert(IMPLIES(JIT::tx->mode() == TransKind::Profile, !ni.outputPredicted)); if (ni.guardedThis) { // Task #2067635: This should really generate an AssertThis ht.setThisAvailable(); } ht.emitRB(RBTypeBytecodeStart, ni.source, 2); auto pc = reinterpret_cast<const Op*>(ni.pc()); for (auto i = 0, num = instrNumPops(pc); i < num; ++i) { auto const type = flavorToType(instrInputFlavor(pc, i)); if (type != Type::Gen) m_hhbcTrans.assertTypeStack(i, type); } if (RuntimeOption::EvalHHIRGenerateAsserts >= 2) { ht.emitDbgAssertRetAddr(); } if (instrMustInterp(ni) || ni.interp) { interpretInstr(ni); } else { translateInstrWork(ni); } passPredictedAndInferredTypes(ni); }
StackTransInfo instrStackTransInfo(const Opcode* opcode) { static const StackTransInfo::Kind transKind[] = { #define NOV StackTransInfo::PushPop #define ONE(...) StackTransInfo::PushPop #define TWO(...) StackTransInfo::PushPop #define THREE(...) StackTransInfo::PushPop #define FOUR(...) StackTransInfo::PushPop #define INS_1(...) StackTransInfo::InsertMid #define INS_2(...) StackTransInfo::InsertMid #define O(name, imm, pop, push, flags) push, OPCODES #undef NOV #undef ONE #undef TWO #undef THREE #undef FOUR #undef INS_1 #undef INS_2 #undef O }; static const int8_t peekPokeType[] = { #define NOV -1 #define ONE(...) -1 #define TWO(...) -1 #define THREE(...) -1 #define FOUR(...) -1 #define INS_1(...) 0 #define INS_2(...) 1 #define O(name, imm, pop, push, flags) push, OPCODES #undef NOV #undef ONE #undef TWO #undef THREE #undef FOUR #undef INS_2 #undef INS_1 #undef O }; StackTransInfo ret; ret.kind = transKind[*opcode]; switch (ret.kind) { case StackTransInfo::PushPop: ret.pos = 0; ret.numPushes = instrNumPushes(opcode); ret.numPops = instrNumPops(opcode); return ret; case StackTransInfo::InsertMid: ret.numPops = 0; ret.numPushes = 0; ret.pos = peekPokeType[*opcode]; return ret; default: NOT_REACHED(); } }
void translateInstr( IRGS& irgs, const NormalizedInstruction& ni, bool checkOuterTypeOnly, bool firstInst ) { irgen::prepareForNextHHBC( irgs, &ni, ni.source, ni.endsRegion && !irgen::isInlining(irgs) ); const Func* builtinFunc = nullptr; if (ni.op() == OpFCallBuiltin) { auto str = ni.m_unit->lookupLitstrId(ni.imm[2].u_SA); builtinFunc = Unit::lookupFunc(str); } auto pc = ni.pc(); for (auto i = 0, num = instrNumPops(pc); i < num; ++i) { auto const type = !builtinFunc ? flavorToType(instrInputFlavor(pc, i)) : builtinFunc->byRef(num - i - 1) ? TGen : TCell; // TODO(#5706706): want to use assertTypeLocation, but Location::Stack // is a little unsure of itself. irgen::assertTypeStack(irgs, BCSPOffset{i}, type); } FTRACE(1, "\nTranslating {}: {} with state:\n{}\n", ni.offset(), ni, show(irgs)); irgen::ringbufferEntry(irgs, Trace::RBTypeBytecodeStart, ni.source, 2); irgen::emitIncStat(irgs, Stats::Instr_TC, 1); if (Stats::enableInstrCount()) { irgen::emitIncStat(irgs, Stats::opToTranslStat(ni.op()), 1); } if (Trace::moduleEnabledRelease(Trace::llvm_count, 1) || RuntimeOption::EvalJitLLVMCounters) { irgen::gen(irgs, CountBytecode); } if (isAlwaysNop(ni.op())) return; if (ni.interp || RuntimeOption::EvalJitAlwaysInterpOne) { irgen::interpOne(irgs, ni); return; } translateDispatch(irgs, ni); FTRACE(3, "\nTranslated {}: {} with state:\n{}\n", ni.offset(), ni, show(irgs)); }
int instrSpToArDelta(const Op* opcode) { // This function should only be called for instructions that read // the current FPI assert(instrReadsCurrentFpi(*opcode)); // The delta from sp to ar is equal to the number of values on the stack // that will be consumed by this instruction (numPops) plus the number of // parameters pushed onto the stack so far that are not being consumed by // this instruction (numExtra). For the FPass* instructions, numExtra will // be equal to the first immediate argument (param id). For the FCall // instructions, numExtra will be 0 because all of the parameters on the // stack are already accounted for by numPops. int numPops = instrNumPops(opcode); int numExtra = isFCallStar(*opcode) ? 0 : getImm(opcode, 0).u_IVA; return numPops + numExtra; }