}
}
+/* Get the normal op corresponding to a compound assignment op */
+static inline zend_uchar get_compound_assign_op(zend_uchar opcode) {
+ switch (opcode) {
+ case ZEND_ASSIGN_ADD: return ZEND_ADD;
+ case ZEND_ASSIGN_SUB: return ZEND_SUB;
+ case ZEND_ASSIGN_MUL: return ZEND_MUL;
+ case ZEND_ASSIGN_DIV: return ZEND_DIV;
+ case ZEND_ASSIGN_MOD: return ZEND_MOD;
+ case ZEND_ASSIGN_SL: return ZEND_SL;
+ case ZEND_ASSIGN_SR: return ZEND_SR;
+ case ZEND_ASSIGN_CONCAT: return ZEND_CONCAT;
+ case ZEND_ASSIGN_BW_OR: return ZEND_BW_OR;
+ case ZEND_ASSIGN_BW_AND: return ZEND_BW_AND;
+ case ZEND_ASSIGN_BW_XOR: return ZEND_BW_XOR;
+ case ZEND_ASSIGN_POW: return ZEND_POW;
+ EMPTY_SWITCH_DEFAULT_CASE()
+ }
+}
+
+static int zend_inference_calc_binary_op_range(
+ const zend_op_array *op_array, zend_ssa *ssa,
+ zend_op *opline, zend_ssa_op *ssa_op, zend_uchar opcode, zend_ssa_range *tmp) {
+ zend_long op1_min, op2_min, op1_max, op2_max, t1, t2, t3, t4;
+
+ switch (opcode) {
+ case ZEND_ADD:
+ if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
+ op1_min = OP1_MIN_RANGE();
+ op2_min = OP2_MIN_RANGE();
+ op1_max = OP1_MAX_RANGE();
+ op2_max = OP2_MAX_RANGE();
+ tmp->min = op1_min + op2_min;
+ tmp->max = op1_max + op2_max;
+ if (OP1_RANGE_UNDERFLOW() ||
+ OP2_RANGE_UNDERFLOW() ||
+ (op1_min < 0 && op2_min < 0 && tmp->min >= 0)) {
+ tmp->underflow = 1;
+ tmp->min = ZEND_LONG_MIN;
+ }
+ if (OP1_RANGE_OVERFLOW() ||
+ OP2_RANGE_OVERFLOW() ||
+ (op1_max > 0 && op2_max > 0 && tmp->max <= 0)) {
+ tmp->overflow = 1;
+ tmp->max = ZEND_LONG_MAX;
+ }
+ return 1;
+ }
+ break;
+ case ZEND_SUB:
+ if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
+ op1_min = OP1_MIN_RANGE();
+ op2_min = OP2_MIN_RANGE();
+ op1_max = OP1_MAX_RANGE();
+ op2_max = OP2_MAX_RANGE();
+ tmp->min = op1_min - op2_max;
+ tmp->max = op1_max - op2_min;
+ if (OP1_RANGE_UNDERFLOW() ||
+ OP2_RANGE_OVERFLOW() ||
+ (op1_min < 0 && op2_max > 0 && tmp->min >= 0)) {
+ tmp->underflow = 1;
+ tmp->min = ZEND_LONG_MIN;
+ }
+ if (OP1_RANGE_OVERFLOW() ||
+ OP2_RANGE_UNDERFLOW() ||
+ (op1_max > 0 && op2_min < 0 && tmp->max <= 0)) {
+ tmp->overflow = 1;
+ tmp->max = ZEND_LONG_MAX;
+ }
+ return 1;
+ }
+ break;
+ case ZEND_MUL:
+ if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
+ op1_min = OP1_MIN_RANGE();
+ op2_min = OP2_MIN_RANGE();
+ op1_max = OP1_MAX_RANGE();
+ op2_max = OP2_MAX_RANGE();
+ t1 = op1_min * op2_min;
+ t2 = op1_min * op2_max;
+ t3 = op1_max * op2_min;
+ t4 = op1_max * op2_max;
+ // FIXME: more careful overflow checks?
+ if (OP1_RANGE_UNDERFLOW() ||
+ OP2_RANGE_UNDERFLOW() ||
+ OP1_RANGE_OVERFLOW() ||
+ OP2_RANGE_OVERFLOW() ||
+ (double)t1 != (double)op1_min * (double)op2_min ||
+ (double)t2 != (double)op1_min * (double)op2_max ||
+ (double)t3 != (double)op1_max * (double)op2_min ||
+ (double)t4 != (double)op1_max * (double)op2_max) {
+ tmp->underflow = 1;
+ tmp->overflow = 1;
+ tmp->min = ZEND_LONG_MIN;
+ tmp->max = ZEND_LONG_MAX;
+ } else {
+ tmp->min = MIN(MIN(t1, t2), MIN(t3, t4));
+ tmp->max = MAX(MAX(t1, t2), MAX(t3, t4));
+ }
+ return 1;
+ }
+ break;
+ case ZEND_DIV:
+ if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
+ op1_min = OP1_MIN_RANGE();
+ op2_min = OP2_MIN_RANGE();
+ op1_max = OP1_MAX_RANGE();
+ op2_max = OP2_MAX_RANGE();
+ if (op2_min <= 0 && op2_max >= 0) {
+ break;
+ }
+ t1 = op1_min / op2_min;
+ t2 = op1_min / op2_max;
+ t3 = op1_max / op2_min;
+ t4 = op1_max / op2_max;
+ // FIXME: more careful overflow checks?
+ if (OP1_RANGE_UNDERFLOW() ||
+ OP2_RANGE_UNDERFLOW() ||
+ OP1_RANGE_OVERFLOW() ||
+ OP2_RANGE_OVERFLOW() ||
+ t1 != (zend_long)((double)op1_min / (double)op2_min) ||
+ t2 != (zend_long)((double)op1_min / (double)op2_max) ||
+ t3 != (zend_long)((double)op1_max / (double)op2_min) ||
+ t4 != (zend_long)((double)op1_max / (double)op2_max)) {
+ tmp->underflow = 1;
+ tmp->overflow = 1;
+ tmp->min = ZEND_LONG_MIN;
+ tmp->max = ZEND_LONG_MAX;
+ } else {
+ tmp->min = MIN(MIN(t1, t2), MIN(t3, t4));
+ tmp->max = MAX(MAX(t1, t2), MAX(t3, t4));
+ }
+ return 1;
+ }
+ break;
+ case ZEND_MOD:
+ if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
+ if (OP1_RANGE_UNDERFLOW() ||
+ OP2_RANGE_UNDERFLOW() ||
+ OP1_RANGE_OVERFLOW() ||
+ OP2_RANGE_OVERFLOW()) {
+ tmp->min = ZEND_LONG_MIN;
+ tmp->max = ZEND_LONG_MAX;
+ } else {
+ op1_min = OP1_MIN_RANGE();
+ op2_min = OP2_MIN_RANGE();
+ op1_max = OP1_MAX_RANGE();
+ op2_max = OP2_MAX_RANGE();
+ if (op2_min == 0 || op2_max == 0) {
+ /* avoid division by zero */
+ break;
+ }
+ t1 = (op2_min == -1) ? 0 : (op1_min % op2_min);
+ t2 = (op2_max == -1) ? 0 : (op1_min % op2_max);
+ t3 = (op2_min == -1) ? 0 : (op1_max % op2_min);
+ t4 = (op2_max == -1) ? 0 : (op1_max % op2_max);
+ tmp->min = MIN(MIN(t1, t2), MIN(t3, t4));
+ tmp->max = MAX(MAX(t1, t2), MAX(t3, t4));
+ }
+ return 1;
+ }
+ break;
+ case ZEND_SL:
+ if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
+ if (OP1_RANGE_UNDERFLOW() ||
+ OP2_RANGE_UNDERFLOW() ||
+ OP1_RANGE_OVERFLOW() ||
+ OP2_RANGE_OVERFLOW()) {
+ tmp->min = ZEND_LONG_MIN;
+ tmp->max = ZEND_LONG_MAX;
+ } else {
+ op1_min = OP1_MIN_RANGE();
+ op2_min = OP2_MIN_RANGE();
+ op1_max = OP1_MAX_RANGE();
+ op2_max = OP2_MAX_RANGE();
+ t1 = op1_min << op2_min;
+ t2 = op1_min << op2_max;
+ t3 = op1_max << op2_min;
+ t4 = op1_max << op2_max;
+ tmp->min = MIN(MIN(t1, t2), MIN(t3, t4));
+ tmp->max = MAX(MAX(t1, t2), MAX(t3, t4));
+ }
+ return 1;
+ }
+ break;
+ case ZEND_SR:
+ if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
+ if (OP1_RANGE_UNDERFLOW() ||
+ OP2_RANGE_UNDERFLOW() ||
+ OP1_RANGE_OVERFLOW() ||
+ OP2_RANGE_OVERFLOW()) {
+ tmp->min = ZEND_LONG_MIN;
+ tmp->max = ZEND_LONG_MAX;
+ } else {
+ op1_min = OP1_MIN_RANGE();
+ op2_min = OP2_MIN_RANGE();
+ op1_max = OP1_MAX_RANGE();
+ op2_max = OP2_MAX_RANGE();
+ t1 = op1_min >> op2_min;
+ t2 = op1_min >> op2_max;
+ t3 = op1_max >> op2_min;
+ t4 = op1_max >> op2_max;
+ tmp->min = MIN(MIN(t1, t2), MIN(t3, t4));
+ tmp->max = MAX(MAX(t1, t2), MAX(t3, t4));
+ }
+ return 1;
+ }
+ break;
+ case ZEND_BW_OR:
+ if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
+ if (OP1_RANGE_UNDERFLOW() ||
+ OP2_RANGE_UNDERFLOW() ||
+ OP1_RANGE_OVERFLOW() ||
+ OP2_RANGE_OVERFLOW()) {
+ tmp->min = ZEND_LONG_MIN;
+ tmp->max = ZEND_LONG_MAX;
+ } else {
+ op1_min = OP1_MIN_RANGE();
+ op2_min = OP2_MIN_RANGE();
+ op1_max = OP1_MAX_RANGE();
+ op2_max = OP2_MAX_RANGE();
+ zend_ssa_range_or(op1_min, op1_max, op2_min, op2_max, tmp);
+ }
+ return 1;
+ }
+ break;
+ case ZEND_BW_AND:
+ if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
+ if (OP1_RANGE_UNDERFLOW() ||
+ OP2_RANGE_UNDERFLOW() ||
+ OP1_RANGE_OVERFLOW() ||
+ OP2_RANGE_OVERFLOW()) {
+ tmp->min = ZEND_LONG_MIN;
+ tmp->max = ZEND_LONG_MAX;
+ } else {
+ op1_min = OP1_MIN_RANGE();
+ op2_min = OP2_MIN_RANGE();
+ op1_max = OP1_MAX_RANGE();
+ op2_max = OP2_MAX_RANGE();
+ zend_ssa_range_and(op1_min, op1_max, op2_min, op2_max, tmp);
+ }
+ return 1;
+ }
+ break;
+ case ZEND_BW_XOR:
+ // TODO
+ break;
+ EMPTY_SWITCH_DEFAULT_CASE()
+ }
+ return 0;
+}
+
int zend_inference_calc_range(const zend_op_array *op_array, zend_ssa *ssa, int var, int widening, int narrowing, zend_ssa_range *tmp)
{
uint32_t line;
zend_op *opline;
- zend_long op1_min, op2_min, op1_max, op2_max, t1, t2, t3, t4;
+ zend_long op1_min, op2_min, op1_max, op2_max;
if (ssa->vars[var].definition_phi) {
zend_ssa_phi *p = ssa->vars[var].definition_phi;
tmp->overflow = 0;
switch (opline->opcode) {
case ZEND_ADD:
- if (ssa->ops[line].result_def == var) {
- if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
- op1_min = OP1_MIN_RANGE();
- op2_min = OP2_MIN_RANGE();
- op1_max = OP1_MAX_RANGE();
- op2_max = OP2_MAX_RANGE();
- tmp->min = op1_min + op2_min;
- tmp->max = op1_max + op2_max;
- if (OP1_RANGE_UNDERFLOW() ||
- OP2_RANGE_UNDERFLOW() ||
- (op1_min < 0 && op2_min < 0 && tmp->min >= 0)) {
- tmp->underflow = 1;
- tmp->min = ZEND_LONG_MIN;
- }
- if (OP1_RANGE_OVERFLOW() ||
- OP2_RANGE_OVERFLOW() ||
- (op1_max > 0 && op2_max > 0 && tmp->max <= 0)) {
- tmp->overflow = 1;
- tmp->max = ZEND_LONG_MAX;
- }
- return 1;
- }
- }
- break;
case ZEND_SUB:
- if (ssa->ops[line].result_def == var) {
- if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
- op1_min = OP1_MIN_RANGE();
- op2_min = OP2_MIN_RANGE();
- op1_max = OP1_MAX_RANGE();
- op2_max = OP2_MAX_RANGE();
- tmp->min = op1_min - op2_max;
- tmp->max = op1_max - op2_min;
- if (OP1_RANGE_UNDERFLOW() ||
- OP2_RANGE_OVERFLOW() ||
- (op1_min < 0 && op2_max > 0 && tmp->min >= 0)) {
- tmp->underflow = 1;
- tmp->min = ZEND_LONG_MIN;
- }
- if (OP1_RANGE_OVERFLOW() ||
- OP2_RANGE_UNDERFLOW() ||
- (op1_max > 0 && op2_min < 0 && tmp->max <= 0)) {
- tmp->overflow = 1;
- tmp->max = ZEND_LONG_MAX;
- }
- return 1;
- }
- }
- break;
case ZEND_MUL:
- if (ssa->ops[line].result_def == var) {
- if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
- op1_min = OP1_MIN_RANGE();
- op2_min = OP2_MIN_RANGE();
- op1_max = OP1_MAX_RANGE();
- op2_max = OP2_MAX_RANGE();
- t1 = op1_min * op2_min;
- t2 = op1_min * op2_max;
- t3 = op1_max * op2_min;
- t4 = op1_max * op2_max;
- // FIXME: more careful overflow checks?
- if (OP1_RANGE_UNDERFLOW() ||
- OP2_RANGE_UNDERFLOW() ||
- OP1_RANGE_OVERFLOW() ||
- OP2_RANGE_OVERFLOW() ||
- (double)t1 != (double)op1_min * (double)op2_min ||
- (double)t2 != (double)op1_min * (double)op2_max ||
- (double)t3 != (double)op1_max * (double)op2_min ||
- (double)t4 != (double)op1_max * (double)op2_max) {
- tmp->underflow = 1;
- tmp->overflow = 1;
- tmp->min = ZEND_LONG_MIN;
- tmp->max = ZEND_LONG_MAX;
- } else {
- tmp->min = MIN(MIN(t1, t2), MIN(t3, t4));
- tmp->max = MAX(MAX(t1, t2), MAX(t3, t4));
- }
- return 1;
- }
- }
- break;
case ZEND_DIV:
- if (ssa->ops[line].result_def == var) {
- if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
- op1_min = OP1_MIN_RANGE();
- op2_min = OP2_MIN_RANGE();
- op1_max = OP1_MAX_RANGE();
- op2_max = OP2_MAX_RANGE();
- if (op2_min <= 0 && op2_max >= 0) {
- break;
- }
- t1 = op1_min / op2_min;
- t2 = op1_min / op2_max;
- t3 = op1_max / op2_min;
- t4 = op1_max / op2_max;
- // FIXME: more careful overflow checks?
- if (OP1_RANGE_UNDERFLOW() ||
- OP2_RANGE_UNDERFLOW() ||
- OP1_RANGE_OVERFLOW() ||
- OP2_RANGE_OVERFLOW() ||
- t1 != (zend_long)((double)op1_min / (double)op2_min) ||
- t2 != (zend_long)((double)op1_min / (double)op2_max) ||
- t3 != (zend_long)((double)op1_max / (double)op2_min) ||
- t4 != (zend_long)((double)op1_max / (double)op2_max)) {
- tmp->underflow = 1;
- tmp->overflow = 1;
- tmp->min = ZEND_LONG_MIN;
- tmp->max = ZEND_LONG_MAX;
- } else {
- tmp->min = MIN(MIN(t1, t2), MIN(t3, t4));
- tmp->max = MAX(MAX(t1, t2), MAX(t3, t4));
- }
- return 1;
- }
- }
- break;
case ZEND_MOD:
- if (ssa->ops[line].result_def == var) {
- if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
- if (OP1_RANGE_UNDERFLOW() ||
- OP2_RANGE_UNDERFLOW() ||
- OP1_RANGE_OVERFLOW() ||
- OP2_RANGE_OVERFLOW()) {
- tmp->min = ZEND_LONG_MIN;
- tmp->max = ZEND_LONG_MAX;
- } else {
- op1_min = OP1_MIN_RANGE();
- op2_min = OP2_MIN_RANGE();
- op1_max = OP1_MAX_RANGE();
- op2_max = OP2_MAX_RANGE();
- if (op2_min == 0 || op2_max == 0) {
- /* avoid division by zero */
- break;
- }
- t1 = (op2_min == -1) ? 0 : (op1_min % op2_min);
- t2 = (op2_max == -1) ? 0 : (op1_min % op2_max);
- t3 = (op2_min == -1) ? 0 : (op1_max % op2_min);
- t4 = (op2_max == -1) ? 0 : (op1_max % op2_max);
- tmp->min = MIN(MIN(t1, t2), MIN(t3, t4));
- tmp->max = MAX(MAX(t1, t2), MAX(t3, t4));
- }
- }
- }
- break;
case ZEND_SL:
- if (ssa->ops[line].result_def == var) {
- if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
- if (OP1_RANGE_UNDERFLOW() ||
- OP2_RANGE_UNDERFLOW() ||
- OP1_RANGE_OVERFLOW() ||
- OP2_RANGE_OVERFLOW()) {
- tmp->min = ZEND_LONG_MIN;
- tmp->max = ZEND_LONG_MAX;
- } else {
- op1_min = OP1_MIN_RANGE();
- op2_min = OP2_MIN_RANGE();
- op1_max = OP1_MAX_RANGE();
- op2_max = OP2_MAX_RANGE();
- t1 = op1_min << op2_min;
- t2 = op1_min << op2_max;
- t3 = op1_max << op2_min;
- t4 = op1_max << op2_max;
- tmp->min = MIN(MIN(t1, t2), MIN(t3, t4));
- tmp->max = MAX(MAX(t1, t2), MAX(t3, t4));
- }
- return 1;
- }
- }
- break;
case ZEND_SR:
- if (ssa->ops[line].result_def == var) {
- if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
- if (OP1_RANGE_UNDERFLOW() ||
- OP2_RANGE_UNDERFLOW() ||
- OP1_RANGE_OVERFLOW() ||
- OP2_RANGE_OVERFLOW()) {
- tmp->min = ZEND_LONG_MIN;
- tmp->max = ZEND_LONG_MAX;
- } else {
- op1_min = OP1_MIN_RANGE();
- op2_min = OP2_MIN_RANGE();
- op1_max = OP1_MAX_RANGE();
- op2_max = OP2_MAX_RANGE();
- t1 = op1_min >> op2_min;
- t2 = op1_min >> op2_max;
- t3 = op1_max >> op2_min;
- t4 = op1_max >> op2_max;
- tmp->min = MIN(MIN(t1, t2), MIN(t3, t4));
- tmp->max = MAX(MAX(t1, t2), MAX(t3, t4));
- }
- return 1;
- }
- }
- break;
case ZEND_BW_OR:
- if (ssa->ops[line].result_def == var) {
- if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
- if (OP1_RANGE_UNDERFLOW() ||
- OP2_RANGE_UNDERFLOW() ||
- OP1_RANGE_OVERFLOW() ||
- OP2_RANGE_OVERFLOW()) {
- tmp->min = ZEND_LONG_MIN;
- tmp->max = ZEND_LONG_MAX;
- } else {
- op1_min = OP1_MIN_RANGE();
- op2_min = OP2_MIN_RANGE();
- op1_max = OP1_MAX_RANGE();
- op2_max = OP2_MAX_RANGE();
- zend_ssa_range_or(op1_min, op1_max, op2_min, op2_max, tmp);
- }
- return 1;
- }
- }
- break;
case ZEND_BW_AND:
+ case ZEND_BW_XOR:
if (ssa->ops[line].result_def == var) {
- if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
- if (OP1_RANGE_UNDERFLOW() ||
- OP2_RANGE_UNDERFLOW() ||
- OP1_RANGE_OVERFLOW() ||
- OP2_RANGE_OVERFLOW()) {
- tmp->min = ZEND_LONG_MIN;
- tmp->max = ZEND_LONG_MAX;
- } else {
- op1_min = OP1_MIN_RANGE();
- op2_min = OP2_MIN_RANGE();
- op1_max = OP1_MAX_RANGE();
- op2_max = OP2_MAX_RANGE();
- zend_ssa_range_and(op1_min, op1_max, op2_min, op2_max, tmp);
- }
- return 1;
- }
+ return zend_inference_calc_binary_op_range(
+ op_array, ssa, opline, &ssa->ops[line], opline->opcode, tmp);
}
break;
-// case ZEND_BW_XOR:
+
case ZEND_BW_NOT:
if (ssa->ops[line].result_def == var) {
if (OP1_HAS_RANGE()) {
}
break;
case ZEND_ASSIGN_ADD:
- if (opline->extended_value == 0) {
- if (ssa->ops[line].op1_def == var || ssa->ops[line].result_def == var) {
- if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
- op1_min = OP1_MIN_RANGE();
- op2_min = OP2_MIN_RANGE();
- op1_max = OP1_MAX_RANGE();
- op2_max = OP2_MAX_RANGE();
- tmp->min = op1_min + op2_min;
- tmp->max = op1_max + op2_max;
- if (OP1_RANGE_UNDERFLOW() ||
- OP2_RANGE_UNDERFLOW() ||
- (op1_min < 0 && op2_min < 0 && tmp->min >= 0)) {
- tmp->underflow = 1;
- tmp->min = ZEND_LONG_MIN;
- }
- if (OP1_RANGE_OVERFLOW() ||
- OP2_RANGE_OVERFLOW() ||
- (op1_max > 0 && op2_max > 0 && tmp->max <= 0)) {
- tmp->overflow = 1;
- tmp->max = ZEND_LONG_MAX;
- }
- return 1;
- }
- }
- } else if ((opline+1)->opcode == ZEND_OP_DATA) {
- if (ssa->ops[line+1].op1_def == var) {
- opline++;
- if (OP1_HAS_RANGE()) {
- tmp->min = OP1_MIN_RANGE();
- tmp->max = OP1_MAX_RANGE();
- tmp->underflow = OP1_RANGE_UNDERFLOW();
- tmp->overflow = OP1_RANGE_OVERFLOW();
- return 1;
- }
- }
- }
- break;
case ZEND_ASSIGN_SUB:
- if (opline->extended_value == 0) {
- if (ssa->ops[line].op1_def == var || ssa->ops[line].result_def == var) {
- if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
- op1_min = OP1_MIN_RANGE();
- op2_min = OP2_MIN_RANGE();
- op1_max = OP1_MAX_RANGE();
- op2_max = OP2_MAX_RANGE();
- tmp->min = op1_min - op2_max;
- tmp->max = op1_max - op2_min;
- if (OP1_RANGE_UNDERFLOW() ||
- OP2_RANGE_OVERFLOW() ||
- (op1_min < 0 && op2_max > 0 && tmp->min >= 0)) {
- tmp->underflow = 1;
- tmp->min = ZEND_LONG_MIN;
- }
- if (OP1_RANGE_OVERFLOW() ||
- OP2_RANGE_UNDERFLOW() ||
- (op1_max > 0 && op2_min < 0 && tmp->max <= 0)) {
- tmp->overflow = 1;
- tmp->max = ZEND_LONG_MAX;
- }
- return 1;
- }
- }
- } else if ((opline+1)->opcode == ZEND_OP_DATA) {
- if (ssa->ops[line+1].op1_def == var) {
- opline++;
- if (OP1_HAS_RANGE()) {
- tmp->min = OP1_MIN_RANGE();
- tmp->max = OP1_MAX_RANGE();
- tmp->underflow = OP1_RANGE_UNDERFLOW();
- tmp->overflow = OP1_RANGE_OVERFLOW();
- return 1;
- }
- }
- }
- break;
case ZEND_ASSIGN_MUL:
- if (opline->extended_value == 0) {
- if (ssa->ops[line].op1_def == var || ssa->ops[line].result_def == var) {
- if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
- op1_min = OP1_MIN_RANGE();
- op2_min = OP2_MIN_RANGE();
- op1_max = OP1_MAX_RANGE();
- op2_max = OP2_MAX_RANGE();
- t1 = op1_min * op2_min;
- t2 = op1_min * op2_max;
- t3 = op1_max * op2_min;
- t4 = op1_max * op2_max;
- // FIXME: more careful overflow checks?
- if (OP1_RANGE_UNDERFLOW() ||
- OP2_RANGE_UNDERFLOW() ||
- OP1_RANGE_OVERFLOW() ||
- OP2_RANGE_OVERFLOW() ||
- (double)t1 != (double)op1_min * (double)op2_min ||
- (double)t2 != (double)op1_min * (double)op2_min ||
- (double)t3 != (double)op1_min * (double)op2_min ||
- (double)t4 != (double)op1_min * (double)op2_min) {
- tmp->underflow = 1;
- tmp->overflow = 1;
- tmp->min = ZEND_LONG_MIN;
- tmp->max = ZEND_LONG_MAX;
- } else {
- tmp->min = MIN(MIN(t1, t2), MIN(t3, t4));
- tmp->max = MAX(MAX(t1, t2), MAX(t3, t4));
- }
- return 1;
- }
- }
- } else if ((opline+1)->opcode == ZEND_OP_DATA) {
- if (ssa->ops[line+1].op1_def == var) {
- if (OP1_HAS_RANGE()) {
- opline++;
- tmp->min = OP1_MIN_RANGE();
- tmp->max = OP1_MAX_RANGE();
- tmp->underflow = OP1_RANGE_UNDERFLOW();
- tmp->overflow = OP1_RANGE_OVERFLOW();
- return 1;
- }
- }
- }
- break;
case ZEND_ASSIGN_DIV:
- if (opline->extended_value == 0) {
- if (ssa->ops[line].op1_def == var || ssa->ops[line].result_def == var) {
- if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
- op1_min = OP1_MIN_RANGE();
- op2_min = OP2_MIN_RANGE();
- op1_max = OP1_MAX_RANGE();
- op2_max = OP2_MAX_RANGE();
- if (op2_min <= 0 && op2_max >= 0) {
- break;
- }
- t1 = op1_min / op2_min;
- t2 = op1_min / op2_max;
- t3 = op1_max / op2_min;
- t4 = op1_max / op2_max;
- // FIXME: more careful overflow checks?
- if (OP1_RANGE_UNDERFLOW() ||
- OP2_RANGE_UNDERFLOW() ||
- OP1_RANGE_OVERFLOW() ||
- OP2_RANGE_OVERFLOW() ||
- t1 != (zend_long)((double)op1_min / (double)op2_min) ||
- t2 != (zend_long)((double)op1_min / (double)op2_max) ||
- t3 != (zend_long)((double)op1_max / (double)op2_min) ||
- t4 != (zend_long)((double)op1_max / (double)op2_max)) {
- tmp->underflow = 1;
- tmp->overflow = 1;
- tmp->min = ZEND_LONG_MIN;
- tmp->max = ZEND_LONG_MAX;
- } else {
- tmp->min = MIN(MIN(t1, t2), MIN(t3, t4));
- tmp->max = MAX(MAX(t1, t2), MAX(t3, t4));
- }
- return 1;
- }
- }
- } else if ((opline+1)->opcode == ZEND_OP_DATA) {
- if (ssa->ops[line+1].op1_def == var) {
- if (OP1_HAS_RANGE()) {
- opline++;
- tmp->min = OP1_MIN_RANGE();
- tmp->max = OP1_MAX_RANGE();
- tmp->underflow = OP1_RANGE_UNDERFLOW();
- tmp->overflow = OP1_RANGE_OVERFLOW();
- return 1;
- }
- }
- }
- break;
case ZEND_ASSIGN_MOD:
- if (opline->extended_value == 0) {
- if (ssa->ops[line].op1_def == var || ssa->ops[line].result_def == var) {
- if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
- if (OP1_RANGE_UNDERFLOW() ||
- OP2_RANGE_UNDERFLOW() ||
- OP1_RANGE_OVERFLOW() ||
- OP2_RANGE_OVERFLOW()) {
- tmp->min = ZEND_LONG_MIN;
- tmp->max = ZEND_LONG_MAX;
- } else {
- op1_min = OP1_MIN_RANGE();
- op2_min = OP2_MIN_RANGE();
- op1_max = OP1_MAX_RANGE();
- op2_max = OP2_MAX_RANGE();
- if (op2_min == 0 || op2_max == 0) {
- /* avoid division by zero */
- break;
- }
- t1 = op1_min % op2_min;
- t2 = op1_min % op2_max;
- t3 = op1_max % op2_min;
- t4 = op1_max % op2_max;
- tmp->min = MIN(MIN(t1, t2), MIN(t3, t4));
- tmp->max = MAX(MAX(t1, t2), MAX(t3, t4));
- }
- return 1;
- }
- }
- } else if ((opline+1)->opcode == ZEND_OP_DATA) {
- if (ssa->ops[line+1].op1_def == var) {
- if (OP1_HAS_RANGE()) {
- opline++;
- tmp->min = OP1_MIN_RANGE();
- tmp->max = OP1_MAX_RANGE();
- tmp->underflow = OP1_RANGE_UNDERFLOW();
- tmp->overflow = OP1_RANGE_OVERFLOW();
- return 1;
- }
- }
- }
- break;
case ZEND_ASSIGN_SL:
- if (opline->extended_value == 0) {
- if (ssa->ops[line].op1_def == var || ssa->ops[line].result_def == var) {
- if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
- if (OP1_RANGE_UNDERFLOW() ||
- OP2_RANGE_UNDERFLOW() ||
- OP1_RANGE_OVERFLOW() ||
- OP2_RANGE_OVERFLOW()) {
- tmp->min = ZEND_LONG_MIN;
- tmp->max = ZEND_LONG_MAX;
- } else {
- op1_min = OP1_MIN_RANGE();
- op2_min = OP2_MIN_RANGE();
- op1_max = OP1_MAX_RANGE();
- op2_max = OP2_MAX_RANGE();
- t1 = op1_min << op2_min;
- t2 = op1_min << op2_max;
- t3 = op1_max << op2_min;
- t4 = op1_max << op2_max;
- tmp->min = MIN(MIN(t1, t2), MIN(t3, t4));
- tmp->max = MAX(MAX(t1, t2), MAX(t3, t4));
- }
- return 1;
- }
- }
- } else if ((opline+1)->opcode == ZEND_OP_DATA) {
- if (ssa->ops[line+1].op1_def == var) {
- if (OP1_HAS_RANGE()) {
- opline++;
- tmp->min = OP1_MIN_RANGE();
- tmp->max = OP1_MAX_RANGE();
- tmp->underflow = OP1_RANGE_UNDERFLOW();
- tmp->overflow = OP1_RANGE_OVERFLOW();
- return 1;
- }
- }
- }
- break;
case ZEND_ASSIGN_SR:
- if (opline->extended_value == 0) {
- if (ssa->ops[line].op1_def == var || ssa->ops[line].result_def == var) {
- if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
- if (OP1_RANGE_UNDERFLOW() ||
- OP2_RANGE_UNDERFLOW() ||
- OP1_RANGE_OVERFLOW() ||
- OP2_RANGE_OVERFLOW()) {
- tmp->min = ZEND_LONG_MIN;
- tmp->max = ZEND_LONG_MAX;
- } else {
- op1_min = OP1_MIN_RANGE();
- op2_min = OP2_MIN_RANGE();
- op1_max = OP1_MAX_RANGE();
- op2_max = OP2_MAX_RANGE();
- t1 = op1_min >> op2_min;
- t2 = op1_min >> op2_max;
- t3 = op1_max >> op2_min;
- t4 = op1_max >> op2_max;
- tmp->min = MIN(MIN(t1, t2), MIN(t3, t4));
- tmp->max = MAX(MAX(t1, t2), MAX(t3, t4));
- }
- return 1;
- }
- }
- } else if ((opline+1)->opcode == ZEND_OP_DATA) {
- if (ssa->ops[line+1].op1_def == var) {
- if (OP1_HAS_RANGE()) {
- opline++;
- tmp->min = OP1_MIN_RANGE();
- tmp->max = OP1_MAX_RANGE();
- tmp->underflow = OP1_RANGE_UNDERFLOW();
- tmp->overflow = OP1_RANGE_OVERFLOW();
- return 1;
- }
- }
- }
- break;
case ZEND_ASSIGN_BW_OR:
- if (opline->extended_value == 0) {
- if (ssa->ops[line].op1_def == var || ssa->ops[line].result_def == var) {
- if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
- if (OP1_RANGE_UNDERFLOW() ||
- OP2_RANGE_UNDERFLOW() ||
- OP1_RANGE_OVERFLOW() ||
- OP2_RANGE_OVERFLOW()) {
- tmp->min = ZEND_LONG_MIN;
- tmp->max = ZEND_LONG_MAX;
- } else {
- op1_min = OP1_MIN_RANGE();
- op2_min = OP2_MIN_RANGE();
- op1_max = OP1_MAX_RANGE();
- op2_max = OP2_MAX_RANGE();
- zend_ssa_range_or(op1_min, op1_max, op2_min, op2_max, tmp);
- }
- return 1;
- }
- }
- } else if ((opline+1)->opcode == ZEND_OP_DATA) {
- if (ssa->ops[line+1].op1_def == var) {
- if (OP1_HAS_RANGE()) {
- opline++;
- tmp->min = OP1_MIN_RANGE();
- tmp->max = OP1_MAX_RANGE();
- tmp->underflow = OP1_RANGE_UNDERFLOW();
- tmp->overflow = OP1_RANGE_OVERFLOW();
- return 1;
- }
- }
- }
- break;
case ZEND_ASSIGN_BW_AND:
+ case ZEND_ASSIGN_BW_XOR:
if (opline->extended_value == 0) {
if (ssa->ops[line].op1_def == var || ssa->ops[line].result_def == var) {
- if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
- if (OP1_RANGE_UNDERFLOW() ||
- OP2_RANGE_UNDERFLOW() ||
- OP1_RANGE_OVERFLOW() ||
- OP2_RANGE_OVERFLOW()) {
- tmp->min = ZEND_LONG_MIN;
- tmp->max = ZEND_LONG_MAX;
- } else {
- op1_min = OP1_MIN_RANGE();
- op2_min = OP2_MIN_RANGE();
- op1_max = OP1_MAX_RANGE();
- op2_max = OP2_MAX_RANGE();
- zend_ssa_range_and(op1_min, op1_max, op2_min, op2_max, tmp);
- }
- return 1;
- }
+ return zend_inference_calc_binary_op_range(
+ op_array, ssa, opline, &ssa->ops[line],
+ get_compound_assign_op(opline->opcode), tmp);
}
} else if ((opline+1)->opcode == ZEND_OP_DATA) {
if (ssa->ops[line+1].op1_def == var) {
+ opline++;
if (OP1_HAS_RANGE()) {
- opline++;
tmp->min = OP1_MIN_RANGE();
tmp->max = OP1_MAX_RANGE();
tmp->underflow = OP1_RANGE_UNDERFLOW();
}
}
break;
-// case ZEND_ASSIGN_BW_XOR:
// case ZEND_ASSIGN_CONCAT:
case ZEND_OP_DATA:
if ((opline-1)->opcode == ZEND_ASSIGN_DIM ||
return tmp;
}
-/* Get the normal op corresponding to a compound assignment op */
-static inline zend_uchar get_compound_assign_op(zend_uchar opcode) {
- switch (opcode) {
- case ZEND_ASSIGN_ADD: return ZEND_ADD;
- case ZEND_ASSIGN_SUB: return ZEND_SUB;
- case ZEND_ASSIGN_MUL: return ZEND_MUL;
- case ZEND_ASSIGN_DIV: return ZEND_DIV;
- case ZEND_ASSIGN_MOD: return ZEND_MOD;
- case ZEND_ASSIGN_SL: return ZEND_SL;
- case ZEND_ASSIGN_SR: return ZEND_SR;
- case ZEND_ASSIGN_CONCAT: return ZEND_CONCAT;
- case ZEND_ASSIGN_BW_OR: return ZEND_BW_OR;
- case ZEND_ASSIGN_BW_AND: return ZEND_BW_AND;
- case ZEND_ASSIGN_BW_XOR: return ZEND_BW_XOR;
- case ZEND_ASSIGN_POW: return ZEND_POW;
- EMPTY_SWITCH_DEFAULT_CASE()
- }
-}
-
static inline zend_class_entry *get_class_entry(const zend_script *script, zend_string *lcname) {
zend_class_entry *ce = script ? zend_hash_find_ptr(&script->class_table, lcname) : NULL;
if (ce) {