diff options
author | rth <rth@138bc75d-0d04-0410-961f-82ee72b054a4> | 2007-02-13 16:42:12 +0000 |
---|---|---|
committer | rth <rth@138bc75d-0d04-0410-961f-82ee72b054a4> | 2007-02-13 16:42:12 +0000 |
commit | 1b5539c804940cf6d88e41d4484428c208edccd3 (patch) | |
tree | e19c66e1c3488380d7c834096930e84a64744608 /gcc/optabs.c | |
parent | 1aff3f782d9872f361915303bb3693b87b4c737a (diff) | |
download | gcc-1b5539c804940cf6d88e41d4484428c208edccd3.tar.gz |
* config/i386/i386.md (bswapsi_1): Rename from bswapsi2,
remove flags clobber.
(bswapsi2): New expander, emit code for !TARGET_BSWAP.
(bswaphi_lowpart): New.
(bswapdi2): Rename from bswapdi2_rex, remove flags clobber,
remove TARGET_BSWAP test. Delete expander of the same name.
* optabs.c (widen_bswap, expand_doubleword_bswap): New.
(expand_unop): Use them.
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@121884 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc/optabs.c')
-rw-r--r-- | gcc/optabs.c | 85 |
1 files changed, 83 insertions, 2 deletions
diff --git a/gcc/optabs.c b/gcc/optabs.c index e66c115c0c0..c8ec9ef05a5 100644 --- a/gcc/optabs.c +++ b/gcc/optabs.c @@ -2401,6 +2401,73 @@ widen_clz (enum machine_mode mode, rtx op0, rtx target) return 0; } +/* Try calculating + (bswap:narrow x) + as + (lshiftrt:wide (bswap:wide x) ((width wide) - (width narrow))). */ +static rtx +widen_bswap (enum machine_mode mode, rtx op0, rtx target) +{ + enum mode_class class = GET_MODE_CLASS (mode); + enum machine_mode wider_mode; + rtx x, last; + + if (!CLASS_HAS_WIDER_MODES_P (class)) + return NULL_RTX; + + for (wider_mode = GET_MODE_WIDER_MODE (mode); + wider_mode != VOIDmode; + wider_mode = GET_MODE_WIDER_MODE (wider_mode)) + if (bswap_optab->handlers[wider_mode].insn_code != CODE_FOR_nothing) + goto found; + return NULL_RTX; + + found: + last = get_last_insn (); + + x = widen_operand (op0, wider_mode, mode, true, true); + x = expand_unop (wider_mode, bswap_optab, x, NULL_RTX, true); + + if (x != 0) + x = expand_shift (RSHIFT_EXPR, wider_mode, x, + size_int (GET_MODE_BITSIZE (wider_mode) + - GET_MODE_BITSIZE (mode)), + NULL_RTX, true); + + if (x != 0) + { + if (target == 0) + target = gen_reg_rtx (mode); + emit_move_insn (target, gen_lowpart (mode, x)); + } + else + delete_insns_since (last); + + return target; +} + +/* Try calculating bswap as two bswaps of two word-sized operands. */ + +static rtx +expand_doubleword_bswap (enum machine_mode mode, rtx op, rtx target) +{ + rtx t0, t1; + + t1 = expand_unop (word_mode, bswap_optab, + operand_subword_force (op, 0, mode), NULL_RTX, true); + t0 = expand_unop (word_mode, bswap_optab, + operand_subword_force (op, 1, mode), NULL_RTX, true); + + if (target == 0) + target = gen_reg_rtx (mode); + if (REG_P (target)) + emit_insn (gen_rtx_CLOBBER (VOIDmode, target)); + emit_move_insn (operand_subword (target, 0, 1, mode), t0); + emit_move_insn (operand_subword (target, 1, 1, mode), t1); + + return target; +} + /* Try calculating (parity x) as (and (popcount x) 1), where popcount can also be done in a wider mode. */ static rtx @@ -2639,9 +2706,23 @@ expand_unop (enum machine_mode mode, optab unoptab, rtx op0, rtx target, goto try_libcall; } - /* We can't widen a bswap. */ + /* Widening (or narrowing) bswap needs special treatment. */ if (unoptab == bswap_optab) - goto try_libcall; + { + temp = widen_bswap (mode, op0, target); + if (temp) + return temp; + + if (GET_MODE_SIZE (mode) == 2 * UNITS_PER_WORD + && unoptab->handlers[word_mode].insn_code != CODE_FOR_nothing) + { + temp = expand_doubleword_bswap (mode, op0, target); + if (temp) + return temp; + } + + goto try_libcall; + } if (CLASS_HAS_WIDER_MODES_P (class)) for (wider_mode = GET_MODE_WIDER_MODE (mode); |