diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index fed6835d9c7267caa8d7e32066e0b63e039b2b79..8271924da317b926d2db78edfa428dd2c862fb9a 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,9 @@
+2015-10-21  Richard Sandiford  <richard.sandiford@arm.com>
+
+	* builtins.c (fold_builtin_cabs): Delete.
+	(fold_builtin_1): Update accordingly.  Handle constant arguments here.
+	* match.pd: Add rules previously handled by fold_builtin_cabs.
+
 2015-10-21  Richard Sandiford  <richard.sandiford@arm.com>
 
 	* fold-const.h (fold_strip_sign_ops): Delete.
diff --git a/gcc/builtins.c b/gcc/builtins.c
index fa02d26651b819ede9f19eb21f86e7bace22ca33..2318b2859fcfa5684b7cd28f8494bc2a250f9dcf 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -7538,82 +7538,6 @@ fold_fixed_mathfn (location_t loc, tree fndecl, tree arg)
   return NULL_TREE;
 }
 
-/* Fold call to builtin cabs, cabsf or cabsl with argument ARG.  TYPE is the
-   return type.  Return NULL_TREE if no simplification can be made.  */
-
-static tree
-fold_builtin_cabs (location_t loc, tree arg, tree type, tree fndecl)
-{
-  tree res;
-
-  if (!validate_arg (arg, COMPLEX_TYPE)
-      || TREE_CODE (TREE_TYPE (TREE_TYPE (arg))) != REAL_TYPE)
-    return NULL_TREE;
-
-  /* Calculate the result when the argument is a constant.  */
-  if (TREE_CODE (arg) == COMPLEX_CST
-      && (res = do_mpfr_arg2 (TREE_REALPART (arg), TREE_IMAGPART (arg),
-			      type, mpfr_hypot)))
-    return res;
-
-  if (TREE_CODE (arg) == COMPLEX_EXPR)
-    {
-      tree real = TREE_OPERAND (arg, 0);
-      tree imag = TREE_OPERAND (arg, 1);
-
-      /* If either part is zero, cabs is fabs of the other.  */
-      if (real_zerop (real))
-	return fold_build1_loc (loc, ABS_EXPR, type, imag);
-      if (real_zerop (imag))
-	return fold_build1_loc (loc, ABS_EXPR, type, real);
-
-      /* cabs(x+xi) -> fabs(x)*sqrt(2).  */
-      if (flag_unsafe_math_optimizations
-	  && operand_equal_p (real, imag, OEP_PURE_SAME))
-        {
-	  STRIP_NOPS (real);
-	  return fold_build2_loc (loc, MULT_EXPR, type,
-				  fold_build1_loc (loc, ABS_EXPR, type, real),
-				  build_real_truncate (type, dconst_sqrt2 ()));
-	}
-    }
-
-  /* Optimize cabs(-z) and cabs(conj(z)) as cabs(z).  */
-  if (TREE_CODE (arg) == NEGATE_EXPR
-      || TREE_CODE (arg) == CONJ_EXPR)
-    return build_call_expr_loc (loc, fndecl, 1, TREE_OPERAND (arg, 0));
-
-  /* Don't do this when optimizing for size.  */
-  if (flag_unsafe_math_optimizations
-      && optimize && optimize_function_for_speed_p (cfun))
-    {
-      tree sqrtfn = mathfn_built_in (type, BUILT_IN_SQRT);
-
-      if (sqrtfn != NULL_TREE)
-	{
-	  tree rpart, ipart, result;
-
-	  arg = builtin_save_expr (arg);
-
-	  rpart = fold_build1_loc (loc, REALPART_EXPR, type, arg);
-	  ipart = fold_build1_loc (loc, IMAGPART_EXPR, type, arg);
-
-	  rpart = builtin_save_expr (rpart);
-	  ipart = builtin_save_expr (ipart);
-
-	  result = fold_build2_loc (loc, PLUS_EXPR, type,
-				fold_build2_loc (loc, MULT_EXPR, type,
-					     rpart, rpart),
-				fold_build2_loc (loc, MULT_EXPR, type,
-					     ipart, ipart));
-
-	  return build_call_expr_loc (loc, sqrtfn, 1, result);
-	}
-    }
-
-  return NULL_TREE;
-}
-
 /* Build a complex (inf +- 0i) for the result of cproj.  TYPE is the
    complex tree type of the result.  If NEG is true, the imaginary
    zero is negative.  */
@@ -9655,7 +9579,11 @@ fold_builtin_1 (location_t loc, tree fndecl, tree arg0)
     break;
 
     CASE_FLT_FN (BUILT_IN_CABS):
-      return fold_builtin_cabs (loc, arg0, type, fndecl);
+      if (TREE_CODE (arg0) == COMPLEX_CST
+	  && TREE_CODE (TREE_TYPE (TREE_TYPE (arg0))) == REAL_TYPE)
+        return do_mpfr_arg2 (TREE_REALPART (arg0), TREE_IMAGPART (arg0),
+			     type, mpfr_hypot);
+      break;
 
     CASE_FLT_FN (BUILT_IN_CARG):
       return fold_builtin_carg (loc, arg0, type);
diff --git a/gcc/match.pd b/gcc/match.pd
index c5aa001cd71e0d3021384646d463f86155121a86..0a9598e1f799e76852b425aca1283de69bcd4b92 100644
--- a/gcc/match.pd
+++ b/gcc/match.pd
@@ -68,6 +68,7 @@ along with GCC; see the file COPYING3.  If not see
 (define_operator_list COPYSIGN BUILT_IN_COPYSIGNF
 			       BUILT_IN_COPYSIGN
 			       BUILT_IN_COPYSIGNL)
+(define_operator_list CABS BUILT_IN_CABSF BUILT_IN_CABS BUILT_IN_CABSL)
 
 /* Simplifications of operations with one constant operand and
    simplifications to constants or single values.  */
@@ -394,6 +395,13 @@ along with GCC; see the file COPYING3.  If not see
   (ccoss (negate @0))
    (ccoss @0)))
 
+/* cabs(-x) and cos(conj(x)) -> cabs(x).  */
+(for ops (conj negate)
+ (for cabss (CABS)
+  (simplify
+   (cabss (ops @0))
+   (cabss @0))))
+
 /* Fold (a * (1 << b)) into (a << b)  */
 (simplify
  (mult:c @0 (convert? (lshift integer_onep@1 @2)))
@@ -2368,6 +2376,11 @@ along with GCC; see the file COPYING3.  If not see
    (cbrts (exps @0))
    (exps (mult @0 { build_real_truncate (type, dconst_third ()); })))))
 
+/* cabs(x+0i) or cabs(0+xi) -> abs(x).  */
+(simplify
+ (CABS (complex:c @0 real_zerop@1))
+ (abs @0))
+
 /* Canonicalization of sequences of math builtins.  These rules represent
    IL simplifications but are not necessarily optimizations.
 
@@ -2459,7 +2472,12 @@ along with GCC; see the file COPYING3.  If not see
   /* cbrt(pow(x,y)) -> pow(x,y/3), iff x is nonnegative.  */
   (simplify
    (cbrts (pows tree_expr_nonnegative_p@0 @1))
-   (pows @0 (mult @1 { build_real_truncate (type, dconst_third ()); })))))
+   (pows @0 (mult @1 { build_real_truncate (type, dconst_third ()); }))))
+
+ /* cabs(x+xi) -> fabs(x)*sqrt(2).  */
+ (simplify
+  (CABS (complex @0 @0))
+  (mult (abs @0) { build_real_truncate (type, dconst_sqrt2 ()); })))
 
 /* If the real part is inf and the imag part is known to be
    nonnegative, return (inf + 0i).  */
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index 4abdf50ff9bbb31dcfcb815e92bc67a552185f99..9c76295c91689b74f21d6103d79448bce244912a 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,3 +1,9 @@
+2015-10-21  Richard Sandiford  <richard.sandiford@arm.com>
+
+	* gcc.dg/lto/20110201-1_0.c: Restrict to sqrt_insn targets.
+	Add associated options for arm*-*-*.
+	(sqrt): Remove dummy definition.
+
 2015-10-21  Richard Sandiford  <richard.sandiford@arm.com>
 
 	* gcc.dg/torture/builtin-symmetric-1.c: Don't run at -O0.
diff --git a/gcc/testsuite/gcc.dg/lto/20110201-1_0.c b/gcc/testsuite/gcc.dg/lto/20110201-1_0.c
index 5073a50fc52099d9a0d7013124b355dd3b00f2f7..068dddc3a84f7c4fd823fe8558e476c7d9bed9b4 100644
--- a/gcc/testsuite/gcc.dg/lto/20110201-1_0.c
+++ b/gcc/testsuite/gcc.dg/lto/20110201-1_0.c
@@ -1,6 +1,8 @@
 /* { dg-lto-do run } */
 /* { dg-lto-options { { -O0 -flto } } } */
+/* { dg-lto-options { "-O0 -flto -mfloat-abi=softfp -mfpu=neon-vfpv4" } { target arm*-*-* } } */
 /* { dg-require-linker-plugin "" } */
+/* { dg-require-effective-target sqrt_insn } */
 
 /* We require a linker plugin because otherwise we'd need to link
    against libm which we are not sure here has cabs on all targets.
@@ -16,13 +18,4 @@ foo (_Complex double x, int b)
   return cabs(x);
 }
 
-/* We provide a dummy sqrt to avoid link failures on targets that do not
-   expand sqrt inline.  Note that we do not link against libm in order
-   to ensure cabs is not satisfied by the library, but must be folded.  */
-double __attribute__((used))
-sqrt (double x)
-{
-  return x;
-}
-
 int main() { return 0; }