2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present 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 +----------------------------------------------------------------------+
17 #ifndef incl_HPHP_VM_UNIT_H_
18 #define incl_HPHP_VM_UNIT_H_
20 #include "hphp/parser/location.h"
22 #include "hphp/runtime/base/typed-value.h"
23 #include "hphp/runtime/base/repo-auth-type-array.h"
24 #include "hphp/runtime/vm/class.h"
25 #include "hphp/runtime/vm/containers.h"
26 #include "hphp/runtime/vm/hhbc.h"
27 #include "hphp/runtime/vm/named-entity.h"
28 #include "hphp/runtime/vm/named-entity-pair-table.h"
29 #include "hphp/runtime/vm/preclass.h"
30 #include "hphp/runtime/vm/record.h"
31 #include "hphp/runtime/vm/type-alias.h"
33 #include "hphp/util/compact-vector.h"
34 #include "hphp/util/fixed-vector.h"
35 #include "hphp/util/functional.h"
36 #include "hphp/util/hash-map.h"
37 #include "hphp/util/lock-free-ptr-wrapper.h"
38 #include "hphp/util/mutex.h"
39 #include "hphp/util/service-data.h"
40 #include "hphp/util/sha1.h"
49 ///////////////////////////////////////////////////////////////////////////////
60 ///////////////////////////////////////////////////////////////////////////////
64 * Where was a given Unit defined from?
66 enum class UnitOrigin
{
71 ///////////////////////////////////////////////////////////////////////////////
75 * Delimiter pairs for a location in the source code.
82 explicit SourceLoc(const Location::Range
& l
);
85 * Reset to, or check for, the invalid state.
91 * Set to a parser Location.
93 void setLoc(const Location::Range
* l
);
98 bool same(const SourceLoc
* l
) const;
99 bool operator==(const SourceLoc
& l
) const;
102 * Start and end lines and characters.
104 * The default {1, 1, 1, 1} is an invalid sentinel value.
113 * Pair of (base, past) offsets.
118 OffsetRange(Offset base
, Offset past
)
127 using OffsetRangeVec
= std::vector
<OffsetRange
>;
130 * Generic entry for representing many-to-one mappings of Offset -> T.
132 * Each entry's `pastOffset' is expected to be the offset just past the range
133 * of offsets which logically map to its `val'. In this way, by maintaining a
134 * relatively sparse set of entries in a vector, we can use least upper bound
135 * searches on an offset key to find its corresponding T.
137 * The values of `pastOffset' in such a table are expected to be sorted and
138 * unique, but the values of `val' need not be.
150 TableEntry(Offset pastOffset
, T val
)
151 : m_pastOffset(pastOffset
)
158 Offset
pastOffset() const;
164 bool operator<(const TableEntry
& other
) const;
166 template<class SerDe
> void serde(SerDe
& sd
);
169 Offset m_pastOffset
{0};
174 * Table specializations.
176 using LineEntry
= TableEntry
<int>;
177 using SourceLocEntry
= TableEntry
<SourceLoc
>;
178 using LineInfo
= std::pair
<OffsetRange
, int>;
180 using LineTable
= std::vector
<LineEntry
>;
181 using SourceLocTable
= std::vector
<SourceLocEntry
>;
182 using FuncTable
= VMCompactVector
<const Func
*>;
185 * Get the line number or SourceLoc for Offset `pc' in `table'.
187 int getLineNumber(const LineTable
& table
, Offset pc
);
188 bool getSourceLoc(const SourceLocTable
& table
, Offset pc
, SourceLoc
& sLoc
);
189 void stashLineTable(const Unit
* unit
, LineTable table
);
190 void stashExtendedLineTable(const Unit
* unit
, SourceLocTable table
);
192 const SourceLocTable
& getSourceLocTable(const Unit
*);
195 * Sum of all Unit::m_bclen
197 extern ServiceData::ExportedTimeSeries
* g_hhbc_size
;
199 ///////////////////////////////////////////////////////////////////////////////
202 * Metadata about a compilation unit.
204 * Contains the list of PreClasses and global functions, along with a special
205 * function called the 'pseudomain', which is logically invoked (modulo
206 * optimizations that avoid it) during execution when the unit is included or
210 friend struct UnitExtended
;
211 friend struct UnitEmitter
;
212 friend struct UnitRepoProxy
;
214 /////////////////////////////////////////////////////////////////////////////
219 * The Unit's current merge state.
221 * Merging is the process by which functions, classes, and constants defined
222 * in a pseudomain are added to the unit in advance (or, optimistically,
223 * instead of) running the pseudomain's code. This is necessary for
224 * correctness in a number of cases---e.g., toplevel functions defined in the
225 * pseudomain need to be available before the line where the definition
228 * Whenever we want to evaluate a Unit, we call merge() on it, and then
229 * invoke its pseudomain only if necessary.
231 enum MergeState
: uint8_t {
235 UniqueFuncs
= 1 << 2,
236 NeedsCompact
= 1 << 3,
242 * Information on all the mergeable defs within a Unit.
244 * Allocated with a variable-length pointer array in m_mergeables, structured
246 * - the Unit's pseudomain
247 * - hoistable functions (i.e., toplevel functions that need to be available
248 * from the beginning of the pseudomain)
249 * - all other mergeable objects, with the bottom three bits of the pointer
250 * tagged with a MergeKind
253 using FuncRange
= folly::Range
<Func
* const*>;
254 using MutableFuncRange
= folly::Range
<Func
**>;
257 * Allocate a new MergeInfo with `num' mergeables.
259 static MergeInfo
* alloc(size_t num
);
264 * funcHoistableBegin() is in (funcBegin, funcEnd].
266 Func
** funcBegin() const;
267 Func
** funcEnd() const;
268 Func
** funcHoistableBegin() const;
273 * All ranges end at funcEnd().
275 FuncRange
funcs() const;
276 MutableFuncRange
mutableFuncs() const;
277 MutableFuncRange
nonMainFuncs() const;
280 * Get a reference or pointer to the mergeable at index `idx'.
282 void*& mergeableObj(int idx
);
283 void** mergeableData(int idx
);
285 unsigned m_firstHoistableFunc
;
286 unsigned m_firstHoistablePreClass
;
287 unsigned m_firstMergeablePreClass
;
288 unsigned m_mergeablesSize
;
289 void* m_mergeables
[1];
293 * Type of a mergeable object.
295 * This is encoded in the lowest three bits of a pointer to the object.
297 enum class MergeKind
{
298 Class
= 0, // Class is required to be 0 for correctness.
299 UniqueDefinedClass
= 1,
300 Define
= 2, // Toplevel scalar define.
301 PersistentDefine
= 3, // Cross-request persistent toplevel defines.
302 Global
= 4, // Global variable declarations.
306 // We cannot add more kinds here; this has to fit in 3 bits.
312 using FuncRange
= MergeInfo::FuncRange
;
313 using MutableFuncRange
= MergeInfo::MutableFuncRange
;
316 * Cache for pseudomains for this unit, keyed by Class context.
318 using PseudoMainCacheMap
= hphp_hash_map
<
319 const Class
*, Func
*, pointer_hash
<Class
>
322 using PreClassPtrVec
= VMCompactVector
<PreClassPtr
>;
323 using TypeAliasVec
= VMFixedVector
<TypeAlias
>;
325 /////////////////////////////////////////////////////////////////////////////
326 // Construction and destruction.
332 * New and delete using low memory.
334 void* operator new(size_t sz
);
335 void operator delete(void* p
, size_t sz
);
337 /////////////////////////////////////////////////////////////////////////////
338 // Basic accessors. [const]
341 * Repo ID and serial number.
347 * SHA1 of the source code for Unit.
352 * SHA1 of the bytecode for Unit.
357 * File and directory paths.
359 const StringData
* filepath() const;
360 const StringData
* dirpath() const;
363 * Was this unit created in response to an internal compiler error?
367 /////////////////////////////////////////////////////////////////////////////
371 * Start and size of the bytecode for the Unit.
374 Offset
bclen() const;
377 * Convert between PC and Offset from entry().
379 PC
at(Offset off
) const;
380 Offset
offsetOf(PC pc
) const;
383 * Is `pc' in this Unit?
385 bool contains(PC pc
) const;
388 * Get the Op at `instrOffset'.
390 Op
getOp(Offset instrOffset
) const;
392 /////////////////////////////////////////////////////////////////////////////
393 // Code locations. [const]
396 * Get the line number corresponding to `pc'.
398 * Return -1 if not found.
400 int getLineNumber(Offset pc
) const;
403 * Get the SourceLoc corresponding to `pc'.
405 * Return false if not found, else true.
407 bool getSourceLoc(Offset pc
, SourceLoc
& sLoc
) const;
410 * Get the Offset range(s) corresponding to `pc' or `line'.
412 * Return false if not found, else true.
414 bool getOffsetRange(Offset pc
, OffsetRange
& range
) const;
415 bool getOffsetRanges(int line
, OffsetRangeVec
& offsets
) const;
418 * Get next line with executable code starting from input `line'.
420 * Return -1 if not found.
422 int getNearestLineWithCode(int line
) const;
425 * Return the Func* for the code at offset `pc'.
427 * Return nullptr if the offset is not in a Func body (but this should be
430 const Func
* getFunc(Offset pc
) const;
432 /////////////////////////////////////////////////////////////////////////////
433 // Litstrs and NamedEntitys. [const]
436 * Size of the Unit's litstr table.
438 * This excludes litstrs that are instead found in the global table---thus,
439 * it is not a source of truth for the number of litstrs a Unit needs, only
440 * those it happens to own.
442 size_t numLitstrs() const;
445 * Is `id' a valid litstr in LitstrTable or the Unit's local
446 * NamedEntityPairTable?
448 bool isLitstrId(Id id
) const;
451 * Dispatch to either the global LitstrTable or the Unit's local
452 * NamedEntityPairTable, depending on whether `id' is global.
454 * @see: NamedEntityPairTable
456 StringData
* lookupLitstrId(Id id
) const;
457 const NamedEntity
* lookupNamedEntityId(Id id
) const;
458 NamedEntityPair
lookupNamedEntityPairId(Id id
) const;
460 /////////////////////////////////////////////////////////////////////////////
464 * Size of the Unit's scalar array table.
466 size_t numArrays() const;
469 * Look up a scalar array by ID.
471 const ArrayData
* lookupArrayId(Id id
) const;
474 * Look up a RepoAuthType::Array by ID
476 const RepoAuthType::Array
* lookupArrayTypeId(Id id
) const;
478 /////////////////////////////////////////////////////////////////////////////
479 // Funcs, PreClasses, and RecordDescs. [const]
482 * Look up a Func or PreClass or PreRecordDesc by ID.
484 Func
* lookupFuncId(Id id
) const;
485 PreClass
* lookupPreClassId(Id id
) const;
486 PreRecordDesc
* lookupPreRecordId(Id id
) const;
489 * Range over all Funcs or PreClasses or RecordDescs in the Unit.
491 FuncRange
funcs() const;
492 folly::Range
<PreClassPtr
*> preclasses();
493 folly::Range
<const PreClassPtr
*> preclasses() const;
494 folly::Range
<PreRecordDescPtr
*> prerecords();
495 folly::Range
<const PreRecordDescPtr
*> prerecords() const;
498 * Get a pseudomain for the Unit with the context class `cls'.
500 * We clone the toplevel pseudomain for each context class and cache the
501 * results in m_pseudoMainCache.
503 Func
* getMain(Class
* cls
, bool hasThis
) const;
505 // Return the cached EntryPoint
506 Func
* getCachedEntryPoint() const;
509 * Visit all functions and methods in this unit.
511 template<class Fn
> void forEachFunc(Fn fn
) const;
513 /////////////////////////////////////////////////////////////////////////////
514 // Func lookup. [static]
517 * Define `func' for this request by initializing its RDS handle.
519 static void defFunc(Func
* func
, bool debugger
);
522 * Look up the defined Func in this request with name `name', or with the name
523 * mapped to the NamedEntity `ne'.
525 * Return nullptr if the function is not yet defined in this request.
527 static Func
* lookupFunc(const NamedEntity
* ne
);
528 static Func
* lookupFunc(const StringData
* name
);
531 * Look up, or autoload and define, the Func in this request with name `name',
532 * or with the name mapped to the NamedEntity `ne'.
534 * @requires: NamedEntity::get(name) == ne
536 static Func
* loadFunc(const NamedEntity
* ne
, const StringData
* name
);
537 static Func
* loadFunc(const StringData
* name
);
540 * bind (or rebind) a func to the NamedEntity corresponding to its
543 static void bindFunc(Func
* func
);
546 * Lookup the builtin in this request with name `name', or nullptr if none
547 * exists. This does not access RDS so it is safe to use from within the
548 * compiler. Note that does not mean imply that the name binding for the
549 * builtin is immutable. The builtin could be renamed or intercepted.
551 static Func
* lookupBuiltin(const StringData
* name
);
553 /////////////////////////////////////////////////////////////////////////////
554 // Class lookup. [static]
557 * Define a new Class from `preClass' for this request.
559 * Raises a fatal error in various conditions (e.g., Class already defined,
560 * parent Class not defined, etc.) if `failIsFatal' is set).
562 * Also always fatals if a type alias already exists in this request with the
563 * same name as that of `preClass', regardless of the value of `failIsFatal'.
565 static Class
* defClass(const PreClass
* preClass
, bool failIsFatal
= true);
568 * Define a closure from preClass. Closures have unique names, so unlike
569 * defClass, this is a one time operation.
571 static Class
* defClosure(const PreClass
* preClass
);
574 * Set the NamedEntity for `alias' to refer to the class `original' in this
577 * Raises a warning and returns false if `alias' already refers to a
578 * Class in this request, or if original is not loaded, and autoload
579 * is false, or it can't be autoloaded. Returns true otherwise.
581 static bool aliasClass(const StringData
* original
, const StringData
* alias
,
585 * Look up the Class in this request with name `name', or with the name
586 * mapped to the NamedEntity `ne'.
588 * Return nullptr if the class is not yet defined in this request.
590 static Class
* lookupClass(const NamedEntity
* ne
);
591 static Class
* lookupClass(const StringData
* name
);
594 * Finds a class which is guaranteed to be unique in the specified
595 * context. The class has not necessarily been loaded in the
598 * Return nullptr if there is no such class.
600 static const Class
* lookupUniqueClassInContext(const NamedEntity
* ne
,
602 static const Class
* lookupUniqueClassInContext(const StringData
* name
,
606 * Look up, or autoload and define, the Class in this request with name
607 * `name', or with the name mapped to the NamedEntity `ne'.
609 * @requires: NamedEntity::get(name) == ne
611 static Class
* loadClass(const NamedEntity
* ne
, const StringData
* name
);
612 static Class
* loadClass(const StringData
* name
);
615 * Autoload the Class with name `name' and bind it `ne' in this request.
617 * @requires: NamedEntity::get(name) == ne
619 static Class
* loadMissingClass(const NamedEntity
* ne
, const StringData
* name
);
622 * Same as lookupClass(), but if `tryAutoload' is set, call and return
623 * loadMissingClass().
625 static Class
* getClass(const NamedEntity
* ne
, const StringData
* name
,
627 static Class
* getClass(const StringData
* name
, bool tryAutoload
);
630 * Whether a Class with name `name' of type `kind' has been defined in this
631 * request, autoloading it if `autoload' is set.
633 static bool classExists(const StringData
* name
,
634 bool autoload
, ClassKind kind
);
636 /////////////////////////////////////////////////////////////////////////////
637 // RecordDesc lookup. [static]
640 * Define a new RecordDesc from `record' for this request.
642 * Raises a fatal error in various conditions (e.g., RecordDesc already
643 * defined, etc.) if `failIsFatal' is set).
645 * Also always fatals if a type alias already exists in this request with the
646 * same name as that of `record', regardless of the value of `failIsFatal'.
648 static RecordDesc
* defRecordDesc(PreRecordDesc
* record
,
649 bool failIsFatal
= true);
652 * Look up the RecordDesc in this request with name `name', or with the name
653 * mapped to the NamedEntity `ne'.
655 * Return nullptr if the record is not yet defined in this request.
657 static RecordDesc
* lookupRecordDesc(const NamedEntity
* ne
);
658 static RecordDesc
* lookupRecordDesc(const StringData
* name
);
661 * Finds a record which is guaranteed to be unique.
662 * The record has not necessarily been loaded in the current request.
664 * Return nullptr if there is no such record.
666 static const RecordDesc
* lookupUniqueRecDesc(const StringData
* name
);
669 * Autoload the RecordDesc with name `name' and bind it `ne' in this request.
671 * @requires: NamedEntity::get(name) == ne
673 static RecordDesc
* loadMissingRecordDesc(const NamedEntity
* ne
,
674 const StringData
* name
);
677 * Same as lookupRecordDesc(), but if `tryAutoload' is set, call and return
678 * loadMissingRecordDesc().
680 static RecordDesc
* getRecordDesc(const NamedEntity
* ne
,
681 const StringData
* name
,
683 static RecordDesc
* getRecordDesc(const StringData
* name
, bool tryAutoload
);
685 /////////////////////////////////////////////////////////////////////////////
686 // Constant lookup. [static]
689 * Look up the value of the defined constant in this request with name
692 * Return nullptr if no such constant is defined.
694 static tv_rval
lookupCns(const StringData
* cnsName
);
697 * Look up the value of the persistent constant with name `cnsName'.
699 * Return nullptr if no such constant exists, or the constant is not
702 static const Cell
* lookupPersistentCns(const StringData
* cnsName
);
705 * Look up, or autoload and define, the value of the constant with name
706 * `cnsName' for this request.
708 static tv_rval
loadCns(const StringData
* cnsName
);
711 * Define a constant (either request-local or persistent) with name `cnsName'
714 * May raise notices or warnings if a constant with the given name is already
715 * defined or if value is invalid.
717 static bool defCns(const StringData
* cnsName
, const TypedValue
* value
);
720 * Define a constant with name `cnsName' with a magic callback. The
721 * Cell should be KindOfUninit, with a Native::ConstantCallback in
724 * The canonical examples are STDIN, STDOUT, and STDERR.
726 static bool defNativeConstantCallback(const StringData
* cnsName
, Cell cell
);
728 /////////////////////////////////////////////////////////////////////////////
731 folly::Range
<TypeAlias
*> typeAliases();
732 folly::Range
<const TypeAlias
*> typeAliases() const;
735 * Look up without autoloading a type alias named `name'. Returns nullptr
736 * if one cannot be found.
738 * If the type alias is found and `persistent' is provided, it will be set to
739 * whether or not the TypeAliasReq's RDS handle is persistent.
741 static const TypeAliasReq
* lookupTypeAlias(const StringData
* name
,
742 bool* persistent
= nullptr);
745 * Look up or attempt to autoload a type alias named `name'. Returns nullptr
746 * if one cannot be found or autoloaded.
748 * If the type alias is found and `persistent' is provided, it will be set to
749 * whether or not the TypeAliasReq's RDS handle is persistent.
751 static const TypeAliasReq
* loadTypeAlias(const StringData
* name
,
752 bool* persistent
= nullptr);
755 * Define the type alias given by `id', binding it to the appropriate
756 * NamedEntity for this request.
758 * returns true iff the bound type alias is persistent.
760 bool defTypeAlias(Id id
);
762 /////////////////////////////////////////////////////////////////////////////
765 const UserAttributeMap
& fileAttributes() const;
767 /////////////////////////////////////////////////////////////////////////////
771 * Merge the Unit if it is not already merged.
776 * Is it sufficient to merge the Unit, and skip invoking its pseudomain?
778 bool isMergeOnly() const;
781 * Is this Unit empty---i.e., does it define nothing and have no
784 bool isEmpty() const;
787 * Get the return value of the pseudomain, or KindOfUninit if not
790 * @requires: isMergeOnly()
792 const TypedValue
* getMainReturn() const;
794 /////////////////////////////////////////////////////////////////////////////
795 // Info arrays. [static]
798 * Generate class info arrays.
800 static Array
getClassesInfo();
801 static Array
getInterfacesInfo();
802 static Array
getTraitsInfo();
805 * Generate function info arrays.
807 static Array
getUserFunctions();
808 static Array
getSystemFunctions();
810 /////////////////////////////////////////////////////////////////////////////
811 // Pretty printer. [const]
815 : startOffset(kInvalidOffset
)
816 , stopOffset(kInvalidOffset
)
822 PrintOpts
& range(Offset start
, Offset stop
) {
828 PrintOpts
& noLineNumbers() {
833 PrintOpts
& noFuncs() {
838 PrintOpts
& indent(int i
) {
850 void prettyPrint(std::ostream
&, PrintOpts
= PrintOpts()) const;
851 std::string
toString() const;
853 /////////////////////////////////////////////////////////////////////////////
857 * Is this Unit a compile-time fatal?
859 * A compile-time fatal is encoded as a pseudomain that contains precisely:
861 * String <id>; Fatal;
863 * Decode enough of pseudomain to determine whether it contains a
864 * compile-time fatal, and if so, extract the error message and line number.
866 * Parse-time fatals are a subset of compile-time fatals.
868 bool compileTimeFatal(const StringData
*& msg
, int& line
) const;
869 bool parseFatal(const StringData
*& msg
, int& line
) const;
872 * Get or set whether this Unit is interpret-only.
874 * This is used by the debugger to signal to the JIT that eval'd commands
875 * should not be jitted.
877 bool isInterpretOnly() const;
878 void setInterpretOnly();
883 void* replaceUnit() const;
886 * Does this unit correspond to a file with "<?hh" at the top?
888 bool isHHFile() const;
890 UserAttributeMap
metaData() const;
892 // Return true, and set the m_serialized flag, iff this Unit hasn't
893 // been serialized yet (see prof-data-serialize.cpp).
894 bool serialize() const {
895 if (m_serialized
) return false;
896 const_cast<Unit
*>(this)->m_serialized
= true;
900 /////////////////////////////////////////////////////////////////////////////
901 // Offset accessors. [static]
903 static constexpr ptrdiff_t bcOff() {
904 return offsetof(Unit
, m_bc
);
907 /////////////////////////////////////////////////////////////////////////////
912 template<bool debugger
>
913 void mergeImpl(MergeInfo
* mi
);
914 UnitExtended
* getExtended();
915 const UnitExtended
* getExtended() const;
916 MergeInfo
* mergeInfo() const {
917 return m_mergeInfo
.load(std::memory_order_acquire
);
920 /////////////////////////////////////////////////////////////////////////////
923 // These are organized in reverse order of frequency of use. Do not re-order
924 // without checking perf!
926 unsigned char const* m_bc
{nullptr};
928 LowStringPtr m_filepath
{nullptr};
929 std::atomic
<MergeInfo
*> m_mergeInfo
{nullptr};
933 * m_mergeState is read without a lock, but only written to under
934 * unitInitLock (see unit.cpp).
936 std::atomic
<uint8_t> m_mergeState
{MergeState::Unmerged
};
938 bool m_interpretOnly
: 1;
941 bool m_serialized
: 1;
942 bool m_ICE
: 1; // was this unit the result of an internal compiler error
943 LowStringPtr m_dirpath
{nullptr};
945 TypedValue m_mainReturn
;
946 PreClassPtrVec m_preClasses
;
947 TypeAliasVec m_typeAliases
;
948 CompactVector
<PreRecordDescPtr
> m_preRecords
;
950 * Cached the EntryPoint for an unit, since compactMergeInfo() inside of
951 * mergeImpl will drop the original EP.
953 Func
* m_cachedEntryPoint
{nullptr};
956 * The remaining fields are cold, and arbitrarily ordered.
959 int64_t m_sn
{-1}; // Note: could be 32-bit
962 VMFixedVector
<const ArrayData
*> m_arrays
;
963 mutable PseudoMainCacheMap
* m_pseudoMainCache
{nullptr};
964 mutable LockFreePtrWrapper
<VMCompactVector
<LineInfo
>> m_lineMap
;
965 UserAttributeMap m_metaData
;
966 UserAttributeMap m_fileAttributes
;
969 struct UnitExtended
: Unit
{
971 friend struct UnitEmitter
;
973 UnitExtended() { m_extended
= true; }
975 NamedEntityPairTable m_namedInfo
;
976 ArrayTypeTable m_arrayTypeTable
;
977 FuncTable m_funcTable
;
980 ///////////////////////////////////////////////////////////////////////////////
983 #define incl_HPHP_VM_UNIT_INL_H_
984 #include "hphp/runtime/vm/unit-inl.h"
985 #undef incl_HPHP_VM_UNIT_INL_H_
987 #endif // incl_HPHP_VM_UNIT_H_