Hi,

the patch below might be useful for testcase preparation and debugging
compiler bugs such as PR 60965.  When
-ftrap-on-impossible-devirtualization is supplied on the command line,
it makes the devirtualization produce __builtin_trap instead of
__builtin_unreachable when it comes to the conclusion that there is no
legal target of a virtual call.

Apart from dealing with our bugs, it may be even useful to debug
compiled programs when a user triggers some sort of illegal
devirtualization, typically by missing a type check somewhere.
Currently the compiled program might simply take a wrong branch, with
the patch it will abort.

Bootstrapped and tested (with the option on) on x86_64-linux, I have
also successfully LTO built Firefox with it.  If I add some
documentation, would like to see this in trunk?

Thanks,

Martin


2014-04-03  Martin Jambor  <mjam...@suse.cz>

        * cgraph.c (verify_edge_corresponds_to_fndecl): Also always accept
        builtin_trap.
        * cgraphclones.c (cgraph_clone_node): Do not redirect calls to
        builtin_trap.
        * cgraphunit.c (walk_polymorphic_call_targets): Use
        ipa_impossible_devirt_target_node.
        * common.opt (ftrap-on-impossible-devirtualization): New option.
        * gimple-fold.c (fold_gimple_assign): Use
        ipa_impossible_devirt_target_decl.
        (gimple_fold_call): Likewise.
        (gimple_get_virt_method_for_vtable): Likewise.
        * ipa-cp.c (ipa_get_indirect_edge_target_1): Check also for
        builtin_trap, use ipa_impossible_devirt_target_decl.
        * ipa-devirt.c (ipa_impossible_devirt_target_decl): New function.
        (ipa_impossible_devirt_target_node): Likewise.
        * ipa-prop.c (ipa_make_edge_direct_to_target): Use
        ipa_impossible_devirt_target_decl.
        (try_make_edge_direct_virtual_call): Check also for builtin_trap, use
        ipa_impossible_devirt_target_decl.
        * ipa-utils.h (ipa_impossible_devirt_target_decl): Declare.
        (ipa_impossible_devirt_target_node): Likewise.
        * ipa.c (walk_polymorphic_call_targets): Use
        ipa_impossible_devirt_target_node.


diff --git a/gcc/cgraph.c b/gcc/cgraph.c
index be3661a..25e0775 100644
--- a/gcc/cgraph.c
+++ b/gcc/cgraph.c
@@ -2664,9 +2664,10 @@ verify_edge_corresponds_to_fndecl (struct cgraph_edge 
*e, tree decl)
     return false;
 
   /* Optimizers can redirect unreachable calls or calls triggering undefined
-     behaviour to builtin_unreachable.  */
+     behaviour to builtin_unreachable or builtin_trap.  */
   if (DECL_BUILT_IN_CLASS (e->callee->decl) == BUILT_IN_NORMAL
-      && DECL_FUNCTION_CODE (e->callee->decl) == BUILT_IN_UNREACHABLE)
+      && (DECL_FUNCTION_CODE (e->callee->decl) == BUILT_IN_UNREACHABLE
+         || DECL_FUNCTION_CODE (e->callee->decl) == BUILT_IN_TRAP))
     return false;
   node = cgraph_function_or_thunk_node (node, NULL);
 
diff --git a/gcc/cgraphclones.c b/gcc/cgraphclones.c
index cd2d73d..e7bebe3 100644
--- a/gcc/cgraphclones.c
+++ b/gcc/cgraphclones.c
@@ -449,8 +449,9 @@ cgraph_clone_node (struct cgraph_node *n, tree decl, 
gcov_type count, int freq,
         be unreachable during the clonning procedure.  */
       if (!e->callee
          || DECL_BUILT_IN_CLASS (e->callee->decl) != BUILT_IN_NORMAL
-         || DECL_FUNCTION_CODE (e->callee->decl) != BUILT_IN_UNREACHABLE)
-        redirect_edge_duplicating_thunks (e, new_node, args_to_skip);
+         || (DECL_FUNCTION_CODE (e->callee->decl) != BUILT_IN_UNREACHABLE
+             && DECL_FUNCTION_CODE (e->callee->decl) != BUILT_IN_TRAP))
+        cgraph_redirect_edge_callee (e, new_node);
     }
 
 
diff --git a/gcc/cgraphunit.c b/gcc/cgraphunit.c
index 06283fc..6c56c90 100644
--- a/gcc/cgraphunit.c
+++ b/gcc/cgraphunit.c
@@ -892,8 +892,7 @@ walk_polymorphic_call_targets (pointer_set_t 
*reachable_call_targets,
          if (targets.length () == 1)
            target = targets[0];
          else
-           target = cgraph_get_create_node
-                      (builtin_decl_implicit (BUILT_IN_UNREACHABLE));
+           target = ipa_impossible_devirt_target_node ();
 
          if (cgraph_dump_file)
            {
diff --git a/gcc/common.opt b/gcc/common.opt
index da275e5..3e8b359 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -1019,6 +1019,10 @@ fdevirtualize
 Common Report Var(flag_devirtualize) Optimization
 Try to convert virtual calls to direct ones.
 
+ftrap-on-impossible-devirtualization
+Common Report Var(flag_trap_impossible_devirt)
+Convert virtual calls that cannot have any target to builtin_trap.
+
 fdiagnostics-show-location=
 Common Joined RejectNegative Enum(diagnostic_prefixing_rule)
 -fdiagnostics-show-location=[once|every-line]  How often to emit source 
location at the beginning of line-wrapped diagnostics
diff --git a/gcc/gimple-fold.c b/gcc/gimple-fold.c
index 6402cce..e0dfcb9 100644
--- a/gcc/gimple-fold.c
+++ b/gcc/gimple-fold.c
@@ -392,7 +392,7 @@ fold_gimple_assign (gimple_stmt_iterator *si)
                    if (targets.length () == 1)
                      fndecl = targets[0]->decl;
                    else
-                     fndecl = builtin_decl_implicit (BUILT_IN_UNREACHABLE);
+                     fndecl = ipa_impossible_devirt_target_decl ();
                    val = fold_convert (TREE_TYPE (val), fndecl);
                    STRIP_USELESS_TYPE_CONVERSION (val);
                    return val;
@@ -1146,7 +1146,7 @@ gimple_fold_call (gimple_stmt_iterator *gsi, bool inplace)
                }
              else
                {
-                 tree fndecl = builtin_decl_implicit (BUILT_IN_UNREACHABLE);
+                 tree fndecl = ipa_impossible_devirt_target_decl ();
                  gimple new_stmt = gimple_build_call (fndecl, 0);
                  gimple_set_location (new_stmt, gimple_location (stmt));
                  if (lhs && TREE_CODE (lhs) == SSA_NAME)
@@ -3332,7 +3332,7 @@ gimple_get_virt_method_for_vtable (HOST_WIDE_INT token,
   if (!fn
       || (TREE_CODE (fn) != ADDR_EXPR && TREE_CODE (fn) != FDESC_EXPR)
       || TREE_CODE (TREE_OPERAND (fn, 0)) != FUNCTION_DECL)
-    fn = builtin_decl_implicit (BUILT_IN_UNREACHABLE);
+    fn = ipa_impossible_devirt_target_decl ();
   else
     {
       fn = TREE_OPERAND (fn, 0);
diff --git a/gcc/ipa-cp.c b/gcc/ipa-cp.c
index 479963c..124f31d 100644
--- a/gcc/ipa-cp.c
+++ b/gcc/ipa-cp.c
@@ -1584,7 +1584,8 @@ ipa_get_indirect_edge_target_1 (struct cgraph_edge *ie,
          if (target)
            {
              if ((TREE_CODE (TREE_TYPE (target)) == FUNCTION_TYPE
-                  && DECL_FUNCTION_CODE (target) == BUILT_IN_UNREACHABLE)
+                  && (DECL_FUNCTION_CODE (target) == BUILT_IN_UNREACHABLE
+                      || DECL_FUNCTION_CODE (target) == BUILT_IN_TRAP))
                  || !possible_polymorphic_call_target_p
                       (ie, cgraph_get_node (target)))
                {
@@ -1593,7 +1594,7 @@ ipa_get_indirect_edge_target_1 (struct cgraph_edge *ie,
                             "Type inconsident devirtualization: %s/%i->%s\n",
                             ie->caller->name (), ie->caller->order,
                             IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (target)));
-                 target = builtin_decl_implicit (BUILT_IN_UNREACHABLE);
+                 target = ipa_impossible_devirt_target_decl ();
                  cgraph_get_create_node (target);
                }
              return target;
@@ -1629,7 +1630,7 @@ ipa_get_indirect_edge_target_1 (struct cgraph_edge *ie,
       if (targets.length () == 1)
        target = targets[0]->decl;
       else
-       target = builtin_decl_implicit (BUILT_IN_UNREACHABLE);
+       target = ipa_impossible_devirt_target_decl ();
     }
   else
     {
@@ -1649,7 +1650,7 @@ ipa_get_indirect_edge_target_1 (struct cgraph_edge *ie,
                 "Type inconsident devirtualization: %s/%i->%s\n",
                 ie->caller->name (), ie->caller->order,
                 IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (target)));
-      target = builtin_decl_implicit (BUILT_IN_UNREACHABLE);
+      target = ipa_impossible_devirt_target_decl ();
       cgraph_get_create_node (target);
     }
 
diff --git a/gcc/ipa-devirt.c b/gcc/ipa-devirt.c
index d484b20..600836d 100644
--- a/gcc/ipa-devirt.c
+++ b/gcc/ipa-devirt.c
@@ -2148,4 +2148,26 @@ make_pass_ipa_devirt (gcc::context *ctxt)
   return new pass_ipa_devirt (ctxt);
 }
 
+/* Return function declaration that we want to generate call to when
+   encountering a a virtual call which cannot have any valid target. */
+
+tree
+ipa_impossible_devirt_target_decl (void)
+{
+  if (flag_trap_impossible_devirt)
+    return builtin_decl_implicit (BUILT_IN_TRAP);
+  else
+    return builtin_decl_implicit (BUILT_IN_UNREACHABLE);
+}
+
+/* Return call graph node of a function that we want to generate call to when
+   encountering a a virtual call which cannot have any valid target. */
+
+cgraph_node *
+ipa_impossible_devirt_target_node (void)
+{
+  return cgraph_get_create_node (ipa_impossible_devirt_target_decl ());
+}
+
+
 #include "gt-ipa-devirt.h"
diff --git a/gcc/ipa-prop.c b/gcc/ipa-prop.c
index 9f144fa..42623f4 100644
--- a/gcc/ipa-prop.c
+++ b/gcc/ipa-prop.c
@@ -2494,7 +2494,7 @@ ipa_make_edge_direct_to_target (struct cgraph_edge *ie, 
tree target)
            fprintf (dump_file, "ipa-prop: Discovered direct call to 
non-function"
                                " in %s/%i, making it unreachable.\n",
                     ie->caller->name (), ie->caller->order);
-         target = builtin_decl_implicit (BUILT_IN_UNREACHABLE);
+         target = ipa_impossible_devirt_target_decl ();
          callee = cgraph_get_create_node (target);
          unreachable = true;
        }
@@ -2732,7 +2732,8 @@ try_make_edge_direct_virtual_call (struct cgraph_edge *ie,
          if (target)
            {
              if ((TREE_CODE (TREE_TYPE (target)) == FUNCTION_TYPE
-                  && DECL_FUNCTION_CODE (target) == BUILT_IN_UNREACHABLE)
+                  && (DECL_FUNCTION_CODE (target) == BUILT_IN_UNREACHABLE
+                      || DECL_FUNCTION_CODE (target) == BUILT_IN_TRAP))
                  || !possible_polymorphic_call_target_p
                       (ie, cgraph_get_node (target)))
                {
@@ -2741,7 +2742,7 @@ try_make_edge_direct_virtual_call (struct cgraph_edge *ie,
                             "Type inconsident devirtualization: %s/%i->%s\n",
                             ie->caller->name (), ie->caller->order,
                             IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (target)));
-                 target = builtin_decl_implicit (BUILT_IN_UNREACHABLE);
+                 target = ipa_impossible_devirt_target_decl ();
                  cgraph_get_create_node (target);
                }
              return ipa_make_edge_direct_to_target (ie, target);
@@ -2774,7 +2775,7 @@ try_make_edge_direct_virtual_call (struct cgraph_edge *ie,
        target = targets[0]->decl;
       else
        {
-          target = builtin_decl_implicit (BUILT_IN_UNREACHABLE);
+          target = ipa_impossible_devirt_target_decl ();
          cgraph_get_create_node (target);
        }
     }
diff --git a/gcc/ipa-utils.h b/gcc/ipa-utils.h
index a2c985a..81c7983 100644
--- a/gcc/ipa-utils.h
+++ b/gcc/ipa-utils.h
@@ -186,6 +186,10 @@ possible_polymorphic_call_target_p (tree call,
                                             ipa_dummy_polymorphic_call_context,
                                             n);
 }
+
+tree ipa_impossible_devirt_target_decl (void);
+cgraph_node *ipa_impossible_devirt_target_node (void);
+
 #endif  /* GCC_IPA_UTILS_H  */
 
 
diff --git a/gcc/ipa.c b/gcc/ipa.c
index 8b65abd..e8fd04d 100644
--- a/gcc/ipa.c
+++ b/gcc/ipa.c
@@ -219,8 +219,7 @@ walk_polymorphic_call_targets (pointer_set_t 
*reachable_call_targets,
          if (targets.length () == 1)
            target = targets[0];
          else
-           target = cgraph_get_create_node
-                      (builtin_decl_implicit (BUILT_IN_UNREACHABLE));
+           target = ipa_impossible_devirt_target_node ();
 
          if (dump_file)
            fprintf (dump_file,

Reply via email to