Browse code

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 authored on 11/12/2008 18:30:41
Showing 1 changed files
... ...
@@ -62,8 +62,9 @@ void rval_destroy(struct rvalue* rv)
62 62
 {
63 63
 	if (rv && rv_unref(rv)){
64 64
 		rval_force_clean(rv);
65
-		if (rv->flags & RV_RV_ALLOCED_F)
65
+		if (rv->flags & RV_RV_ALLOCED_F){
66 66
 			pkg_free(rv);
67
+		}
67 68
 	}
68 69
 }
69 70
 
... ...
@@ -416,6 +417,42 @@ int rve_is_constant(struct rval_expr* rve)
416 417
 
417 418
 
418 419
 
420
+/** returns true if operator is unary (takes only 1 arg).
421
+  * @return 0 or 1 on
422
+  */
423
+static int rve_op_unary(enum rval_expr_op op)
424
+{
425
+	switch(op){
426
+		case RVE_RVAL_OP: /* not realy an operator */
427
+			return -1;
428
+		case RVE_UMINUS_OP:
429
+		case RVE_BOOL_OP:
430
+		case RVE_LNOT_OP:
431
+			return 1;
432
+		case RVE_MINUS_OP:
433
+		case RVE_MUL_OP:
434
+		case RVE_DIV_OP:
435
+		case RVE_BOR_OP:
436
+		case RVE_BAND_OP:
437
+		case RVE_LAND_OP:
438
+		case RVE_LOR_OP:
439
+		case RVE_GT_OP:
440
+		case RVE_GTE_OP:
441
+		case RVE_LT_OP:
442
+		case RVE_LTE_OP:
443
+		case RVE_EQ_OP:
444
+		case RVE_DIFF_OP:
445
+		case RVE_PLUS_OP:
446
+			return 0;
447
+		case RVE_NONE_OP:
448
+			return -1;
449
+			break;
450
+	}
451
+	return 0;
452
+}
453
+
454
+
455
+
419 456
 /** returns 1 if expression is valid (type-wise).
420 457
   * @return 0 or 1  and sets *type to the resulting type
421 458
   * (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,
1626 1663
 
1627 1664
 
1628 1665
 
1666
+/** returns true if the operator is associative. */
1667
+static int rve_op_is_assoc(enum rval_expr_op op)
1668
+{
1669
+	switch(op){
1670
+		case RVE_NONE_OP:
1671
+		case RVE_RVAL_OP:
1672
+		case RVE_UMINUS_OP:
1673
+		case RVE_BOOL_OP:
1674
+		case RVE_LNOT_OP:
1675
+			/* one operand expression => cannot be assoc. */
1676
+			return 0;
1677
+		case RVE_DIV_OP:
1678
+		case RVE_MINUS_OP:
1679
+			return 0;
1680
+		case RVE_PLUS_OP:
1681
+		case RVE_MUL_OP:
1682
+		case RVE_BAND_OP:
1683
+		case RVE_BOR_OP:
1684
+			return 1;
1685
+		case RVE_LAND_OP:
1686
+		case RVE_LOR_OP:
1687
+			return 1;
1688
+		case RVE_GT_OP:
1689
+		case RVE_GTE_OP:
1690
+		case RVE_LT_OP:
1691
+		case RVE_LTE_OP:
1692
+		case RVE_EQ_OP:
1693
+		case RVE_DIFF_OP:
1694
+			return 0;
1695
+	}
1696
+	return 0;
1697
+}
1698
+
1699
+
1700
+
1701
+/** returns true if the operator is commutative. */
1702
+static int rve_op_is_commutative(enum rval_expr_op op, enum rval_type type)
1703
+{
1704
+	switch(op){
1705
+		case RVE_NONE_OP:
1706
+		case RVE_RVAL_OP:
1707
+		case RVE_UMINUS_OP:
1708
+		case RVE_BOOL_OP:
1709
+		case RVE_LNOT_OP:
1710
+			/* one operand expression => cannot be commut. */
1711
+			return 0;
1712
+		case RVE_DIV_OP:
1713
+		case RVE_MINUS_OP:
1714
+			return 0;
1715
+		case RVE_PLUS_OP:
1716
+			return type==RV_INT; /* commutative only for INT*/
1717
+		case RVE_MUL_OP:
1718
+		case RVE_BAND_OP:
1719
+		case RVE_BOR_OP:
1720
+			return 1;
1721
+		case RVE_LAND_OP:
1722
+		case RVE_LOR_OP:
1723
+			return 1;
1724
+		case RVE_GT_OP:
1725
+		case RVE_GTE_OP:
1726
+		case RVE_LT_OP:
1727
+		case RVE_LTE_OP:
1728
+		case RVE_EQ_OP:
1729
+		case RVE_DIFF_OP:
1730
+			return 0;
1731
+	}
1732
+	return 0;
1733
+}
1734
+
1735
+
1736
+#if 0
1629 1737
 /** returns true if the rval expr can be optimized to an int.
1630 1738
  *  (if left & right are leafs (RVE_RVAL_OP) and both of them are
1631 1739
  *   ints return true, else false)
... ...
@@ -1680,6 +1788,7 @@ static int rve_can_optimize_str(struct rval_expr* rve)
1680 1788
 	}
1681 1789
 	return 1;
1682 1790
 }
1791
+#endif
1683 1792
 
1684 1793
 
1685 1794
 
... ...
@@ -1721,6 +1830,261 @@ static int fix_rval(struct rvalue* rv)
1721 1830
 
1722 1831
 
1723 1832
 
1833
+static int rve_replace_with_ct_rv(struct rval_expr* rve, struct rvalue* rv)
1834
+{
1835
+	enum rval_type type;
1836
+	int flags;
1837
+	int i;
1838
+	union rval_val v;
1839
+	
1840
+	type=rv->type;
1841
+	flags=0;
1842
+	if (rv->type==RV_INT){
1843
+		if (rval_get_int(0, 0, &i, rv, 0)!=0){
1844
+			BUG("unexpected int evaluation failure\n");
1845
+			return -1;
1846
+		}
1847
+		v.l=i;
1848
+	}else if(rv->type==RV_STR){
1849
+		if (rval_get_str(0, 0, &v.s, rv, 0)<0){
1850
+			BUG("unexpected str evaluation failure\n");
1851
+			return -1;
1852
+		}
1853
+		flags=RV_CNT_ALLOCED_F;
1854
+	}else{
1855
+		BUG("unknown constant expression type %d\n", rv->type);
1856
+		return -1;
1857
+	}
1858
+	if (rve->op!=RVE_RVAL_OP){
1859
+		rve_destroy(rve->left.rve);
1860
+		if (rve_op_unary(rve->op)==0)
1861
+			rve_destroy(rve->right.rve);
1862
+	}else
1863
+		rval_destroy(&rve->left.rval);
1864
+	rval_init(&rve->left.rval, type, &v, flags);
1865
+	rval_init(&rve->right.rval, RV_NONE, 0, 0);
1866
+	rve->op=RVE_RVAL_OP;
1867
+	return 0;
1868
+}
1869
+
1870
+
1871
+
1872
+/** tries to optimize a rval_expr. */
1873
+static int rve_optimize(struct rval_expr* rve)
1874
+{
1875
+	int ret;
1876
+	struct rvalue* rv;
1877
+	struct rvalue* trv; /* used only for DBG() */
1878
+	enum rval_expr_op op;
1879
+	int flags;
1880
+	struct rval_expr tmp_rve;
1881
+	enum rval_type type;
1882
+	
1883
+	ret=0;
1884
+	flags=0;
1885
+	rv=0;
1886
+	if (scr_opt_lev<1)
1887
+		return 0;
1888
+	if (rve->op == RVE_RVAL_OP) /* if rval, nothing to do */
1889
+		return 0;
1890
+	if (rve_is_constant(rve)){
1891
+		if ((rv=rval_expr_eval(0, 0, rve))==0){
1892
+			ERR("optimization failure, bad expression\n");
1893
+			goto error;
1894
+		}
1895
+		op=rve->op;
1896
+		if (rve_replace_with_ct_rv(rve, rv)<0)
1897
+			goto error;
1898
+		rval_destroy(rv);
1899
+		rv=0;
1900
+		trv=&rve->left.rval;
1901
+		if (trv->type==RV_INT)
1902
+			DBG("FIXUP RVE: optimized constant int rve (old op %d) to %d\n",
1903
+					op, (int)trv->v.l);
1904
+		else if (trv->type==RV_STR)
1905
+			DBG("FIXUP RVE: optimized constant str rve (old op %d) to"
1906
+					" \"%.*s\"\n", op, trv->v.s.len, trv->v.s.s);
1907
+		ret=1;
1908
+	}else{
1909
+		/* expression is not constant */
1910
+		/* if unary => nothing to do */
1911
+		if (rve_op_unary(rve->op))
1912
+			return rve_optimize(rve->left.rve);
1913
+		rve_optimize(rve->left.rve);
1914
+		rve_optimize(rve->right.rve);
1915
+		if (!rve_check_type(&type, rve)){
1916
+			ERR("optimization failure, type mismatch in expression\n");
1917
+			return 0;
1918
+		}
1919
+		/* TODO: $v - a => $v + (-a) */
1920
+		/* TODO: $v * 0 => 0; $v * 1 => $v (for *, /, &, |, &&, ||, +, -) */
1921
+		
1922
+		/* op(op($v, a), b) => op($v, op(a,b)) */
1923
+		if (rve_is_constant(rve->right.rve)){
1924
+			/* op1(op2(...), b) */
1925
+			if ((rve->op==rve->left.rve->op) && rve_op_is_assoc(rve->op)){
1926
+				/* op(op(...), b) */
1927
+				if (rve_is_constant(rve->left.rve->right.rve)){
1928
+					/* op(op($v, a), b) => op($v, op(a, b)) */
1929
+					/* rv= op(a, b) */
1930
+					tmp_rve.op=rve->op;
1931
+					tmp_rve.left.rve=rve->left.rve->right.rve;
1932
+					tmp_rve.right.rve=rve->right.rve;
1933
+					/* hack for RVE_PLUS_OP which can work on string, ints
1934
+					   or a combination of them */
1935
+					if ((rve->op==RVE_PLUS_OP) &&
1936
+						(rve_guess_type(tmp_rve.left.rve)!=RV_STR)){
1937
+						DBG("RVE optimization failed: cannot optimize"
1938
+								" +(+($v, a), b) when typeof(a)==INT\n");
1939
+						return 0;
1940
+					}
1941
+					if ((rv=rval_expr_eval(0, 0, &tmp_rve))==0){
1942
+						ERR("optimization failure, bad expression\n");
1943
+						goto error;
1944
+					}
1945
+					/* op($v, rv) */
1946
+					if (rve_replace_with_ct_rv(rve->right.rve, rv)<0)
1947
+						goto error;
1948
+					rval_destroy(rv);
1949
+					rv=0;
1950
+					rve_destroy(tmp_rve.left.rve);
1951
+					rve->left.rve=rve->left.rve->left.rve;
1952
+					trv=&rve->right.rve->left.rval;
1953
+					if (trv->type==RV_INT)
1954
+						DBG("FIXUP RVE: optimized int rve: op(op($v, a), b)"
1955
+								" with op($v, %d); op=%d\n",
1956
+								(int)trv->v.l, rve->op);
1957
+					else if (trv->type==RV_STR)
1958
+						DBG("FIXUP RVE: optimized str rve op(op($v, a), b)"
1959
+								" with op($v, \"%.*s\"); op=%d\n",
1960
+								trv->v.s.len, trv->v.s.s, rve->op);
1961
+					ret=1;
1962
+				}else if (rve_is_constant(rve->left.rve->left.rve) &&
1963
+							rve_op_is_commutative(rve->op, type)){
1964
+					/* op(op(a, $v), b) => op(op(a, b), $v) */
1965
+					/* rv= op(a, b) */
1966
+					tmp_rve.op=rve->op;
1967
+					tmp_rve.left.rve=rve->left.rve->left.rve;
1968
+					tmp_rve.right.rve=rve->right.rve;
1969
+					/* no need for the RVE_PLUS_OP hack, all the bad
1970
+					   cases are caught by rve_op_is_commutative()
1971
+					   (in this case type will be typeof(a)) => ok only if
1972
+					   typeof(a) is int) */
1973
+					if ((rv=rval_expr_eval(0, 0, &tmp_rve))==0){
1974
+						ERR("optimization failure, bad expression\n");
1975
+						goto error;
1976
+					}
1977
+					/* op(rv, $v) */
1978
+					rve_destroy(rve->right.rve);
1979
+					rve->right.rve=rve->left.rve->right.rve;
1980
+					rve->left.rve->right.rve=0;
1981
+					if (rve_replace_with_ct_rv(rve->left.rve, rv)<0)
1982
+						goto error;
1983
+					rval_destroy(rv);
1984
+					rv=0;
1985
+					trv=&rve->left.rve->left.rval;
1986
+					if (trv->type==RV_INT)
1987
+						DBG("FIXUP RVE: optimized int rve: op(op(a, $v), b)"
1988
+								" with op(%d, $v); op=%d\n",
1989
+								(int)trv->v.l, rve->op);
1990
+					else if (trv->type==RV_STR)
1991
+						DBG("FIXUP RVE: optimized str rve op(op(a, $v), b)"
1992
+								" with op(\"%.*s\", $v); op=%d\n",
1993
+								trv->v.s.len, trv->v.s.s, rve->op);
1994
+					ret=1;
1995
+				}
1996
+				/* op(op($v, $w),b) => can't optimize */
1997
+			}
1998
+			/* op1(op2(...), b) and op1!=op2 or op is non assoc.
1999
+			   => can't optimize */
2000
+		}else if (rve_is_constant(rve->left.rve)){
2001
+			/* op1(a, op2(...)) */
2002
+			if ((rve->op==rve->right.rve->op) && rve_op_is_assoc(rve->op)){
2003
+				/* op(a, op(...)) */
2004
+				if (rve_is_constant(rve->right.rve->right.rve) &&
2005
+						rve_op_is_commutative(rve->op, type)){
2006
+					/* op(a, op($v, b)) => op(op(a, b), $v) */
2007
+					/* rv= op(a, b) */
2008
+					tmp_rve.op=rve->op;
2009
+					tmp_rve.left.rve=rve->left.rve;
2010
+					tmp_rve.right.rve=rve->right.rve->right.rve;
2011
+					/* no need for the RVE_PLUS_OP hack, all the bad
2012
+					   cases are caught by rve_op_is_commutative()
2013
+					   (in this case type will be typeof(a)) => ok only if
2014
+					   typeof(a) is int) */
2015
+					if ((rv=rval_expr_eval(0, 0, &tmp_rve))==0){
2016
+						ERR("optimization failure, bad expression\n");
2017
+						goto error;
2018
+					}
2019
+					/* op(rv, $v) */
2020
+					if (rve_replace_with_ct_rv(rve->left.rve, rv)<0)
2021
+						goto error;
2022
+					rval_destroy(rv);
2023
+					rv=0;
2024
+					rve_destroy(tmp_rve.right.rve);
2025
+					rve->right.rve=rve->right.rve->left.rve;
2026
+					trv=&rve->left.rve->left.rval;
2027
+					if (trv->type==RV_INT)
2028
+						DBG("FIXUP RVE: optimized int rve: op(a, op($v, b))"
2029
+								" with op(%d, $v); op=%d\n",
2030
+								(int)trv->v.l, rve->op);
2031
+					else if (trv->type==RV_STR)
2032
+						DBG("FIXUP RVE: optimized str rve op(a, op($v, b))"
2033
+								" with op(\"%.*s\", $v); op=%d\n",
2034
+								trv->v.s.len, trv->v.s.s, rve->op);
2035
+					ret=1;
2036
+				}else if (rve_is_constant(rve->right.rve->left.rve)){
2037
+					/* op(a, op(b, $v)) => op(op(a, b), $v) */
2038
+					/* rv= op(a, b) */
2039
+					tmp_rve.op=rve->op;
2040
+					tmp_rve.left.rve=rve->left.rve;
2041
+					tmp_rve.right.rve=rve->right.rve->left.rve;
2042
+					/* hack for RVE_PLUS_OP which can work on string, ints
2043
+					   or a combination of them */
2044
+					if ((rve->op==RVE_PLUS_OP) &&
2045
+						(rve_guess_type(tmp_rve.left.rve) != 
2046
+						 	rve_guess_type(tmp_rve.right.rve))){
2047
+						DBG("RVE optimization failed: cannot optimize"
2048
+								" +(a, +(b, $v)) when typeof(a)!=typeof(b)\n");
2049
+						return 0;
2050
+					}
2051
+					if ((rv=rval_expr_eval(0, 0, &tmp_rve))==0){
2052
+						ERR("optimization failure, bad expression\n");
2053
+						goto error;
2054
+					}
2055
+					/* op(rv, $v) */
2056
+					if (rve_replace_with_ct_rv(rve->left.rve, rv)<0)
2057
+						goto error;
2058
+					rval_destroy(rv);
2059
+					rv=0;
2060
+					rve_destroy(tmp_rve.right.rve);
2061
+					rve->right.rve=rve->right.rve->right.rve;
2062
+					trv=&rve->left.rve->left.rval;
2063
+					if (trv->type==RV_INT)
2064
+						DBG("FIXUP RVE: optimized int rve: op(a, op(b, $v))"
2065
+								" with op(%d, $v); op=%d\n",
2066
+								(int)trv->v.l, rve->op);
2067
+					else if (trv->type==RV_STR)
2068
+						DBG("FIXUP RVE: optimized str rve op(a, op(b, $v))"
2069
+								" with op(\"%.*s\", $v); op=%d\n",
2070
+								trv->v.s.len, trv->v.s.s, rve->op);
2071
+					ret=1;
2072
+				}
2073
+				/* op(a, op($v, $w)) => can't optimize */
2074
+			}
2075
+			/* op1(a, op2(...)) and op1!=op2 or op is non assoc.
2076
+			   => can't optimize */
2077
+		}
2078
+		/* op(op($v,a), op($w,b)) => no optimizations for now (TODO) */
2079
+	}
2080
+	return ret;
2081
+error:
2082
+	if (rv) rval_destroy(rv);
2083
+	return -1;
2084
+}
2085
+
2086
+
2087
+
1724 2088
 /** fix a rval_expr.
1725 2089
  * fixes action, bexprs, resolves selects, pvars and
1726 2090
  * optimizes simple sub expressions (e.g. 1+2).
... ...
@@ -1733,10 +2097,7 @@ int fix_rval_expr(void** p)
1733 2097
 {
1734 2098
 	struct rval_expr** prve;
1735 2099
 	struct rval_expr* rve;
1736
-	union rval_val v;
1737
-	struct rvalue* rv;
1738 2100
 	int ret;
1739
-	int i;
1740 2101
 	
1741 2102
 	prve=(struct rval_expr**)p;
1742 2103
 	rve=*prve;
... ...
@@ -1776,35 +2137,6 @@ int fix_rval_expr(void** p)
1776 2137
 			BUG("unsupported op type %d\n", rve->op);
1777 2138
 	}
1778 2139
 	/* try to optimize */
1779
-	if (rve_can_optimize_int(rve)){
1780
-		if (rval_expr_eval_int(0, 0, &i, rve)<0){
1781
-			BUG("unexpected failure\n");
1782
-			return -1;
1783
-		}
1784
-		rve_destroy(rve->left.rve);
1785
-		rve_destroy(rve->right.rve);
1786
-		v.l=i;
1787
-		rval_init(&rve->left.rval, RV_INT, &v, 0);
1788
-		rval_init(&rve->right.rval, RV_NONE, 0, 0);
1789
-		DBG("FIXUP RVE: optimized int rve/rves (op %d) to %d\n", rve->op, i);
1790
-		rve->op=RVE_RVAL_OP;
1791
-	}else if (rve_can_optimize_str(rve)){
1792
-		if ((rv=rval_expr_eval(0, 0, rve))==0){
1793
-			BUG("unexpected failure\n");
1794
-			return -1;
1795
-		}
1796
-		if (rval_get_str(0, 0, &v.s, rv, 0)<0){
1797
-			BUG("unexpected failure\n");
1798
-			return -1;
1799
-		}
1800
-		rval_destroy(rv);
1801
-		rve_destroy(rve->left.rve);
1802
-		rve_destroy(rve->right.rve);
1803
-		rval_init(&rve->left.rval, RV_STR, &v, RV_CNT_ALLOCED_F);
1804
-		rval_init(&rve->right.rval, RV_NONE, 0, 0);
1805
-		DBG("FIXUP RVE: optimized str rves (op %d) to %.*s\n",
1806
-				rve->op, v.s.len, v.s.s);
1807
-		rve->op=RVE_RVAL_OP;
1808
-	}
2140
+	rve_optimize(rve);
1809 2141
 	return 0;
1810 2142
 }