remove operator-> from String
[hiphop-php.git] / hphp / runtime / vm / jit / translator.h
blob9fcc4a44a3e522fd29b5227924ad6409e3836964
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
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_
19 #include <limits.h>
20 #include <string.h>
21 #include <stdio.h>
22 #include <assert.h>
23 #include <memory>
24 #include <map>
25 #include <vector>
26 #include <set>
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. */
48 namespace HPHP {
49 namespace JIT {
50 struct HhbcTranslator;
51 struct IRTranslator;
53 namespace Debug {
54 struct DebugInfo;
56 namespace JIT {
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 {
66 CLEAN,
67 DIRTY
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.
75 struct DynLocation {
76 Location location;
77 RuntimeType rtt;
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;
89 // Hash function
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();
106 bool isInt() const {
107 return rtt.isInt();
109 bool isDouble() const {
110 return rtt.isDouble();
112 bool isBoolean() const {
113 return rtt.isBoolean();
115 bool isRef() const {
116 return rtt.isRef();
118 bool isRefToObject() const {
119 return rtt.isRef() && innerType() == KindOfObject;
121 bool isValue() const {
122 return rtt.isValue();
124 bool isNull() const {
125 return rtt.isNull();
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.
159 enum TXFlags {
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
168 struct Tracelet;
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 @ {}:{}",
177 file, line).str())
181 struct UnknownInputExc : std::runtime_error {
182 UnknownInputExc(const char* file, int line)
183 : std::runtime_error(folly::format("UnknownInputExc @ {}:{}",
184 file, line).str())
185 , m_file(file)
186 , m_line(line)
189 const char* m_file; // must be static
190 const int m_line;
193 #define punt() do { \
194 throw JIT::TranslationFailedExc(__FILE__, __LINE__); \
195 } while(0)
197 #define throwUnknownInput() do { \
198 throw JIT::UnknownInputExc(__FILE__, __LINE__); \
199 } while(0);
201 struct GuardType {
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;
225 private:
226 DataType outerType;
227 DataType innerType;
228 union {
229 const Class* klass;
230 struct {
231 bool arrayKindValid;
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 {
248 MD5 md5;
249 Offset bcStart;
250 TCA aStart;
251 TCA astubsStart;
255 * A record with various information about a translation.
257 struct TransRec {
258 TransID id;
259 TransKind kind;
260 SrcKey src;
261 MD5 md5;
262 Offset bcStopOffset;
263 std::vector<DynLocation>
264 dependencies;
265 TCA aStart;
266 uint32_t aLen;
267 TCA astubsStart;
268 uint32_t astubsLen;
269 std::vector<TransBCMapping>
270 bcMapping;
272 TransRec() {}
274 TransRec(SrcKey s,
275 MD5 _md5,
276 TransKind _kind,
277 TCA _aStart = 0,
278 uint32_t _aLen = 0,
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)
286 TransRec(SrcKey s,
287 MD5 _md5,
288 TransKind _kind,
289 const Tracelet* t,
290 TCA _aStart = 0,
291 uint32_t _aLen = 0,
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;
301 struct TranslArgs {
302 TranslArgs(const SrcKey& sk, bool align)
303 : m_sk(sk)
304 , m_align(align)
305 , m_interp(false)
306 , m_setFuncBody(false)
307 , m_transId(InvalidID)
308 , m_region(nullptr)
311 TranslArgs& sk(const SrcKey& sk) {
312 m_sk = sk;
313 return *this;
315 TranslArgs& align(bool align) {
316 m_align = align;
317 return *this;
319 TranslArgs& interp(bool interp) {
320 m_interp = interp;
321 return *this;
323 TranslArgs& setFuncBody() {
324 m_setFuncBody = true;
325 return *this;
327 TranslArgs& transId(TransID transId) {
328 m_transId = transId;
329 return *this;
331 TranslArgs& region(JIT::RegionDescPtr region) {
332 m_region = region;
333 return *this;
336 SrcKey m_sk;
337 bool m_align;
338 bool m_interp;
339 bool m_setFuncBody;
340 TransID m_transId;
341 JIT::RegionDescPtr m_region;
344 class Translator;
345 extern Translator* tx;
348 * Translator annotates a tracelet with input/output locations/types.
350 struct Translator {
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;
359 private:
360 friend struct TraceletContext;
362 void analyzeCallee(TraceletContext&,
363 Tracelet& parent,
364 NormalizedInstruction* fcall);
365 bool applyInputMetaData(Unit::MetaHandle&,
366 NormalizedInstruction* ni,
367 TraceletContext& tas,
368 InputInfos& ii);
369 void handleAssertionEffects(Tracelet&,
370 const NormalizedInstruction&,
371 TraceletContext&,
372 int currentStackOffset);
373 void getOutputs(Tracelet& t,
374 NormalizedInstruction* ni,
375 int& currentStackOffset,
376 bool& varEnvTaint);
377 void relaxDeps(Tracelet& tclet, TraceletContext& tctxt);
378 void constrainDep(const DynLocation* loc,
379 NormalizedInstruction* firstInstr,
380 GuardType specType,
381 GuardType& relxType);
382 DataTypeCategory getOperandConstraintCategory(NormalizedInstruction* instr,
383 size_t opndIdx,
384 const GuardType& specType);
385 GuardType getOperandConstraintType(NormalizedInstruction* instr,
386 size_t opndIdx,
387 const GuardType& specType);
389 void constrainOperandType(GuardType& relxType,
390 NormalizedInstruction* instr,
391 size_t opndIdx,
392 const GuardType& specType);
395 RuntimeType liveType(Location l, const Unit &u, bool specialize = false);
396 RuntimeType liveType(const Cell* outer,
397 const Location& l,
398 bool specialize = false);
400 public:
401 enum TranslateResult {
402 Failure,
403 Retry,
404 Success
406 static const char* translateResultName(TranslateResult r);
407 void traceStart(Offset initBcOffset, Offset initSpOffset, bool inGenerator,
408 const Func* func);
409 void traceEnd();
410 void traceFree();
412 void requestResetHighLevelTranslator();
414 public:
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);
422 private:
423 typedef std::map<TCA, TransID> TransDB;
424 TransDB m_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;
432 public:
433 JIT::IRTranslator* irTrans() {
434 return m_irTrans.get();
437 private:
438 SrcDB m_srcDB;
440 static Lease s_writeLease;
442 public:
444 Translator();
445 virtual ~Translator();
446 static Lease& WriteLease() {
447 return s_writeLease;
449 static RuntimeType outThisObjectType();
451 const TransDB& getTransDB() const {
452 return m_transDB;
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()) {
460 return nullptr;
462 if (it->second >= m_translations.size()) {
463 return nullptr;
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 {
493 return m_srcDB;
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;
517 private:
518 PCFilter m_dbgBLPC;
519 hphp_hash_set<SrcKey,SrcKey::Hasher> m_dbgBLSrcKey;
520 Mutex m_dbgBlacklistLock;
522 public:
523 bool isSrcKeyInBL(const SrcKey& sk);
525 private:
526 TransKind m_mode;
527 ProfData* m_profData;
529 private:
530 int m_analysisDepth;
532 public:
533 void clearDbgBL();
534 bool addDbgBLPC(PC pc);
536 ProfData* profData() const {
537 return m_profData;
540 TransKind mode() const {
541 return m_mode;
543 void setMode(TransKind mode) {
544 m_mode = 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
553 // a new space.
554 bool replace();
557 int getStackDelta(const NormalizedInstruction& ni);
558 int64_t getStackPopped(PC pc);
559 int64_t getStackPushed(PC pc);
561 enum class ControlFlowInfo {
562 None,
563 ChangesPC,
564 BreaksBB
567 inline ControlFlowInfo
568 opcodeControlFlowInfo(const Op instr) {
569 switch (instr) {
570 case Op::Jmp:
571 case Op::JmpNS:
572 case Op::JmpZ:
573 case Op::JmpNZ:
574 case Op::Switch:
575 case Op::SSwitch:
576 case Op::ContSuspend:
577 case Op::ContSuspendK:
578 case Op::ContRetC:
579 case Op::RetC:
580 case Op::RetV:
581 case Op::Exit:
582 case Op::Fatal:
583 case Op::IterNext:
584 case Op::IterNextK:
585 case Op::MIterNext:
586 case Op::MIterNextK:
587 case Op::WIterNext:
588 case Op::WIterNextK:
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
596 case Op::IterBreak:
597 case Op::Throw:
598 case Op::Unwind:
599 case Op::Eval:
600 case Op::NativeImpl:
601 case Op::ContHandle:
602 case Op::BreakTraceHint:
603 return ControlFlowInfo::BreaksBB;
604 case Op::FCall:
605 case Op::FCallArray:
606 case Op::ContEnter:
607 case Op::Incl:
608 case Op::InclOnce:
609 case Op::Req:
610 case Op::ReqOnce:
611 case Op::ReqDoc:
612 return ControlFlowInfo::ChangesPC;
613 default:
614 return ControlFlowInfo::None;
619 * opcodeChangesPC --
621 * Returns true if the instruction can potentially set PC to point
622 * to something other than the next instruction in the bytecode
624 inline bool
625 opcodeChangesPC(const Op instr) {
626 return opcodeControlFlowInfo(instr) >= ControlFlowInfo::ChangesPC;
630 * opcodeBreaksBB --
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).
636 inline bool
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
646 * output.
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,
661 Class* ctx);
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
666 // return types.
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 ||
675 t == KindOfResource;
678 void populateImmediates(NormalizedInstruction&);
679 void preInputApplyMetaData(Unit::MetaHandle, NormalizedInstruction*);
680 enum class MetaMode {
681 Normal,
682 Legacy,
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,
696 const Func* caller);
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 {
703 OutNull,
704 OutNullUninit,
705 OutString,
706 OutStringImm, // String w/ precisely known immediate.
707 OutDouble,
708 OutBoolean,
709 OutBooleanImm,
710 OutInt64,
711 OutArray,
712 OutArrayImm,
713 OutObject,
714 OutResource,
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
742 OutNone,
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).
751 enum Operands {
752 None = 0,
753 Stack3 = 1 << 0,
754 Stack2 = 1 << 1,
755 Stack1 = 1 << 2,
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;
770 // n = imm[0].u_IVA
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));
780 struct InstrInfo {
781 InstrFlags::Operands in;
782 InstrFlags::Operands out;
783 InstrFlags::OutTypeConstraints type; // How are outputs related to inputs?
784 int numPushed;
787 const InstrInfo& getInstrInfo(Op op);
789 typedef const int COff; // Const offsets
791 } } // HPHP::JIT
793 #endif