builtins.c: Implement support for sincos function.
authorUros Bizjak <uros@kss-loka.si>
Tue, 6 Apr 2004 19:34:33 +0000 (21:34 +0200)
committerRoger Sayle <sayle@gcc.gnu.org>
Tue, 6 Apr 2004 19:34:33 +0000 (19:34 +0000)
2004-04-06  Uros Bizjak  <uros@kss-loka.si>

* builtins.c: Implement support for sincos function.
(expand_builtin_mathfn): Remove BUILT_IN_SIN{,F,L} and
BUILT_IN_COS{,F,L}.
(expand_builtin_mathfn_3): New function.
(expand_builtin): Expand BUILT_IN_SIN{,F,L} and
BUILT_IN_COS{,F,L} using expand_builtin_mathfn_3 if
flag_unsafe_math_optimization is set.

* optabs.h (enum optab_index): Add new OTI_sincos.
(sincos_optab): Define corresponding macro.

* optabs.c (init_optabs): Initialize sincos_optab.
(expand_twoval_unop): New function.

* genopinit.c (optabs): Implement sincos_optab using sincos?f3
patterns.

* reg-stack.c (subst_stack_regs_pat): Handle UNSPEC_SINCOS_COS
and UNSPEC_SINCOS_SIN.

* config/i386/i386.md (sincosdf3, sincossf3, *sincosextendsfdf3,
sincosxf3): New patterns to implement sincos, sincosf and sincosl
built-ins as inline x87 intrinsics. Define splits for
sindf2, sinsf2, *sinextendsfdf2, sinxf2, cosdf2,
cossf2, *cosextendsfdf2 and cosxf2 patterns from corresponding
sincos patterns.
(sindf2, sinsf2, sinxf2): Rename to *sindf2, *sinsf2, *sinxf2.
(cosdf2, cossf2, cosxf2): Rename to *cosdf2, *cossf2, *cosxf2.

(UNSPEC_SINCOS_SIN, UNPEC_SINCOS_COS): New unspecs to represent
x87's unspec insn.

* gcc.dg/builtins-36.c: New test.

From-SVN: r80463

gcc/ChangeLog
gcc/builtins.c
gcc/config/i386/i386.md
gcc/genopinit.c
gcc/optabs.c
gcc/optabs.h
gcc/reg-stack.c
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.dg/builtins-36.c [new file with mode: 0644]

index c83da8aeac3fd1a25ea96cf6bd504fb65b7e9f5a..a6df24a238a99c6215206f917650b3421d9fb72c 100644 (file)
@@ -1,3 +1,37 @@
+2004-04-06  Uros Bizjak  <uros@kss-loka.si>
+
+       * builtins.c: Implement support for sincos function.
+       (expand_builtin_mathfn): Remove BUILT_IN_SIN{,F,L} and
+       BUILT_IN_COS{,F,L}.
+       (expand_builtin_mathfn_3): New function.
+       (expand_builtin): Expand BUILT_IN_SIN{,F,L} and
+       BUILT_IN_COS{,F,L} using expand_builtin_mathfn_3 if
+       flag_unsafe_math_optimization is set.
+
+       * optabs.h (enum optab_index): Add new OTI_sincos.
+       (sincos_optab): Define corresponding macro.
+
+       * optabs.c (init_optabs): Initialize sincos_optab.
+       (expand_twoval_unop): New function.
+
+       * genopinit.c (optabs): Implement sincos_optab using sincos?f3
+       patterns.
+
+       * reg-stack.c (subst_stack_regs_pat): Handle UNSPEC_SINCOS_COS
+       and UNSPEC_SINCOS_SIN.
+
+       * config/i386/i386.md (sincosdf3, sincossf3, *sincosextendsfdf3,
+       sincosxf3): New patterns to implement sincos, sincosf and sincosl
+       built-ins as inline x87 intrinsics. Define splits for
+       sindf2, sinsf2, *sinextendsfdf2, sinxf2, cosdf2,
+       cossf2, *cosextendsfdf2 and cosxf2 patterns from corresponding
+       sincos patterns.
+       (sindf2, sinsf2, sinxf2): Rename to *sindf2, *sinsf2, *sinxf2.
+       (cosdf2, cossf2, cosxf2): Rename to *cosdf2, *cossf2, *cosxf2.
+
+       (UNSPEC_SINCOS_SIN, UNPEC_SINCOS_COS): New unspecs to represent
+       x87's unspec insn.
+
 2004-04-06  Devang Patel  <dpatel@apple.com>
 
        PR 14467
index 33df5eca6846c8f4f1b95219d20eab0b72b98b64..b0d0d48f2fdd2a9fe78931c45a4ee398d66e7cab 100644 (file)
@@ -94,6 +94,7 @@ static rtx expand_builtin_classify_type (tree);
 static void expand_errno_check (tree, rtx);
 static rtx expand_builtin_mathfn (tree, rtx, rtx);
 static rtx expand_builtin_mathfn_2 (tree, rtx, rtx);
+static rtx expand_builtin_mathfn_3 (tree, rtx, rtx);
 static rtx expand_builtin_constant_p (tree, enum machine_mode);
 static rtx expand_builtin_args_info (tree);
 static rtx expand_builtin_next_arg (tree);
@@ -1520,7 +1521,7 @@ expand_errno_check (tree exp, rtx target)
 }
 
 
-/* Expand a call to one of the builtin math functions (sin, cos, or sqrt).
+/* Expand a call to one of the builtin math functions (sqrt, exp, or log).
    Return 0 if a normal call should be emitted rather than expanding the
    function in-line.  EXP is the expression that is a call to the builtin
    function; if convenient, the result should be placed in TARGET.
@@ -1544,14 +1545,6 @@ expand_builtin_mathfn (tree exp, rtx target, rtx subtarget)
 
   switch (DECL_FUNCTION_CODE (fndecl))
     {
-    case BUILT_IN_SIN:
-    case BUILT_IN_SINF:
-    case BUILT_IN_SINL:
-      builtin_optab = sin_optab; break;
-    case BUILT_IN_COS:
-    case BUILT_IN_COSF:
-    case BUILT_IN_COSL:
-      builtin_optab = cos_optab; break;
     case BUILT_IN_SQRT:
     case BUILT_IN_SQRTF:
     case BUILT_IN_SQRTL:
@@ -1815,6 +1808,138 @@ expand_builtin_mathfn_2 (tree exp, rtx target, rtx subtarget)
   return target;
 }
 
+/* Expand a call to the builtin sin and cos math functions.
+   Return 0 if a normal call should be emitted rather than expanding the
+   function in-line.  EXP is the expression that is a call to the builtin
+   function; if convenient, the result should be placed in TARGET.
+   SUBTARGET may be used as the target for computing one of EXP's
+   operands.  */
+
+static rtx
+expand_builtin_mathfn_3 (tree exp, rtx target, rtx subtarget)
+{
+  optab builtin_optab;
+  rtx op0, insns, before_call;
+  tree fndecl = get_callee_fndecl (exp);
+  tree arglist = TREE_OPERAND (exp, 1);
+  enum machine_mode mode;
+  bool errno_set = false;
+  tree arg, narg;
+
+  if (!validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
+    return 0;
+
+  arg = TREE_VALUE (arglist);
+
+  switch (DECL_FUNCTION_CODE (fndecl))
+    {
+    case BUILT_IN_SIN:
+    case BUILT_IN_SINF:
+    case BUILT_IN_SINL:
+    case BUILT_IN_COS:
+    case BUILT_IN_COSF:
+    case BUILT_IN_COSL:
+      builtin_optab = sincos_optab; break;
+    default:
+      abort ();
+    }
+
+  /* Make a suitable register to place result in.  */
+  mode = TYPE_MODE (TREE_TYPE (exp));
+
+  if (! flag_errno_math || ! HONOR_NANS (mode))
+    errno_set = false;
+
+  /* Check if sincos insn is available, otherwise fallback
+     to sin or cos insn. */
+  if (builtin_optab->handlers[(int) mode].insn_code == CODE_FOR_nothing) {
+    switch (DECL_FUNCTION_CODE (fndecl))
+      {
+      case BUILT_IN_SIN:
+      case BUILT_IN_SINF:
+      case BUILT_IN_SINL:
+       builtin_optab = sin_optab; break;
+      case BUILT_IN_COS:
+      case BUILT_IN_COSF:
+      case BUILT_IN_COSL:
+       builtin_optab = cos_optab; break;
+      default:
+       abort();
+      }
+  }
+
+  /* Before working hard, check whether the instruction is available.  */
+  if (builtin_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
+    {
+      target = gen_reg_rtx (mode);
+
+      /* Wrap the computation of the argument in a SAVE_EXPR, as we may
+        need to expand the argument again.  This way, we will not perform
+        side-effects more the once.  */
+      narg = save_expr (arg);
+      if (narg != arg)
+       {
+         arglist = build_tree_list (NULL_TREE, arg);
+         exp = build_function_call_expr (fndecl, arglist);
+       }
+
+      op0 = expand_expr (arg, subtarget, VOIDmode, 0);
+
+      emit_queue ();
+      start_sequence ();
+
+      /* Compute into TARGET.
+        Set TARGET to wherever the result comes back.  */
+      if (builtin_optab == sincos_optab)
+       {
+         switch (DECL_FUNCTION_CODE (fndecl))
+           {
+           case BUILT_IN_SIN:
+           case BUILT_IN_SINF:
+           case BUILT_IN_SINL:
+             if (! expand_twoval_unop(builtin_optab, 0, target, op0, 0))    
+               abort();
+             break;
+           case BUILT_IN_COS:
+           case BUILT_IN_COSF:
+           case BUILT_IN_COSL:
+             if (! expand_twoval_unop(builtin_optab, target, 0, op0, 0))
+               abort();
+             break;
+           default:
+             abort();
+           }
+       }
+      else
+       {
+         target = expand_unop (mode, builtin_optab, op0, target, 0);
+       }
+
+      if (target != 0)
+       {
+         if (errno_set)
+           expand_errno_check (exp, target);
+
+         /* Output the entire sequence.  */
+         insns = get_insns ();
+         end_sequence ();
+         emit_insn (insns);
+         return target;
+       }
+
+      /* If we were unable to expand via the builtin, stop the sequence
+        (without outputting the insns) and call to the library function
+        with the stabilized argument list.  */
+      end_sequence ();
+    }
+
+  before_call = get_last_insn ();
+
+  target = expand_call (exp, target, target == const0_rtx);
+
+  return target;
+}
+
 /* To evaluate powi(x,n), the floating point value x raised to the
    constant integer exponent n, we use a hybrid algorithm that
    combines the "window method" with look-up tables.  For an
@@ -5042,12 +5167,6 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode,
         and IMAGPART_EXPR.  */
       abort ();
 
-    case BUILT_IN_SIN:
-    case BUILT_IN_SINF:
-    case BUILT_IN_SINL:
-    case BUILT_IN_COS:
-    case BUILT_IN_COSF:
-    case BUILT_IN_COSL:
     case BUILT_IN_EXP:
     case BUILT_IN_EXPF:
     case BUILT_IN_EXPL:
@@ -5120,6 +5239,19 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode,
        return target;
       break;
 
+    case BUILT_IN_SIN:
+    case BUILT_IN_SINF:
+    case BUILT_IN_SINL:
+    case BUILT_IN_COS:
+    case BUILT_IN_COSF:
+    case BUILT_IN_COSL:
+      if (! flag_unsafe_math_optimizations)
+       break;
+      target = expand_builtin_mathfn_3 (exp, target, subtarget);
+      if (target)
+       return target;
+      break;
+
     case BUILT_IN_APPLY_ARGS:
       return expand_builtin_apply_args ();
 
index ad8731b2c65403a248e1fb2839df7997035cc530..e9afa7730669a493a12881920c017d805e6aeb2b 100644 (file)
    (UNSPEC_FRNDINT             68)
    (UNSPEC_F2XM1               69)
 
+   (UNSPEC_SINCOS_COS          80)
+   (UNSPEC_SINCOS_SIN          81)
+
    ; REP instruction
    (UNSPEC_REP                 75)
   ])
    (set_attr "mode" "XF")
    (set_attr "athlon_decode" "direct")])
 
-(define_insn "sindf2"
+(define_insn "*sindf2"
   [(set (match_operand:DF 0 "register_operand" "=f")
        (unspec:DF [(match_operand:DF 1 "register_operand" "0")] UNSPEC_SIN))]
   "! TARGET_NO_FANCY_MATH_387 && TARGET_80387 
   [(set_attr "type" "fpspc")
    (set_attr "mode" "DF")])
 
-(define_insn "sinsf2"
+(define_insn "*sinsf2"
   [(set (match_operand:SF 0 "register_operand" "=f")
        (unspec:SF [(match_operand:SF 1 "register_operand" "0")] UNSPEC_SIN))]
   "! TARGET_NO_FANCY_MATH_387 && TARGET_80387 
   [(set_attr "type" "fpspc")
    (set_attr "mode" "DF")])
 
-(define_insn "sinxf2"
+(define_insn "*sinxf2"
   [(set (match_operand:XF 0 "register_operand" "=f")
        (unspec:XF [(match_operand:XF 1 "register_operand" "0")] UNSPEC_SIN))]
   "TARGET_80387 && !TARGET_NO_FANCY_MATH_387
   [(set_attr "type" "fpspc")
    (set_attr "mode" "XF")])
 
-(define_insn "cosdf2"
+(define_insn "*cosdf2"
   [(set (match_operand:DF 0 "register_operand" "=f")
        (unspec:DF [(match_operand:DF 1 "register_operand" "0")] UNSPEC_COS))]
   "! TARGET_NO_FANCY_MATH_387 && TARGET_80387 
   [(set_attr "type" "fpspc")
    (set_attr "mode" "DF")])
 
-(define_insn "cossf2"
+(define_insn "*cossf2"
   [(set (match_operand:SF 0 "register_operand" "=f")
        (unspec:SF [(match_operand:SF 1 "register_operand" "0")] UNSPEC_COS))]
   "! TARGET_NO_FANCY_MATH_387 && TARGET_80387 
   [(set_attr "type" "fpspc")
    (set_attr "mode" "DF")])
 
-(define_insn "cosxf2"
+(define_insn "*cosxf2"
   [(set (match_operand:XF 0 "register_operand" "=f")
        (unspec:XF [(match_operand:XF 1 "register_operand" "0")] UNSPEC_COS))]
   "! TARGET_NO_FANCY_MATH_387 && TARGET_80387
   [(set_attr "type" "fpspc")
    (set_attr "mode" "XF")])
 
+;; With sincos pattern defined, sin and cos builtin function will be
+;; expanded to sincos pattern with one of its outputs left unused. 
+;; Cse pass  will detected, if two sincos patterns can be combined,
+;; otherwise sincos pattern will be splitted back to sin or cos pattern,
+;; depending on the unused output.
+
+(define_insn "sincosdf3"
+  [(set (match_operand:DF 0 "register_operand" "=f")
+       (unspec:DF [(match_operand:DF 2 "register_operand" "0")]
+                  UNSPEC_SINCOS_COS))
+   (set (match_operand:DF 1 "register_operand" "=u")
+        (unspec:DF [(match_dup 2)] UNSPEC_SINCOS_SIN))]
+  "! TARGET_NO_FANCY_MATH_387 && TARGET_80387 
+   && flag_unsafe_math_optimizations"
+  "fsincos"
+  [(set_attr "type" "fpspc")
+   (set_attr "mode" "DF")])
+
+(define_split
+  [(set (match_operand:DF 0 "register_operand" "")
+       (unspec:DF [(match_operand:DF 2 "register_operand" "")]
+                  UNSPEC_SINCOS_COS))
+   (set (match_operand:DF 1 "register_operand" "")
+       (unspec:DF [(match_dup 2)] UNSPEC_SINCOS_SIN))]
+  "find_regno_note (insn, REG_UNUSED, REGNO (operands[0]))
+   && !reload_completed && !reload_in_progress"
+  [(set (match_dup 1) (unspec:DF [(match_dup 2)] UNSPEC_SIN))]
+  "")
+
+(define_split
+  [(set (match_operand:DF 0 "register_operand" "")
+       (unspec:DF [(match_operand:DF 2 "register_operand" "")]
+                  UNSPEC_SINCOS_COS))
+   (set (match_operand:DF 1 "register_operand" "")
+       (unspec:DF [(match_dup 2)] UNSPEC_SINCOS_SIN))]
+  "find_regno_note (insn, REG_UNUSED, REGNO (operands[1]))
+   && !reload_completed && !reload_in_progress"
+  [(set (match_dup 0) (unspec:DF [(match_dup 2)] UNSPEC_COS))]
+  "")
+
+(define_insn "sincossf3"
+  [(set (match_operand:SF 0 "register_operand" "=f")
+       (unspec:SF [(match_operand:SF 2 "register_operand" "0")]
+                  UNSPEC_SINCOS_COS))
+   (set (match_operand:SF 1 "register_operand" "=u")
+        (unspec:SF [(match_dup 2)] UNSPEC_SINCOS_SIN))]
+  "! TARGET_NO_FANCY_MATH_387 && TARGET_80387 
+   && flag_unsafe_math_optimizations"
+  "fsincos"
+  [(set_attr "type" "fpspc")
+   (set_attr "mode" "SF")])
+
+(define_split
+  [(set (match_operand:SF 0 "register_operand" "")
+       (unspec:SF [(match_operand:SF 2 "register_operand" "")]
+                  UNSPEC_SINCOS_COS))
+   (set (match_operand:SF 1 "register_operand" "")
+       (unspec:SF [(match_dup 2)] UNSPEC_SINCOS_SIN))]
+  "find_regno_note (insn, REG_UNUSED, REGNO (operands[0]))
+   && !reload_completed && !reload_in_progress"
+  [(set (match_dup 1) (unspec:SF [(match_dup 2)] UNSPEC_SIN))]
+  "")
+
+(define_split
+  [(set (match_operand:SF 0 "register_operand" "")
+       (unspec:SF [(match_operand:SF 2 "register_operand" "")]
+                  UNSPEC_SINCOS_COS))
+   (set (match_operand:SF 1 "register_operand" "")
+       (unspec:SF [(match_dup 2)] UNSPEC_SINCOS_SIN))]
+  "find_regno_note (insn, REG_UNUSED, REGNO (operands[1]))
+   && !reload_completed && !reload_in_progress"
+  [(set (match_dup 0) (unspec:SF [(match_dup 2)] UNSPEC_COS))]
+  "")
+
+(define_insn "*sincosextendsfdf3"
+  [(set (match_operand:DF 0 "register_operand" "=f")
+       (unspec:DF [(float_extend:DF
+                    (match_operand:SF 2 "register_operand" "0"))]
+                  UNSPEC_SINCOS_COS))
+   (set (match_operand:DF 1 "register_operand" "=u")
+        (unspec:DF [(float_extend:DF
+                    (match_dup 2))] UNSPEC_SINCOS_SIN))]
+  "! TARGET_NO_FANCY_MATH_387 && TARGET_80387 
+   && flag_unsafe_math_optimizations"
+  "fsincos"
+  [(set_attr "type" "fpspc")
+   (set_attr "mode" "DF")])
+
+(define_split
+  [(set (match_operand:DF 0 "register_operand" "")
+       (unspec:DF [(float_extend:DF
+                    (match_operand:SF 2 "register_operand" ""))]
+                  UNSPEC_SINCOS_COS))
+   (set (match_operand:DF 1 "register_operand" "")
+        (unspec:DF [(float_extend:DF
+                    (match_dup 2))] UNSPEC_SINCOS_SIN))]
+  "find_regno_note (insn, REG_UNUSED, REGNO (operands[0]))
+   && !reload_completed && !reload_in_progress"
+  [(set (match_dup 1) (unspec:DF [(float_extend:DF
+                                  (match_dup 2))] UNSPEC_SIN))]
+  "")
+
+(define_split
+  [(set (match_operand:DF 0 "register_operand" "")
+       (unspec:DF [(float_extend:DF
+                    (match_operand:SF 2 "register_operand" ""))]
+                  UNSPEC_SINCOS_COS))
+   (set (match_operand:DF 1 "register_operand" "")
+        (unspec:DF [(float_extend:DF
+                    (match_dup 2))] UNSPEC_SINCOS_SIN))]
+  "find_regno_note (insn, REG_UNUSED, REGNO (operands[1]))
+   && !reload_completed && !reload_in_progress"
+  [(set (match_dup 0) (unspec:DF [(float_extend:DF
+                                  (match_dup 2))] UNSPEC_COS))]
+  "")
+
+(define_insn "sincosxf3"
+  [(set (match_operand:XF 0 "register_operand" "=f")
+       (unspec:XF [(match_operand:XF 2 "register_operand" "0")]
+                  UNSPEC_SINCOS_COS))
+   (set (match_operand:XF 1 "register_operand" "=u")
+        (unspec:XF [(match_dup 2)] UNSPEC_SINCOS_SIN))]
+  "! TARGET_NO_FANCY_MATH_387 && TARGET_80387 
+   && flag_unsafe_math_optimizations"
+  "fsincos"
+  [(set_attr "type" "fpspc")
+   (set_attr "mode" "XF")])
+
+(define_split
+  [(set (match_operand:XF 0 "register_operand" "")
+       (unspec:XF [(match_operand:XF 2 "register_operand" "")]
+                  UNSPEC_SINCOS_COS))
+   (set (match_operand:XF 1 "register_operand" "")
+       (unspec:XF [(match_dup 2)] UNSPEC_SINCOS_SIN))]
+  "find_regno_note (insn, REG_UNUSED, REGNO (operands[0]))
+   && !reload_completed && !reload_in_progress"
+  [(set (match_dup 1) (unspec:XF [(match_dup 2)] UNSPEC_SIN))]
+  "")
+
+(define_split
+  [(set (match_operand:XF 0 "register_operand" "")
+       (unspec:XF [(match_operand:XF 2 "register_operand" "")]
+                  UNSPEC_SINCOS_COS))
+   (set (match_operand:XF 1 "register_operand" "")
+       (unspec:XF [(match_dup 2)] UNSPEC_SINCOS_SIN))]
+  "find_regno_note (insn, REG_UNUSED, REGNO (operands[1]))
+   && !reload_completed && !reload_in_progress"
+  [(set (match_dup 0) (unspec:XF [(match_dup 2)] UNSPEC_COS))]
+  "")
+
 (define_insn "atan2df3_1"
   [(set (match_operand:DF 0 "register_operand" "=f")
        (unspec:DF [(match_operand:DF 2 "register_operand" "0")
index b3b651821c0b96dcd520cf3661f0ddd2bba08d19..fad18793e50983d1ce6258c315355a22208ab04a 100644 (file)
@@ -122,6 +122,7 @@ static const char * const optabs[] =
   "round_optab->handlers[$A].insn_code = CODE_FOR_$(round$a2$)",
   "trunc_optab->handlers[$A].insn_code = CODE_FOR_$(trunc$a2$)",
   "nearbyint_optab->handlers[$A].insn_code = CODE_FOR_$(nearbyint$a2$)",
+  "sincos_optab->handlers[$A].insn_code = CODE_FOR_$(sincos$a3$)",
   "sin_optab->handlers[$A].insn_code = CODE_FOR_$(sin$a2$)",
   "cos_optab->handlers[$A].insn_code = CODE_FOR_$(cos$a2$)",
   "exp_optab->handlers[$A].insn_code = CODE_FOR_$(exp$a2$)",
index 1091cc81201a602c9d56500db1ad41614e587dfe..5fe24b7d1fbe42c5a2786696061d4f8d0d86c0ba 100644 (file)
@@ -2147,6 +2147,109 @@ sign_expand_binop (enum machine_mode mode, optab uoptab, optab soptab,
   return 0;
 }
 \f
+/* Generate code to perform an operation specified by UNOPPTAB
+   on operand OP0, with two results to TARG0 and TARG1.
+   We assume that the order of the operands for the instruction
+   is TARG0, TARG1, OP0.
+
+   Either TARG0 or TARG1 may be zero, but what that means is that
+   the result is not actually wanted.  We will generate it into
+   a dummy pseudo-reg and discard it.  They may not both be zero.
+
+   Returns 1 if this operation can be performed; 0 if not.  */
+
+int
+expand_twoval_unop (optab unoptab, rtx targ0, rtx targ1, rtx op0,
+                   int unsignedp)
+{
+  enum machine_mode mode = GET_MODE (targ0 ? targ0 : targ1);
+  enum mode_class class;
+  enum machine_mode wider_mode;
+  rtx entry_last = get_last_insn ();
+  rtx last;
+
+  class = GET_MODE_CLASS (mode);
+
+  op0 = protect_from_queue (op0, 0);
+
+  if (flag_force_mem)
+    {
+      op0 = force_not_mem (op0);
+    }
+
+  if (targ0)
+    targ0 = protect_from_queue (targ0, 1);
+  else
+    targ0 = gen_reg_rtx (mode);
+  if (targ1)
+    targ1 = protect_from_queue (targ1, 1);
+  else
+    targ1 = gen_reg_rtx (mode);
+
+  /* Record where to go back to if we fail.  */
+  last = get_last_insn ();
+
+  if (unoptab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
+    {
+      int icode = (int) unoptab->handlers[(int) mode].insn_code;
+      enum machine_mode mode0 = insn_data[icode].operand[2].mode;
+      rtx pat;
+      rtx xop0 = op0;
+
+      if (GET_MODE (xop0) != VOIDmode
+         && GET_MODE (xop0) != mode0)
+       xop0 = convert_to_mode (mode0, xop0, unsignedp);
+
+      /* Now, if insn doesn't accept these operands, put them into pseudos.  */
+      if (! (*insn_data[icode].operand[2].predicate) (xop0, mode0))
+       xop0 = copy_to_mode_reg (mode0, xop0);
+
+      /* We could handle this, but we should always be called with a pseudo
+        for our targets and all insns should take them as outputs.  */
+      if (! (*insn_data[icode].operand[0].predicate) (targ0, mode)
+         || ! (*insn_data[icode].operand[1].predicate) (targ1, mode))
+       abort ();
+
+      pat = GEN_FCN (icode) (targ0, targ1, xop0);
+      if (pat)
+       {
+         emit_insn (pat);
+         return 1;
+       }
+      else
+       delete_insns_since (last);
+    }
+
+  /* It can't be done in this mode.  Can we do it in a wider mode?  */
+
+  if (class == MODE_INT || class == MODE_FLOAT || class == MODE_COMPLEX_FLOAT)
+    {
+      for (wider_mode = GET_MODE_WIDER_MODE (mode); wider_mode != VOIDmode;
+          wider_mode = GET_MODE_WIDER_MODE (wider_mode))
+       {
+         if (unoptab->handlers[(int) wider_mode].insn_code
+             != CODE_FOR_nothing)
+           {
+             rtx t0 = gen_reg_rtx (wider_mode);
+             rtx t1 = gen_reg_rtx (wider_mode);
+             rtx cop0 = convert_modes (wider_mode, mode, op0, unsignedp);
+
+             if (expand_twoval_unop (unoptab, t0, t1, cop0, unsignedp))
+               {
+                 convert_move (targ0, t0, unsignedp);
+                 convert_move (targ1, t1, unsignedp);
+                 return 1;
+               }
+             else
+               delete_insns_since (last);
+           }
+       }
+    }
+
+  delete_insns_since (entry_last);
+  return 0;
+}
+\f
 /* Generate code to perform an operation specified by BINOPTAB
    on operands OP0 and OP1, with two results to TARG1 and TARG2.
    We assume that the order of the operands for the instruction
@@ -5275,6 +5378,7 @@ init_optabs (void)
   round_optab = init_optab (UNKNOWN);
   btrunc_optab = init_optab (UNKNOWN);
   nearbyint_optab = init_optab (UNKNOWN);
+  sincos_optab = init_optab (UNKNOWN);
   sin_optab = init_optab (UNKNOWN);
   cos_optab = init_optab (UNKNOWN);
   exp_optab = init_optab (UNKNOWN);
index 765e169cb4650b3b11c2afbddb7252b2db33a94f..524cb678200c444cc4aebd8e1c63958b80c2e6fc 100644 (file)
@@ -148,6 +148,8 @@ enum optab_index
   OTI_parity,
   /* Square root */
   OTI_sqrt,
+  /* Sine-Cosine */
+  OTI_sincos,
   /* Sine */
   OTI_sin,
   /* Cosine */
@@ -264,6 +266,7 @@ extern GTY(()) optab optab_table[OTI_MAX];
 #define popcount_optab (optab_table[OTI_popcount])
 #define parity_optab (optab_table[OTI_parity])
 #define sqrt_optab (optab_table[OTI_sqrt])
+#define sincos_optab (optab_table[OTI_sincos])
 #define sin_optab (optab_table[OTI_sin])
 #define cos_optab (optab_table[OTI_cos])
 #define exp_optab (optab_table[OTI_exp])
@@ -386,6 +389,9 @@ extern rtx expand_binop (enum machine_mode, optab, rtx, rtx, rtx, int,
 extern rtx sign_expand_binop (enum machine_mode, optab, optab, rtx, rtx,
                              rtx, int, enum optab_methods);
 
+/* Generate code to perform an operation on one operand with two results.  */
+extern int expand_twoval_unop (optab, rtx, rtx, rtx, int);
+
 /* Generate code to perform an operation on two operands with two results.  */
 extern int expand_twoval_binop (optab, rtx, rtx, rtx, rtx, int);
 
index 31ebddb46401a7370b102ee56b5b9678e1d2690a..f238267987afda11475d01cacdda48298bb98b99 100644 (file)
@@ -1768,6 +1768,60 @@ subst_stack_regs_pat (rtx insn, stack regstack, rtx pat)
                replace_reg (dest, FIRST_STACK_REG);
                break;
 
+             case UNSPEC_SINCOS_COS:
+               /* These insns operate on the top two stack slots,
+                  first part of one input, double output insn.  */
+
+               src1 = get_true_reg (&XVECEXP (pat_src, 0, 0));
+
+               emit_swap_insn (insn, regstack, *src1);
+
+               src1_note = find_regno_note (insn, REG_DEAD, REGNO (*src1));
+
+               /* Push the result back onto stack. Empty stack slot
+                  will be filled in second part of insn. */
+               if (STACK_REG_P (*dest)) {
+                 regstack->reg[regstack->top + 1] = REGNO (*dest);
+                 SET_HARD_REG_BIT (regstack->reg_set, REGNO (*dest));
+                 replace_reg (dest, FIRST_STACK_REG);
+               }
+
+               if (src1_note)
+                 {
+                   replace_reg (&XEXP (src1_note, 0), FIRST_STACK_REG);
+                   regstack->top--;
+                   CLEAR_HARD_REG_BIT (regstack->reg_set, REGNO (*src1));
+                 }
+               replace_reg (src1, FIRST_STACK_REG);
+               break;
+
+             case UNSPEC_SINCOS_SIN:
+               src1 = get_true_reg (&XVECEXP (pat_src, 0, 0));
+
+               emit_swap_insn (insn, regstack, *src1);
+
+               src1_note = find_regno_note (insn, REG_DEAD, REGNO (*src1));
+
+               /* Push the result back onto stack. Fill empty slot from
+                  first part of insn and fix top of stack pointer.  */
+               if (STACK_REG_P (*dest)) {
+                 regstack->reg[regstack->top] = REGNO (*dest);
+                 SET_HARD_REG_BIT (regstack->reg_set, REGNO (*dest));
+                 replace_reg (dest, FIRST_STACK_REG + 1);
+
+                 regstack->top++;
+               }
+
+               if (src1_note)
+                 {
+                   replace_reg (&XEXP (src1_note, 0), FIRST_STACK_REG);
+                   regstack->top--;
+                   CLEAR_HARD_REG_BIT (regstack->reg_set, REGNO (*src1));
+                 }
+
+               replace_reg (src1, FIRST_STACK_REG);
+               break;
+
              case UNSPEC_SAHF:
                /* (unspec [(unspec [(compare)] UNSPEC_FNSTSW)] UNSPEC_SAHF)
                   The combination matches the PPRO fcomi instruction.  */
index c7c589da9deb65a3825ef80db63c86e1bf482db6..e4d8c8abfb993d0aa1ac2bc9a68770dcc06d56d2 100644 (file)
@@ -1,3 +1,7 @@
+2004-04-06  Uros Bizjak  <uros@kss-loka.si>
+
+       * gcc.dg/builtins-36.c: New test.
+
 2004-04-06  Paul Brook  <paul@codesourcery.com>
 
        * README.gcc: Remove obsolete contraint on testcases.
diff --git a/gcc/testsuite/gcc.dg/builtins-36.c b/gcc/testsuite/gcc.dg/builtins-36.c
new file mode 100644 (file)
index 0000000..dc71198
--- /dev/null
@@ -0,0 +1,79 @@
+/* Copyright (C) 2004 Free Software Foundation.
+
+   Check sin, sinf, sinl, cos, cosf and cosl built-in functions
+   eventually compile to sincos, sincosf and sincosl.
+
+   Written by Uros Bizjak, 5th April 2004.  */
+
+/* { dg-do compile } */
+/* { dg-options "-O2 -ffast-math" } */
+
+extern double sin(double);
+extern float sinf(float);
+extern long double sinl(long double);
+
+extern double cos(double);
+extern float cosf(float);
+extern long double cosl(long double);
+
+
+double test1(double x)
+{
+       double y1, y2;
+
+       y1 = sin(x);
+       y2 = cos(x);
+
+       return y1 - y2;
+}
+
+float test1f(float x)
+{
+       float y1, y2;
+
+       y1 = sinf(x);
+       y2 = cosf(x);
+
+       return y1 - y2;
+}
+
+long double test1l(long double x)
+{
+       long double y1, y2;
+
+       y1 = sinl(x);
+       y2 = cosl(x);
+
+       return y1 - y2;
+}
+
+double test2(double x)
+{
+       return sin(x);
+}
+
+float test2f(float x)
+{
+       return sinf(x);
+}
+
+long double test2l(long double x)
+{
+       return sinl(x);
+}
+
+double test3(double x)
+{
+       return cos(x);
+}
+
+float test3f(float x)
+{
+       return cosf(x);
+}
+
+long double test3l(long double x)
+{
+       return cosl(x);
+}
+