+/**
+ * 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) {
+ /* because overflow gives strange results we don't touch this case */
+ return false;
+ } 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);
+}
+