Fix load-elim bug for branching instructions going to the same block
[hiphop-php.git] / hphp / doc / php.extension.compat.layer
blob9802e93c2e5fc73610831670e6e6d1531ea7286a
2 *************************************
3 * PHP Extension Compatibility Layer *
4 *************************************
7 Introduction
8 ------------
10 HHVM has experimental support for PHP extensions that were originally written
11 to target the PHP5 execution engine.
13 PHP extensions are written in C and make use of a number of APIs and macros
14 provided by the PHP5 engine to read and manipulate PHP program values.
15 Documentation for these APIs and macros can be found here:
17   http://php.net/manual/en/internals2.ze1.zendapi.php
19 The goal of HHVM's PHP Extension Compatibility Layer is to offer equivalent
20 implementations of these Zend engine APIs to allow PHP extensions to work
21 together with the HHVM runtime with minimal changes to the PHP extensions'
22 source code.
24 To make a PHP extension work with HHVM, the PHP extension needs to be compiled
25 from source code using HHVM's implementations of the APIs and macros instead of
26 the Zend engine's implementations. Also, the PHP extensions' source code must
27 be compiled as C++, and this may require making small changes to the source
28 code to make the code compile correctly with a C++ compiler (as C++ compilers
29 are stricter than C compilers with respect to implicit casts and a few other
30 issues).
32 Once the appropriate modifications have been made the PHP extensions' source,
33 HHVM must be updated with the appropriate definitions so that it knows about
34 the PHP extension (documentation for this coming soon), and then HHVM needs to
35 be compiled together with the PHP extension source code.
38 Representing zvals
39 ------------------
41 One of the core issues that the PHP Extension Compatibility Layer must address
42 is how to reconcile PHP's way and HHVM's way of representing program values and
43 operating on program values. This includes the shape of the object graph in the
44 heap (what points to what), how refcounting is done and how "reffiness" is
45 handled, how copy-on-write semantics are honored, how values are stored into
46 arrays, and so forth.
48 One of the core ideas here is that HHVM supports "boxing" a program value via
49 the RefData type and that there's a reasonably clean mapping between PHP's
50 object graph and HHVM's object graph (when HHVM program values are "boxed")
51 that maps PHP "zval" objects onto HHVM RefData objects. The diagram below
52 compares how values are represent in PHP vs. HHVM (where a "value slot" can be
53 a local variable slot, an array slot, an object property slot, etc):
55                  PHP            HHVM "unboxed"    HHVM "boxed"
56                  ---            --------------    ------------
58                 zval*             TypedValue       TypedValue
59                +-----+           +---+------+     +---+------+
60   Value slot:  |  *  |           | * | ARR  |     | * | REF  |
61                +--|--+           +-|-+------+     +-|-+------+
62                   |                |                |
63                   V                V                V
64                 zval              ArrayData        RefData
65                +---+-----+---+   +-----+---+      +---+-----+---+
66                | * | ARR | 1 |   | ... | 1 |      | * | ARR | 1 |
67                +-|-+-----+---+   +-----+---+      +-|-+-----+---+
68                  |                                  |
69                  V                                  V
70                 Hashtable                          ArrayData
71                +---------+                        +-----+---+
72                | ...     |                        | ... | 1 |
73                +---------+                        +-----+---+
75 For the purposes of the PHP extension compatibility layer, it makes sense to
76 use RefData to represent "zvals". PHP extensions assume that zvals are
77 heap-allocated and refcounted and that it is valid to hold a pointer to zval
78 for the duration of the request (as long as the zval's refcount has been
79 incremented), and RefData fits in with these assumptions nicely. Of course, it
80 is unreasonable to box every program variable in HHVM since this would hurt
81 performance. Luckily, it is unnecessary to box all program values; instead we
82 can just box program values lazily as needed in the right places to ensure that
83 PHP5 extensions only deal with boxed values.
86 Dealing with "reffiness"
87 ------------------------
89 In PHP, zvals can be shared amongst multiple value slots "by value" or "by
90 reference". zvals have an "is_ref" flag which is set to 1 if the zval is being
91 shared by reference, otherwise is_ref is set to 0. Note that the is_ref flag is
92 independent of the zval's refcount. We will refer to the state of the zval's
93 is_ref flag as the zval's "reffiness", and we will say a zval is "reffy" if its
94 is_ref flag is set to 1.
96 Under HHVM, historically RefData have been considered to be "reffy" when their
97 refcount was 2 or greater. This did not jive well with PHP extensions, which
98 can set multiple value slots to point to the same zval with is_ref=0 (i.e. a
99 zval can be shared "by value" between multiple value slots). To make this work,
100 we need to change RefData somehow so that we can keep track of when the RefData
101 is being shared "by value" by 2 or more value slots vs. when it is being shared
102 by reference. Adding an "is_ref" flag directly poses some challenges in terms
103 of performance, because it means that when a value's refcount decreases from 2
104 to 1 we'll need to make sure the "is_ref" flag gets cleared. Instead, we add two
105 new flags "m_cow" and "m_z", and we employ a scheme that maps the 3-tuple
106 (m_count, m_cow, m_z) to the 2-tuple (realRefcount, reffy). Under this scheme
107 decRef continues to work as it does today, decrementing the m_count field and
108 calling a helper when m_count reaches zero. Another neat thing about this scheme is
109 that m_cow is set to 1 iff the RefData is being shared by 2 or more value slots
110 "by value".
112 We should be able to avoid situations where a local variable slot points to a
113 RefData with m_cow=1, which will keep codegen for manipulating local variables
114 simple. We should be able to avoid such situations because (1) PHP extensions
115 do not have direct access to local variables; (2) because a RefData pointed to
116 by a local variable can only get passed into a PHP extension when a parameter
117 is passed by reference (in which case its refcount is 2 or greater and it is
118 being shared by reference); (3) a PHP extension is not allowed to toggle the
119 state of a zval's is_ref flag when the zval's refcount is 2 or greater; and (4)
120 if the PHP extension decrements the zval's refcount down to 1 it is no longer
121 allowed to operate on the zval since it has relinquished all of the references
122 it owned (since the last reference is owned by the local variable).
125 Copy-on-write
126 -------------
128 Another issue that must be addressed is how to reconcile PHP's way and HHVM's
129 way of honoring copy-on-write semantics. The diagram below compares how two
130 value slots share an array "by value" under PHP vs. HHVM:
132         PHP               HHVM "unboxed"                HHVM "boxed"
133         ---               --------------                ------------
135    zval*    zval*    TypedValue    TypedValue    TypedValue      TypedValue
136   +-----+  +-----+  +---+------+  +---+------+  +---+------+    +---+------+
137   |  *  |  |  *  |  | * | ARR  |  | * | ARR  |  | * | REF  |    | * | REF  |
138   +--|--+  +--|--+  +-|-+------+  +-|-+------+  +-|-+------+    +-|-+------+
139      |        |       |     +-------+             |               |
140      V        |       V     V                     V               V
141    zval       V      ArrayData                   RefData         RefData
142   +---+-----+----+  +-----+---+                 +---+-----+---+ +---+-----+---+
143   |   | ARR | 2  |  | ... | 2 |                 | * | ARR | 1 | | * | ARR | 1 |
144   | * +-----+----+  +-----+---+                 +-|-+-----+---+ +-|-+-----+---+
145   | | | is_ref=0 |                                |     +---------+
146   +-|-+----------+                                V     V
147     |                                            ArrayData
148     V                                           +-----+---+
149    Hashtable                                    | ... | 2 |
150   +---------+                                   +-----+---+
151   | ...     |
152   +---------+
154 Under PHP, a zval exclusively "owns" the array it points to, and the array
155 itself does not have a refcount field. Instead of having multiple things point
156 to the same array, sharing "by value" is achieved by having multiple things
157 point to the zval that exclusively owns the array (with the zval's is_ref flag
158 set to 0). When the PHP runtime or a PHP extension wants to mutate an array,
159 the SEPARATE_ZVAL_IF_NOT_REF() macro is used, which makes a copy of the zval
160 and the array it points to iff the zval's refcount is 2 or greater and the
161 zval's is_ref flag is set to 0. This is how PHP honors copy-on-write semantics.
163 HHVM, on the other hand, allows multiple things to point to the same array, and
164 the array has a refcount field to keep track of how many things are pointing at
165 it. When the HHVM runtime or an HHVM extension wants to mutate an array, it
166 checks the refcount of the array and makes a copy if the array's refcount is 2
167 or greater.
169 PHP extensions simply assume that a zval exclusively owns its array, so if the
170 zval's refcount is 1 or the zval's is_ref flag is 1 a PHP extension will assume
171 that the array does not need to be copied before mutating it. Thus, the PHP
172 extension compatibility layer must assure that all arrays that are exposed to
173 PHP extensions have a refcount of 1. This can be achieved by making copies of
174 arrays at the right places for arrays with a refcount of 2 or greater.
177 Objects and resources
178 ---------------------
180 Under PHP, a zval does not directly point to an object or a resource. Instead,
181 the zval contains either an object ID or a resource ID, which can be looked up
182 in a request-local tables to retrieve the object or resource. The digram below
183 shows an example:
185                 Table of                               Table of
186    zval         objects                   zval         resources
187   +---+-----+  +----+----+               +---+-----+  +----+----+
188   | 4 | OBJ |  | .. | .. |    Object     | 7 | RES |  | .. | .. |    Resource
189   +-|-+-----+  +----+----+   +------+    +-|-+-----+  +----+----+   +--------+
190     +--------->| 4  | *--+-->| ...  |      +--------->| 7  | *--+-->| ...    |
191                +----+----+   +------+                 +----+----+   +--------+
192                | .. | .. |                            | .. | .. |
193                +----+----+                            +----+----+
195 It is quite common for PHP extensions to use an ID to refer to an object or
196 resource instead of using a pointer to the object/resource. Also, there are a
197 number of APIs and macros provided by the Zend engine which are able to
198 retrieve an object or resource using an ID alone.
200 HHVM, on the other hand, does not have request-local tables for looking up
201 objects and resources by ID. The problem that must be solved here is how HHVM's
202 PHP Extension Compatibility Layer will represent object IDs and resource IDs,
203 and how it will convert an object or resource ID to a pointer as needed. One of
204 two approaches may prevail:
206  (1) It may be possible for HHVM to simply use the object/resource pointer as
207      the ID, casting the pointer to a sufficiently large integer type. The Zend
208      engine uses C's "long" type for resource IDs and the "zend_object_handle"
209      type (a typedef for C's "unsigned int" type) to represent object IDs. The
210      "long" type is pointer-sized on most platforms, and HHVM may be able to
211      define "zend_object_handle" as typedef for "uintptr_t", so this scheme
212      could possibly work out.
214  (2) A more conservative solution would be for HHVM to maintain request-local
215      tables of objects and resources and to populate these tables lazily and
216      individual objects and resources are exposed to PHP extensions via the
217      relevant APIs.
220 Other challenges
221 ----------------
223 There are some other challenges that need to be solved if we want HHVM's PHP
224 Extension Compatibility Layer to support most PHP extensions:
226  (1) Some PHP extensions create strings by assigning to Z_STRVAL_P(zval),
227      Z_STRLEN_P(zval), and Z_TYPE_P(zval) in no particular order, sometimes
228      performing other operations in between.
230  (2) PHP extensions assume that Z_STRVAL_P(zval) returns a req::malloc'd
231      buffer of characters that can be passed to efree() or detached from the
232      zval (provided the PHP extension owns the only reference to the zval),
233      and it assumes that req::malloc'd buffers of characters can be assigned
234      to Z_STRVAL_P(zval).
236  (3) PHP extensions assign to Z_LVAL_P(zval) and Z_TYPE_P(zval) in no
237      particular order for both integers values and resource IDs.