add more Conv(Conv) localopt rules
authorMatthias Braun <matthias.braun@kit.edu>
Wed, 22 Aug 2012 14:41:09 +0000 (16:41 +0200)
committerMatthias Braun <matthias.braun@kit.edu>
Thu, 23 Aug 2012 11:34:27 +0000 (13:34 +0200)
include/libfirm/irmode.h
ir/ir/irmode.c
ir/ir/iropt.c
ir/ir/iropt_t.h
ir/opt/convopt.c

index 4795f08..b22e347 100644 (file)
@@ -330,20 +330,25 @@ FIRM_API int mode_is_datab (const ir_mode *mode);
 FIRM_API int mode_is_dataM (const ir_mode *mode);
 
 /**
- * Returns true if sm can be converted to lm without loss
- * according to firm definition.
+ * Returns true if a value of mode @p sm can be converted to mode @p lm without
+ * loss.
  *
- * Note that mode_Iu is NOT smaller than mode_Is here.
+ * That is the interpretation of the numbers does not changes, so you a signed
+ * integer mode is never smaller than an unsigned integer mode since the
+ * unsigned mode can't represent negative numbers in a way that they are
+ * interpreted as negative numbers.
  *
  * @see values_in_mode()
  */
 FIRM_API int smaller_mode(const ir_mode *sm, const ir_mode *lm);
 
 /**
- * Returns true if a value of mode sm can be converted into mode lm
- * and backwards without loss.
+ * Returns true if no information is lost when converting a value of mode @p sm
+ * into mode @p lm (and back to mode @p sm).
  *
- * Note that mode_Iu values CAN be converted in mode_Is and back.
+ * So the interpretation of the values may change in the intermediate mode @p sm
+ * (for example when converting negative signed integer numbers into unsigned
+ * integers) but after a conversion back they are exactly the same value.
  *
  * @see smaller_mode()
  */
index 736d3de..f416054 100644 (file)
@@ -506,28 +506,34 @@ int smaller_mode(const ir_mode *sm, const ir_mode *lm)
 
 int values_in_mode(const ir_mode *sm, const ir_mode *lm)
 {
-       ir_mode_arithmetic arith;
-
-       assert(sm);
-       assert(lm);
-
-       if (sm == lm) return 1;
+       if (sm == lm)
+               return true;
 
        if (sm == mode_b)
-               return mode_is_int(lm);
-
-       arith = get_mode_arithmetic(sm);
-       if (arith != get_mode_arithmetic(lm))
-               return 0;
-
-       switch (arith) {
-               case irma_twos_complement:
-               case irma_ieee754:
+               return mode_is_int(lm) || mode_is_float(lm);
+
+       ir_mode_arithmetic larith = get_mode_arithmetic(lm);
+       ir_mode_arithmetic sarith = get_mode_arithmetic(sm);
+       switch (larith) {
+       case irma_x86_extended_float:
+       case irma_ieee754:
+               if (sarith == irma_ieee754 || sarith == irma_x86_extended_float) {
                        return get_mode_size_bits(sm) <= get_mode_size_bits(lm);
-
-               default:
-                       return 0;
+               } else if (sarith == irma_twos_complement) {
+                       unsigned int_mantissa   = get_mode_size_bits(sm) - (mode_is_signed(sm) ? 1 : 0);
+                       unsigned float_mantissa = get_mode_mantissa_size(lm) + 1;
+                       return int_mantissa <= float_mantissa;
+               }
+               break;
+       case irma_twos_complement:
+               if (sarith == irma_twos_complement) {
+                       return get_mode_size_bits(sm) <= get_mode_size_bits(lm);
+               }
+               break;
+       case irma_none:
+               break;
        }
+       return false;
 }
 
 ir_mode *get_reference_mode_signed_eq(ir_mode *mode)
index 0667f74..d37db02 100644 (file)
@@ -50,6 +50,7 @@
 #include "firm_types.h"
 #include "bitfiddle.h"
 #include "be.h"
+#include "error.h"
 
 #include "entity_t.h"
 
@@ -1109,34 +1110,10 @@ static ir_node *equivalent_node_Conv(ir_node *n)
                ir_node *b      = get_Conv_op(a);
                ir_mode *b_mode = get_irn_mode(b);
 
-               if (n_mode == b_mode) {
-                       if (n_mode == mode_b) {
-                               n = b; /* Convb(Conv*(xxxb(...))) == xxxb(...) */
-                               DBG_OPT_ALGSIM1(oldn, a, b, n, FS_OPT_CONV);
-                               return n;
-                       } else if (get_mode_arithmetic(n_mode) == get_mode_arithmetic(a_mode)) {
-                               if (values_in_mode(b_mode, a_mode)) {
-                                       n = b;        /* ConvS(ConvL(xxxS(...))) == xxxS(...) */
-                                       DBG_OPT_ALGSIM1(oldn, a, b, n, FS_OPT_CONV);
-                                       return n;
-                               }
-                       }
-                       if (mode_is_int(n_mode) && get_mode_arithmetic(a_mode) == irma_ieee754) {
-                               /* ConvI(ConvF(I)) -> I, iff float mantissa >= int mode */
-                               unsigned int_mantissa   = get_mode_size_bits(n_mode) - (mode_is_signed(n_mode) ? 1 : 0);
-                               unsigned float_mantissa = get_mode_mantissa_size(a_mode);
-
-                               if (float_mantissa >= int_mantissa) {
-                                       n = b;
-                                       DBG_OPT_ALGSIM1(oldn, a, b, n, FS_OPT_CONV);
-                                       return n;
-                               }
-                       }
-                       if (is_Conv(b) && smaller_mode(b_mode, a_mode)) {
-                               n = b; /* ConvA(ConvB(ConvA(...))) == ConvA(...) */
-                               DBG_OPT_ALGSIM1(oldn, a, b, n, FS_OPT_CONV);
-                               return n;
-                       }
+               if (n_mode == b_mode && values_in_mode(b_mode, a_mode)) {
+                       n = b;
+                       DBG_OPT_ALGSIM1(oldn, a, b, n, FS_OPT_CONV);
+                       return n;
                }
        }
        return n;
@@ -5487,6 +5464,77 @@ static ir_node *transform_node_Rotl(ir_node *n)
        return n;
 }
 
+/**
+ * returns mode size for may_leave_out_middle_mode
+ */
+static unsigned get_significand_size(ir_mode *mode)
+{
+       const ir_mode_arithmetic arithmetic = get_mode_arithmetic(mode);
+       switch (arithmetic) {
+       case irma_ieee754:
+       case irma_x86_extended_float:
+               return get_mode_mantissa_size(mode) + 1;
+       case irma_twos_complement:
+               return get_mode_size_bits(mode);
+       case irma_none:
+               panic("Conv node with irma_none mode?");
+       }
+       panic("unexpected mode_arithmetic in get_significand_size");
+}
+
+/**
+ * Returns true if a conversion from mode @p m0 to @p m1 has the same effect
+ * as converting from @p m0 to @p m1 and then to @p m2.
+ * Classifying the 3 modes as the big(b), middle(m) and small(s) mode this
+ * gives the following truth table:
+ * s -> b -> m  : true
+ * s -> m -> b  : !signed(s) || signed(m)
+ * m -> b -> s  : true
+ * m -> s -> b  : false
+ * b -> s -> m  : false
+ * b -> m -> s  : true
+ *
+ * s -> b -> b  : true
+ * s -> s -> b  : false
+ *
+ * additional float constraints:
+ * F -> F -> F: fine
+ * F -> I -> I: signedness of Is must match
+ * I -> F -> I: signedness of Is must match
+ * I -> I -> F: signedness of Is must match
+ * F -> I -> F: bad
+ * I -> F -> F: fine
+ * F -> F -> I: fine
+ * at least 1 float involved: signedness must match
+ */
+bool may_leave_out_middle_conv(ir_mode *m0, ir_mode *m1, ir_mode *m2)
+{
+       int n_floats = mode_is_float(m0) + mode_is_float(m1) + mode_is_float(m2);
+       if (n_floats == 1) {
+#if 0
+               int n_signed = mode_is_signed(m0) + mode_is_signed(m1)
+                            + mode_is_signed(m2);
+               /* we assume that float modes are always signed */
+               if ((n_signed & 1) != 1)
+                       return false;
+#else
+               /* because overflow gives strange results we don't touch this case */
+               return false;
+#endif
+       } else if (n_floats == 2 && !mode_is_float(m1)) {
+               return false;
+       }
+
+       unsigned size0 = get_significand_size(m0);
+       unsigned size1 = get_significand_size(m1);
+       unsigned size2 = get_significand_size(m2);
+       if (size1 < size2 && size0 >= size1)
+               return false;
+       if (size1 >= size2)
+               return true;
+       return !mode_is_signed(m0) || mode_is_signed(m1);
+}
+
 /**
  * Transform a Conv.
  */
@@ -5496,6 +5544,17 @@ static ir_node *transform_node_Conv(ir_node *n)
        ir_mode *mode = get_irn_mode(n);
        ir_node *a    = get_Conv_op(n);
 
+       if (is_Conv(a)) {
+               ir_mode *a_mode = get_irn_mode(a);
+               ir_node *b      = get_Conv_op(a);
+               ir_mode *b_mode = get_irn_mode(b);
+               if (may_leave_out_middle_conv(b_mode, a_mode, mode)) {
+                       dbg_info *dbgi  = get_irn_dbg_info(n);
+                       ir_node  *block = get_nodes_block(n);
+                       return new_rd_Conv(dbgi, block, b, mode);
+               }
+       }
+
        if (mode != mode_b && is_const_Phi(a)) {
                /* Do NOT optimize mode_b Conv's, this leads to remaining
                 * Phib nodes later, because the conv_b_lower operation
index effa924..ebf8cad 100644 (file)
@@ -138,6 +138,11 @@ ir_node *ir_get_abs_op(const ir_node *sel, ir_node *mux_false,
 bool ir_is_optimizable_mux(const ir_node *sel, const ir_node *mux_false,
                            const ir_node *mux_true);
 
+/**
+ * Returns true if Conv_m0(Conv_m1( x_m2)) is equivalent to Conv_m0(x_m2)
+ */
+bool may_leave_out_middle_conv(ir_mode *m0, ir_mode *m1, ir_mode *m2);
+
 void ir_register_opt_node_ops(void);
 
 #endif
index ee9b382..fac7e32 100644 (file)
@@ -109,7 +109,6 @@ static int get_conv_costs(const ir_node *node, ir_mode *dest_mode)
                return 0;
 
        if (is_Const(node)) {
-               /* TODO tarval module is incomplete and can't convert floats to ints */
                return conv_const_tv(node, dest_mode) == tarval_bad ? 1 : 0;
        }
 
@@ -155,10 +154,14 @@ static int get_conv_costs(const ir_node *node, ir_mode *dest_mode)
                ir_node *pred      = get_Conv_op(node);
                ir_mode *pred_mode = get_irn_mode(pred);
 
-               if (!values_in_mode(dest_mode, pred_mode)) {
+               if (smaller_mode(pred_mode, dest_mode)) {
+                       return get_conv_costs(get_Conv_op(node), dest_mode) - 1;
+               }
+               if (may_leave_out_middle_conv(pred_mode, mode, dest_mode)) {
+                       return 0;
+               } else {
                        return 1;
                }
-               return get_conv_costs(get_Conv_op(node), dest_mode) - 1;
        }
 
        if (!is_optimizable_node(node, dest_mode)) {
@@ -197,7 +200,6 @@ static ir_node *conv_transform(ir_node *node, ir_mode *dest_mode)
                return node;
 
        if (is_Const(node)) {
-               /* TODO tarval module is incomplete and can't convert floats to ints */
                ir_tarval *tv = conv_const_tv(node, dest_mode);
                if (tv == tarval_bad) {
                        return place_conv(node, dest_mode);
@@ -224,10 +226,10 @@ static ir_node *conv_transform(ir_node *node, ir_mode *dest_mode)
                ir_node *pred      = get_Conv_op(node);
                ir_mode *pred_mode = get_irn_mode(pred);
 
-               if (!values_in_mode(dest_mode, pred_mode)) {
-                       return place_conv(node, dest_mode);
+               if (smaller_mode(pred_mode, dest_mode)) {
+                       return conv_transform(get_Conv_op(node), dest_mode);
                }
-               return conv_transform(get_Conv_op(node), dest_mode);
+               return place_conv(node, dest_mode);
        }
 
        if (!is_optimizable_node(node, dest_mode)) {