Ver código fonte

script engine: more rvalue optimizations

- optimize:
    ($v * a) * b  -> $v * (a * b) if '*' is associative
    (a * $v) * b  -> (a * b) * $v if '*' is assoc. and commutative
    a * ($v * b)  -> (a * b) * $v if '*' is assoc. and commutative
    a * (b * $v)  -> (a * b) * $v if '*' is associative
  where a & b are constants, $v is a var (pvar or avp) and '*' is
  the operator.

- special hacks for optimizing '+' ('+' is a special case because
  in the current form it can work both with strings and ints which
  combined with the dynamic typed vars make it very hard to
  optimize)
Andrei Pelinescu-Onciul 16 anos atrás
pai
commit
0b3512af8f
1 arquivos alterados com 366 adições e 34 exclusões
  1. 366 34
      rvalue.c

+ 366 - 34
rvalue.c

@@ -62,8 +62,9 @@ void rval_destroy(struct rvalue* rv)
 {
 	if (rv && rv_unref(rv)){
 		rval_force_clean(rv);
-		if (rv->flags & RV_RV_ALLOCED_F)
+		if (rv->flags & RV_RV_ALLOCED_F){
 			pkg_free(rv);
+		}
 	}
 }
 
@@ -416,6 +417,42 @@ int rve_is_constant(struct rval_expr* rve)
 
 
 
+/** returns true if operator is unary (takes only 1 arg).
+  * @return 0 or 1 on
+  */
+static int rve_op_unary(enum rval_expr_op op)
+{
+	switch(op){
+		case RVE_RVAL_OP: /* not realy an operator */
+			return -1;
+		case RVE_UMINUS_OP:
+		case RVE_BOOL_OP:
+		case RVE_LNOT_OP:
+			return 1;
+		case RVE_MINUS_OP:
+		case RVE_MUL_OP:
+		case RVE_DIV_OP:
+		case RVE_BOR_OP:
+		case RVE_BAND_OP:
+		case RVE_LAND_OP:
+		case RVE_LOR_OP:
+		case RVE_GT_OP:
+		case RVE_GTE_OP:
+		case RVE_LT_OP:
+		case RVE_LTE_OP:
+		case RVE_EQ_OP:
+		case RVE_DIFF_OP:
+		case RVE_PLUS_OP:
+			return 0;
+		case RVE_NONE_OP:
+			return -1;
+			break;
+	}
+	return 0;
+}
+
+
+
 /** returns 1 if expression is valid (type-wise).
   * @return 0 or 1  and sets *type to the resulting type
   * (RV_INT, RV_STR or RV_NONE if it can be found only at runtime)
@@ -1626,6 +1663,77 @@ struct rval_expr* mk_rval_expr2(enum rval_expr_op op, struct rval_expr* rve1,
 
 
 
+/** returns true if the operator is associative. */
+static int rve_op_is_assoc(enum rval_expr_op op)
+{
+	switch(op){
+		case RVE_NONE_OP:
+		case RVE_RVAL_OP:
+		case RVE_UMINUS_OP:
+		case RVE_BOOL_OP:
+		case RVE_LNOT_OP:
+			/* one operand expression => cannot be assoc. */
+			return 0;
+		case RVE_DIV_OP:
+		case RVE_MINUS_OP:
+			return 0;
+		case RVE_PLUS_OP:
+		case RVE_MUL_OP:
+		case RVE_BAND_OP:
+		case RVE_BOR_OP:
+			return 1;
+		case RVE_LAND_OP:
+		case RVE_LOR_OP:
+			return 1;
+		case RVE_GT_OP:
+		case RVE_GTE_OP:
+		case RVE_LT_OP:
+		case RVE_LTE_OP:
+		case RVE_EQ_OP:
+		case RVE_DIFF_OP:
+			return 0;
+	}
+	return 0;
+}
+
+
+
+/** returns true if the operator is commutative. */
+static int rve_op_is_commutative(enum rval_expr_op op, enum rval_type type)
+{
+	switch(op){
+		case RVE_NONE_OP:
+		case RVE_RVAL_OP:
+		case RVE_UMINUS_OP:
+		case RVE_BOOL_OP:
+		case RVE_LNOT_OP:
+			/* one operand expression => cannot be commut. */
+			return 0;
+		case RVE_DIV_OP:
+		case RVE_MINUS_OP:
+			return 0;
+		case RVE_PLUS_OP:
+			return type==RV_INT; /* commutative only for INT*/
+		case RVE_MUL_OP:
+		case RVE_BAND_OP:
+		case RVE_BOR_OP:
+			return 1;
+		case RVE_LAND_OP:
+		case RVE_LOR_OP:
+			return 1;
+		case RVE_GT_OP:
+		case RVE_GTE_OP:
+		case RVE_LT_OP:
+		case RVE_LTE_OP:
+		case RVE_EQ_OP:
+		case RVE_DIFF_OP:
+			return 0;
+	}
+	return 0;
+}
+
+
+#if 0
 /** returns true if the rval expr can be optimized to an int.
  *  (if left & right are leafs (RVE_RVAL_OP) and both of them are
  *   ints return true, else false)
@@ -1680,6 +1788,7 @@ static int rve_can_optimize_str(struct rval_expr* rve)
 	}
 	return 1;
 }
+#endif
 
 
 
@@ -1721,6 +1830,261 @@ static int fix_rval(struct rvalue* rv)
 
 
 
+static int rve_replace_with_ct_rv(struct rval_expr* rve, struct rvalue* rv)
+{
+	enum rval_type type;
+	int flags;
+	int i;
+	union rval_val v;
+	
+	type=rv->type;
+	flags=0;
+	if (rv->type==RV_INT){
+		if (rval_get_int(0, 0, &i, rv, 0)!=0){
+			BUG("unexpected int evaluation failure\n");
+			return -1;
+		}
+		v.l=i;
+	}else if(rv->type==RV_STR){
+		if (rval_get_str(0, 0, &v.s, rv, 0)<0){
+			BUG("unexpected str evaluation failure\n");
+			return -1;
+		}
+		flags=RV_CNT_ALLOCED_F;
+	}else{
+		BUG("unknown constant expression type %d\n", rv->type);
+		return -1;
+	}
+	if (rve->op!=RVE_RVAL_OP){
+		rve_destroy(rve->left.rve);
+		if (rve_op_unary(rve->op)==0)
+			rve_destroy(rve->right.rve);
+	}else
+		rval_destroy(&rve->left.rval);
+	rval_init(&rve->left.rval, type, &v, flags);
+	rval_init(&rve->right.rval, RV_NONE, 0, 0);
+	rve->op=RVE_RVAL_OP;
+	return 0;
+}
+
+
+
+/** tries to optimize a rval_expr. */
+static int rve_optimize(struct rval_expr* rve)
+{
+	int ret;
+	struct rvalue* rv;
+	struct rvalue* trv; /* used only for DBG() */
+	enum rval_expr_op op;
+	int flags;
+	struct rval_expr tmp_rve;
+	enum rval_type type;
+	
+	ret=0;
+	flags=0;
+	rv=0;
+	if (scr_opt_lev<1)
+		return 0;
+	if (rve->op == RVE_RVAL_OP) /* if rval, nothing to do */
+		return 0;
+	if (rve_is_constant(rve)){
+		if ((rv=rval_expr_eval(0, 0, rve))==0){
+			ERR("optimization failure, bad expression\n");
+			goto error;
+		}
+		op=rve->op;
+		if (rve_replace_with_ct_rv(rve, rv)<0)
+			goto error;
+		rval_destroy(rv);
+		rv=0;
+		trv=&rve->left.rval;
+		if (trv->type==RV_INT)
+			DBG("FIXUP RVE: optimized constant int rve (old op %d) to %d\n",
+					op, (int)trv->v.l);
+		else if (trv->type==RV_STR)
+			DBG("FIXUP RVE: optimized constant str rve (old op %d) to"
+					" \"%.*s\"\n", op, trv->v.s.len, trv->v.s.s);
+		ret=1;
+	}else{
+		/* expression is not constant */
+		/* if unary => nothing to do */
+		if (rve_op_unary(rve->op))
+			return rve_optimize(rve->left.rve);
+		rve_optimize(rve->left.rve);
+		rve_optimize(rve->right.rve);
+		if (!rve_check_type(&type, rve)){
+			ERR("optimization failure, type mismatch in expression\n");
+			return 0;
+		}
+		/* TODO: $v - a => $v + (-a) */
+		/* TODO: $v * 0 => 0; $v * 1 => $v (for *, /, &, |, &&, ||, +, -) */
+		
+		/* op(op($v, a), b) => op($v, op(a,b)) */
+		if (rve_is_constant(rve->right.rve)){
+			/* op1(op2(...), b) */
+			if ((rve->op==rve->left.rve->op) && rve_op_is_assoc(rve->op)){
+				/* op(op(...), b) */
+				if (rve_is_constant(rve->left.rve->right.rve)){
+					/* op(op($v, a), b) => op($v, op(a, b)) */
+					/* rv= op(a, b) */
+					tmp_rve.op=rve->op;
+					tmp_rve.left.rve=rve->left.rve->right.rve;
+					tmp_rve.right.rve=rve->right.rve;
+					/* hack for RVE_PLUS_OP which can work on string, ints
+					   or a combination of them */
+					if ((rve->op==RVE_PLUS_OP) &&
+						(rve_guess_type(tmp_rve.left.rve)!=RV_STR)){
+						DBG("RVE optimization failed: cannot optimize"
+								" +(+($v, a), b) when typeof(a)==INT\n");
+						return 0;
+					}
+					if ((rv=rval_expr_eval(0, 0, &tmp_rve))==0){
+						ERR("optimization failure, bad expression\n");
+						goto error;
+					}
+					/* op($v, rv) */
+					if (rve_replace_with_ct_rv(rve->right.rve, rv)<0)
+						goto error;
+					rval_destroy(rv);
+					rv=0;
+					rve_destroy(tmp_rve.left.rve);
+					rve->left.rve=rve->left.rve->left.rve;
+					trv=&rve->right.rve->left.rval;
+					if (trv->type==RV_INT)
+						DBG("FIXUP RVE: optimized int rve: op(op($v, a), b)"
+								" with op($v, %d); op=%d\n",
+								(int)trv->v.l, rve->op);
+					else if (trv->type==RV_STR)
+						DBG("FIXUP RVE: optimized str rve op(op($v, a), b)"
+								" with op($v, \"%.*s\"); op=%d\n",
+								trv->v.s.len, trv->v.s.s, rve->op);
+					ret=1;
+				}else if (rve_is_constant(rve->left.rve->left.rve) &&
+							rve_op_is_commutative(rve->op, type)){
+					/* op(op(a, $v), b) => op(op(a, b), $v) */
+					/* rv= op(a, b) */
+					tmp_rve.op=rve->op;
+					tmp_rve.left.rve=rve->left.rve->left.rve;
+					tmp_rve.right.rve=rve->right.rve;
+					/* no need for the RVE_PLUS_OP hack, all the bad
+					   cases are caught by rve_op_is_commutative()
+					   (in this case type will be typeof(a)) => ok only if
+					   typeof(a) is int) */
+					if ((rv=rval_expr_eval(0, 0, &tmp_rve))==0){
+						ERR("optimization failure, bad expression\n");
+						goto error;
+					}
+					/* op(rv, $v) */
+					rve_destroy(rve->right.rve);
+					rve->right.rve=rve->left.rve->right.rve;
+					rve->left.rve->right.rve=0;
+					if (rve_replace_with_ct_rv(rve->left.rve, rv)<0)
+						goto error;
+					rval_destroy(rv);
+					rv=0;
+					trv=&rve->left.rve->left.rval;
+					if (trv->type==RV_INT)
+						DBG("FIXUP RVE: optimized int rve: op(op(a, $v), b)"
+								" with op(%d, $v); op=%d\n",
+								(int)trv->v.l, rve->op);
+					else if (trv->type==RV_STR)
+						DBG("FIXUP RVE: optimized str rve op(op(a, $v), b)"
+								" with op(\"%.*s\", $v); op=%d\n",
+								trv->v.s.len, trv->v.s.s, rve->op);
+					ret=1;
+				}
+				/* op(op($v, $w),b) => can't optimize */
+			}
+			/* op1(op2(...), b) and op1!=op2 or op is non assoc.
+			   => can't optimize */
+		}else if (rve_is_constant(rve->left.rve)){
+			/* op1(a, op2(...)) */
+			if ((rve->op==rve->right.rve->op) && rve_op_is_assoc(rve->op)){
+				/* op(a, op(...)) */
+				if (rve_is_constant(rve->right.rve->right.rve) &&
+						rve_op_is_commutative(rve->op, type)){
+					/* op(a, op($v, b)) => op(op(a, b), $v) */
+					/* rv= op(a, b) */
+					tmp_rve.op=rve->op;
+					tmp_rve.left.rve=rve->left.rve;
+					tmp_rve.right.rve=rve->right.rve->right.rve;
+					/* no need for the RVE_PLUS_OP hack, all the bad
+					   cases are caught by rve_op_is_commutative()
+					   (in this case type will be typeof(a)) => ok only if
+					   typeof(a) is int) */
+					if ((rv=rval_expr_eval(0, 0, &tmp_rve))==0){
+						ERR("optimization failure, bad expression\n");
+						goto error;
+					}
+					/* op(rv, $v) */
+					if (rve_replace_with_ct_rv(rve->left.rve, rv)<0)
+						goto error;
+					rval_destroy(rv);
+					rv=0;
+					rve_destroy(tmp_rve.right.rve);
+					rve->right.rve=rve->right.rve->left.rve;
+					trv=&rve->left.rve->left.rval;
+					if (trv->type==RV_INT)
+						DBG("FIXUP RVE: optimized int rve: op(a, op($v, b))"
+								" with op(%d, $v); op=%d\n",
+								(int)trv->v.l, rve->op);
+					else if (trv->type==RV_STR)
+						DBG("FIXUP RVE: optimized str rve op(a, op($v, b))"
+								" with op(\"%.*s\", $v); op=%d\n",
+								trv->v.s.len, trv->v.s.s, rve->op);
+					ret=1;
+				}else if (rve_is_constant(rve->right.rve->left.rve)){
+					/* op(a, op(b, $v)) => op(op(a, b), $v) */
+					/* rv= op(a, b) */
+					tmp_rve.op=rve->op;
+					tmp_rve.left.rve=rve->left.rve;
+					tmp_rve.right.rve=rve->right.rve->left.rve;
+					/* hack for RVE_PLUS_OP which can work on string, ints
+					   or a combination of them */
+					if ((rve->op==RVE_PLUS_OP) &&
+						(rve_guess_type(tmp_rve.left.rve) != 
+						 	rve_guess_type(tmp_rve.right.rve))){
+						DBG("RVE optimization failed: cannot optimize"
+								" +(a, +(b, $v)) when typeof(a)!=typeof(b)\n");
+						return 0;
+					}
+					if ((rv=rval_expr_eval(0, 0, &tmp_rve))==0){
+						ERR("optimization failure, bad expression\n");
+						goto error;
+					}
+					/* op(rv, $v) */
+					if (rve_replace_with_ct_rv(rve->left.rve, rv)<0)
+						goto error;
+					rval_destroy(rv);
+					rv=0;
+					rve_destroy(tmp_rve.right.rve);
+					rve->right.rve=rve->right.rve->right.rve;
+					trv=&rve->left.rve->left.rval;
+					if (trv->type==RV_INT)
+						DBG("FIXUP RVE: optimized int rve: op(a, op(b, $v))"
+								" with op(%d, $v); op=%d\n",
+								(int)trv->v.l, rve->op);
+					else if (trv->type==RV_STR)
+						DBG("FIXUP RVE: optimized str rve op(a, op(b, $v))"
+								" with op(\"%.*s\", $v); op=%d\n",
+								trv->v.s.len, trv->v.s.s, rve->op);
+					ret=1;
+				}
+				/* op(a, op($v, $w)) => can't optimize */
+			}
+			/* op1(a, op2(...)) and op1!=op2 or op is non assoc.
+			   => can't optimize */
+		}
+		/* op(op($v,a), op($w,b)) => no optimizations for now (TODO) */
+	}
+	return ret;
+error:
+	if (rv) rval_destroy(rv);
+	return -1;
+}
+
+
+
 /** fix a rval_expr.
  * fixes action, bexprs, resolves selects, pvars and
  * optimizes simple sub expressions (e.g. 1+2).
@@ -1733,10 +2097,7 @@ int fix_rval_expr(void** p)
 {
 	struct rval_expr** prve;
 	struct rval_expr* rve;
-	union rval_val v;
-	struct rvalue* rv;
 	int ret;
-	int i;
 	
 	prve=(struct rval_expr**)p;
 	rve=*prve;
@@ -1776,35 +2137,6 @@ int fix_rval_expr(void** p)
 			BUG("unsupported op type %d\n", rve->op);
 	}
 	/* try to optimize */
-	if (rve_can_optimize_int(rve)){
-		if (rval_expr_eval_int(0, 0, &i, rve)<0){
-			BUG("unexpected failure\n");
-			return -1;
-		}
-		rve_destroy(rve->left.rve);
-		rve_destroy(rve->right.rve);
-		v.l=i;
-		rval_init(&rve->left.rval, RV_INT, &v, 0);
-		rval_init(&rve->right.rval, RV_NONE, 0, 0);
-		DBG("FIXUP RVE: optimized int rve/rves (op %d) to %d\n", rve->op, i);
-		rve->op=RVE_RVAL_OP;
-	}else if (rve_can_optimize_str(rve)){
-		if ((rv=rval_expr_eval(0, 0, rve))==0){
-			BUG("unexpected failure\n");
-			return -1;
-		}
-		if (rval_get_str(0, 0, &v.s, rv, 0)<0){
-			BUG("unexpected failure\n");
-			return -1;
-		}
-		rval_destroy(rv);
-		rve_destroy(rve->left.rve);
-		rve_destroy(rve->right.rve);
-		rval_init(&rve->left.rval, RV_STR, &v, RV_CNT_ALLOCED_F);
-		rval_init(&rve->right.rval, RV_NONE, 0, 0);
-		DBG("FIXUP RVE: optimized str rves (op %d) to %.*s\n",
-				rve->op, v.s.len, v.s.s);
-		rve->op=RVE_RVAL_OP;
-	}
+	rve_optimize(rve);
 	return 0;
 }