OSDN Git Service

* ifcvt.c (struct noce_if_info): Add test_bb.
authorrth <rth@138bc75d-0d04-0410-961f-82ee72b054a4>
Sun, 25 Feb 2001 19:34:06 +0000 (19:34 +0000)
committerrth <rth@138bc75d-0d04-0410-961f-82ee72b054a4>
Sun, 25 Feb 2001 19:34:06 +0000 (19:34 +0000)
        (noce_get_alt_condition): New.
        (noce_try_minmax, noce_try_abs): New.
        (noce_operand_ok): New.
        (noce_process_if_block): Use them.
        * rtlanal.c (may_trap_p): NEG and ABS can never trap.

git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@40060 138bc75d-0d04-0410-961f-82ee72b054a4

gcc/ChangeLog
gcc/ifcvt.c
gcc/rtlanal.c

index 27c6737..3648e8a 100644 (file)
@@ -1,3 +1,12 @@
+2001-02-25  Richard Henderson  <rth@redhat.com>
+
+       * ifcvt.c (struct noce_if_info): Add test_bb.
+       (noce_get_alt_condition): New.
+       (noce_try_minmax, noce_try_abs): New.
+       (noce_operand_ok): New.
+       (noce_process_if_block): Use them.
+       * rtlanal.c (may_trap_p): NEG and ABS can never trap.
+
 Sun Feb 25 14:26:17 2001  Christopher Faylor <cgf@cygnus.com>
 
        * config/i386/cygwin.h (CPP_SPEC): Add missing space before w32api
@@ -19,18 +28,18 @@ Sun Feb 25 08:34:23 2001  Richard Kenner  <kenner@vlsi1.ultra.nyu.edu>
 
 2001-02-25  Neil Booth  <neil@daikokuya.demon.co.uk>
 
-        * cppinit.c (builtin_array): Update.
-        (init_builtins): Flag builtins to warn if redefined or
-        undefined.  Define __GXX_WEAK as a normal macro.
-        * cpplib.c (do_undef): Warn if flagged NODE_WARN.
-        * cpplib.h (NODE_WARN): New flag.
-        * cppmacro.c (builtin_macro): Remove handling of __GXX_WEAK__.
-        Handle __STDC__ as a builtin only on Solaris.
-        (warn_of_redefinition): Renamed from check_macro_definition.
-        Reverse sense of test.  Always warn if NODE_WARN.
-        (_cpp_create_definition): Use warn_of_redefinition.  Flag
-        any macro beginning with "__STDC_" to require a mandatory
-        warning if redefined or undefined.
+       * cppinit.c (builtin_array): Update.
+       (init_builtins): Flag builtins to warn if redefined or
+       undefined.  Define __GXX_WEAK as a normal macro.
+       * cpplib.c (do_undef): Warn if flagged NODE_WARN.
+       * cpplib.h (NODE_WARN): New flag.
+       * cppmacro.c (builtin_macro): Remove handling of __GXX_WEAK__.
+       Handle __STDC__ as a builtin only on Solaris.
+       (warn_of_redefinition): Renamed from check_macro_definition.
+       Reverse sense of test.  Always warn if NODE_WARN.
+       (_cpp_create_definition): Use warn_of_redefinition.  Flag
+       any macro beginning with "__STDC_" to require a mandatory
+       warning if redefined or undefined.
 
 2001-02-24  Zack Weinberg  <zackw@stanford.edu>
 
index a5494b8..c5778e1 100644 (file)
@@ -30,6 +30,7 @@
 #include "hard-reg-set.h"
 #include "basic-block.h"
 #include "expr.h"
+#include "real.h"
 #include "output.h"
 #include "tm_p.h"
 
@@ -79,6 +80,7 @@ static int cond_exec_process_if_block PARAMS ((basic_block, basic_block,
                                                 basic_block, basic_block));
 
 static rtx noce_get_condition          PARAMS ((rtx, rtx *));
+static int noce_operand_ok             PARAMS ((rtx));
 static int noce_process_if_block       PARAMS ((basic_block, basic_block,
                                                 basic_block, basic_block));
 
@@ -451,6 +453,7 @@ cond_exec_process_if_block (test_bb, then_bb, else_bb, join_bb)
 
 struct noce_if_info
 {
+  basic_block test_bb;
   rtx insn_a, insn_b;
   rtx x, a, b;
   rtx jump, cond, cond_earliest;
@@ -467,6 +470,10 @@ static rtx noce_emit_cmove         PARAMS ((struct noce_if_info *,
                                                 rtx, rtx, rtx));
 static int noce_try_cmove              PARAMS ((struct noce_if_info *));
 static int noce_try_cmove_arith                PARAMS ((struct noce_if_info *));
+static rtx noce_get_alt_condition      PARAMS ((struct noce_if_info *,
+                                                rtx, rtx *));
+static int noce_try_minmax             PARAMS ((struct noce_if_info *));
+static int noce_try_abs                        PARAMS ((struct noce_if_info *));
 
 /* Helper function for noce_try_store_flag*.  */
 
@@ -1066,6 +1073,274 @@ noce_try_cmove_arith (if_info)
   return FALSE;
 }
 
+/* For most cases, the simplified condition we found is the best
+   choice, but this is not the case for the min/max/abs transforms.
+   For these we wish to know that it is A or B in the condition.  */
+
+static rtx
+noce_get_alt_condition (if_info, target, earliest)
+     struct noce_if_info *if_info;
+     rtx target;
+     rtx *earliest;
+{
+  rtx cond, set, insn;
+  int reverse;
+
+  /* If target is already mentioned in the known condition, return it.  */
+  if (reg_mentioned_p (target, if_info->cond))
+    {
+      *earliest = if_info->cond_earliest;
+      return if_info->cond;
+    }
+
+  set = pc_set (if_info->jump);
+  cond = XEXP (SET_SRC (set), 0);
+  reverse
+    = GET_CODE (XEXP (SET_SRC (set), 2)) == LABEL_REF
+      && XEXP (XEXP (SET_SRC (set), 2), 0) == JUMP_LABEL (if_info->jump);
+
+  cond = canonicalize_condition (if_info->jump, cond, reverse,
+                                earliest, target);
+  if (! cond || ! reg_mentioned_p (target, cond))
+    return NULL;
+
+  /* We almost certainly searched back to a different place.
+     Need to re-verify correct lifetimes.  */
+
+  /* X may not be mentioned in the range (cond_earliest, jump].  */
+  for (insn = if_info->jump; insn != *earliest; insn = PREV_INSN (insn))
+    if (INSN_P (insn) && reg_mentioned_p (if_info->x, insn))
+      return NULL;
+
+  /* A and B may not be modified in the range [cond_earliest, jump).  */
+  for (insn = *earliest; insn != if_info->jump; insn = NEXT_INSN (insn))
+    if (INSN_P (insn)
+       && (modified_in_p (if_info->a, insn)
+           || modified_in_p (if_info->b, insn)))
+      return NULL;
+
+  return cond;
+}
+
+/* Convert "if (a < b) x = a; else x = b;" to "x = min(a, b);", etc.  */
+
+static int
+noce_try_minmax (if_info)
+     struct noce_if_info *if_info;
+{ 
+  rtx cond, earliest, target, seq;
+  enum rtx_code code;
+  int unsignedp;
+  optab op;
+
+  /* ??? Can't guarantee that expand_binop won't create pseudos.  */
+  if (no_new_pseudos)
+    return FALSE;
+
+  /* ??? Reject FP modes since we don't know how 0 vs -0 or NaNs
+     will be resolved with an SMIN/SMAX.  It wouldn't be too hard
+     to get the target to tell us...  */
+  if (FLOAT_MODE_P (GET_MODE (if_info->x))
+      && TARGET_FLOAT_FORMAT == IEEE_FLOAT_FORMAT
+      && ! flag_fast_math)
+    return FALSE;
+
+  cond = noce_get_alt_condition (if_info, if_info->a, &earliest);
+  if (!cond)
+    return FALSE;
+
+  /* Verify the condition is of the form we expect, and canonicalize
+     the comparison code.  */
+  code = GET_CODE (cond);
+  if (rtx_equal_p (XEXP (cond, 0), if_info->a))
+    {
+      if (! rtx_equal_p (XEXP (cond, 1), if_info->b))
+       return FALSE;
+    }
+  else if (rtx_equal_p (XEXP (cond, 1), if_info->a))
+    {
+      if (! rtx_equal_p (XEXP (cond, 0), if_info->b))
+       return FALSE;
+      code = swap_condition (code);
+    }
+  else
+    return FALSE;
+
+  /* Determine what sort of operation this is.  Note that the code is for
+     a taken branch, so the code->operation mapping appears backwards.  */
+  switch (code)
+    {
+    case LT:
+    case LE:
+    case UNLT:
+    case UNLE:
+      op = smax_optab;
+      unsignedp = 0;
+      break;
+    case GT:
+    case GE:
+    case UNGT:
+    case UNGE:
+      op = smin_optab;
+      unsignedp = 0;
+      break;
+    case LTU:
+    case LEU:
+      op = umax_optab;
+      unsignedp = 1;
+      break;
+    case GTU:
+    case GEU:
+      op = umin_optab;
+      unsignedp = 1;
+      break;
+    default:
+      return FALSE;
+    }
+
+  start_sequence ();
+
+  target = expand_binop (GET_MODE (if_info->x), op, if_info->a, if_info->b,
+                        if_info->x, unsignedp, OPTAB_WIDEN);
+  if (! target)
+    {
+      end_sequence ();
+      return FALSE;
+    }
+  if (target != if_info->x)
+    emit_move_insn (if_info->x, target);
+
+  seq = get_insns ();
+  end_sequence ();  
+
+  if (seq_contains_jump (seq))
+    return FALSE;
+
+  emit_insns_before (seq, earliest);
+  if_info->cond = cond;
+  if_info->cond_earliest = earliest;
+
+  return TRUE;
+}
+
+/* Convert "if (a < 0) x = -a; else x = a;" to "x = abs(a);", etc.  */
+
+static int
+noce_try_abs (if_info)
+     struct noce_if_info *if_info;
+{ 
+  rtx cond, earliest, target, seq, a, b, c;
+  int negate;
+
+  /* ??? Can't guarantee that expand_binop won't create pseudos.  */
+  if (no_new_pseudos)
+    return FALSE;
+
+  /* Recognize A and B as constituting an ABS or NABS.  */
+  a = if_info->a;
+  b = if_info->b;
+  if (GET_CODE (a) == NEG && rtx_equal_p (XEXP (a, 0), b))
+    negate = 0;
+  else if (GET_CODE (b) == NEG && rtx_equal_p (XEXP (b, 0), a))
+    {
+      c = a; a = b; b = c;
+      negate = 1;
+    }
+  else
+    return FALSE;
+   
+  cond = noce_get_alt_condition (if_info, b, &earliest);
+  if (!cond)
+    return FALSE;
+
+  /* Verify the condition is of the form we expect.  */
+  if (rtx_equal_p (XEXP (cond, 0), b))
+    c = XEXP (cond, 1);
+  else if (rtx_equal_p (XEXP (cond, 1), b))
+    c = XEXP (cond, 0);
+  else
+    return FALSE;
+
+  /* Verify that C is zero.  Search backward through the block for
+     a REG_EQUAL note if necessary.  */
+  if (REG_P (c))
+    {
+      rtx insn, note = NULL;
+      for (insn = earliest;
+          insn != if_info->test_bb->head;
+          insn = PREV_INSN (insn))
+       if (INSN_P (insn) 
+           && ((note = find_reg_note (insn, REG_EQUAL, c))
+               || (note = find_reg_note (insn, REG_EQUIV, c))))
+         break;
+      if (! note)
+       return FALSE;
+      c = XEXP (note, 0);
+    }
+  if (GET_CODE (c) == MEM
+      && GET_CODE (XEXP (c, 0)) == SYMBOL_REF
+      && CONSTANT_POOL_ADDRESS_P (XEXP (c, 0)))
+    c = get_pool_constant (XEXP (c, 0));
+
+  /* Work around funny ideas get_condition has wrt canonicalization.
+     Note that these rtx constants are known to be CONST_INT, and 
+     therefore imply integer comparisons.  */
+  if (c == constm1_rtx && GET_CODE (cond) == GT)
+    ;
+  else if (c == const1_rtx && GET_CODE (cond) == LT)
+    ;
+  else if (c != CONST0_RTX (GET_MODE (b)))
+    return FALSE;
+
+  /* Determine what sort of operation this is.  */
+  switch (GET_CODE (cond))
+    {
+    case LT:
+    case LE:
+    case UNLT:
+    case UNLE:
+      negate = !negate;
+      break;
+    case GT:
+    case GE:
+    case UNGT:
+    case UNGE:
+      break;
+    default:
+      return FALSE;
+    }
+
+  start_sequence ();
+
+  target = expand_unop (GET_MODE (if_info->x), abs_optab, b, if_info->x, 0);
+
+  /* ??? It's a quandry whether cmove would be better here, especially
+     for integers.  Perhaps combine will clean things up.  */
+  if (target && negate)
+    target = expand_unop (GET_MODE (target), neg_optab, target, if_info->x, 0);
+
+  if (! target)
+    {
+      end_sequence ();
+      return FALSE;
+    }
+
+  if (target != if_info->x)
+    emit_move_insn (if_info->x, target);
+
+  seq = get_insns ();
+  end_sequence ();  
+
+  if (seq_contains_jump (seq))
+    return FALSE;
+
+  emit_insns_before (seq, earliest);
+  if_info->cond = cond;
+  if_info->cond_earliest = earliest;
+
+  return TRUE;
+}
+
 /* Look for the condition for the jump first.  We'd prefer to avoid
    get_condition if we can -- it tries to look back for the contents
    of an original compare.  On targets that use normal integers for
@@ -1107,6 +1382,51 @@ noce_get_condition (jump, earliest)
   return cond;
 }
 
+/* Return true if OP is ok for if-then-else processing.  */
+
+static int
+noce_operand_ok (op)
+     rtx op;
+{
+  /* We special-case memories, so handle any of them with
+     no address side effects.  */
+  if (GET_CODE (op) == MEM)
+    return ! side_effects_p (XEXP (op, 0));
+
+  if (side_effects_p (op))
+    return FALSE;
+
+  /* ??? Unfortuantely may_trap_p can't look at flag_fast_math, due to
+     being linked into the genfoo programs.  This is probably a mistake.
+     With finite operands, most fp operations don't trap.  */
+  if (flag_fast_math && FLOAT_MODE_P (GET_MODE (op)))
+    switch (GET_CODE (op))
+      {
+      case DIV:
+      case MOD:
+      case UDIV:
+      case UMOD:
+       /* ??? This is kinda lame -- almost every target will have forced
+          the constant into a register first.  But given the expense of
+          division, this is probably for the best.  */
+       return (CONSTANT_P (XEXP (op, 1))
+               && XEXP (op, 1) != CONST0_RTX (GET_MODE (op))
+               && ! may_trap_p (XEXP (op, 0)));
+
+      default:
+       switch (GET_RTX_CLASS (GET_CODE (op)))
+         {
+         case 'c':
+         case '1':
+         case '2':
+           return ! may_trap_p (XEXP (op, 0)) && ! may_trap_p (XEXP (op, 1));
+         }
+       break;
+      }
+
+  return ! may_trap_p (op);
+}
+
 /* Given a simple IF-THEN or IF-THEN-ELSE block, attempt to convert it
    without using conditional execution.  Return TRUE if we were
    successful at converting the the block.  */
@@ -1214,12 +1534,11 @@ noce_process_if_block (test_bb, then_bb, else_bb, join_bb)
     }
 
   /* Don't operate on sources that may trap or are volatile.  */
-  if (side_effects_p (a) || side_effects_p (b)
-      || (GET_CODE (a) != MEM && may_trap_p (a))
-      || (GET_CODE (b) != MEM && may_trap_p (b)))
+  if (! noce_operand_ok (a) || ! noce_operand_ok (b))
     return FALSE;
 
   /* Set up the info block for our subroutines.  */
+  if_info.test_bb = test_bb;
   if_info.cond = cond;
   if_info.jump = jump;
   if_info.insn_a = insn_a;
@@ -1258,6 +1577,10 @@ noce_process_if_block (test_bb, then_bb, else_bb, join_bb)
 
   if (noce_try_store_flag (&if_info))
     goto success;
+  if (noce_try_minmax (&if_info))
+    goto success;
+  if (noce_try_abs (&if_info))
+    goto success;
   if (HAVE_conditional_move
       && noce_try_cmove (&if_info))
     goto success;
index 6e6ed8e..237382f 100644 (file)
@@ -2020,6 +2020,11 @@ may_trap_p (x)
        return 1;
       break;
 
+    case NEG:
+    case ABS:
+      /* These operations don't trap even with floating point.  */
+      break;
+
     default:
       /* Any floating arithmetic may trap.  */
       if (GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT)