diff options
author | Richard Henderson <rth@redhat.com> | 2012-07-10 01:25:20 -0700 |
---|---|---|
committer | Richard Henderson <rth@gcc.gnu.org> | 2012-07-10 01:25:20 -0700 |
commit | 00f07b86e7d7c80695fb655738b89d893b54fc9a (patch) | |
tree | 92ca34cfac26d6253011a1acbd60501bca8cdf7a /gcc/optabs.c | |
parent | 9283726f7623410f58bf23da7d7589c7b261b449 (diff) | |
download | gcc-00f07b86e7d7c80695fb655738b89d893b54fc9a.zip gcc-00f07b86e7d7c80695fb655738b89d893b54fc9a.tar.gz gcc-00f07b86e7d7c80695fb655738b89d893b54fc9a.tar.bz2 |
Move vector highpart emulation to the optabs layer
* expmed.c (expmed_mult_highpart): Rename from expand_mult_highpart.
(expmed_mult_highpart_optab): Rename from expand_mult_highpart_optab.
* optabs.c (can_mult_highpart_p): New.
(expand_mult_highpart): New.
* expr.c (expand_expr_real_2) [MULT_HIGHPART_EXPR): Use it.
* tree-vect-generic.c (expand_vector_operations_1): Don't expand
by pieces if can_mult_highpart_p.
(expand_vector_divmod): Use can_mult_highpart_p and always
generate MULT_HIGHPART_EXPR.
* tree-vect-patterns.c (vect_recog_divmod_pattern): Likewise.
* tree-vect-stmts.c (vectorizable_operation): Likewise.
From-SVN: r189407
Diffstat (limited to 'gcc/optabs.c')
-rw-r--r-- | gcc/optabs.c | 126 |
1 files changed, 126 insertions, 0 deletions
diff --git a/gcc/optabs.c b/gcc/optabs.c index fbea879..e1ecc65 100644 --- a/gcc/optabs.c +++ b/gcc/optabs.c @@ -7162,6 +7162,132 @@ expand_vec_cond_expr (tree vec_cond_type, tree op0, tree op1, tree op2, return ops[0].value; } +/* Return non-zero if a highpart multiply is supported of can be synthisized. + For the benefit of expand_mult_highpart, the return value is 1 for direct, + 2 for even/odd widening, and 3 for hi/lo widening. */ + +int +can_mult_highpart_p (enum machine_mode mode, bool uns_p) +{ + optab op; + unsigned char *sel; + unsigned i, nunits; + + op = uns_p ? umul_highpart_optab : smul_highpart_optab; + if (optab_handler (op, mode) != CODE_FOR_nothing) + return 1; + + /* If the mode is an integral vector, synth from widening operations. */ + if (GET_MODE_CLASS (mode) != MODE_VECTOR_INT) + return 0; + + nunits = GET_MODE_NUNITS (mode); + sel = XALLOCAVEC (unsigned char, nunits); + + op = uns_p ? vec_widen_umult_even_optab : vec_widen_smult_even_optab; + if (optab_handler (op, mode) != CODE_FOR_nothing) + { + op = uns_p ? vec_widen_umult_odd_optab : vec_widen_smult_odd_optab; + if (optab_handler (op, mode) != CODE_FOR_nothing) + { + for (i = 0; i < nunits; ++i) + sel[i] = !BYTES_BIG_ENDIAN + (i & ~1) + ((i & 1) ? nunits : 0); + if (can_vec_perm_p (mode, false, sel)) + return 2; + } + } + + op = uns_p ? vec_widen_umult_hi_optab : vec_widen_smult_hi_optab; + if (optab_handler (op, mode) != CODE_FOR_nothing) + { + op = uns_p ? vec_widen_umult_lo_optab : vec_widen_smult_lo_optab; + if (optab_handler (op, mode) != CODE_FOR_nothing) + { + for (i = 0; i < nunits; ++i) + sel[i] = 2 * i + (BYTES_BIG_ENDIAN ? 0 : 1); + if (can_vec_perm_p (mode, false, sel)) + return 3; + } + } + + return 0; +} + +/* Expand a highpart multiply. */ + +rtx +expand_mult_highpart (enum machine_mode mode, rtx op0, rtx op1, + rtx target, bool uns_p) +{ + struct expand_operand eops[3]; + enum insn_code icode; + int method, i, nunits; + enum machine_mode wmode; + rtx m1, m2, perm; + optab tab1, tab2; + rtvec v; + + method = can_mult_highpart_p (mode, uns_p); + switch (method) + { + case 0: + return NULL_RTX; + case 1: + tab1 = uns_p ? umul_highpart_optab : smul_highpart_optab; + return expand_binop (mode, tab1, op0, op1, target, uns_p, + OPTAB_LIB_WIDEN); + case 2: + tab1 = uns_p ? vec_widen_umult_even_optab : vec_widen_smult_even_optab; + tab2 = uns_p ? vec_widen_umult_odd_optab : vec_widen_smult_odd_optab; + break; + case 3: + tab1 = uns_p ? vec_widen_umult_lo_optab : vec_widen_smult_lo_optab; + tab2 = uns_p ? vec_widen_umult_hi_optab : vec_widen_smult_hi_optab; + if (BYTES_BIG_ENDIAN) + { + optab t = tab1; + tab1 = tab2; + tab2 = t; + } + break; + default: + gcc_unreachable (); + } + + icode = optab_handler (tab1, mode); + nunits = GET_MODE_NUNITS (mode); + wmode = insn_data[icode].operand[0].mode; + gcc_checking_assert (2 * GET_MODE_NUNITS (wmode) == nunits); + gcc_checking_assert (GET_MODE_SIZE (wmode) == GET_MODE_SIZE (mode)); + + create_output_operand (&eops[0], gen_reg_rtx (wmode), wmode); + create_input_operand (&eops[1], op0, mode); + create_input_operand (&eops[2], op1, mode); + expand_insn (icode, 3, eops); + m1 = gen_lowpart (mode, eops[0].value); + + create_output_operand (&eops[0], gen_reg_rtx (wmode), wmode); + create_input_operand (&eops[1], op0, mode); + create_input_operand (&eops[2], op1, mode); + expand_insn (optab_handler (tab2, mode), 3, eops); + m2 = gen_lowpart (mode, eops[0].value); + + v = rtvec_alloc (nunits); + if (method == 2) + { + for (i = 0; i < nunits; ++i) + RTVEC_ELT (v, i) = GEN_INT (!BYTES_BIG_ENDIAN + (i & ~1) + + ((i & 1) ? nunits : 0)); + } + else + { + for (i = 0; i < nunits; ++i) + RTVEC_ELT (v, i) = GEN_INT (2 * i + (BYTES_BIG_ENDIAN ? 0 : 1)); + } + perm = gen_rtx_CONST_VECTOR (mode, v); + + return expand_vec_perm (mode, m1, m2, perm, target); +} /* Return true if there is a compare_and_swap pattern. */ |