This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[RFC] Tree level sin, cos -> sincos transformation


The patch below implements sin and cos fusion to sincos using
canonicalization to __imag/__real cexp ({0, x}) and relying on
PRE to CSE them.  At expand time this canonicalization is
undone either to sin, cos or sincos, depending on the expression
generated by TER (that's a hack, a separate pass for this is
needed, see also the related hack in out-of-ssa to mark the
target of c = cexp ({0, x}) addressable).  Further a new
expand_builtin_sincos is provided to make use of f.i. the
i387 fsincos instruction (we didn't expand calls to sincos
to fsincos before).

With this patch we can do the sin, cos to sincos transformation
on targets other than i387.

Comments?

Thanks,
Richard.


2005-12-15  Richard Guenther  <rguenther@suse.de>

	* builtins.c (fold_builtin_sin): Canonicalize to
	__imag cexp ( { 0, x } ).
	(fold_builtin_cos): Canonicalize to __real cexp ( { 0, x } ).
	(expand_builtin_sincos): New function.
	(expand_builtin): Use it.
	* expr.c (expand_expr_real_1): Expand C = cexp ( { 0, x} )
	as sincos (x, &__imag C, &__real C), expand
	__imag cexp ( { 0, x } ) as sin (x), expand
	__real cexp ( { 0, x } ) as cos (x).
	* tree-outof-ssa.c (rewrite_trees): Mark C in
	C = cexp (c) as addressable.

	* f95-lang.c (gfc_init_builtin_functions): Declare sincos
	builtin function.


=== builtins.c
==================================================================
--- builtins.c	(revision 72408)
+++ builtins.c	(local)
@@ -95,6 +95,7 @@
 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_sincos (tree, rtx, rtx);
 static rtx expand_builtin_int_roundingfn (tree, rtx, rtx);
 static rtx expand_builtin_args_info (tree);
 static rtx expand_builtin_next_arg (void);
@@ -2168,6 +2169,64 @@
   return target;
 }
 
+/* Expand a call to the builtin sincos math function.
+   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_sincos (tree exp, rtx target, rtx subtarget)
+{
+  rtx op0, op1, op2, target1, target2, insns;
+  tree arglist = TREE_OPERAND (exp, 1);
+  enum machine_mode mode;
+  tree arg;
+  int result;
+
+  if (!validate_arglist (arglist, REAL_TYPE,
+			 POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
+    return 0;
+
+  arg = TREE_VALUE (arglist);
+
+  /* Make a suitable register to place result in.  */
+  mode = TYPE_MODE (TREE_TYPE (arg));
+
+  /* Check if sincos insn is available, otherwise emit the call.  */
+  if (sincos_optab->handlers[(int) mode].insn_code == CODE_FOR_nothing)
+    return NULL_RTX;
+
+  target1 = gen_reg_rtx (mode);
+  target2 = gen_reg_rtx (mode);
+
+  op0 = expand_expr (arg, subtarget, VOIDmode, 0);
+  op1 = expand_expr (build_fold_indirect_ref (TREE_VALUE (TREE_CHAIN (arglist))),
+		     NULL_RTX, VOIDmode, 0);
+  op2 = expand_expr (build_fold_indirect_ref (TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)))),
+		     NULL_RTX, VOIDmode, 0);
+
+  start_sequence ();
+
+  /* Compute into target1 and target2.
+     Set TARGET to wherever the result comes back.  */
+  result = expand_twoval_unop (sincos_optab, op0, target2, target1, 0);
+  gcc_assert (result);
+
+  /* Move target1 and target2 to the memory locations indicated
+     by op1 and op2.  */
+  emit_move_insn (op1, target1);
+  emit_move_insn (op2, target2);
+
+  /* Output the entire sequence.  */
+  insns = get_insns ();
+  end_sequence ();
+  emit_insn (insns);
+
+  return const0_rtx;
+}
+
 /* Expand a call to one of the builtin rounding functions (lfloor).
    If expanding via optab fails, lower expression to (int)(floor(x)).
    EXP is the expression that is a call to the builtin function;
@@ -5651,6 +5710,14 @@
 	return target;
       break;
 
+    CASE_FLT_FN (BUILT_IN_SINCOS):
+      if (! flag_unsafe_math_optimizations)
+	break;
+      target = expand_builtin_sincos (exp, target, subtarget);
+      if (target)
+	return target;
+      break;
+
     case BUILT_IN_APPLY_ARGS:
       return expand_builtin_apply_args ();
 
@@ -6939,6 +7006,28 @@
   if (real_zerop (arg))
     return arg;
 
+  /* Canonicalize to __imag cexp ( { 0, x } ), if cexp is
+     available.  */
+  if (optimize)
+    {
+      tree type = TREE_TYPE (arg);
+      tree fn = NULL_TREE;
+      if (TYPE_MAIN_VARIANT (type) == float_type_node)
+        fn = implicit_built_in_decls[BUILT_IN_CEXPF];
+      else if (TYPE_MAIN_VARIANT (type) == double_type_node)
+        fn = implicit_built_in_decls[BUILT_IN_CEXP];
+      else if (TYPE_MAIN_VARIANT (type) == long_double_type_node)
+        fn = implicit_built_in_decls[BUILT_IN_CEXPL];
+      if (fn)
+        {
+          tree x = build2 (COMPLEX_EXPR, build_complex_type (type),
+		           build_real (type, dconst0), arg);
+	  x = build_tree_list (NULL_TREE, x);
+	  return build1 (IMAGPART_EXPR, type,
+		       build_function_call_expr (fn, x));
+        }
+    }
+
   return NULL_TREE;
 }
 
@@ -6964,6 +7053,28 @@
       return build_function_call_expr (fndecl, args);
     }
 
+  /* Canonicalize to __real cexp ( { 0, x } ), if cexp is
+     available.  */
+  if (optimize)
+    {
+      tree type = TREE_TYPE (arg);
+      tree fn = NULL_TREE;
+      if (TYPE_MAIN_VARIANT (type) == float_type_node)
+        fn = implicit_built_in_decls[BUILT_IN_CEXPF];
+      else if (TYPE_MAIN_VARIANT (type) == double_type_node)
+        fn = implicit_built_in_decls[BUILT_IN_CEXP];
+      else if (TYPE_MAIN_VARIANT (type) == long_double_type_node)
+        fn = implicit_built_in_decls[BUILT_IN_CEXPL];
+      if (fn)
+        {
+          tree x = build2 (COMPLEX_EXPR, build_complex_type (type),
+		           build_real (type, dconst0), arg);
+	  x = build_tree_list (NULL_TREE, x);
+	  return build1 (REALPART_EXPR, type,
+		         build_function_call_expr (fn, x));
+        }
+    }
+
   return NULL_TREE;
 }
 
=== expr.c
==================================================================
--- expr.c	(revision 72408)
+++ expr.c	(local)
@@ -8407,6 +8407,40 @@
 	    return const0_rtx;
 	  }
 
+	/* Transform c = cexp ( { 0, x } ) to
+	   sincos (x, &__imag c, &__real c).  */
+	if (TREE_CODE (rhs) == CALL_EXPR
+	    && TREE_ADDRESSABLE (lhs)
+	    && (DECL_FUNCTION_CODE (get_callee_fndecl (rhs)) == BUILT_IN_CEXPF
+	        || DECL_FUNCTION_CODE (get_callee_fndecl (rhs)) == BUILT_IN_CEXP
+	        || DECL_FUNCTION_CODE (get_callee_fndecl (rhs)) == BUILT_IN_CEXPL))
+	  {
+	    tree rtype = TREE_TYPE (TREE_TYPE (rhs));
+	    tree fn = NULL_TREE;
+	    tree x = TREE_VALUE (TREE_OPERAND (rhs, 1));
+	    tree r;
+	    r = fold_build1 (REALPART_EXPR, rtype, x);
+	    if (TYPE_MAIN_VARIANT (rtype) == float_type_node)
+	      fn = built_in_decls[BUILT_IN_SINCOSF];
+	    else if (TYPE_MAIN_VARIANT (rtype) == double_type_node)
+	      fn = built_in_decls[BUILT_IN_SINCOS];
+	    else if (TYPE_MAIN_VARIANT (rtype) == long_double_type_node)
+	      fn = built_in_decls[BUILT_IN_SINCOSL];
+	    if (fn && real_zerop (r))
+	      {
+		tree args;
+		tree s, c;
+		s = build_fold_addr_expr (build1 (IMAGPART_EXPR, rtype, lhs));
+		c = build_fold_addr_expr (build1 (REALPART_EXPR, rtype, lhs));
+		x = fold_build1 (IMAGPART_EXPR, rtype, x);
+		args = build_tree_list (NULL_TREE, c);
+		args = tree_cons (NULL_TREE, s, args);
+		args = tree_cons (NULL_TREE, x, args);
+		return expand_expr (build_function_call_expr (fn, args),
+				    0, VOIDmode, 0);
+	      }
+	  }
+
 	expand_assignment (lhs, rhs);
 
 	return const0_rtx;
@@ -8437,10 +8471,54 @@
       return target;
 
     case REALPART_EXPR:
+      /* Recognize __real cexp ( { 0, x } ) here and transform
+	 it back to cos (x).  */
+      if (TREE_CODE (TREE_OPERAND (exp, 0)) == CALL_EXPR
+	  && (DECL_FUNCTION_CODE (get_callee_fndecl (TREE_OPERAND (exp, 0))) == BUILT_IN_CEXPF
+	      || DECL_FUNCTION_CODE (get_callee_fndecl (TREE_OPERAND (exp, 0))) == BUILT_IN_CEXP
+	      || DECL_FUNCTION_CODE (get_callee_fndecl (TREE_OPERAND (exp, 0))) == BUILT_IN_CEXPL))
+	{
+	  tree rtype = TREE_TYPE (exp);
+	  tree x = TREE_VALUE (TREE_OPERAND (TREE_OPERAND (exp, 0), 1));
+	  tree r;
+	  r = fold_build1 (REALPART_EXPR, rtype, x);
+	  if (real_zerop (r))
+	    {
+	      tree fn = mathfn_built_in (rtype, BUILT_IN_COS);
+	      tree call = build1 (ADDR_EXPR,
+				  build_pointer_type (TREE_TYPE (fn)), fn);
+	      x = build_tree_list (NULL_TREE,
+				   fold_build1 (IMAGPART_EXPR, rtype, x));
+	      call = build3 (CALL_EXPR, rtype, call, x, NULL_TREE);
+	      return expand_expr (call, 0, VOIDmode, 0);
+	    }
+	}
       op0 = expand_expr (TREE_OPERAND (exp, 0), 0, VOIDmode, 0);
       return read_complex_part (op0, false);
 
     case IMAGPART_EXPR:
+      /* Recognize __imag cexp ( { 0, x } ) here and transform
+	 it back to sin (x).  */
+      if (TREE_CODE (TREE_OPERAND (exp, 0)) == CALL_EXPR
+	  && (DECL_FUNCTION_CODE (get_callee_fndecl (TREE_OPERAND (exp, 0))) == BUILT_IN_CEXPF
+	      || DECL_FUNCTION_CODE (get_callee_fndecl (TREE_OPERAND (exp, 0))) == BUILT_IN_CEXP
+	      || DECL_FUNCTION_CODE (get_callee_fndecl (TREE_OPERAND (exp, 0))) == BUILT_IN_CEXPL))
+	{
+	  tree rtype = TREE_TYPE (exp);
+	  tree x = TREE_VALUE (TREE_OPERAND (TREE_OPERAND (exp, 0), 1));
+	  tree r;
+	  r = fold_build1 (REALPART_EXPR, rtype, x);
+	  if (real_zerop (r))
+	    {
+	      tree fn = mathfn_built_in (rtype, BUILT_IN_SIN);
+	      tree call = build1 (ADDR_EXPR,
+				 build_pointer_type (TREE_TYPE (fn)), fn);
+	      x = build_tree_list (NULL_TREE,
+				   fold_build1 (IMAGPART_EXPR, rtype, x));
+	      call = build3 (CALL_EXPR, rtype, call, x, NULL_TREE);
+	      return expand_expr (call, 0, VOIDmode, 0);
+	    }
+	}
       op0 = expand_expr (TREE_OPERAND (exp, 0), 0, VOIDmode, 0);
       return read_complex_part (op0, true);
 
=== fortran/f95-lang.c
==================================================================
--- fortran/f95-lang.c	(revision 72408)
+++ fortran/f95-lang.c	(local)
@@ -881,6 +881,13 @@
   gfc_define_builtin ("__builtin_expect", ftype, BUILT_IN_EXPECT,
 		      "__builtin_expect", true);
 
+  tmp = tree_cons (NULL_TREE, double_type_node, void_list_node);
+  tmp = tree_cons (NULL_TREE, build_pointer_type (double_type_node), tmp);
+  tmp = tree_cons (NULL_TREE, build_pointer_type (double_type_node), tmp);
+  ftype = build_function_type (void_type_node, tmp);
+  gfc_define_builtin ("__builtin_sincos", ftype,
+                      BUILT_IN_SINCOS, "sincos", false);
+
   build_common_builtin_nodes ();
   targetm.init_builtins ();
 }
=== tree-outof-ssa.c
==================================================================
--- tree-outof-ssa.c	(revision 72408)
+++ tree-outof-ssa.c	(local)
@@ -1954,6 +1954,26 @@
 	}
     }
 
+  FOR_EACH_BB (bb)
+    {
+      for (si = bsi_start (bb); !bsi_end_p (si); )
+	{
+	  tree stmt = bsi_stmt (si);
+
+	  /* HACK, mark target of c = cexp () addressable.  */
+	  if (TREE_CODE (stmt) == MODIFY_EXPR
+	      && TREE_CODE (TREE_OPERAND (stmt, 1)) == CALL_EXPR
+	      && get_callee_fndecl (TREE_OPERAND (stmt, 1))
+              && (DECL_FUNCTION_CODE (get_callee_fndecl (TREE_OPERAND (stmt, 1))) == BUILT_IN_CEXPF
+                  || DECL_FUNCTION_CODE (get_callee_fndecl (TREE_OPERAND (stmt, 1))) == BUILT_IN_CEXP
+                  || DECL_FUNCTION_CODE (get_callee_fndecl (TREE_OPERAND (stmt, 1))) == BUILT_IN_CEXPL))
+
+	    TREE_ADDRESSABLE (TREE_OPERAND (stmt, 0)) = 1;
+
+	  bsi_next (&si);
+	}
+    }
+
   delete_elim_graph (g);
 }
 


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]