xref: /PHP-8.2/Zend/zend_vm_gen.php (revision 3f7ec69b)
1#!/usr/bin/env php
2<?php
3/*
4   +----------------------------------------------------------------------+
5   | Zend Engine                                                          |
6   +----------------------------------------------------------------------+
7   | Copyright (c) Zend Technologies Ltd. (http://www.zend.com)           |
8   +----------------------------------------------------------------------+
9   | This source file is subject to version 2.00 of the Zend license,     |
10   | that is bundled with this package in the file LICENSE, and is        |
11   | available through the world-wide-web at the following url:           |
12   | http://www.zend.com/license/2_00.txt.                                |
13   | If you did not receive a copy of the Zend license and are unable to  |
14   | obtain it through the world-wide-web, please send a note to          |
15   | license@zend.com so we can mail you a copy immediately.              |
16   +----------------------------------------------------------------------+
17   | Authors: Dmitry Stogov <dmitry@php.net>                              |
18   +----------------------------------------------------------------------+
19*/
20
21const HEADER_TEXT = <<< DATA
22/*
23   +----------------------------------------------------------------------+
24   | Zend Engine                                                          |
25   +----------------------------------------------------------------------+
26   | Copyright (c) Zend Technologies Ltd. (http://www.zend.com)           |
27   +----------------------------------------------------------------------+
28   | This source file is subject to version 2.00 of the Zend license,     |
29   | that is bundled with this package in the file LICENSE, and is        |
30   | available through the world-wide-web at the following url:           |
31   | http://www.zend.com/license/2_00.txt.                                |
32   | If you did not receive a copy of the Zend license and are unable to  |
33   | obtain it through the world-wide-web, please send a note to          |
34   | license@zend.com so we can mail you a copy immediately.              |
35   +----------------------------------------------------------------------+
36   | Authors: Andi Gutmans <andi@php.net>                                 |
37   |          Zeev Suraski <zeev@php.net>                                 |
38   |          Dmitry Stogov <dmitry@php.net>                              |
39   +----------------------------------------------------------------------+
40*/
41
42
43DATA;
44
45/*
46    This script creates zend_vm_execute.h and zend_vm_opcodes.{h,c}
47    from existing zend_vm_def.h and zend_vm_execute.skl
48*/
49
50error_reporting(E_ALL);
51
52const ZEND_VM_KIND_CALL   = 1;
53const ZEND_VM_KIND_SWITCH = 2;
54const ZEND_VM_KIND_GOTO   = 3;
55const ZEND_VM_KIND_HYBRID = 4;
56
57$vm_op_flags = array(
58    "ZEND_VM_OP_SPEC"         => 1<<0,
59    "ZEND_VM_OP_CONST"        => 1<<1,
60    "ZEND_VM_OP_TMPVAR"       => 1<<2,
61    "ZEND_VM_OP_TMPVARCV"     => 1<<3,
62    "ZEND_VM_OP_MASK"         => 0xf0,
63    "ZEND_VM_OP_NUM"          => 0x10,
64    "ZEND_VM_OP_JMP_ADDR"     => 0x20,
65    "ZEND_VM_OP_TRY_CATCH"    => 0x30,
66    // unused 0x40
67    "ZEND_VM_OP_THIS"         => 0x50,
68    "ZEND_VM_OP_NEXT"         => 0x60,
69    "ZEND_VM_OP_CLASS_FETCH"  => 0x70,
70    "ZEND_VM_OP_CONSTRUCTOR"  => 0x80,
71    "ZEND_VM_OP_CONST_FETCH"  => 0x90,
72    "ZEND_VM_OP_CACHE_SLOT"   => 0xa0,
73
74    "ZEND_VM_EXT_VAR_FETCH"   => 1<<16,
75    "ZEND_VM_EXT_ISSET"       => 1<<17,
76    "ZEND_VM_EXT_CACHE_SLOT"  => 1<<18,
77    "ZEND_VM_EXT_ARRAY_INIT"  => 1<<19,
78    "ZEND_VM_EXT_REF"         => 1<<20,
79    "ZEND_VM_EXT_FETCH_REF"   => 1<<21,
80    "ZEND_VM_EXT_DIM_WRITE"    => 1<<22,
81    "ZEND_VM_EXT_MASK"        => 0x0f000000,
82    "ZEND_VM_EXT_NUM"         => 0x01000000,
83    "ZEND_VM_EXT_LAST_CATCH"  => 0x02000000,
84    "ZEND_VM_EXT_JMP_ADDR"    => 0x03000000,
85    "ZEND_VM_EXT_OP"          => 0x04000000,
86    // unused 0x5000000
87    // unused 0x6000000
88    "ZEND_VM_EXT_TYPE"        => 0x07000000,
89    "ZEND_VM_EXT_EVAL"        => 0x08000000,
90    "ZEND_VM_EXT_TYPE_MASK"   => 0x09000000,
91    // unused 0x0a000000,
92    "ZEND_VM_EXT_SRC"         => 0x0b000000,
93    // unused 0x0c000000,
94    "ZEND_VM_NO_CONST_CONST"  => 0x40000000,
95    "ZEND_VM_COMMUTATIVE"     => 0x80000000,
96);
97
98foreach ($vm_op_flags as $name => $val) {
99    define($name, $val);
100}
101
102$vm_op_decode = array(
103    "ANY"                  => 0,
104    "CONST"                => ZEND_VM_OP_SPEC | ZEND_VM_OP_CONST,
105    "TMP"                  => ZEND_VM_OP_SPEC,
106    "VAR"                  => ZEND_VM_OP_SPEC,
107    "UNUSED"               => ZEND_VM_OP_SPEC,
108    "CV"                   => ZEND_VM_OP_SPEC,
109    "TMPVAR"               => ZEND_VM_OP_SPEC | ZEND_VM_OP_TMPVAR,
110    "TMPVARCV"             => ZEND_VM_OP_SPEC | ZEND_VM_OP_TMPVARCV,
111    "NUM"                  => ZEND_VM_OP_NUM,
112    "JMP_ADDR"             => ZEND_VM_OP_JMP_ADDR,
113    "TRY_CATCH"            => ZEND_VM_OP_TRY_CATCH,
114    "THIS"                 => ZEND_VM_OP_THIS,
115    "NEXT"                 => ZEND_VM_OP_NEXT,
116    "CLASS_FETCH"          => ZEND_VM_OP_CLASS_FETCH,
117    "CONSTRUCTOR"          => ZEND_VM_OP_CONSTRUCTOR,
118    "CONST_FETCH"          => ZEND_VM_OP_CONST_FETCH,
119    "CACHE_SLOT"           => ZEND_VM_OP_CACHE_SLOT,
120);
121
122$vm_ext_decode = array(
123    "NUM"                  => ZEND_VM_EXT_NUM,
124    "LAST_CATCH"           => ZEND_VM_EXT_LAST_CATCH,
125    "JMP_ADDR"             => ZEND_VM_EXT_JMP_ADDR,
126    "OP"                   => ZEND_VM_EXT_OP,
127    "VAR_FETCH"            => ZEND_VM_EXT_VAR_FETCH,
128    "ARRAY_INIT"           => ZEND_VM_EXT_ARRAY_INIT,
129    "TYPE"                 => ZEND_VM_EXT_TYPE,
130    "EVAL"                 => ZEND_VM_EXT_EVAL,
131    "TYPE_MASK"            => ZEND_VM_EXT_TYPE_MASK,
132    "ISSET"                => ZEND_VM_EXT_ISSET,
133    "REF"                  => ZEND_VM_EXT_REF,
134    "FETCH_REF"            => ZEND_VM_EXT_FETCH_REF,
135    "SRC"                  => ZEND_VM_EXT_SRC,
136    "CACHE_SLOT"           => ZEND_VM_EXT_CACHE_SLOT,
137    "DIM_WRITE"            => ZEND_VM_EXT_DIM_WRITE,
138);
139
140$vm_kind_name = array(
141    ZEND_VM_KIND_CALL      => "ZEND_VM_KIND_CALL",
142    ZEND_VM_KIND_SWITCH    => "ZEND_VM_KIND_SWITCH",
143    ZEND_VM_KIND_GOTO      => "ZEND_VM_KIND_GOTO",
144    ZEND_VM_KIND_HYBRID    => "ZEND_VM_KIND_HYBRID",
145);
146
147$op_types = array(
148    "ANY",
149    "CONST",
150    "TMP",
151    "VAR",
152    "UNUSED",
153    "CV",
154);
155
156$op_types_ex = array(
157    "ANY",
158    "CONST",
159    "TMPVARCV",
160    "TMPVAR",
161    "TMP",
162    "VAR",
163    "UNUSED",
164    "CV",
165);
166
167$prefix = array(
168    "ANY"      => "",
169    "TMP"      => "_TMP",
170    "VAR"      => "_VAR",
171    "CONST"    => "_CONST",
172    "UNUSED"   => "_UNUSED",
173    "CV"       => "_CV",
174    "TMPVAR"   => "_TMPVAR",
175    "TMPVARCV" => "_TMPVARCV",
176);
177
178$commutative_order = array(
179    "ANY"      => 0,
180    "TMP"      => 1,
181    "VAR"      => 2,
182    "CONST"    => 0,
183    "UNUSED"   => 0,
184    "CV"       => 4,
185    "TMPVAR"   => 2,
186    "TMPVARCV" => 4,
187);
188
189$op1_type = array(
190    "ANY"      => "opline->op1_type",
191    "TMP"      => "IS_TMP_VAR",
192    "VAR"      => "IS_VAR",
193    "CONST"    => "IS_CONST",
194    "UNUSED"   => "IS_UNUSED",
195    "CV"       => "IS_CV",
196    "TMPVAR"   => "(IS_TMP_VAR|IS_VAR)",
197    "TMPVARCV" => "(IS_TMP_VAR|IS_VAR|IS_CV)",
198);
199
200$op2_type = array(
201    "ANY"      => "opline->op2_type",
202    "TMP"      => "IS_TMP_VAR",
203    "VAR"      => "IS_VAR",
204    "CONST"    => "IS_CONST",
205    "UNUSED"   => "IS_UNUSED",
206    "CV"       => "IS_CV",
207    "TMPVAR"   => "(IS_TMP_VAR|IS_VAR)",
208    "TMPVARCV" => "(IS_TMP_VAR|IS_VAR|IS_CV)",
209);
210
211$op1_get_zval_ptr = array(
212    "ANY"      => "get_zval_ptr(opline->op1_type, opline->op1, \\1)",
213    "TMP"      => "_get_zval_ptr_tmp(opline->op1.var EXECUTE_DATA_CC)",
214    "VAR"      => "_get_zval_ptr_var(opline->op1.var EXECUTE_DATA_CC)",
215    "CONST"    => "RT_CONSTANT(opline, opline->op1)",
216    "UNUSED"   => "NULL",
217    "CV"       => "_get_zval_ptr_cv_\\1(opline->op1.var EXECUTE_DATA_CC)",
218    "TMPVAR"   => "_get_zval_ptr_var(opline->op1.var EXECUTE_DATA_CC)",
219    "TMPVARCV" => "???",
220);
221
222$op2_get_zval_ptr = array(
223    "ANY"      => "get_zval_ptr(opline->op2_type, opline->op2, \\1)",
224    "TMP"      => "_get_zval_ptr_tmp(opline->op2.var EXECUTE_DATA_CC)",
225    "VAR"      => "_get_zval_ptr_var(opline->op2.var EXECUTE_DATA_CC)",
226    "CONST"    => "RT_CONSTANT(opline, opline->op2)",
227    "UNUSED"   => "NULL",
228    "CV"       => "_get_zval_ptr_cv_\\1(opline->op2.var EXECUTE_DATA_CC)",
229    "TMPVAR"   => "_get_zval_ptr_var(opline->op2.var EXECUTE_DATA_CC)",
230    "TMPVARCV" => "???",
231);
232
233$op1_get_zval_ptr_ptr = array(
234    "ANY"      => "get_zval_ptr_ptr(opline->op1_type, opline->op1, \\1)",
235    "TMP"      => "NULL",
236    "VAR"      => "_get_zval_ptr_ptr_var(opline->op1.var EXECUTE_DATA_CC)",
237    "CONST"    => "NULL",
238    "UNUSED"   => "NULL",
239    "CV"       => "_get_zval_ptr_cv_\\1(opline->op1.var EXECUTE_DATA_CC)",
240    "TMPVAR"   => "???",
241    "TMPVARCV" => "???",
242);
243
244$op2_get_zval_ptr_ptr = array(
245    "ANY"      => "get_zval_ptr_ptr(opline->op2_type, opline->op2, \\1)",
246    "TMP"      => "NULL",
247    "VAR"      => "_get_zval_ptr_ptr_var(opline->op2.var EXECUTE_DATA_CC)",
248    "CONST"    => "NULL",
249    "UNUSED"   => "NULL",
250    "CV"       => "_get_zval_ptr_cv_\\1(opline->op2.var EXECUTE_DATA_CC)",
251    "TMPVAR"   => "???",
252    "TMPVARCV" => "???",
253);
254
255$op1_get_zval_ptr_deref = array(
256    "ANY"      => "get_zval_ptr_deref(opline->op1_type, opline->op1, \\1)",
257    "TMP"      => "_get_zval_ptr_tmp(opline->op1.var EXECUTE_DATA_CC)",
258    "VAR"      => "_get_zval_ptr_var_deref(opline->op1.var EXECUTE_DATA_CC)",
259    "CONST"    => "RT_CONSTANT(opline, opline->op1)",
260    "UNUSED"   => "NULL",
261    "CV"       => "_get_zval_ptr_cv_deref_\\1(opline->op1.var EXECUTE_DATA_CC)",
262    "TMPVAR"   => "???",
263    "TMPVARCV" => "???",
264);
265
266$op2_get_zval_ptr_deref = array(
267    "ANY"      => "get_zval_ptr_deref(opline->op2_type, opline->op2, \\1)",
268    "TMP"      => "_get_zval_ptr_tmp(opline->op2.var EXECUTE_DATA_CC)",
269    "VAR"      => "_get_zval_ptr_var_deref(opline->op2.var EXECUTE_DATA_CC)",
270    "CONST"    => "RT_CONSTANT(opline, opline->op2)",
271    "UNUSED"   => "NULL",
272    "CV"       => "_get_zval_ptr_cv_deref_\\1(opline->op2.var EXECUTE_DATA_CC)",
273    "TMPVAR"   => "???",
274    "TMPVARCV" => "???",
275);
276
277$op1_get_zval_ptr_undef = array(
278    "ANY"      => "get_zval_ptr_undef(opline->op1_type, opline->op1, \\1)",
279    "TMP"      => "_get_zval_ptr_tmp(opline->op1.var EXECUTE_DATA_CC)",
280    "VAR"      => "_get_zval_ptr_var(opline->op1.var EXECUTE_DATA_CC)",
281    "CONST"    => "RT_CONSTANT(opline, opline->op1)",
282    "UNUSED"   => "NULL",
283    "CV"       => "EX_VAR(opline->op1.var)",
284    "TMPVAR"   => "_get_zval_ptr_var(opline->op1.var EXECUTE_DATA_CC)",
285    "TMPVARCV" => "EX_VAR(opline->op1.var)",
286);
287
288$op2_get_zval_ptr_undef = array(
289    "ANY"      => "get_zval_ptr_undef(opline->op2_type, opline->op2, \\1)",
290    "TMP"      => "_get_zval_ptr_tmp(opline->op2.var EXECUTE_DATA_CC)",
291    "VAR"      => "_get_zval_ptr_var(opline->op2.var EXECUTE_DATA_CC)",
292    "CONST"    => "RT_CONSTANT(opline, opline->op2)",
293    "UNUSED"   => "NULL",
294    "CV"       => "EX_VAR(opline->op2.var)",
295    "TMPVAR"   => "_get_zval_ptr_var(opline->op2.var EXECUTE_DATA_CC)",
296    "TMPVARCV" => "EX_VAR(opline->op2.var)",
297);
298
299$op1_get_zval_ptr_ptr_undef = array(
300    "ANY"      => "get_zval_ptr_ptr_undef(opline->op1_type, opline->op1, \\1)",
301    "TMP"      => "NULL",
302    "VAR"      => "_get_zval_ptr_ptr_var(opline->op1.var EXECUTE_DATA_CC)",
303    "CONST"    => "NULL",
304    "UNUSED"   => "NULL",
305    "CV"       => "EX_VAR(opline->op1.var)",
306    "TMPVAR"   => "???",
307    "TMPVARCV" => "???",
308);
309
310$op2_get_zval_ptr_ptr_undef = array(
311    "ANY"      => "get_zval_ptr_ptr_undef(opline->op2_type, opline->op2, \\1)",
312    "TMP"      => "NULL",
313    "VAR"      => "_get_zval_ptr_ptr_var(opline->op2.var EXECUTE_DATA_CC)",
314    "CONST"    => "NULL",
315    "UNUSED"   => "NULL",
316    "CV"       => "EX_VAR(opline->op2.var)",
317    "TMPVAR"   => "???",
318    "TMPVARCV" => "???",
319);
320
321$op1_get_obj_zval_ptr = array(
322    "ANY"      => "get_obj_zval_ptr(opline->op1_type, opline->op1, \\1)",
323    "TMP"      => "_get_zval_ptr_tmp(opline->op1.var EXECUTE_DATA_CC)",
324    "VAR"      => "_get_zval_ptr_var(opline->op1.var EXECUTE_DATA_CC)",
325    "CONST"    => "RT_CONSTANT(opline, opline->op1)",
326    "UNUSED"   => "&EX(This)",
327    "CV"       => "_get_zval_ptr_cv_\\1(opline->op1.var EXECUTE_DATA_CC)",
328    "TMPVAR"   => "_get_zval_ptr_var(opline->op1.var EXECUTE_DATA_CC)",
329    "TMPVARCV" => "???",
330);
331
332$op2_get_obj_zval_ptr = array(
333    "ANY"      => "get_obj_zval_ptr(opline->op2_type, opline->op2, \\1)",
334    "TMP"      => "_get_zval_ptr_tmp(opline->op2.var EXECUTE_DATA_CC)",
335    "VAR"      => "_get_zval_ptr_var(opline->op2.var EXECUTE_DATA_CC)",
336    "CONST"    => "RT_CONSTANT(opline, opline->op2)",
337    "UNUSED"   => "&EX(This)",
338    "CV"       => "_get_zval_ptr_cv_\\1(opline->op2.var EXECUTE_DATA_CC)",
339    "TMPVAR"   => "_get_zval_ptr_var(opline->op2.var EXECUTE_DATA_CC)",
340    "TMPVARCV" => "???",
341);
342
343$op1_get_obj_zval_ptr_undef = array(
344    "ANY"      => "get_obj_zval_ptr_undef(opline->op1_type, opline->op1, \\1)",
345    "TMP"      => "_get_zval_ptr_tmp(opline->op1.var EXECUTE_DATA_CC)",
346    "VAR"      => "_get_zval_ptr_var(opline->op1.var EXECUTE_DATA_CC)",
347    "CONST"    => "RT_CONSTANT(opline, opline->op1)",
348    "UNUSED"   => "&EX(This)",
349    "CV"       => "EX_VAR(opline->op1.var)",
350    "TMPVAR"   => "_get_zval_ptr_var(opline->op1.var EXECUTE_DATA_CC)",
351    "TMPVARCV" => "EX_VAR(opline->op1.var)",
352);
353
354$op2_get_obj_zval_ptr_undef = array(
355    "ANY"      => "get_obj_zval_ptr_undef(opline->op2_type, opline->op2, \\1)",
356    "TMP"      => "_get_zval_ptr_tmp(opline->op2.var EXECUTE_DATA_CC)",
357    "VAR"      => "_get_zval_ptr_var(opline->op2.var EXECUTE_DATA_CC)",
358    "CONST"    => "RT_CONSTANT(opline, opline->op2)",
359    "UNUSED"   => "&EX(This)",
360    "CV"       => "EX_VAR(opline->op2.var)",
361    "TMPVAR"   => "_get_zval_ptr_var(opline->op2.var EXECUTE_DATA_CC)",
362    "TMPVARCV" => "EX_VAR(opline->op2.var)",
363);
364
365$op1_get_obj_zval_ptr_deref = array(
366    "ANY"      => "get_obj_zval_ptr(opline->op1_type, opline->op1, \\1)",
367    "TMP"      => "_get_zval_ptr_tmp(opline->op1.var EXECUTE_DATA_CC)",
368    "VAR"      => "_get_zval_ptr_var_deref(opline->op1.var EXECUTE_DATA_CC)",
369    "CONST"    => "RT_CONSTANT(opline, opline->op1)",
370    "UNUSED"   => "&EX(This)",
371    "CV"       => "_get_zval_ptr_cv_deref_\\1(opline->op1.var EXECUTE_DATA_CC)",
372    "TMPVAR"   => "???",
373    "TMPVARCV" => "???",
374);
375
376$op2_get_obj_zval_ptr_deref = array(
377    "ANY"      => "get_obj_zval_ptr(opline->op2_type, opline->op2, \\1)",
378    "TMP"      => "_get_zval_ptr_tmp(opline->op2.var EXECUTE_DATA_CC)",
379    "VAR"      => "_get_zval_ptr_var_deref(opline->op2.var EXECUTE_DATA_CC)",
380    "CONST"    => "RT_CONSTANT(opline, opline->op2)",
381    "UNUSED"   => "&EX(This)",
382    "CV"       => "_get_zval_ptr_cv_deref_\\1(opline->op2.var EXECUTE_DATA_CC)",
383    "TMPVAR"   => "???",
384    "TMPVARCV" => "???",
385);
386
387$op1_get_obj_zval_ptr_ptr = array(
388    "ANY"      => "get_obj_zval_ptr_ptr(opline->op1_type, opline->op1, \\1)",
389    "TMP"      => "NULL",
390    "VAR"      => "_get_zval_ptr_ptr_var(opline->op1.var EXECUTE_DATA_CC)",
391    "CONST"    => "NULL",
392    "UNUSED"   => "&EX(This)",
393    "CV"       => "_get_zval_ptr_cv_\\1(opline->op1.var EXECUTE_DATA_CC)",
394    "TMPVAR"   => "???",
395    "TMPVARCV" => "???",
396);
397
398$op2_get_obj_zval_ptr_ptr = array(
399    "ANY"      => "get_obj_zval_ptr_ptr(opline->op2_type, opline->op2, \\1)",
400    "TMP"      => "NULL",
401    "VAR"      => "_get_zval_ptr_ptr_var(opline->op2.var EXECUTE_DATA_CC)",
402    "CONST"    => "NULL",
403    "UNUSED"   => "&EX(This)",
404    "CV"       => "_get_zval_ptr_cv_\\1(opline->op2.var EXECUTE_DATA_CC)",
405    "TMPVAR"   => "???",
406    "TMPVARCV" => "???",
407);
408
409$op1_get_obj_zval_ptr_ptr_undef = array(
410    "ANY"      => "get_obj_zval_ptr_ptr(opline->op1_type, opline->op1, \\1)",
411    "TMP"      => "NULL",
412    "VAR"      => "_get_zval_ptr_ptr_var(opline->op1.var EXECUTE_DATA_CC)",
413    "CONST"    => "NULL",
414    "UNUSED"   => "&EX(This)",
415    "CV"       => "EX_VAR(opline->op1.var)",
416    "TMPVAR"   => "???",
417    "TMPVARCV" => "???",
418);
419
420$op2_get_obj_zval_ptr_ptr_undef = array(
421    "ANY"      => "get_obj_zval_ptr_ptr(opline->op2_type, opline->op2, \\1)",
422    "TMP"      => "NULL",
423    "VAR"      => "_get_zval_ptr_ptr_var(opline->op2.var EXECUTE_DATA_CC)",
424    "CONST"    => "NULL",
425    "UNUSED"   => "&EX(This)",
426    "CV"       => "EX_VAR(opline->op2.var)",
427    "TMPVAR"   => "???",
428    "TMPVARCV" => "???",
429);
430
431$op1_free_op = array(
432    "ANY"      => "FREE_OP(opline->op1_type, opline->op1.var)",
433    "TMP"      => "zval_ptr_dtor_nogc(EX_VAR(opline->op1.var))",
434    "VAR"      => "zval_ptr_dtor_nogc(EX_VAR(opline->op1.var))",
435    "CONST"    => "",
436    "UNUSED"   => "",
437    "CV"       => "",
438    "TMPVAR"   => "zval_ptr_dtor_nogc(EX_VAR(opline->op1.var))",
439    "TMPVARCV" => "???",
440);
441
442$op2_free_op = array(
443    "ANY"      => "FREE_OP(opline->op2_type, opline->op2.var)",
444    "TMP"      => "zval_ptr_dtor_nogc(EX_VAR(opline->op2.var))",
445    "VAR"      => "zval_ptr_dtor_nogc(EX_VAR(opline->op2.var))",
446    "CONST"    => "",
447    "UNUSED"   => "",
448    "CV"       => "",
449    "TMPVAR"   => "zval_ptr_dtor_nogc(EX_VAR(opline->op2.var))",
450    "TMPVARCV" => "???",
451);
452
453$op1_free_op_if_var = array(
454    "ANY"      => "if (opline->op1_type == IS_VAR) {zval_ptr_dtor_nogc(EX_VAR(opline->op1.var));}",
455    "TMP"      => "",
456    "VAR"      => "zval_ptr_dtor_nogc(EX_VAR(opline->op1.var))",
457    "CONST"    => "",
458    "UNUSED"   => "",
459    "CV"       => "",
460    "TMPVAR"   => "???",
461    "TMPVARCV" => "???",
462);
463
464$op2_free_op_if_var = array(
465    "ANY"      => "if (opline->op2_type == IS_VAR) {zval_ptr_dtor_nogc(EX_VAR(opline->op2.var));}",
466    "TMP"      => "",
467    "VAR"      => "zval_ptr_dtor_nogc(EX_VAR(opline->op2.var))",
468    "CONST"    => "",
469    "UNUSED"   => "",
470    "CV"       => "",
471    "TMPVAR"   => "???",
472    "TMPVARCV" => "???",
473);
474
475$op_data_type = array(
476    "ANY"      => "(opline+1)->op1_type",
477    "TMP"      => "IS_TMP_VAR",
478    "VAR"      => "IS_VAR",
479    "CONST"    => "IS_CONST",
480    "UNUSED"   => "IS_UNUSED",
481    "CV"       => "IS_CV",
482    "TMPVAR"   => "(IS_TMP_VAR|IS_VAR)",
483    "TMPVARCV" => "(IS_TMP_VAR|IS_VAR|IS_CV)",
484);
485
486$op_data_get_zval_ptr = array(
487    "ANY"      => "get_op_data_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1)",
488    "TMP"      => "_get_zval_ptr_tmp((opline+1)->op1.var EXECUTE_DATA_CC)",
489    "VAR"      => "_get_zval_ptr_var((opline+1)->op1.var EXECUTE_DATA_CC)",
490    "CONST"    => "RT_CONSTANT((opline+1), (opline+1)->op1)",
491    "UNUSED"   => "NULL",
492    "CV"       => "_get_zval_ptr_cv_\\1((opline+1)->op1.var EXECUTE_DATA_CC)",
493    "TMPVAR"   => "_get_zval_ptr_var((opline+1)->op1.var EXECUTE_DATA_CC)",
494    "TMPVARCV" => "???",
495);
496
497$op_data_get_zval_ptr_undef = array(
498    "ANY"      => "get_op_data_zval_ptr_undef((opline+1)->op1_type, (opline+1)->op1)",
499    "TMP"      => "_get_zval_ptr_tmp((opline+1)->op1.var EXECUTE_DATA_CC)",
500    "VAR"      => "_get_zval_ptr_var((opline+1)->op1.var EXECUTE_DATA_CC)",
501    "CONST"    => "RT_CONSTANT((opline+1), (opline+1)->op1)",
502    "UNUSED"   => "NULL",
503    "CV"       => "EX_VAR((opline+1)->op1.var)",
504    "TMPVAR"   => "_get_zval_ptr_var((opline+1)->op1.var EXECUTE_DATA_CC)",
505    "TMPVARCV" => "EX_VAR((opline+1)->op1.var)",
506);
507
508$op_data_get_zval_ptr_deref = array(
509    "ANY"      => "get_op_data_zval_ptr_deref_r((opline+1)->op1_type, (opline+1)->op1)",
510    "TMP"      => "_get_zval_ptr_tmp((opline+1)->op1.var EXECUTE_DATA_CC)",
511    "VAR"      => "_get_zval_ptr_var_deref((opline+1)->op1.var EXECUTE_DATA_CC)",
512    "CONST"    => "RT_CONSTANT((opline+1), (opline+1)->op1)",
513    "UNUSED"   => "NULL",
514    "CV"       => "_get_zval_ptr_cv_deref_\\1((opline+1)->op1.var EXECUTE_DATA_CC)",
515    "TMPVAR"   => "???",
516    "TMPVARCV" => "???",
517);
518
519$op_data_get_zval_ptr_ptr = array(
520    "ANY"      => "get_zval_ptr_ptr((opline+1)->op1_type, (opline+1)->op1, \\1)",
521    "TMP"      => "NULL",
522    "VAR"      => "_get_zval_ptr_ptr_var((opline+1)->op1.var EXECUTE_DATA_CC)",
523    "CONST"    => "NULL",
524    "UNUSED"   => "NULL",
525    "CV"       => "_get_zval_ptr_cv_\\1((opline+1)->op1.var EXECUTE_DATA_CC)",
526    "TMPVAR"   => "???",
527    "TMPVARCV" => "???",
528);
529
530$op_data_free_op = array(
531    "ANY"      => "FREE_OP((opline+1)->op1_type, (opline+1)->op1.var)",
532    "TMP"      => "zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var))",
533    "VAR"      => "zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var))",
534    "CONST"    => "",
535    "UNUSED"   => "",
536    "CV"       => "",
537    "TMPVAR"   => "zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var))",
538    "TMPVARCV" => "???",
539);
540
541$list    = array(); // list of opcode handlers and helpers in original order
542$opcodes = array(); // opcode handlers by code
543$helpers = array(); // opcode helpers by name
544$params  = array(); // parameters of helpers
545$opnames = array(); // opcode name to code mapping
546$line_no = 1;
547
548$used_extra_spec = array();
549
550// Writes $s into resulting executor
551function out($f, $s) {
552    global $line_no;
553
554    fputs($f,$s);
555    $line_no += substr_count($s, "\n");
556}
557
558// Resets #line directives in resulting executor
559function out_line($f) {
560    global $line_no, $executor_file;
561
562    fputs($f,"#line ".($line_no+1)." \"".$executor_file."\"\n");
563    ++$line_no;
564}
565
566function is_hot_helper($name) {
567    global $helpers;
568
569    if (isset($helpers[$name]["hot"])) {
570        return $helpers[$name]["hot"];
571    }
572
573    return false;
574}
575
576// Returns name of specialized helper
577function helper_name($name, $spec, $op1, $op2, $extra_spec) {
578    global $prefix, $helpers;
579
580    $extra = "";
581
582    if (isset($helpers[$name])) {
583        // If we have no helper with specified specialized operands then
584        // using unspecialized helper
585        if (!isset($helpers[$name]["op1"][$op1])) {
586            if (($op1 == 'TMP' || $op1 == 'VAR') &&
587                isset($helpers[$name]["op1"]["TMPVAR"])) {
588                $op1 = "TMPVAR";
589            } else if (($op1 == 'TMP' || $op1 == 'VAR') &&
590                isset($helpers[$name]["op1"]["TMPVARCV"])) {
591                $op1 = "TMPVARCV";
592            } else if ($op1 == 'CV' &&
593                isset($helpers[$name]["op1"]["TMPVARCV"])) {
594                $op1 = "TMPVARCV";
595            } else if (isset($helpers[$name]["op1"]["ANY"])) {
596                $op1 = "ANY";
597            }
598        }
599        if (!isset($helpers[$name]["op2"][$op2])) {
600            if (($op2 == 'TMP' || $op2 == 'VAR') &&
601                isset($helpers[$name]["op2"]["TMPVAR"])) {
602                $op2 = "TMPVAR";
603            } else if (($op2 == 'TMP' || $op2 == 'VAR') &&
604                isset($helpers[$name]["op2"]["TMPVARCV"])) {
605                $op2 = "TMPVARCV";
606            } else if ($op2 == 'CV' &&
607                isset($helpers[$name]["op2"]["TMPVARCV"])) {
608                $op2 = "TMPVARCV";
609            } else if (isset($helpers[$name]["op2"]["ANY"])) {
610                $op2 = "ANY";
611            }
612        }
613        /* forward common specs (e.g. in ZEND_VM_DISPATCH_TO_HELPER) */
614        if (isset($extra_spec, $helpers[$name]["spec"])) {
615            $extra = extra_spec_name(array_intersect_key($extra_spec, $helpers[$name]["spec"]));
616        }
617    }
618
619    return $name . ($spec ? "_SPEC" : "") . $prefix[$op1] . $prefix[$op2] . $extra;
620}
621
622function opcode_name($name, $spec, $op1, $op2, $extra_spec) {
623    global $prefix, $opnames, $opcodes;
624
625    $extra = "";
626
627    if (isset($opnames[$name])) {
628        $opcode = $opcodes[$opnames[$name]];
629        // If we have no helper with specified specialized operands then
630        // using unspecialized helper
631        if (!isset($opcode["op1"][$op1])) {
632            if (($op1 == 'TMP' || $op1 == 'VAR') &&
633                isset($opcode["op1"]["TMPVAR"])) {
634                $op1 = "TMPVAR";
635            } else if (($op1 == 'TMP' || $op1 == 'VAR') &&
636                isset($opcode["op1"]["TMPVARCV"])) {
637                $op1 = "TMPVARCV";
638            } else if ($op1 == 'CV' &&
639                isset($opcode["op1"]["TMPVARCV"])) {
640                $op1 = "TMPVARCV";
641            } else if (isset($opcode["op1"]["ANY"])) {
642                $op1 = "ANY";
643            } else if ($spec) {
644                /* dispatch to invalid handler from unreachable code */
645                return "ZEND_NULL";
646            }
647        }
648        if (!isset($opcode["op2"][$op2])) {
649            if (($op2 == 'TMP' || $op2 == 'VAR') &&
650                isset($opcode["op2"]["TMPVAR"])) {
651                $op2 = "TMPVAR";
652            } else if (($op2 == 'TMP' || $op2 == 'VAR') &&
653                isset($opcode["op2"]["TMPVARCV"])) {
654                $op2 = "TMPVARCV";
655            } else if ($op2 == 'CV' &&
656                isset($opcode["op2"]["TMPVARCV"])) {
657                $op2 = "TMPVARCV";
658            } else if (isset($opcode["op2"]["ANY"])) {
659                $op2 = "ANY";
660            } else if ($spec) {
661                /* dispatch to unknown handler in unreachable code */
662                return "ZEND_NULL";
663            }
664        }
665        /* forward common specs (e.g. in ZEND_VM_DISPATCH_TO_HANDLER) */
666        if (isset($extra_spec, $opcode["spec"])) {
667            $extra = extra_spec_name(array_intersect_key($extra_spec, $opcode["spec"]));
668        }
669    }
670
671    return $name . ($spec ? "_SPEC" : "") . $prefix[$op1] . $prefix[$op2] . $extra;
672}
673
674// Formats condition, protecting it by parentheses when needed.
675function format_condition($condition) {
676    if ($condition === "") {
677        throw new InvalidArgumentException("A non empty string condition was expected.");
678    }
679
680    if ($condition[0] === "(" && substr($condition, -1) === ")") {
681        return $condition;
682    }
683
684    return "(" . $condition . ")";
685}
686
687// Generates code for opcode handler or helper
688function gen_code($f, $spec, $kind, $code, $op1, $op2, $name, $extra_spec=null) {
689    global $op1_type, $op2_type, $op1_get_zval_ptr, $op2_get_zval_ptr,
690        $op1_get_zval_ptr_deref, $op2_get_zval_ptr_deref,
691        $op1_get_zval_ptr_undef, $op2_get_zval_ptr_undef,
692        $op1_get_zval_ptr_ptr, $op2_get_zval_ptr_ptr,
693        $op1_get_zval_ptr_ptr_undef, $op2_get_zval_ptr_ptr_undef,
694        $op1_get_obj_zval_ptr, $op2_get_obj_zval_ptr,
695        $op1_get_obj_zval_ptr_undef, $op2_get_obj_zval_ptr_undef,
696        $op1_get_obj_zval_ptr_deref, $op2_get_obj_zval_ptr_deref,
697        $op1_get_obj_zval_ptr_ptr, $op2_get_obj_zval_ptr_ptr,
698        $op1_get_obj_zval_ptr_ptr_undef, $op2_get_obj_zval_ptr_ptr_undef,
699        $op1_free_op, $op2_free_op, $op1_free_op_if_var, $op2_free_op_if_var,
700        $prefix,
701        $op_data_type, $op_data_get_zval_ptr, $op_data_get_zval_ptr_undef,
702        $op_data_get_zval_ptr_deref, $op_data_get_zval_ptr_ptr,
703        $op_data_free_op;
704
705    // Specializing
706    $specialized_replacements = array(
707        "/OP1_TYPE/" => $op1_type[$op1],
708        "/OP2_TYPE/" => $op2_type[$op2],
709        "/GET_OP1_ZVAL_PTR\(([^)]*)\)/" => $op1_get_zval_ptr[$op1],
710        "/GET_OP2_ZVAL_PTR\(([^)]*)\)/" => $op2_get_zval_ptr[$op2],
711        "/GET_OP1_ZVAL_PTR_DEREF\(([^)]*)\)/" => $op1_get_zval_ptr_deref[$op1],
712        "/GET_OP2_ZVAL_PTR_DEREF\(([^)]*)\)/" => $op2_get_zval_ptr_deref[$op2],
713        "/GET_OP1_ZVAL_PTR_UNDEF\(([^)]*)\)/" => $op1_get_zval_ptr_undef[$op1],
714        "/GET_OP2_ZVAL_PTR_UNDEF\(([^)]*)\)/" => $op2_get_zval_ptr_undef[$op2],
715        "/GET_OP1_ZVAL_PTR_PTR\(([^)]*)\)/" => $op1_get_zval_ptr_ptr[$op1],
716        "/GET_OP2_ZVAL_PTR_PTR\(([^)]*)\)/" => $op2_get_zval_ptr_ptr[$op2],
717        "/GET_OP1_ZVAL_PTR_PTR_UNDEF\(([^)]*)\)/" => $op1_get_zval_ptr_ptr_undef[$op1],
718        "/GET_OP2_ZVAL_PTR_PTR_UNDEF\(([^)]*)\)/" => $op2_get_zval_ptr_ptr_undef[$op2],
719        "/GET_OP1_OBJ_ZVAL_PTR\(([^)]*)\)/" => $op1_get_obj_zval_ptr[$op1],
720        "/GET_OP2_OBJ_ZVAL_PTR\(([^)]*)\)/" => $op2_get_obj_zval_ptr[$op2],
721        "/GET_OP1_OBJ_ZVAL_PTR_UNDEF\(([^)]*)\)/" => $op1_get_obj_zval_ptr_undef[$op1],
722        "/GET_OP2_OBJ_ZVAL_PTR_UNDEF\(([^)]*)\)/" => $op2_get_obj_zval_ptr_undef[$op2],
723        "/GET_OP1_OBJ_ZVAL_PTR_DEREF\(([^)]*)\)/" => $op1_get_obj_zval_ptr_deref[$op1],
724        "/GET_OP2_OBJ_ZVAL_PTR_DEREF\(([^)]*)\)/" => $op2_get_obj_zval_ptr_deref[$op2],
725        "/GET_OP1_OBJ_ZVAL_PTR_PTR\(([^)]*)\)/" => $op1_get_obj_zval_ptr_ptr[$op1],
726        "/GET_OP2_OBJ_ZVAL_PTR_PTR\(([^)]*)\)/" => $op2_get_obj_zval_ptr_ptr[$op2],
727        "/GET_OP1_OBJ_ZVAL_PTR_PTR_UNDEF\(([^)]*)\)/" => $op1_get_obj_zval_ptr_ptr_undef[$op1],
728        "/GET_OP2_OBJ_ZVAL_PTR_PTR_UNDEF\(([^)]*)\)/" => $op2_get_obj_zval_ptr_ptr_undef[$op2],
729        "/FREE_OP1\(\)/" => $op1_free_op[$op1],
730        "/FREE_OP2\(\)/" => $op2_free_op[$op2],
731        "/FREE_OP1_IF_VAR\(\)/" => $op1_free_op_if_var[$op1],
732        "/FREE_OP2_IF_VAR\(\)/" => $op2_free_op_if_var[$op2],
733        "/\!ZEND_VM_SPEC/m" => ($op1!="ANY"||$op2!="ANY")?"0":"1",
734        "/ZEND_VM_SPEC/m" => ($op1!="ANY"||$op2!="ANY")?"1":"0",
735        "/ZEND_VM_C_LABEL\(\s*([A-Za-z_]*)\s*\)/m" => "\\1".(($spec && $kind != ZEND_VM_KIND_CALL)?("_SPEC".$prefix[$op1].$prefix[$op2].extra_spec_name($extra_spec)):""),
736        "/ZEND_VM_C_GOTO\(\s*([A-Za-z_]*)\s*\)/m" => "goto \\1".(($spec && $kind != ZEND_VM_KIND_CALL)?("_SPEC".$prefix[$op1].$prefix[$op2].extra_spec_name($extra_spec)):""),
737        "/^#(\s*)if\s+1\s*\\|\\|.*[^\\\\]$/m" => "#\\1if 1",
738        "/^#(\s*)if\s+0\s*&&.*[^\\\\]$/m" => "#\\1if 0",
739        "/^#(\s*)elif\s+1\s*\\|\\|.*[^\\\\]$/m" => "#\\1elif 1",
740        "/^#(\s*)elif\s+0\s*&&.*[^\\\\]$/m" => "#\\1elif 0",
741        "/OP_DATA_TYPE/" => $op_data_type[isset($extra_spec['OP_DATA']) ? $extra_spec['OP_DATA'] : "ANY"],
742        "/GET_OP_DATA_ZVAL_PTR\(([^)]*)\)/" => $op_data_get_zval_ptr[isset($extra_spec['OP_DATA']) ? $extra_spec['OP_DATA'] : "ANY"],
743        "/GET_OP_DATA_ZVAL_PTR_UNDEF\(([^)]*)\)/" => $op_data_get_zval_ptr_undef[isset($extra_spec['OP_DATA']) ? $extra_spec['OP_DATA'] : "ANY"],
744        "/GET_OP_DATA_ZVAL_PTR_DEREF\(([^)]*)\)/" => $op_data_get_zval_ptr_deref[isset($extra_spec['OP_DATA']) ? $extra_spec['OP_DATA'] : "ANY"],
745        "/GET_OP_DATA_ZVAL_PTR_PTR\(([^)]*)\)/" => $op_data_get_zval_ptr_ptr[isset($extra_spec['OP_DATA']) ? $extra_spec['OP_DATA'] : "ANY"],
746        "/FREE_OP_DATA\(\)/" => $op_data_free_op[isset($extra_spec['OP_DATA']) ? $extra_spec['OP_DATA'] : "ANY"],
747        "/RETURN_VALUE_USED\(opline\)/" => isset($extra_spec['RETVAL']) ? $extra_spec['RETVAL'] : "RETURN_VALUE_USED(opline)",
748        "/arg_num <= MAX_ARG_FLAG_NUM/" => isset($extra_spec['QUICK_ARG']) ? $extra_spec['QUICK_ARG'] : "arg_num <= MAX_ARG_FLAG_NUM",
749        "/ZEND_VM_SMART_BRANCH\(\s*([^,)]*)\s*,\s*([^)]*)\s*\)/" => isset($extra_spec['SMART_BRANCH']) ?
750            ($extra_spec['SMART_BRANCH'] == 1 ?
751                    "ZEND_VM_SMART_BRANCH_JMPZ(\\1, \\2)"
752                :	($extra_spec['SMART_BRANCH'] == 2 ?
753                        "ZEND_VM_SMART_BRANCH_JMPNZ(\\1, \\2)" : "ZEND_VM_SMART_BRANCH_NONE(\\1, \\2)"))
754            :	"ZEND_VM_SMART_BRANCH(\\1, \\2)",
755        "/ZEND_VM_SMART_BRANCH_TRUE\(\s*\)/" => isset($extra_spec['SMART_BRANCH']) ?
756            ($extra_spec['SMART_BRANCH'] == 1 ?
757                    "ZEND_VM_SMART_BRANCH_TRUE_JMPZ()"
758                :	($extra_spec['SMART_BRANCH'] == 2 ?
759                        "ZEND_VM_SMART_BRANCH_TRUE_JMPNZ()" : "ZEND_VM_SMART_BRANCH_TRUE_NONE()"))
760            :	"ZEND_VM_SMART_BRANCH_TRUE()",
761        "/ZEND_VM_SMART_BRANCH_FALSE\(\s*\)/" => isset($extra_spec['SMART_BRANCH']) ?
762            ($extra_spec['SMART_BRANCH'] == 1 ?
763                    "ZEND_VM_SMART_BRANCH_FALSE_JMPZ()"
764                :	($extra_spec['SMART_BRANCH'] == 2 ?
765                        "ZEND_VM_SMART_BRANCH_FALSE_JMPNZ()" : "ZEND_VM_SMART_BRANCH_FALSE_NONE()"))
766            :	"ZEND_VM_SMART_BRANCH_FALSE()",
767        "/opline->extended_value\s*&\s*ZEND_ISEMPTY/" => isset($extra_spec['ISSET']) ?
768            ($extra_spec['ISSET'] == 0 ? "0" : "1")
769            : "\\0",
770        "/opline->extended_value\s*&\s*~\s*ZEND_ISEMPTY/" => isset($extra_spec['ISSET']) ?
771            ($extra_spec['ISSET'] == 0 ? "\\0" : "opline->extended_value")
772            : "\\0",
773        "/ZEND_OBSERVER_ENABLED/" => isset($extra_spec['OBSERVER']) && $extra_spec['OBSERVER'] == 1 ? "1" : "0",
774        "/ZEND_OBSERVER_USE_RETVAL/" => isset($extra_spec['OBSERVER']) && $extra_spec['OBSERVER'] == 1 ? "zval observer_retval" : "",
775        "/ZEND_OBSERVER_SET_RETVAL\(\)/" => isset($extra_spec['OBSERVER']) && $extra_spec['OBSERVER'] == 1 ? "if (!return_value) { return_value = &observer_retval; }" : "",
776        "/ZEND_OBSERVER_FREE_RETVAL\(\)/" => isset($extra_spec['OBSERVER']) && $extra_spec['OBSERVER'] == 1 ? "if (return_value == &observer_retval) { zval_ptr_dtor_nogc(&observer_retval); }" : "",
777        "/ZEND_OBSERVER_SAVE_OPLINE\(\)/" => isset($extra_spec['OBSERVER']) && $extra_spec['OBSERVER'] == 1 ? "SAVE_OPLINE()" : "",
778        "/ZEND_OBSERVER_FCALL_BEGIN\(\s*(.*)\s*\)/" => isset($extra_spec['OBSERVER']) ?
779            ($extra_spec['OBSERVER'] == 0 ? "" : "zend_observer_fcall_begin(\\1)")
780            : "",
781        "/ZEND_OBSERVER_FCALL_END\(\s*([^,]*)\s*,\s*(.*)\s*\)/" => isset($extra_spec['OBSERVER']) ?
782            ($extra_spec['OBSERVER'] == 0 ? "" : "zend_observer_fcall_end(\\1, \\2)")
783            : "",
784    );
785    $code = preg_replace(array_keys($specialized_replacements), array_values($specialized_replacements), $code);
786
787    if (0 && strpos($code, '{') === 0) {
788        $code = "{\n\tfprintf(stderr, \"$name\\n\");\n" . substr($code, 1);
789    }
790    // Updating code according to selected threading model
791    switch ($kind) {
792        case ZEND_VM_KIND_HYBRID:
793            $code = preg_replace_callback(
794                array(
795                    "/EXECUTE_DATA(?=[^_])/m",
796                    "/ZEND_VM_DISPATCH_TO_HANDLER\(\s*([A-Z_]*)\s*\)/m",
797                    "/ZEND_VM_DISPATCH_TO_HELPER\(\s*([A-Za-z_]*)\s*(,[^)]*)?\)/m",
798                ),
799                function($matches) use ($spec, $prefix, $op1, $op2, $extra_spec) {
800                    if (strncasecmp($matches[0], "EXECUTE_DATA", strlen("EXECUTE_DATA")) == 0) {
801                        return "execute_data";
802                    } else if (strncasecmp($matches[0], "ZEND_VM_DISPATCH_TO_HANDLER", strlen("ZEND_VM_DISPATCH_TO_HANDLER")) == 0) {
803                        global $opcodes, $opnames;
804
805                        $name = $matches[1];
806                        $opcode = $opcodes[$opnames[$name]];
807                        return "goto " . opcode_name($name, $spec, $op1, $op2, $extra_spec) . "_LABEL";
808                    } else {
809                        // ZEND_VM_DISPATCH_TO_HELPER
810                        if (is_hot_helper($matches[1])) {
811                            if (isset($matches[2])) {
812                                // extra args
813                                $args = preg_replace("/,\s*([A-Za-z0-9_]*)\s*,\s*([^,)\s]*)\s*/", "$1 = $2; ", $matches[2]);
814                                return $args . "goto " . helper_name($matches[1], $spec, $op1, $op2, $extra_spec) . "_LABEL";
815                            }
816                            return "goto " . helper_name($matches[1], $spec, $op1, $op2, $extra_spec) . "_LABEL";
817                        }
818                        if (isset($matches[2])) {
819                            // extra args
820                            $args = substr(preg_replace("/,\s*[A-Za-z0-9_]*\s*,\s*([^,)\s]*)\s*/", ", $1", $matches[2]), 2);
821                            return "ZEND_VM_TAIL_CALL(" . helper_name($matches[1], $spec, $op1, $op2, $extra_spec) . "(" . $args. " ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC))";
822                        }
823                        return "ZEND_VM_TAIL_CALL(" . helper_name($matches[1], $spec, $op1, $op2, $extra_spec) . "(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU))";
824                    }
825                },
826                $code);
827            break;
828        case ZEND_VM_KIND_CALL:
829            $code = preg_replace_callback(
830                array(
831                    "/EXECUTE_DATA(?=[^_])/m",
832                    "/ZEND_VM_DISPATCH_TO_HANDLER\(\s*([A-Z_]*)\s*\)/m",
833                    "/ZEND_VM_DISPATCH_TO_HELPER\(\s*([A-Za-z_]*)\s*(,[^)]*)?\)/m",
834                ),
835                function($matches) use ($spec, $prefix, $op1, $op2, $extra_spec, $name) {
836                    if (strncasecmp($matches[0], "EXECUTE_DATA", strlen("EXECUTE_DATA")) == 0) {
837                        return "execute_data";
838                    } else if (strncasecmp($matches[0], "ZEND_VM_DISPATCH_TO_HANDLER", strlen("ZEND_VM_DISPATCH_TO_HANDLER")) == 0) {
839                        global $opcodes, $opnames;
840
841                        $handler = $matches[1];
842                        $opcode = $opcodes[$opnames[$handler]];
843                        $inline =
844                            ZEND_VM_KIND == ZEND_VM_KIND_HYBRID &&
845                            isset($opcode["use"]) &&
846                            is_hot_handler($opcode["hot"], $op1, $op2, $extra_spec) &&
847                            is_hot_handler($opcodes[$opnames[$name]]["hot"], $op1, $op2, $extra_spec) ?
848                            "_INLINE" : "";
849                        return "ZEND_VM_TAIL_CALL(" . opcode_name($handler, $spec, $op1, $op2, $extra_spec) . $inline . "_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU))";
850                    } else {
851                        // ZEND_VM_DISPATCH_TO_HELPER
852                        if (isset($matches[2])) {
853                            // extra args
854                            $args = substr(preg_replace("/,\s*[A-Za-z0-9_]*\s*,\s*([^,)\s]*)\s*/", ", $1", $matches[2]), 2);
855                            return "ZEND_VM_TAIL_CALL(" . helper_name($matches[1], $spec, $op1, $op2, $extra_spec) . "(" . $args. " ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC))";
856                        }
857                        return "ZEND_VM_TAIL_CALL(" . helper_name($matches[1], $spec, $op1, $op2, $extra_spec) . "(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU))";
858                    }
859                },
860                $code);
861            break;
862        case ZEND_VM_KIND_SWITCH:
863            $code = preg_replace_callback(
864                array(
865                    "/EXECUTE_DATA(?=[^_])/m",
866                    "/ZEND_VM_DISPATCH_TO_HANDLER\(\s*([A-Z_]*)\s*\)/m",
867                    "/ZEND_VM_DISPATCH_TO_HELPER\(\s*([A-Za-z_]*)\s*(,[^)]*)?\)/m",
868                ),
869                function($matches) use ($spec, $prefix, $op1, $op2, $extra_spec) {
870                    if (strncasecmp($matches[0], "EXECUTE_DATA", strlen("EXECUTE_DATA")) == 0) {
871                        return "execute_data";
872                    } else if (strncasecmp($matches[0], "ZEND_VM_DISPATCH_TO_HANDLER", strlen("ZEND_VM_DISPATCH_TO_HANDLER")) == 0) {
873                        return "goto " . opcode_name($matches[1], $spec, $op1, $op2, $extra_spec) . "_LABEL";
874                    } else {
875                        // ZEND_VM_DISPATCH_TO_HELPER
876                        if (isset($matches[2])) {
877                            // extra args
878                            $args = preg_replace("/,\s*([A-Za-z0-9_]*)\s*,\s*([^,)\s]*)\s*/", "$1 = $2; ", $matches[2]);
879                            return $args .  "goto " . helper_name($matches[1], $spec, $op1, $op2, $extra_spec);
880                        }
881                        return "goto " . helper_name($matches[1], $spec, $op1, $op2, $extra_spec);
882                    }
883                },
884                    $code);
885            break;
886        case ZEND_VM_KIND_GOTO:
887            $code = preg_replace_callback(
888                array(
889                    "/EXECUTE_DATA(?=[^_])/m",
890                    "/ZEND_VM_DISPATCH_TO_HANDLER\(\s*([A-Z_]*)\s*\)/m",
891                    "/ZEND_VM_DISPATCH_TO_HELPER\(\s*([A-Za-z_]*)\s*(,[^)]*)?\)/m",
892                ),
893                function($matches) use ($spec, $prefix, $op1, $op2, $extra_spec) {
894                    if (strncasecmp($matches[0], "EXECUTE_DATA", strlen("EXECUTE_DATA")) == 0) {
895                        return "execute_data";
896                    } else if (strncasecmp($matches[0], "ZEND_VM_DISPATCH_TO_HANDLER", strlen("ZEND_VM_DISPATCH_TO_HANDLER")) == 0) {
897                        return "goto " . opcode_name($matches[1], $spec, $op1, $op2, $extra_spec) . "_LABEL";
898                    } else {
899                        // ZEND_VM_DISPATCH_TO_HELPER
900                        if (isset($matches[2])) {
901                            // extra args
902                            $args = preg_replace("/,\s*([A-Za-z0-9_]*)\s*,\s*([^,)\s]*)\s*/", "$1 = $2; ", $matches[2]);
903                            return $args .  "goto " . helper_name($matches[1], $spec, $op1, $op2, $extra_spec);
904                        }
905                        return "goto " . helper_name($matches[1], $spec, $op1, $op2, $extra_spec);
906                    }
907                },
908                    $code);
909            break;
910    }
911
912    /* Remove unnecessary ';' */
913    $code = preg_replace('/^\s*;\s*$/m', '', $code);
914
915    /* Remove WS */
916    $code = preg_replace('/[ \t]+\n/m', "\n", $code);
917
918    out($f, $code);
919}
920
921function skip_extra_spec_function($op1, $op2, $extra_spec) {
922    global $commutative_order;
923
924    if (isset($extra_spec["NO_CONST_CONST"]) &&
925        $op1 == "CONST" && $op2 == "CONST") {
926        // Skip useless constant handlers
927        return true;
928    }
929
930    if (isset($extra_spec["COMMUTATIVE"]) &&
931        $commutative_order[$op1] < $commutative_order[$op2]) {
932        // Skip duplicate commutative handlers
933        return true;
934    }
935
936    return false;
937}
938
939function is_hot_handler($hot, $op1, $op2, $extra_spec) {
940    if (isset($extra_spec["SMART_BRANCH"]) && $extra_spec["SMART_BRANCH"] == 0) {
941        return false;
942    }
943    if (isset($extra_spec["OBSERVER"]) && $extra_spec["OBSERVER"] == 1) {
944        return false;
945    }
946    if ($hot === 'HOT_' || $hot === 'INLINE_') {
947        return true;
948    } else if ($hot === 'HOT_NOCONST_') {
949        return ($op1 !== 'CONST');
950    } else if ($hot === 'HOT_NOCONSTCONST_') {
951        return (($op1 !== 'CONST') || ($op2 !== 'CONST')) ;
952    } else if ($hot === 'HOT_OBJ_') {
953        return (($op1 === 'UNUSED') || ($op1 === 'CV')) && ($op2 === 'CONST');
954    } else if ($hot === 'HOT_SEND_') {
955        return !empty($extra_spec["QUICK_ARG"]);
956    } else {
957        return false;
958    }
959}
960
961function is_cold_handler($hot, $op1, $op2, $extra_spec) {
962    if ($hot === 'COLD_') {
963        return true;
964    } else if (isset($extra_spec["OBSERVER"]) && $extra_spec["OBSERVER"] == 1) {
965        return true;
966    } else if ($hot === 'COLD_CONST_') {
967        return ($op1 === 'CONST');
968    } else if ($hot === 'COLD_CONSTCONST_') {
969        return ($op1 === 'CONST' && $op2 === 'CONST');
970    } else if ($hot === 'HOT_OBJ_') {
971        return ($op1 === 'CONST');
972    } else if ($hot === 'HOT_NOCONST_') {
973        return ($op1 === 'CONST');
974    } else if ($hot === 'HOT_NOCONSTCONST_') {
975        return ($op1 === 'CONST' && $op2 === 'CONST');
976    } else {
977        return false;
978    }
979}
980
981function is_inline_hybrid_handler($name, $hot, $op1, $op2, $extra_spec) {
982    return ($hot === 'INLINE_');
983}
984
985// Generates opcode handler
986function gen_handler($f, $spec, $kind, $name, $op1, $op2, $use, $code, $lineno, $opcode, $extra_spec = null, &$switch_labels = array()) {
987    global $definition_file, $prefix, $opnames, $gen_order;
988
989    static $used_observer_handlers = array();
990
991    if (isset($opcode['alias']) && ($spec || $kind != ZEND_VM_KIND_SWITCH)) {
992        return;
993    }
994
995    if ($spec && skip_extra_spec_function($op1, $op2, $extra_spec)) {
996        return;
997    }
998
999    /* Skip SMART_BRANCH specialization for "cold" CONST_CONST instructions */
1000    if (isset($extra_spec["SMART_BRANCH"])) {
1001        if ($opcode["hot"] === 'HOT_NOCONSTCONST_'
1002         || $opcode["hot"] === 'COLD_CONSTCONST_') {
1003            if (($op1 === 'CONST') && ($op2 === 'CONST')) {
1004                if ($extra_spec["SMART_BRANCH"] == 0) {
1005                    unset($extra_spec["SMART_BRANCH"]);
1006                } else {
1007                    return;
1008                }
1009            }
1010        }
1011    }
1012
1013    /* Skip QUICK_ARG specialization for named parameters */
1014    if (isset($extra_spec["QUICK_ARG"])) {
1015        if ($op2 === "CONST") {
1016            if ($extra_spec["QUICK_ARG"] == 0) {
1017                unset($extra_spec["QUICK_ARG"]);
1018            } else {
1019                return;
1020            }
1021        }
1022    }
1023
1024    /* Skip all specialization for OBSERVER handlers */
1025    if (isset($extra_spec["OBSERVER"]) && $extra_spec["OBSERVER"] == 1) {
1026        if (isset($extra_spec["RETVAL"])) {
1027            if ($extra_spec["RETVAL"] == 0) {
1028                unset($extra_spec["RETVAL"]);
1029            } else {
1030                return;
1031            }
1032        }
1033        if ($op1 != "ANY" || $op2 != "ANY") {
1034            if (!isset($used_observer_handlers[$kind][$opcode["op"]])) {
1035                $used_observer_handlers[$kind][$opcode["op"]] = true;
1036                $op1 = "ANY";
1037                $op2 = "ANY";
1038            } else {
1039                return;
1040            }
1041        }
1042    }
1043
1044    if (ZEND_VM_LINES) {
1045        out($f, "#line $lineno \"$definition_file\"\n");
1046    }
1047
1048    // Generate opcode handler's entry point according to selected threading model
1049    $additional_func = false;
1050    $spec_name = $name.($spec?"_SPEC":"").$prefix[$op1].$prefix[$op2].($spec?extra_spec_name($extra_spec):"");
1051    switch ($kind) {
1052        case ZEND_VM_KIND_HYBRID:
1053            if (is_inline_hybrid_handler($name, $opcode["hot"], $op1, $op2, $extra_spec)) {
1054                $out = fopen('php://memory', 'w+');
1055                gen_code($out, $spec, $kind, $code, $op1, $op2, $name, $extra_spec);
1056                rewind($out);
1057                $code =
1058                      "\t\t\tHYBRID_CASE({$spec_name}):\n"
1059                    . "\t\t\t\tVM_TRACE($spec_name)\n"
1060                    . stream_get_contents($out);
1061                fclose($out);
1062            } else {
1063                $inline =
1064                    isset($opcode["use"]) &&
1065                    is_hot_handler($opcode["hot"], $op1, $op2, $extra_spec) ?
1066                    "_INLINE" : "";
1067                $code =
1068                      "\t\t\tHYBRID_CASE({$spec_name}):\n"
1069                    . "\t\t\t\tVM_TRACE($spec_name)\n"
1070                    . "\t\t\t\t{$spec_name}{$inline}_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);\n"
1071                    . "\t\t\t\tHYBRID_BREAK();\n";
1072            }
1073            if (is_array($gen_order)) {
1074                $gen_order[$spec_name] = $code;
1075            } else {
1076                out($f, $code);
1077            }
1078            return;
1079        case ZEND_VM_KIND_CALL:
1080            if ($opcode["hot"] && ZEND_VM_KIND == ZEND_VM_KIND_HYBRID && is_hot_handler($opcode["hot"], $op1, $op2, $extra_spec)) {
1081                if (isset($opcode["use"])) {
1082                    out($f,"static zend_always_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL {$spec_name}_INLINE_HANDLER(ZEND_OPCODE_HANDLER_ARGS)\n");
1083                    $additional_func = true;
1084                } else {
1085                    out($f,"static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL {$spec_name}_HANDLER(ZEND_OPCODE_HANDLER_ARGS)\n");
1086                }
1087            } else if ($opcode["hot"] && is_cold_handler($opcode["hot"], $op1, $op2, $extra_spec)) {
1088                out($f,"static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL {$spec_name}_HANDLER(ZEND_OPCODE_HANDLER_ARGS)\n");
1089            } else {
1090                out($f,"static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL {$spec_name}_HANDLER(ZEND_OPCODE_HANDLER_ARGS)\n");
1091            }
1092            break;
1093        case ZEND_VM_KIND_SWITCH:
1094            if ($spec) {
1095                $cur = $switch_labels ? end($switch_labels) + 1 : 0;
1096                out($f,"case $cur: /* $spec_name */");
1097                $switch_labels[$spec_name] = $cur;
1098            } else {
1099                out($f,"case ".$name.":");
1100            }
1101            if ($use) {
1102                // This handler is used by other handlers. We will add label to call it.
1103                out($f," {$spec_name}_LABEL: ZEND_ATTRIBUTE_UNUSED_LABEL\n");
1104            } else {
1105                out($f,"\n");
1106            }
1107            break;
1108        case ZEND_VM_KIND_GOTO:
1109            out($f,"{$spec_name}_LABEL: ZEND_VM_GUARD($spec_name);\n");
1110            break;
1111    }
1112
1113    // Generate opcode handler's code
1114    gen_code($f, $spec, $kind, $code, $op1, $op2, $name, $extra_spec);
1115
1116    if ($additional_func) {
1117        out($f,"static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL {$spec_name}_HANDLER(ZEND_OPCODE_HANDLER_ARGS)\n");
1118        out($f,"{\n");
1119        out($f,"\tZEND_VM_TAIL_CALL({$spec_name}_INLINE_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU));\n");
1120        out($f,"}\n");
1121        out($f,"\n");
1122    }
1123}
1124
1125// Generates helper
1126function gen_helper($f, $spec, $kind, $name, $op1, $op2, $param, $code, $lineno, $inline, $cold = false, $hot = false, $extra_spec = null) {
1127    global $definition_file, $prefix;
1128
1129    if ($kind == ZEND_VM_KIND_HYBRID && !$hot) {
1130        return;
1131    }
1132
1133    if ($spec && skip_extra_spec_function($op1, $op2, $extra_spec)) {
1134        return;
1135    }
1136
1137    if (ZEND_VM_LINES) {
1138        out($f, "#line $lineno \"$definition_file\"\n");
1139    }
1140
1141    $spec_name = $name.($spec?"_SPEC":"").$prefix[$op1].$prefix[$op2].($spec?extra_spec_name($extra_spec):"");
1142
1143    // Generate helper's entry point according to selected threading model
1144    switch ($kind) {
1145        case ZEND_VM_KIND_HYBRID:
1146            out($f, $spec_name . "_LABEL:\n");
1147            break;
1148        case ZEND_VM_KIND_CALL:
1149            if ($inline) {
1150                $zend_attributes = " zend_always_inline";
1151                $zend_fastcall = "";
1152            } else {
1153                if ($cold) {
1154                    $zend_attributes = " zend_never_inline ZEND_COLD";
1155                } else {
1156                    $zend_attributes = " zend_never_inline";
1157                }
1158                $zend_fastcall = " ZEND_FASTCALL";
1159            }
1160            if ($param == null) {
1161              // Helper without parameters
1162                out($f, "static$zend_attributes ZEND_OPCODE_HANDLER_RET$zend_fastcall $spec_name(ZEND_OPCODE_HANDLER_ARGS)\n");
1163            } else {
1164              // Helper with parameter
1165                out($f, "static$zend_attributes ZEND_OPCODE_HANDLER_RET$zend_fastcall $spec_name($param ZEND_OPCODE_HANDLER_ARGS_DC)\n");
1166            }
1167            break;
1168        case ZEND_VM_KIND_SWITCH:
1169            out($f, "$spec_name:\n");
1170            break;
1171        case ZEND_VM_KIND_GOTO:
1172            out($f, "$spec_name:\n");
1173            break;
1174    }
1175
1176    // Generate helper's code
1177    gen_code($f, $spec, $kind, $code, $op1, $op2, $name, $extra_spec);
1178}
1179
1180
1181function gen_null_label($f, $kind, $prolog) {
1182    switch ($kind) {
1183        case ZEND_VM_KIND_CALL:
1184            out($f,$prolog."ZEND_NULL_HANDLER,\n");
1185            break;
1186        case ZEND_VM_KIND_SWITCH:
1187            out($f,$prolog."(void*)(uintptr_t)-1,\n");
1188            break;
1189        case ZEND_VM_KIND_GOTO:
1190            out($f,$prolog."(void*)&&ZEND_NULL_LABEL,\n");
1191            break;
1192    }
1193}
1194
1195// Generates array of opcode handlers (specialized or unspecialized)
1196function gen_labels($f, $spec, $kind, $prolog, &$specs, $switch_labels = array()) {
1197    global $opcodes, $opnames, $op_types, $prefix, $op_types_ex;
1198
1199    $list = [];
1200    $next = 0;
1201    $label = 0;
1202    if ($spec) {
1203      // Emit labels for specialized executor
1204
1205      // For each opcode in opcode number order
1206        foreach ($opcodes as $num => $dsc) {
1207            if (isset($dsc['alias'])) {
1208                $specs[$num] = $specs[$opnames[$dsc['alias']]];
1209                continue;
1210            }
1211            $specs[$num] = "$label";
1212            $spec_op1 = $spec_op2 = $spec_extra = false;
1213            $def_op1_type = $def_op2_type = "ANY";
1214            $next = $num + 1;
1215            if (isset($dsc["op1"]) && !isset($dsc["op1"]["ANY"])) {
1216                $count = 0;
1217                foreach ($op_types_ex as $t) {
1218                    if (isset($dsc["op1"][$t])) {
1219                        $def_op1_type = $t;
1220                        $count++;
1221                    }
1222                }
1223                if ($count > 1) {
1224                    $spec_op1 = true;
1225                    $specs[$num] .= " | SPEC_RULE_OP1";
1226                    $def_op1_type = "ANY";
1227                }
1228            }
1229            if (isset($dsc["op2"]) && !isset($dsc["op2"]["ANY"])) {
1230                $count = 0;
1231                foreach ($op_types_ex as $t) {
1232                    if (isset($dsc["op2"][$t])) {
1233                        $def_op2_type = $t;
1234                        $count++;
1235                    }
1236                }
1237                if ($count > 1) {
1238                    $spec_op2 = true;
1239                    $specs[$num] .= " | SPEC_RULE_OP2";
1240                    $def_op2_type = "ANY";
1241                }
1242            }
1243            $spec_extra = call_user_func_array("array_merge", extra_spec_handler($dsc) ?: array(array()));
1244            $flags = extra_spec_flags($spec_extra);
1245            if ($flags) {
1246                $specs[$num] .= " | " . implode(" | ", $flags);
1247            }
1248            if ($num >= 256) {
1249                $opcodes[$num]['spec_code'] = $specs[$num];
1250                unset($specs[$num]);
1251            }
1252
1253            $foreach_op1 = function($do) use ($dsc, $op_types) {
1254                return function($_, $op2) use ($do, $dsc, $op_types) {
1255                    // For each op1.op_type except ANY
1256                    foreach ($op_types as $op1) {
1257                        if ($op1 != "ANY") {
1258                            if (!isset($dsc["op1"][$op1])) {
1259                                if ($op1 == "TMP" || $op1 == "VAR") {
1260                                    if (isset($dsc["op1"]["TMPVAR"])) {
1261                                        $op1 = "TMPVAR";
1262                                    } else if (isset($dsc["op1"]["TMPVARCV"])) {
1263                                        $op1 = "TMPVARCV";
1264                                    } else {
1265                                        $op1 = "ANY";
1266                                    }
1267                                } else if ($op1 == "CV" && isset($dsc["op1"]["TMPVARCV"])) {
1268                                    $op1 = "TMPVARCV";
1269                                } else {
1270                                    // Try to use unspecialized handler
1271                                    $op1 = "ANY";
1272                                }
1273                            }
1274                            $do($op1, $op2);
1275                        }
1276                    }
1277                };
1278            };
1279            $foreach_op2 = function($do) use ($dsc, $op_types) {
1280                return function($op1, $_) use ($do, $dsc, $op_types) {
1281                    // For each op2.op_type except ANY
1282                    foreach ($op_types as $op2) {
1283                        if ($op2 != "ANY") {
1284                            if (!isset($dsc["op2"][$op2])) {
1285                                if ($op2 == "TMP" || $op2 == "VAR") {
1286                                    if (isset($dsc["op2"]["TMPVAR"])) {
1287                                        $op2 = "TMPVAR";
1288                                    } else if (isset($dsc["op2"]["TMPVARCV"])) {
1289                                        $op2 = "TMPVARCV";
1290                                    } else {
1291                                        $op2 = "ANY";
1292                                    }
1293                                } else if ($op2 == "CV" && isset($dsc["op2"]["TMPVARCV"])) {
1294                                    $op2 = "TMPVARCV";
1295                                } else {
1296                                    // Try to use unspecialized handler
1297                                    $op2 = "ANY";
1298                                }
1299                            }
1300                            $do($op1, $op2);
1301                        }
1302                    }
1303                };
1304            };
1305            $foreach_op_data = function($do) use ($dsc, $op_types) {
1306                return function($op1, $op2, $extra_spec = array()) use ($do, $dsc, $op_types) {
1307                    // For each op_data.op_type except ANY
1308                    foreach ($op_types as $op_data) {
1309                        if ($op_data != "ANY") {
1310                            if (!isset($dsc["spec"]["OP_DATA"][$op_data])) {
1311                                if ($op_data == "TMP" || $op_data == "VAR") {
1312                                    if (isset($dsc["spec"]["OP_DATA"]["TMPVAR"])) {
1313                                        $op_data = "TMPVAR";
1314                                    } else if (isset($dsc["spec"]["OP_DATA"]["TMPVARCV"])) {
1315                                        $op_data = "TMPVARCV";
1316                                    } else {
1317                                        // Try to use unspecialized handler
1318                                        $op_data = "ANY";
1319                                    }
1320                                } else if ($op_data == "CV" && isset($dsc["OP_DATA"]["TMPVARCV"])) {
1321                                    $op_data = "TMPVARCV";
1322                                } else {
1323                                    // Try to use unspecialized handler
1324                                    $op_data = "ANY";
1325                                }
1326                            }
1327                            $do($op1, $op2, array("OP_DATA" => $op_data) + $extra_spec);
1328                        }
1329                    }
1330                };
1331            };
1332            $foreach_extra_spec = function($do, $spec) use ($dsc) {
1333                return function($op1, $op2, $extra_spec = array()) use ($do, $spec, $dsc) {
1334                    foreach ($dsc["spec"][$spec] as $val) {
1335                        $do($op1, $op2, array($spec => $val) + $extra_spec);
1336                    }
1337                };
1338            };
1339            $generate = function ($op1, $op2, $extra_spec = array()) use ($f, $kind, $dsc, $prefix, $prolog, $num, $switch_labels, &$label, &$list) {
1340                global $commutative_order;
1341
1342                // Check if specialized handler is defined
1343                /* TODO: figure out better way to signal "specialized and not defined" than an extra lookup */
1344                if (isset($dsc["op1"][$op1]) &&
1345                    isset($dsc["op2"][$op2]) &&
1346                    (!isset($extra_spec["OP_DATA"]) || isset($dsc["spec"]["OP_DATA"][$extra_spec["OP_DATA"]]))) {
1347                    if (skip_extra_spec_function($op1, $op2, $extra_spec)) {
1348                        gen_null_label($f, $kind, $prolog);
1349                        $list[$label] = null;
1350                        $label++;
1351                        return;
1352                    }
1353
1354                    /* Skip SMART_BRANCH specialization for "cold" CONST_CONST instructions */
1355                    if (isset($extra_spec["SMART_BRANCH"])) {
1356                        if ($dsc["hot"] === 'HOT_NOCONSTCONST_'
1357                         || $dsc["hot"] === 'COLD_CONSTCONST_') {
1358                            if (($op1 === 'CONST') && ($op2 === 'CONST')) {
1359                                unset($extra_spec["SMART_BRANCH"]);
1360                            }
1361                        }
1362                    }
1363
1364                    /* Skip QUICK_ARG specialization for named parameters */
1365                    if (isset($extra_spec["QUICK_ARG"])) {
1366                        if ($op2 === "CONST") {
1367                            unset($extra_spec["QUICK_ARG"]);
1368                        }
1369                    }
1370
1371                    /* Skip all specialization for OBSERVER handlers */
1372                    if (isset($extra_spec["OBSERVER"]) && $extra_spec["OBSERVER"] == 1) {
1373                        if (isset($extra_spec["RETVAL"])) {
1374                            unset($extra_spec["RETVAL"]);
1375                        }
1376                        if ($op1 != "ANY" || $op2 != "ANY") {
1377                            $op1 = "ANY";
1378                            $op2 = "ANY";
1379                        }
1380                    }
1381
1382                    // Emit pointer to specialized handler
1383                    $spec_name = $dsc["op"]."_SPEC".$prefix[$op1].$prefix[$op2].extra_spec_name($extra_spec);
1384                    switch ($kind) {
1385                        case ZEND_VM_KIND_CALL:
1386                            out($f,"$prolog{$spec_name}_HANDLER,\n");
1387                            break;
1388                        case ZEND_VM_KIND_SWITCH:
1389                            out($f,$prolog."(void*)(uintptr_t)$switch_labels[$spec_name],\n");
1390                            break;
1391                        case ZEND_VM_KIND_GOTO:
1392                            out($f,$prolog."(void*)&&{$spec_name}_LABEL,\n");
1393                            break;
1394                    }
1395                    $list[$label] = $spec_name;
1396                    $label++;
1397                } else {
1398                    // Emit pointer to handler of undefined opcode
1399                    gen_null_label($f, $kind, $prolog);
1400                    $list[$label] = null;
1401                    $label++;
1402                }
1403            };
1404
1405            $do = $generate;
1406            if ($spec_extra) {
1407                foreach ($spec_extra as $extra => $devnull) {
1408                    if ($extra == "OP_DATA") {
1409                        $do = $foreach_op_data($do);
1410                    } else {
1411                        $do = $foreach_extra_spec($do, $extra);
1412                    }
1413                }
1414            }
1415            if ($spec_op2) {
1416                $do = $foreach_op2($do);
1417            }
1418            if ($spec_op1) {
1419                $do = $foreach_op1($do);
1420            }
1421
1422            $do($def_op1_type, $def_op2_type);
1423        }
1424    } else {
1425      // Emit labels for unspecialized executor
1426
1427      // For each opcode in opcode number order
1428        foreach ($opcodes as $num => $dsc) {
1429            while ($next != $num) {
1430              // If some opcode numbers are not used then fill hole with pointers
1431              // to handler of undefined opcode
1432                switch ($kind) {
1433                    case ZEND_VM_KIND_CALL:
1434                        out($f,$prolog."ZEND_NULL_HANDLER,\n");
1435                        break;
1436                    case ZEND_VM_KIND_SWITCH:
1437                        out($f,$prolog."(void*)(uintptr_t)-1,\n");
1438                        break;
1439                    case ZEND_VM_KIND_GOTO:
1440                        out($f,$prolog."(void*)&&ZEND_NULL_LABEL,\n");
1441                        break;
1442                }
1443                $next++;
1444            }
1445            if ($num >= 256) {
1446                continue;
1447            }
1448            $next = $num+1;
1449
1450            if (isset($dsc['alias']) && $kind != ZEND_VM_KIND_SWITCH) {
1451                // Emit pointer to unspecialized handler
1452                switch ($kind) {
1453                case ZEND_VM_KIND_CALL:
1454                    out($f,$prolog.$dsc['alias']."_HANDLER,\n");
1455                    break;
1456                case ZEND_VM_KIND_GOTO:
1457                    out($f,$prolog."(void*)&&".$dsc['alias']."_LABEL,\n");
1458                    break;
1459                }
1460                $list[] = $dsc["op"];
1461            } else if ($dsc["code"]) { //ugly trick for ZEND_VM_DEFINE_OP
1462                // Emit pointer to unspecialized handler
1463                switch ($kind) {
1464                case ZEND_VM_KIND_CALL:
1465                    out($f,$prolog.$dsc["op"]."_HANDLER,\n");
1466                    break;
1467                case ZEND_VM_KIND_SWITCH:
1468                    out($f,$prolog."(void*)(uintptr_t)".((string)$num).",\n");
1469                    break;
1470                case ZEND_VM_KIND_GOTO:
1471                    out($f,$prolog."(void*)&&".$dsc["op"]."_LABEL,\n");
1472                    break;
1473                }
1474                $list[] = $dsc["op"];
1475            } else {
1476                switch ($kind) {
1477                    case ZEND_VM_KIND_CALL:
1478                        out($f,$prolog."ZEND_NULL_HANDLER,\n");
1479                        break;
1480                    case ZEND_VM_KIND_SWITCH:
1481                        out($f,$prolog."(void*)(uintptr_t)-1,\n");
1482                        break;
1483                    case ZEND_VM_KIND_GOTO:
1484                        out($f,$prolog."(void*)&&ZEND_NULL_LABEL,\n");
1485                        break;
1486                }
1487                $list[] = null;
1488            }
1489        }
1490    }
1491
1492    // Emit last handler's label (undefined opcode)
1493    switch ($kind) {
1494        case ZEND_VM_KIND_CALL:
1495            out($f,$prolog."ZEND_NULL_HANDLER\n");
1496            break;
1497        case ZEND_VM_KIND_SWITCH:
1498            out($f,$prolog."(void*)(uintptr_t)-1\n");
1499            break;
1500        case ZEND_VM_KIND_GOTO:
1501            out($f,$prolog."(void*)&&ZEND_NULL_LABEL\n");
1502            break;
1503    }
1504    $specs[$num + 1] = "$label";
1505
1506    $l = fopen(__DIR__ . "/zend_vm_handlers.h", "w+") or die("ERROR: Cannot create zend_vm_handlers.h\n");
1507    out($l, "#define VM_HANDLERS(_) \\\n");
1508    foreach ($list as $n => $name) {
1509        if (null !== $name) {
1510            out($l, "\t_($n, $name) \\\n");
1511        }
1512    }
1513    out($l, "\t_($n+1, ZEND_NULL)\n");
1514    fclose($l);
1515}
1516
1517// Generates specialized offsets
1518function gen_specs($f, $prolog, $specs) {
1519    $lastdef = array_pop($specs);
1520    $last = 0;
1521    foreach ($specs as $num => $def) {
1522        while (++$last < $num) {
1523            out($f, "$prolog$lastdef,\n");
1524        }
1525        $last = $num;
1526        out($f, "$prolog$def,\n");
1527    }
1528    while ($last++ < 255) {
1529        out($f, "$prolog$lastdef,\n");
1530    }
1531}
1532
1533// Generates handler for undefined opcodes (CALL threading model)
1534function gen_null_handler($f) {
1535    static $done = 0;
1536
1537    // New and all executors with CALL threading model can use the same handler
1538    // for undefined opcodes, do we emit code for it only once
1539    if (!$done) {
1540        $done = 1;
1541        out($f,"static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_NULL_HANDLER(ZEND_OPCODE_HANDLER_ARGS)\n");
1542        out($f,"{\n");
1543        out($f,"\tUSE_OPLINE\n");
1544        out($f,"\n");
1545        out($f,"\tSAVE_OPLINE();\n");
1546        out($f,"\tzend_error_noreturn(E_ERROR, \"Invalid opcode %d/%d/%d.\", OPLINE->opcode, OPLINE->op1_type, OPLINE->op2_type);\n");
1547        out($f,"\tZEND_VM_NEXT_OPCODE(); /* Never reached */\n");
1548        out($f,"}\n\n");
1549    }
1550}
1551
1552function extra_spec_name($extra_spec) {
1553    global $prefix;
1554
1555    $s = "";
1556    if (isset($extra_spec["OP_DATA"])) {
1557        $s .= "_OP_DATA" . $prefix[$extra_spec["OP_DATA"]];
1558    }
1559    if (isset($extra_spec["RETVAL"])) {
1560        $s .= "_RETVAL_".($extra_spec["RETVAL"] ? "USED" : "UNUSED");
1561    }
1562    if (isset($extra_spec["QUICK_ARG"])) {
1563        if ($extra_spec["QUICK_ARG"]) {
1564            $s .= "_QUICK";
1565        }
1566    }
1567    if (isset($extra_spec["SMART_BRANCH"])) {
1568        if ($extra_spec["SMART_BRANCH"] == 1) {
1569            $s .= "_JMPZ";
1570        } else if ($extra_spec["SMART_BRANCH"] == 2) {
1571            $s .= "_JMPNZ";
1572        }
1573    }
1574    if (isset($extra_spec["ISSET"])) {
1575        if ($extra_spec["ISSET"] == 0) {
1576            $s .= "_SET";
1577        } else {
1578            $s .= "_EMPTY";
1579        }
1580    }
1581    if (isset($extra_spec["OBSERVER"])) {
1582        if ($extra_spec["OBSERVER"]) {
1583            $s .= "_OBSERVER";
1584        }
1585    }
1586    return $s;
1587}
1588
1589function extra_spec_flags($extra_spec) {
1590    $s = array();
1591    if (isset($extra_spec["OP_DATA"])) {
1592        $s[] = "SPEC_RULE_OP_DATA";
1593    }
1594    if (isset($extra_spec["RETVAL"])) {
1595        $s[] = "SPEC_RULE_RETVAL";
1596    }
1597    if (isset($extra_spec["QUICK_ARG"])) {
1598        $s[] = "SPEC_RULE_QUICK_ARG";
1599    }
1600    if (isset($extra_spec["SMART_BRANCH"])) {
1601        $s[] = "SPEC_RULE_SMART_BRANCH";
1602    }
1603    if (isset($extra_spec["COMMUTATIVE"])) {
1604        $s[] = "SPEC_RULE_COMMUTATIVE";
1605    }
1606    if (isset($extra_spec["ISSET"])) {
1607        $s[] = "SPEC_RULE_ISSET";
1608    }
1609    if (isset($extra_spec["OBSERVER"])) {
1610        $s[] = "SPEC_RULE_OBSERVER";
1611    }
1612    return $s;
1613}
1614
1615function extra_spec_handler($dsc) {
1616    global $op_types_ex;
1617
1618    if (!isset($dsc["spec"])) {
1619        return array(array());
1620    }
1621    $specs = $dsc["spec"];
1622
1623    if (isset($specs["OP_DATA"])) {
1624        $op_data_specs = $specs["OP_DATA"];
1625        $specs["OP_DATA"] = array();
1626        foreach ($op_types_ex as $op_data) {
1627            if (isset($dsc["spec"]["OP_DATA"][$op_data])) {
1628                $specs["OP_DATA"][] = $op_data;
1629            }
1630        }
1631    }
1632
1633    $f = function($specs) use (&$f) {
1634        $spec = key($specs);
1635        $top = array_shift($specs);
1636        if ($specs) {
1637            $next = $f($specs);
1638        } else {
1639            $next = array(array());
1640        }
1641        $ret = array();
1642        foreach ($next as $existing) {
1643            foreach ($top as $mode) {
1644                $ret[] = array($spec => $mode) + $existing;
1645            }
1646        }
1647        return $ret;
1648    };
1649    return $f($specs);
1650}
1651
1652function read_order_file($fn) {
1653    $f = fopen($fn, "r");
1654    if (!is_resource($f)) {
1655        return false;
1656    }
1657    $order = [];
1658    while (!feof($f)) {
1659        $op = trim(fgets($f));
1660        if ($op !== "") {
1661            $order[$op] = null;
1662        }
1663    }
1664    fclose($f);
1665    return $order;
1666}
1667
1668// Generates all opcode handlers and helpers (specialized or unspecialized)
1669function gen_executor_code($f, $spec, $kind, $prolog, &$switch_labels = array()) {
1670    global $list, $opcodes, $helpers, $op_types_ex, $gen_order;
1671
1672    if ($spec) {
1673        // Produce specialized executor
1674        $op1t = $op_types_ex;
1675        // for each op1.op_type
1676        foreach ($op1t as $op1) {
1677            $op2t = $op_types_ex;
1678            // for each op2.op_type
1679            foreach ($op2t as $op2) {
1680                // for each handlers in helpers in original order
1681                foreach ($list as $lineno => $dsc) {
1682                    if (isset($dsc["handler"])) {
1683                        $num = $dsc["handler"];
1684                        foreach (extra_spec_handler($opcodes[$num]) as $extra_spec) {
1685                            // Check if handler accepts such types of operands (op1 and op2)
1686                            if (isset($opcodes[$num]["op1"][$op1]) &&
1687                                isset($opcodes[$num]["op2"][$op2])) {
1688                              // Generate handler code
1689                                gen_handler($f, 1, $kind, $opcodes[$num]["op"], $op1, $op2, isset($opcodes[$num]["use"]), $opcodes[$num]["code"], $lineno, $opcodes[$num], $extra_spec, $switch_labels);
1690                            }
1691                        }
1692                    } else if (isset($dsc["helper"])) {
1693                        $num = $dsc["helper"];
1694                        foreach (extra_spec_handler($helpers[$num]) as $extra_spec) {
1695                            // Check if handler accepts such types of operands (op1 and op2)
1696                            if (isset($helpers[$num]["op1"][$op1]) &&
1697                                isset($helpers[$num]["op2"][$op2])) {
1698                              // Generate helper code
1699                                gen_helper($f, 1, $kind, $num, $op1, $op2, $helpers[$num]["param"], $helpers[$num]["code"], $lineno, $helpers[$num]["inline"], $helpers[$num]["cold"], $helpers[$num]["hot"], $extra_spec);
1700                            }
1701                        }
1702                    } else {
1703                        var_dump($dsc);
1704                        die("??? $kind:$num\n");
1705                    }
1706                }
1707            }
1708        }
1709    } else {
1710        // Produce unspecialized executor
1711
1712        // for each handlers in helpers in original order
1713        foreach ($list as $lineno => $dsc) {
1714            if (isset($dsc["handler"])) {
1715                $num = $dsc["handler"];
1716                // Generate handler code
1717                if ($num < 256) {
1718                    gen_handler($f, 0, $kind, $opcodes[$num]["op"], "ANY", "ANY", isset($opcodes[$num]["use"]), $opcodes[$num]["code"], $lineno, $opcodes[$num]);
1719                }
1720            } else if (isset($dsc["helper"])) {
1721                $num = $dsc["helper"];
1722                // Generate helper code
1723                gen_helper($f, 0, $kind, $num, "ANY", "ANY", $helpers[$num]["param"], $helpers[$num]["code"], $lineno, $helpers[$num]["inline"], $helpers[$num]["cold"], $helpers[$num]["hot"]);
1724            } else {
1725                var_dump($dsc);
1726                die("??? $kind:$num\n");
1727            }
1728        }
1729    }
1730
1731    if (is_array($gen_order)) {
1732        foreach ($gen_order as $txt) {
1733            if ($txt !== null) {
1734                out($f, $txt);
1735            }
1736        }
1737    }
1738
1739    if (ZEND_VM_LINES) {
1740        // Reset #line directives
1741        out_line($f);
1742    }
1743
1744    // Generate handler for undefined opcodes
1745    switch ($kind) {
1746        case ZEND_VM_KIND_CALL:
1747            gen_null_handler($f);
1748            break;
1749        case ZEND_VM_KIND_SWITCH:
1750            out($f,"default: ZEND_NULL_LABEL:\n");
1751            out($f,"\tzend_error_noreturn(E_ERROR, \"Invalid opcode %d/%d/%d.\", OPLINE->opcode, OPLINE->op1_type, OPLINE->op2_type);\n");
1752            out($f,"\tZEND_VM_NEXT_OPCODE(); /* Never reached */\n");
1753            break;
1754        case ZEND_VM_KIND_GOTO:
1755            out($f,"ZEND_NULL_LABEL:\n");
1756            out($f,"\tzend_error_noreturn(E_ERROR, \"Invalid opcode %d/%d/%d.\", OPLINE->opcode, OPLINE->op1_type, OPLINE->op2_type);\n");
1757            out($f,"\tZEND_VM_NEXT_OPCODE(); /* Never reached */\n");
1758            break;
1759        case ZEND_VM_KIND_HYBRID:
1760            out($f,"\t\t\tHYBRID_CASE(HYBRID_HALT):\n");
1761            out($f,"#ifdef ZEND_VM_FP_GLOBAL_REG\n");
1762            out($f,"\t\t\t\texecute_data = vm_stack_data.orig_execute_data;\n");
1763            out($f,"#endif\n");
1764            out($f,"#ifdef ZEND_VM_IP_GLOBAL_REG\n");
1765            out($f,"\t\t\t\topline = vm_stack_data.orig_opline;\n");
1766            out($f,"#endif\n");
1767            out($f,"\t\t\t\treturn;\n");
1768            out($f,"\t\t\tHYBRID_DEFAULT:\n");
1769            out($f,"\t\t\t\tVM_TRACE(ZEND_NULL)\n");
1770            out($f,"\t\t\t\tZEND_NULL_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);\n");
1771            out($f,"\t\t\t\tHYBRID_BREAK(); /* Never reached */\n");
1772            break;
1773    }
1774}
1775
1776function skip_blanks($f, $prolog, $epilog) {
1777    if (trim($prolog) != "" || trim($epilog) != "") {
1778        out($f, $prolog.$epilog);
1779    }
1780}
1781
1782// Generates executor from skeleton file and definition (specialized or unspecialized)
1783function gen_executor($f, $skl, $spec, $kind, $executor_name, $initializer_name) {
1784    global $params, $skeleton_file, $line_no, $gen_order;
1785
1786    if ($kind == ZEND_VM_KIND_HYBRID && file_exists(__DIR__ . "/zend_vm_order.txt")) {
1787        $gen_order = read_order_file(__DIR__ . "/zend_vm_order.txt");
1788    } else {
1789        $gen_order = null;
1790    }
1791
1792    $switch_labels = array();
1793    $lineno = 0;
1794    foreach ($skl as $line) {
1795      // Skeleton file contains special markers in form %NAME% those are
1796      // substituted by custom code
1797        if (preg_match("/(.*)[{][%]([A-Z_]*)[%][}](.*)/", $line, $m)) {
1798            switch ($m[2]) {
1799                case "DEFINES":
1800                    out($f,"#define SPEC_START_MASK        0x0000ffff\n");
1801                    out($f,"#define SPEC_EXTRA_MASK        0xfffc0000\n");
1802                    out($f,"#define SPEC_RULE_OP1          0x00010000\n");
1803                    out($f,"#define SPEC_RULE_OP2          0x00020000\n");
1804                    out($f,"#define SPEC_RULE_OP_DATA      0x00040000\n");
1805                    out($f,"#define SPEC_RULE_RETVAL       0x00080000\n");
1806                    out($f,"#define SPEC_RULE_QUICK_ARG    0x00100000\n");
1807                    out($f,"#define SPEC_RULE_SMART_BRANCH 0x00200000\n");
1808                    out($f,"#define SPEC_RULE_COMMUTATIVE  0x00800000\n");
1809                    out($f,"#define SPEC_RULE_ISSET        0x01000000\n");
1810                    out($f,"#define SPEC_RULE_OBSERVER     0x02000000\n");
1811                    out($f,"\n");
1812                    out($f,"static const uint32_t *zend_spec_handlers;\n");
1813                    out($f,"static const void * const *zend_opcode_handlers;\n");
1814                    out($f,"static int zend_handlers_count;\n");
1815                    if ($kind == ZEND_VM_KIND_HYBRID) {
1816                        out($f,"#if (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID)\n");
1817                        out($f,"static const void * const * zend_opcode_handler_funcs;\n");
1818                        out($f,"static zend_op hybrid_halt_op;\n");
1819                        out($f,"#endif\n");
1820                    }
1821                    out($f,"#if (ZEND_VM_KIND != ZEND_VM_KIND_HYBRID) || !ZEND_VM_SPEC\n");
1822                    out($f,"static const void *zend_vm_get_opcode_handler(zend_uchar opcode, const zend_op* op);\n");
1823                    out($f,"#endif\n\n");
1824                    if ($kind == ZEND_VM_KIND_HYBRID) {
1825                        out($f,"#if (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID)\n");
1826                        out($f,"static const void *zend_vm_get_opcode_handler_func(zend_uchar opcode, const zend_op* op);\n");
1827                        out($f,"#else\n");
1828                        out($f,"# define zend_vm_get_opcode_handler_func zend_vm_get_opcode_handler\n");
1829                        out($f,"#endif\n\n");
1830                    }
1831                    out($f,"#ifndef VM_TRACE\n");
1832                    if (is_array($gen_order)) {
1833                        out($f,"# define VM_TRACE(op) ZEND_VM_GUARD(op);\n");
1834                    } else {
1835                        out($f,"# define VM_TRACE(op)\n");
1836                    }
1837                    out($f,"#endif\n");
1838                    out($f,"#ifndef VM_TRACE_START\n");
1839                    out($f,"# define VM_TRACE_START()\n");
1840                    out($f,"#endif\n");
1841                    out($f,"#ifndef VM_TRACE_END\n");
1842                    out($f,"# define VM_TRACE_END()\n");
1843                    out($f,"#endif\n");
1844                    switch ($kind) {
1845                        case ZEND_VM_KIND_HYBRID:
1846                            out($f,"#if (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID)\n");
1847                            out($f,"#define HYBRID_NEXT()     goto *(void**)(OPLINE->handler)\n");
1848                            out($f,"#define HYBRID_SWITCH()   HYBRID_NEXT();\n");
1849                            out($f,"#define HYBRID_CASE(op)   op ## _LABEL\n");
1850                            out($f,"#define HYBRID_BREAK()    HYBRID_NEXT()\n");
1851                            out($f,"#define HYBRID_DEFAULT    ZEND_NULL_LABEL\n");
1852                            out($f,"#endif\n");
1853                        case ZEND_VM_KIND_CALL:
1854                            out($f,"\n");
1855                            out($f,"#ifdef ZEND_VM_FP_GLOBAL_REG\n");
1856                            out($f,"# define ZEND_OPCODE_HANDLER_ARGS void\n");
1857                            out($f,"# define ZEND_OPCODE_HANDLER_ARGS_PASSTHRU\n");
1858                            out($f,"# define ZEND_OPCODE_HANDLER_ARGS_DC\n");
1859                            out($f,"# define ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC\n");
1860                            out($f,"#else\n");
1861                            out($f,"# define ZEND_OPCODE_HANDLER_ARGS zend_execute_data *execute_data\n");
1862                            out($f,"# define ZEND_OPCODE_HANDLER_ARGS_PASSTHRU execute_data\n");
1863                            out($f,"# define ZEND_OPCODE_HANDLER_ARGS_DC , ZEND_OPCODE_HANDLER_ARGS\n");
1864                            out($f,"# define ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC , ZEND_OPCODE_HANDLER_ARGS_PASSTHRU\n");
1865                            out($f,"#endif\n");
1866                            out($f,"\n");
1867                            out($f,"#if defined(ZEND_VM_FP_GLOBAL_REG) && defined(ZEND_VM_IP_GLOBAL_REG)\n");
1868                            out($f,"# define ZEND_OPCODE_HANDLER_RET void\n");
1869                            out($f,"# define ZEND_VM_TAIL_CALL(call) call; return\n");
1870                            out($f,"# ifdef ZEND_VM_TAIL_CALL_DISPATCH\n");
1871                            out($f,"#  define ZEND_VM_CONTINUE()     ((opcode_handler_t)OPLINE->handler)(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); return\n");
1872                            out($f,"# else\n");
1873                            out($f,"#  define ZEND_VM_CONTINUE()     return\n");
1874                            out($f,"# endif\n");
1875                            if ($kind == ZEND_VM_KIND_HYBRID) {
1876                                out($f,"# if (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID)\n");
1877                                out($f,"#  define ZEND_VM_RETURN()        opline = &hybrid_halt_op; return\n");
1878                                out($f,"#  define ZEND_VM_HOT             zend_always_inline ZEND_COLD ZEND_OPT_SIZE\n");
1879                                out($f,"#  define ZEND_VM_COLD            ZEND_COLD ZEND_OPT_SIZE\n");
1880                                out($f,"# else\n");
1881                                out($f,"#  define ZEND_VM_RETURN()        opline = NULL; return\n");
1882                                out($f,"#  define ZEND_VM_HOT\n");
1883                                out($f,"#  define ZEND_VM_COLD            ZEND_COLD ZEND_OPT_SIZE\n");
1884                                out($f,"# endif\n");
1885                            } else {
1886                                out($f,"# define ZEND_VM_RETURN()        opline = NULL; return\n");
1887                                out($f,"# define ZEND_VM_COLD            ZEND_COLD ZEND_OPT_SIZE\n");
1888                            }
1889                            out($f,"#else\n");
1890                            out($f,"# define ZEND_OPCODE_HANDLER_RET int\n");
1891                            out($f,"# define ZEND_VM_TAIL_CALL(call) return call\n");
1892                            out($f,"# define ZEND_VM_CONTINUE()      return  0\n");
1893                            out($f,"# define ZEND_VM_RETURN()        return -1\n");
1894                            if ($kind == ZEND_VM_KIND_HYBRID) {
1895                                out($f,"# define ZEND_VM_HOT\n");
1896                            }
1897                            out($f,"# define ZEND_VM_COLD            ZEND_COLD ZEND_OPT_SIZE\n");
1898                            out($f,"#endif\n");
1899                            out($f,"\n");
1900                            out($f,"typedef ZEND_OPCODE_HANDLER_RET (ZEND_FASTCALL *opcode_handler_t) (ZEND_OPCODE_HANDLER_ARGS);\n");
1901                            out($f,"\n");
1902                            out($f,"#define DCL_OPLINE\n");
1903                            out($f,"#ifdef ZEND_VM_IP_GLOBAL_REG\n");
1904                            out($f,"# define OPLINE opline\n");
1905                            out($f,"# define USE_OPLINE\n");
1906                            out($f,"# define LOAD_OPLINE() opline = EX(opline)\n");
1907                            out($f,"# define LOAD_OPLINE_EX()\n");
1908                            out($f,"# define LOAD_NEXT_OPLINE() opline = EX(opline) + 1\n");
1909                            out($f,"# define SAVE_OPLINE() EX(opline) = opline\n");
1910                            out($f,"# define SAVE_OPLINE_EX() SAVE_OPLINE()\n");
1911                            out($f,"#else\n");
1912                            out($f,"# define OPLINE EX(opline)\n");
1913                            out($f,"# define USE_OPLINE const zend_op *opline = EX(opline);\n");
1914                            out($f,"# define LOAD_OPLINE()\n");
1915                            out($f,"# define LOAD_OPLINE_EX()\n");
1916                            out($f,"# define LOAD_NEXT_OPLINE() ZEND_VM_INC_OPCODE()\n");
1917                            out($f,"# define SAVE_OPLINE()\n");
1918                            out($f,"# define SAVE_OPLINE_EX()\n");
1919                            out($f,"#endif\n");
1920                            out($f,"#define HANDLE_EXCEPTION() ZEND_ASSERT(EG(exception)); LOAD_OPLINE(); ZEND_VM_CONTINUE()\n");
1921                            out($f,"#define HANDLE_EXCEPTION_LEAVE() ZEND_ASSERT(EG(exception)); LOAD_OPLINE(); ZEND_VM_LEAVE()\n");
1922                            out($f,"#if defined(ZEND_VM_FP_GLOBAL_REG)\n");
1923                            out($f,"# define ZEND_VM_ENTER_EX()        ZEND_VM_INTERRUPT_CHECK(); ZEND_VM_CONTINUE()\n");
1924                            out($f,"# define ZEND_VM_ENTER()           execute_data = EG(current_execute_data); LOAD_OPLINE(); ZEND_VM_ENTER_EX()\n");
1925                            out($f,"# define ZEND_VM_LEAVE()           ZEND_VM_CONTINUE()\n");
1926                            out($f,"#elif defined(ZEND_VM_IP_GLOBAL_REG)\n");
1927                            out($f,"# define ZEND_VM_ENTER_EX()        return  1\n");
1928                            out($f,"# define ZEND_VM_ENTER()           opline = EG(current_execute_data)->opline; ZEND_VM_ENTER_EX()\n");
1929                            out($f,"# define ZEND_VM_LEAVE()           return  2\n");
1930                            out($f,"#else\n");
1931                            out($f,"# define ZEND_VM_ENTER_EX()        return  1\n");
1932                            out($f,"# define ZEND_VM_ENTER()           return  1\n");
1933                            out($f,"# define ZEND_VM_LEAVE()           return  2\n");
1934                            out($f,"#endif\n");
1935                            out($f,"#define ZEND_VM_INTERRUPT()      ZEND_VM_TAIL_CALL(zend_interrupt_helper".($spec?"_SPEC":"")."(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU));\n");
1936                            out($f,"#define ZEND_VM_LOOP_INTERRUPT() zend_interrupt_helper".($spec?"_SPEC":"")."(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);\n");
1937                            if ($kind == ZEND_VM_KIND_HYBRID) {
1938                                out($f,"#define ZEND_VM_DISPATCH(opcode, opline) ZEND_VM_TAIL_CALL(((opcode_handler_t)zend_vm_get_opcode_handler_func(opcode, opline))(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU));\n");
1939                            } else {
1940                                out($f,"#define ZEND_VM_DISPATCH(opcode, opline) ZEND_VM_TAIL_CALL(((opcode_handler_t)zend_vm_get_opcode_handler(opcode, opline))(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU));\n");
1941                            }
1942                            out($f,"\n");
1943                            out($f,"static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_interrupt_helper".($spec?"_SPEC":"")."(ZEND_OPCODE_HANDLER_ARGS);\n");
1944                            out($f,"static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_NULL_HANDLER(ZEND_OPCODE_HANDLER_ARGS);\n");
1945                            out($f,"\n");
1946                            break;
1947                        case ZEND_VM_KIND_SWITCH:
1948                            out($f,"\n");
1949                            out($f,"#define OPLINE opline\n");
1950                            out($f,"#ifdef ZEND_VM_IP_GLOBAL_REG\n");
1951                            out($f,"# define DCL_OPLINE register const zend_op *opline __asm__(ZEND_VM_IP_GLOBAL_REG);\n");
1952                            out($f,"#else\n");
1953                            out($f,"# define DCL_OPLINE const zend_op *opline;\n");
1954                            out($f,"#endif\n");
1955                            out($f,"#define USE_OPLINE\n");
1956                            out($f,"#define LOAD_OPLINE() opline = EX(opline)\n");
1957                            out($f,"# define LOAD_OPLINE_EX() LOAD_OPLINE()\n");
1958                            out($f,"#define LOAD_NEXT_OPLINE() opline = EX(opline) + 1\n");
1959                            out($f,"#define SAVE_OPLINE() EX(opline) = opline\n");
1960                            out($f,"#define SAVE_OPLINE_EX()\n");
1961                            out($f,"#define HANDLE_EXCEPTION() ZEND_ASSERT(EG(exception)); LOAD_OPLINE(); ZEND_VM_CONTINUE()\n");
1962                            out($f,"#define HANDLE_EXCEPTION_LEAVE() ZEND_ASSERT(EG(exception)); LOAD_OPLINE(); ZEND_VM_LEAVE()\n");
1963                            out($f,"#define ZEND_VM_CONTINUE() goto zend_vm_continue\n");
1964                            out($f,"#define ZEND_VM_RETURN()   return\n");
1965                            out($f,"#define ZEND_VM_ENTER_EX() ZEND_VM_INTERRUPT_CHECK(); ZEND_VM_CONTINUE()\n");
1966                            out($f,"#define ZEND_VM_ENTER()    execute_data = EG(current_execute_data); LOAD_OPLINE(); ZEND_VM_ENTER_EX()\n");
1967                            out($f,"#define ZEND_VM_LEAVE()    ZEND_VM_CONTINUE()\n");
1968                            out($f,"#define ZEND_VM_INTERRUPT()              goto zend_interrupt_helper".($spec?"_SPEC":"").";\n");
1969                            out($f,"#define ZEND_VM_LOOP_INTERRUPT()         goto zend_interrupt_helper".($spec?"_SPEC":"").";\n");
1970                            out($f,"#define ZEND_VM_DISPATCH(opcode, opline) dispatch_handler = zend_vm_get_opcode_handler(opcode, opline); goto zend_vm_dispatch;\n");
1971                            out($f,"\n");
1972                            break;
1973                        case ZEND_VM_KIND_GOTO:
1974                            out($f,"\n");
1975                            out($f,"#define OPLINE opline\n");
1976                            out($f,"#ifdef ZEND_VM_IP_GLOBAL_REG\n");
1977                            out($f,"# define DCL_OPLINE register const zend_op *opline __asm__(ZEND_VM_IP_GLOBAL_REG);\n");
1978                            out($f,"#else\n");
1979                            out($f,"# define DCL_OPLINE const zend_op *opline;\n");
1980                            out($f,"#endif\n");
1981                            out($f,"#define USE_OPLINE\n");
1982                            out($f,"#define LOAD_OPLINE() opline = EX(opline)\n");
1983                            out($f,"#define LOAD_OPLINE_EX() LOAD_OPLINE()\n");
1984                            out($f,"#define LOAD_NEXT_OPLINE() opline = EX(opline) + 1\n");
1985                            out($f,"#define SAVE_OPLINE() EX(opline) = opline\n");
1986                            out($f,"#define SAVE_OPLINE_EX()\n");
1987                            if (ZEND_VM_SPEC) {
1988                                out($f,"#define HANDLE_EXCEPTION() ZEND_ASSERT(EG(exception)); goto ZEND_HANDLE_EXCEPTION_SPEC_LABEL\n");
1989                                out($f,"#define HANDLE_EXCEPTION_LEAVE() ZEND_ASSERT(EG(exception)); goto ZEND_HANDLE_EXCEPTION_SPEC_LABEL\n");
1990                            } else {
1991                                out($f,"#define HANDLE_EXCEPTION() ZEND_ASSERT(EG(exception)); goto ZEND_HANDLE_EXCEPTION_LABEL\n");
1992                                out($f,"#define HANDLE_EXCEPTION_LEAVE() ZEND_ASSERT(EG(exception)); goto ZEND_HANDLE_EXCEPTION_LABEL\n");
1993                            }
1994                            out($f,"#define ZEND_VM_CONTINUE() goto *(void**)(OPLINE->handler)\n");
1995                            out($f,"#define ZEND_VM_RETURN()   return\n");
1996                            out($f,"#define ZEND_VM_ENTER_EX() ZEND_VM_INTERRUPT_CHECK(); ZEND_VM_CONTINUE()\n");
1997                            out($f,"#define ZEND_VM_ENTER()    execute_data = EG(current_execute_data); LOAD_OPLINE(); ZEND_VM_ENTER_EX()\n");
1998                            out($f,"#define ZEND_VM_LEAVE()    ZEND_VM_CONTINUE()\n");
1999                            out($f,"#define ZEND_VM_INTERRUPT()              goto zend_interrupt_helper".($spec?"_SPEC":"").";\n");
2000                            out($f,"#define ZEND_VM_LOOP_INTERRUPT()         goto zend_interrupt_helper".($spec?"_SPEC":"").";\n");
2001                            out($f,"#define ZEND_VM_DISPATCH(opcode, opline) goto *(void**)(zend_vm_get_opcode_handler(opcode, opline));\n");
2002                            out($f,"\n");
2003                            break;
2004                    }
2005                    if ($kind == ZEND_VM_KIND_HYBRID) {
2006                        gen_executor_code($f, $spec, ZEND_VM_KIND_CALL, $m[1]);
2007                        out($f,"\n");
2008                        out($f,"#if (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID)\n");
2009                        out($f,"# undef ZEND_VM_TAIL_CALL\n");
2010                        out($f,"# undef ZEND_VM_CONTINUE\n");
2011                        out($f,"# undef ZEND_VM_RETURN\n");
2012//						out($f,"# undef ZEND_VM_INTERRUPT\n");
2013                        out($f,"\n");
2014                        out($f,"# define ZEND_VM_TAIL_CALL(call) call; ZEND_VM_CONTINUE()\n");
2015                        out($f,"# define ZEND_VM_CONTINUE()      HYBRID_NEXT()\n");
2016                        out($f,"# define ZEND_VM_RETURN()        goto HYBRID_HALT_LABEL\n");
2017//						out($f,"# define ZEND_VM_INTERRUPT()     goto zend_interrupt_helper_SPEC_LABEL\n");
2018                        out($f,"#endif\n\n");
2019                    }
2020                    break;
2021                case "EXECUTOR_NAME":
2022                    out($f, $m[1].$executor_name.$m[3]."\n");
2023                    break;
2024                case "HELPER_VARS":
2025                    if ($kind == ZEND_VM_KIND_SWITCH) {
2026                        out($f,$m[1]."const void *dispatch_handler;\n");
2027                    }
2028                    if ($kind != ZEND_VM_KIND_CALL && count($params)) {
2029                        if ($kind == ZEND_VM_KIND_HYBRID) {
2030                            out($f, "#if (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID)\n");
2031                        }
2032                        // Emit local variables those are used for helpers' parameters
2033                        foreach ($params as $param => $x) {
2034                            out($f,$m[1].$param.";\n");
2035                        }
2036                        if ($kind == ZEND_VM_KIND_HYBRID) {
2037                            out($f, "#endif\n");
2038                        }
2039                    }
2040                    if ($kind != ZEND_VM_KIND_CALL && $kind != ZEND_VM_KIND_HYBRID) {
2041                        out($f,"#ifdef ZEND_VM_FP_GLOBAL_REG\n");
2042                        out($f,$m[1]."register zend_execute_data *execute_data __asm__(ZEND_VM_FP_GLOBAL_REG) = ex;\n");
2043                        out($f,"#else\n");
2044                        out($f,$m[1]."zend_execute_data *execute_data = ex;\n");
2045                        out($f,"#endif\n");
2046                    } else {
2047                        out($f,"#if defined(ZEND_VM_IP_GLOBAL_REG) || defined(ZEND_VM_FP_GLOBAL_REG)\n");
2048                        out($f,$m[1]."struct {\n");
2049                        out($f,"#ifdef ZEND_VM_IP_GLOBAL_REG\n");
2050                        out($f,$m[1]."\tconst zend_op *orig_opline;\n");
2051                        out($f,"#endif\n");
2052                        out($f,"#ifdef ZEND_VM_FP_GLOBAL_REG\n");
2053                        out($f,$m[1]."\tzend_execute_data *orig_execute_data;\n");
2054                        out($f,"#ifdef ZEND_VM_HYBRID_JIT_RED_ZONE_SIZE\n");
2055                        out($f,$m[1]."\tchar hybrid_jit_red_zone[ZEND_VM_HYBRID_JIT_RED_ZONE_SIZE];\n");
2056                        out($f,"#endif\n");
2057                        out($f,"#endif\n");
2058                        out($f,$m[1]."} vm_stack_data;\n");
2059                        out($f,"#endif\n");
2060                        out($f,"#ifdef ZEND_VM_IP_GLOBAL_REG\n");
2061                        out($f,$m[1]."vm_stack_data.orig_opline = opline;\n");
2062                        out($f,"#endif\n");
2063                        out($f,"#ifdef ZEND_VM_FP_GLOBAL_REG\n");
2064                        out($f,$m[1]."vm_stack_data.orig_execute_data = execute_data;\n");
2065                        out($f,$m[1]."execute_data = ex;\n");
2066                        out($f,"#else\n");
2067                        out($f,$m[1]."zend_execute_data *execute_data = ex;\n");
2068                        out($f,"#endif\n");
2069                    }
2070                    break;
2071                case "INTERNAL_LABELS":
2072                    if ($kind == ZEND_VM_KIND_GOTO || $kind == ZEND_VM_KIND_HYBRID) {
2073                      // Emit array of labels of opcode handlers and code for
2074                      // zend_opcode_handlers initialization
2075                        if ($kind == ZEND_VM_KIND_HYBRID) {
2076                            out($f,"#if (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID)\n");
2077                        }
2078                        $prolog = $m[1];
2079                        out($f,$prolog."if (UNEXPECTED(execute_data == NULL)) {\n");
2080                        out($f,$prolog."\tstatic const void * const labels[] = {\n");
2081                        gen_labels($f, $spec, ($kind == ZEND_VM_KIND_HYBRID) ? ZEND_VM_KIND_GOTO : $kind, $prolog."\t\t", $specs);
2082                        out($f,$prolog."\t};\n");
2083                        out($f,$prolog."\tzend_opcode_handlers = (const void **) labels;\n");
2084                        out($f,$prolog."\tzend_handlers_count = sizeof(labels) / sizeof(void*);\n");
2085                        if ($kind == ZEND_VM_KIND_HYBRID) {
2086                            out($f,$prolog."\tmemset(&hybrid_halt_op, 0, sizeof(hybrid_halt_op));\n");
2087                            out($f,$prolog."\thybrid_halt_op.handler = (void*)&&HYBRID_HALT_LABEL;\n");
2088	                        out($f,"#ifdef ZEND_VM_HYBRID_JIT_RED_ZONE_SIZE\n");
2089	                        out($f,$prolog."\tmemset(vm_stack_data.hybrid_jit_red_zone, 0, ZEND_VM_HYBRID_JIT_RED_ZONE_SIZE);\n");
2090	                        out($f,"#endif\n");
2091	                        out($f,$prolog."\tif (zend_touch_vm_stack_data) {\n");
2092	                        out($f,$prolog."\t\tzend_touch_vm_stack_data(&vm_stack_data);\n");
2093	                        out($f,$prolog."\t}\n");
2094                            out($f,$prolog."\tgoto HYBRID_HALT_LABEL;\n");
2095                        } else {
2096                            out($f,$prolog."\treturn;\n");
2097                        }
2098                        out($f,$prolog."}\n");
2099                        if ($kind == ZEND_VM_KIND_HYBRID) {
2100                            out($f,"#endif\n");
2101                        }
2102                    } else {
2103                        skip_blanks($f, $m[1], $m[3]);
2104                    }
2105                    break;
2106                case "ZEND_VM_CONTINUE_LABEL":
2107                    if ($kind == ZEND_VM_KIND_CALL || $kind == ZEND_VM_KIND_HYBRID) {
2108                      // Only SWITCH dispatch method use it
2109                        out($f,"#if !defined(ZEND_VM_FP_GLOBAL_REG) || !defined(ZEND_VM_IP_GLOBAL_REG)\n");
2110                        out($f,$m[1]."\tint ret;".$m[3]."\n");
2111                        out($f,"#endif\n");
2112                    } else if ($kind == ZEND_VM_KIND_SWITCH) {
2113                      // Only SWITCH dispatch method use it
2114                        out($f,"zend_vm_continue:".$m[3]."\n");
2115                    } else {
2116                        skip_blanks($f, $m[1], $m[3]);
2117                    }
2118                    break;
2119                case "ZEND_VM_DISPATCH":
2120                  // Emit code that dispatches to opcode handler
2121                    switch ($kind) {
2122                        case ZEND_VM_KIND_SWITCH:
2123                            out($f, $m[1]."dispatch_handler = OPLINE->handler;\nzend_vm_dispatch:\n".$m[1]."switch ((int)(uintptr_t)dispatch_handler)".$m[3]."\n");
2124                            break;
2125                        case ZEND_VM_KIND_GOTO:
2126                            out($f, $m[1]."goto *(void**)(OPLINE->handler);".$m[3]."\n");
2127                            break;
2128                        case ZEND_VM_KIND_HYBRID:
2129                            out($f,"#if (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID)\n");
2130                            out($f, $m[1]."HYBRID_SWITCH()".$m[3]."\n");
2131                            out($f,"#else\n");
2132                        case ZEND_VM_KIND_CALL:
2133                            out($f,"#if defined(ZEND_VM_FP_GLOBAL_REG) && defined(ZEND_VM_IP_GLOBAL_REG)\n");
2134                            out($f, $m[1]."((opcode_handler_t)OPLINE->handler)(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);\n");
2135                            out($f, $m[1]."if (UNEXPECTED(!OPLINE))".$m[3]."\n");
2136                            out($f,"#else\n");
2137                            out($f, $m[1]."if (UNEXPECTED((ret = ((opcode_handler_t)OPLINE->handler)(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)) != 0))".$m[3]."\n");
2138                            out($f,"#endif\n");
2139                            if ($kind == ZEND_VM_KIND_HYBRID) {
2140                                out($f,"#endif\n");
2141                            }
2142                            break;
2143                    }
2144                    break;
2145                case "INTERNAL_EXECUTOR":
2146                    if ($kind != ZEND_VM_KIND_CALL) {
2147                        // Emit executor code
2148                        if ($kind == ZEND_VM_KIND_HYBRID) {
2149                            out($f,"#if (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID)\n");
2150                        }
2151                        gen_executor_code($f, $spec, $kind, $m[1], $switch_labels);
2152                    }
2153                    if ($kind == ZEND_VM_KIND_CALL || $kind == ZEND_VM_KIND_HYBRID) {
2154                        // Executor is defined as a set of functions
2155                        if ($kind == ZEND_VM_KIND_HYBRID) {
2156                            out($f,"#else\n");
2157                        }
2158                        out($f,
2159                                "#ifdef ZEND_VM_FP_GLOBAL_REG\n" .
2160                                $m[1]."execute_data = vm_stack_data.orig_execute_data;\n" .
2161                                "# ifdef ZEND_VM_IP_GLOBAL_REG\n" .
2162                                $m[1]."opline = vm_stack_data.orig_opline;\n" .
2163                                "# endif\n" .
2164                                $m[1]."return;\n" .
2165                                "#else\n" .
2166                                $m[1]."if (EXPECTED(ret > 0)) {\n" .
2167                                $m[1]."\texecute_data = EG(current_execute_data);\n".
2168                                $m[1]."\tZEND_VM_LOOP_INTERRUPT_CHECK();\n".
2169                                $m[1]."} else {\n" .
2170                                "# ifdef ZEND_VM_IP_GLOBAL_REG\n" .
2171                                $m[1]."\topline = vm_stack_data.orig_opline;\n" .
2172                                "# endif\n".
2173                                $m[1]."\treturn;\n".
2174                                $m[1]."}\n".
2175                                "#endif\n");
2176                        if ($kind == ZEND_VM_KIND_HYBRID) {
2177                            out($f,"#endif\n");
2178                        }
2179                    }
2180                    break;
2181                case "EXTERNAL_EXECUTOR":
2182                    if ($kind == ZEND_VM_KIND_CALL) {
2183                        gen_executor_code($f, $spec, $kind, $m[1]);
2184                    }
2185                    break;
2186                case "INITIALIZER_NAME":
2187                    out($f, $m[1].$initializer_name.$m[3]."\n");
2188                    break;
2189                case "EXTERNAL_LABELS":
2190                  // Emit code that initializes zend_opcode_handlers array
2191                    $prolog = $m[1];
2192                    if ($kind == ZEND_VM_KIND_GOTO) {
2193                      // Labels are defined in the executor itself, so we call it
2194                      // with execute_data NULL and it sets zend_opcode_handlers array
2195                        out($f,$prolog."static const uint32_t specs[] = {\n");
2196                        gen_specs($f, $prolog."\t", $specs);
2197                        out($f,$prolog."};\n");
2198                        out($f,$prolog."zend_spec_handlers = specs;\n");
2199                        out($f,$prolog.$executor_name."_ex(NULL);\n");
2200                    } else {
2201                        out($f,$prolog."static const void * const labels[] = {\n");
2202                        gen_labels($f, $spec, ($kind == ZEND_VM_KIND_HYBRID) ? ZEND_VM_KIND_CALL : $kind, $prolog."\t", $specs, $switch_labels);
2203                        out($f,$prolog."};\n");
2204                        out($f,$prolog."static const uint32_t specs[] = {\n");
2205                        gen_specs($f, $prolog."\t", $specs);
2206                        out($f,$prolog."};\n");
2207                        if ($kind == ZEND_VM_KIND_HYBRID) {
2208                            out($f,"#if (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID)\n");
2209                            out($f,$prolog."zend_opcode_handler_funcs = labels;\n");
2210                            out($f,$prolog."zend_spec_handlers = specs;\n");
2211                            out($f,$prolog.$executor_name."_ex(NULL);\n");
2212                            out($f,"#else\n");
2213                        }
2214                        out($f,$prolog."zend_opcode_handlers = labels;\n");
2215                        out($f,$prolog."zend_handlers_count = sizeof(labels) / sizeof(void*);\n");
2216                        out($f,$prolog."zend_spec_handlers = specs;\n");
2217                        if ($kind == ZEND_VM_KIND_HYBRID) {
2218                            out($f,"#endif\n");
2219                        }
2220                    }
2221                    break;
2222                default:
2223                    die("ERROR: Unknown keyword ".$m[2]." in skeleton file.\n");
2224            }
2225        } else {
2226          // Copy the line as is
2227            out($f, $line);
2228        }
2229    }
2230}
2231
2232function parse_operand_spec($def, $lineno, $str, &$flags) {
2233    global $vm_op_decode;
2234
2235    $flags = 0;
2236    $a = explode("|",$str);
2237    foreach ($a as $val) {
2238        if (isset($vm_op_decode[$val])) {
2239            $flags |= $vm_op_decode[$val];
2240        } else {
2241            die("ERROR ($def:$lineno): Wrong operand type '$str'\n");
2242        }
2243    }
2244    if (!($flags & ZEND_VM_OP_SPEC)) {
2245        if (count($a) != 1) {
2246            die("ERROR ($def:$lineno): Wrong operand type '$str'\n");
2247        }
2248        $a = array("ANY");
2249    }
2250    return array_flip($a);
2251}
2252
2253function parse_ext_spec($def, $lineno, $str) {
2254    global $vm_ext_decode;
2255
2256    $flags = 0;
2257    $a = explode("|",$str);
2258    foreach ($a as $val) {
2259        if (isset($vm_ext_decode[$val])) {
2260            $flags |= $vm_ext_decode[$val];
2261        } else {
2262            die("ERROR ($def:$lineno): Wrong extended_value type '$str'\n");
2263        }
2264    }
2265    return $flags;
2266}
2267
2268function parse_spec_rules($def, $lineno, $str) {
2269    global $used_extra_spec;
2270
2271    $ret = array();
2272    $a = explode(",", $str);
2273    foreach ($a as $rule) {
2274        $n = strpos($rule, "=");
2275        if ($n !== false) {
2276            $id = trim(substr($rule, 0, $n));
2277            $val = trim(substr($rule, $n+1));
2278            switch ($id) {
2279                case "OP_DATA":
2280                    $ret["OP_DATA"] = parse_operand_spec($def, $lineno, $val, $devnull);
2281                    break;
2282                default:
2283                    die("ERROR ($def:$lineno): Wrong specialization rules '$str'\n");
2284            }
2285            $used_extra_spec[$id] = 1;
2286        } else {
2287            switch ($rule) {
2288                case "RETVAL":
2289                    $ret["RETVAL"] = array(0, 1);
2290                    break;
2291                case "QUICK_ARG":
2292                    $ret["QUICK_ARG"] = array(0, 1);
2293                    break;
2294                case "SMART_BRANCH":
2295                    $ret["SMART_BRANCH"] = array(0, 1, 2);
2296                    break;
2297                case "NO_CONST_CONST":
2298                    $ret["NO_CONST_CONST"] = array(1);
2299                    break;
2300                case "COMMUTATIVE":
2301                    $ret["COMMUTATIVE"] = array(1);
2302                    break;
2303                case "ISSET":
2304                    $ret["ISSET"] = array(0, 1);
2305                    break;
2306                case "OBSERVER":
2307                    $ret["OBSERVER"] = array(0, 1);
2308                    break;
2309                default:
2310                    die("ERROR ($def:$lineno): Wrong specialization rules '$str'\n");
2311            }
2312            $used_extra_spec[$rule] = 1;
2313        }
2314    }
2315    return $ret;
2316}
2317
2318function gen_vm_opcodes_header(
2319    array $opcodes, int $max_opcode, int $max_opcode_len, array $vm_op_flags
2320): string {
2321    $str = HEADER_TEXT;
2322    $str .= "#ifndef ZEND_VM_OPCODES_H\n#define ZEND_VM_OPCODES_H\n\n";
2323    $str .= "#define ZEND_VM_SPEC\t\t" . ZEND_VM_SPEC . "\n";
2324    $str .= "#define ZEND_VM_LINES\t\t" . ZEND_VM_LINES . "\n";
2325    $str .= "#define ZEND_VM_KIND_CALL\t" . ZEND_VM_KIND_CALL . "\n";
2326    $str .= "#define ZEND_VM_KIND_SWITCH\t" . ZEND_VM_KIND_SWITCH . "\n";
2327    $str .= "#define ZEND_VM_KIND_GOTO\t" . ZEND_VM_KIND_GOTO . "\n";
2328    $str .= "#define ZEND_VM_KIND_HYBRID\t" . ZEND_VM_KIND_HYBRID . "\n";
2329    if ($GLOBALS["vm_kind_name"][ZEND_VM_KIND] === "ZEND_VM_KIND_HYBRID") {
2330        $str .= "/* HYBRID requires support for computed GOTO and global register variables*/\n";
2331        $str .= "#if (defined(__GNUC__) && defined(HAVE_GCC_GLOBAL_REGS))\n";
2332        $str .= "# define ZEND_VM_KIND\t\tZEND_VM_KIND_HYBRID\n";
2333        $str .= "#else\n";
2334        $str .= "# define ZEND_VM_KIND\t\tZEND_VM_KIND_CALL\n";
2335        $str .= "#endif\n";
2336    } else {
2337        $str .= "#define ZEND_VM_KIND\t\t" . $GLOBALS["vm_kind_name"][ZEND_VM_KIND] . "\n";
2338    }
2339    $str .= "\n";
2340    $str .= "#if (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID) && !defined(__SANITIZE_ADDRESS__)\n";
2341    $str .= "# if ((defined(i386) && !defined(__PIC__)) || defined(__x86_64__) || defined(_M_X64))\n";
2342    $str .= "#  define ZEND_VM_HYBRID_JIT_RED_ZONE_SIZE 16\n";
2343    $str .= "# endif\n";
2344    $str .= "#endif\n";
2345    $str .= "\n";
2346    foreach ($vm_op_flags as $name => $val) {
2347        $str .= sprintf("#define %-24s 0x%08x\n", $name, $val);
2348    }
2349    $str .= "#define ZEND_VM_OP1_FLAGS(flags) (flags & 0xff)\n";
2350    $str .= "#define ZEND_VM_OP2_FLAGS(flags) ((flags >> 8) & 0xff)\n";
2351    $str .= "\n";
2352    $str .= "BEGIN_EXTERN_C()\n\n";
2353    $str .= "ZEND_API const char* ZEND_FASTCALL zend_get_opcode_name(zend_uchar opcode);\n";
2354    $str .= "ZEND_API uint32_t ZEND_FASTCALL zend_get_opcode_flags(zend_uchar opcode);\n";
2355    $str .= "ZEND_API zend_uchar zend_get_opcode_id(const char *name, size_t length);\n\n";
2356    $str .= "END_EXTERN_C()\n\n";
2357
2358    $code_len = strlen((string) $max_opcode);
2359    foreach ($opcodes as $code => $dsc) {
2360        $code = str_pad((string)$code, $code_len, " ", STR_PAD_LEFT);
2361        $op = str_pad($dsc["op"], $max_opcode_len);
2362        if ($code <= $max_opcode) {
2363            $str .= "#define $op $code\n";
2364        }
2365    }
2366
2367    $code = str_pad((string)$max_opcode, $code_len, " ", STR_PAD_LEFT);
2368    $op = str_pad("ZEND_VM_LAST_OPCODE", $max_opcode_len);
2369    $str .= "\n#define $op $code\n";
2370
2371    $str .= "\n#endif\n";
2372    return $str;
2373}
2374
2375function gen_vm($def, $skel) {
2376    global $definition_file, $skeleton_file, $executor_file,
2377        $op_types, $list, $opcodes, $helpers, $params, $opnames,
2378        $vm_op_flags, $used_extra_spec;
2379
2380    // Load definition file
2381    $in = @file($def);
2382    if (!$in) {
2383        die("ERROR: Cannot open definition file '$def'\n");
2384    }
2385    // We need absolute path to definition file to use it in #line directives
2386    $definition_file = realpath($def);
2387
2388    // Load skeleton file
2389    $skl = @file($skel);
2390    if (!$skl) {
2391        die("ERROR: Cannot open skeleton file '$skel'\n");
2392    }
2393    // We need absolute path to skeleton file to use it in #line directives
2394    $skeleton_file = realpath($skel);
2395
2396    // Parse definition file into tree
2397    $lineno         = 0;
2398    $handler        = null;
2399    $helper         = null;
2400    $max_opcode_len = 0;
2401    $max_opcode     = 0;
2402    $extra_num      = 256;
2403    foreach ($in as $line) {
2404        ++$lineno;
2405        if (strpos($line,"ZEND_VM_HANDLER(") === 0 ||
2406            strpos($line,"ZEND_VM_INLINE_HANDLER(") === 0 ||
2407            strpos($line,"ZEND_VM_HOT_HANDLER(") === 0 ||
2408            strpos($line,"ZEND_VM_HOT_NOCONST_HANDLER(") === 0 ||
2409            strpos($line,"ZEND_VM_HOT_NOCONSTCONST_HANDLER(") === 0 ||
2410            strpos($line,"ZEND_VM_HOT_SEND_HANDLER(") === 0 ||
2411            strpos($line,"ZEND_VM_HOT_OBJ_HANDLER(") === 0 ||
2412            strpos($line,"ZEND_VM_COLD_HANDLER(") === 0 ||
2413            strpos($line,"ZEND_VM_COLD_CONST_HANDLER(") === 0 ||
2414            strpos($line,"ZEND_VM_COLD_CONSTCONST_HANDLER(") === 0) {
2415          // Parsing opcode handler's definition
2416            if (preg_match(
2417                    "/^ZEND_VM_(HOT_|INLINE_|HOT_OBJ_|HOT_SEND_|HOT_NOCONST_|HOT_NOCONSTCONST_|COLD_|COLD_CONST_|COLD_CONSTCONST_)?HANDLER\(\s*([0-9]+)\s*,\s*([A-Z_]+)\s*,\s*([A-Z_|]+)\s*,\s*([A-Z_|]+)\s*(,\s*([A-Z_|]+)\s*)?(,\s*SPEC\(([A-Z_|=,]+)\)\s*)?\)/",
2418                    $line,
2419                    $m) == 0) {
2420                die("ERROR ($def:$lineno): Invalid ZEND_VM_HANDLER definition.\n");
2421            }
2422            $hot = !empty($m[1]) ? $m[1] : false;
2423            $code = (int)$m[2];
2424            $op   = $m[3];
2425            $len  = strlen($op);
2426            $op1  = parse_operand_spec($def, $lineno, $m[4], $flags1);
2427            $op2  = parse_operand_spec($def, $lineno, $m[5], $flags2);
2428            $flags = $flags1 | ($flags2 << 8);
2429            if (!empty($m[7])) {
2430                $flags |= parse_ext_spec($def, $lineno, $m[7]);
2431            }
2432
2433            if ($len > $max_opcode_len) {
2434                $max_opcode_len = $len;
2435            }
2436            if ($code > $max_opcode) {
2437                $max_opcode = $code;
2438            }
2439            if (isset($opcodes[$code])) {
2440                die("ERROR ($def:$lineno): Opcode with code '$code' is already defined.\n");
2441            }
2442            if (isset($opnames[$op])) {
2443                die("ERROR ($def:$lineno): Opcode with name '$op' is already defined.\n");
2444            }
2445            $opcodes[$code] = array("op"=>$op,"op1"=>$op1,"op2"=>$op2,"code"=>"","flags"=>$flags,"hot"=>$hot);
2446            if (isset($m[9])) {
2447                $opcodes[$code]["spec"] = parse_spec_rules($def, $lineno, $m[9]);
2448                if (isset($opcodes[$code]["spec"]["NO_CONST_CONST"])) {
2449                    $opcodes[$code]["flags"] |= $vm_op_flags["ZEND_VM_NO_CONST_CONST"];
2450                }
2451                if (isset($opcodes[$code]["spec"]["COMMUTATIVE"])) {
2452                    $opcodes[$code]["flags"] |= $vm_op_flags["ZEND_VM_COMMUTATIVE"];
2453                }
2454            }
2455            $opnames[$op] = $code;
2456            $handler = $code;
2457            $helper = null;
2458            $list[$lineno] = array("handler"=>$handler);
2459        } else if (strpos($line,"ZEND_VM_TYPE_SPEC_HANDLER(") === 0 ||
2460                   strpos($line,"ZEND_VM_INLINE_TYPE_SPEC_HANDLER(") === 0 ||
2461                   strpos($line,"ZEND_VM_HOT_TYPE_SPEC_HANDLER(") === 0 ||
2462                   strpos($line,"ZEND_VM_HOT_NOCONST_TYPE_SPEC_HANDLER(") === 0 ||
2463                   strpos($line,"ZEND_VM_HOT_NOCONSTCONST_TYPE_SPEC_HANDLER(") === 0 ||
2464                   strpos($line,"ZEND_VM_HOT_SEND_TYPE_SPEC_HANDLER(") === 0 ||
2465                   strpos($line,"ZEND_VM_HOT_OBJ_TYPE_SPEC_HANDLER(") === 0) {
2466          // Parsing opcode handler's definition
2467            if (preg_match(
2468                    "/^ZEND_VM_(HOT_|INLINE_|HOT_OBJ_|HOT_SEND_|HOT_NOCONST_|HOT_NOCONSTCONST_)?TYPE_SPEC_HANDLER\(\s*([A-Z_|]+)\s*,\s*((?:[^(,]|\([^()]*|(?R)*\))*),\s*([A-Za-z_]+)\s*,\s*([A-Z_|]+)\s*,\s*([A-Z_|]+)\s*(,\s*([A-Z_|]+)\s*)?(,\s*SPEC\(([A-Z_|=,]+)\)\s*)?\)/",
2469                    $line,
2470                    $m) == 0) {
2471                die("ERROR ($def:$lineno): Invalid ZEND_VM_TYPE_HANDLER_HANDLER definition.\n");
2472            }
2473            $hot = !empty($m[1]) ? $m[1] : false;
2474            $orig_op_list = $m[2];
2475            $code = $extra_num++;
2476            foreach (explode('|', $orig_op_list) as $orig_op) {
2477                if (!isset($opnames[$orig_op])) {
2478                    die("ERROR ($def:$lineno): Opcode with name '$orig_op' is not defined.\n");
2479                }
2480                $orig_code = $opnames[$orig_op];
2481                $condition = $m[3];
2482                $opcodes[$orig_code]['type_spec'][$code] = $condition;
2483            }
2484            $op = $m[4];
2485            $op1 = parse_operand_spec($def, $lineno, $m[5], $flags1);
2486            $op2 = parse_operand_spec($def, $lineno, $m[6], $flags2);
2487            $flags = $flags1 | ($flags2 << 8);
2488            if (!empty($m[8])) {
2489                $flags |= parse_ext_spec($def, $lineno, $m[8]);
2490            }
2491
2492            if (isset($opcodes[$code])) {
2493                die("ERROR ($def:$lineno): Opcode with name '$code' is already defined.\n");
2494            }
2495            $used_extra_spec["TYPE"] = 1;
2496            $opcodes[$code] = array("op"=>$op,"op1"=>$op1,"op2"=>$op2,"code"=>"","flags"=>$flags,"hot"=>$hot,"is_type_spec"=>true);
2497            if (isset($m[10])) {
2498                $opcodes[$code]["spec"] = parse_spec_rules($def, $lineno, $m[10]);
2499                if (isset($opcodes[$code]["spec"]["NO_CONST_CONST"])) {
2500                    $opcodes[$code]["flags"] |= $vm_op_flags["ZEND_VM_NO_CONST_CONST"];
2501                }
2502                if (isset($opcodes[$code]["spec"]["COMMUTATIVE"])) {
2503                    $opcodes[$code]["flags"] |= $vm_op_flags["ZEND_VM_COMMUTATIVE"];
2504                }
2505            }
2506            $opnames[$op] = $code;
2507            $handler = $code;
2508            $helper = null;
2509            $list[$lineno] = array("handler"=>$handler);
2510        } else if (strpos($line,"ZEND_VM_HELPER(") === 0 ||
2511                   strpos($line,"ZEND_VM_INLINE_HELPER(") === 0 ||
2512                   strpos($line,"ZEND_VM_COLD_HELPER(") === 0 ||
2513                   strpos($line,"ZEND_VM_HOT_HELPER(") === 0) {
2514          // Parsing helper's definition
2515            if (preg_match(
2516                    "/^ZEND_VM(_INLINE|_COLD|_HOT)?_HELPER\(\s*([A-Za-z_]+)\s*,\s*([A-Z_|]+)\s*,\s*([A-Z_|]+)\s*(?:,\s*SPEC\(([A-Z_|=,]+)\)\s*)?(?:,\s*([^)]*)\s*)?\)/",
2517                    $line,
2518                    $m) == 0) {
2519                die("ERROR ($def:$lineno): Invalid ZEND_VM_HELPER definition.\n");
2520            }
2521            $inline = !empty($m[1]) && $m[1] === "_INLINE";
2522            $cold   = !empty($m[1]) && $m[1] === "_COLD";
2523            $hot    = !empty($m[1]) && $m[1] === "_HOT";
2524            $helper = $m[2];
2525            $op1    = parse_operand_spec($def, $lineno, $m[3], $flags1);
2526            $op2    = parse_operand_spec($def, $lineno, $m[4], $flags2);
2527            $param  = isset($m[6]) ? $m[6] : null;
2528            if (isset($helpers[$helper])) {
2529                die("ERROR ($def:$lineno): Helper with name '$helper' is already defined.\n");
2530            }
2531
2532            // Store parameters
2533            if ((ZEND_VM_KIND == ZEND_VM_KIND_GOTO
2534             || ZEND_VM_KIND == ZEND_VM_KIND_SWITCH
2535             || (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID && $hot))
2536             && $param) {
2537                foreach (explode(",", $param ) as $p) {
2538                    $p = trim($p);
2539                    if ($p !== "") {
2540                        $params[$p] = 1;
2541                    }
2542                }
2543            }
2544
2545            $helpers[$helper] = array("op1"=>$op1,"op2"=>$op2,"param"=>$param,"code"=>"","inline"=>$inline,"cold"=>$cold,"hot"=>$hot);
2546
2547            if (!empty($m[5])) {
2548                $helpers[$helper]["spec"] = parse_spec_rules($def, $lineno, $m[5]);
2549            }
2550
2551            $handler = null;
2552            $list[$lineno] = array("helper"=>$helper);
2553        } else if (strpos($line,"ZEND_VM_DEFINE_OP(") === 0) {
2554            if (preg_match(
2555                    "/^ZEND_VM_DEFINE_OP\(\s*([0-9]+)\s*,\s*([A-Z_]+)\s*\);/",
2556                    $line,
2557                    $m) == 0) {
2558                die("ERROR ($def:$lineno): Invalid ZEND_VM_DEFINE_OP definition.\n");
2559            }
2560            $code = (int)$m[1];
2561            $op   = $m[2];
2562            $len  = strlen($op);
2563
2564            if ($len > $max_opcode_len) {
2565                $max_opcode_len = $len;
2566            }
2567            if ($code > $max_opcode) {
2568                $max_opcode = $code;
2569            }
2570            if (isset($opcodes[$code])) {
2571                die("ERROR ($def:$lineno): Opcode with code '$code' is already defined.\n");
2572            }
2573            if (isset($opnames[$op])) {
2574                die("ERROR ($def:$lineno): Opcode with name '$op' is already defined.\n");
2575            }
2576            $opcodes[$code] = array("op"=>$op,"code"=>"");
2577            $opnames[$op] = $code;
2578        } else if ($handler !== null) {
2579          // Add line of code to current opcode handler
2580            $opcodes[$handler]["code"] .= $line;
2581        } else if ($helper !== null) {
2582          // Add line of code to current helper
2583            $helpers[$helper]["code"] .= $line;
2584        }
2585    }
2586
2587    ksort($opcodes);
2588
2589    // Search for opcode handlers those are used by other opcode handlers
2590    foreach ($opcodes as $dsc) {
2591        if (preg_match("/^\s*{\s*ZEND_VM_DISPATCH_TO_HANDLER\(\s*([A-Z_]*)\s*\)\s*;\s*}\s*/", $dsc["code"], $m)) {
2592            $op = $m[1];
2593            if (!isset($opnames[$op])) {
2594                die("ERROR ($def:$lineno): Opcode with name '$op' is not defined.\n");
2595            }
2596            $opcodes[$opnames[$dsc['op']]]['alias'] = $op;
2597            if (!ZEND_VM_SPEC && ZEND_VM_KIND == ZEND_VM_KIND_SWITCH) {
2598                $code = $opnames[$op];
2599                $opcodes[$code]['use'] = 1;
2600            }
2601        } else if (preg_match_all("/ZEND_VM_DISPATCH_TO_HANDLER\(\s*([A-Z_]*)\s*\)/m", $dsc["code"], $mm, PREG_SET_ORDER)) {
2602            foreach ($mm as $m) {
2603                $op = $m[1];
2604                if (!isset($opnames[$op])) {
2605                    die("ERROR ($def:$lineno): Opcode with name '$op' is not defined.\n");
2606                }
2607                $code = $opnames[$op];
2608                $opcodes[$code]['use'] = 1;
2609            }
2610        }
2611    }
2612
2613    // Generate opcode #defines (zend_vm_opcodes.h)
2614    $str = gen_vm_opcodes_header($opcodes, $max_opcode, $max_opcode_len, $vm_op_flags);
2615    write_file_if_changed(__DIR__ . "/zend_vm_opcodes.h", $str);
2616    echo "zend_vm_opcodes.h generated successfully.\n";
2617
2618    // zend_vm_opcodes.c
2619    $f = fopen(__DIR__ . "/zend_vm_opcodes.c", "w+") or die("ERROR: Cannot create zend_vm_opcodes.c\n");
2620
2621    // Insert header
2622    out($f, HEADER_TEXT);
2623    fputs($f,"#include <stdio.h>\n");
2624    fputs($f,"#include <zend.h>\n");
2625    fputs($f,"#include <zend_vm_opcodes.h>\n\n");
2626
2627    fputs($f,"static const char *zend_vm_opcodes_names[".($max_opcode + 1)."] = {\n");
2628    for ($i = 0; $i <= $max_opcode; $i++) {
2629        fputs($f,"\t".(isset($opcodes[$i]["op"])?'"'.$opcodes[$i]["op"].'"':"NULL").",\n");
2630    }
2631    fputs($f, "};\n\n");
2632
2633    fputs($f,"static uint32_t zend_vm_opcodes_flags[".($max_opcode + 1)."] = {\n");
2634    for ($i = 0; $i <= $max_opcode; $i++) {
2635        fprintf($f, "\t0x%08x,\n", isset($opcodes[$i]["flags"]) ? $opcodes[$i]["flags"] : 0);
2636    }
2637    fputs($f, "};\n\n");
2638
2639    fputs($f, "ZEND_API const char* ZEND_FASTCALL zend_get_opcode_name(zend_uchar opcode) {\n");
2640    fputs($f, "\tif (UNEXPECTED(opcode > ZEND_VM_LAST_OPCODE)) {\n");
2641    fputs($f, "\t\treturn NULL;\n");
2642    fputs($f, "\t}\n");
2643    fputs($f, "\treturn zend_vm_opcodes_names[opcode];\n");
2644    fputs($f, "}\n");
2645
2646    fputs($f, "ZEND_API uint32_t ZEND_FASTCALL zend_get_opcode_flags(zend_uchar opcode) {\n");
2647    fputs($f, "\tif (UNEXPECTED(opcode > ZEND_VM_LAST_OPCODE)) {\n");
2648    fputs($f, "\t\topcode = ZEND_NOP;\n");
2649    fputs($f, "\t}\n");
2650    fputs($f, "\treturn zend_vm_opcodes_flags[opcode];\n");
2651    fputs($f, "}\n");
2652
2653    fputs($f, "ZEND_API zend_uchar zend_get_opcode_id(const char *name, size_t length) {\n");
2654    fputs($f, "\tzend_uchar opcode;\n");
2655    fputs($f, "\tfor (opcode = 0; opcode < (sizeof(zend_vm_opcodes_names) / sizeof(zend_vm_opcodes_names[0])) - 1; opcode++) {\n");
2656    fputs($f, "\t\tconst char *opcode_name = zend_vm_opcodes_names[opcode];\n");
2657    fputs($f, "\t\tif (opcode_name && strncmp(opcode_name, name, length) == 0) {\n");
2658    fputs($f, "\t\t\treturn opcode;\n");
2659    fputs($f, "\t\t}\n");
2660    fputs($f, "\t}\n");
2661    fputs($f, "\treturn ZEND_VM_LAST_OPCODE + 1;\n");
2662    fputs($f, "}\n");
2663
2664    fclose($f);
2665    echo "zend_vm_opcodes.c generated successfully.\n";
2666
2667    // Generate zend_vm_execute.h
2668    $f = fopen(__DIR__ . "/zend_vm_execute.h", "w+") or die("ERROR: Cannot create zend_vm_execute.h\n");
2669    $executor_file = realpath(__DIR__ . "/zend_vm_execute.h");
2670
2671    // Insert header
2672    out($f, HEADER_TEXT);
2673
2674    out($f, "#ifdef ZEND_WIN32\n");
2675    // Suppress free_op1 warnings on Windows
2676    out($f, "# pragma warning(disable : 4101)\n");
2677    if (ZEND_VM_SPEC) {
2678        // Suppress (<non-zero constant> || <expression>) warnings on windows
2679        out($f, "# pragma warning(once : 6235)\n");
2680        // Suppress (<zero> && <expression>) warnings on windows
2681        out($f, "# pragma warning(once : 6237)\n");
2682        // Suppress (<non-zero constant> && <expression>) warnings on windows
2683        out($f, "# pragma warning(once : 6239)\n");
2684        // Suppress (<expression> && <non-zero constant>) warnings on windows
2685        out($f, "# pragma warning(once : 6240)\n");
2686        // Suppress (<non-zero constant> || <non-zero constant>) warnings on windows
2687        out($f, "# pragma warning(once : 6285)\n");
2688        // Suppress (<non-zero constant> || <expression>) warnings on windows
2689        out($f, "# pragma warning(once : 6286)\n");
2690        // Suppress constant with constant comparison warnings on windows
2691        out($f, "# pragma warning(once : 6326)\n");
2692    }
2693    out($f, "#endif\n");
2694
2695    // Support for ZEND_USER_OPCODE
2696    out($f, "static user_opcode_handler_t zend_user_opcode_handlers[256] = {\n");
2697    for ($i = 0; $i < 255; ++$i) {
2698        out($f, "\t(user_opcode_handler_t)NULL,\n");
2699    }
2700    out($f, "\t(user_opcode_handler_t)NULL\n};\n\n");
2701
2702    out($f, "static zend_uchar zend_user_opcodes[256] = {");
2703    for ($i = 0; $i < 255; ++$i) {
2704        if ($i % 16 == 1) out($f, "\n\t");
2705        out($f, "$i,");
2706    }
2707    out($f, "255\n};\n\n");
2708
2709    // Generate specialized executor
2710    gen_executor($f, $skl, ZEND_VM_SPEC, ZEND_VM_KIND, "execute", "zend_vm_init");
2711    out($f, "\n");
2712
2713    // Generate zend_vm_get_opcode_handler() function
2714    out($f, "static uint32_t ZEND_FASTCALL zend_vm_get_opcode_handler_idx(uint32_t spec, const zend_op* op)\n");
2715    out($f, "{\n");
2716    if (!ZEND_VM_SPEC) {
2717        out($f, "\treturn spec;\n");
2718    } else {
2719        out($f, "\tstatic const int zend_vm_decode[] = {\n");
2720        out($f, "\t\t_UNUSED_CODE, /* 0 = IS_UNUSED  */\n");
2721        out($f, "\t\t_CONST_CODE,  /* 1 = IS_CONST   */\n");
2722        out($f, "\t\t_TMP_CODE,    /* 2 = IS_TMP_VAR */\n");
2723        out($f, "\t\t_UNUSED_CODE, /* 3              */\n");
2724        out($f, "\t\t_VAR_CODE,    /* 4 = IS_VAR     */\n");
2725        out($f, "\t\t_UNUSED_CODE, /* 5              */\n");
2726        out($f, "\t\t_UNUSED_CODE, /* 6              */\n");
2727        out($f, "\t\t_UNUSED_CODE, /* 7              */\n");
2728        out($f, "\t\t_CV_CODE      /* 8 = IS_CV      */\n");
2729        out($f, "\t};\n");
2730        out($f, "\tuint32_t offset = 0;\n");
2731        out($f, "\tif (spec & SPEC_RULE_OP1) offset = offset * 5 + zend_vm_decode[op->op1_type];\n");
2732        out($f, "\tif (spec & SPEC_RULE_OP2) offset = offset * 5 + zend_vm_decode[op->op2_type];\n");
2733
2734        if (isset($used_extra_spec["OP_DATA"]) ||
2735            isset($used_extra_spec["RETVAL"]) ||
2736            isset($used_extra_spec["QUICK_ARG"]) ||
2737            isset($used_extra_spec["SMART_BRANCH"]) ||
2738            isset($used_extra_spec["ISSET"]) ||
2739            isset($used_extra_spec["OBSERVER"])) {
2740
2741            $else = "";
2742            out($f, "\tif (spec & SPEC_EXTRA_MASK) {\n");
2743
2744            if (isset($used_extra_spec["RETVAL"])) {
2745                out($f, "\t\t{$else}if (spec & SPEC_RULE_RETVAL) {\n");
2746                out($f, "\t\t\toffset = offset * 2 + (op->result_type != IS_UNUSED);\n");
2747                out($f, "\t\t\tif ((spec & SPEC_RULE_OBSERVER) && ZEND_OBSERVER_ENABLED) {\n");
2748                out($f,	"\t\t\t\toffset += 2;\n");
2749                out($f, "\t\t\t}\n");
2750                $else = "} else ";
2751            }
2752            if (isset($used_extra_spec["QUICK_ARG"])) {
2753                out($f, "\t\t{$else}if (spec & SPEC_RULE_QUICK_ARG) {\n");
2754                out($f, "\t\t\toffset = offset * 2 + (op->op2.num <= MAX_ARG_FLAG_NUM);\n");
2755                $else = "} else ";
2756            }
2757            if (isset($used_extra_spec["OP_DATA"])) {
2758                out($f, "\t\t{$else}if (spec & SPEC_RULE_OP_DATA) {\n");
2759                out($f, "\t\t\toffset = offset * 5 + zend_vm_decode[(op + 1)->op1_type];\n");
2760                $else = "} else ";
2761            }
2762            if (isset($used_extra_spec["ISSET"])) {
2763                out($f, "\t\t{$else}if (spec & SPEC_RULE_ISSET) {\n");
2764                out($f, "\t\t\toffset = offset * 2 + (op->extended_value & ZEND_ISEMPTY);\n");
2765                $else = "} else ";
2766            }
2767            if (isset($used_extra_spec["SMART_BRANCH"])) {
2768                out($f, "\t\t{$else}if (spec & SPEC_RULE_SMART_BRANCH) {\n");
2769                out($f,	"\t\t\toffset = offset * 3;\n");
2770                out($f, "\t\t\tif (op->result_type == (IS_SMART_BRANCH_JMPZ|IS_TMP_VAR)) {\n");
2771                out($f,	"\t\t\t\toffset += 1;\n");
2772                out($f, "\t\t\t} else if (op->result_type == (IS_SMART_BRANCH_JMPNZ|IS_TMP_VAR)) {\n");
2773                out($f,	"\t\t\t\toffset += 2;\n");
2774                out($f, "\t\t\t}\n");
2775                $else = "} else ";
2776            }
2777            if (isset($used_extra_spec["OBSERVER"])) {
2778                out($f, "\t\t{$else}if (spec & SPEC_RULE_OBSERVER) {\n");
2779                out($f,	"\t\t\toffset = offset * 2;\n");
2780                out($f, "\t\t\tif (ZEND_OBSERVER_ENABLED) {\n");
2781                out($f,	"\t\t\t\toffset += 1;\n");
2782                out($f, "\t\t\t}\n");
2783                $else = "} else ";
2784            }
2785            if ($else !== "") {
2786                out($f, "\t\t}\n");
2787            }
2788            out($f, "\t}\n");
2789        }
2790        out($f, "\treturn (spec & SPEC_START_MASK) + offset;\n");
2791    }
2792    out($f, "}\n\n");
2793    out($f, "#if (ZEND_VM_KIND != ZEND_VM_KIND_HYBRID) || !ZEND_VM_SPEC\n");
2794    out($f, "static const void *zend_vm_get_opcode_handler(zend_uchar opcode, const zend_op* op)\n");
2795    out($f, "{\n");
2796    if (!ZEND_VM_SPEC) {
2797        out($f, "\treturn zend_opcode_handlers[zend_vm_get_opcode_handler_idx(opcode, op)];\n");
2798    } else {
2799        out($f, "\treturn zend_opcode_handlers[zend_vm_get_opcode_handler_idx(zend_spec_handlers[opcode], op)];\n");
2800    }
2801    out($f, "}\n");
2802    out($f, "#endif\n\n");
2803
2804    if (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID) {
2805        // Generate zend_vm_get_opcode_handler_func() function
2806        out($f, "#if ZEND_VM_KIND == ZEND_VM_KIND_HYBRID\n");
2807        out($f,"static const void *zend_vm_get_opcode_handler_func(zend_uchar opcode, const zend_op* op)\n");
2808        out($f, "{\n");
2809        out($f, "\tuint32_t spec = zend_spec_handlers[opcode];\n");
2810        if (!ZEND_VM_SPEC) {
2811            out($f, "\treturn zend_opcode_handler_funcs[spec];\n");
2812        } else {
2813            out($f, "\treturn zend_opcode_handler_funcs[zend_vm_get_opcode_handler_idx(spec, op)];\n");
2814        }
2815        out($f, "}\n\n");
2816        out($f, "#endif\n\n");
2817    }
2818
2819    // Generate zend_vm_get_opcode_handler() function
2820    out($f, "ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler(zend_op* op)\n");
2821    out($f, "{\n");
2822    out($f, "\tzend_uchar opcode = zend_user_opcodes[op->opcode];\n");
2823    if (!ZEND_VM_SPEC) {
2824        out($f, "\top->handler = zend_opcode_handlers[zend_vm_get_opcode_handler_idx(opcode, op)];\n");
2825    } else {
2826        out($f, "\n");
2827        out($f, "\tif (zend_spec_handlers[op->opcode] & SPEC_RULE_COMMUTATIVE) {\n");
2828        out($f, "\t\tif (op->op1_type < op->op2_type) {\n");
2829        out($f, "\t\t\tzend_swap_operands(op);\n");
2830        out($f, "\t\t}\n");
2831        out($f, "\t}\n");
2832        out($f, "\top->handler = zend_opcode_handlers[zend_vm_get_opcode_handler_idx(zend_spec_handlers[opcode], op)];\n");
2833    }
2834    out($f, "}\n\n");
2835
2836    // Generate zend_vm_set_opcode_handler_ex() function
2837    out($f, "ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t op1_info, uint32_t op2_info, uint32_t res_info)\n");
2838    out($f, "{\n");
2839    out($f, "\tzend_uchar opcode = zend_user_opcodes[op->opcode];\n");
2840    if (!ZEND_VM_SPEC) {
2841        out($f, "\top->handler = zend_opcode_handlers[zend_vm_get_opcode_handler_idx(opcode, op)];\n");
2842    } else {
2843        out($f, "\tuint32_t spec = zend_spec_handlers[opcode];\n");
2844        if (isset($used_extra_spec["TYPE"])) {
2845            out($f, "\tswitch (opcode) {\n");
2846            foreach ($opcodes as $code => $dsc) {
2847                if (isset($dsc['type_spec'])) {
2848                    $orig_op = $dsc['op'];
2849                    out($f, "\t\tcase $orig_op:\n");
2850                    if (isset($dsc["spec"]["COMMUTATIVE"])) {
2851                        out($f, "\t\t\tif (op->op1_type < op->op2_type) {\n");
2852                        out($f, "\t\t\t\tzend_swap_operands(op);\n");
2853                        out($f, "\t\t\t}\n");
2854                    }
2855                    $first = true;
2856                    foreach ($dsc['type_spec'] as $code => $condition) {
2857                        $condition = format_condition($condition);
2858                        if ($first) {
2859                            out($f, "\t\t\tif $condition {\n");
2860                            $first = false;
2861                        } else {
2862                            out($f, "\t\t\t} else if $condition {\n");
2863                        }
2864                        $spec_dsc = $opcodes[$code];
2865                        if (isset($spec_dsc["spec"]["NO_CONST_CONST"])) {
2866                            out($f, "\t\t\t\tif (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {\n");
2867                            out($f, "\t\t\t\t\tbreak;\n");
2868                            out($f, "\t\t\t\t}\n");
2869                        }
2870                        out($f, "\t\t\t\tspec = {$spec_dsc['spec_code']};\n");
2871                        if (isset($spec_dsc["spec"]["COMMUTATIVE"]) && !isset($dsc["spec"]["COMMUTATIVE"])) {
2872                            out($f, "\t\t\t\tif (op->op1_type < op->op2_type) {\n");
2873                            out($f, "\t\t\t\t\tzend_swap_operands(op);\n");
2874                            out($f, "\t\t\t\t}\n");
2875                        }
2876                    }
2877                    if (!$first) {
2878                        out($f, "\t\t\t}\n");
2879                    }
2880                    out($f, "\t\t\tbreak;\n");
2881                }
2882            }
2883            $has_commutative = false;
2884            foreach ($opcodes as $code => $dsc) {
2885                if (!isset($dsc['is_type_spec']) &&
2886                    !isset($dsc['type_spec']) &&
2887                    isset($dsc["spec"]["COMMUTATIVE"])) {
2888                    $orig_op = $dsc['op'];
2889                    out($f, "\t\tcase $orig_op:\n");
2890                    $has_commutative = true;
2891                }
2892            }
2893            if ($has_commutative) {
2894                out($f, "\t\t\tif (op->op1_type < op->op2_type) {\n");
2895                out($f, "\t\t\t\tzend_swap_operands(op);\n");
2896                out($f, "\t\t\t}\n");
2897                out($f, "\t\t\tbreak;\n");
2898                out($f, "\t\tcase ZEND_USER_OPCODE:\n");
2899                out($f, "\t\t\tif (zend_spec_handlers[op->opcode] & SPEC_RULE_COMMUTATIVE) {\n");
2900                out($f, "\t\t\t\tif (op->op1_type < op->op2_type) {\n");
2901                out($f, "\t\t\t\t\tzend_swap_operands(op);\n");
2902                out($f, "\t\t\t\t}\n");
2903                out($f, "\t\t\t}\n");
2904                out($f, "\t\t\tbreak;\n");
2905            }
2906            out($f, "\t\tdefault:\n");
2907            out($f, "\t\t\tbreak;\n");
2908            out($f, "\t}\n");
2909        }
2910        out($f, "\top->handler = zend_opcode_handlers[zend_vm_get_opcode_handler_idx(spec, op)];\n");
2911    }
2912    out($f, "}\n\n");
2913
2914    // Generate zend_vm_call_opcode_handler() function
2915    if (ZEND_VM_KIND == ZEND_VM_KIND_CALL || ZEND_VM_KIND == ZEND_VM_KIND_HYBRID) {
2916        out($f, "ZEND_API int ZEND_FASTCALL zend_vm_call_opcode_handler(zend_execute_data* ex)\n");
2917        out($f, "{\n");
2918        if (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID) {
2919            out($f,"#if (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID)\n");
2920            out($f, "\topcode_handler_t handler;\n");
2921            out($f,"#endif\n");
2922        }
2923        out($f, "\tint ret;\n");
2924        out($f, "#ifdef ZEND_VM_IP_GLOBAL_REG\n");
2925        out($f, "\tconst zend_op *orig_opline = opline;\n");
2926        out($f, "#endif\n");
2927        out($f, "#ifdef ZEND_VM_FP_GLOBAL_REG\n");
2928        out($f, "\tzend_execute_data *orig_execute_data = execute_data;\n");
2929        out($f, "\texecute_data = ex;\n");
2930        out($f, "#else\n");
2931        out($f, "\tzend_execute_data *execute_data = ex;\n");
2932        out($f, "#endif\n");
2933        out($f, "\n");
2934        out($f, "\tLOAD_OPLINE();\n");
2935        out($f,"#if defined(ZEND_VM_FP_GLOBAL_REG) && defined(ZEND_VM_IP_GLOBAL_REG)\n");
2936        if (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID) {
2937            out($f,"#if (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID)\n");
2938            out($f, "\thandler = (opcode_handler_t)zend_vm_get_opcode_handler_func(zend_user_opcodes[opline->opcode], opline);\n");
2939            out($f, "\thandler(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);\n");
2940            out($f, "\tif (EXPECTED(opline != &hybrid_halt_op)) {\n");
2941            out($f,"#else\n");
2942        }
2943        out($f, "\t((opcode_handler_t)OPLINE->handler)(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);\n");
2944        if (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID) {
2945            out($f, "\tif (EXPECTED(opline)) {\n");
2946            out($f,"#endif\n");
2947        } else {
2948            out($f, "\tif (EXPECTED(opline)) {\n");
2949        }
2950        out($f, "\t\tret = execute_data != ex ? (int)(execute_data->prev_execute_data != ex) + 1 : 0;\n");
2951        out($f, "\t\tSAVE_OPLINE();\n");
2952        out($f, "\t} else {\n");
2953        out($f, "\t\tret = -1;\n");
2954        out($f, "\t}\n");
2955        out($f, "#else\n");
2956        out($f, "\tret = ((opcode_handler_t)OPLINE->handler)(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);\n");
2957        out($f, "\tSAVE_OPLINE();\n");
2958        out($f, "#endif\n");
2959        out($f, "#ifdef ZEND_VM_FP_GLOBAL_REG\n");
2960        out($f, "\texecute_data = orig_execute_data;\n");
2961        out($f, "#endif\n");
2962        out($f, "#ifdef ZEND_VM_IP_GLOBAL_REG\n");
2963        out($f, "\topline = orig_opline;\n");
2964        out($f, "#endif\n");
2965        out($f, "\treturn ret;\n");
2966        out($f, "}\n\n");
2967    } else {
2968        out($f, "ZEND_API int ZEND_FASTCALL zend_vm_call_opcode_handler(zend_execute_data* ex)\n");
2969        out($f, "{\n");
2970        out($f, "\tzend_error_noreturn(E_CORE_ERROR, \"zend_vm_call_opcode_handler() is not supported\");\n");
2971        out($f, "\treturn 0;\n");
2972        out($f, "}\n\n");
2973    }
2974
2975    fclose($f);
2976    echo "zend_vm_execute.h generated successfully.\n";
2977}
2978
2979function write_file_if_changed(string $filename, string $contents) {
2980    if (file_exists($filename)) {
2981        $orig_contents = file_get_contents($filename);
2982        if ($orig_contents === $contents) {
2983            // Unchanged, no need to write.
2984            return;
2985        }
2986    }
2987
2988    file_put_contents($filename, $contents);
2989}
2990
2991function usage() {
2992    echo("\nUsage: php zend_vm_gen.php [options]\n".
2993         "\nOptions:".
2994         "\n  --with-vm-kind=CALL|SWITCH|GOTO|HYBRID - select threading model (default is HYBRID)".
2995         "\n  --without-specializer                  - disable executor specialization".
2996         "\n  --with-lines                           - enable #line directives".
2997         "\n\n");
2998}
2999
3000// Parse arguments
3001for ($i = 1; $i < $argc; $i++) {
3002    if (strpos($argv[$i],"--with-vm-kind=") === 0) {
3003        $kind = substr($argv[$i], strlen("--with-vm-kind="));
3004        switch ($kind) {
3005            case "CALL":
3006                define("ZEND_VM_KIND", ZEND_VM_KIND_CALL);
3007                break;
3008            case "SWITCH":
3009                define("ZEND_VM_KIND", ZEND_VM_KIND_SWITCH);
3010                break;
3011            case "GOTO":
3012                define("ZEND_VM_KIND", ZEND_VM_KIND_GOTO);
3013                break;
3014            case "HYBRID":
3015                define("ZEND_VM_KIND", ZEND_VM_KIND_HYBRID);
3016                break;
3017            default:
3018                echo("ERROR: Invalid vm kind '$kind'\n");
3019                usage();
3020                die();
3021        }
3022    } else if ($argv[$i] == "--without-specializer") {
3023        // Disabling specialization
3024        define("ZEND_VM_SPEC", 0);
3025    } else if ($argv[$i] == "--with-lines") {
3026        // Enabling debugging using original zend_vm_def.h
3027        define("ZEND_VM_LINES", 1);
3028    } else if ($argv[$i] == "--help") {
3029        usage();
3030        exit();
3031    } else {
3032        echo("ERROR: Invalid option '".$argv[$i]."'\n");
3033        usage();
3034        die();
3035    }
3036}
3037
3038// Using defaults
3039if (!defined("ZEND_VM_KIND")) {
3040    // Using CALL threading by default
3041    define("ZEND_VM_KIND", ZEND_VM_KIND_HYBRID);
3042}
3043if (!defined("ZEND_VM_SPEC")) {
3044    // Using specialized executor by default
3045    define("ZEND_VM_SPEC", 1);
3046}
3047if (!defined("ZEND_VM_LINES")) {
3048    // Disabling #line directives
3049    define("ZEND_VM_LINES", 0);
3050}
3051
3052gen_vm(__DIR__ . "/zend_vm_def.h", __DIR__ . "/zend_vm_execute.skl");
3053