ssa_integrity.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378
  1. /*
  2. +----------------------------------------------------------------------+
  3. | Zend Engine, SSA validation |
  4. +----------------------------------------------------------------------+
  5. | Copyright (c) The PHP Group |
  6. +----------------------------------------------------------------------+
  7. | This source file is subject to version 3.01 of the PHP license, |
  8. | that is bundled with this package in the file LICENSE, and is |
  9. | available through the world-wide-web at the following url: |
  10. | https://www.php.net/license/3_01.txt |
  11. | If you did not receive a copy of the PHP license and are unable to |
  12. | obtain it through the world-wide-web, please send a note to |
  13. | license@php.net so we can mail you a copy immediately. |
  14. +----------------------------------------------------------------------+
  15. | Authors: Nikita Popov <nikic@php.net> |
  16. +----------------------------------------------------------------------+
  17. */
  18. #include "Optimizer/zend_optimizer_internal.h"
  19. /* The ssa_verify_integrity() function ensures that that certain invariants of the SSA form and
  20. * CFG are upheld and prints messages to stderr if this is not the case. */
  21. static inline bool is_in_use_chain(zend_ssa *ssa, int var, int check) {
  22. int use;
  23. FOREACH_USE(&ssa->vars[var], use) {
  24. if (use == check) {
  25. return 1;
  26. }
  27. } FOREACH_USE_END();
  28. return 0;
  29. }
  30. static inline bool is_in_phi_use_chain(zend_ssa *ssa, int var, zend_ssa_phi *check) {
  31. zend_ssa_phi *phi;
  32. FOREACH_PHI_USE(&ssa->vars[var], phi) {
  33. if (phi == check) {
  34. return 1;
  35. }
  36. } FOREACH_PHI_USE_END();
  37. return 0;
  38. }
  39. static inline bool is_used_by_op(zend_ssa *ssa, int op, int check) {
  40. zend_ssa_op *ssa_op = &ssa->ops[op];
  41. return (ssa_op->op1_use == check)
  42. || (ssa_op->op2_use == check)
  43. || (ssa_op->result_use == check);
  44. }
  45. static inline bool is_defined_by_op(zend_ssa *ssa, int op, int check) {
  46. zend_ssa_op *ssa_op = &ssa->ops[op];
  47. return (ssa_op->op1_def == check)
  48. || (ssa_op->op2_def == check)
  49. || (ssa_op->result_def == check);
  50. }
  51. static inline bool is_in_phi_sources(zend_ssa *ssa, zend_ssa_phi *phi, int check) {
  52. int source;
  53. FOREACH_PHI_SOURCE(phi, source) {
  54. if (source == check) {
  55. return 1;
  56. }
  57. } FOREACH_PHI_SOURCE_END();
  58. return 0;
  59. }
  60. static inline bool is_in_predecessors(zend_cfg *cfg, zend_basic_block *block, int check) {
  61. int i, *predecessors = &cfg->predecessors[block->predecessor_offset];
  62. for (i = 0; i < block->predecessors_count; i++) {
  63. if (predecessors[i] == check) {
  64. return 1;
  65. }
  66. }
  67. return 0;
  68. }
  69. static inline bool is_in_successors(zend_basic_block *block, int check) {
  70. int s;
  71. for (s = 0; s < block->successors_count; s++) {
  72. if (block->successors[s] == check) {
  73. return 1;
  74. }
  75. }
  76. return 0;
  77. }
  78. static inline bool is_var_type(zend_uchar type) {
  79. return (type & (IS_CV|IS_VAR|IS_TMP_VAR)) != 0;
  80. }
  81. #define FAIL(...) do { \
  82. if (status == SUCCESS) { \
  83. fprintf(stderr, "\nIn function %s::%s (%s):\n", \
  84. op_array->scope ? ZSTR_VAL(op_array->scope->name) : "", \
  85. op_array->function_name ? ZSTR_VAL(op_array->function_name) : "{main}", extra); \
  86. } \
  87. fprintf(stderr, __VA_ARGS__); \
  88. status = FAILURE; \
  89. } while (0)
  90. #define VARFMT "%d (%s%s)"
  91. #define VAR(i) \
  92. (i), (ssa->vars[i].var < op_array->last_var ? "CV $" : "TMP"), \
  93. (ssa->vars[i].var < op_array->last_var ? ZSTR_VAL(op_array->vars[ssa->vars[i].var]) : "")
  94. #define INSTRFMT "%d (%s)"
  95. #define INSTR(i) \
  96. (i), (zend_get_opcode_name(op_array->opcodes[i].opcode))
  97. int ssa_verify_integrity(zend_op_array *op_array, zend_ssa *ssa, const char *extra) {
  98. zend_cfg *cfg = &ssa->cfg;
  99. zend_ssa_phi *phi;
  100. int i, status = SUCCESS;
  101. /* Vars */
  102. for (i = 0; i < ssa->vars_count; i++) {
  103. zend_ssa_var *var = &ssa->vars[i];
  104. int use, c;
  105. uint32_t type = ssa->var_info[i].type;
  106. if (var->definition < 0 && !var->definition_phi && i > op_array->last_var) {
  107. if (var->use_chain >= 0) {
  108. FAIL("var " VARFMT " without def has op uses\n", VAR(i));
  109. }
  110. if (var->phi_use_chain) {
  111. FAIL("var " VARFMT " without def has phi uses\n", VAR(i));
  112. }
  113. }
  114. if (var->definition >= 0 && var->definition_phi) {
  115. FAIL("var " VARFMT " has both def and def_phi\n", VAR(i));
  116. }
  117. if (var->definition >= 0) {
  118. if (!is_defined_by_op(ssa, var->definition, i)) {
  119. FAIL("var " VARFMT " not defined by op " INSTRFMT "\n",
  120. VAR(i), INSTR(var->definition));
  121. }
  122. }
  123. if (var->definition_phi) {
  124. if (var->definition_phi->ssa_var != i) {
  125. FAIL("var " VARFMT " not defined by given phi\n", VAR(i));
  126. }
  127. }
  128. c = 0;
  129. FOREACH_USE(var, use) {
  130. if (++c > 10000) {
  131. FAIL("cycle in uses of " VARFMT "\n", VAR(i));
  132. return status;
  133. }
  134. if (!is_used_by_op(ssa, use, i)) {
  135. fprintf(stderr, "var " VARFMT " not in uses of op %d\n", VAR(i), use);
  136. }
  137. } FOREACH_USE_END();
  138. c = 0;
  139. FOREACH_PHI_USE(var, phi) {
  140. if (++c > 10000) {
  141. FAIL("cycle in phi uses of " VARFMT "\n", VAR(i));
  142. return status;
  143. }
  144. if (!is_in_phi_sources(ssa, phi, i)) {
  145. FAIL("var " VARFMT " not in phi sources of %d\n", VAR(i), phi->ssa_var);
  146. }
  147. } FOREACH_PHI_USE_END();
  148. if ((type & MAY_BE_ARRAY_KEY_ANY) && !(type & MAY_BE_ARRAY_OF_ANY)) {
  149. FAIL("var " VARFMT " has array key type but not value type\n", VAR(i));
  150. }
  151. if ((type & MAY_BE_ARRAY_OF_ANY) && !(type & MAY_BE_ARRAY_KEY_ANY)) {
  152. FAIL("var " VARFMT " has array value type but not key type\n", VAR(i));
  153. }
  154. }
  155. /* Instructions */
  156. FOREACH_INSTR_NUM(i) {
  157. zend_ssa_op *ssa_op = &ssa->ops[i];
  158. zend_op *opline = &op_array->opcodes[i];
  159. if (is_var_type(opline->op1_type)) {
  160. if (ssa_op->op1_use < 0 && ssa_op->op1_def < 0) {
  161. FAIL("var op1 of " INSTRFMT " does not use/def an ssa var\n", INSTR(i));
  162. }
  163. } else {
  164. if (ssa_op->op1_use >= 0 || ssa_op->op1_def >= 0) {
  165. FAIL("non-var op1 of " INSTRFMT " uses or defs an ssa var\n", INSTR(i));
  166. }
  167. }
  168. if (is_var_type(opline->op2_type)) {
  169. if (ssa_op->op2_use < 0 && ssa_op->op2_def < 0) {
  170. FAIL("var op2 of " INSTRFMT " does not use/def an ssa var\n", INSTR(i));
  171. }
  172. } else {
  173. if (ssa_op->op2_use >= 0 || ssa_op->op2_def >= 0) {
  174. FAIL("non-var op2 of " INSTRFMT " uses or defs an ssa var\n", INSTR(i));
  175. }
  176. }
  177. if (is_var_type(opline->result_type)) {
  178. if (ssa_op->result_use < 0 && ssa_op->result_def < 0) {
  179. FAIL("var result of " INSTRFMT " does not use/def an ssa var\n", INSTR(i));
  180. }
  181. } else {
  182. if (ssa_op->result_use >= 0 || ssa_op->result_def >= 0) {
  183. FAIL("non-var result of " INSTRFMT " uses or defs an ssa var\n", INSTR(i));
  184. }
  185. }
  186. if (ssa_op->op1_use >= 0) {
  187. if (ssa_op->op1_use >= ssa->vars_count) {
  188. FAIL("op1 use %d out of range\n", ssa_op->op1_use);
  189. }
  190. if (!is_in_use_chain(ssa, ssa_op->op1_use, i)) {
  191. FAIL("op1 use of " VARFMT " in " INSTRFMT " not in use chain\n",
  192. VAR(ssa_op->op1_use), INSTR(i));
  193. }
  194. if (VAR_NUM(opline->op1.var) != ssa->vars[ssa_op->op1_use].var) {
  195. FAIL("op1 use of " VARFMT " does not match op %d of " INSTRFMT "\n",
  196. VAR(ssa_op->op1_use), VAR_NUM(opline->op1.var), INSTR(i));
  197. }
  198. }
  199. if (ssa_op->op2_use >= 0) {
  200. if (ssa_op->op2_use >= ssa->vars_count) {
  201. FAIL("op2 use %d out of range\n", ssa_op->op2_use);
  202. }
  203. if (!is_in_use_chain(ssa, ssa_op->op2_use, i)) {
  204. FAIL("op2 use of " VARFMT " in " INSTRFMT " not in use chain\n",
  205. VAR(ssa_op->op2_use), INSTR(i));
  206. }
  207. if (VAR_NUM(opline->op2.var) != ssa->vars[ssa_op->op2_use].var) {
  208. FAIL("op2 use of " VARFMT " does not match op %d of " INSTRFMT "\n",
  209. VAR(ssa_op->op2_use), VAR_NUM(opline->op2.var), INSTR(i));
  210. }
  211. }
  212. if (ssa_op->result_use >= 0) {
  213. if (ssa_op->result_use >= ssa->vars_count) {
  214. FAIL("result use %d out of range\n", ssa_op->result_use);
  215. }
  216. if (!is_in_use_chain(ssa, ssa_op->result_use, i)) {
  217. FAIL("result use of " VARFMT " in " INSTRFMT " not in use chain\n",
  218. VAR(ssa_op->result_use), INSTR(i));
  219. }
  220. if (VAR_NUM(opline->result.var) != ssa->vars[ssa_op->result_use].var) {
  221. FAIL("result use of " VARFMT " does not match op %d of " INSTRFMT "\n",
  222. VAR(ssa_op->result_use), VAR_NUM(opline->result.var), INSTR(i));
  223. }
  224. }
  225. if (ssa_op->op1_def >= 0) {
  226. if (ssa_op->op1_def >= ssa->vars_count) {
  227. FAIL("op1 def %d out of range\n", ssa_op->op1_def);
  228. }
  229. if (ssa->vars[ssa_op->op1_def].definition != i) {
  230. FAIL("op1 def of " VARFMT " in " INSTRFMT " invalid\n",
  231. VAR(ssa_op->op1_def), INSTR(i));
  232. }
  233. if (VAR_NUM(opline->op1.var) != ssa->vars[ssa_op->op1_def].var) {
  234. FAIL("op1 def of " VARFMT " does not match op %d of " INSTRFMT "\n",
  235. VAR(ssa_op->op1_def), VAR_NUM(opline->op1.var), INSTR(i));
  236. }
  237. }
  238. if (ssa_op->op2_def >= 0) {
  239. if (ssa_op->op2_def >= ssa->vars_count) {
  240. FAIL("op2 def %d out of range\n", ssa_op->op2_def);
  241. }
  242. if (ssa->vars[ssa_op->op2_def].definition != i) {
  243. FAIL("op2 def of " VARFMT " in " INSTRFMT " invalid\n",
  244. VAR(ssa_op->op2_def), INSTR(i));
  245. }
  246. if (VAR_NUM(opline->op2.var) != ssa->vars[ssa_op->op2_def].var) {
  247. FAIL("op2 def of " VARFMT " does not match op %d of " INSTRFMT "\n",
  248. VAR(ssa_op->op2_def), VAR_NUM(opline->op2.var), INSTR(i));
  249. }
  250. }
  251. if (ssa_op->result_def >= 0) {
  252. if (ssa_op->result_def >= ssa->vars_count) {
  253. FAIL("result def %d out of range\n", ssa_op->result_def);
  254. }
  255. if (ssa->vars[ssa_op->result_def].definition != i) {
  256. FAIL("result def of " VARFMT " in " INSTRFMT " invalid\n",
  257. VAR(ssa_op->result_def), INSTR(i));
  258. }
  259. if (VAR_NUM(opline->result.var) != ssa->vars[ssa_op->result_def].var) {
  260. FAIL("result def of " VARFMT " does not match op %d of " INSTRFMT "\n",
  261. VAR(ssa_op->result_def), VAR_NUM(opline->result.var), INSTR(i));
  262. }
  263. }
  264. } FOREACH_INSTR_NUM_END();
  265. /* Phis */
  266. FOREACH_PHI(phi) {
  267. unsigned num_sources = NUM_PHI_SOURCES(phi);
  268. for (i = 0; i < num_sources; i++) {
  269. int source = phi->sources[i];
  270. if (source < 0) {
  271. FAIL(VARFMT " negative source\n", VAR(phi->ssa_var));
  272. }
  273. if (!is_in_phi_use_chain(ssa, source, phi)) {
  274. FAIL(VARFMT " not in phi use chain of %d\n", VAR(phi->ssa_var), source);
  275. }
  276. if (ssa->vars[source].var != ssa->vars[phi->ssa_var].var) {
  277. FAIL(VARFMT " source of phi for " VARFMT "\n", VAR(source), VAR(phi->ssa_var));
  278. }
  279. if (phi->use_chains[i]) {
  280. int j;
  281. for (j = i + 1; j < num_sources; j++) {
  282. if (phi->sources[j] == source && phi->use_chains[j]) {
  283. FAIL("use chain for source " VARFMT " of phi " VARFMT
  284. " at %d despite earlier use\n", VAR(source), VAR(phi->ssa_var), j);
  285. }
  286. }
  287. }
  288. }
  289. if (ssa->vars[phi->ssa_var].definition_phi != phi) {
  290. FAIL(VARFMT " does not define this phi\n", VAR(phi->ssa_var));
  291. }
  292. } FOREACH_PHI_END();
  293. /* Blocks */
  294. for (i = 0; i < cfg->blocks_count; i++) {
  295. zend_basic_block *block = &cfg->blocks[i];
  296. int *predecessors = &cfg->predecessors[block->predecessor_offset];
  297. int s, j;
  298. if (i != 0 && block->start < (block-1)->start + (block-1)->len) {
  299. FAIL("Block %d start %d smaller previous end %d\n",
  300. i, block->start, (block-1)->start + (block-1)->len);
  301. }
  302. if (i != cfg->blocks_count-1 && block->start + block->len > (block+1)->start) {
  303. FAIL("Block %d end %d greater next start %d\n",
  304. i, block->start + block->len, (block+1)->start);
  305. }
  306. for (j = block->start; j < block->start + block->len; j++) {
  307. if (cfg->map[j] != i) {
  308. FAIL("Instr " INSTRFMT " not associated with block %d\n", INSTR(j), i);
  309. }
  310. }
  311. if (!(block->flags & ZEND_BB_REACHABLE)) {
  312. if (ssa->blocks[i].phis) {
  313. FAIL("Unreachable block %d has phis\n", i);
  314. }
  315. continue;
  316. }
  317. for (s = 0; s < block->successors_count; s++) {
  318. zend_basic_block *next_block;
  319. if (block->successors[s] < 0) {
  320. FAIL("Successor number %d of %d negative", s, i);
  321. }
  322. next_block = &cfg->blocks[block->successors[s]];
  323. if (!(next_block->flags & ZEND_BB_REACHABLE)) {
  324. FAIL("Successor %d of %d not reachable\n", block->successors[s], i);
  325. }
  326. if (!is_in_predecessors(cfg, next_block, i)) {
  327. FAIL("Block %d predecessors missing %d\n", block->successors[s], i);
  328. }
  329. }
  330. for (j = 0; j < block->predecessors_count; j++) {
  331. if (predecessors[j] >= 0) {
  332. int k;
  333. zend_basic_block *prev_block = &cfg->blocks[predecessors[j]];
  334. if (!(prev_block->flags & ZEND_BB_REACHABLE)) {
  335. FAIL("Predecessor %d of %d not reachable\n", predecessors[j], i);
  336. }
  337. if (!is_in_successors(prev_block, i)) {
  338. FAIL("Block %d successors missing %d\n", predecessors[j], i);
  339. }
  340. for (k = 0; k < block->predecessors_count; k++) {
  341. if (k != j && predecessors[k] == predecessors[j]) {
  342. FAIL("Block %d has duplicate predecessor %d\n", i, predecessors[j]);
  343. }
  344. }
  345. }
  346. }
  347. }
  348. return status;
  349. }