root/ext/opcache/Optimizer/compact_literals.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. optimizer_literal_obj_info
  2. optimizer_literal_class_info
  3. optimizer_compact_literals

   1 /* pass 11
   2  * - compact literals table
   3  */
   4 #if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
   5 
   6 #define DEBUG_COMPACT_LITERALS 0
   7 
   8 #define LITERAL_VALUE                        0x0100
   9 #define LITERAL_FUNC                         0x0200
  10 #define LITERAL_CLASS                        0x0300
  11 #define LITERAL_CONST                        0x0400
  12 #define LITERAL_CLASS_CONST                  0x0500
  13 #define LITERAL_STATIC_METHOD                0x0600
  14 #define LITERAL_STATIC_PROPERTY              0x0700
  15 #define LITERAL_METHOD                       0x0800
  16 #define LITERAL_PROPERTY                     0x0900
  17 
  18 #define LITERAL_EX_CLASS                     0x4000
  19 #define LITERAL_EX_OBJ                       0x2000
  20 #define LITERAL_MAY_MERGE                    0x1000
  21 #define LITERAL_KIND_MASK                    0x0f00
  22 #define LITERAL_NUM_RELATED_MASK             0x000f
  23 #define LITERAL_NUM_SLOTS_MASK               0x00f0
  24 #define LITERAL_NUM_SLOTS_SHIFT              4
  25 
  26 #define LITERAL_NUM_RELATED(info) (info & LITERAL_NUM_RELATED_MASK)
  27 #define LITERAL_NUM_SLOTS(info)   ((info & LITERAL_NUM_SLOTS_MASK) >> LITERAL_NUM_SLOTS_SHIFT)
  28 
  29 typedef struct _literal_info {
  30         zend_uint  flags; /* bitmask (see defines above) */
  31         union {
  32                 int    num;   /* variable number or class name literal number */
  33         } u;
  34 } literal_info;
  35 
  36 #define LITERAL_FLAGS(kind, slots, related) \
  37         ((kind) | ((slots) << LITERAL_NUM_SLOTS_SHIFT) | (related))
  38 
  39 #define LITERAL_INFO(n, kind, merge, slots, related) do { \
  40                 info[n].flags = (((merge) ? LITERAL_MAY_MERGE : 0) | LITERAL_FLAGS(kind, slots, related)); \
  41         } while (0)
  42 
  43 #define LITERAL_INFO_CLASS(n, kind, merge, slots, related, _num) do { \
  44                 info[n].flags = (LITERAL_EX_CLASS | ((merge) ? LITERAL_MAY_MERGE : 0) | LITERAL_FLAGS(kind, slots, related)); \
  45                 info[n].u.num = (_num); \
  46         } while (0)
  47 
  48 #define LITERAL_INFO_OBJ(n, kind, merge, slots, related, _num) do { \
  49                 info[n].flags = (LITERAL_EX_OBJ | ((merge) ? LITERAL_MAY_MERGE : 0) | LITERAL_FLAGS(kind, slots, related)); \
  50                 info[n].u.num = (_num); \
  51         } while (0)
  52 
  53 static void optimizer_literal_obj_info(literal_info   *info,
  54                                        zend_uchar      op_type,
  55                                        znode_op        op,
  56                                        int             constant,
  57                                        zend_uint       kind,
  58                                        zend_uint       slots,
  59                                        zend_uint       related,
  60                                        zend_op_array  *op_array)
  61 {
  62         /* For now we merge only $this object properties and methods.
  63          * In general it's also possible to do it for any CV variable as well,
  64          * but it would require complex dataflow and/or type analysis.
  65          */
  66         if (Z_TYPE(op_array->literals[constant].constant) == IS_STRING &&
  67             op_type == IS_UNUSED) {
  68                 LITERAL_INFO_OBJ(constant, kind, 1, slots, related, op_array->this_var);
  69         } else {
  70                 LITERAL_INFO(constant, kind, 0, slots, related);
  71         }
  72 }
  73 
  74 static void optimizer_literal_class_info(literal_info   *info,
  75                                          zend_uchar      op_type,
  76                                          znode_op        op,
  77                                          int             constant,
  78                                          zend_uint       kind,
  79                                          zend_uint       slots,
  80                                          zend_uint       related,
  81                                          zend_op_array  *op_array)
  82 {
  83         if (op_type == IS_CONST) {
  84                 LITERAL_INFO_CLASS(constant, kind, 1, slots, related, op.constant);
  85         } else {
  86                 LITERAL_INFO(constant, kind, 0, slots, related);
  87         }
  88 }
  89 
  90 static void optimizer_compact_literals(zend_op_array *op_array TSRMLS_DC)
  91 {
  92         zend_op *opline, *end;
  93         int i, j, n, *pos, *map, cache_slots;
  94         ulong h;
  95         literal_info *info;
  96         int l_null = -1;
  97         int l_false = -1;
  98         int l_true = -1;
  99         HashTable hash;
 100         char *key;
 101         int key_len;
 102 
 103         if (op_array->last_literal) {
 104                 info = (literal_info*)ecalloc(op_array->last_literal, sizeof(literal_info));
 105 
 106             /* Mark literals of specific types */
 107                 opline = op_array->opcodes;
 108                 end = opline + op_array->last;
 109                 while (opline < end) {
 110                         switch (opline->opcode) {
 111                                 case ZEND_DO_FCALL:
 112                                         LITERAL_INFO(opline->op1.constant, LITERAL_FUNC, 1, 1, 1);
 113                                         break;
 114                                 case ZEND_INIT_FCALL_BY_NAME:
 115                                         if (ZEND_OP2_TYPE(opline) == IS_CONST) {
 116                                                 LITERAL_INFO(opline->op2.constant, LITERAL_FUNC, 1, 1, 2);
 117                                         }
 118                                         break;
 119                                 case ZEND_INIT_NS_FCALL_BY_NAME:
 120                                         LITERAL_INFO(opline->op2.constant, LITERAL_FUNC, 1, 1, 3);
 121                                         break;
 122                                 case ZEND_INIT_METHOD_CALL:
 123                                         if (ZEND_OP2_TYPE(opline) == IS_CONST) {
 124                                                 optimizer_literal_obj_info(
 125                                                         info,
 126                                                         opline->op1_type,
 127                                                         opline->op1,
 128                                                         opline->op2.constant,
 129                                                         LITERAL_METHOD, 2, 2,
 130                                                         op_array);
 131                                         }
 132                                         break;
 133                                 case ZEND_INIT_STATIC_METHOD_CALL:
 134                                         if (ZEND_OP1_TYPE(opline) == IS_CONST) {
 135                                                 LITERAL_INFO(opline->op1.constant, LITERAL_CLASS, 1, 1, 2);
 136                                         }
 137                                         if (ZEND_OP2_TYPE(opline) == IS_CONST) {
 138                                                 optimizer_literal_class_info(
 139                                                         info,
 140                                                         opline->op1_type,
 141                                                         opline->op1,
 142                                                         opline->op2.constant,
 143                                                         LITERAL_STATIC_METHOD, (ZEND_OP1_TYPE(opline) == IS_CONST) ? 1 : 2, 2,
 144                                                         op_array);
 145                                         }
 146                                         break;
 147                                 case ZEND_CATCH:
 148                                         LITERAL_INFO(opline->op1.constant, LITERAL_CLASS, 1, 1, 2);
 149                                         break;
 150                                 case ZEND_FETCH_CONSTANT:
 151                                         if (ZEND_OP1_TYPE(opline) == IS_UNUSED) {
 152                                                 if ((opline->extended_value & (IS_CONSTANT_IN_NAMESPACE|IS_CONSTANT_UNQUALIFIED)) == (IS_CONSTANT_IN_NAMESPACE|IS_CONSTANT_UNQUALIFIED)) {
 153                                                         LITERAL_INFO(opline->op2.constant, LITERAL_CONST, 1, 1, 5);
 154                                                 } else {
 155                                                         LITERAL_INFO(opline->op2.constant, LITERAL_CONST, 1, 1, 3);
 156                                                 }
 157                                         } else {
 158                                                 if (ZEND_OP1_TYPE(opline) == IS_CONST) {
 159                                                         LITERAL_INFO(opline->op1.constant, LITERAL_CLASS, 1, 1, 2);
 160                                                 }
 161                                                 optimizer_literal_class_info(
 162                                                         info,
 163                                                         opline->op1_type,
 164                                                         opline->op1,
 165                                                         opline->op2.constant,
 166                                                         LITERAL_CLASS_CONST, (ZEND_OP1_TYPE(opline) == IS_CONST) ? 1 : 2, 1,
 167                                                         op_array);
 168                                         }
 169                                         break;
 170                                 case ZEND_FETCH_R:
 171                                 case ZEND_FETCH_W:
 172                                 case ZEND_FETCH_RW:
 173                                 case ZEND_FETCH_IS:
 174                                 case ZEND_FETCH_UNSET:
 175                                 case ZEND_FETCH_FUNC_ARG:
 176                                 case ZEND_UNSET_VAR:
 177                                 case ZEND_ISSET_ISEMPTY_VAR:
 178                                         if (ZEND_OP2_TYPE(opline) == IS_UNUSED) {
 179                                                 if (ZEND_OP1_TYPE(opline) == IS_CONST) {
 180                                                         LITERAL_INFO(opline->op1.constant, LITERAL_VALUE, 1, 0, 1);
 181                                                 }
 182                                         } else {
 183                                                 if (ZEND_OP2_TYPE(opline) == IS_CONST) {
 184                                                         LITERAL_INFO(opline->op2.constant, LITERAL_CLASS, 1, 1, 2);
 185                                                 }
 186                                                 if (ZEND_OP1_TYPE(opline) == IS_CONST) {
 187                                                         optimizer_literal_class_info(
 188                                                                 info,
 189                                                                 opline->op2_type,
 190                                                                 opline->op2,
 191                                                                 opline->op1.constant,
 192                                                                 LITERAL_STATIC_PROPERTY, 2, 1,
 193                                                                 op_array);
 194                                                 }
 195                                         }
 196                                         break;
 197                                 case ZEND_FETCH_CLASS:
 198                                 case ZEND_ADD_INTERFACE:
 199                                 case ZEND_ADD_TRAIT:
 200                                         if (ZEND_OP2_TYPE(opline) == IS_CONST) {
 201                                                 LITERAL_INFO(opline->op2.constant, LITERAL_CLASS, 1, 1, 2);
 202                                         }
 203                                         break;
 204                                 case ZEND_ASSIGN_OBJ:
 205                                 case ZEND_FETCH_OBJ_R:
 206                                 case ZEND_FETCH_OBJ_W:
 207                                 case ZEND_FETCH_OBJ_RW:
 208                                 case ZEND_FETCH_OBJ_IS:
 209                                 case ZEND_FETCH_OBJ_UNSET:
 210                                 case ZEND_FETCH_OBJ_FUNC_ARG:
 211                                 case ZEND_UNSET_OBJ:
 212                                 case ZEND_PRE_INC_OBJ:
 213                                 case ZEND_PRE_DEC_OBJ:
 214                                 case ZEND_POST_INC_OBJ:
 215                                 case ZEND_POST_DEC_OBJ:
 216                                 case ZEND_ISSET_ISEMPTY_PROP_OBJ:
 217                                         if (ZEND_OP2_TYPE(opline) == IS_CONST) {
 218                                                 optimizer_literal_obj_info(
 219                                                         info,
 220                                                         opline->op1_type,
 221                                                         opline->op1,
 222                                                         opline->op2.constant,
 223                                                         LITERAL_PROPERTY, 2, 1,
 224                                                         op_array);
 225                                         }
 226                                         break;
 227                                 case ZEND_ASSIGN_ADD:
 228                                 case ZEND_ASSIGN_SUB:
 229                                 case ZEND_ASSIGN_MUL:
 230                                 case ZEND_ASSIGN_DIV:
 231                                 case ZEND_ASSIGN_MOD:
 232                                 case ZEND_ASSIGN_SL:
 233                                 case ZEND_ASSIGN_SR:
 234                                 case ZEND_ASSIGN_CONCAT:
 235                                 case ZEND_ASSIGN_BW_OR:
 236                                 case ZEND_ASSIGN_BW_AND:
 237                                 case ZEND_ASSIGN_BW_XOR:
 238                                         if (ZEND_OP2_TYPE(opline) == IS_CONST) {
 239                                                 if (opline->extended_value == ZEND_ASSIGN_OBJ) {
 240                                                         optimizer_literal_obj_info(
 241                                                                 info,
 242                                                                 opline->op1_type,
 243                                                                 opline->op1,
 244                                                                 opline->op2.constant,
 245                                                                 LITERAL_PROPERTY, 2, 1,
 246                                                                 op_array);
 247                                                 } else {
 248                                                         LITERAL_INFO(opline->op2.constant, LITERAL_VALUE, 1, 0, 1);
 249                                                 }
 250                                         }
 251                                         break;
 252                                 default:
 253                                         if (ZEND_OP1_TYPE(opline) == IS_CONST) {
 254                                                 LITERAL_INFO(opline->op1.constant, LITERAL_VALUE, 1, 0, 1);
 255                                         }
 256                                         if (ZEND_OP2_TYPE(opline) == IS_CONST) {
 257                                                 LITERAL_INFO(opline->op2.constant, LITERAL_VALUE, 1, 0, 1);
 258                                         }
 259                                         break;
 260                         }
 261                         opline++;
 262                 }
 263 
 264 #if DEBUG_COMPACT_LITERALS
 265                 {
 266                         int i, use_copy;
 267                         fprintf(stderr, "File %s func %s\n", op_array->filename,
 268                                         op_array->function_name? op_array->function_name : "main");
 269                         fprintf(stderr, "Literlas table size %d\n", op_array->last_literal);
 270 
 271                         for (i = 0; i < op_array->last_literal; i++) {
 272                                 zval zv = op_array->literals[i].constant;
 273                                 zend_make_printable_zval(&op_array->literals[i].constant, &zv, &use_copy);
 274                                 fprintf(stderr, "Literal %d, val (%d):%s\n", i, Z_STRLEN(zv), Z_STRVAL(zv));
 275                                 if (use_copy) {
 276                                         zval_dtor(&zv);
 277                                 }
 278                         }
 279                         fflush(stderr);
 280                 }
 281 #endif
 282 
 283                 /* Merge equal constants */
 284                 j = 0; cache_slots = 0;
 285                 zend_hash_init(&hash, 16, NULL, NULL, 0);
 286                 map = (int*)ecalloc(op_array->last_literal, sizeof(int));
 287                 for (i = 0; i < op_array->last_literal; i++) {
 288                         if (!info[i].flags) {
 289                                 /* unsed literal */
 290                                 zval_dtor(&op_array->literals[i].constant);
 291                                 continue;
 292                         }
 293                         switch (Z_TYPE(op_array->literals[i].constant)) {
 294                                 case IS_NULL:
 295                                         if (l_null < 0) {
 296                                                 l_null = j;
 297                                                 if (i != j) {
 298                                                         op_array->literals[j] = op_array->literals[i];
 299                                                         info[j] = info[i];
 300                                                 }
 301                                                 j++;
 302                                         }
 303                                         map[i] = l_null;
 304                                         break;
 305                                 case IS_BOOL:
 306                                         if (Z_LVAL(op_array->literals[i].constant)) {
 307                                                 if (l_true < 0) {
 308                                                         l_true = j;
 309                                                         if (i != j) {
 310                                                                 op_array->literals[j] = op_array->literals[i];
 311                                                                 info[j] = info[i];
 312                                                         }
 313                                                         j++;
 314                                                 }
 315                                                 map[i] = l_true;
 316                                         } else {
 317                                                 if (l_false < 0) {
 318                                                         l_false = j;
 319                                                         if (i != j) {
 320                                                                 op_array->literals[j] = op_array->literals[i];
 321                                                                 info[j] = info[i];
 322                                                         }
 323                                                         j++;
 324                                                 }
 325                                                 map[i] = l_false;
 326                                         }
 327                                         break;
 328                                 case IS_LONG:
 329                                         if (zend_hash_index_find(&hash, Z_LVAL(op_array->literals[i].constant), (void**)&pos) == SUCCESS) {
 330                                                 map[i] = *pos;
 331                                         } else {
 332                                                 map[i] = j;
 333                                                 zend_hash_index_update(&hash, Z_LVAL(op_array->literals[i].constant), (void**)&j, sizeof(int), NULL);
 334                                                 if (i != j) {
 335                                                         op_array->literals[j] = op_array->literals[i];
 336                                                         info[j] = info[i];
 337                                                 }
 338                                                 j++;
 339                                         }
 340                                         break;
 341                                 case IS_DOUBLE:
 342                                         if (zend_hash_find(&hash, (char*)&Z_DVAL(op_array->literals[i].constant), sizeof(double), (void**)&pos) == SUCCESS) {
 343                                                 map[i] = *pos;
 344                                         } else {
 345                                                 map[i] = j;
 346                                                 zend_hash_add(&hash, (char*)&Z_DVAL(op_array->literals[i].constant), sizeof(double), (void**)&j, sizeof(int), NULL);
 347                                                 if (i != j) {
 348                                                         op_array->literals[j] = op_array->literals[i];
 349                                                         info[j] = info[i];
 350                                                 }
 351                                                 j++;
 352                                         }
 353                                         break;
 354                                 case IS_STRING:
 355                                 case IS_CONSTANT:
 356                                         if (info[i].flags & LITERAL_MAY_MERGE) {
 357                                                 if (info[i].flags & LITERAL_EX_OBJ) {
 358                                                         key_len = MAX_LENGTH_OF_LONG + sizeof("->") + Z_STRLEN(op_array->literals[i].constant);
 359                                                         key = emalloc(key_len);
 360                                                         key_len = snprintf(key, key_len-1, "%d->%s", info[i].u.num, Z_STRVAL(op_array->literals[i].constant));
 361                                                 } else if (info[i].flags & LITERAL_EX_CLASS) {
 362                                                         zval *class_name = &op_array->literals[(info[i].u.num < i) ? map[info[i].u.num] : info[i].u.num].constant;
 363                                                         key_len = Z_STRLEN_P(class_name) + sizeof("::") + Z_STRLEN(op_array->literals[i].constant);
 364                                                         key = emalloc(key_len);
 365                                                         memcpy(key, Z_STRVAL_P(class_name), Z_STRLEN_P(class_name));
 366                                                         memcpy(key + Z_STRLEN_P(class_name), "::", sizeof("::") - 1);
 367                                                         memcpy(key + Z_STRLEN_P(class_name) + sizeof("::") - 1,
 368                                                                 Z_STRVAL(op_array->literals[i].constant),
 369                                                                 Z_STRLEN(op_array->literals[i].constant) + 1);
 370                                                 } else {
 371                                                         key = Z_STRVAL(op_array->literals[i].constant);
 372                                                         key_len = Z_STRLEN(op_array->literals[i].constant)+1;
 373                                                 }
 374                                                 h = zend_hash_func(key, key_len);
 375                                                 h += info[i].flags;
 376                                         }
 377                                         if ((info[i].flags & LITERAL_MAY_MERGE) &&
 378                                                 zend_hash_quick_find(&hash, key, key_len, h, (void**)&pos) == SUCCESS &&
 379                                                 Z_TYPE(op_array->literals[i].constant) == Z_TYPE(op_array->literals[*pos].constant) &&
 380                                                 info[i].flags == info[*pos].flags) {
 381 
 382                                                 if (info[i].flags & (LITERAL_EX_OBJ|LITERAL_EX_CLASS)) {
 383                                                         efree(key);
 384                                                 }
 385                                                 map[i] = *pos;
 386                                                 zval_dtor(&op_array->literals[i].constant);
 387                                                 n = LITERAL_NUM_RELATED(info[i].flags);
 388                                                 while (n > 1) {
 389                                                         i++;
 390                                                         zval_dtor(&op_array->literals[i].constant);
 391                                                         n--;
 392                                                 }
 393                                         } else {
 394                                                 map[i] = j;
 395                                                 if (info[i].flags & LITERAL_MAY_MERGE) {
 396                                                         zend_hash_quick_add(&hash, key, key_len, h, (void**)&j, sizeof(int), NULL);
 397                                                         if (info[i].flags & (LITERAL_EX_OBJ|LITERAL_EX_CLASS)) {
 398                                                                 efree(key);
 399                                                         }
 400                                                 }
 401                                                 if (i != j) {
 402                                                         op_array->literals[j] = op_array->literals[i];
 403                                                         info[j] = info[i];
 404                                                 }
 405                                                 if (!op_array->literals[j].hash_value) {
 406                                                         if (IS_INTERNED(Z_STRVAL(op_array->literals[j].constant))) {
 407                                                                 op_array->literals[j].hash_value = INTERNED_HASH(Z_STRVAL(op_array->literals[j].constant));
 408                                                         } else {
 409                                                                 op_array->literals[j].hash_value = zend_hash_func(Z_STRVAL(op_array->literals[j].constant), Z_STRLEN(op_array->literals[j].constant)+1);
 410                                                         }
 411                                                 }
 412                                                 if (LITERAL_NUM_SLOTS(info[i].flags)) {
 413                                                         op_array->literals[j].cache_slot = cache_slots;
 414                                                         cache_slots += LITERAL_NUM_SLOTS(info[i].flags);
 415                                                 }
 416                                                 j++;
 417                                                 n = LITERAL_NUM_RELATED(info[i].flags);
 418                                                 while (n > 1) {
 419                                                         i++;
 420                                                         if (i != j) op_array->literals[j] = op_array->literals[i];
 421                                                         if (!op_array->literals[j].hash_value) {
 422                                                                 if (IS_INTERNED(Z_STRVAL(op_array->literals[j].constant))) {
 423                                                                         op_array->literals[j].hash_value = INTERNED_HASH(Z_STRVAL(op_array->literals[j].constant));
 424                                                                 } else {
 425                                                                         op_array->literals[j].hash_value = zend_hash_func(Z_STRVAL(op_array->literals[j].constant), Z_STRLEN(op_array->literals[j].constant)+1);
 426                                                                 }
 427                                                         }
 428                                                         j++;
 429                                                         n--;
 430                                                 }
 431                                         }
 432                                         break;
 433                                 default:
 434                                         /* don't merge other types */
 435                                         map[i] = j;
 436                                         if (i != j) {
 437                                                 op_array->literals[j] = op_array->literals[i];
 438                                                 info[j] = info[i];
 439                                         }
 440                                         j++;
 441                                         break;
 442                         }
 443                 }
 444                 zend_hash_destroy(&hash);
 445                 op_array->last_literal = j;
 446                 op_array->last_cache_slot = cache_slots;
 447 
 448             /* Update opcodes to use new literals table */
 449                 opline = op_array->opcodes;
 450                 end = opline + op_array->last;
 451                 while (opline < end) {
 452                         if (ZEND_OP1_TYPE(opline) == IS_CONST) {
 453                                 opline->op1.constant = map[opline->op1.constant];
 454                         }
 455                         if (ZEND_OP2_TYPE(opline) == IS_CONST) {
 456                                 opline->op2.constant = map[opline->op2.constant];
 457                         }
 458                         opline++;
 459                 }
 460                 efree(map);
 461                 efree(info);
 462 
 463 #if DEBUG_COMPACT_LITERALS
 464                 {
 465                         int i, use_copy;
 466                         fprintf(stderr, "Optimized literlas table size %d\n", op_array->last_literal);
 467 
 468                         for (i = 0; i < op_array->last_literal; i++) {
 469                                 zval zv = op_array->literals[i].constant;
 470                                 zend_make_printable_zval(&op_array->literals[i].constant, &zv, &use_copy);
 471                                 fprintf(stderr, "Literal %d, val (%d):%s\n", i, Z_STRLEN(zv), Z_STRVAL(zv));
 472                                 if (use_copy) {
 473                                         zval_dtor(&zv);
 474                                 }
 475                         }
 476                         fflush(stderr);
 477                 }
 478 #endif
 479         }
 480 }
 481 #endif

/* [<][>][^][v][top][bottom][index][help] */