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/fixup.h"
38 #include "hphp/runtime/vm/jit/runtime-type.h"
39 #include "hphp/runtime/vm/jit/srcdb.h"
40 #include "hphp/runtime/vm/jit/translator-instrs.h"
41 #include "hphp/runtime/vm/jit/write-lease.h"
42 #include "hphp/runtime/vm/jit/prof-data.h"
43 #include "hphp/runtime/vm/jit/unique-stubs.h"
44 #include "hphp/runtime/vm/debugger-hook.h"
45 #include "hphp/runtime/vm/srckey.h"
47 /* Translator front-end. */
50 struct HhbcTranslator
;
59 static const uint32_t transCountersPerChunk
= 1024 * 1024 / 8;
62 * DIRTY when the live register state is spread across the stack and m_fixup,
63 * CLEAN when it has been sync'ed into g_context.
65 enum class VMRegState
{
69 extern __thread VMRegState tl_regState
;
71 struct NormalizedInstruction
;
73 // A DynLocation is a Location-in-execution: a location, along with
74 // whatever is known about its runtime type.
79 DynLocation(Location l
, DataType t
) : location(l
), rtt(t
) {}
81 DynLocation(Location l
, RuntimeType t
) : location(l
), rtt(t
) {}
83 DynLocation() : location(), rtt() {}
85 bool operator==(const DynLocation
& r
) const {
86 return rtt
== r
.rtt
&& location
== r
.location
;
90 size_t operator()(const DynLocation
&dl
) const {
91 uint64_t rtthash
= rtt(rtt
);
92 uint64_t locHash
= location(location
);
93 return rtthash
^ locHash
;
96 std::string
pretty() const {
97 return Trace::prettyNode("DynLocation", location
, rtt
);
100 // Punch through a bunch of frequently called rtt and location methods.
101 // While this is unlovely here, we use DynLocation in bazillions of
102 // places in the translator, and constantly saying ".rtt" is worse.
103 bool isString() const {
104 return rtt
.isString();
109 bool isDouble() const {
110 return rtt
.isDouble();
112 bool isBoolean() const {
113 return rtt
.isBoolean();
118 bool isRefToObject() const {
119 return rtt
.isRef() && innerType() == KindOfObject
;
121 bool isValue() const {
122 return rtt
.isValue();
124 bool isNull() const {
127 bool isObject() const {
128 return rtt
.isObject();
130 bool isArray() const {
131 return rtt
.isArray();
133 DataType
valueType() const {
134 return rtt
.valueType();
136 DataType
innerType() const {
137 return rtt
.innerType();
139 DataType
outerType() const {
140 return rtt
.outerType();
143 bool isStack() const {
144 return location
.isStack();
146 bool isLocal() const {
147 return location
.isLocal();
149 bool isLiteral() const {
150 return location
.isLiteral();
153 // Uses the runtime state. True if this dynLocation can be overwritten by
154 // SetG's and SetM's.
155 bool canBeAliased() const;
158 // Flags that summarize the plan for handling a given instruction.
160 Interp
= 0, // default; must be boolean false
161 Supported
= 1, // Not interpreted, though possibly with C++
162 NonReentrant
= 2, // Supported with no possibility of reentry.
163 MachineCode
= 4, // Supported without C++ at all.
164 Simple
= NonReentrant
| Supported
,
165 Native
= MachineCode
| Simple
169 struct TraceletContext
;
171 // Return a summary string of the bytecode in a tracelet.
172 std::string
traceletShape(const Tracelet
&);
174 struct TranslationFailedExc
: std::runtime_error
{
175 TranslationFailedExc(const char* file
, int line
)
176 : std::runtime_error(folly::format("TranslationFailedExc @ {}:{}",
181 struct UnknownInputExc
: std::runtime_error
{
182 UnknownInputExc(const char* file
, int line
)
183 : std::runtime_error(folly::format("UnknownInputExc @ {}:{}",
189 const char* m_file
; // must be static
193 #define punt() do { \
194 throw JIT::TranslationFailedExc(__FILE__, __LINE__); \
197 #define throwUnknownInput() do { \
198 throw JIT::UnknownInputExc(__FILE__, __LINE__); \
202 explicit GuardType(DataType outer
= KindOfAny
,
203 DataType inner
= KindOfNone
);
204 explicit GuardType(const RuntimeType
& rtt
);
205 GuardType(const GuardType
& other
);
206 const DataType
getOuterType() const;
207 const DataType
getInnerType() const;
208 const Class
* getSpecializedClass() const;
209 bool isSpecific() const;
210 bool isSpecialized() const;
211 bool isRelaxed() const;
212 bool isGeneric() const;
213 bool isCounted() const;
214 bool isMoreRefinedThan(const GuardType
& other
) const;
215 bool mayBeUninit() const;
216 GuardType
getCountness() const;
217 GuardType
getCountnessInit() const;
218 DataTypeCategory
getCategory() const;
219 GuardType
dropSpecialization() const;
220 RuntimeType
getRuntimeType() const;
221 bool isEqual(GuardType other
) const;
222 bool hasArrayKind() const;
223 ArrayData::ArrayKind
getArrayKind() const;
232 ArrayData::ArrayKind arrayKind
;
237 typedef hphp_hash_map
<Location
,RuntimeType
,Location
> TypeMap
;
238 typedef hphp_hash_set
<Location
, Location
> LocationSet
;
239 typedef hphp_hash_map
<DynLocation
*, GuardType
> DynLocTypeMap
;
242 const char* getTransKindName(TransKind kind
);
245 * Used to maintain a mapping from the bytecode to its corresponding x86.
247 struct TransBCMapping
{
255 * A record with various information about a translation.
263 std::vector
<DynLocation
>
269 std::vector
<TransBCMapping
>
279 TCA _astubsStart
= 0,
280 uint32_t _astubsLen
= 0) :
281 id(0), kind(_kind
), src(s
), md5(_md5
), bcStopOffset(0),
282 aStart(_aStart
), aLen(_aLen
),
283 astubsStart(_astubsStart
), astubsLen(_astubsLen
)
292 TCA _astubsStart
= 0,
293 uint32_t _astubsLen
= 0,
294 std::vector
<TransBCMapping
> _bcMapping
=
295 std::vector
<TransBCMapping
>());
297 void setID(TransID newID
) { id
= newID
; }
298 std::string
print(uint64_t profCount
) const;
302 TranslArgs(const SrcKey
& sk
, bool align
)
306 , m_setFuncBody(false)
307 , m_transId(InvalidID
)
311 TranslArgs
& sk(const SrcKey
& sk
) {
315 TranslArgs
& align(bool align
) {
319 TranslArgs
& interp(bool interp
) {
323 TranslArgs
& setFuncBody() {
324 m_setFuncBody
= true;
327 TranslArgs
& transId(TransID transId
) {
331 TranslArgs
& region(JIT::RegionDescPtr region
) {
341 JIT::RegionDescPtr m_region
;
345 extern Translator
* tx
;
348 * Translator annotates a tracelet with input/output locations/types.
351 // kMaxInlineReturnDecRefs is the maximum ref-counted locals to
352 // generate an inline return for.
353 static const int kMaxInlineReturnDecRefs
= 1;
355 static const int MaxJmpsTracedThrough
= 5;
357 JIT::UniqueStubs uniqueStubs
;
360 friend struct TraceletContext
;
362 void analyzeCallee(TraceletContext
&,
364 NormalizedInstruction
* fcall
);
365 bool applyInputMetaData(Unit::MetaHandle
&,
366 NormalizedInstruction
* ni
,
367 TraceletContext
& tas
,
369 void handleAssertionEffects(Tracelet
&,
370 const NormalizedInstruction
&,
372 int currentStackOffset
);
373 void getOutputs(Tracelet
& t
,
374 NormalizedInstruction
* ni
,
375 int& currentStackOffset
,
377 void relaxDeps(Tracelet
& tclet
, TraceletContext
& tctxt
);
378 void constrainDep(const DynLocation
* loc
,
379 NormalizedInstruction
* firstInstr
,
381 GuardType
& relxType
);
382 DataTypeCategory
getOperandConstraintCategory(NormalizedInstruction
* instr
,
384 const GuardType
& specType
);
385 GuardType
getOperandConstraintType(NormalizedInstruction
* instr
,
387 const GuardType
& specType
);
389 void constrainOperandType(GuardType
& relxType
,
390 NormalizedInstruction
* instr
,
392 const GuardType
& specType
);
395 RuntimeType
liveType(Location l
, const Unit
&u
, bool specialize
= false);
396 RuntimeType
liveType(const Cell
* outer
,
398 bool specialize
= false);
401 enum TranslateResult
{
406 static const char* translateResultName(TranslateResult r
);
407 void traceStart(Offset initBcOffset
, Offset initSpOffset
, bool inGenerator
,
412 void requestResetHighLevelTranslator();
415 /* translateRegion reads from the RegionBlacklist to determine when
416 * to interpret an instruction, and adds failed instructions to the
417 * blacklist so they're interpreted on the next attempt. */
418 typedef hphp_hash_set
<SrcKey
, SrcKey::Hasher
> RegionBlacklist
;
419 TranslateResult
translateRegion(const RegionDesc
& region
,
420 RegionBlacklist
& interp
);
423 typedef std::map
<TCA
, TransID
> TransDB
;
425 std::vector
<TransRec
> m_translations
;
426 std::vector
<uint64_t*> m_transCounters
;
428 int64_t m_createdTime
;
430 std::unique_ptr
<JIT::IRTranslator
> m_irTrans
;
433 JIT::IRTranslator
* irTrans() {
434 return m_irTrans
.get();
440 static Lease s_writeLease
;
445 virtual ~Translator();
446 static Lease
& WriteLease() {
449 static RuntimeType
outThisObjectType();
451 const TransDB
& getTransDB() const {
455 const TransRec
* getTransRec(TCA tca
) const {
456 if (!isTransDBEnabled()) return nullptr;
458 TransDB::const_iterator it
= m_transDB
.find(tca
);
459 if (it
== m_transDB
.end()) {
462 if (it
->second
>= m_translations
.size()) {
465 return &m_translations
[it
->second
];
468 const TransRec
* getTransRec(TransID transId
) const {
469 if (!isTransDBEnabled()) return nullptr;
471 always_assert(transId
< m_translations
.size());
472 return &m_translations
[transId
];
475 TransID
getCurrentTransID() const {
476 return m_translations
.size();
479 uint64_t* getTransCounterAddr();
480 uint64_t getTransCounter(TransID transId
) const;
482 void addTranslation(const TransRec
& transRec
);
484 // helpers for srcDB.
485 SrcRec
* getSrcRec(SrcKey sk
) {
486 // TODO: add a insert-or-find primitive to THM
487 if (SrcRec
* r
= m_srcDB
.find(sk
)) return r
;
488 assert(s_writeLease
.amOwner());
489 return m_srcDB
.insert(sk
);
492 const SrcDB
& getSrcDB() const {
497 * Create a Tracelet for the given SrcKey, which must actually be
498 * the current VM frame.
500 * XXX The analysis pass will inspect the live state of the VM stack
501 * as needed to determine the current types of in-flight values.
503 std::unique_ptr
<Tracelet
> analyze(SrcKey sk
, const TypeMap
& = TypeMap());
505 void postAnalyze(NormalizedInstruction
* ni
, SrcKey
& sk
,
506 Tracelet
& t
, TraceletContext
& tas
);
507 static bool liveFrameIsPseudoMain();
509 inline bool stateIsDirty() {
510 return tl_regState
== VMRegState::DIRTY
;
513 inline bool isTransDBEnabled() const {
514 return debug
|| RuntimeOption::EvalDumpTC
;
519 hphp_hash_set
<SrcKey
,SrcKey::Hasher
> m_dbgBLSrcKey
;
520 Mutex m_dbgBlacklistLock
;
523 bool isSrcKeyInBL(const SrcKey
& sk
);
527 ProfData
* m_profData
;
534 bool addDbgBLPC(PC pc
);
536 ProfData
* profData() const {
540 TransKind
mode() const {
543 void setMode(TransKind mode
) {
547 int analysisDepth() const {
548 assert(m_analysisDepth
>= 0);
549 return m_analysisDepth
;
552 // Start a new translation space. Returns true IFF this thread created
557 int getStackDelta(const NormalizedInstruction
& ni
);
558 int64_t getStackPopped(PC pc
);
559 int64_t getStackPushed(PC pc
);
561 enum class ControlFlowInfo
{
567 inline ControlFlowInfo
568 opcodeControlFlowInfo(const Op instr
) {
576 case Op::ContSuspend
:
577 case Op::ContSuspendK
:
589 case Op::IterInit
: // May branch to fail case.
590 case Op::IterInitK
: // Ditto
591 case Op::MIterInit
: // Ditto
592 case Op::MIterInitK
: // Ditto
593 case Op::WIterInit
: // Ditto
594 case Op::WIterInitK
: // Ditto
595 case Op::DecodeCufIter
: // Ditto
602 case Op::BreakTraceHint
:
603 return ControlFlowInfo::BreaksBB
;
612 return ControlFlowInfo::ChangesPC
;
614 return ControlFlowInfo::None
;
621 * Returns true if the instruction can potentially set PC to point
622 * to something other than the next instruction in the bytecode
625 opcodeChangesPC(const Op instr
) {
626 return opcodeControlFlowInfo(instr
) >= ControlFlowInfo::ChangesPC
;
632 * Returns true if the instruction always breaks a tracelet. Most
633 * instructions that change PC will break the tracelet, though some
634 * do not (ex. FCall).
637 opcodeBreaksBB(const Op instr
) {
638 return opcodeControlFlowInfo(instr
) == ControlFlowInfo::BreaksBB
;
642 * instrBreaksProfileBB --
644 * Similar to opcodeBreaksBB but more strict. We break profiling blocks after
645 * any instruction that can side exit, including instructions with predicted
648 bool instrBreaksProfileBB(const NormalizedInstruction
* instr
);
651 * If this returns true, we dont generate guards for any of the inputs
652 * to this instruction (this is essentially to avoid generating guards
653 * on behalf of interpreted instructions).
655 bool dontGuardAnyInputs(Op op
);
656 bool outputDependsOnInput(const Op instr
);
658 extern bool tc_dump();
659 const Func
* lookupImmutableMethod(const Class
* cls
, const StringData
* name
,
660 bool& magicCall
, bool staticLookup
,
663 // This is used to check that return types of builtins are not simple
664 // types. This is different from IS_REFCOUNTED_TYPE because builtins
665 // can return Variants, and we use KindOfUnknown to denote these
667 static inline bool isCppByRef(DataType t
) {
668 return t
!= KindOfBoolean
&& t
!= KindOfInt64
&& t
!= KindOfNull
;
671 // return true if type is passed in/out of C++ as String&/Array&/Object&
672 static inline bool isSmartPtrRef(DataType t
) {
673 return t
== KindOfString
|| t
== KindOfStaticString
||
674 t
== KindOfArray
|| t
== KindOfObject
||
678 void populateImmediates(NormalizedInstruction
&);
679 void preInputApplyMetaData(Unit::MetaHandle
, NormalizedInstruction
*);
680 enum class MetaMode
{
684 void readMetaData(Unit::MetaHandle
&, NormalizedInstruction
&, HhbcTranslator
&,
685 bool profiling
, MetaMode m
= MetaMode::Normal
);
686 bool instrMustInterp(const NormalizedInstruction
&);
688 typedef std::function
<Type(int)> LocalTypeFn
;
689 void getInputs(SrcKey startSk
, NormalizedInstruction
& inst
, InputInfos
& infos
,
690 const Func
* func
, const LocalTypeFn
& localType
);
691 void getInputsImpl(SrcKey startSk
, NormalizedInstruction
* inst
,
692 int& currentStackOffset
, InputInfos
& inputs
,
693 const Func
* func
, const LocalTypeFn
& localType
);
694 bool outputIsPredicted(NormalizedInstruction
& inst
);
695 bool callDestroysLocals(const NormalizedInstruction
& inst
,
697 int locPhysicalOffset(Location l
, const Func
* f
= nullptr);
698 bool shouldAnalyzeCallee(const NormalizedInstruction
*, const FPIEnt
*,
699 const Op
, const int);
701 namespace InstrFlags
{
702 enum OutTypeConstraints
{
706 OutStringImm
, // String w/ precisely known immediate.
715 OutThisObject
, // Object from current environment
716 OutFDesc
, // Blows away the current function desc
718 OutUnknown
, // Not known at tracelet compile-time
719 OutPred
, // Unknown, but give prediction a whirl.
720 OutCns
, // Constant; may be known at compile-time
721 OutVUnknown
, // type is V(unknown)
723 OutSameAsInput
, // type is the same as the first stack inpute
724 OutCInput
, // type is C(input)
725 OutVInput
, // type is V(input)
726 OutCInputL
, // type is C(type) of local input
727 OutVInputL
, // type is V(type) of local input
728 OutFInputL
, // type is V(type) of local input if current param is
729 // by ref, else type is C(type) of local input
730 OutFInputR
, // Like FInputL, but for R's on the stack.
732 OutArith
, // For Add, Sub, Mul
733 OutBitOp
, // For BitAnd, BitOr, BitXor
734 OutSetOp
, // For SetOpL
735 OutIncDec
, // For IncDecL
736 OutStrlen
, // OpStrLen
737 OutClassRef
, // KindOfClass
738 OutFPushCufSafe
, // FPushCufSafe pushes two values of different
739 // types and an ActRec
740 OutAsyncAwait
, // AwaitHandle pushes its input and then a bool
746 * Input codes indicate what an instruction reads, and some other
747 * things about their behavior. The order these show up in the inputs
748 * vector is given in getInputs(), and is relevant in a few cases
749 * (e.g. instructions taking both stack inputs and MVectors).
756 StackIns1
= 1 << 3, // Insert an element under top of stack
757 StackIns2
= 1 << 4, // Insert an element under top 2 of stack
758 FuncdRef
= 1 << 5, // Input to FPass*
759 FStack
= 1 << 6, // output of FPushFuncD and friends
760 Local
= 1 << 7, // Writes to a local
761 MVector
= 1 << 8, // Member-vector input
762 Iter
= 1 << 9, // Iterator in imm[0]
763 AllLocals
= 1 << 10, // All locals (used by RetC)
764 DontGuardStack1
= 1 << 11, // Dont force a guard on behalf of stack1 input
765 IgnoreInnerType
= 1 << 12, // Instruction doesnt care about the inner types
766 DontGuardAny
= 1 << 13, // Dont force a guard for any input
767 This
= 1 << 14, // Input to CheckThis
768 StackN
= 1 << 15, // pop N cells from stack; n = imm[0].u_IVA
769 BStackN
= 1 << 16, // consume N cells from stack for builtin call;
771 StackTop2
= Stack1
| Stack2
,
772 StackTop3
= Stack1
| Stack2
| Stack3
,
775 inline Operands
operator|(const Operands
& l
, const Operands
& r
) {
776 return Operands(int(r
) | int(l
));
781 InstrFlags::Operands in
;
782 InstrFlags::Operands out
;
783 InstrFlags::OutTypeConstraints type
; // How are outputs related to inputs?
787 const InstrInfo
& getInstrInfo(Op op
);
789 typedef const int COff
; // Const offsets