Add TranslArgs and delete a bunch of tx64 code
[hiphop-php.git] / hphp / runtime / vm / translator / translator.h
blobea2e6025d7d107a1b8e8513616e883e84a3b18d8
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010- 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>
29 #include <boost/ptr_container/ptr_vector.hpp>
31 #include "hphp/util/hash.h"
32 #include "hphp/util/timer.h"
33 #include "hphp/runtime/base/execution_context.h"
34 #include "hphp/runtime/vm/bytecode.h"
35 #include "hphp/runtime/vm/translator/immstack.h"
36 #include "hphp/runtime/vm/translator/runtime-type.h"
37 #include "hphp/runtime/vm/translator/fixup.h"
38 #include "hphp/runtime/vm/translator/writelease.h"
39 #include "hphp/runtime/vm/translator/trans-data.h"
40 #include "hphp/runtime/vm/debugger_hook.h"
41 #include "hphp/runtime/base/md5.h"
43 /* Translator front-end. */
44 namespace HPHP {
45 namespace Transl {
47 static const bool trustSigSegv = false;
49 static const uint32_t transCountersPerChunk = 1024 * 1024 / 8;
51 class TranslatorX64;
52 extern TranslatorX64* volatile nextTx64;
53 extern __thread TranslatorX64* tx64;
56 * REGSTATE_DIRTY when the live register state is spread across the
57 * stack and m_fixup, REGSTATE_CLEAN when it has been sync'ed into
58 * g_context.
60 enum VMRegState {
61 REGSTATE_CLEAN,
62 REGSTATE_DIRTY
64 extern __thread VMRegState tl_regState;
67 * A SrcKey is a logical source instruction, currently a unit/instruction pair.
68 * The units are identified by contents rather than Unit; Unit's are
69 * ephemeral, and we want to reuse SrcKey's when we encounter Unit's with
70 * the same contents.
72 struct SrcKey {
73 private:
74 Func::FuncId m_funcId;
76 public:
77 Offset m_offset;
79 SrcKey() : m_funcId(Func::InvalidId), m_offset(0) { }
81 SrcKey(const Func* f, Offset off) :
82 m_funcId(f->getFuncId()), m_offset(off) { }
84 SrcKey(const Func* f, const Opcode* i) :
85 m_funcId(f->getFuncId()), m_offset(f->unit()->offsetOf(i)) { }
87 int cmp(const SrcKey &r) const {
88 // Can't use memcmp because of pad bytes. Frowny.
89 #define CMP(field) \
90 if (field < r.field) return -1; \
91 if (field > r.field) return 1
92 CMP(getFuncId());
93 CMP(m_offset);
94 #undef CMP
95 return 0;
97 bool operator==(const SrcKey& r) const {
98 return cmp(r) == 0;
100 bool operator!=(const SrcKey& r) const {
101 return cmp(r) != 0;
103 bool operator<(const SrcKey& r) const {
104 return cmp(r) < 0;
106 bool operator>(const SrcKey& r) const {
107 return cmp(r) > 0;
109 // Hash function for both hash_map and tbb conventions.
110 static size_t hash(const SrcKey &sk) {
111 return HPHP::hash_int64_pair(sk.getFuncId(), uint64_t(sk.m_offset));
113 size_t operator()(const SrcKey& sk) const {
114 return hash(sk);
116 static bool equal(const SrcKey& sk1, const SrcKey& sk2) {
117 return sk1 == sk2;
120 // Packed representation of SrcKeys for use in contexts where we
121 // want atomicity. (SrcDB.)
122 typedef uint64_t AtomicInt;
124 AtomicInt toAtomicInt() const {
125 return uint64_t(getFuncId()) << 32 | uint64_t(m_offset);
128 static SrcKey fromAtomicInt(AtomicInt in) {
129 SrcKey k;
130 k.setFuncId(in >> 32);
131 k.m_offset = in & 0xffffffff;
132 return k;
135 void setFuncId(Func::FuncId id) {
136 assert(id != Func::InvalidId);
137 m_funcId = id;
139 Func::FuncId getFuncId() const {
140 assert(m_funcId != Func::InvalidId);
141 return m_funcId;
144 void trace(const char *fmt, ...) const;
145 void print(int ninstr) const;
146 std::string pretty() const;
147 int offset() const {
148 return m_offset;
150 void advance(const Unit* u) {
151 m_offset += instrLen(u->at(offset()));
155 typedef hphp_hash_set<SrcKey, SrcKey> SrcKeySet;
156 #define SKTRACE(level, sk, ...) \
157 ONTRACE(level, (sk).trace(__VA_ARGS__))
159 struct NormalizedInstruction;
161 // A DynLocation is a Location-in-execution: a location, along with
162 // whatever is known about its runtime type.
163 struct DynLocation {
164 Location location;
165 RuntimeType rtt;
166 NormalizedInstruction* source;
168 DynLocation(Location l, DataType t) : location(l), rtt(t), source(nullptr) {}
170 DynLocation(Location l, RuntimeType t) : location(l), rtt(t), source(nullptr) {}
172 DynLocation() : location(), rtt(KindOfInvalid), source(nullptr) {}
174 bool operator==(const DynLocation& r) const {
175 return rtt == r.rtt && location == r.location;
178 // Hash function
179 size_t operator()(const DynLocation &dl) const {
180 uint64_t rtthash = rtt(rtt);
181 uint64_t locHash = location(location);
182 return rtthash ^ locHash;
185 std::string pretty() const {
186 return Trace::prettyNode("DynLocation", location, rtt);
189 // Punch through a bunch of frequently called rtt and location methods.
190 // While this is unlovely here, we use DynLocation in bazillions of
191 // places in the translator, and constantly saying ".rtt" is worse.
192 bool isString() const {
193 return rtt.isString();
195 bool isInt() const {
196 return rtt.isInt();
198 bool isDouble() const {
199 return rtt.isDouble();
201 bool isBoolean() const {
202 return rtt.isBoolean();
204 bool isRef() const {
205 return rtt.isRef();
207 bool isValue() const {
208 return rtt.isValue();
210 bool isNull() const {
211 return rtt.isNull();
213 bool isObject() const {
214 return rtt.isObject();
216 bool isArray() const {
217 return rtt.isArray();
219 DataType valueType() const {
220 return rtt.valueType();
222 DataType innerType() const {
223 return rtt.innerType();
225 DataType outerType() const {
226 return rtt.outerType();
229 bool isStack() const {
230 return location.isStack();
232 bool isLocal() const {
233 return location.isLocal();
235 bool isLiteral() const {
236 return location.isLiteral();
239 // Uses the runtime state. True if this dynLocation can be overwritten by
240 // SetG's and SetM's.
241 bool canBeAliased() const;
244 // Flags that summarize the plan for handling a given instruction.
245 enum TXFlags {
246 Interp = 0, // default; must be boolean false
247 Supported = 1, // Not interpreted, though possibly with C++
248 NonReentrant = 2, // Supported with no possibility of reentry.
249 MachineCode = 4, // Supported without C++ at all.
250 Simple = NonReentrant | Supported,
251 Native = MachineCode | Simple
254 struct Tracelet;
255 struct TraceletContext;
257 // A NormalizedInstruction has been decorated with its typed inputs and
258 // outputs.
259 class NormalizedInstruction {
260 public:
261 NormalizedInstruction* next;
262 NormalizedInstruction* prev;
264 SrcKey source;
265 const Func* funcd; // The Func in the topmost AR on the stack. Guaranteed to
266 // be accurate. Don't guess about this. Note that this is
267 // *not* the function whose body the NI belongs to.
268 // Note that for an FPush* may be set to the (statically
269 // known Func* that /this/ instruction is pushing)
270 const StringData* funcName;
271 // For FCall's, an opaque identifier that is either null, or uniquely
272 // identifies the (functionName, -arity) pair of this call site.
273 const Unit* m_unit;
274 vector<DynLocation*> inputs;
275 DynLocation* outStack;
276 DynLocation* outLocal;
277 DynLocation* outLocal2; // Used for IterInitK, MIterInitK, IterNextK, and
278 // MIterNextK
279 DynLocation* outStack2; // Used for CGetL2
280 DynLocation* outStack3; // Used for CGetL3
281 vector<Location> deadLocs; // locations that die at the end of this
282 // instruction
283 ArgUnion imm[4];
284 ImmVector immVec; // vector immediate; will have !isValid() if the
285 // instruction has no vector immediate
287 // The member codes for the M-vector.
288 std::vector<MemberCode> immVecM;
291 * For property dims, if we know the Class* for the base when we'll
292 * be executing a given dim, it is stored here (at the index for the
293 * relevant member code minus 1, because the known class for the
294 * first member code is given by the base in inputs[]).
296 * Other entries here store null. See MetaInfo::MVecPropClass.
298 std::vector<Class*> immVecClasses;
301 * On certain FCalls, we can inspect the callee and generate a
302 * tracelet with information about what happens over there.
304 * The HHIR translator uses this to possibly inline callees.
306 std::unique_ptr<Tracelet> calleeTrace;
308 unsigned checkedInputs;
309 // StackOff: logical delta at *start* of this instruction to
310 // stack at tracelet entry.
311 int stackOff;
312 int sequenceNum;
313 bool hasConstImm:1;
314 bool startsBB:1;
315 bool breaksTracelet:1;
316 bool changesPC:1;
317 bool fuseBranch:1;
318 bool preppedByRef:1; // For FPass*; indicates parameter reffiness
319 bool manuallyAllocInputs:1;
320 bool invertCond:1;
321 bool outputPredicted:1;
322 bool outputPredictionStatic:1;
323 bool ignoreInnerType:1;
326 * skipSync indicates that a previous instruction that should have
327 * adjusted the stack (eg FCall, Req*) didnt, because it could see
328 * that the next one was going to immediately adjust it again
329 * (ie at this point, rVmSp holds the "correct" value, rather
330 * than the value it had at the beginning of the tracelet)
332 bool skipSync:1;
335 * grouped indicates that the tracelet should not be broken
336 * (eg by a side exit) between the preceding instruction and
337 * this one
339 bool grouped:1;
342 * guardedThis indicates that we know that ar->m_this is
343 * a valid $this. eg:
345 * $this->foo = 1; # needs to check that $this is non-null
346 * $this->bar = 2; # can skip the check
347 * return 5; # can decRef ar->m_this unconditionally
349 bool guardedThis:1;
352 * guardedCls indicates that we know the class exists
354 bool guardedCls:1;
357 * dont check the surprise flag
359 bool noSurprise:1;
362 noCtor is set on FPushCtorD to say that the ctor is
363 going to be skipped (so dont setup an actrec)
365 bool noCtor:1;
368 * instruction is statically known to have no effect, e.g. unboxing a Cell
370 bool noOp:1;
373 * Used with HHIR. Instruction shoud be interpreted, because previous attempt
374 * to translate it has failed.
376 bool interp:1;
379 * This is an FPush* that will be directly bound to a Func*
381 bool directCall:1;
384 * Indicates that a RetC/RetV should generate inlined return code
385 * rather than calling the shared stub.
387 bool inlineReturn:1;
389 // For returns, this tracks local ids that are statically known not
390 // to be reference counted at this point (i.e. won't require guards
391 // or decrefs).
392 boost::dynamic_bitset<> nonRefCountedLocals;
394 ArgUnion constImm;
395 TXFlags m_txFlags;
397 Op op() const;
398 Op mInstrOp() const;
399 PC pc() const;
400 const Unit* unit() const;
401 Offset offset() const;
403 NormalizedInstruction()
404 : next(nullptr)
405 , prev(nullptr)
406 , outStack(nullptr)
407 , outLocal(nullptr)
408 , outLocal2(nullptr)
409 , outStack2(nullptr)
410 , outStack3(nullptr)
411 , checkedInputs(0)
412 , hasConstImm(false)
413 , invertCond(false)
414 , ignoreInnerType(false)
415 , skipSync(false)
416 , grouped(false)
417 , guardedThis(false)
418 , guardedCls(false)
419 , noSurprise(false)
420 , noCtor(false)
421 , noOp(false)
422 , interp(false)
423 , directCall(false)
424 , inlineReturn(false)
425 , m_txFlags(Interp)
427 memset(imm, 0, sizeof(imm));
430 bool isJmpNZ() const {
431 assert(op() == OpJmpNZ || op() == OpJmpZ);
432 return (op() == OpJmpNZ) != invertCond;
435 bool isSupported() const {
436 return (m_txFlags & Supported) == Supported;
439 bool isSimple() const {
440 return (m_txFlags & Simple) == Simple;
443 bool isNative() const {
444 return (m_txFlags & Native) == Native;
447 void markInputInferred(int i) {
448 if (i < 32) checkedInputs |= 1u << i;
451 bool inputWasInferred(int i) const {
452 return i < 32 && ((checkedInputs >> i) & 1);
455 bool wasGroupedWith(Op op) const {
456 return grouped && prev->op() == op;
459 template<class... OpTypes>
460 bool wasGroupedWith(Op op, OpTypes... ops) const {
461 return wasGroupedWith(op) || wasGroupedWith(ops...);
464 enum OutputUse {
465 OutputUsed,
466 OutputUnused,
467 OutputInferred,
468 OutputDoesntCare
470 OutputUse getOutputUsage(DynLocation* output, bool ignorePops = false) const;
472 std::string toString() const;
475 // Return a summary string of the bytecode in a tracelet.
476 std::string traceletShape(const Tracelet&);
478 class TranslationFailedExc : public std::exception {
479 public:
480 const char* m_file; // must be static
481 const int m_line;
482 TranslationFailedExc(const char* file, int line) :
483 m_file(file), m_line(line) { }
486 class UnknownInputExc : public std::exception {
487 public:
488 const char* m_file; // must be static
489 const int m_line;
490 UnknownInputExc(const char* file, int line)
491 : m_file(file), m_line(line) { }
494 #define punt() do { \
495 throw TranslationFailedExc(__FILE__, __LINE__); \
496 } while(0)
498 #define throwUnknownInput() do { \
499 throw UnknownInputExc(__FILE__, __LINE__); \
500 } while(0);
502 class GuardType {
503 public:
504 explicit GuardType(DataType outer = KindOfInvalid,
505 DataType inner = KindOfInvalid);
506 explicit GuardType(const RuntimeType& rtt);
507 GuardType(const GuardType& other);
508 const DataType getOuterType() const;
509 const DataType getInnerType() const;
510 bool isSpecific() const;
511 bool isRelaxed() const;
512 bool isGeneric() const;
513 bool isCounted() const;
514 bool isMoreRefinedThan(const GuardType& other) const;
515 bool mayBeUninit() const;
516 GuardType getCountness() const;
517 GuardType getCountnessInit() const;
518 DataTypeCategory getCategory() const;
520 private:
521 DataType outerType;
522 DataType innerType;
526 * A tracelet is a unit of input to the back-end. It is a partially typed,
527 * non-maximal basic block, representing the next slice of the program to
528 * be executed.
529 * It is a consecutive set of instructions, only the last of which may be a
530 * transfer of control, annotated types and locations for each opcode's input
531 * and output.
533 typedef hphp_hash_map<Location, DynLocation*, Location> ChangeMap;
534 typedef hphp_hash_map<Location,RuntimeType,Location> TypeMap;
535 typedef ChangeMap DepMap;
536 typedef hphp_hash_set<Location, Location> LocationSet;
537 typedef hphp_hash_map<DynLocation*, GuardType> DynLocTypeMap;
539 struct InstrStream {
540 InstrStream() : first(nullptr), last(nullptr) {}
541 void append(NormalizedInstruction* ni);
542 void remove(NormalizedInstruction* ni);
543 NormalizedInstruction* first;
544 NormalizedInstruction* last;
547 struct RefDeps {
548 struct Record {
549 vector<bool> m_mask;
550 vector<bool> m_vals;
552 std::string pretty() const {
553 std::ostringstream out;
554 out << "mask=";
555 for (size_t i = 0; i < m_mask.size(); ++i) {
556 out << (m_mask[i] ? "1" : "0");
558 out << " vals=";
559 for (size_t i = 0; i < m_vals.size(); ++i) {
560 out << (m_vals[i] ? "1" : "0");
562 return out.str();
565 typedef hphp_hash_map<int64_t, Record, int64_hash> ArMap;
566 ArMap m_arMap;
568 RefDeps() {}
570 void addDep(int entryArDelta, unsigned argNum, bool isRef) {
571 if (m_arMap.find(entryArDelta) == m_arMap.end()) {
572 m_arMap[entryArDelta] = Record();
574 Record& r = m_arMap[entryArDelta];
575 if (argNum >= r.m_mask.size()) {
576 assert(argNum >= r.m_vals.size());
577 r.m_mask.resize(argNum + 1);
578 r.m_vals.resize(argNum + 1);
580 r.m_mask[argNum] = true;
581 r.m_vals[argNum] = isRef;
584 size_t size() const {
585 return m_arMap.size();
589 struct ActRecState {
590 // State for tracking function param reffiness. m_topFunc is the function
591 // for the activation record that is closest to the top of the stack, or
592 // NULL if it is currently unknown. A tracelet can be in one of three
593 // epistemological states: GUESSABLE, KNOWN, and UNKNOWABLE. We start out in
594 // GUESSABLE, with m_topFunc == NULL (not yet guessed); when it's time to
595 // guess, we will use the ActRec seen on the top of stack at compilation
596 // time as a hint for refs going forward.
598 // The KNOWN state is a very strong guarantee. It means that no matter when
599 // this tracelet is executed, no matter what else has happened, the ActRec
600 // closest to the top of the stack WILL contain m_topFunc. This means: if that
601 // function is defined conditionally, or defined in some other module, you
602 // cannot correctly make that assertion. KNOWN indicates absolute certainty
603 // about all possible futures.
605 // This strange "not-guessed-yet-but-could" state is required by our
606 // VM design; at present, the ActRec is not easily recoverable from an
607 // arbitrary instruction boundary. However, it can be recovered from the
608 // instructions that need to do so.
609 static const int InvalidEntryArDelta = INT_MAX;
611 enum State {
612 GUESSABLE, KNOWN, UNKNOWABLE
615 struct Record {
616 State m_state;
617 const Func* m_topFunc;
618 int m_entryArDelta; // delta at BB entry to guessed ActRec.
621 std::vector<Record> m_arStack;
623 ActRecState() {}
624 void pushFuncD(const Func* func);
625 void pushDynFunc();
626 void pop();
627 bool getReffiness(int argNum, int stackOffset, RefDeps* outRefDeps);
628 const Func* getCurrentFunc();
629 State getCurrentState();
632 struct Tracelet : private boost::noncopyable {
633 ChangeMap m_changes;
634 DepMap m_dependencies;
635 DepMap m_resolvedDeps; // dependencies resolved by static analysis
636 InstrStream m_instrStream;
637 int m_stackChange;
639 // SrcKey for the start of the Tracelet. This might be different
640 // from m_instrStream.first->source.
641 SrcKey m_sk;
643 // After this Tracelet runs, this is the SrcKey for the next
644 // instruction that we should go to. Note that this can be
645 // different from m_instrStream.last->source.advance() if we removed
646 // the last instruction from the stream.
647 SrcKey m_nextSk;
649 // numOpcodes is the number of raw opcode instructions, before optimization.
650 // The immediates optimization may both:
652 // 1. remove the first opcode, thus making
653 // sk.instr != instrs.first->source.instr
654 // 2. remove no longer needed instructions
655 int m_numOpcodes;
657 // Assumptions about entering actRec's reffiness.
658 ActRecState m_arState;
659 RefDeps m_refDeps;
661 // Live range suport.
663 // Maintain a per-location last-read and last-written map. We don't need
664 // to remember the start of the live range, since we implicitly discover it
665 // at translation time. The entries in these maps are the sequence number
666 // of the instruction after which the location is no longer read/written.
667 typedef hphp_hash_map<Location, int, Location> RangeMap;
668 RangeMap m_liveEnd;
669 RangeMap m_liveDirtyEnd;
672 * If we were unable to make sense of the instruction stream (e.g., it
673 * used instructions that the translator does not understand), then this
674 * tracelet is useful only for defining the boundaries of a basic block.
675 * The low-level translator can handle this by backing off to the
676 * bytecode interpreter.
678 bool m_analysisFailed;
680 // Track which NormalizedInstructions and DynLocations are owned by this
681 // Tracelet; used for cleanup purposes
682 boost::ptr_vector<NormalizedInstruction> m_instrs;
683 boost::ptr_vector<DynLocation> m_dynlocs;
685 Tracelet() :
686 m_stackChange(0),
687 m_arState(),
688 m_analysisFailed(false) { }
690 void constructLiveRanges();
691 bool isLiveAfterInstr(Location l, const NormalizedInstruction& i) const;
692 bool isWrittenAfterInstr(Location l, const NormalizedInstruction& i) const;
694 NormalizedInstruction* newNormalizedInstruction();
695 DynLocation* newDynLocation(Location l, DataType t);
696 DynLocation* newDynLocation(Location l, RuntimeType t);
697 DynLocation* newDynLocation();
699 /* These aren't merged into a single method with a default argument
700 * to make gdb happy. */
701 void print() const;
702 void print(std::ostream& out) const;
705 enum TransKind {
706 TransInterp = 0,
707 TransNormalIR = 1,
708 TransAnchor = 2,
709 TransProlog = 3,
712 const char* getTransKindName(TransKind kind);
715 * Used to maintain a mapping from the bytecode to its corresponding x86.
717 struct TransBCMapping {
718 Offset bcStart;
719 TCA aStart;
720 TCA astubsStart;
724 * A record with various information about a translation.
726 struct TransRec {
727 TransID id;
728 TransKind kind;
729 SrcKey src;
730 MD5 md5;
731 uint32_t bcStopOffset;
732 vector<DynLocation> dependencies;
733 TCA aStart;
734 uint32_t aLen;
735 TCA astubsStart;
736 uint32_t astubsLen;
737 TCA counterStart;
738 uint8_t counterLen;
739 vector<TransBCMapping> bcMapping;
741 static const TransID InvalidID = -1LL;
743 TransRec() {}
745 TransRec(SrcKey s,
746 MD5 _md5,
747 TransKind _kind,
748 TCA _aStart = 0,
749 uint32_t _aLen = 0,
750 TCA _astubsStart = 0,
751 uint32_t _astubsLen = 0) :
752 id(0), kind(_kind), src(s), md5(_md5), bcStopOffset(0),
753 aStart(_aStart), aLen(_aLen),
754 astubsStart(_astubsStart), astubsLen(_astubsLen),
755 counterStart(0), counterLen(0) { }
757 TransRec(SrcKey s,
758 MD5 _md5,
759 TransKind _kind,
760 const Tracelet& t,
761 TCA _aStart = 0,
762 uint32_t _aLen = 0,
763 TCA _astubsStart = 0,
764 uint32_t _astubsLen = 0,
765 TCA _counterStart = 0,
766 uint8_t _counterLen = 0,
767 vector<TransBCMapping> _bcMapping = vector<TransBCMapping>()) :
768 id(0), kind(_kind), src(s), md5(_md5),
769 bcStopOffset(t.m_nextSk.offset()), aStart(_aStart), aLen(_aLen),
770 astubsStart(_astubsStart), astubsLen(_astubsLen),
771 counterStart(_counterStart), counterLen(_counterLen),
772 bcMapping(_bcMapping) {
773 for (DepMap::const_iterator dep = t.m_dependencies.begin();
774 dep != t.m_dependencies.end();
775 ++dep) {
776 dependencies.push_back(*dep->second);
780 void setID(TransID newID) { id = newID; }
781 string print(uint64_t profCount) const;
784 struct TranslArgs {
785 TranslArgs(const SrcKey& sk, bool align)
786 : m_sk(sk)
787 , m_src(nullptr)
788 , m_align(align)
789 , m_interp(false)
792 TranslArgs& sk(const SrcKey& sk) {
793 m_sk = sk;
794 return *this;
796 TranslArgs& src(TCA src) {
797 m_src = src;
798 return *this;
800 TranslArgs& align(bool align) {
801 m_align = align;
802 return *this;
804 TranslArgs& interp(bool interp) {
805 m_interp = interp;
806 return *this;
809 SrcKey m_sk;
810 TCA m_src;
811 bool m_align;
812 bool m_interp;
816 * Translator annotates a tracelet with input/output locations/types.
818 class Translator {
819 static const int MaxJmpsTracedThrough = 5;
821 public:
822 // kMaxInlineReturnDecRefs is the maximum ref-counted locals to
823 // generate an inline return for.
824 static const int kMaxInlineReturnDecRefs = 1;
826 private:
827 friend struct TraceletContext;
829 void analyzeSecondPass(Tracelet& t);
830 void analyzeCallee(TraceletContext&,
831 Tracelet& parent,
832 NormalizedInstruction* fcall);
833 void preInputApplyMetaData(Unit::MetaHandle, NormalizedInstruction*);
834 bool applyInputMetaData(Unit::MetaHandle&,
835 NormalizedInstruction* ni,
836 TraceletContext& tas,
837 InputInfos& ii);
838 void getInputs(Tracelet& t,
839 NormalizedInstruction* ni,
840 int& currentStackOffset,
841 InputInfos& inputs,
842 const TraceletContext& tas);
843 void getOutputs(Tracelet& t,
844 NormalizedInstruction* ni,
845 int& currentStackOffset,
846 bool& varEnvTaint);
847 void relaxDeps(Tracelet& tclet, TraceletContext& tctxt);
848 void reanalizeConsumers(Tracelet& tclet, DynLocation* depDynLoc);
849 DataTypeCategory getOperandConstraintCategory(NormalizedInstruction* instr,
850 size_t opndIdx);
851 GuardType getOperandConstraintType(NormalizedInstruction* instr,
852 size_t opndIdx,
853 const GuardType& specType);
855 void constrainOperandType(GuardType& relxType,
856 NormalizedInstruction* instr,
857 size_t opndIdx,
858 const GuardType& specType);
861 RuntimeType liveType(Location l, const Unit &u);
862 RuntimeType liveType(const Cell* outer, const Location& l);
864 void consumeStackEntry(Tracelet* tlet, NormalizedInstruction* ni);
865 void produceStackEntry(Tracelet* tlet, NormalizedInstruction* ni);
866 void produceDataRef(Tracelet* tlet, NormalizedInstruction* ni,
867 Location loc);
869 void findImmable(ImmStack &stack, NormalizedInstruction* ni);
871 virtual void syncWork() = 0;
872 virtual void invalidateSrcKey(SrcKey sk) = 0;
874 protected:
875 void requestResetHighLevelTranslator();
877 TCA m_resumeHelper;
878 TCA m_resumeHelperRet;
880 typedef std::map<TCA, TransID> TransDB;
881 TransDB m_transDB;
882 vector<TransRec> m_translations;
883 vector<uint64_t*> m_transCounters;
885 int64_t m_createdTime;
887 static Lease s_writeLease;
888 static volatile bool s_replaceInFlight;
890 public:
892 Translator();
893 virtual ~Translator();
894 static Translator* Get();
895 static Lease& WriteLease() {
896 return s_writeLease;
898 static bool ReplaceInFlight() {
899 return s_replaceInFlight;
901 static RuntimeType outThisObjectType();
904 * Interface between the arch-dependent translator and outside world.
906 virtual void requestInit() = 0;
907 virtual void requestExit() = 0;
908 virtual void analyzeInstr(Tracelet& t, NormalizedInstruction& i) = 0;
909 virtual TCA funcPrologue(Func* f, int nArgs, ActRec* ar = nullptr) = 0;
910 virtual TCA getCallToExit() = 0;
911 virtual TCA getRetFromInterpretedFrame() = 0;
912 virtual std::string getUsage() = 0;
913 virtual size_t getCodeSize() = 0;
914 virtual size_t getStubSize() = 0;
915 virtual size_t getTargetCacheSize() = 0;
916 virtual bool dumpTC(bool ignoreLease = false) = 0;
917 virtual bool dumpTCCode(const char *filename) = 0;
918 virtual bool dumpTCData() = 0;
919 virtual void protectCode() = 0;
920 virtual void unprotectCode() = 0;
921 virtual bool isValidCodeAddress(TCA) const = 0;
923 const TransDB& getTransDB() const {
924 return m_transDB;
927 const TransRec* getTransRec(TCA tca) const {
928 if (!isTransDBEnabled()) return nullptr;
930 TransDB::const_iterator it = m_transDB.find(tca);
931 if (it == m_transDB.end()) {
932 return nullptr;
934 if (it->second >= m_translations.size()) {
935 return nullptr;
937 return &m_translations[it->second];
940 const TransRec* getTransRec(TransID transId) const {
941 if (!isTransDBEnabled()) return nullptr;
943 always_assert(transId < m_translations.size());
944 return &m_translations[transId];
947 TransID getNumTrans() const {
948 return m_translations.size();
951 TransID getCurrentTransID() const {
952 return m_translations.size();
955 uint64_t* getTransCounterAddr();
957 uint64_t getTransCounter(TransID transId) const;
959 void setTransCounter(TransID transId, uint64_t value);
961 uint32_t addTranslation(const TransRec& transRec) {
962 if (Trace::moduleEnabledRelease(Trace::trans, 1)) {
963 // Log the translation's size, creation time, SrcKey, and size
964 Trace::traceRelease("New translation: %lld %s %u %u %d\n",
965 Timer::GetCurrentTimeMicros() - m_createdTime,
966 transRec.src.pretty().c_str(), transRec.aLen,
967 transRec.astubsLen, transRec.kind);
970 if (!isTransDBEnabled()) return -1u;
971 uint32_t id = getCurrentTransID();
972 m_translations.push_back(transRec);
973 m_translations[id].setID(id);
975 if (transRec.aLen > 0) {
976 m_transDB[transRec.aStart] = id;
978 if (transRec.astubsLen > 0) {
979 m_transDB[transRec.astubsStart] = id;
982 return id;
986 * Create a Tracelet for the given SrcKey, which must actually be
987 * the current VM frame.
989 * XXX The analysis pass will inspect the live state of the VM stack
990 * as needed to determine the current types of in-flight values.
992 std::unique_ptr<Tracelet> analyze(SrcKey sk, const TypeMap& = TypeMap());
994 void postAnalyze(NormalizedInstruction* ni, SrcKey& sk,
995 Tracelet& t, TraceletContext& tas);
996 void advance(Opcode const **instrs);
997 static int locPhysicalOffset(Location l, const Func* f = nullptr);
998 static Location tvToLocation(const TypedValue* tv, const TypedValue* frame);
999 static bool typeIsString(DataType type) {
1000 return type == KindOfString || type == KindOfStaticString;
1002 static bool liveFrameIsPseudoMain();
1004 inline void sync() {
1005 if (tl_regState == REGSTATE_CLEAN) return;
1006 syncWork();
1009 inline bool stateIsDirty() {
1010 return tl_regState == REGSTATE_DIRTY;
1013 inline bool isTransDBEnabled() const {
1014 return debug || RuntimeOption::EvalDumpTC;
1018 * If this returns true, we dont generate guards for any of the
1019 * inputs to this instruction (this is essentially to avoid
1020 * generating guards on behalf of interpreted instructions).
1022 virtual bool dontGuardAnyInputs(Opcode op) { return false; }
1024 protected:
1025 PCFilter m_dbgBLPC;
1026 SrcKeySet m_dbgBLSrcKey;
1027 Mutex m_dbgBlacklistLock;
1028 bool isSrcKeyInBL(const Unit* unit, const SrcKey& sk);
1030 private:
1031 int m_analysisDepth;
1033 public:
1034 void clearDbgBL();
1035 bool addDbgBLPC(PC pc);
1036 virtual bool addDbgGuards(const Unit* unit) = 0;
1037 virtual bool addDbgGuard(const Func* func, Offset offset) = 0;
1039 TCA getResumeHelper() {
1040 return m_resumeHelper;
1042 TCA getResumeHelperRet() {
1043 return m_resumeHelperRet;
1046 int analysisDepth() const {
1047 assert(m_analysisDepth >= 0);
1048 return m_analysisDepth;
1052 int getStackDelta(const NormalizedInstruction& ni);
1054 enum ControlFlowInfo {
1055 ControlFlowNone,
1056 ControlFlowChangesPC,
1057 ControlFlowBreaksBB
1060 static inline ControlFlowInfo
1061 opcodeControlFlowInfo(const Opcode instr) {
1062 switch (instr) {
1063 case OpJmp:
1064 case OpJmpZ:
1065 case OpJmpNZ:
1066 case OpSwitch:
1067 case OpSSwitch:
1068 case OpContExit:
1069 case OpContRetC:
1070 case OpRetC:
1071 case OpRetV:
1072 case OpExit:
1073 case OpFatal:
1074 case OpIterNext:
1075 case OpIterNextK:
1076 case OpMIterNext:
1077 case OpMIterNextK:
1078 case OpIterInit: // May branch to fail case.
1079 case OpIterInitK: // Ditto
1080 case OpMIterInit: // Ditto
1081 case OpMIterInitK: // Ditto
1082 case OpThrow:
1083 case OpUnwind:
1084 case OpEval:
1085 case OpNativeImpl:
1086 case OpContHandle:
1087 return ControlFlowBreaksBB;
1088 case OpFCall:
1089 case OpFCallArray:
1090 case OpContEnter:
1091 case OpIncl:
1092 case OpInclOnce:
1093 case OpReq:
1094 case OpReqOnce:
1095 case OpReqDoc:
1096 return ControlFlowChangesPC;
1097 default:
1098 return ControlFlowNone;
1103 * opcodeChangesPC --
1105 * Returns true if the instruction can potentially set PC to point
1106 * to something other than the next instruction in the bytecode
1108 static inline bool
1109 opcodeChangesPC(const Opcode instr) {
1110 return opcodeControlFlowInfo(instr) >= ControlFlowChangesPC;
1114 * opcodeBreaksBB --
1116 * Returns true if the instruction always breaks a tracelet. Most
1117 * instructions that change PC will break the tracelet, though some
1118 * do not (ex. FCall).
1120 static inline bool
1121 opcodeBreaksBB(const Opcode instr) {
1122 return opcodeControlFlowInfo(instr) == ControlFlowBreaksBB;
1125 bool outputDependsOnInput(const Opcode instr);
1127 extern bool tc_dump();
1128 const Func* lookupImmutableMethod(const Class* cls, const StringData* name,
1129 bool& magicCall, bool staticLookup);
1131 // This is used to check that return types of builtins are not simple
1132 // types. This is different from IS_REFCOUNTED_TYPE because builtins
1133 // can return Variants, and we use KindOfUnknown to denote these
1134 // return types.
1135 static inline bool isCppByRef(DataType t) {
1136 return t != KindOfBoolean && t != KindOfInt64 && t != KindOfNull;
1139 // return true if type is passed in/out of C++ as String&/Array&/Object&
1140 static inline bool isSmartPtrRef(DataType t) {
1141 return t == KindOfString || t == KindOfStaticString ||
1142 t == KindOfArray || t == KindOfObject;
1145 } } // HPHP::Transl
1147 #endif