Commit cbad497b authored by Matthias Braun's avatar Matthias Braun
Browse files

add more Conv(Conv) localopt rules

parent ef3e7db6
......@@ -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()
*/
......
......@@ -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)
......
......@@ -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
......
......@@ -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
......@@ -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)) {
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment