diff --git a/hphp/doc/ir.specification b/hphp/doc/ir.specification index d5de8599b..e8ba645db 100644 --- a/hphp/doc/ir.specification +++ b/hphp/doc/ir.specification @@ -376,6 +376,11 @@ OverrideLoc S0:FramePtr T doesn't have to be related to the local's current type, so this may be used to update tracked state after InterpOne instructions. +OverrideLocVal S0:FramePtr S1:Gen + + Overrides tracked information about the value of a local in frame + S0, updating it to be S1. + SmashLocals S0:FramePtr Erases the tracked types and values of all locals in the frame diff --git a/hphp/runtime/vm/jit/code-gen.cpp b/hphp/runtime/vm/jit/code-gen.cpp index da1ebe13d..f67ec4faa 100644 --- a/hphp/runtime/vm/jit/code-gen.cpp +++ b/hphp/runtime/vm/jit/code-gen.cpp @@ -351,6 +351,7 @@ NOOP_OPCODE(DefFP) NOOP_OPCODE(DefSP) NOOP_OPCODE(AssertLoc) NOOP_OPCODE(OverrideLoc) +NOOP_OPCODE(OverrideLocVal) NOOP_OPCODE(SmashLocals) NOOP_OPCODE(AssertStk) NOOP_OPCODE(AssertStkVal) diff --git a/hphp/runtime/vm/jit/hhbc-translator.cpp b/hphp/runtime/vm/jit/hhbc-translator.cpp index 9b080ea64..499b6bd63 100644 --- a/hphp/runtime/vm/jit/hhbc-translator.cpp +++ b/hphp/runtime/vm/jit/hhbc-translator.cpp @@ -18,13 +18,14 @@ #include "hphp/util/trace.h" #include "hphp/runtime/ext/ext_closure.h" #include "hphp/runtime/ext/ext_continuation.h" -#include "hphp/runtime/vm/jit/translator-runtime.h" -#include "hphp/runtime/vm/jit/translator-x64.h" #include "hphp/runtime/base/stats.h" #include "hphp/runtime/vm/unit.h" #include "hphp/runtime/vm/runtime.h" +#include "hphp/runtime/vm/jit/code-gen.h" #include "hphp/runtime/vm/jit/ir-factory.h" -#include "hphp/runtime/vm/jit/code-gen.h" // ArrayIdx helpers +#include "hphp/runtime/vm/jit/translator-inline.h" +#include "hphp/runtime/vm/jit/translator-runtime.h" +#include "hphp/runtime/vm/jit/translator-x64.h" // Include last to localize effects to this file #include "hphp/util/assert_throw.h" @@ -66,12 +67,10 @@ bool classIsUniqueInterface(const Class* cls) { ////////////////////////////////////////////////////////////////////// -HhbcTranslator::HhbcTranslator(IRFactory& irFactory, - Offset startOffset, +HhbcTranslator::HhbcTranslator(Offset startOffset, uint32_t initialSpOffsetFromFp, const Func* func) - : m_irFactory(irFactory) - , m_tb(new TraceBuilder(startOffset, + : m_tb(new TraceBuilder(startOffset, initialSpOffsetFromFp, m_irFactory, func)) @@ -2602,16 +2601,13 @@ void HhbcTranslator::guardTypeLocal(uint32_t locId, Type type) { gen(GuardLoc, type, LocalId(locId), m_tb->fp()); } -void HhbcTranslator::guardTypeLocation(const Location& loc, Type type) { +void HhbcTranslator::guardTypeLocation(const RegionDesc::Location& loc, + Type type) { assert(type.subtypeOf(Type::Gen | Type::Cls)); - - if (loc.isStack()) { - guardTypeStack(loc.offset, type); - } else if (loc.isLocal()) { - assert(type.not(Type::Cls)); - guardTypeLocal(loc.offset, type); - } else { - not_reached(); + typedef RegionDesc::Location::Tag T; + switch (loc.tag()) { + case T::Stack: guardTypeStack(loc.stackOffset(), type); break; + case T::Local: guardTypeLocal(loc.localId(), type); break; } } @@ -2628,29 +2624,23 @@ void HhbcTranslator::overrideTypeLocal(uint32_t locId, Type type) { gen(OverrideLoc, type, LocalId(locId), m_tb->fp()); } -void HhbcTranslator::checkTypeLocation(const Location& loc, Type type, - Offset dest) { +void HhbcTranslator::checkTypeLocation(const RegionDesc::Location& loc, + Type type, Offset dest) { assert(type.subtypeOf(Type::Gen)); - - if (loc.isStack()) { - checkTypeStack(loc.offset, type, dest); - } else if (loc.isLocal()) { - checkTypeLocal(loc.offset, type, dest); - } else { - not_reached(); + typedef RegionDesc::Location::Tag T; + switch (loc.tag()) { + case T::Stack: checkTypeStack(loc.stackOffset(), type, dest); break; + case T::Local: checkTypeLocal(loc.localId(), type, dest); break; } } -void HhbcTranslator::assertTypeLocation(const Location& loc, Type type) { +void HhbcTranslator::assertTypeLocation(const RegionDesc::Location& loc, + Type type) { assert(type.subtypeOf(Type::Gen | Type::Cls)); - - if (loc.isStack()) { - assertTypeStack(loc.offset, type); - } else if (loc.isLocal()) { - assert(type.not(Type::Cls)); - assertTypeLocal(loc.offset, type); - } else { - not_reached(); + typedef RegionDesc::Location::Tag T; + switch (loc.tag()) { + case T::Stack: assertTypeStack(loc.stackOffset(), type); break; + case T::Local: assertTypeLocal(loc.localId(), type); break; } } @@ -2698,23 +2688,28 @@ void HhbcTranslator::assertTypeStack(uint32_t idx, Type type) { } } -void HhbcTranslator::assertString(const Location& loc, const StringData* str) { - auto idx = loc.offset; - - if (loc.isStack()) { - if (idx < m_evalStack.size()) { - DEBUG_ONLY SSATmp* oldStr = m_evalStack.top(idx); - assert(oldStr->type().maybe(Type::Str)); - m_evalStack.replace(idx, cns(str)); - } else { - gen(AssertStkVal, StackOffset(idx - m_evalStack.size() + m_stackDeficit), - m_tb->sp(), cns(str)); +void HhbcTranslator::assertString(const RegionDesc::Location& loc, + const StringData* str) { + typedef RegionDesc::Location::Tag T; + switch (loc.tag()) { + case T::Stack: { + auto idx = loc.stackOffset(); + if (idx < m_evalStack.size()) { + DEBUG_ONLY SSATmp* oldStr = m_evalStack.top(idx); + assert(oldStr->type().maybe(Type::Str)); + m_evalStack.replace(idx, cns(str)); + } else { + gen(AssertStkVal, + StackOffset(idx - m_evalStack.size() + m_stackDeficit), + m_tb->sp(), cns(str)); + } } - } else if (loc.isLocal()) { - assert(m_tb->getLocalType(loc.offset).maybe(Type::Str)); - m_tb->setLocalValue(idx, cns(str)); - } else { - not_reached(); + break; + + case T::Local: + assert(m_tb->getLocalType(loc.localId()).maybe(Type::Str)); + gen(OverrideLocVal, LocalId(loc.localId()), m_tb->fp(), cns(str)); + break; } } @@ -3491,10 +3486,10 @@ uint32_t localOutputId(const NormalizedInstruction& inst) { case OpSetWithRefLM: case OpFPassL: - return inst.imm[1].u_IVA; + return inst.imm[1].u_HA; default: - return inst.imm[0].u_IVA; + return inst.imm[0].u_HA; } } @@ -3727,8 +3722,7 @@ std::string HhbcTranslator::showStack() const { out << folly::format("+{:-^62}+\n", str); }; - const int32_t frameCells = curFunc()->numLocals() + - curFunc()->numIterators() * kNumIterCells; + const int32_t frameCells = curFunc()->numSlotsInFrame(); const int32_t stackDepth = m_tb->spOffset() + m_evalStack.size() - m_stackDeficit - (curFunc()->isGenerator() ? 0 : frameCells); diff --git a/hphp/runtime/vm/jit/hhbc-translator.h b/hphp/runtime/vm/jit/hhbc-translator.h index 42d75c41d..75ce2541a 100644 --- a/hphp/runtime/vm/jit/hhbc-translator.h +++ b/hphp/runtime/vm/jit/hhbc-translator.h @@ -26,6 +26,7 @@ #include "hphp/runtime/vm/member_operations.h" #include "hphp/runtime/vm/jit/runtime-type.h" #include "hphp/runtime/vm/jit/trace-builder.h" +#include "hphp/runtime/vm/jit/translator.h" #include "hphp/runtime/vm/srckey.h" using HPHP::Transl::NormalizedInstruction; @@ -103,14 +104,14 @@ private: * made at this level. */ struct HhbcTranslator { - HhbcTranslator(IRFactory& irFactory, - Offset startOffset, + HhbcTranslator(Offset startOffset, uint32_t initialSpOffsetFromFp, const Func* func); // Accessors. IRTrace* trace() const { return m_tb->trace(); } TraceBuilder* traceBuilder() const { return m_tb.get(); } + IRFactory& irFactory() { return m_irFactory; } // In between each emit* call, irtranslator indicates the new // bytecode offset (or whether we're finished) using this API. @@ -120,7 +121,7 @@ struct HhbcTranslator { // Tracelet guards. void guardTypeStack(uint32_t stackIndex, Type type); void guardTypeLocal(uint32_t locId, Type type); - void guardTypeLocation(const Location& loc, Type type); + void guardTypeLocation(const RegionDesc::Location& loc, Type type); void guardRefs(int64_t entryArDelta, const vector& mask, const vector& vals); @@ -132,9 +133,10 @@ struct HhbcTranslator { void checkTypeStack(uint32_t idx, Type type, Offset dest); void checkTypeTopOfStack(Type type, Offset nextByteCode); void overrideTypeLocal(uint32_t localIndex, Type type); - void assertTypeLocation(const Location& loc, Type type); - void checkTypeLocation(const Location& loc, Type type, Offset dest); - void assertString(const Location& loc, const StringData* sd); + void assertTypeLocation(const RegionDesc::Location& loc, Type type); + void checkTypeLocation(const RegionDesc::Location& loc, Type type, + Offset dest); + void assertString(const RegionDesc::Location& loc, const StringData* sd); RuntimeType rttFromLocation(const Location& loc); @@ -564,7 +566,7 @@ private: return m_tb.gen(std::forward(args)...); } - const Transl::NormalizedInstruction& m_ni; + const NormalizedInstruction& m_ni; HhbcTranslator& m_ht; TraceBuilder& m_tb; IRFactory& m_irf; @@ -839,7 +841,7 @@ private: }; private: - IRFactory& m_irFactory; + IRFactory m_irFactory; std::unique_ptr const m_tb; std::vector m_bcStateStack; diff --git a/hphp/runtime/vm/jit/ir-translator.cpp b/hphp/runtime/vm/jit/ir-translator.cpp index ca3398ab4..5c26a5afd 100644 --- a/hphp/runtime/vm/jit/ir-translator.cpp +++ b/hphp/runtime/vm/jit/ir-translator.cpp @@ -13,6 +13,9 @@ | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ + +#include "hphp/runtime/vm/jit/ir-translator.h" + #include #include "hphp/runtime/base/strings.h" @@ -31,25 +34,25 @@ #include "hphp/runtime/vm/jit/translator-x64.h" #include "hphp/runtime/base/stats.h" -#include "hphp/runtime/vm/jit/ir.h" -#include "hphp/runtime/vm/jit/opt.h" -#include "hphp/runtime/vm/jit/linear-scan.h" +#include "hphp/runtime/vm/jit/check.h" #include "hphp/runtime/vm/jit/code-gen.h" #include "hphp/runtime/vm/jit/hhbc-translator.h" +#include "hphp/runtime/vm/jit/ir.h" +#include "hphp/runtime/vm/jit/ir-translator.h" +#include "hphp/runtime/vm/jit/linear-scan.h" +#include "hphp/runtime/vm/jit/opt.h" #include "hphp/runtime/vm/jit/print.h" -#include "hphp/runtime/vm/jit/check.h" // Include last to localize effects to this file #include "hphp/util/assert_throw.h" namespace HPHP { -namespace Transl { +namespace JIT { using namespace reg; using namespace Util; using namespace Trace; using std::max; -using JIT::HhbcTranslator; TRACE_SET_MOD(hhir); #ifdef DEBUG @@ -61,6 +64,12 @@ static const bool debug = false; #define TVOFF(nm) offsetof(TypedValue, nm) #define AROFF(nm) offsetof(ActRec, nm) +#define HHIR_EMIT(op, ...) \ + do { \ + m_hhbcTrans.emit ## op(__VA_ARGS__); \ + return; \ + } while (0) + #define HHIR_UNIMPLEMENTED_OP(op) \ do { \ throw JIT::FailedIRGen(__FILE__, __LINE__, op); \ @@ -79,12 +88,7 @@ static const bool debug = false; } \ } while (0) -#define HHIR_EMIT(op, ...) \ - do { \ - m_hhbcTrans->emit ## op(__VA_ARGS__); \ - return; \ - } while (0) - +namespace { bool isInferredType(const NormalizedInstruction& i) { return (i.getOutputUsage(i.outStack) == NormalizedInstruction::OutputUse::Inferred); @@ -98,49 +102,88 @@ JIT::Type getInferredOrPredictedType(const NormalizedInstruction& i) { } return JIT::Type::None; } +} -void -TranslatorX64::irCheckType(X64Assembler& a, - const Location& l, - const RuntimeType& rtt, - SrcRec& fail) { +IRTranslator::IRTranslator(Offset bcOff, Offset spOff, const Func* curFunc) + : m_hhbcTrans(bcOff, spOff, curFunc) +{ +} + +void IRTranslator::checkType(const Transl::Location& l, + const Transl::RuntimeType& rtt) { // We can get invalid inputs as a side effect of reading invalid // items out of BBs we truncate; they don't need guards. - assert(!rtt.isVagueValue()); + if (rtt.isVagueValue() || l.isThis()) return; + using Transl::Location; switch (l.space) { - case Location::Stack: - { + case Location::Stack: + m_hhbcTrans.guardTypeStack(locPhysicalOffset(l), + Type::fromRuntimeType(rtt)); + break; + + case Location::Local: + m_hhbcTrans.guardTypeLocal(l.offset, Type::fromRuntimeType(rtt)); + break; + + case Location::Iter: + case Location::Invalid: + case Location::Litstr: + case Location::Litint: + case Location::This: + not_reached(); + } +} + +void IRTranslator::assertType(const Transl::Location& l, + const Transl::RuntimeType& rtt) { + if (rtt.isVagueValue()) return; + + using Transl::Location; + switch (l.space) { + case Location::Stack: { + // tx64LocPhysicalOffset returns positive offsets for stack values, + // relative to rVmSp uint32_t stackOffset = locPhysicalOffset(l); - m_hhbcTrans->guardTypeStack(stackOffset, - JIT::Type::fromRuntimeType(rtt)); + m_hhbcTrans.assertTypeStack(stackOffset, Type::fromRuntimeType(rtt)); + break; } - break; + case Location::Local: // Stack frame's registers; offset == local register + m_hhbcTrans.assertTypeLocal(l.offset, Type::fromRuntimeType(rtt)); + break; - case Location::Local: - m_hhbcTrans->guardTypeLocal(l.offset, JIT::Type::fromRuntimeType(rtt)); - break; + case Location::Invalid: // Unknown location + HHIR_UNIMPLEMENTED(Invalid); + break; - case Location::Iter: - case Location::Invalid: - case Location::Litstr: - case Location::Litint: - case Location::This: - not_reached(); + case Location::Iter: // Stack frame's iterators + HHIR_UNIMPLEMENTED(AssertType_Iter); + break; + + case Location::Litstr: // Literal string pseudo-location + HHIR_UNIMPLEMENTED(AssertType_Litstr); + break; + + case Location::Litint: // Literal int pseudo-location + HHIR_UNIMPLEMENTED(AssertType_Litint); + break; + + case Location::This: + HHIR_UNIMPLEMENTED(AssertType_This); + break; } } void -Translator::translateMod(const NormalizedInstruction& i) { +IRTranslator::translateMod(const NormalizedInstruction& i) { HHIR_EMIT(Mod); } void -Translator::translateBinaryArithOp(const NormalizedInstruction& i) { +IRTranslator::translateBinaryArithOp(const NormalizedInstruction& i) { auto const op = i.op(); switch (op) { -#define CASE(OpBc) \ - case Op ## OpBc: HHIR_EMIT(OpBc); +#define CASE(OpBc) case Op::OpBc: HHIR_EMIT(OpBc); CASE(Add) CASE(Sub) CASE(BitAnd) @@ -156,10 +199,10 @@ Translator::translateBinaryArithOp(const NormalizedInstruction& i) { } void -Translator::translateSameOp(const NormalizedInstruction& i) { +IRTranslator::translateSameOp(const NormalizedInstruction& i) { auto const op = i.op(); - assert(op == OpSame || op == OpNSame); - if (op == OpSame) { + assert(op == Op::Same || op == Op::NSame); + if (op == Op::Same) { HHIR_EMIT(Same); } else { HHIR_EMIT(NSame); @@ -167,10 +210,10 @@ Translator::translateSameOp(const NormalizedInstruction& i) { } void -Translator::translateEqOp(const NormalizedInstruction& i) { +IRTranslator::translateEqOp(const NormalizedInstruction& i) { auto const op = i.op(); - assert(op == OpEq || op == OpNeq); - if (op == OpEq) { + assert(op == Op::Eq || op == Op::Neq); + if (op == Op::Eq) { HHIR_EMIT(Eq); } else { HHIR_EMIT(Neq); @@ -178,9 +221,9 @@ Translator::translateEqOp(const NormalizedInstruction& i) { } void -Translator::translateLtGtOp(const NormalizedInstruction& i) { +IRTranslator::translateLtGtOp(const NormalizedInstruction& i) { auto const op = i.op(); - assert(op == OpLt || op == OpLte || op == OpGt || op == OpGte); + assert(op == Op::Lt || op == Op::Lte || op == Op::Gt || op == Op::Gte); assert(i.inputs.size() == 2); assert(i.inputs[0]->outerType() != KindOfRef); assert(i.inputs[1]->outerType() != KindOfRef); @@ -194,16 +237,16 @@ Translator::translateLtGtOp(const NormalizedInstruction& i) { HHIR_UNIMPLEMENTED_WHEN(!ok, LtGtOp); switch (op) { - case OpLt : HHIR_EMIT(Lt); - case OpLte : HHIR_EMIT(Lte); - case OpGt : HHIR_EMIT(Gt); - case OpGte : HHIR_EMIT(Gte); + case Op::Lt : HHIR_EMIT(Lt); + case Op::Lte : HHIR_EMIT(Lte); + case Op::Gt : HHIR_EMIT(Gt); + case Op::Gte : HHIR_EMIT(Gte); default : HHIR_UNIMPLEMENTED(LtGtOp); } } void -Translator::translateUnaryBooleanOp(const NormalizedInstruction& i) { +IRTranslator::translateUnaryBooleanOp(const NormalizedInstruction& i) { auto const op = i.op(); assert(op == OpCastBool || op == OpEmptyL); if (op == OpCastBool) { @@ -214,7 +257,7 @@ Translator::translateUnaryBooleanOp(const NormalizedInstruction& i) { } void -Translator::translateBranchOp(const NormalizedInstruction& i) { +IRTranslator::translateBranchOp(const NormalizedInstruction& i) { auto const op = i.op(); assert(op == OpJmpZ || op == OpJmpNZ); assert(!i.next); @@ -227,7 +270,7 @@ Translator::translateBranchOp(const NormalizedInstruction& i) { } void -Translator::translateCGetL(const NormalizedInstruction& i) { +IRTranslator::translateCGetL(const NormalizedInstruction& i) { DEBUG_ONLY auto const op = i.op(); assert(op == OpFPassL || op == OpCGetL); const vector& inputs = i.inputs; @@ -238,19 +281,19 @@ Translator::translateCGetL(const NormalizedInstruction& i) { } void -Translator::translateCGetL2(const NormalizedInstruction& ni) { +IRTranslator::translateCGetL2(const NormalizedInstruction& ni) { const int locIdx = 1; HHIR_EMIT(CGetL2, ni.inputs[locIdx]->location.offset); } void -Translator::translateVGetL(const NormalizedInstruction& i) { +IRTranslator::translateVGetL(const NormalizedInstruction& i) { HHIR_EMIT(VGetL, i.inputs[0]->location.offset); } void -Translator::translateAssignToLocalOp(const NormalizedInstruction& ni) { +IRTranslator::translateAssignToLocalOp(const NormalizedInstruction& ni) { DEBUG_ONLY const int rhsIdx = 0; const int locIdx = 1; auto const op = ni.op(); @@ -271,7 +314,7 @@ Translator::translateAssignToLocalOp(const NormalizedInstruction& ni) { } void -Translator::translatePopC(const NormalizedInstruction& i) { +IRTranslator::translatePopC(const NormalizedInstruction& i) { assert(i.inputs.size() == 1); if (i.inputs[0]->rtt.isVagueValue()) { @@ -282,97 +325,96 @@ Translator::translatePopC(const NormalizedInstruction& i) { } void -Translator::translatePopV(const NormalizedInstruction& i) { +IRTranslator::translatePopV(const NormalizedInstruction& i) { assert(i.inputs[0]->rtt.isVagueValue() || i.inputs[0]->isRef()); HHIR_EMIT(PopV); } void -Translator::translatePopR(const NormalizedInstruction& i) { +IRTranslator::translatePopR(const NormalizedInstruction& i) { translatePopC(i); } void -Translator::translateUnboxR(const NormalizedInstruction& i) { +IRTranslator::translateUnboxR(const NormalizedInstruction& i) { if (i.noOp) { // statically proved to be unboxed -- just pass that info to the IR TRACE(1, "HHIR: translateUnboxR: output inferred to be Cell\n"); - m_hhbcTrans->assertTypeLocation(Location(Location::Stack, 0), - JIT::Type::Cell); + m_hhbcTrans.assertTypeStack(0, JIT::Type::Cell); } else { HHIR_EMIT(UnboxR); } } void -Translator::translateNull(const NormalizedInstruction& i) { +IRTranslator::translateNull(const NormalizedInstruction& i) { assert(i.inputs.size() == 0); HHIR_EMIT(Null); } void -Translator::translateNullUninit(const NormalizedInstruction& i) { +IRTranslator::translateNullUninit(const NormalizedInstruction& i) { HHIR_EMIT(NullUninit); } void -Translator::translateTrue(const NormalizedInstruction& i) { +IRTranslator::translateTrue(const NormalizedInstruction& i) { assert(i.inputs.size() == 0); HHIR_EMIT(True); } void -Translator::translateFalse(const NormalizedInstruction& i) { +IRTranslator::translateFalse(const NormalizedInstruction& i) { assert(i.inputs.size() == 0); HHIR_EMIT(False); } void -Translator::translateInt(const NormalizedInstruction& i) { +IRTranslator::translateInt(const NormalizedInstruction& i) { assert(i.inputs.size() == 0); HHIR_EMIT(Int, i.imm[0].u_I64A); } void -Translator::translateDouble(const NormalizedInstruction& i) { +IRTranslator::translateDouble(const NormalizedInstruction& i) { HHIR_EMIT(Double, i.imm[0].u_DA); } void -Translator::translateString(const NormalizedInstruction& i) { +IRTranslator::translateString(const NormalizedInstruction& i) { assert(i.inputs.size() == 0); HHIR_EMIT(String, (i.imm[0].u_SA)); } void -Translator::translateArray(const NormalizedInstruction& i) { +IRTranslator::translateArray(const NormalizedInstruction& i) { assert(i.inputs.size() == 0); HHIR_EMIT(Array, i.imm[0].u_AA); } void -Translator::translateNewArray(const NormalizedInstruction& i) { +IRTranslator::translateNewArray(const NormalizedInstruction& i) { HHIR_EMIT(NewArray, i.imm[0].u_IVA); } void -Translator::translateNop(const NormalizedInstruction& i) { +IRTranslator::translateNop(const NormalizedInstruction& i) { HHIR_EMIT(Nop); } void -Translator::translateAddElemC(const NormalizedInstruction& i) { +IRTranslator::translateAddElemC(const NormalizedInstruction& i) { HHIR_EMIT(AddElemC); } void -Translator::translateAddNewElemC(const NormalizedInstruction& i) { +IRTranslator::translateAddNewElemC(const NormalizedInstruction& i) { assert(i.inputs.size() == 2); assert(i.inputs[0]->outerType() != KindOfRef); assert(i.inputs[1]->outerType() != KindOfRef); @@ -383,27 +425,27 @@ Translator::translateAddNewElemC(const NormalizedInstruction& i) { } void -Translator::translateCns(const NormalizedInstruction& i) { +IRTranslator::translateCns(const NormalizedInstruction& i) { HHIR_EMIT(Cns, i.imm[0].u_SA); } void -Translator::translateDefCns(const NormalizedInstruction& i) { +IRTranslator::translateDefCns(const NormalizedInstruction& i) { HHIR_EMIT(DefCns, (i.imm[0].u_SA)); } void -Translator::translateClsCnsD(const NormalizedInstruction& i) { +IRTranslator::translateClsCnsD(const NormalizedInstruction& i) { HHIR_EMIT(ClsCnsD, (i.imm[0].u_SA), (i.imm[1].u_SA), i.outPred); } void -Translator::translateConcat(const NormalizedInstruction& i) { +IRTranslator::translateConcat(const NormalizedInstruction& i) { HHIR_EMIT(Concat); } void -Translator::translateAdd(const NormalizedInstruction& i) { +IRTranslator::translateAdd(const NormalizedInstruction& i) { assert(i.inputs.size() == 2); if (i.inputs[0]->valueType() == KindOfArray && @@ -415,22 +457,22 @@ Translator::translateAdd(const NormalizedInstruction& i) { } void -Translator::translateXor(const NormalizedInstruction& i) { +IRTranslator::translateXor(const NormalizedInstruction& i) { HHIR_EMIT(Xor); } void -Translator::translateNot(const NormalizedInstruction& i) { +IRTranslator::translateNot(const NormalizedInstruction& i) { HHIR_EMIT(Not); } void -Translator::translateBitNot(const NormalizedInstruction& i) { +IRTranslator::translateBitNot(const NormalizedInstruction& i) { HHIR_EMIT(BitNot); } void -Translator::translateCastInt(const NormalizedInstruction& i) { +IRTranslator::translateCastInt(const NormalizedInstruction& i) { assert(i.inputs.size() == 1); HHIR_EMIT(CastInt); @@ -438,42 +480,42 @@ Translator::translateCastInt(const NormalizedInstruction& i) { } void -Translator::translateCastArray(const NormalizedInstruction& i) { +IRTranslator::translateCastArray(const NormalizedInstruction& i) { HHIR_EMIT(CastArray); } void -Translator::translateCastObject(const NormalizedInstruction& i) { +IRTranslator::translateCastObject(const NormalizedInstruction& i) { HHIR_EMIT(CastObject); } void -Translator::translateCastDouble(const NormalizedInstruction& i) { +IRTranslator::translateCastDouble(const NormalizedInstruction& i) { HHIR_EMIT(CastDouble); } void -Translator::translateCastString(const NormalizedInstruction& i) { +IRTranslator::translateCastString(const NormalizedInstruction& i) { HHIR_EMIT(CastString); } void -Translator::translatePrint(const NormalizedInstruction& i) { +IRTranslator::translatePrint(const NormalizedInstruction& i) { HHIR_EMIT(Print); } void -Translator::translateJmp(const NormalizedInstruction& i) { +IRTranslator::translateJmp(const NormalizedInstruction& i) { HHIR_EMIT(Jmp, i.offset() + i.imm[0].u_BA, i.breaksTracelet, i.noSurprise); } void -Translator::translateSwitch(const NormalizedInstruction& i) { +IRTranslator::translateSwitch(const NormalizedInstruction& i) { HHIR_EMIT(Switch, i.immVec, i.imm[1].u_I64A, i.imm[2].u_IVA); } void -Translator::translateSSwitch(const NormalizedInstruction& i) { +IRTranslator::translateSSwitch(const NormalizedInstruction& i) { HHIR_EMIT(SSwitch, i.immVec); } @@ -484,17 +526,17 @@ Translator::translateSSwitch(const NormalizedInstruction& i) { * top-of-stack return value. */ void -Translator::translateRetC(const NormalizedInstruction& i) { +IRTranslator::translateRetC(const NormalizedInstruction& i) { HHIR_EMIT(RetC, i.inlineReturn); } void -Translator::translateRetV(const NormalizedInstruction& i) { +IRTranslator::translateRetV(const NormalizedInstruction& i) { HHIR_EMIT(RetV, i.inlineReturn); } void -Translator::translateNativeImpl(const NormalizedInstruction& ni) { +IRTranslator::translateNativeImpl(const NormalizedInstruction& ni) { HHIR_EMIT(NativeImpl); } @@ -507,208 +549,208 @@ Translator::translateNativeImpl(const NormalizedInstruction& ni) { const int kEmitClsLocalIdx = 0; -void Translator::translateAGetC(const NormalizedInstruction& ni) { +void IRTranslator::translateAGetC(const NormalizedInstruction& ni) { const StringData* clsName = ni.inputs[kEmitClsLocalIdx]->rtt.valueStringOrNull(); HHIR_EMIT(AGetC, clsName); } -void Translator::translateAGetL(const NormalizedInstruction& i) { +void IRTranslator::translateAGetL(const NormalizedInstruction& i) { assert(i.inputs[kEmitClsLocalIdx]->isLocal()); const DynLocation* dynLoc = i.inputs[kEmitClsLocalIdx]; const StringData* clsName = dynLoc->rtt.valueStringOrNull(); HHIR_EMIT(AGetL, dynLoc->location.offset, clsName); } -void Translator::translateSelf(const NormalizedInstruction& i) { +void IRTranslator::translateSelf(const NormalizedInstruction& i) { HHIR_EMIT(Self); } -void Translator::translateParent(const NormalizedInstruction& i) { +void IRTranslator::translateParent(const NormalizedInstruction& i) { HHIR_EMIT(Parent); } -void Translator::translateDup(const NormalizedInstruction& ni) { +void IRTranslator::translateDup(const NormalizedInstruction& ni) { HHIR_EMIT(Dup); } -void Translator::translateCreateCont(const NormalizedInstruction& i) { +void IRTranslator::translateCreateCont(const NormalizedInstruction& i) { HHIR_EMIT(CreateCont, i.imm[0].u_SA); } -void Translator::translateContEnter(const NormalizedInstruction& i) { +void IRTranslator::translateContEnter(const NormalizedInstruction& i) { auto after = nextSrcKey(i).offset(); // ContEnter can't exist in an inlined function right now. (If it // ever can, this curFunc() needs to change.) - assert(!m_hhbcTrans->isInlining()); + assert(!m_hhbcTrans.isInlining()); const Func* srcFunc = curFunc(); int32_t callOffsetInUnit = after - srcFunc->base(); HHIR_EMIT(ContEnter, callOffsetInUnit); } -void Translator::translateUnpackCont(const NormalizedInstruction& i) { +void IRTranslator::translateUnpackCont(const NormalizedInstruction& i) { HHIR_EMIT(UnpackCont); } -void Translator::translateContSuspend(const NormalizedInstruction& i) { +void IRTranslator::translateContSuspend(const NormalizedInstruction& i) { HHIR_EMIT(ContSuspend, i.imm[0].u_IVA); } -void Translator::translateContSuspendK(const NormalizedInstruction& i) { +void IRTranslator::translateContSuspendK(const NormalizedInstruction& i) { HHIR_EMIT(ContSuspendK, i.imm[0].u_IVA); } -void Translator::translateContRetC(const NormalizedInstruction& i) { +void IRTranslator::translateContRetC(const NormalizedInstruction& i) { HHIR_EMIT(ContRetC); } -void Translator::translateContCheck(const NormalizedInstruction& i) { +void IRTranslator::translateContCheck(const NormalizedInstruction& i) { HHIR_EMIT(ContCheck, i.imm[0].u_IVA); } -void Translator::translateContRaise(const NormalizedInstruction& i) { +void IRTranslator::translateContRaise(const NormalizedInstruction& i) { HHIR_EMIT(ContRaise); } -void Translator::translateContValid(const NormalizedInstruction& i) { +void IRTranslator::translateContValid(const NormalizedInstruction& i) { HHIR_EMIT(ContValid); } -void Translator::translateContKey(const NormalizedInstruction& i) { +void IRTranslator::translateContKey(const NormalizedInstruction& i) { HHIR_EMIT(ContKey); } -void Translator::translateContCurrent(const NormalizedInstruction& i) { +void IRTranslator::translateContCurrent(const NormalizedInstruction& i) { HHIR_EMIT(ContCurrent); } -void Translator::translateContStopped(const NormalizedInstruction& i) { +void IRTranslator::translateContStopped(const NormalizedInstruction& i) { HHIR_EMIT(ContStopped); } -void Translator::translateContHandle(const NormalizedInstruction& i) { +void IRTranslator::translateContHandle(const NormalizedInstruction& i) { HHIR_EMIT(ContHandle); } -void Translator::translateStrlen(const NormalizedInstruction& i) { +void IRTranslator::translateStrlen(const NormalizedInstruction& i) { HHIR_EMIT(Strlen); } -void Translator::translateIncStat(const NormalizedInstruction& i) { +void IRTranslator::translateIncStat(const NormalizedInstruction& i) { HHIR_EMIT(IncStat, i.imm[0].u_IVA, i.imm[1].u_IVA); } -void Translator::translateArrayIdx(const NormalizedInstruction& i) { +void IRTranslator::translateArrayIdx(const NormalizedInstruction& i) { HHIR_EMIT(ArrayIdx); } -void Translator::translateClassExists(const NormalizedInstruction& i) { +void IRTranslator::translateClassExists(const NormalizedInstruction& i) { const StringData* clsName = i.inputs[1]->rtt.valueStringOrNull(); HHIR_EMIT(ClassExists, clsName); } -void Translator::translateInterfaceExists(const NormalizedInstruction& i) { +void IRTranslator::translateInterfaceExists(const NormalizedInstruction& i) { const StringData* ifaceName = i.inputs[1]->rtt.valueStringOrNull(); HHIR_EMIT(InterfaceExists, ifaceName); } -void Translator::translateTraitExists(const NormalizedInstruction& i) { +void IRTranslator::translateTraitExists(const NormalizedInstruction& i) { const StringData* traitName = i.inputs[1]->rtt.valueStringOrNull(); HHIR_EMIT(TraitExists, traitName); } -void Translator::translateVGetS(const NormalizedInstruction& i) { +void IRTranslator::translateVGetS(const NormalizedInstruction& i) { const int kPropNameIdx = 1; const StringData* propName = i.inputs[kPropNameIdx]->rtt.valueStringOrNull(); HHIR_EMIT(VGetS, propName); } void -Translator::translateVGetG(const NormalizedInstruction& i) { +IRTranslator::translateVGetG(const NormalizedInstruction& i) { const StringData* name = i.inputs[0]->rtt.valueStringOrNull(); HHIR_EMIT(VGetG, name); } -void Translator::translateBindS(const NormalizedInstruction& i) { +void IRTranslator::translateBindS(const NormalizedInstruction& i) { const int kPropIdx = 2; const StringData* propName = i.inputs[kPropIdx]->rtt.valueStringOrNull(); HHIR_EMIT(BindS, propName); } -void Translator::translateEmptyS(const NormalizedInstruction& i) { +void IRTranslator::translateEmptyS(const NormalizedInstruction& i) { const int kPropNameIdx = 1; const StringData* propName = i.inputs[kPropNameIdx]->rtt.valueStringOrNull(); HHIR_EMIT(EmptyS, propName); } -void Translator::translateEmptyG(const NormalizedInstruction& i) { +void IRTranslator::translateEmptyG(const NormalizedInstruction& i) { const StringData* gblName = i.inputs[0]->rtt.valueStringOrNull(); HHIR_EMIT(EmptyG, gblName); } void -Translator::translateIssetS(const NormalizedInstruction& i) { +IRTranslator::translateIssetS(const NormalizedInstruction& i) { const int kPropNameIdx = 1; const StringData* propName = i.inputs[kPropNameIdx]->rtt.valueStringOrNull(); HHIR_EMIT(IssetS, propName); } void -Translator::translateIssetG(const NormalizedInstruction& i) { +IRTranslator::translateIssetG(const NormalizedInstruction& i) { const StringData* gblName = i.inputs[0]->rtt.valueStringOrNull(); HHIR_EMIT(IssetG, gblName); } void -Translator::translateUnsetG(const NormalizedInstruction& i) { +IRTranslator::translateUnsetG(const NormalizedInstruction& i) { const StringData* gblName = i.inputs[0]->rtt.valueStringOrNull(); HHIR_EMIT(UnsetG, gblName); } void -Translator::translateUnsetN(const NormalizedInstruction& i) { +IRTranslator::translateUnsetN(const NormalizedInstruction& i) { HHIR_EMIT(UnsetN); } -void Translator::translateCGetS(const NormalizedInstruction& i) { +void IRTranslator::translateCGetS(const NormalizedInstruction& i) { const int kPropNameIdx = 1; const StringData* propName = i.inputs[kPropNameIdx]->rtt.valueStringOrNull(); HHIR_EMIT(CGetS, propName, getInferredOrPredictedType(i), isInferredType(i)); } -void Translator::translateSetS(const NormalizedInstruction& i) { +void IRTranslator::translateSetS(const NormalizedInstruction& i) { const int kPropIdx = 2; const StringData* propName = i.inputs[kPropIdx]->rtt.valueStringOrNull(); HHIR_EMIT(SetS, propName); } void -Translator::translateCGetG(const NormalizedInstruction& i) { +IRTranslator::translateCGetG(const NormalizedInstruction& i) { const StringData* name = i.inputs[0]->rtt.valueStringOrNull(); HHIR_EMIT(CGetG, name, getInferredOrPredictedType(i), isInferredType(i)); } -void Translator::translateSetG(const NormalizedInstruction& i) { +void IRTranslator::translateSetG(const NormalizedInstruction& i) { const StringData* name = i.inputs[1]->rtt.valueStringOrNull(); HHIR_EMIT(SetG, name); } -void Translator::translateBindG(const NormalizedInstruction& i) { +void IRTranslator::translateBindG(const NormalizedInstruction& i) { const StringData* name = i.inputs[1]->rtt.valueStringOrNull(); HHIR_EMIT(BindG, name); } void -Translator::translateLateBoundCls(const NormalizedInstruction&i) { +IRTranslator::translateLateBoundCls(const NormalizedInstruction&i) { HHIR_EMIT(LateBoundCls); } -void Translator::translateFPassL(const NormalizedInstruction& ni) { +void IRTranslator::translateFPassL(const NormalizedInstruction& ni) { if (ni.preppedByRef) { translateVGetL(ni); } else { @@ -716,7 +758,7 @@ void Translator::translateFPassL(const NormalizedInstruction& ni) { } } -void Translator::translateFPassS(const NormalizedInstruction& ni) { +void IRTranslator::translateFPassS(const NormalizedInstruction& ni) { if (ni.preppedByRef) { translateVGetS(ni); } else { @@ -724,7 +766,7 @@ void Translator::translateFPassS(const NormalizedInstruction& ni) { } } -void Translator::translateFPassG(const NormalizedInstruction& ni) { +void IRTranslator::translateFPassG(const NormalizedInstruction& ni) { if (ni.preppedByRef) { translateVGetG(ni); } else { @@ -733,7 +775,7 @@ void Translator::translateFPassG(const NormalizedInstruction& ni) { } void -Translator::translateCheckTypeOp(const NormalizedInstruction& ni) { +IRTranslator::translateCheckTypeOp(const NormalizedInstruction& ni) { assert(ni.inputs.size() == 1); auto const op = ni.op(); @@ -761,13 +803,13 @@ Translator::translateCheckTypeOp(const NormalizedInstruction& ni) { } void -Translator::translateAKExists(const NormalizedInstruction& ni) { +IRTranslator::translateAKExists(const NormalizedInstruction& ni) { HHIR_EMIT(AKExists); } void -Translator::translateSetOpL(const NormalizedInstruction& i) { - JIT::Opcode opc; +IRTranslator::translateSetOpL(const NormalizedInstruction& i) { + Opcode opc; switch (i.imm[1].u_OA) { case SetOpPlusEqual: opc = JIT::OpAdd; break; case SetOpMinusEqual: opc = JIT::OpSub; break; @@ -787,7 +829,7 @@ Translator::translateSetOpL(const NormalizedInstruction& i) { } void -Translator::translateIncDecL(const NormalizedInstruction& i) { +IRTranslator::translateIncDecL(const NormalizedInstruction& i) { const vector& inputs = i.inputs; assert(inputs.size() == 1); assert(inputs[0]->isLocal()); @@ -802,33 +844,33 @@ Translator::translateIncDecL(const NormalizedInstruction& i) { } void -Translator::translateUnsetL(const NormalizedInstruction& i) { +IRTranslator::translateUnsetL(const NormalizedInstruction& i) { HHIR_EMIT(UnsetL, i.inputs[0]->location.offset); } void -Translator::translateReqDoc(const NormalizedInstruction& i) { +IRTranslator::translateReqDoc(const NormalizedInstruction& i) { const StringData* name = i.inputs[0]->rtt.valueStringOrNull(); HHIR_EMIT(ReqDoc, name); } -void Translator::translateDefCls(const NormalizedInstruction& i) { +void IRTranslator::translateDefCls(const NormalizedInstruction& i) { int cid = i.imm[0].u_IVA; HHIR_EMIT(DefCls, cid, i.source.offset()); } -void Translator::translateDefFunc(const NormalizedInstruction& i) { +void IRTranslator::translateDefFunc(const NormalizedInstruction& i) { int fid = i.imm[0].u_IVA; HHIR_EMIT(DefFunc, fid); } void -Translator::translateFPushFunc(const NormalizedInstruction& i) { +IRTranslator::translateFPushFunc(const NormalizedInstruction& i) { HHIR_EMIT(FPushFunc, (i.imm[0].u_IVA)); } void -Translator::translateFPushClsMethodD(const NormalizedInstruction& i) { +IRTranslator::translateFPushClsMethodD(const NormalizedInstruction& i) { HHIR_EMIT(FPushClsMethodD, (i.imm[0].u_IVA), (i.imm[1].u_SA), @@ -836,7 +878,7 @@ Translator::translateFPushClsMethodD(const NormalizedInstruction& i) { } void -Translator::translateFPushClsMethodF(const NormalizedInstruction& i) { +IRTranslator::translateFPushClsMethodF(const NormalizedInstruction& i) { // For now, only support cases where both the class and the method are known. const int classIdx = 0; const int methodIdx = 1; @@ -857,7 +899,7 @@ Translator::translateFPushClsMethodF(const NormalizedInstruction& i) { } void -Translator::translateFPushObjMethodD(const NormalizedInstruction& i) { +IRTranslator::translateFPushObjMethodD(const NormalizedInstruction& i) { assert(i.inputs.size() == 1); HHIR_UNIMPLEMENTED_WHEN((i.inputs[0]->valueType() != KindOfObject), FPushObjMethod_nonObj); @@ -870,53 +912,53 @@ Translator::translateFPushObjMethodD(const NormalizedInstruction& i) { baseClass); } -void Translator::translateFPushCtor(const NormalizedInstruction& i) { +void IRTranslator::translateFPushCtor(const NormalizedInstruction& i) { HHIR_EMIT(FPushCtor, (i.imm[0].u_IVA)); } -void Translator::translateFPushCtorD(const NormalizedInstruction& i) { +void IRTranslator::translateFPushCtorD(const NormalizedInstruction& i) { HHIR_EMIT(FPushCtorD, (i.imm[0].u_IVA), (i.imm[1].u_SA)); } -void Translator::translateCreateCl(const NormalizedInstruction& i) { +void IRTranslator::translateCreateCl(const NormalizedInstruction& i) { HHIR_EMIT(CreateCl, (i.imm[0].u_IVA), (i.imm[1].u_SA)); } // static void fatalNullThis() { raise_error(Strings::FATAL_NULL_THIS); } void -Translator::translateThis(const NormalizedInstruction &i) { +IRTranslator::translateThis(const NormalizedInstruction &i) { HHIR_EMIT(This); } void -Translator::translateBareThis(const NormalizedInstruction &i) { +IRTranslator::translateBareThis(const NormalizedInstruction &i) { HHIR_EMIT(BareThis, (i.imm[0].u_OA)); } void -Translator::translateCheckThis(const NormalizedInstruction& i) { +IRTranslator::translateCheckThis(const NormalizedInstruction& i) { HHIR_EMIT(CheckThis); } void -Translator::translateInitThisLoc(const NormalizedInstruction& i) { +IRTranslator::translateInitThisLoc(const NormalizedInstruction& i) { HHIR_EMIT(InitThisLoc, i.imm[0].u_HA); } void -Translator::translateFPushFuncD(const NormalizedInstruction& i) { +IRTranslator::translateFPushFuncD(const NormalizedInstruction& i) { HHIR_EMIT(FPushFuncD, (i.imm[0].u_IVA), (i.imm[1].u_SA)); } void -Translator::translateFPushFuncU(const NormalizedInstruction& i) { +IRTranslator::translateFPushFuncU(const NormalizedInstruction& i) { HHIR_EMIT(FPushFuncU, i.imm[0].u_IVA, i.imm[1].u_SA, i.imm[2].u_SA); } void -Translator::translateFPassCOp(const NormalizedInstruction& i) { +IRTranslator::translateFPassCOp(const NormalizedInstruction& i) { auto const op = i.op(); if (i.preppedByRef && (op == OpFPassCW || op == OpFPassCE)) { // These cases might have to raise a warning or an error @@ -927,7 +969,7 @@ Translator::translateFPassCOp(const NormalizedInstruction& i) { } void -Translator::translateFPassV(const NormalizedInstruction& i) { +IRTranslator::translateFPassV(const NormalizedInstruction& i) { if (i.preppedByRef || i.noOp) { TRACE(1, "HHIR: translateFPassV: noOp\n"); return; @@ -936,7 +978,7 @@ Translator::translateFPassV(const NormalizedInstruction& i) { } void -Translator::translateFPushCufIter(const NormalizedInstruction& i) { +IRTranslator::translateFPushCufIter(const NormalizedInstruction& i) { HHIR_EMIT(FPushCufIter, i.imm[0].u_IVA, i.imm[1].u_IA); } @@ -1015,7 +1057,7 @@ findCuf(const NormalizedInstruction& ni, } void -Translator::translateFPushCufOp(const NormalizedInstruction& i) { +IRTranslator::translateFPushCufOp(const NormalizedInstruction& i) { Class* cls = nullptr; StringData* invName = nullptr; bool forward = false; @@ -1024,7 +1066,7 @@ Translator::translateFPushCufOp(const NormalizedInstruction& i) { } void -Translator::translateFPassR(const NormalizedInstruction& i) { +IRTranslator::translateFPassR(const NormalizedInstruction& i) { /* * Like FPassC, FPassR is able to cheat on boxing if the current * parameter is pass by reference but we have a cell: the box would refer @@ -1042,7 +1084,7 @@ Translator::translateFPassR(const NormalizedInstruction& i) { } void -Translator::translateFCallBuiltin(const NormalizedInstruction& i) { +IRTranslator::translateFCallBuiltin(const NormalizedInstruction& i) { int numArgs = i.imm[0].u_IVA; int numNonDefault = i.imm[1].u_IVA; Id funcId = i.imm[2].u_SA; @@ -1120,11 +1162,11 @@ bool shouldIRInline(const Func* curFunc, // Simple two-cell comparison operators. auto simpleCmp = [&]() -> bool { switch (current) { - case OpAdd: case OpSub: case OpMul: case OpDiv: case OpMod: - case OpXor: case OpNot: case OpSame: case OpNSame: case OpEq: - case OpNeq: case OpLt: case OpLte: case OpGt: case OpGte: - case OpBitAnd: case OpBitOr: case OpBitXor: case OpBitNot: - case OpShl: case OpShr: + case Op::Add: case Op::Sub: case Op::Mul: case Op::Div: case Op::Mod: + case Op::Xor: case Op::Not: case Op::Same: case Op::NSame: case Op::Eq: + case Op::Neq: case Op::Lt: case Op::Lte: case Op::Gt: case Op::Gte: + case Op::BitAnd: case Op::BitOr: case Op::BitXor: case Op::BitNot: + case Op::Shl: case Op::Shr: next(); return true; default: @@ -1136,7 +1178,7 @@ bool shouldIRInline(const Func* curFunc, // stack, this is used to allow simple constant-foldable // manipulations of it before return. auto cellManipRet = [&]() -> bool { - if (nextIf(OpNot)) return atRet(); + if (nextIf(Op::Not)) return atRet(); if (simpleCell() && simpleCmp()) return atRet(); return atRet(); }; @@ -1235,11 +1277,11 @@ bool shouldIRInline(const Func* curFunc, } void -Translator::translateFCall(const NormalizedInstruction& i) { +IRTranslator::translateFCall(const NormalizedInstruction& i) { auto const numArgs = i.imm[0].u_IVA; - always_assert(!m_hhbcTrans->isInlining() && "curUnit and curFunc calls"); - const Opcode* after = curUnit()->at(nextSrcKey(i).offset()); + always_assert(!m_hhbcTrans.isInlining() && "curUnit and curFunc calls"); + const PC after = curUnit()->at(nextSrcKey(i).offset()); const Func* srcFunc = curFunc(); Offset returnBcOffset = srcFunc->unit()->offsetOf(after - srcFunc->base()); @@ -1249,20 +1291,18 @@ Translator::translateFCall(const NormalizedInstruction& i) { * the call. */ if (i.calleeTrace) { - if (!i.calleeTrace->m_inliningFailed && !m_hhbcTrans->isInlining()) { + if (!i.calleeTrace->m_inliningFailed && !m_hhbcTrans.isInlining()) { assert(shouldIRInline(curFunc(), i.funcd, *i.calleeTrace)); - m_hhbcTrans->beginInlining(numArgs, i.funcd, returnBcOffset); + m_hhbcTrans.beginInlining(numArgs, i.funcd, returnBcOffset); static const bool shapeStats = Stats::enabledAny() && getenv("HHVM_STATS_INLINESHAPE"); if (shapeStats) { - m_hhbcTrans->profileInlineFunctionShape(traceletShape(*i.calleeTrace)); + m_hhbcTrans.profileInlineFunctionShape(traceletShape(*i.calleeTrace)); } for (auto* ni = i.calleeTrace->m_instrStream.first; - ni; ni = ni->next) { - m_curNI = ni; - SCOPE_EXIT { m_curNI = &i; }; + ni; ni = ni->next) { translateInstr(*ni); } return; @@ -1271,8 +1311,8 @@ Translator::translateFCall(const NormalizedInstruction& i) { static const auto enabled = Stats::enabledAny() && getenv("HHVM_STATS_FAILEDINL"); if (enabled) { - m_hhbcTrans->profileFunctionEntry("FailedCandidate"); - m_hhbcTrans->profileFailedInlShape(traceletShape(*i.calleeTrace)); + m_hhbcTrans.profileFunctionEntry("FailedCandidate"); + m_hhbcTrans.profileFailedInlShape(traceletShape(*i.calleeTrace)); } } @@ -1280,7 +1320,7 @@ Translator::translateFCall(const NormalizedInstruction& i) { } void -Translator::translateFCallArray(const NormalizedInstruction& i) { +IRTranslator::translateFCallArray(const NormalizedInstruction& i) { const Offset pcOffset = i.offset(); SrcKey next = nextSrcKey(i); const Offset after = next.offset(); @@ -1289,45 +1329,45 @@ Translator::translateFCallArray(const NormalizedInstruction& i) { } void -Translator::translateNewTuple(const NormalizedInstruction& i) { +IRTranslator::translateNewTuple(const NormalizedInstruction& i) { int numArgs = i.imm[0].u_IVA; HHIR_EMIT(NewTuple, numArgs); } void -Translator::translateNewCol(const NormalizedInstruction& i) { +IRTranslator::translateNewCol(const NormalizedInstruction& i) { HHIR_EMIT(NewCol, i.imm[0].u_IVA, i.imm[1].u_IVA); } void -Translator::translateColAddNewElemC(const NormalizedInstruction& i) { +IRTranslator::translateColAddNewElemC(const NormalizedInstruction& i) { HHIR_EMIT(ColAddNewElemC); } void -Translator::translateColAddElemC(const NormalizedInstruction& i) { +IRTranslator::translateColAddElemC(const NormalizedInstruction& i) { HHIR_EMIT(ColAddElemC); } void -Translator::translateStaticLocInit(const NormalizedInstruction& i) { +IRTranslator::translateStaticLocInit(const NormalizedInstruction& i) { HHIR_EMIT(StaticLocInit, i.imm[0].u_IVA, i.imm[1].u_SA); } // check class hierarchy and fail if no match void -Translator::translateVerifyParamType(const NormalizedInstruction& i) { +IRTranslator::translateVerifyParamType(const NormalizedInstruction& i) { int param = i.imm[0].u_IVA; HHIR_EMIT(VerifyParamType, param); } void -Translator::translateInstanceOfD(const NormalizedInstruction& i) { +IRTranslator::translateInstanceOfD(const NormalizedInstruction& i) { HHIR_EMIT(InstanceOfD, (i.imm[0].u_SA)); } void -Translator::translateIterInit(const NormalizedInstruction& i) { +IRTranslator::translateIterInit(const NormalizedInstruction& i) { HHIR_EMIT(IterInit, i.imm[0].u_IVA, i.offset() + i.imm[1].u_BA, @@ -1335,7 +1375,7 @@ Translator::translateIterInit(const NormalizedInstruction& i) { } void -Translator::translateIterInitK(const NormalizedInstruction& i) { +IRTranslator::translateIterInitK(const NormalizedInstruction& i) { HHIR_EMIT(IterInitK, i.imm[0].u_IVA, i.offset() + i.imm[1].u_BA, @@ -1344,7 +1384,7 @@ Translator::translateIterInitK(const NormalizedInstruction& i) { } void -Translator::translateIterNext(const NormalizedInstruction& i) { +IRTranslator::translateIterNext(const NormalizedInstruction& i) { HHIR_EMIT(IterNext, i.imm[0].u_IVA, @@ -1353,7 +1393,7 @@ Translator::translateIterNext(const NormalizedInstruction& i) { } void -Translator::translateIterNextK(const NormalizedInstruction& i) { +IRTranslator::translateIterNextK(const NormalizedInstruction& i) { HHIR_EMIT(IterNextK, i.imm[0].u_IVA, @@ -1363,7 +1403,7 @@ Translator::translateIterNextK(const NormalizedInstruction& i) { } void -Translator::translateMIterInit(const NormalizedInstruction& i) { +IRTranslator::translateMIterInit(const NormalizedInstruction& i) { HHIR_EMIT(MIterInit, i.imm[0].u_IVA, i.offset() + i.imm[1].u_BA, @@ -1371,7 +1411,7 @@ Translator::translateMIterInit(const NormalizedInstruction& i) { } void -Translator::translateMIterInitK(const NormalizedInstruction& i) { +IRTranslator::translateMIterInitK(const NormalizedInstruction& i) { HHIR_EMIT(MIterInitK, i.imm[0].u_IVA, i.offset() + i.imm[1].u_BA, @@ -1380,7 +1420,7 @@ Translator::translateMIterInitK(const NormalizedInstruction& i) { } void -Translator::translateMIterNext(const NormalizedInstruction& i) { +IRTranslator::translateMIterNext(const NormalizedInstruction& i) { HHIR_EMIT(MIterNext, i.imm[0].u_IVA, @@ -1389,7 +1429,7 @@ Translator::translateMIterNext(const NormalizedInstruction& i) { } void -Translator::translateMIterNextK(const NormalizedInstruction& i) { +IRTranslator::translateMIterNextK(const NormalizedInstruction& i) { HHIR_EMIT(MIterNextK, i.imm[0].u_IVA, @@ -1399,7 +1439,7 @@ Translator::translateMIterNextK(const NormalizedInstruction& i) { } void -Translator::translateWIterInit(const NormalizedInstruction& i) { +IRTranslator::translateWIterInit(const NormalizedInstruction& i) { HHIR_EMIT(WIterInit, i.imm[0].u_IVA, i.offset() + i.imm[1].u_BA, @@ -1407,7 +1447,7 @@ Translator::translateWIterInit(const NormalizedInstruction& i) { } void -Translator::translateWIterInitK(const NormalizedInstruction& i) { +IRTranslator::translateWIterInitK(const NormalizedInstruction& i) { HHIR_EMIT(WIterInitK, i.imm[0].u_IVA, i.offset() + i.imm[1].u_BA, @@ -1416,7 +1456,7 @@ Translator::translateWIterInitK(const NormalizedInstruction& i) { } void -Translator::translateWIterNext(const NormalizedInstruction& i) { +IRTranslator::translateWIterNext(const NormalizedInstruction& i) { HHIR_EMIT(WIterNext, i.imm[0].u_IVA, @@ -1425,7 +1465,7 @@ Translator::translateWIterNext(const NormalizedInstruction& i) { } void -Translator::translateWIterNextK(const NormalizedInstruction& i) { +IRTranslator::translateWIterNextK(const NormalizedInstruction& i) { HHIR_EMIT(WIterNextK, i.imm[0].u_IVA, @@ -1435,19 +1475,19 @@ Translator::translateWIterNextK(const NormalizedInstruction& i) { } void -Translator::translateIterFree(const NormalizedInstruction& i) { +IRTranslator::translateIterFree(const NormalizedInstruction& i) { HHIR_EMIT(IterFree, i.imm[0].u_IVA); } void -Translator::translateMIterFree(const NormalizedInstruction& i) { +IRTranslator::translateMIterFree(const NormalizedInstruction& i) { HHIR_EMIT(MIterFree, i.imm[0].u_IVA); } void -Translator::translateIterBreak(const NormalizedInstruction& i) { +IRTranslator::translateIterBreak(const NormalizedInstruction& i) { assert(i.breaksTracelet); HHIR_EMIT(IterBreak, i.immVec, i.offset() + i.imm[1].u_BA, i.breaksTracelet, @@ -1455,33 +1495,33 @@ Translator::translateIterBreak(const NormalizedInstruction& i) { } void -Translator::translateDecodeCufIter(const NormalizedInstruction& i) { +IRTranslator::translateDecodeCufIter(const NormalizedInstruction& i) { HHIR_EMIT(DecodeCufIter, i.imm[0].u_IVA, i.offset() + i.imm[1].u_BA); } void -Translator::translateCIterFree(const NormalizedInstruction& i) { +IRTranslator::translateCIterFree(const NormalizedInstruction& i) { HHIR_EMIT(CIterFree, i.imm[0].u_IVA); } // All vector instructions are handled by one HhbcTranslator method. #define MII(instr, ...) \ - void Translator::translate##instr##M(const NormalizedInstruction& ni) { \ - m_hhbcTrans->emitMInstr(ni); \ + void IRTranslator::translate##instr##M(const NormalizedInstruction& ni) { \ + m_hhbcTrans.emitMInstr(ni); \ } MINSTRS MII(FPass) #undef MII void -Translator::translateInstrWork(const NormalizedInstruction& i) { +IRTranslator::translateInstrWork(const NormalizedInstruction& i) { auto const op = i.op(); switch (op) { #define CASE(iNm) \ - case Op ## iNm: \ + case Op::iNm: \ translate ## iNm(i); \ break; #define TRANSLATE(name, inst) translate ## name(inst); break; @@ -1499,7 +1539,7 @@ static bool isPop(Op opc) { } void -Translator::passPredictedAndInferredTypes(const NormalizedInstruction& i) { +IRTranslator::passPredictedAndInferredTypes(const NormalizedInstruction& i) { if (!i.outStack || i.breaksTracelet) return; NormalizedInstruction::OutputUse u = i.getOutputUsage(i.outStack); @@ -1508,7 +1548,7 @@ Translator::passPredictedAndInferredTypes(const NormalizedInstruction& i) { if (u == NormalizedInstruction::OutputUse::Inferred) { TRACE(1, "irPassPredictedAndInferredTypes: output inferred as %s\n", jitType.toString().c_str()); - m_hhbcTrans->assertTypeStack(0, jitType); + m_hhbcTrans.assertTypeStack(0, jitType); } else if (u == NormalizedInstruction::OutputUse::Used && i.outputPredicted) { // If the value was predicted statically by the front-end, it @@ -1521,11 +1561,11 @@ Translator::passPredictedAndInferredTypes(const NormalizedInstruction& i) { !jitType.isCounted()) { TRACE(1, "irPassPredictedAndInferredTypes: output inferred as %s\n", jitType.toString().c_str()); - m_hhbcTrans->assertTypeStack(0, JIT::Type::Uncounted); + m_hhbcTrans.assertTypeStack(0, JIT::Type::Uncounted); } else { TRACE(1, "irPassPredictedAndInferredTypes: output predicted as %s\n", jitType.toString().c_str()); - m_hhbcTrans->checkTypeTopOfStack(jitType, i.next->offset()); + m_hhbcTrans.checkTypeTopOfStack(jitType, i.next->offset()); } } } @@ -1541,39 +1581,39 @@ static int getNumPopped(const NormalizedInstruction& i) { + (pushesActRec(i.op()) ? kNumActRecCells : 0); } -void Translator::interpretInstr(const NormalizedInstruction& i) { +void IRTranslator::interpretInstr(const NormalizedInstruction& i) { int poppedCells = getNumPopped(i); FTRACE(5, "HHIR: BC Instr {} Popped = {}\n", i.toString(), poppedCells); if (i.changesPC) { - m_hhbcTrans->emitInterpOneCF(poppedCells); + m_hhbcTrans.emitInterpOneCF(poppedCells); } else { - m_hhbcTrans->emitInterpOne(i); + m_hhbcTrans.emitInterpOne(i); } } -void Translator::translateInstr(const NormalizedInstruction& i) { +void IRTranslator::translateInstr(const NormalizedInstruction& i) { FTRACE(1, "\n{:-^60}\n", folly::format("translating {} with stack:\n{}", i.toString(), - m_hhbcTrans->showStack())); + m_hhbcTrans.showStack())); - m_hhbcTrans->setBcOff(i.source.offset(), - i.breaksTracelet && !m_hhbcTrans->isInlining()); + m_hhbcTrans.setBcOff(i.source.offset(), + i.breaksTracelet && !m_hhbcTrans.isInlining()); if (i.guardedThis) { // Task #2067635: This should really generate an AssertThis - m_hhbcTrans->setThisAvailable(); + m_hhbcTrans.setThisAvailable(); } if (moduleEnabled(HPHP::Trace::stats, 2)) { - m_hhbcTrans->emitIncStat(Stats::opcodeToIRPreStatCounter(i.op()), 1); + m_hhbcTrans.emitIncStat(Stats::opcodeToIRPreStatCounter(i.op()), 1); } if (RuntimeOption::EnableInstructionCounts || moduleEnabled(HPHP::Trace::stats, 3)) { // If the instruction takes a slow exit, the exit trace will // decrement the post counter for that opcode. - m_hhbcTrans->emitIncStat(Stats::opcodeToIRPostStatCounter(i.op()), + m_hhbcTrans.emitIncStat(Stats::opcodeToIRPostStatCounter(i.op()), 1, true); } @@ -1586,230 +1626,4 @@ void Translator::translateInstr(const NormalizedInstruction& i) { passPredictedAndInferredTypes(i); } -void TranslatorX64::irAssertType(const Location& l, - const RuntimeType& rtt) { - if (rtt.isVagueValue()) return; - - switch (l.space) { - case Location::Stack: { - // tx64LocPhysicalOffset returns positive offsets for stack values, - // relative to rVmSp - uint32_t stackOffset = locPhysicalOffset(l); - m_hhbcTrans->assertTypeStack(stackOffset, - JIT::Type::fromRuntimeType(rtt)); - break; - } - case Location::Local: // Stack frame's registers; offset == local register - m_hhbcTrans->assertTypeLocal(l.offset, JIT::Type::fromRuntimeType(rtt)); - break; - - case Location::Invalid: // Unknown location - HHIR_UNIMPLEMENTED(Invalid); - break; - - case Location::Iter: // Stack frame's iterators - HHIR_UNIMPLEMENTED(AssertType_Iter); - break; - - case Location::Litstr: // Literal string pseudo-location - HHIR_UNIMPLEMENTED(AssertType_Litstr); - break; - - case Location::Litint: // Literal int pseudo-location - HHIR_UNIMPLEMENTED(AssertType_Litint); - break; - - case Location::This: - HHIR_UNIMPLEMENTED(AssertType_This); - break; - } -} - -void TranslatorX64::irEmitResolvedDeps(const ChangeMap& resolvedDeps) { - for (const auto dep : resolvedDeps) { - irAssertType(dep.first, dep.second->rtt); - } -} - -TranslatorX64::TranslateResult -TranslatorX64::irTranslateTracelet(Tracelet& t) { - FTRACE(2, "attempting to translate tracelet:\n{}\n", t.toString()); - const SrcKey &sk = t.m_sk; - SrcRec& srcRec = *getSrcRec(sk); - assert(srcRec.inProgressTailJumps().size() == 0); - try { - irEmitResolvedDeps(t.m_resolvedDeps); - emitGuardChecks(a, sk, t.m_dependencies, t.m_refDeps, srcRec); - - dumpTranslationInfo(t, a.frontier()); - - // after guards, add a counter for the translation if requested - if (RuntimeOption::EvalJitTransCounters) { - m_hhbcTrans->emitIncTransCounter(); - } - - emitRB(a, RBTypeTraceletBody, t.m_sk); - Stats::emitInc(a, Stats::Instr_TC, t.m_numOpcodes); - - // Profiling on function entry. - if (m_curTrace->m_sk.offset() == curFunc()->base()) { - m_hhbcTrans->profileFunctionEntry("Normal"); - } - - /* - * Profiling on the shapes of tracelets that are whole functions. - * (These are the things we might consider trying to support - * inlining.) - */ - [&]{ - static const bool enabled = Stats::enabledAny() && - getenv("HHVM_STATS_FUNCSHAPE"); - if (!enabled) return; - if (m_curTrace->m_sk.offset() != curFunc()->base()) return; - if (auto last = m_curTrace->m_instrStream.last) { - if (last->op() != OpRetC && last->op() != OpRetV) { - return; - } - } - m_hhbcTrans->profileSmallFunctionShape(traceletShape(*m_curTrace)); - }(); - - // Translate each instruction in the tracelet - for (auto* ni = t.m_instrStream.first; ni && !m_hhbcTrans->hasExit(); - ni = ni->next) { - try { - SKTRACE(1, ni->source, "HHIR: translateInstr\n"); - Nuller niNuller(&m_curNI); - m_curNI = ni; - translateInstr(*ni); - } catch (JIT::FailedIRGen& fcg) { - always_assert(!ni->interp); - ni->interp = true; - return Retry; - } - assert(ni->source.offset() >= curFunc()->base()); - // We sometimes leave the tail of a truncated tracelet in place to aid - // analysis, but breaksTracelet is authoritative. - if (ni->breaksTracelet) break; - } - traceEnd(); - - try { - traceCodeGen(); - TRACE(1, "HHIR: SUCCEEDED to generate code for Translation %d\n\n\n", - getCurrentTransID()); - return Success; - } catch (JIT::FailedCodeGen& fcg) { - // Code-gen failed. Search for the bytecode instruction that caused the - // problem, flag it to be interpreted, and retranslate the tracelet. - for (auto ni = t.m_instrStream.first; ni; ni = ni->next) { - if (ni->source.offset() == fcg.bcOff) { - always_assert(!ni->interp); - ni->interp = true; - TRACE(1, "HHIR: RETRY Translation %d: will interpOne BC instr %s " - "after failing to code-gen \n\n", - getCurrentTransID(), ni->toString().c_str()); - return Retry; - } - } - throw fcg; - } - } catch (JIT::FailedCodeGen& fcg) { - TRACE(1, "HHIR: FAILED to generate code for Translation %d " - "@ %s:%d (%s)\n", getCurrentTransID(), - fcg.file, fcg.line, fcg.func); - // HHIR:TODO Remove extra TRACE and adjust tools - TRACE(1, "HHIR: FAILED to translate @ %s:%d (%s)\n", - fcg.file, fcg.line, fcg.func); - } catch (JIT::FailedIRGen& x) { - TRACE(1, "HHIR: FAILED to translate @ %s:%d (%s)\n", - x.file, x.line, x.func); - } catch (const FailedAssertion& fa) { - fa.print(); - StackTraceNoHeap::AddExtraLogging( - "Assertion failure", - folly::format("{}\n\nActive Trace:\n{}\n", - fa.summary, m_hhbcTrans->trace()->toString()).str()); - abort(); - } catch (const std::exception& e) { - FTRACE(1, "HHIR: FAILED with exception: {}\n", e.what()); - assert(0); - } - return Failure; -} - -void Translator::traceStart(Offset bcStartOffset) { - assert(!m_irFactory); - - Cell* fp = vmfp(); - if (curFunc()->isGenerator()) { - fp = (Cell*)Stack::generatorStackBase((ActRec*)fp); - } - FTRACE(1, "{}{:-^40}{}\n", - color(ANSI_COLOR_BLACK, ANSI_BGCOLOR_GREEN), - " HHIR during translation ", - color(ANSI_COLOR_END)); - - m_irFactory.reset(new JIT::IRFactory()); - m_hhbcTrans.reset(new JIT::HhbcTranslator( - *m_irFactory, bcStartOffset, fp - vmsp(), curFunc())); -} - -void Translator::traceEnd() { - m_hhbcTrans->end(); - FTRACE(1, "{}{:-^40}{}\n", - color(ANSI_COLOR_BLACK, ANSI_BGCOLOR_GREEN), - "", - color(ANSI_COLOR_END)); -} - -void TranslatorX64::traceCodeGen() { - using namespace JIT; - - HPHP::JIT::IRTrace* trace = m_hhbcTrans->trace(); - auto finishPass = [&](const char* msg, int level, - const RegAllocInfo* regs, - const LifetimeInfo* lifetime) { - dumpTrace(level, trace, msg, regs, lifetime); - assert(checkCfg(trace, *m_irFactory)); - }; - - finishPass(" after initial translation ", kIRLevel, nullptr, nullptr); - optimizeTrace(trace, m_hhbcTrans->traceBuilder()); - finishPass(" after optimizing ", kOptLevel, nullptr, nullptr); - - auto* factory = m_irFactory.get(); - recordBCInstr(OpTraceletGuard, a, a.frontier()); - if (dumpIREnabled() || RuntimeOption::EvalJitCompareHHIR) { - LifetimeInfo lifetime(factory); - RegAllocInfo regs = allocRegsForTrace(trace, factory, &lifetime); - finishPass(" after reg alloc ", kRegAllocLevel, ®s, &lifetime); - assert(checkRegisters(trace, *factory, regs)); - AsmInfo ai(factory); - genCodeForTrace(trace, a, astubs, factory, &m_bcMap, this, regs, - &lifetime, &ai); - if (RuntimeOption::EvalJitCompareHHIR) { - std::ostringstream out; - dumpTraceImpl(trace, out, ®s, &lifetime, &ai); - } else { - dumpTrace(kCodeGenLevel, trace, " after code gen ", ®s, - &lifetime, &ai); - } - } else { - RegAllocInfo regs = allocRegsForTrace(trace, factory); - finishPass(" after reg alloc ", kRegAllocLevel, nullptr, nullptr); - assert(checkRegisters(trace, *factory, regs)); - genCodeForTrace(trace, a, astubs, factory, &m_bcMap, this, regs); - } - - m_numHHIRTrans++; -} - -void Translator::traceFree() { - FTRACE(1, "HHIR free: arena size: {}\n", - m_irFactory->arena().size()); - m_hhbcTrans.reset(); - m_irFactory.reset(); -} - }} diff --git a/hphp/runtime/vm/jit/ir-translator.h b/hphp/runtime/vm/jit/ir-translator.h new file mode 100644 index 000000000..9cb487e58 --- /dev/null +++ b/hphp/runtime/vm/jit/ir-translator.h @@ -0,0 +1,62 @@ +/* + +----------------------------------------------------------------------+ + | HipHop for PHP | + +----------------------------------------------------------------------+ + | Copyright (c) 2010-2013 Facebook, Inc. (http://www.facebook.com) | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ +*/ +#ifndef incl_HPHP_IRTRANSLATOR_H_ +#define incl_HPHP_IRTRANSLATOR_H_ + +#include "hphp/runtime/vm/jit/hhbc-translator.h" +#include "hphp/runtime/vm/jit/translator-instrs.h" + +namespace HPHP { +namespace Transl { +struct NormalizedInstruction; +struct Tracelet; +struct Location; +struct RuntimeType; +} +namespace JIT { +using Transl::NormalizedInstruction; + +bool shouldIRInline(const Func* curFunc, const Func* func, + const Transl::Tracelet& callee); + +/* + * IRTranslator is used to convert hhbc instructions to an IRTrace of hhir + * instructions. It uses an HhbcTranslator to do the actual translation, + * driving it with a translate method for each supported bytecode. + */ +struct IRTranslator { + IRTranslator(Offset bcOff, Offset spOff, const Func* curFunc); + + void translateInstr(const NormalizedInstruction& i); + void checkType(const Transl::Location& l, const Transl::RuntimeType& rtt); + void assertType(const Transl::Location&, const Transl::RuntimeType&); + HhbcTranslator& hhbcTrans() { return m_hhbcTrans; } + + private: + void translateInstrWork(const NormalizedInstruction& i); + void interpretInstr(const NormalizedInstruction& i); + void passPredictedAndInferredTypes(const NormalizedInstruction& i); +# define CASE(nm) void translate ## nm(const NormalizedInstruction& i); +INSTRS +PSEUDOINSTRS +# undef CASE + + HhbcTranslator m_hhbcTrans; +}; + +} } + +#endif diff --git a/hphp/runtime/vm/jit/ir.cpp b/hphp/runtime/vm/jit/ir.cpp index 0524f6dd0..058273691 100644 --- a/hphp/runtime/vm/jit/ir.cpp +++ b/hphp/runtime/vm/jit/ir.cpp @@ -307,9 +307,7 @@ bool BCMarker::valid() const { return func != nullptr && bcOff >= func->base() && bcOff < func->past() && - spOff <= func->numLocals() + - func->numIterators() * kNumIterCells + - func->maxStackCells(); + spOff <= func->numSlotsInFrame() + func->maxStackCells(); } diff --git a/hphp/runtime/vm/jit/ir.h b/hphp/runtime/vm/jit/ir.h index eb8dd2145..50bb56eee 100644 --- a/hphp/runtime/vm/jit/ir.h +++ b/hphp/runtime/vm/jit/ir.h @@ -202,6 +202,7 @@ O(GuardRefs, ND, S(Func) \ S(Int), E) \ O(AssertLoc, ND, S(FramePtr), E) \ O(OverrideLoc, ND, S(FramePtr), E) \ +O(OverrideLocVal, ND, S(FramePtr) S(Gen), E) \ O(SmashLocals, ND, S(FramePtr), E) \ O(BeginCatch, ND, NA, E|Mem) \ O(EndCatch, ND, S(StkPtr), E|Mem) \ @@ -1000,11 +1001,6 @@ struct CatchInfo { typedef folly::Range TcaRange; -/** - * Run all optimization passes on this trace - */ -void optimizeTrace(IRTrace*, IRFactory* irFactory); - /* * Counts the number of cells a SpillStack will logically push. (Not * including the number it pops.) That is, for each SSATmp in the diff --git a/hphp/runtime/vm/jit/runtime-type.h b/hphp/runtime/vm/jit/runtime-type.h index 43cacf24c..01bd5a675 100644 --- a/hphp/runtime/vm/jit/runtime-type.h +++ b/hphp/runtime/vm/jit/runtime-type.h @@ -18,6 +18,7 @@ #define incl_HPHP_RUNTIME_TYPE_H_ #include "hphp/runtime/vm/bytecode.h" +#include "hphp/runtime/vm/jit/region-selection.h" namespace HPHP { namespace Transl { @@ -120,6 +121,15 @@ struct Location { return space == Iter; } + JIT::RegionDesc::Location toLocation() const { + typedef JIT::RegionDesc::Location L; + switch (space) { + case Stack: return L::Stack{safe_cast(offset)}; + case Local: return L::Local{safe_cast(offset)}; + default: not_reached(); + } + } + public: Space space; int64_t offset; diff --git a/hphp/runtime/vm/jit/trace-builder.cpp b/hphp/runtime/vm/jit/trace-builder.cpp index 9a8664d8d..dae65e0a1 100644 --- a/hphp/runtime/vm/jit/trace-builder.cpp +++ b/hphp/runtime/vm/jit/trace-builder.cpp @@ -291,6 +291,10 @@ void TraceBuilder::updateTrackedState(IRInstruction* inst) { inst->typeParam()); break; + case OverrideLocVal: + setLocalValue(inst->extra()->locId, inst->src(1)); + break; + case SmashLocals: clearLocals(); break; diff --git a/hphp/runtime/vm/jit/translator-inline.h b/hphp/runtime/vm/jit/translator-inline.h index a5d0958c8..0eb10c4e4 100644 --- a/hphp/runtime/vm/jit/translator-inline.h +++ b/hphp/runtime/vm/jit/translator-inline.h @@ -53,6 +53,13 @@ inline Class* curClass() { } return cls; } +inline Offset curSpOff() { + Cell* fp = vmfp(); + if (curFunc()->isGenerator()) { + fp = (Cell*)Stack::generatorStackBase((ActRec*)fp); + } + return fp - vmsp(); +} namespace Transl { diff --git a/hphp/runtime/vm/jit/translator-runtime.cpp b/hphp/runtime/vm/jit/translator-runtime.cpp index 8e24170f8..f5fa4b5ec 100644 --- a/hphp/runtime/vm/jit/translator-runtime.cpp +++ b/hphp/runtime/vm/jit/translator-runtime.cpp @@ -19,6 +19,7 @@ #include "hphp/runtime/ext/ext_function.h" #include "hphp/runtime/vm/member_operations.h" #include "hphp/runtime/vm/type_constraint.h" +#include "hphp/runtime/vm/jit/translator-inline.h" namespace HPHP { namespace Transl { diff --git a/hphp/runtime/vm/jit/translator-x64-internal.h b/hphp/runtime/vm/jit/translator-x64-internal.h index ad26f2bf2..26b94c133 100644 --- a/hphp/runtime/vm/jit/translator-x64-internal.h +++ b/hphp/runtime/vm/jit/translator-x64-internal.h @@ -224,7 +224,7 @@ locToRegDisp(const Location& l, PhysReg *outbase, int *outdisp, assert_not_implemented((l.space == Location::Stack || l.space == Location::Local || l.space == Location::Iter)); - *outdisp = cellsToBytes(Translator::locPhysicalOffset(l, f)); + *outdisp = cellsToBytes(locPhysicalOffset(l, f)); *outbase = l.space == Location::Stack ? rVmSp : rVmFp; } diff --git a/hphp/runtime/vm/jit/translator-x64.cpp b/hphp/runtime/vm/jit/translator-x64.cpp index 39afe6d34..0b26edb60 100644 --- a/hphp/runtime/vm/jit/translator-x64.cpp +++ b/hphp/runtime/vm/jit/translator-x64.cpp @@ -77,11 +77,6 @@ #include "hphp/runtime/ext/ext_continuation.h" #include "hphp/runtime/ext/ext_function.h" #include "hphp/runtime/vm/debug/debug.h" -#include "hphp/runtime/vm/jit/target-cache.h" -#include "hphp/runtime/vm/jit/translator-inline.h" -#include "hphp/runtime/vm/jit/srcdb.h" -#include "hphp/runtime/vm/jit/x64-util.h" -#include "hphp/runtime/vm/jit/unwind-x64.h" #include "hphp/runtime/base/stats.h" #include "hphp/runtime/vm/pendq.h" #include "hphp/runtime/vm/treadmill.h" @@ -89,8 +84,18 @@ #include "hphp/runtime/vm/type_profile.h" #include "hphp/runtime/vm/member_operations.h" #include "hphp/runtime/vm/jit/abi-x64.h" +#include "hphp/runtime/vm/jit/check.h" +#include "hphp/runtime/vm/jit/code-gen.h" #include "hphp/runtime/vm/jit/hhbc-translator.h" +#include "hphp/runtime/vm/jit/ir-translator.h" +#include "hphp/runtime/vm/jit/opt.h" +#include "hphp/runtime/vm/jit/print.h" #include "hphp/runtime/vm/jit/region-selection.h" +#include "hphp/runtime/vm/jit/srcdb.h" +#include "hphp/runtime/vm/jit/target-cache.h" +#include "hphp/runtime/vm/jit/translator-inline.h" +#include "hphp/runtime/vm/jit/unwind-x64.h" +#include "hphp/runtime/vm/jit/x64-util.h" #include "hphp/runtime/vm/jit/translator-x64-internal.h" @@ -2017,16 +2022,10 @@ TranslatorX64::emitBindJmp(SrcKey dest) { emitBindJmp(a, dest); } -void -TranslatorX64::checkType(X64Assembler& a, - const Location& l, - const RuntimeType& rtt, - SrcRec& fail) { - // We can get invalid inputs as a side effect of reading invalid - // items out of BBs we truncate; they don't need guards. - if (rtt.isVagueValue() || l.isThis()) return; - - irCheckType(a, l, rtt, fail); +void TranslatorX64::emitResolvedDeps(const ChangeMap& resolvedDeps) { + for (const auto dep : resolvedDeps) { + m_irTrans->assertType(dep.first, dep.second->rtt); + } } void @@ -2091,9 +2090,9 @@ TranslatorX64::checkRefs(X64Assembler& a, int entryArDelta = it->first; - m_hhbcTrans->guardRefs(entryArDelta, - it->second.m_mask, - it->second.m_vals); + m_irTrans->hhbcTrans().guardRefs(entryArDelta, + it->second.m_mask, + it->second.m_vals); } } @@ -3031,22 +3030,6 @@ int64_t switchObjHelper(ObjectData* o, int64_t base, int64_t nTargets) { return switchBoundsCheck(ival, base, nTargets); } -bool -TranslatorX64::dontGuardAnyInputs(Op op) { - switch (op) { -#define CASE(iNm) case Op ## iNm: -#define NOOP(...) - INSTRS - PSEUDOINSTR_DISPATCH(NOOP) - return false; - - default: - return true; - } -#undef NOOP -#undef CASE -} - bool TranslatorX64::checkTranslationLimit(SrcKey sk, const SrcRec& srcRec) const { @@ -3101,7 +3084,7 @@ TranslatorX64::emitGuardChecks(X64Assembler& a, dep != dependencies.end(); ++dep) { if (!pseudoMain || !dep->second->isLocal() || !dep->second->isValue()) { - checkType(a, dep->first, dep->second->rtt, fail); + m_irTrans->checkType(dep->first, dep->second->rtt); } else { TRACE(3, "Skipping tracelet guard for %s %d\n", dep->second->location.pretty().c_str(), @@ -3171,8 +3154,6 @@ TranslatorX64::translateWork(const TranslArgs& args) { auto sk = args.m_sk; std::unique_ptr tp = analyze(sk); Tracelet& t = *tp; - m_curTrace = &t; - Nuller ctNuller(&m_curTrace); SKTRACE(1, sk, "translateWork\n"); assert(m_srcDB.find(sk)); @@ -3235,7 +3216,7 @@ TranslatorX64::translateWork(const TranslArgs& args) { if (!region || result == Failure) { FTRACE(1, "trying irTranslateTracelet\n"); assertCleanState(); - result = irTranslateTracelet(*tp); + result = translateTracelet(*tp); DEBUG_ONLY static const bool reqRegion = getenv("HHVM_REQUIRE_REGION"); assert(IMPLIES(region && reqRegion, result != Success)); } @@ -3298,6 +3279,156 @@ TranslatorX64::translateWork(const TranslArgs& args) { } } +TranslatorX64::TranslateResult +TranslatorX64::translateTracelet(Tracelet& t) { + FTRACE(2, "attempting to translate tracelet:\n{}\n", t.toString()); + const SrcKey &sk = t.m_sk; + SrcRec& srcRec = *getSrcRec(sk); + HhbcTranslator& ht = m_irTrans->hhbcTrans(); + + assert(srcRec.inProgressTailJumps().size() == 0); + try { + emitResolvedDeps(t.m_resolvedDeps); + emitGuardChecks(a, sk, t.m_dependencies, t.m_refDeps, srcRec); + + dumpTranslationInfo(t, a.frontier()); + + // after guards, add a counter for the translation if requested + if (RuntimeOption::EvalJitTransCounters) { + ht.emitIncTransCounter(); + } + + emitRB(a, RBTypeTraceletBody, t.m_sk); + Stats::emitInc(a, Stats::Instr_TC, t.m_numOpcodes); + + // Profiling on function entry. + if (t.m_sk.offset() == curFunc()->base()) { + ht.profileFunctionEntry("Normal"); + } + + /* + * Profiling on the shapes of tracelets that are whole functions. + * (These are the things we might consider trying to support + * inlining.) + */ + [&]{ + static const bool enabled = Stats::enabledAny() && + getenv("HHVM_STATS_FUNCSHAPE"); + if (!enabled) return; + if (t.m_sk.offset() != curFunc()->base()) return; + if (auto last = t.m_instrStream.last) { + if (last->op() != OpRetC && last->op() != OpRetV) { + return; + } + } + ht.profileSmallFunctionShape(traceletShape(t)); + }(); + + // Translate each instruction in the tracelet + for (auto* ni = t.m_instrStream.first; ni && !ht.hasExit(); + ni = ni->next) { + try { + SKTRACE(1, ni->source, "HHIR: translateInstr\n"); + m_irTrans->translateInstr(*ni); + } catch (JIT::FailedIRGen& fcg) { + always_assert(!ni->interp); + ni->interp = true; + return Retry; + } + assert(ni->source.offset() >= curFunc()->base()); + // We sometimes leave the tail of a truncated tracelet in place to aid + // analysis, but breaksTracelet is authoritative. + if (ni->breaksTracelet) break; + } + traceEnd(); + + try { + traceCodeGen(); + TRACE(1, "HHIR: SUCCEEDED to generate code for Translation %d\n\n\n", + getCurrentTransID()); + return Success; + } catch (JIT::FailedCodeGen& fcg) { + // Code-gen failed. Search for the bytecode instruction that caused the + // problem, flag it to be interpreted, and retranslate the tracelet. + for (auto ni = t.m_instrStream.first; ni; ni = ni->next) { + if (ni->source.offset() == fcg.bcOff) { + always_assert(!ni->interp); + ni->interp = true; + TRACE(1, "HHIR: RETRY Translation %d: will interpOne BC instr %s " + "after failing to code-gen \n\n", + getCurrentTransID(), ni->toString().c_str()); + return Retry; + } + } + throw fcg; + } + } catch (JIT::FailedCodeGen& fcg) { + TRACE(1, "HHIR: FAILED to generate code for Translation %d " + "@ %s:%d (%s)\n", getCurrentTransID(), + fcg.file, fcg.line, fcg.func); + // HHIR:TODO Remove extra TRACE and adjust tools + TRACE(1, "HHIR: FAILED to translate @ %s:%d (%s)\n", + fcg.file, fcg.line, fcg.func); + } catch (JIT::FailedIRGen& x) { + TRACE(1, "HHIR: FAILED to translate @ %s:%d (%s)\n", + x.file, x.line, x.func); + } catch (const FailedAssertion& fa) { + fa.print(); + StackTraceNoHeap::AddExtraLogging( + "Assertion failure", + folly::format("{}\n\nActive Trace:\n{}\n", + fa.summary, ht.trace()->toString()).str()); + abort(); + } catch (const std::exception& e) { + FTRACE(1, "HHIR: FAILED with exception: {}\n", e.what()); + assert(0); + } + return Failure; +} + +void TranslatorX64::traceCodeGen() { + using namespace JIT; + + HhbcTranslator& ht = m_irTrans->hhbcTrans(); + HPHP::JIT::IRTrace* trace = ht.trace(); + auto finishPass = [&](const char* msg, int level, + const RegAllocInfo* regs, + const LifetimeInfo* lifetime) { + dumpTrace(level, trace, msg, regs, lifetime); + assert(checkCfg(trace, ht.irFactory())); + }; + + finishPass(" after initial translation ", kIRLevel, nullptr, nullptr); + optimizeTrace(trace, ht.traceBuilder()); + finishPass(" after optimizing ", kOptLevel, nullptr, nullptr); + + auto* factory = &ht.irFactory(); + recordBCInstr(OpTraceletGuard, a, a.frontier()); + if (dumpIREnabled() || RuntimeOption::EvalJitCompareHHIR) { + LifetimeInfo lifetime(factory); + RegAllocInfo regs = allocRegsForTrace(trace, factory, &lifetime); + finishPass(" after reg alloc ", kRegAllocLevel, ®s, &lifetime); + assert(checkRegisters(trace, *factory, regs)); + AsmInfo ai(factory); + genCodeForTrace(trace, a, astubs, factory, &m_bcMap, this, regs, + &lifetime, &ai); + if (RuntimeOption::EvalJitCompareHHIR) { + std::ostringstream out; + dumpTraceImpl(trace, out, ®s, &lifetime, &ai); + } else { + dumpTrace(kCodeGenLevel, trace, " after code gen ", ®s, + &lifetime, &ai); + } + } else { + RegAllocInfo regs = allocRegsForTrace(trace, factory); + finishPass(" after reg alloc ", kRegAllocLevel, nullptr, nullptr); + assert(checkRegisters(trace, *factory, regs)); + genCodeForTrace(trace, a, astubs, factory, &m_bcMap, this, regs); + } + + m_numHHIRTrans++; +} + /* * Defines functions called by emitGenericReturn, and * cgGenericRetDecRefs. diff --git a/hphp/runtime/vm/jit/translator-x64.h b/hphp/runtime/vm/jit/translator-x64.h index 51f5cf9c4..0f5baa41a 100644 --- a/hphp/runtime/vm/jit/translator-x64.h +++ b/hphp/runtime/vm/jit/translator-x64.h @@ -284,7 +284,6 @@ private: static uint64_t toStringHelper(ObjectData *obj); void invalidateSrcKey(SrcKey sk); - bool dontGuardAnyInputs(Op op); public: template void invalidateSrcKeys(const T& keys) { @@ -352,7 +351,7 @@ public: void emitGuardChecks(Asm& a, SrcKey, const ChangeMap&, const RefDeps&, SrcRec&); - void irEmitResolvedDeps(const ChangeMap& resolvedDeps); + void emitResolvedDeps(const ChangeMap& resolvedDeps); Debug::DebugInfo* getDebugInfo() { return &m_debugInfo; } @@ -365,13 +364,7 @@ public: bool freeRequestStub(TCA stub); TCA getFreeStub(); bool checkTranslationLimit(SrcKey, const SrcRec&) const; - TranslateResult irTranslateTracelet(Tracelet& t); - - void irAssertType(const Location& l, const RuntimeType& rtt); - void checkType(Asm&, const Location& l, const RuntimeType& rtt, - SrcRec& fail); - void irCheckType(Asm&, const Location& l, const RuntimeType& rtt, - SrcRec& fail); + TranslateResult translateTracelet(Tracelet& t); void checkRefs(Asm&, SrcKey, const RefDeps&, SrcRec&); diff --git a/hphp/runtime/vm/jit/translator.cpp b/hphp/runtime/vm/jit/translator.cpp index 5838cfa0c..1bf899194 100644 --- a/hphp/runtime/vm/jit/translator.cpp +++ b/hphp/runtime/vm/jit/translator.cpp @@ -42,6 +42,7 @@ #include "hphp/runtime/vm/jit/annotation.h" #include "hphp/runtime/vm/jit/hhbc-translator.h" #include "hphp/runtime/vm/jit/ir-factory.h" +#include "hphp/runtime/vm/jit/ir-translator.h" #include "hphp/runtime/vm/jit/region-selection.h" #include "hphp/runtime/vm/jit/target-cache.h" #include "hphp/runtime/vm/jit/translator-inline.h" @@ -211,8 +212,7 @@ SrcKey Tracelet::nextSk() const { * to skip for iterators; if the current frame pointer is not the context * you're looking for, be sure to pass in a non-default f. */ -int -Translator::locPhysicalOffset(Location l, const Func* f) { +int locPhysicalOffset(Location l, const Func* f) { f = f ? f : curFunc(); assert_not_implemented(l.space == Location::Stack || l.space == Location::Local || @@ -1409,6 +1409,24 @@ static NormalizedInstruction* findInputSrc(NormalizedInstruction* ni, return ni; } +bool outputIsPredicted(SrcKey startSk, + NormalizedInstruction& inst) { + auto const& iInfo = getInstrInfo(inst.op()); + auto doPrediction = iInfo.type == OutPred && !inst.breaksTracelet; + if (doPrediction) { + // All OutPred ops have a single stack output for now. + assert(iInfo.out == Stack1); + auto dt = predictOutputs(startSk, &inst); + if (dt != KindOfInvalid) { + inst.outPred = Type::fromDataType(dt); + } else { + doPrediction = false; + } + } + + return doPrediction; +} + /* * For MetaData information that affects whether we want to even put a * value in the ni->inputs, we need to look at it before we call @@ -1417,8 +1435,8 @@ static NormalizedInstruction* findInputSrc(NormalizedInstruction* ni, * We also check GuardedThis here, since RetC is short-circuited in * applyInputMetaData. */ -void Translator::preInputApplyMetaData(Unit::MetaHandle metaHand, - NormalizedInstruction* ni) { +void preInputApplyMetaData(Unit::MetaHandle metaHand, + NormalizedInstruction* ni) { if (!metaHand.findMeta(ni->unit(), ni->offset())) return; Unit::MetaInfo info; @@ -1754,8 +1772,21 @@ static void addMVectorInputs(NormalizedInstruction& ni, "inputs, %d locals\n", stackCount, localCount); } +void getInputs(SrcKey startSk, NormalizedInstruction& inst, InputInfos& infos, + const LocalTypeFn& localType) { + // TranslatorX64 expected top of stack to be index -1, with indexes growing + // down from there. hhir defines top of stack to be index 0, with indexes + // growing up from there. To compensate we start with a stack offset of 1 and + // negate the index of any stack input after the call to getInputs. + int stackOff = 1; + getInputsImpl(startSk, &inst, stackOff, infos, localType); + for (auto& info : infos) { + if (info.loc.isStack()) info.loc.offset = -info.loc.offset; + } +} + /* - * getInputs -- + * getInputsImpl -- * Returns locations for this instruction's inputs. * * Throws: @@ -1768,11 +1799,11 @@ static void addMVectorInputs(NormalizedInstruction& ni, * Truncate the tracelet at the preceding instruction, which must * exists because *something* modified something in it. */ -void Translator::getInputs(SrcKey startSk, - NormalizedInstruction* ni, - int& currentStackOffset, - InputInfos& inputs, - std::function localType) { +void getInputsImpl(SrcKey startSk, + NormalizedInstruction* ni, + int& currentStackOffset, + InputInfos& inputs, + const LocalTypeFn& localType) { #ifdef USE_TRACE const SrcKey& sk = ni->source; #endif @@ -1880,7 +1911,7 @@ void Translator::getInputs(SrcKey startSk, } numRefCounted += curType.maybeCounted(); } - return numRefCounted <= kMaxInlineReturnDecRefs; + return numRefCounted <= Translator::kMaxInlineReturnDecRefs; }(); if ((input & AllLocals) && wantInlineReturn) { @@ -1908,6 +1939,21 @@ void Translator::getInputs(SrcKey startSk, } } +bool dontGuardAnyInputs(Op op) { + switch (op) { +#define CASE(iNm) case Op ## iNm: +#define NOOP(...) + INSTRS + PSEUDOINSTR_DISPATCH(NOOP) + return false; + + default: + return true; + } +#undef NOOP +#undef CASE +} + bool outputDependsOnInput(const Op instr) { switch (instrInfo[instr].type) { case OutNull: @@ -2470,7 +2516,7 @@ NormalizedInstruction::getOutputUsage(const DynLocation* output) const { if (succ->inputWasInferred(i)) { return OutputUse::Inferred; } - if (Translator::Get()->dontGuardAnyInputs(succ->op())) { + if (dontGuardAnyInputs(succ->op())) { /* the consumer doesnt care about its inputs but we may still have inferred something about its outputs that a later instruction may depend on @@ -2971,10 +3017,6 @@ static bool shouldAnalyzeCallee(const NormalizedInstruction* fcall, return false; } -extern bool shouldIRInline(const Func* curFunc, - const Func* func, - const Tracelet& callee); - void Translator::analyzeCallee(TraceletContext& tas, Tracelet& parent, NormalizedInstruction* fcall) { @@ -3094,7 +3136,7 @@ void Translator::analyzeCallee(TraceletContext& tas, * (potentially increasing the specificity of guards), and we don't * want to do that unnecessarily. */ - if (!shouldIRInline(callerFunc, target, *subTrace)) { + if (!JIT::shouldIRInline(callerFunc, target, *subTrace)) { if (UNLIKELY(Stats::enabledAny() && getenv("HHVM_STATS_FAILEDINL"))) { subTrace->m_inliningFailed = true; // Save the trace for stats purposes but don't waste time doing any @@ -3223,8 +3265,6 @@ std::unique_ptr Translator::analyze(SrcKey sk, ni->breaksTracelet = false; ni->changesPC = opcodeChangesPC(ni->op()); ni->fuseBranch = false; - ni->outputPredicted = false; - ni->outputPredictionStatic = false; assert(!t.m_analysisFailed); oldStackFrameOffset = stackFrameOffset; @@ -3237,7 +3277,7 @@ std::unique_ptr Translator::analyze(SrcKey sk, try { preInputApplyMetaData(metaHand, ni); InputInfos inputInfos; - getInputs(t.m_sk, ni, stackFrameOffset, inputInfos, [&](int i) { + getInputsImpl(t.m_sk, ni, stackFrameOffset, inputInfos, [&](int i) { return Type::fromRuntimeType( tas.currentType(Location(Location::Local, i))); }); @@ -3488,9 +3528,7 @@ breakBB: } Translator::Translator() - : m_curTrace(nullptr) - , m_curNI(nullptr) - , m_resumeHelper(nullptr) + : m_resumeHelper(nullptr) , m_createdTime(Timer::GetCurrentTimeMicros()) , m_analysisDepth(0) { @@ -3541,11 +3579,11 @@ Translator::addDbgBLPC(PC pc) { // Return the SrcKey for the operation that should follow the supplied // NormalizedInstruction. -SrcKey Translator::nextSrcKey(const NormalizedInstruction& i) { +SrcKey nextSrcKey(const NormalizedInstruction& i) { return i.source.advanced(curUnit()); } -void Translator::populateImmediates(NormalizedInstruction& inst) { +void populateImmediates(NormalizedInstruction& inst) { for (int i = 0; i < numImmediates(inst.op()); i++) { inst.imm[i] = getImm((Op*)inst.pc(), i); } @@ -3568,11 +3606,11 @@ const char* Translator::translateResultName(TranslateResult r) { /* * Similar to applyInputMetaData, but designed to be used during ir - * generation. Reads and writes types of values using m_hhbcTrans. This will + * generation. Reads and writes types of values using hhbcTrans. This will * eventually replace applyInputMetaData. */ -void Translator::readMetaData(Unit::MetaHandle& handle, - NormalizedInstruction& inst) { +void readMetaData(Unit::MetaHandle& handle, NormalizedInstruction& inst, + HhbcTranslator& hhbcTrans) { if (!handle.findMeta(inst.unit(), inst.offset())) return; Unit::MetaInfo info; @@ -3614,7 +3652,7 @@ void Translator::readMetaData(Unit::MetaHandle& handle, base + (info.m_arg & ~Unit::MetaInfo::VectorArg) : info.m_arg; auto updateType = [&]{ auto& input = *inst.inputs[arg]; - input.rtt = m_hhbcTrans->rttFromLocation(input.location); + input.rtt = hhbcTrans.rttFromLocation(input.location); }; switch (info.m_kind) { @@ -3628,28 +3666,28 @@ void Translator::readMetaData(Unit::MetaHandle& handle, inst.imm[0].u_IVA = info.m_data; break; case Unit::MetaInfo::Kind::DataTypePredicted: { - auto const& loc = inst.inputs[arg]->location; + auto const loc = inst.inputs[arg]->location.toLocation(); auto const t = Type::fromDataType(DataType(info.m_data)); auto const offset = inst.source.offset(); // These 'predictions' mean the type is InitNull or the predicted type, // so we assert InitNull | t, then guard t. This allows certain // optimizations in the IR. - m_hhbcTrans->assertTypeLocation(loc, Type::InitNull | t); - m_hhbcTrans->checkTypeLocation(loc, t, offset); + hhbcTrans.assertTypeLocation(loc, Type::InitNull | t); + hhbcTrans.checkTypeLocation(loc, t, offset); updateType(); break; } case Unit::MetaInfo::Kind::DataTypeInferred: { - m_hhbcTrans->assertTypeLocation( - inst.inputs[arg]->location, + hhbcTrans.assertTypeLocation( + inst.inputs[arg]->location.toLocation(), Type::fromDataType(DataType(info.m_data))); updateType(); break; } case Unit::MetaInfo::Kind::String: { - m_hhbcTrans->assertString(inst.inputs[arg]->location, - inst.unit()->lookupLitstrId(info.m_data)); + hhbcTrans.assertString(inst.inputs[arg]->location.toLocation(), + inst.unit()->lookupLitstrId(info.m_data)); updateType(); break; } @@ -3705,7 +3743,7 @@ void Translator::readMetaData(Unit::MetaHandle& handle, } while (handle.nextArg(info)); } -bool Translator::instrMustInterp(const NormalizedInstruction& inst) { +bool instrMustInterp(const NormalizedInstruction& inst) { if (RuntimeOption::EvalJitAlwaysInterpOne) return true; switch (inst.op()) { @@ -3723,16 +3761,30 @@ bool Translator::instrMustInterp(const NormalizedInstruction& inst) { } } -static Location toLocation(const RegionDesc::Location& loc) { - typedef RegionDesc::Location::Tag T; - switch (loc.tag()) { - case T::Stack: - return Location(Location::Stack, loc.stackOffset()); +void Translator::traceStart(Offset bcStartOffset) { + assert(!m_irTrans); - case T::Local: - return Location(Location::Local, loc.localId()); - } - not_reached(); + FTRACE(1, "{}{:-^40}{}\n", + color(ANSI_COLOR_BLACK, ANSI_BGCOLOR_GREEN), + " HHIR during translation ", + color(ANSI_COLOR_END)); + + m_irTrans.reset(new JIT::IRTranslator( + bcStartOffset, curSpOff(), curFunc())); +} + +void Translator::traceEnd() { + m_irTrans->hhbcTrans().end(); + FTRACE(1, "{}{:-^40}{}\n", + color(ANSI_COLOR_BLACK, ANSI_BGCOLOR_GREEN), + "", + color(ANSI_COLOR_END)); +} + +void Translator::traceFree() { + FTRACE(1, "HHIR free: arena size: {}\n", + m_irTrans->hhbcTrans().irFactory().arena().size()); + m_irTrans.reset(); } Translator::TranslateResult @@ -3740,8 +3792,10 @@ Translator::translateRegion(const RegionDesc& region, RegionBlacklist& toInterp) { typedef JIT::RegionDesc::Block Block; FTRACE(1, "translateRegion starting with:\n{}\n", show(region)); + HhbcTranslator& ht = m_irTrans->hhbcTrans(); assert(!region.blocks.empty()); const SrcKey startSk = region.blocks.front()->start(); + Unit::MetaHandle metaHand; for (auto const& block : region.blocks) { SrcKey sk = block->start(); @@ -3758,10 +3812,9 @@ Translator::translateRegion(const RegionDesc& region, while (typePreds.hasNext(sk)) { auto const& pred = typePreds.next(); if (block == region.blocks.front() && i == 0) { - m_hhbcTrans->guardTypeLocation(toLocation(pred.location), pred.type); + ht.guardTypeLocation(pred.location, pred.type); } else { - m_hhbcTrans->checkTypeLocation(toLocation(pred.location), pred.type, - sk.offset()); + ht.checkTypeLocation(pred.location, pred.type, sk.offset()); } } @@ -3770,7 +3823,7 @@ Translator::translateRegion(const RegionDesc& region, while (refPreds.hasNext(sk)) { assert(sk == startSk); auto const& pred = refPreds.next(); - m_hhbcTrans->guardRefs(pred.arSpOffset, pred.mask, pred.vals); + ht.guardRefs(pred.arSpOffset, pred.mask, pred.vals); } // Update the current funcd, if we have a new one. @@ -3795,29 +3848,19 @@ Translator::translateRegion(const RegionDesc& region, // Apply the first round of metadata from the repo and get a list of // input locations. - InputInfos inputInfos; - Unit::MetaHandle metaHand; preInputApplyMetaData(metaHand, &inst); - // TranslatorX64 expected top of stack to be index -1, with indexes - // growing down from there. hhir defines top of stack to be index 1, with - // indexes growing up from there. To compensate we start with a stack - // offset of 1 and negate the index of any stack input after the call to - // getInputs. - int stackOff = 1; - getInputs(startSk, &inst, stackOff, inputInfos, [&](int i) { - return m_hhbcTrans->traceBuilder()->getLocalType(i); + InputInfos inputInfos; + getInputs(startSk, inst, inputInfos, [&](int i) { + return ht.traceBuilder()->getLocalType(i); }); - for (auto& info : inputInfos) { - if (info.loc.isStack()) info.loc.offset = -info.loc.offset; - } // Populate the NormalizedInstruction's input vector, using types from // HhbcTranslator. std::vector dynLocs; dynLocs.reserve(inputInfos.size()); auto newDynLoc = [&](const InputInfo& ii) { - dynLocs.emplace_back(ii.loc, m_hhbcTrans->rttFromLocation(ii.loc)); + dynLocs.emplace_back(ii.loc, ht.rttFromLocation(ii.loc)); FTRACE(2, "rttFromLocation: {} -> {}\n", ii.loc.pretty(), dynLocs.back().rtt.pretty()); return &dynLocs.back(); @@ -3829,7 +3872,7 @@ Translator::translateRegion(const RegionDesc& region, // Apply the remaining metadata. This may change the types of some of // inst's inputs. - readMetaData(metaHand, inst); + readMetaData(metaHand, inst, ht); if (!inst.noOp && inputInfos.needsRefCheck) { assert(byRefs.hasNext(sk)); auto byRef = byRefs.next(); @@ -3838,24 +3881,11 @@ Translator::translateRegion(const RegionDesc& region, // Check for a type prediction. Put it in the NormalizedInstruction so // the emit* method can use it if needed. - auto const& iInfo = instrInfo[inst.op()]; - auto doPrediction = iInfo.type == OutPred && !inst.breaksTracelet; - if (doPrediction) { - // All OutPred ops have a single stack output for now. - assert(iInfo.out == Stack1); - auto dt = predictOutputs(startSk, &inst); - if (dt != KindOfInvalid) { - inst.outPred = Type::fromDataType(dt); - } else { - doPrediction = false; - } - } + auto const doPrediction = outputIsPredicted(startSk, inst); // Emit IR for the body of the instruction. - Util::Nuller niNuller(&m_curNI); - m_curNI = &inst; try { - translateInstr(inst); + m_irTrans->translateInstr(inst); } catch (const JIT::FailedIRGen& exn) { FTRACE(1, "ir generation for {} failed with {}\n", inst.toString(), exn.what()); @@ -3867,9 +3897,8 @@ Translator::translateRegion(const RegionDesc& region, // Check the prediction. If the predicted type is less specific than what // is currently on the eval stack, checkTypeLocation won't emit any code. if (doPrediction) { - m_hhbcTrans->checkTypeLocation(Location(Location::Stack, 0), - inst.outPred, - sk.advanced(block->unit()).offset()); + ht.checkTypeStack(0, inst.outPred, + sk.advanced(block->unit()).offset()); } } diff --git a/hphp/runtime/vm/jit/translator.h b/hphp/runtime/vm/jit/translator.h index b3582c72b..ab5cd8402 100644 --- a/hphp/runtime/vm/jit/translator.h +++ b/hphp/runtime/vm/jit/translator.h @@ -48,7 +48,7 @@ namespace HPHP { namespace JIT { class HhbcTranslator; -class IRFactory; +class IRTranslator; } namespace Debug { class DebugInfo; @@ -57,6 +57,7 @@ namespace Transl { using JIT::Type; using JIT::RegionDesc; +using JIT::HhbcTranslator; static const bool trustSigSegv = false; static const uint32_t transCountersPerChunk = 1024 * 1024 / 8; @@ -236,7 +237,6 @@ class NormalizedInstruction { // stack at tracelet entry. int stackOffset; int sequenceNum; - bool startsBB:1; bool breaksTracelet:1; bool changesPC:1; bool fuseBranch:1; @@ -304,6 +304,8 @@ class NormalizedInstruction { , outStack3(nullptr) , outPred(Type::Gen) , checkedInputs(0) + , outputPredicted(false) + , outputPredictionStatic(false) , ignoreInnerType(false) , guardedThis(false) , guardedCls(false) @@ -360,7 +362,7 @@ class UnknownInputExc : public std::exception { } while(0) #define throwUnknownInput() do { \ - throw UnknownInputExc(__FILE__, __LINE__); \ + throw Transl::UnknownInputExc(__FILE__, __LINE__); \ } while(0); class GuardType { @@ -680,12 +682,11 @@ struct TranslArgs { * Translator annotates a tracelet with input/output locations/types. */ class Translator { - static const int MaxJmpsTracedThrough = 5; - public: // kMaxInlineReturnDecRefs is the maximum ref-counted locals to // generate an inline return for. static const int kMaxInlineReturnDecRefs = 1; + static const int MaxJmpsTracedThrough = 5; private: friend struct TraceletContext; @@ -694,18 +695,10 @@ private: void analyzeCallee(TraceletContext&, Tracelet& parent, NormalizedInstruction* fcall); - void preInputApplyMetaData(Unit::MetaHandle, NormalizedInstruction*); bool applyInputMetaData(Unit::MetaHandle&, NormalizedInstruction* ni, TraceletContext& tas, InputInfos& ii); - void readMetaData(Unit::MetaHandle& handle, - NormalizedInstruction& inst); - void getInputs(SrcKey startSk, - NormalizedInstruction* ni, - int& currentStackOffset, - InputInfos& inputs, - std::function localType); void getOutputs(Tracelet& t, NormalizedInstruction* ni, int& currentStackOffset, @@ -748,35 +741,14 @@ protected: Success }; static const char* translateResultName(TranslateResult r); - void translateInstr(const NormalizedInstruction& i); void traceStart(Offset bcStartOffset); virtual void traceCodeGen() = 0; void traceEnd(); void traceFree(); - static bool instrMustInterp(const NormalizedInstruction&); - -private: - void interpretInstr(const NormalizedInstruction& i); - void translateInstrWork(const NormalizedInstruction& i); - void passPredictedAndInferredTypes(const NormalizedInstruction& i); -#define CASE(nm) void translate ## nm(const NormalizedInstruction& i); -INSTRS -PSEUDOINSTRS -#undef CASE - -public: - SrcKey nextSrcKey(const NormalizedInstruction& i); - - // Currently translating trace or instruction---only valid during - // translate phase. - const Tracelet* m_curTrace; - const NormalizedInstruction* m_curNI; protected: void requestResetHighLevelTranslator(); - void populateImmediates(NormalizedInstruction&); - /* translateRegion reads from the RegionBlacklist to determine when * to interpret an instruction, and adds failed instructions to the * blacklist so they're interpreted on the next attempt. */ @@ -794,8 +766,7 @@ protected: int64_t m_createdTime; - std::unique_ptr m_irFactory; - std::unique_ptr m_hhbcTrans; + std::unique_ptr m_irTrans; SrcDB m_srcDB; @@ -903,7 +874,6 @@ public: void postAnalyze(NormalizedInstruction* ni, SrcKey& sk, Tracelet& t, TraceletContext& tas); - static int locPhysicalOffset(Location l, const Func* f = nullptr); static Location tvToLocation(const TypedValue* tv, const TypedValue* frame); static bool typeIsString(DataType type) { return type == KindOfString || type == KindOfStaticString; @@ -923,13 +893,6 @@ public: return debug || RuntimeOption::EvalDumpTC; } - /* - * If this returns true, we dont generate guards for any of the - * inputs to this instruction (this is essentially to avoid - * generating guards on behalf of interpreted instructions). - */ - virtual bool dontGuardAnyInputs(Op op) { return false; } - protected: PCFilter m_dbgBLPC; hphp_hash_set m_dbgBLSrcKey; @@ -1047,6 +1010,12 @@ opcodeBreaksBB(const Op instr) { return opcodeControlFlowInfo(instr) == ControlFlowInfo::BreaksBB; } +/* + * If this returns true, we dont generate guards for any of the inputs + * to this instruction (this is essentially to avoid generating guards + * on behalf of interpreted instructions). + */ +bool dontGuardAnyInputs(Op op); bool outputDependsOnInput(const Op instr); extern bool tc_dump(); @@ -1067,6 +1036,22 @@ static inline bool isSmartPtrRef(DataType t) { t == KindOfArray || t == KindOfObject; } +SrcKey nextSrcKey(const NormalizedInstruction& i); + +void populateImmediates(NormalizedInstruction&); +void preInputApplyMetaData(Unit::MetaHandle, NormalizedInstruction*); +void readMetaData(Unit::MetaHandle&, NormalizedInstruction&, HhbcTranslator&); +bool instrMustInterp(const NormalizedInstruction&); + +typedef std::function LocalTypeFn; +void getInputs(SrcKey startSk, NormalizedInstruction& inst, InputInfos& infos, + const LocalTypeFn& localType); +void getInputsImpl(SrcKey startSk, NormalizedInstruction* inst, + int& currentStackOffset, InputInfos& inputs, + const LocalTypeFn& localType); +bool outputIsPredicted(SrcKey startSk, NormalizedInstruction& inst); +int locPhysicalOffset(Location l, const Func* f = nullptr); + namespace InstrFlags { enum OutTypeConstraints { OutNull, diff --git a/hphp/runtime/vm/runtime.h b/hphp/runtime/vm/runtime.h index 268af715b..c37522c64 100644 --- a/hphp/runtime/vm/runtime.h +++ b/hphp/runtime/vm/runtime.h @@ -20,7 +20,6 @@ #include "hphp/runtime/vm/func.h" #include "hphp/runtime/vm/funcdict.h" #include "hphp/runtime/base/builtin_functions.h" -#include "hphp/runtime/vm/jit/translator-inline.h" namespace HPHP {