summaryrefslogtreecommitdiff
path: root/gcc/optabs.c
diff options
context:
space:
mode:
authorkenner <kenner@138bc75d-0d04-0410-961f-82ee72b054a4>1994-11-15 23:37:55 +0000
committerkenner <kenner@138bc75d-0d04-0410-961f-82ee72b054a4>1994-11-15 23:37:55 +0000
commitb4827f8de74d3b575e7a5c0a43351ab0899766e7 (patch)
treeb25c8a1b9ceb9e2b506ce5b67d16a717da2f1e2b /gcc/optabs.c
parent0536656d5fa0d41eae20d8e16f43256cace77e87 (diff)
downloadgcc-b4827f8de74d3b575e7a5c0a43351ab0899766e7.tar.gz
(expand_abs): New function, code moved from expand_expr.
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@8444 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc/optabs.c')
-rw-r--r--gcc/optabs.c84
1 files changed, 84 insertions, 0 deletions
diff --git a/gcc/optabs.c b/gcc/optabs.c
index 2d188e66520..db68da8f9fb 100644
--- a/gcc/optabs.c
+++ b/gcc/optabs.c
@@ -2051,6 +2051,90 @@ expand_unop (mode, unoptab, op0, target, unsignedp)
MODE is the mode of the operand; the mode of the result is
different but can be deduced from MODE.
+ UNSIGNEDP is relevant if extension is needed. */
+
+rtx
+expand_abs (mode, op0, target, unsignedp, safe)
+ enum machine_mode mode;
+ rtx op0;
+ rtx target;
+ int unsignedp;
+ int safe;
+{
+ rtx temp, op1;
+
+ /* First try to do it with a special abs instruction. */
+ temp = expand_unop (mode, abs_optab, op0, target, 0);
+ if (temp != 0)
+ return temp;
+
+ /* If this machine has expensive jumps, we can do integer absolute
+ value of X as (((signed) x >> (W-1)) ^ x) - ((signed) x >> (W-1)),
+ where W is the width of MODE. */
+
+ if (GET_MODE_CLASS (mode) == MODE_INT && BRANCH_COST >= 2)
+ {
+ rtx extended = expand_shift (RSHIFT_EXPR, mode, op0,
+ size_int (GET_MODE_BITSIZE (mode) - 1),
+ NULL_RTX, 0);
+
+ temp = expand_binop (mode, xor_optab, extended, op0, target, 0,
+ OPTAB_LIB_WIDEN);
+ if (temp != 0)
+ temp = expand_binop (mode, sub_optab, temp, extended, target, 0,
+ OPTAB_LIB_WIDEN);
+
+ if (temp != 0)
+ return temp;
+ }
+
+ /* If that does not win, use conditional jump and negate. */
+ op1 = gen_label_rtx ();
+ if (target == 0 || ! safe
+ || GET_MODE (target) != mode
+ || (GET_CODE (target) == MEM && MEM_VOLATILE_P (target))
+ || (GET_CODE (target) == REG
+ && REGNO (target) < FIRST_PSEUDO_REGISTER))
+ target = gen_reg_rtx (mode);
+
+ emit_move_insn (target, op0);
+ NO_DEFER_POP;
+
+ /* If this mode is an integer too wide to compare properly,
+ compare word by word. Rely on CSE to optimize constant cases. */
+ if (GET_MODE_CLASS (mode) == MODE_INT && ! can_compare_p (mode))
+ do_jump_by_parts_greater_rtx (mode, 0, target, const0_rtx,
+ NULL_RTX, op1);
+ else
+ {
+ temp = compare_from_rtx (target, CONST0_RTX (mode), GE, 0, mode,
+ NULL_RTX, 0);
+ if (temp == const1_rtx)
+ return target;
+ else if (temp != const0_rtx)
+ {
+ if (bcc_gen_fctn[(int) GET_CODE (temp)] != 0)
+ emit_jump_insn ((*bcc_gen_fctn[(int) GET_CODE (temp)]) (op1));
+ else
+ abort ();
+ }
+ }
+
+ op0 = expand_unop (mode, neg_optab, target, target, 0);
+ if (op0 != target)
+ emit_move_insn (target, op0);
+ emit_label (op1);
+ OK_DEFER_POP;
+ return target;
+}
+
+/* Emit code to compute the absolute value of OP0, with result to
+ TARGET if convenient. (TARGET may be 0.) The return value says
+ where the result actually is to be found.
+
+ MODE is the mode of the operand; the mode of the result is
+ different but can be deduced from MODE.
+
UNSIGNEDP is relevant for complex integer modes. */
rtx