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" => "zend_get_bad_ptr()", 236 "VAR" => "_get_zval_ptr_ptr_var(opline->op1.var EXECUTE_DATA_CC)", 237 "CONST" => "zend_get_bad_ptr()", 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" => "zend_get_bad_ptr()", 247 "VAR" => "_get_zval_ptr_ptr_var(opline->op2.var EXECUTE_DATA_CC)", 248 "CONST" => "zend_get_bad_ptr()", 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" => "_get_zval_ptr_tmpvarcv(opline->op1_type, opline->op1, \\1 EXECUTE_DATA_CC)", 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" => "_get_zval_ptr_tmpvarcv(opline->op2_type, opline->op2, \\1 EXECUTE_DATA_CC)", 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" => "zend_get_bad_ptr()", 302 "VAR" => "_get_zval_ptr_ptr_var(opline->op1.var EXECUTE_DATA_CC)", 303 "CONST" => "zend_get_bad_ptr()", 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" => "zend_get_bad_ptr()", 313 "VAR" => "_get_zval_ptr_ptr_var(opline->op2.var EXECUTE_DATA_CC)", 314 "CONST" => "zend_get_bad_ptr()", 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" => "zend_get_bad_ptr()", 390 "VAR" => "_get_zval_ptr_ptr_var(opline->op1.var EXECUTE_DATA_CC)", 391 "CONST" => "zend_get_bad_ptr()", 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" => "zend_get_bad_ptr()", 401 "VAR" => "_get_zval_ptr_ptr_var(opline->op2.var EXECUTE_DATA_CC)", 402 "CONST" => "zend_get_bad_ptr()", 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" => "zend_get_bad_ptr()", 412 "VAR" => "_get_zval_ptr_ptr_var(opline->op1.var EXECUTE_DATA_CC)", 413 "CONST" => "zend_get_bad_ptr()", 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" => "zend_get_bad_ptr()", 423 "VAR" => "_get_zval_ptr_ptr_var(opline->op2.var EXECUTE_DATA_CC)", 424 "CONST" => "zend_get_bad_ptr()", 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" => "FREE_OP(opline->op1_type, opline->op1.var)", 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" => "FREE_OP(opline->op2_type, opline->op2.var)", 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" => "zend_get_bad_ptr()", 522 "VAR" => "_get_zval_ptr_ptr_var((opline+1)->op1.var EXECUTE_DATA_CC)", 523 "CONST" => "zend_get_bad_ptr()", 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(uint8_t 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(uint8_t 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(uint8_t opcode);\n"; 2354 $str .= "ZEND_API uint32_t ZEND_FASTCALL zend_get_opcode_flags(uint8_t opcode);\n"; 2355 $str .= "ZEND_API uint8_t 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(uint8_t 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(uint8_t 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 uint8_t zend_get_opcode_id(const char *name, size_t length) {\n"); 2654 fputs($f, "\tuint8_t 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 uint8_t 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(uint8_t 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(uint8_t 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, "\tuint8_t 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, "\tuint8_t 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