2012-06-02  Paolo Bonzini  <bonzini@gnu.org>

	* tree-vrp.c (simplify_builtin_using_ranges): New function.
	(simplify_stmt_using_ranges): Call it.

testsuite:
2012-06-02  Paolo Bonzini  <bonzini@gnu.org>

	* gcc-dg/tree-ssa/vrp68.c: New testcase.

Index: tree-vrp.c
===================================================================
--- tree-vrp.c	(revision 187910)
+++ tree-vrp.c	(working copy)
@@ -7729,6 +7729,86 @@
   return false;
 }
 
+/* Simplify a call to builtin based on the range information for the
+   operands.  */
+
+static bool
+simplify_builtin_using_ranges (gimple_stmt_iterator *gsi, gimple stmt)
+{
+  tree fndecl = gimple_call_fndecl (stmt);
+  enum built_in_function target_builtin_code;
+
+  if (DECL_BUILT_IN_CLASS (fndecl) != BUILT_IN_NORMAL)
+    return false;
+  
+  switch (DECL_FUNCTION_CODE (fndecl))
+    {
+    case BUILT_IN_FFS:
+      target_builtin_code = BUILT_IN_CTZ;
+      goto optimize_ffs;
+    case BUILT_IN_FFSL:
+      target_builtin_code = BUILT_IN_CTZL;
+      goto optimize_ffs;
+    case BUILT_IN_FFSLL:
+      target_builtin_code = BUILT_IN_CTZLL;
+    optimize_ffs:
+      {
+        tree op0 = gimple_call_arg(stmt, 0);
+        value_range_t *vr = get_value_range (op0);
+        tree argtype, tem, val, lhs;
+        bool sop;
+        gimple newop;
+
+        val = compare_range_with_value (NE_EXPR, vr, integer_zero_node, &sop);
+        if (!val || !integer_onep (val))
+          return false;
+
+        if (sop && issue_strict_overflow_warning (WARN_STRICT_OVERFLOW_MISC))
+          {
+            location_t location;
+
+            if (!gimple_has_location (stmt))
+	      location = input_location;
+            else
+	      location = gimple_location (stmt);
+            warning_at (location, OPT_Wstrict_overflow,
+			"assuming signed overflow does not occur when "
+			"simplifying %<ffs (X)%> to %<ctz (X) + 1%>");
+	  }
+
+        fndecl = builtin_decl_implicit (target_builtin_code);
+        lhs = gimple_call_lhs (stmt);
+        gcc_assert (TREE_TYPE (TREE_TYPE (fndecl)) == TREE_TYPE (lhs));
+
+        /* Convert argument type.  */
+        argtype = TREE_VALUE (TYPE_ARG_TYPES (TREE_TYPE (fndecl)));
+        tem = create_tmp_reg (argtype, NULL);
+        newop = gimple_build_assign_with_ops (NOP_EXPR, tem, op0, NULL_TREE);
+        tem = make_ssa_name (tem, newop);
+        gimple_assign_set_lhs (newop, tem);
+        gsi_insert_before (gsi, newop, GSI_SAME_STMT);
+
+        /* Modify call in place.  */
+        tem = create_tmp_reg (TREE_TYPE (lhs), NULL);
+        tem = make_ssa_name (tem, stmt);
+        gimple_call_set_lhs (stmt, tem);
+        gimple_call_set_fndecl (stmt, fndecl);
+        update_stmt (stmt);
+
+        /* Sum one to the result.  */
+        newop = gimple_build_assign_with_ops (PLUS_EXPR, lhs, tem,
+                                              fold_convert (TREE_TYPE (lhs),
+                                                            integer_one_node));
+        gsi_insert_after (gsi, newop, GSI_NEW_STMT);
+        return true;
+      }
+    default:
+      break;
+    }
+
+  return false;
+}
+
 /* Simplify an integral conversion from an SSA name in STMT.  */
 
 static bool
@@ -7997,6 +8077,10 @@
     return simplify_cond_using_ranges (stmt);
   else if (gimple_code (stmt) == GIMPLE_SWITCH)
     return simplify_switch_using_ranges (stmt);
+  else if (gimple_code (stmt) == GIMPLE_CALL
+           && gimple_call_fndecl (stmt) != NULL_TREE
+           && DECL_BUILT_IN (gimple_call_fndecl (stmt)))
+    return simplify_builtin_using_ranges (gsi, stmt);
 
   return false;
 }
Index: testsuite/gcc.dg/tree-ssa/vrp68.c
===================================================================
--- testsuite/gcc.dg/tree-ssa/vrp68.c	(revision 0)
+++ testsuite/gcc.dg/tree-ssa/vrp68.c	(revision 0)
@@ -0,0 +1,30 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-vrp1 -fdump-tree-optimized" } */
+
+extern void abort(void);
+
+unsigned foo (unsigned i)
+{
+  if (i == 0)
+    abort ();
+  return ffs (i) - 1;
+}
+
+unsigned fool (unsigned long i)
+{
+  if (i == 0)
+    abort ();
+  return ffsl (i) - 1;
+}
+
+unsigned fooll (unsigned long long i)
+{
+  if (i == 0)
+    abort ();
+  return ffsll (i) - 1;
+}
+
+/* { dg-final { scan-tree-dump-times "ctz" 3 "vrp1" } } */
+/* { dg-final { scan-tree-dump-times "\\\+ 1" 0 "optimized" } } */
+/* { dg-final { cleanup-tree-dump "vrp1" } } */
+/* { dg-final { cleanup-tree-dump "optimized" } } */
