2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2014 Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
16 #ifndef incl_HPHP_TRANSLATOR_H_
17 #define incl_HPHP_TRANSLATOR_H_
28 #include <boost/dynamic_bitset.hpp>
30 #include "hphp/util/md5.h"
31 #include "hphp/util/hash.h"
32 #include "hphp/util/timer.h"
33 #include "hphp/util/hash-map-typedefs.h"
34 #include "hphp/runtime/base/execution-context.h"
35 #include "hphp/runtime/base/smart-containers.h"
36 #include "hphp/runtime/vm/bytecode.h"
37 #include "hphp/runtime/vm/jit/block.h"
38 #include "hphp/runtime/vm/jit/fixup.h"
39 #include "hphp/runtime/vm/jit/runtime-type.h"
40 #include "hphp/runtime/vm/jit/srcdb.h"
41 #include "hphp/runtime/vm/jit/translator-instrs.h"
42 #include "hphp/runtime/vm/jit/write-lease.h"
43 #include "hphp/runtime/vm/jit/prof-data.h"
44 #include "hphp/runtime/vm/jit/unique-stubs.h"
45 #include "hphp/runtime/vm/debugger-hook.h"
46 #include "hphp/runtime/vm/srckey.h"
48 /* Translator front-end. */
51 struct HhbcTranslator
;
60 static const uint32_t transCountersPerChunk
= 1024 * 1024 / 8;
63 * DIRTY when the live register state is spread across the stack and m_fixup,
64 * CLEAN when it has been sync'ed into g_context.
66 enum class VMRegState
{
70 extern __thread VMRegState tl_regState
;
72 struct NormalizedInstruction
;
74 // A DynLocation is a Location-in-execution: a location, along with
75 // whatever is known about its runtime type.
80 DynLocation(Location l
, DataType t
) : location(l
), rtt(t
) {}
82 DynLocation(Location l
, RuntimeType t
) : location(l
), rtt(t
) {}
84 DynLocation() : location(), rtt() {}
86 bool operator==(const DynLocation
& r
) const {
87 return rtt
== r
.rtt
&& location
== r
.location
;
91 size_t operator()(const DynLocation
&dl
) const {
92 uint64_t rtthash
= rtt(rtt
);
93 uint64_t locHash
= location(location
);
94 return rtthash
^ locHash
;
97 std::string
pretty() const {
98 return Trace::prettyNode("DynLocation", location
, rtt
);
101 // Punch through a bunch of frequently called rtt and location methods.
102 // While this is unlovely here, we use DynLocation in bazillions of
103 // places in the translator, and constantly saying ".rtt" is worse.
104 bool isString() const {
105 return rtt
.isString();
110 bool isDouble() const {
111 return rtt
.isDouble();
113 bool isBoolean() const {
114 return rtt
.isBoolean();
119 bool isRefToObject() const {
120 return rtt
.isRef() && innerType() == KindOfObject
;
122 bool isValue() const {
123 return rtt
.isValue();
125 bool isNull() const {
128 bool isObject() const {
129 return rtt
.isObject();
131 bool isArray() const {
132 return rtt
.isArray();
134 DataType
valueType() const {
135 return rtt
.valueType();
137 DataType
innerType() const {
138 return rtt
.innerType();
140 DataType
outerType() const {
141 return rtt
.outerType();
144 bool isStack() const {
145 return location
.isStack();
147 bool isLocal() const {
148 return location
.isLocal();
150 bool isLiteral() const {
151 return location
.isLiteral();
154 // Uses the runtime state. True if this dynLocation can be overwritten by
155 // SetG's and SetM's.
156 bool canBeAliased() const;
159 // Flags that summarize the plan for handling a given instruction.
161 Interp
= 0, // default; must be boolean false
162 Supported
= 1, // Not interpreted, though possibly with C++
163 NonReentrant
= 2, // Supported with no possibility of reentry.
164 MachineCode
= 4, // Supported without C++ at all.
165 Simple
= NonReentrant
| Supported
,
166 Native
= MachineCode
| Simple
170 struct TraceletContext
;
172 // Return a summary string of the bytecode in a tracelet.
173 std::string
traceletShape(const Tracelet
&);
175 struct TranslationFailedExc
: std::runtime_error
{
176 TranslationFailedExc(const char* file
, int line
)
177 : std::runtime_error(folly::format("TranslationFailedExc @ {}:{}",
182 struct UnknownInputExc
: std::runtime_error
{
183 UnknownInputExc(const char* file
, int line
)
184 : std::runtime_error(folly::format("UnknownInputExc @ {}:{}",
190 const char* m_file
; // must be static
194 struct ControlFlowFailedExc
: std::runtime_error
{
195 ControlFlowFailedExc(const char* file
, int line
)
196 : std::runtime_error(folly::format("ControlFlowFailedExc @ {}:{}",
201 #define punt() do { \
202 throw JIT::TranslationFailedExc(__FILE__, __LINE__); \
205 #define throwUnknownInput() do { \
206 throw JIT::UnknownInputExc(__FILE__, __LINE__); \
210 explicit GuardType(DataType outer
= KindOfAny
,
211 DataType inner
= KindOfNone
);
212 explicit GuardType(const RuntimeType
& rtt
);
213 GuardType(const GuardType
& other
);
214 const DataType
getOuterType() const;
215 const DataType
getInnerType() const;
216 const Class
* getSpecializedClass() const;
217 bool isSpecific() const;
218 bool isSpecialized() const;
219 bool isRelaxed() const;
220 bool isGeneric() const;
221 bool isCounted() const;
222 bool isMoreRefinedThan(const GuardType
& other
) const;
223 bool mayBeUninit() const;
224 GuardType
getCountness() const;
225 GuardType
getCountnessInit() const;
226 DataTypeCategory
getCategory() const;
227 GuardType
dropSpecialization() const;
228 RuntimeType
getRuntimeType() const;
229 bool isEqual(GuardType other
) const;
230 bool hasArrayKind() const;
231 ArrayData::ArrayKind
getArrayKind() const;
240 ArrayData::ArrayKind arrayKind
;
245 typedef hphp_hash_map
<Location
,RuntimeType
,Location
> TypeMap
;
246 typedef hphp_hash_set
<Location
, Location
> LocationSet
;
247 typedef hphp_hash_map
<DynLocation
*, GuardType
> DynLocTypeMap
;
248 typedef hphp_hash_map
<RegionDesc::BlockId
, Block
*> BlockIdToIRBlockMap
;
249 typedef hphp_hash_map
<RegionDesc::BlockId
,
250 RegionDesc::Block
*> BlockIdToRegionBlockMap
;
254 const char* getTransKindName(TransKind kind
);
257 * Used to maintain a mapping from the bytecode to its corresponding x86.
259 struct TransBCMapping
{
267 * A record with various information about a translation.
275 std::vector
<DynLocation
>
281 std::vector
<TransBCMapping
>
291 TCA _astubsStart
= 0,
292 uint32_t _astubsLen
= 0) :
293 id(0), kind(_kind
), src(s
), md5(_md5
), bcStopOffset(0),
294 aStart(_aStart
), aLen(_aLen
),
295 astubsStart(_astubsStart
), astubsLen(_astubsLen
)
304 TCA _astubsStart
= 0,
305 uint32_t _astubsLen
= 0,
306 std::vector
<TransBCMapping
> _bcMapping
=
307 std::vector
<TransBCMapping
>());
309 void setID(TransID newID
) { id
= newID
; }
310 std::string
print(uint64_t profCount
) const;
314 TranslArgs(const SrcKey
& sk
, bool align
)
318 , m_setFuncBody(false)
319 , m_transId(InvalidID
)
323 TranslArgs
& sk(const SrcKey
& sk
) {
327 TranslArgs
& align(bool align
) {
331 TranslArgs
& interp(bool interp
) {
335 TranslArgs
& setFuncBody() {
336 m_setFuncBody
= true;
339 TranslArgs
& transId(TransID transId
) {
343 TranslArgs
& region(JIT::RegionDescPtr region
) {
353 JIT::RegionDescPtr m_region
;
357 extern Translator
* tx
;
360 * Translator annotates a tracelet with input/output locations/types.
363 // kMaxInlineReturnDecRefs is the maximum ref-counted locals to
364 // generate an inline return for.
365 static const int kMaxInlineReturnDecRefs
= 1;
367 static const int MaxJmpsTracedThrough
= 5;
369 JIT::UniqueStubs uniqueStubs
;
372 friend struct TraceletContext
;
374 void analyzeCallee(TraceletContext
&,
376 NormalizedInstruction
* fcall
);
377 bool applyInputMetaData(Unit::MetaHandle
&,
378 NormalizedInstruction
* ni
,
379 TraceletContext
& tas
,
381 void handleAssertionEffects(Tracelet
&,
382 const NormalizedInstruction
&,
384 int currentStackOffset
);
385 void getOutputs(Tracelet
& t
,
386 NormalizedInstruction
* ni
,
387 int& currentStackOffset
,
389 void relaxDeps(Tracelet
& tclet
, TraceletContext
& tctxt
);
390 void constrainDep(const DynLocation
* loc
,
391 NormalizedInstruction
* firstInstr
,
393 GuardType
& relxType
);
394 DataTypeCategory
getOperandConstraintCategory(NormalizedInstruction
* instr
,
396 const GuardType
& specType
);
397 GuardType
getOperandConstraintType(NormalizedInstruction
* instr
,
399 const GuardType
& specType
);
401 void constrainOperandType(GuardType
& relxType
,
402 NormalizedInstruction
* instr
,
404 const GuardType
& specType
);
407 RuntimeType
liveType(Location l
, const Unit
&u
, bool specialize
= false);
408 RuntimeType
liveType(const Cell
* outer
,
410 bool specialize
= false);
412 void createBlockMaps(const RegionDesc
& region
,
413 BlockIdToIRBlockMap
& blockIdToIRBlock
,
414 BlockIdToRegionBlockMap
& blockIdToRegionBlock
);
416 void setSuccIRBlocks(const RegionDesc
& region
,
417 RegionDesc::BlockId srcBlockId
,
418 const BlockIdToIRBlockMap
& blockIdToIRBlock
,
419 const BlockIdToRegionBlockMap
& blockIdToRegionBlock
);
421 void setIRBlock(RegionDesc::BlockId blockId
,
422 const BlockIdToIRBlockMap
& blockIdToIRBlock
,
423 const BlockIdToRegionBlockMap
& blockIdToRegionBlock
);
426 enum TranslateResult
{
431 static const char* translateResultName(TranslateResult r
);
432 void traceStart(Offset initBcOffset
, Offset initSpOffset
, bool inGenerator
,
437 void requestResetHighLevelTranslator();
440 /* translateRegion reads from the RegionBlacklist to determine when
441 * to interpret an instruction, and adds failed instructions to the
442 * blacklist so they're interpreted on the next attempt. */
443 typedef hphp_hash_set
<SrcKey
, SrcKey::Hasher
> RegionBlacklist
;
444 TranslateResult
translateRegion(const RegionDesc
& region
,
446 RegionBlacklist
& interp
);
449 typedef std::map
<TCA
, TransID
> TransDB
;
451 std::vector
<TransRec
> m_translations
;
452 std::vector
<uint64_t*> m_transCounters
;
454 int64_t m_createdTime
;
456 std::unique_ptr
<JIT::IRTranslator
> m_irTrans
;
459 JIT::IRTranslator
* irTrans() {
460 return m_irTrans
.get();
466 static Lease s_writeLease
;
472 static Lease
& WriteLease() {
475 static RuntimeType
outThisObjectType();
477 const TransDB
& getTransDB() const {
481 const TransRec
* getTransRec(TCA tca
) const {
482 if (!isTransDBEnabled()) return nullptr;
484 TransDB::const_iterator it
= m_transDB
.find(tca
);
485 if (it
== m_transDB
.end()) {
488 if (it
->second
>= m_translations
.size()) {
491 return &m_translations
[it
->second
];
494 const TransRec
* getTransRec(TransID transId
) const {
495 if (!isTransDBEnabled()) return nullptr;
497 always_assert(transId
< m_translations
.size());
498 return &m_translations
[transId
];
501 TransID
getCurrentTransID() const {
502 return m_translations
.size();
505 uint64_t* getTransCounterAddr();
506 uint64_t getTransCounter(TransID transId
) const;
508 void addTranslation(const TransRec
& transRec
);
510 // helpers for srcDB.
511 SrcRec
* getSrcRec(SrcKey sk
) {
512 // TODO: add a insert-or-find primitive to THM
513 if (SrcRec
* r
= m_srcDB
.find(sk
)) return r
;
514 assert(s_writeLease
.amOwner());
515 return m_srcDB
.insert(sk
);
518 const SrcDB
& getSrcDB() const {
523 * Create a Tracelet for the given SrcKey, which must actually be
524 * the current VM frame.
526 * XXX The analysis pass will inspect the live state of the VM stack
527 * as needed to determine the current types of in-flight values.
529 std::unique_ptr
<Tracelet
> analyze(SrcKey sk
, const TypeMap
& = TypeMap());
531 void postAnalyze(NormalizedInstruction
* ni
, SrcKey
& sk
,
532 Tracelet
& t
, TraceletContext
& tas
);
533 static bool liveFrameIsPseudoMain();
535 inline bool stateIsDirty() {
536 return tl_regState
== VMRegState::DIRTY
;
539 inline bool isTransDBEnabled() const {
540 return debug
|| RuntimeOption::EvalDumpTC
;
545 hphp_hash_set
<SrcKey
,SrcKey::Hasher
> m_dbgBLSrcKey
;
546 Mutex m_dbgBlacklistLock
;
549 bool isSrcKeyInBL(const SrcKey
& sk
);
553 std::unique_ptr
<ProfData
> m_profData
;
560 bool addDbgBLPC(PC pc
);
562 ProfData
* profData() const {
563 return m_profData
.get();
566 TransKind
mode() const {
569 void setMode(TransKind mode
) {
573 int analysisDepth() const {
574 assert(m_analysisDepth
>= 0);
575 return m_analysisDepth
;
578 // Start a new translation space. Returns true IFF this thread created
583 int getStackDelta(const NormalizedInstruction
& ni
);
584 int64_t getStackPopped(PC pc
);
585 int64_t getStackPushed(PC pc
);
587 enum class ControlFlowInfo
{
593 inline ControlFlowInfo
594 opcodeControlFlowInfo(const Op instr
) {
603 case Op::ContSuspend
:
604 case Op::ContSuspendK
:
605 case Op::AsyncSuspend
:
616 case Op::IterInit
: // May branch to fail case.
617 case Op::IterInitK
: // Ditto
618 case Op::MIterInit
: // Ditto
619 case Op::MIterInitK
: // Ditto
620 case Op::WIterInit
: // Ditto
621 case Op::WIterInitK
: // Ditto
622 case Op::DecodeCufIter
: // Ditto
628 case Op::BreakTraceHint
:
629 return ControlFlowInfo::BreaksBB
;
640 return ControlFlowInfo::ChangesPC
;
642 return ControlFlowInfo::None
;
649 * Returns true if the instruction can potentially set PC to point
650 * to something other than the next instruction in the bytecode
653 opcodeChangesPC(const Op instr
) {
654 return opcodeControlFlowInfo(instr
) >= ControlFlowInfo::ChangesPC
;
660 * Returns true if the instruction always breaks a tracelet. Most
661 * instructions that change PC will break the tracelet, though some
662 * do not (ex. FCall).
665 opcodeBreaksBB(const Op instr
) {
666 return opcodeControlFlowInfo(instr
) == ControlFlowInfo::BreaksBB
;
670 * instrBreaksProfileBB --
672 * Similar to opcodeBreaksBB but more strict. We break profiling blocks after
673 * any instruction that can side exit, including instructions with predicted
676 bool instrBreaksProfileBB(const NormalizedInstruction
* instr
);
679 * If this returns true, we dont generate guards for any of the inputs
680 * to this instruction (this is essentially to avoid generating guards
681 * on behalf of interpreted instructions).
683 bool dontGuardAnyInputs(Op op
);
684 bool outputDependsOnInput(const Op instr
);
686 extern bool tc_dump();
689 * This routine attempts to find the Func* that will be called for a
690 * given target Class and function name, from a given context. This
691 * function determines if a given Func* will be called in a
692 * request-insensitive way (i.e. suitable for burning into the TC as a
693 * pointer). The class we are targeting is assumed to be a subclass
694 * of `cls', not exactly `cls'.
696 * This function should not be used in a context where the call may
697 * involve late static binding (i.e. FPushClsMethod), since it assumes
698 * static functions will be resolved as targeting on cls regardless of
699 * whether they are overridden.
701 * Returns nullptr if we can't be sure this would always call this
704 const Func
* lookupImmutableMethod(const Class
* cls
, const StringData
* name
,
705 bool& magicCall
, bool staticLookup
,
708 // This is used to check that return types of builtins are not simple
709 // types. This is different from IS_REFCOUNTED_TYPE because builtins
710 // can return Variants, and we use KindOfUnknown to denote these
712 static inline bool isCppByRef(DataType t
) {
713 return t
!= KindOfBoolean
&& t
!= KindOfInt64
&&
714 t
!= KindOfNull
&& t
!= KindOfDouble
;
717 // return true if type is passed in/out of C++ as String&/Array&/Object&
718 static inline bool isSmartPtrRef(DataType t
) {
719 return t
== KindOfString
|| t
== KindOfStaticString
||
720 t
== KindOfArray
|| t
== KindOfObject
||
724 void populateImmediates(NormalizedInstruction
&);
725 void preInputApplyMetaData(Unit::MetaHandle
, NormalizedInstruction
*);
726 enum class MetaMode
{
730 void readMetaData(Unit::MetaHandle
&, NormalizedInstruction
&, HhbcTranslator
&,
731 bool profiling
, MetaMode m
= MetaMode::Normal
);
732 bool instrMustInterp(const NormalizedInstruction
&);
734 typedef std::function
<Type(int)> LocalTypeFn
;
735 void getInputs(SrcKey startSk
, NormalizedInstruction
& inst
, InputInfos
& infos
,
736 const Func
* func
, const LocalTypeFn
& localType
);
737 void getInputsImpl(SrcKey startSk
, NormalizedInstruction
* inst
,
738 int& currentStackOffset
, InputInfos
& inputs
,
739 const Func
* func
, const LocalTypeFn
& localType
);
740 bool outputIsPredicted(NormalizedInstruction
& inst
);
741 bool callDestroysLocals(const NormalizedInstruction
& inst
,
743 int locPhysicalOffset(Location l
, const Func
* f
= nullptr);
744 bool shouldAnalyzeCallee(const NormalizedInstruction
*, const FPIEnt
*,
745 const Op
, const int);
747 namespace InstrFlags
{
748 enum OutTypeConstraints
{
752 OutStringImm
, // String w/ precisely known immediate.
761 OutThisObject
, // Object from current environment
762 OutFDesc
, // Blows away the current function desc
764 OutUnknown
, // Not known at tracelet compile-time
765 OutPred
, // Unknown, but give prediction a whirl.
766 OutCns
, // Constant; may be known at compile-time
767 OutVUnknown
, // type is V(unknown)
769 OutSameAsInput
, // type is the same as the first stack inpute
770 OutCInput
, // type is C(input)
771 OutVInput
, // type is V(input)
772 OutCInputL
, // type is C(type) of local input
773 OutVInputL
, // type is V(type) of local input
774 OutFInputL
, // type is V(type) of local input if current param is
775 // by ref, else type is C(type) of local input
776 OutFInputR
, // Like FInputL, but for R's on the stack.
778 OutArith
, // For Add, Sub, Mul
779 OutArithO
, // For AddO, SubO, MulO
780 OutBitOp
, // For BitAnd, BitOr, BitXor
781 OutSetOp
, // For SetOpL
782 OutIncDec
, // For IncDecL
783 OutStrlen
, // OpStrLen
784 OutClassRef
, // KindOfClass
785 OutFPushCufSafe
, // FPushCufSafe pushes two values of different
786 // types and an ActRec
787 OutAsyncAwait
, // AwaitHandle pushes its input and then a bool
793 * Input codes indicate what an instruction reads, and some other
794 * things about their behavior. The order these show up in the inputs
795 * vector is given in getInputs(), and is relevant in a few cases
796 * (e.g. instructions taking both stack inputs and MVectors).
803 StackIns1
= 1 << 3, // Insert an element under top of stack
804 StackIns2
= 1 << 4, // Insert an element under top 2 of stack
805 FuncdRef
= 1 << 5, // Input to FPass*
806 FStack
= 1 << 6, // output of FPushFuncD and friends
807 Local
= 1 << 7, // Writes to a local
808 MVector
= 1 << 8, // Member-vector input
809 Iter
= 1 << 9, // Iterator in imm[0]
810 AllLocals
= 1 << 10, // All locals (used by RetC)
811 DontGuardStack1
= 1 << 11, // Dont force a guard on behalf of stack1 input
812 IgnoreInnerType
= 1 << 12, // Instruction doesnt care about the inner types
813 DontGuardAny
= 1 << 13, // Dont force a guard for any input
814 This
= 1 << 14, // Input to CheckThis
815 StackN
= 1 << 15, // pop N cells from stack; n = imm[0].u_IVA
816 BStackN
= 1 << 16, // consume N cells from stack for builtin call;
818 StackTop2
= Stack1
| Stack2
,
819 StackTop3
= Stack1
| Stack2
| Stack3
,
822 inline Operands
operator|(const Operands
& l
, const Operands
& r
) {
823 return Operands(int(r
) | int(l
));
828 InstrFlags::Operands in
;
829 InstrFlags::Operands out
;
830 InstrFlags::OutTypeConstraints type
; // How are outputs related to inputs?
834 const InstrInfo
& getInstrInfo(Op op
);
836 typedef const int COff
; // Const offsets