compact_literals.c 15 KB

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