2014-08-15 08:47:54 +00:00
|
|
|
/*
|
|
|
|
+----------------------------------------------------------------------+
|
|
|
|
| Zend OPcache |
|
|
|
|
+----------------------------------------------------------------------+
|
2016-01-01 17:49:07 +00:00
|
|
|
| Copyright (c) 1998-2016 The PHP Group |
|
2014-08-15 08:47:54 +00:00
|
|
|
+----------------------------------------------------------------------+
|
|
|
|
| This source file is subject to version 3.01 of the PHP license, |
|
|
|
|
| that is bundled with this package in the file LICENSE, and is |
|
|
|
|
| available through the world-wide-web at the following url: |
|
|
|
|
| http://www.php.net/license/3_01.txt |
|
|
|
|
| If you did not receive a copy of the PHP license and are unable to |
|
|
|
|
| obtain it through the world-wide-web, please send a note to |
|
|
|
|
| license@php.net so we can mail you a copy immediately. |
|
|
|
|
+----------------------------------------------------------------------+
|
|
|
|
| Authors: Andi Gutmans <andi@zend.com> |
|
|
|
|
| Zeev Suraski <zeev@zend.com> |
|
|
|
|
| Stanislav Malyshev <stas@zend.com> |
|
|
|
|
| Dmitry Stogov <dmitry@zend.com> |
|
|
|
|
+----------------------------------------------------------------------+
|
|
|
|
*/
|
|
|
|
|
2014-08-28 13:23:12 +00:00
|
|
|
#include "php.h"
|
|
|
|
#include "Optimizer/zend_optimizer.h"
|
|
|
|
#include "Optimizer/zend_optimizer_internal.h"
|
|
|
|
#include "zend_API.h"
|
|
|
|
#include "zend_constants.h"
|
|
|
|
#include "zend_execute.h"
|
|
|
|
#include "zend_vm.h"
|
2015-08-06 12:41:50 +00:00
|
|
|
#include "zend_bitset.h"
|
|
|
|
|
|
|
|
#define GET_AVAILABLE_T() \
|
|
|
|
for (i = 0; i < T; i++) { \
|
|
|
|
if (!zend_bitset_in(taken_T, i)) { \
|
|
|
|
break; \
|
|
|
|
} \
|
|
|
|
} \
|
|
|
|
zend_bitset_incl(taken_T, i); \
|
|
|
|
if (i > max) { \
|
|
|
|
max = i; \
|
2013-02-13 12:26:47 +00:00
|
|
|
}
|
2015-01-03 09:22:58 +00:00
|
|
|
|
2016-01-25 08:00:10 +00:00
|
|
|
void zend_optimize_temporary_variables(zend_op_array *op_array, zend_optimizer_ctx *ctx)
|
2013-02-13 12:26:47 +00:00
|
|
|
{
|
|
|
|
int T = op_array->T;
|
2014-03-28 19:34:49 +00:00
|
|
|
int offset = op_array->last_var;
|
2015-08-06 12:41:50 +00:00
|
|
|
uint32_t bitset_len;
|
|
|
|
zend_bitset taken_T; /* T index in use */
|
2013-02-13 12:26:47 +00:00
|
|
|
zend_op **start_of_T; /* opline where T is first used */
|
2015-08-06 12:41:50 +00:00
|
|
|
zend_bitset valid_T; /* Is the map_T valid */
|
2013-02-13 12:26:47 +00:00
|
|
|
int *map_T; /* Map's the T to its new index */
|
|
|
|
zend_op *opline, *end;
|
|
|
|
int currT;
|
|
|
|
int i;
|
|
|
|
int max = -1;
|
|
|
|
int var_to_free = -1;
|
2014-06-18 13:09:37 +00:00
|
|
|
void *checkpoint = zend_arena_checkpoint(ctx->arena);
|
2013-02-13 12:26:47 +00:00
|
|
|
|
2015-08-06 12:41:50 +00:00
|
|
|
bitset_len = zend_bitset_len(T);
|
|
|
|
taken_T = (zend_bitset) zend_arena_alloc(&ctx->arena, bitset_len * ZEND_BITSET_ELM_SIZE);
|
2014-06-18 13:09:37 +00:00
|
|
|
start_of_T = (zend_op **) zend_arena_alloc(&ctx->arena, T * sizeof(zend_op *));
|
2015-08-06 12:41:50 +00:00
|
|
|
valid_T = (zend_bitset) zend_arena_alloc(&ctx->arena, bitset_len * ZEND_BITSET_ELM_SIZE);
|
2014-06-18 13:09:37 +00:00
|
|
|
map_T = (int *) zend_arena_alloc(&ctx->arena, T * sizeof(int));
|
2013-02-13 12:26:47 +00:00
|
|
|
|
|
|
|
end = op_array->opcodes;
|
2013-02-25 07:29:54 +00:00
|
|
|
opline = &op_array->opcodes[op_array->last - 1];
|
2013-02-13 12:26:47 +00:00
|
|
|
|
|
|
|
/* Find T definition points */
|
|
|
|
while (opline >= end) {
|
|
|
|
if (ZEND_RESULT_TYPE(opline) & (IS_VAR | IS_TMP_VAR)) {
|
2014-03-28 19:34:49 +00:00
|
|
|
start_of_T[VAR_NUM(ZEND_RESULT(opline).var) - offset] = opline;
|
2013-02-13 12:26:47 +00:00
|
|
|
}
|
|
|
|
opline--;
|
|
|
|
}
|
|
|
|
|
2015-08-06 12:41:50 +00:00
|
|
|
zend_bitset_clear(valid_T, bitset_len);
|
|
|
|
zend_bitset_clear(taken_T, bitset_len);
|
2013-02-13 12:26:47 +00:00
|
|
|
|
|
|
|
end = op_array->opcodes;
|
2013-02-25 07:29:54 +00:00
|
|
|
opline = &op_array->opcodes[op_array->last - 1];
|
2013-02-13 12:26:47 +00:00
|
|
|
|
|
|
|
while (opline >= end) {
|
2014-08-15 08:40:07 +00:00
|
|
|
if ((ZEND_OP1_TYPE(opline) & (IS_VAR | IS_TMP_VAR))) {
|
2015-02-12 10:57:12 +00:00
|
|
|
currT = VAR_NUM(ZEND_OP1(opline).var) - offset;
|
2015-03-24 19:47:21 +00:00
|
|
|
if (opline->opcode == ZEND_ROPE_END) {
|
|
|
|
int num = (((opline->extended_value + 1) * sizeof(zend_string*)) + (sizeof(zval) - 1)) / sizeof(zval);
|
|
|
|
int var;
|
|
|
|
|
|
|
|
var = max;
|
2015-08-06 12:41:50 +00:00
|
|
|
while (var >= 0 && !zend_bitset_in(taken_T, var)) {
|
2015-03-24 19:47:21 +00:00
|
|
|
var--;
|
|
|
|
}
|
|
|
|
max = MAX(max, var + num);
|
|
|
|
var = var + 1;
|
|
|
|
map_T[currT] = var;
|
2015-08-06 12:41:50 +00:00
|
|
|
zend_bitset_incl(valid_T, currT);
|
|
|
|
zend_bitset_incl(taken_T, var);
|
2015-03-24 19:47:21 +00:00
|
|
|
ZEND_OP1(opline).var = NUM_VAR(var + offset);
|
|
|
|
while (num > 1) {
|
|
|
|
num--;
|
2015-08-06 12:41:50 +00:00
|
|
|
zend_bitset_incl(taken_T, var + num);
|
2015-03-24 19:47:21 +00:00
|
|
|
}
|
|
|
|
} else {
|
2015-08-06 12:41:50 +00:00
|
|
|
if (!zend_bitset_in(valid_T, currT)) {
|
2015-08-10 13:38:43 +00:00
|
|
|
int use_new_var = 0;
|
|
|
|
|
|
|
|
/* Code in "finally" blocks may modify temorary variables.
|
|
|
|
* We allocate new temporaries for values that need to
|
|
|
|
* relive FAST_CALLs.
|
|
|
|
*/
|
|
|
|
if ((op_array->fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK) &&
|
|
|
|
(opline->opcode == ZEND_RETURN ||
|
|
|
|
opline->opcode == ZEND_RETURN_BY_REF ||
|
|
|
|
opline->opcode == ZEND_FREE ||
|
|
|
|
opline->opcode == ZEND_FE_FREE)) {
|
|
|
|
zend_op *curr = opline;
|
|
|
|
|
|
|
|
while (--curr >= end) {
|
|
|
|
if (curr->opcode == ZEND_FAST_CALL) {
|
|
|
|
use_new_var = 1;
|
|
|
|
break;
|
|
|
|
} else if (curr->opcode != ZEND_FREE &&
|
|
|
|
curr->opcode != ZEND_FE_FREE &&
|
|
|
|
curr->opcode != ZEND_VERIFY_RETURN_TYPE &&
|
|
|
|
curr->opcode != ZEND_DISCARD_EXCEPTION) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (use_new_var) {
|
|
|
|
i = ++max;
|
|
|
|
zend_bitset_incl(taken_T, i);
|
|
|
|
} else {
|
|
|
|
GET_AVAILABLE_T();
|
|
|
|
}
|
2015-03-24 19:47:21 +00:00
|
|
|
map_T[currT] = i;
|
2015-08-06 12:41:50 +00:00
|
|
|
zend_bitset_incl(valid_T, currT);
|
2015-03-24 19:47:21 +00:00
|
|
|
}
|
|
|
|
ZEND_OP1(opline).var = NUM_VAR(map_T[currT] + offset);
|
2013-02-13 12:26:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-08-15 08:40:07 +00:00
|
|
|
if ((ZEND_OP2_TYPE(opline) & (IS_VAR | IS_TMP_VAR))) {
|
2014-03-28 19:34:49 +00:00
|
|
|
currT = VAR_NUM(ZEND_OP2(opline).var) - offset;
|
2015-08-06 12:41:50 +00:00
|
|
|
if (!zend_bitset_in(valid_T, currT)) {
|
2013-02-13 12:26:47 +00:00
|
|
|
GET_AVAILABLE_T();
|
|
|
|
map_T[currT] = i;
|
2015-08-06 12:41:50 +00:00
|
|
|
zend_bitset_incl(valid_T, currT);
|
2013-02-13 12:26:47 +00:00
|
|
|
}
|
2014-03-28 19:34:49 +00:00
|
|
|
ZEND_OP2(opline).var = NUM_VAR(map_T[currT] + offset);
|
2013-02-13 12:26:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (ZEND_RESULT_TYPE(opline) & (IS_VAR | IS_TMP_VAR)) {
|
2014-08-15 08:40:07 +00:00
|
|
|
currT = VAR_NUM(ZEND_RESULT(opline).var) - offset;
|
2015-08-06 12:41:50 +00:00
|
|
|
if (zend_bitset_in(valid_T, currT)) {
|
2014-08-15 08:40:07 +00:00
|
|
|
if (start_of_T[currT] == opline) {
|
2015-07-08 09:44:54 +00:00
|
|
|
/* ZEND_FAST_CALL can not share temporary var with others
|
|
|
|
* since the fast_var could also be set by ZEND_HANDLE_EXCEPTION
|
|
|
|
* which could be ahead of it */
|
2015-07-08 09:15:09 +00:00
|
|
|
if (opline->opcode != ZEND_FAST_CALL) {
|
2015-08-06 12:41:50 +00:00
|
|
|
zend_bitset_excl(taken_T, map_T[currT]);
|
2015-07-08 09:15:09 +00:00
|
|
|
}
|
2014-08-15 08:40:07 +00:00
|
|
|
}
|
|
|
|
ZEND_RESULT(opline).var = NUM_VAR(map_T[currT] + offset);
|
2015-03-24 19:47:21 +00:00
|
|
|
if (opline->opcode == ZEND_ROPE_INIT) {
|
|
|
|
if (start_of_T[currT] == opline) {
|
|
|
|
uint32_t num = ((opline->extended_value * sizeof(zend_string*)) + (sizeof(zval) - 1)) / sizeof(zval);
|
|
|
|
while (num > 1) {
|
|
|
|
num--;
|
2015-08-06 12:41:50 +00:00
|
|
|
zend_bitset_excl(taken_T, map_T[currT]+num);
|
2015-03-24 19:47:21 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-02-05 14:23:23 +00:00
|
|
|
} else {
|
|
|
|
/* Code which gets here is using a wrongly built opcode such as RECV() */
|
2014-08-15 08:40:07 +00:00
|
|
|
GET_AVAILABLE_T();
|
2016-02-05 14:23:23 +00:00
|
|
|
map_T[currT] = i;
|
|
|
|
zend_bitset_incl(valid_T, currT);
|
2014-08-15 08:40:07 +00:00
|
|
|
ZEND_RESULT(opline).var = NUM_VAR(i + offset);
|
2013-02-13 12:26:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (var_to_free >= 0) {
|
2015-08-06 12:41:50 +00:00
|
|
|
zend_bitset_excl(taken_T, var_to_free);
|
2013-02-13 12:26:47 +00:00
|
|
|
var_to_free = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
opline--;
|
|
|
|
}
|
|
|
|
|
2015-11-13 12:35:07 +00:00
|
|
|
if (op_array->live_range) {
|
|
|
|
for (i = 0; i < op_array->last_live_range; i++) {
|
|
|
|
op_array->live_range[i].var =
|
|
|
|
NUM_VAR(map_T[VAR_NUM(op_array->live_range[i].var & ~ZEND_LIVE_MASK) - offset] + offset) |
|
|
|
|
(op_array->live_range[i].var & ZEND_LIVE_MASK);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-18 13:09:37 +00:00
|
|
|
zend_arena_release(&ctx->arena, checkpoint);
|
2013-02-22 06:56:05 +00:00
|
|
|
op_array->T = max + 1;
|
2013-02-13 12:26:47 +00:00
|
|
|
}
|