OSDN Git Service

2004-06-28 Paolo Bonzini <bonzini@gnu.org>
[pf3gnuchains/gcc-fork.git] / gcc / builtins.c
index 199900e..92a4dc3 100644 (file)
@@ -45,6 +45,7 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
 #include "tm_p.h"
 #include "target.h"
 #include "langhooks.h"
+#include "basic-block.h"
 
 #define CALLED_AS_BUILT_IN(NODE) \
    (!strncmp (IDENTIFIER_POINTER (DECL_NAME (NODE)), "__builtin_", 10))
@@ -168,6 +169,7 @@ static tree fold_builtin_toascii (tree);
 static tree fold_builtin_isdigit (tree);
 static tree fold_builtin_fabs (tree, tree);
 static tree fold_builtin_abs (tree, tree);
+static tree fold_builtin_unordered_cmp (tree, enum tree_code, enum tree_code);
 
 static tree simplify_builtin_memcmp (tree);
 static tree simplify_builtin_strcmp (tree);
@@ -643,7 +645,7 @@ expand_builtin_setjmp (tree arglist, rtx target)
   if (!validate_arglist (arglist, POINTER_TYPE, VOID_TYPE))
     return NULL_RTX;
 
-  if (target == 0 || GET_CODE (target) != REG
+  if (target == 0 || !REG_P (target)
       || REGNO (target) < FIRST_PSEUDO_REGISTER)
     target = gen_reg_rtx (TYPE_MODE (integer_type_node));
 
@@ -1237,7 +1239,7 @@ expand_builtin_apply_args (void)
        chain current, so the code is placed at the start of the
        function.  */
     push_topmost_sequence ();
-    emit_insn_before (seq, NEXT_INSN (get_insns ()));
+    emit_insn_before (seq, NEXT_INSN (entry_of_function ()));
     pop_topmost_sequence ();
     return temp;
   }
@@ -1334,7 +1336,7 @@ expand_builtin_apply (rtx function, rtx arguments, rtx argsize)
       rtx value = gen_reg_rtx (Pmode);
       emit_move_insn (value, adjust_address (arguments, Pmode, size));
       emit_move_insn (struct_value, value);
-      if (GET_CODE (struct_value) == REG)
+      if (REG_P (struct_value))
        use_reg (&call_fusage, struct_value);
       size += GET_MODE_SIZE (Pmode);
     }
@@ -1829,7 +1831,7 @@ expand_builtin_mathfn (tree exp, rtx target, rtx subtarget)
                  rtx operand = XEXP (XEXP (XEXP (note, 0), 1), 0);
                  /* Check operand is a register with expected mode.  */
                  if (operand
-                     && GET_CODE (operand) == REG
+                     && REG_P (operand)
                      && GET_MODE (operand) == mode)
                    {
                      /* Replace the REG_EQUAL note with a SQRT rtx.  */
@@ -2402,7 +2404,7 @@ expand_builtin_strlen (tree arglist, rtx target,
       /* Make a place to write the result of the instruction.  */
       result = target;
       if (! (result != 0
-            && GET_CODE (result) == REG
+            && REG_P (result)
             && GET_MODE (result) == insn_mode
             && REGNO (result) >= FIRST_PSEUDO_REGISTER))
        result = gen_reg_rtx (insn_mode);
@@ -3413,7 +3415,7 @@ expand_builtin_memcmp (tree exp ATTRIBUTE_UNUSED, tree arglist, rtx target,
     /* Make a place to write the result of the instruction.  */
     result = target;
     if (! (result != 0
-          && GET_CODE (result) == REG && GET_MODE (result) == insn_mode
+          && REG_P (result) && GET_MODE (result) == insn_mode
           && REGNO (result) >= FIRST_PSEUDO_REGISTER))
       result = gen_reg_rtx (insn_mode);
 
@@ -3568,7 +3570,7 @@ expand_builtin_strcmp (tree exp, rtx target, enum machine_mode mode)
     /* Make a place to write the result of the instruction.  */
     result = target;
     if (! (result != 0
-          && GET_CODE (result) == REG && GET_MODE (result) == insn_mode
+          && REG_P (result) && GET_MODE (result) == insn_mode
           && REGNO (result) >= FIRST_PSEUDO_REGISTER))
       result = gen_reg_rtx (insn_mode);
 
@@ -3739,7 +3741,7 @@ expand_builtin_strncmp (tree exp, rtx target, enum machine_mode mode)
     /* Make a place to write the result of the instruction.  */
     result = target;
     if (! (result != 0
-          && GET_CODE (result) == REG && GET_MODE (result) == insn_mode
+          && REG_P (result) && GET_MODE (result) == insn_mode
           && REGNO (result) >= FIRST_PSEUDO_REGISTER))
       result = gen_reg_rtx (insn_mode);
 
@@ -4008,7 +4010,7 @@ expand_builtin_saveregs (void)
      is inside a start_sequence, make the outer-level insn chain current, so
      the code is placed at the start of the function.  */
   push_topmost_sequence ();
-  emit_insn_after (seq, get_insns ());
+  emit_insn_after (seq, entry_of_function ());
   pop_topmost_sequence ();
 
   return val;
@@ -4652,7 +4654,7 @@ expand_builtin_frame_address (tree fndecl, tree arglist)
       if (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_FRAME_ADDRESS)
        return tem;
 
-      if (GET_CODE (tem) != REG
+      if (!REG_P (tem)
          && ! CONSTANT_P (tem))
        tem = copy_to_mode_reg (Pmode, tem);
       return tem;
@@ -6193,9 +6195,7 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode,
       break;
 
     default:   /* just do library call, if unknown builtin */
-      if (!DECL_ASSEMBLER_NAME_SET_P (fndecl))
-       error ("built-in function `%s' not currently supported",
-              IDENTIFIER_POINTER (DECL_NAME (fndecl)));
+      break;
     }
 
   /* The switch statement above can drop through to cause the function
@@ -7608,6 +7608,90 @@ fold_builtin_abs (tree arglist, tree type)
   return fold (build1 (ABS_EXPR, type, arg));
 }
 
+/* Fold a call to an unordered comparison function such as
+   __builtin_isgreater().  ARGLIST is the funtion's argument list
+   and TYPE is the functions return type.  UNORDERED_CODE and
+   ORDERED_CODE are comparison codes that give the opposite of
+   the desired result.  UNORDERED_CODE is used for modes that can
+   hold NaNs and ORDERED_CODE is used for the rest.  */
+
+static tree
+fold_builtin_unordered_cmp (tree exp,
+                           enum tree_code unordered_code,
+                           enum tree_code ordered_code)
+{
+  tree fndecl = get_callee_fndecl (exp);
+  tree arglist = TREE_OPERAND (exp, 1);
+  tree type = TREE_TYPE (TREE_TYPE (fndecl));
+  enum tree_code code;
+  tree arg0, arg1;
+
+  if (!validate_arglist (arglist, REAL_TYPE, REAL_TYPE, VOID_TYPE))
+    {
+      enum tree_code code0, code1;
+      tree type0, type1;
+      tree cmp_type = 0;
+
+      /* Check that we have exactly two arguments.  */
+      if (arglist == 0 || TREE_CHAIN (arglist) == 0)
+       {
+         error ("too few arguments to function `%s'",
+                IDENTIFIER_POINTER (DECL_NAME (fndecl)));
+         return error_mark_node;
+       }
+      else if (TREE_CHAIN (TREE_CHAIN (arglist)) != 0)
+       {
+         error ("too many arguments to function `%s'",
+                IDENTIFIER_POINTER (DECL_NAME (fndecl)));
+         return error_mark_node;
+       }
+
+      arg0 = TREE_VALUE (arglist);
+      arg1 = TREE_VALUE (TREE_CHAIN (arglist));
+
+      type0 = TREE_TYPE (arg0);
+      type1 = TREE_TYPE (arg1);
+
+      code0 = TREE_CODE (type0);
+      code1 = TREE_CODE (type1);
+
+      if (code0 == REAL_TYPE && code1 == REAL_TYPE)
+       /* Choose the wider of two real types.  */
+        cmp_type = TYPE_PRECISION (type0) >= TYPE_PRECISION (type1)
+                  ? type0 : type1;
+      else if (code0 == REAL_TYPE && code1 == INTEGER_TYPE)
+       cmp_type = type0;
+      else if (code0 == INTEGER_TYPE && code1 == REAL_TYPE)
+       cmp_type = type1;
+      else
+       {
+         error ("non-floating-point argument to function `%s'",
+                IDENTIFIER_POINTER (DECL_NAME (fndecl)));
+         return error_mark_node;
+       }
+
+      arg0 = fold_convert (cmp_type, arg0);
+      arg1 = fold_convert (cmp_type, arg1);
+    }
+  else
+    {
+      arg0 = TREE_VALUE (arglist);
+      arg1 = TREE_VALUE (TREE_CHAIN (arglist));
+    }
+
+  if (unordered_code == UNORDERED_EXPR)
+    {
+      if (!MODE_HAS_NANS (TYPE_MODE (TREE_TYPE (arg0))))
+       return omit_two_operands (type, integer_zero_node, arg0, arg1);
+      return fold (build2 (UNORDERED_EXPR, type, arg0, arg1));
+    }
+
+  code = MODE_HAS_NANS (TYPE_MODE (TREE_TYPE (arg0))) ? unordered_code
+                                                     : ordered_code;
+  return fold (build1 (TRUTH_NOT_EXPR, type,
+                      fold (build2 (code, type, arg0, arg1))));
+}
+
 /* Used by constant folding to eliminate some builtin calls early.  EXP is
    the CALL_EXPR of a call to a builtin function.  */
 
@@ -8160,6 +8244,19 @@ fold_builtin_1 (tree exp)
     case BUILT_IN_COPYSIGNL:
       return fold_builtin_copysign (arglist, type);
 
+    case BUILT_IN_ISGREATER:
+      return fold_builtin_unordered_cmp (exp, UNLE_EXPR, LE_EXPR);
+    case BUILT_IN_ISGREATEREQUAL:
+      return fold_builtin_unordered_cmp (exp, UNLT_EXPR, LT_EXPR);
+    case BUILT_IN_ISLESS:
+      return fold_builtin_unordered_cmp (exp, UNGE_EXPR, GE_EXPR);
+    case BUILT_IN_ISLESSEQUAL:
+      return fold_builtin_unordered_cmp (exp, UNGT_EXPR, GT_EXPR);
+    case BUILT_IN_ISLESSGREATER:
+      return fold_builtin_unordered_cmp (exp, UNEQ_EXPR, EQ_EXPR);
+    case BUILT_IN_ISUNORDERED:
+      return fold_builtin_unordered_cmp (exp, UNORDERED_EXPR, NOP_EXPR);
+
     default:
       break;
     }
@@ -8772,13 +8869,9 @@ simplify_builtin_memcmp (tree arglist)
 
   /* If the len parameter is zero, return zero.  */
   if (host_integerp (len, 1) && tree_low_cst (len, 1) == 0)
-    {
-      /* Evaluate and ignore arg1 and arg2 in case they have
-         side-effects.  */
-      return build2 (COMPOUND_EXPR, integer_type_node, arg1,
-                    build2 (COMPOUND_EXPR, integer_type_node,
-                            arg2, integer_zero_node));
-    }
+    /* Evaluate and ignore arg1 and arg2 in case they have side-effects.  */
+    return omit_two_operands (integer_type_node, integer_zero_node,
+                             arg1, arg2);
 
   p1 = c_getstr (arg1);
   p2 = c_getstr (arg2);
@@ -8913,13 +9006,9 @@ simplify_builtin_strncmp (tree arglist)
 
   /* If the len parameter is zero, return zero.  */
   if (integer_zerop (arg3))
-    {
-      /* Evaluate and ignore arg1 and arg2 in case they have
-        side-effects.  */
-      return build2 (COMPOUND_EXPR, integer_type_node, arg1,
-                    build2 (COMPOUND_EXPR, integer_type_node,
-                            arg2, integer_zero_node));
-    }
+    /* Evaluate and ignore arg1 and arg2 in case they have side-effects.  */
+    return omit_two_operands (integer_type_node, integer_zero_node,
+                             arg1, arg2);
 
   /* If arg1 and arg2 are equal (and not volatile), return zero.  */
   if (operand_equal_p (arg1, arg2, 0))
@@ -9030,8 +9119,7 @@ simplify_builtin_strncat (tree arglist)
       /* If the requested length is zero, or the src parameter string
           length is zero, return the dst parameter.  */
       if (integer_zerop (len) || (p && *p == '\0'))
-       return build2 (COMPOUND_EXPR, TREE_TYPE (dst), src,
-                      build2 (COMPOUND_EXPR, integer_type_node, len, dst));
+        return omit_two_operands (TREE_TYPE (dst), dst, src, len);
 
       /* If the requested len is greater than or equal to the string
          length, call strcat.  */
@@ -9089,13 +9177,10 @@ simplify_builtin_strspn (tree arglist)
 
       /* If either argument is "", return 0.  */
       if ((p1 && *p1 == '\0') || (p2 && *p2 == '\0'))
-       {
-         /* Evaluate and ignore both arguments in case either one has
-            side-effects.  */
-         return build2 (COMPOUND_EXPR, integer_type_node, s1,
-                        build2 (COMPOUND_EXPR, integer_type_node,
-                                s2, integer_zero_node));
-       }
+       /* Evaluate and ignore both arguments in case either one has
+          side-effects.  */
+       return omit_two_operands (integer_type_node, integer_zero_node,
+                                 s1, s2);
       return 0;
     }
 }