diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index c81598c1d4387c44bdc16a5eac02f163eec5b642..82a3641ea2884533604ef53b674ebd58a075ba83 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,11 @@
+2006-12-14  Richard Guenther  <rguenther@suse.de>
+
+	PR tree-optimization/30197
+	* builtins.c (fold_builtin_cexp): New function to fold cexp
+	to cexpi and exp parts.
+	* tree-ssa-pre.c (try_combine_conversion): Also handle
+	REALPART_EXPR and IMAGPART_EXPR.
+
 2006-12-14  Richard Guenther  <rguenther@suse.de>
 
 	PR middle-end/30172
diff --git a/gcc/builtins.c b/gcc/builtins.c
index d0271bc2308342f7bfe25c5fe2500a124479041d..f5798e7894774c15d3019f0cb0355fb87067cbdf 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -7367,6 +7367,67 @@ fold_builtin_sincos (tree arglist)
 			 build1 (REALPART_EXPR, type, call)));
 }
 
+/* Fold function call to builtin cexp, cexpf, or cexpl.  Return
+   NULL_TREE if no simplification can be made.  */
+
+static tree
+fold_builtin_cexp (tree arglist, tree type)
+{
+  tree arg0, rtype;
+  tree realp, imagp, ifn;
+
+  if (!validate_arglist (arglist, COMPLEX_TYPE, VOID_TYPE))
+    return NULL_TREE;
+
+  arg0 = TREE_VALUE (arglist);
+  rtype = TREE_TYPE (TREE_TYPE (arg0));
+
+  /* In case we can figure out the real part of arg0 and it is constant zero
+     fold to cexpi.  */
+  ifn = mathfn_built_in (rtype, BUILT_IN_CEXPI);
+  if (!ifn)
+    return NULL_TREE;
+
+  if ((realp = fold_unary (REALPART_EXPR, rtype, arg0))
+      && real_zerop (realp))
+    {
+      tree narg = fold_build1 (IMAGPART_EXPR, rtype, arg0);
+      return build_function_call_expr (ifn, build_tree_list (NULL_TREE, narg));
+    }
+
+  /* In case we can easily decompose real and imaginary parts split cexp
+     to exp (r) * cexpi (i).  */
+  if (flag_unsafe_math_optimizations
+      && realp)
+    {
+      tree rfn, rcall, icall;
+
+      rfn = mathfn_built_in (rtype, BUILT_IN_EXP);
+      if (!rfn)
+	return NULL_TREE;
+
+      imagp = fold_unary (IMAGPART_EXPR, rtype, arg0);
+      if (!imagp)
+	return NULL_TREE;
+
+      icall = build_function_call_expr (ifn,
+				        build_tree_list (NULL_TREE, imagp));
+      icall = builtin_save_expr (icall);
+      rcall = build_function_call_expr (rfn,
+				        build_tree_list (NULL_TREE, realp));
+      rcall = builtin_save_expr (rcall);
+      return build2 (COMPLEX_EXPR, type,
+		     build2 (MULT_EXPR, rtype,
+			     rcall,
+			     build1 (REALPART_EXPR, rtype, icall)),
+		     build2 (MULT_EXPR, rtype,
+			     rcall,
+			     build1 (IMAGPART_EXPR, rtype, icall)));
+    }
+
+  return NULL_TREE;
+}
+
 /* Fold function call to builtin trunc, truncf or truncl.  Return
    NULL_TREE if no simplification can be made.  */
 
@@ -9312,6 +9373,9 @@ fold_builtin_1 (tree fndecl, tree arglist, bool ignore)
     CASE_FLT_FN (BUILT_IN_SINCOS):
       return fold_builtin_sincos (arglist);
 
+    CASE_FLT_FN (BUILT_IN_CEXP):
+      return fold_builtin_cexp (arglist, type);
+
     CASE_FLT_FN (BUILT_IN_CEXPI):
       if (validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
 	return do_mpfr_sincos (TREE_VALUE (arglist), NULL_TREE, NULL_TREE);
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index 296a34ca7c3081beaf31dba7a1fe509fd09eace9..ee65e74c5812613ab552885d0e85318815668990 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,3 +1,9 @@
+2006-12-14  Richard Guenther  <rguenther@suse.de>
+
+	PR tree-optimization/30197
+	* gcc.c-torture/execute/complex-1.c: Fix function name.
+	* gcc.dg/builtins-61.c: New testcase.
+
 2006-12-14  Richard Guenther  <rguenther@suse.de>
 
 	PR middle-end/30172
diff --git a/gcc/testsuite/gcc.c-torture/execute/complex-1.c b/gcc/testsuite/gcc.c-torture/execute/complex-1.c
index 424fa65b1502295670636d9f5d860999b59c7791..59101796475c87b7b537331243e301e8cf9eb442 100644
--- a/gcc/testsuite/gcc.c-torture/execute/complex-1.c
+++ b/gcc/testsuite/gcc.c-torture/execute/complex-1.c
@@ -17,7 +17,7 @@ g2 (double x)
 }
 
 __complex__ double
-cexp (__complex__ double x)
+xcexp (__complex__ double x)
 {
   double r;
 
@@ -31,7 +31,7 @@ main ()
 {
   __complex__ double x;
 
-  x = cexp (1.0i);
+  x = xcexp (1.0i);
   if (__real__ x != -1.0)
     abort ();
   if (__imag__ x != 0.0)
diff --git a/gcc/testsuite/gcc.dg/builtins-61.c b/gcc/testsuite/gcc.dg/builtins-61.c
new file mode 100644
index 0000000000000000000000000000000000000000..9f0c99065da3e0f5e7986c2dc6bf72c6d4a3440b
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtins-61.c
@@ -0,0 +1,31 @@
+/* { dg-do compile } */
+/* { dg-options "-O -ffast-math -fdump-tree-optimized" } */
+
+double test1 (double x)
+{
+  return __real __builtin_cexp(x * (__extension__ 1.0iF));
+}
+
+double test2(double x)
+{
+  return __imag __builtin_cexp((__extension__ 1.0iF) * x);
+}
+
+double test3(double x)
+{
+  _Complex c = __builtin_cexp(x * (__extension__ 1.0iF));
+  return __imag c + __real c;
+}
+
+double test4(double x, double y)
+{
+  _Complex c = __builtin_cexp(x);
+  x = __builtin_exp (x);
+  return x - __real c;
+}
+
+/* { dg-final { scan-tree-dump "cexpi" "optimized" } } */
+/* { dg-final { scan-tree-dump "sin" "optimized" } } */
+/* { dg-final { scan-tree-dump "cos" "optimized" } } */
+/* { dg-final { scan-tree-dump "return 0.0" "optimized" } } */
+/* { dg-final { cleanup-tree-dump "optimized" } } */
diff --git a/gcc/tree-ssa-pre.c b/gcc/tree-ssa-pre.c
index 871006dc232aea1113e3d38f42ef80c75601e053..72af16d2caa8573063152e6bd137e890ac031d37 100644
--- a/gcc/tree-ssa-pre.c
+++ b/gcc/tree-ssa-pre.c
@@ -3613,7 +3613,9 @@ try_combine_conversion (tree *expr_p)
   unsigned int firstbit;
 
   if (!((TREE_CODE (expr) == NOP_EXPR
-	 || TREE_CODE (expr) == CONVERT_EXPR)
+	 || TREE_CODE (expr) == CONVERT_EXPR
+	 || TREE_CODE (expr) == REALPART_EXPR
+	 || TREE_CODE (expr) == IMAGPART_EXPR)
 	&& TREE_CODE (TREE_OPERAND (expr, 0)) == VALUE_HANDLE
 	&& !VALUE_HANDLE_VUSES (TREE_OPERAND (expr, 0))))
     return false;