diff options
Diffstat (limited to 'gcc/tree-vect-patterns.c')
-rw-r--r-- | gcc/tree-vect-patterns.c | 98 |
1 files changed, 74 insertions, 24 deletions
diff --git a/gcc/tree-vect-patterns.c b/gcc/tree-vect-patterns.c index 39b6f82..b216012 100644 --- a/gcc/tree-vect-patterns.c +++ b/gcc/tree-vect-patterns.c @@ -720,34 +720,60 @@ vect_convert_input (stmt_vec_info stmt_info, tree type, if (TREE_CODE (unprom->op) == INTEGER_CST) return wide_int_to_tree (type, wi::to_widest (unprom->op)); - /* See if we can reuse an existing result. */ + tree input = unprom->op; if (unprom->caster) { tree lhs = gimple_get_lhs (unprom->caster->stmt); - if (types_compatible_p (TREE_TYPE (lhs), type)) - return lhs; + tree lhs_type = TREE_TYPE (lhs); + + /* If the result of the existing cast is the right width, use it + instead of the source of the cast. */ + if (TYPE_PRECISION (lhs_type) == TYPE_PRECISION (type)) + input = lhs; + /* If the precision we want is between the source and result + precisions of the existing cast, try splitting the cast into + two and tapping into a mid-way point. */ + else if (TYPE_PRECISION (lhs_type) > TYPE_PRECISION (type) + && TYPE_PRECISION (type) > TYPE_PRECISION (unprom->type)) + { + /* In order to preserve the semantics of the original cast, + give the mid-way point the same signedness as the input value. + + It would be possible to use a signed type here instead if + TYPE is signed and UNPROM->TYPE is unsigned, but that would + make the sign of the midtype sensitive to the order in + which we process the statements, since the signedness of + TYPE is the signedness required by just one of possibly + many users. Also, unsigned promotions are usually as cheap + as or cheaper than signed ones, so it's better to keep an + unsigned promotion. */ + tree midtype = build_nonstandard_integer_type + (TYPE_PRECISION (type), TYPE_UNSIGNED (unprom->type)); + tree vec_midtype = get_vectype_for_scalar_type (midtype); + if (vec_midtype) + { + input = vect_recog_temp_ssa_var (midtype, NULL); + gassign *new_stmt = gimple_build_assign (input, NOP_EXPR, + unprom->op); + if (!vect_split_statement (unprom->caster, input, new_stmt, + vec_midtype)) + append_pattern_def_seq (stmt_info, new_stmt, vec_midtype); + } + } + + /* See if we can reuse an existing result. */ + if (types_compatible_p (type, TREE_TYPE (input))) + return input; } /* We need a new conversion statement. */ tree new_op = vect_recog_temp_ssa_var (type, NULL); - gassign *new_stmt = gimple_build_assign (new_op, NOP_EXPR, unprom->op); - - /* If the operation is the input to a vectorizable cast, try splitting - that cast into two, taking the required result as a mid-way point. */ - if (unprom->caster) - { - tree lhs = gimple_get_lhs (unprom->caster->stmt); - if (TYPE_PRECISION (TREE_TYPE (lhs)) > TYPE_PRECISION (type) - && TYPE_PRECISION (type) > TYPE_PRECISION (unprom->type) - && (TYPE_UNSIGNED (unprom->type) || !TYPE_UNSIGNED (type)) - && vect_split_statement (unprom->caster, new_op, new_stmt, vectype)) - return new_op; - } + gassign *new_stmt = gimple_build_assign (new_op, NOP_EXPR, input); /* If OP is an external value, see if we can insert the new statement on an incoming edge. */ - if (unprom->dt == vect_external_def) - if (edge e = vect_get_external_def_edge (stmt_info->vinfo, unprom->op)) + if (input == unprom->op && unprom->dt == vect_external_def) + if (edge e = vect_get_external_def_edge (stmt_info->vinfo, input)) { basic_block new_bb = gsi_insert_on_edge_immediate (e, new_stmt); gcc_assert (!new_bb); @@ -1644,28 +1670,37 @@ vect_recog_over_widening_pattern (stmt_vec_info last_stmt_info, tree *type_out) bool unsigned_p = (last_stmt_info->operation_sign == UNSIGNED); tree new_type = build_nonstandard_integer_type (new_precision, unsigned_p); + /* If we're truncating an operation, we need to make sure that we + don't introduce new undefined overflow. The codes tested here are + a subset of those accepted by vect_truncatable_operation_p. */ + tree op_type = new_type; + if (TYPE_OVERFLOW_UNDEFINED (new_type) + && (code == PLUS_EXPR || code == MINUS_EXPR || code == MULT_EXPR)) + op_type = build_nonstandard_integer_type (new_precision, true); + /* We specifically don't check here whether the target supports the new operation, since it might be something that a later pattern wants to rewrite anyway. If targets have a minimum element size for some optabs, we should pattern-match smaller ops to larger ops where beneficial. */ tree new_vectype = get_vectype_for_scalar_type (new_type); - if (!new_vectype) + tree op_vectype = get_vectype_for_scalar_type (op_type); + if (!new_vectype || !op_vectype) return NULL; if (dump_enabled_p ()) dump_printf_loc (MSG_NOTE, vect_location, "demoting %T to %T\n", type, new_type); - /* Calculate the rhs operands for an operation on NEW_TYPE. */ + /* Calculate the rhs operands for an operation on OP_TYPE. */ tree ops[3] = {}; for (unsigned int i = 1; i < first_op; ++i) ops[i - 1] = gimple_op (last_stmt, i); vect_convert_inputs (last_stmt_info, nops, &ops[first_op - 1], - new_type, &unprom[0], new_vectype); + op_type, &unprom[0], op_vectype); - /* Use the operation to produce a result of type NEW_TYPE. */ - tree new_var = vect_recog_temp_ssa_var (new_type, NULL); + /* Use the operation to produce a result of type OP_TYPE. */ + tree new_var = vect_recog_temp_ssa_var (op_type, NULL); gimple *pattern_stmt = gimple_build_assign (new_var, code, ops[0], ops[1], ops[2]); gimple_set_location (pattern_stmt, gimple_location (last_stmt)); @@ -1674,6 +1709,13 @@ vect_recog_over_widening_pattern (stmt_vec_info last_stmt_info, tree *type_out) dump_printf_loc (MSG_NOTE, vect_location, "created pattern stmt: %G", pattern_stmt); + /* Convert back to the original signedness, if OP_TYPE is different + from NEW_TYPE. */ + if (op_type != new_type) + pattern_stmt = vect_convert_output (last_stmt_info, new_type, + pattern_stmt, op_vectype); + + /* Promote the result to the original type. */ pattern_stmt = vect_convert_output (last_stmt_info, type, pattern_stmt, new_vectype); @@ -1719,8 +1761,16 @@ vect_recog_average_pattern (stmt_vec_info last_stmt_info, tree *type_out) if (!INTEGRAL_TYPE_P (type) || target_precision >= TYPE_PRECISION (type)) return NULL; - /* Get the definition of the shift input. */ + /* Look through any change in sign on the shift input. */ tree rshift_rhs = gimple_assign_rhs1 (last_stmt); + vect_unpromoted_value unprom_plus; + rshift_rhs = vect_look_through_possible_promotion (vinfo, rshift_rhs, + &unprom_plus); + if (!rshift_rhs + || TYPE_PRECISION (TREE_TYPE (rshift_rhs)) != TYPE_PRECISION (type)) + return NULL; + + /* Get the definition of the shift input. */ stmt_vec_info plus_stmt_info = vect_get_internal_def (vinfo, rshift_rhs); if (!plus_stmt_info) return NULL; |