PR analyzer/110529 notes that -fanalyzer was giving up on execution
paths that follow a computed goto, due to ignoring CFG edges with the
flag EDGE_ABNORMAL set.

This patch implements enough handling for them to allow analysis of
such execution paths to continue.

Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu.
Pushed to trunk as r14-3796-g1b761fede44afa.

gcc/analyzer/ChangeLog:
        PR analyzer/110529
        * program-point.cc (program_point::on_edge): Don't reject
        EDGE_ABNORMAL for computed gotos.
        * region-model.cc (region_model::maybe_update_for_edge): Handle
        computed goto statements.
        (region_model::apply_constraints_for_ggoto): New.
        * region-model.h (region_model::apply_constraints_for_ggoto): New decl.
        * supergraph.cc (supernode::get_label): New.
        * supergraph.h (supernode::get_label): New decl.

gcc/testsuite/ChangeLog:
        PR analyzer/110529
        * c-c++-common/analyzer/computed-goto-1.c: New test.
        * gcc.dg/analyzer/computed-goto-pr110529.c: New test.
---
 gcc/analyzer/program-point.cc                 | 17 +++++-
 gcc/analyzer/region-model.cc                  | 39 +++++++++++-
 gcc/analyzer/region-model.h                   |  3 +
 gcc/analyzer/supergraph.cc                    | 13 ++++
 gcc/analyzer/supergraph.h                     |  2 +
 .../c-c++-common/analyzer/computed-goto-1.c   | 60 +++++++++++++++++++
 .../gcc.dg/analyzer/computed-goto-pr110529.c  | 27 +++++++++
 7 files changed, 158 insertions(+), 3 deletions(-)
 create mode 100644 gcc/testsuite/c-c++-common/analyzer/computed-goto-1.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/computed-goto-pr110529.c

diff --git a/gcc/analyzer/program-point.cc b/gcc/analyzer/program-point.cc
index f2d6490f0c04..d7db2f522394 100644
--- a/gcc/analyzer/program-point.cc
+++ b/gcc/analyzer/program-point.cc
@@ -426,9 +426,22 @@ program_point::on_edge (exploded_graph &eg,
       {
        const cfg_superedge *cfg_sedge = as_a <const cfg_superedge *> (succ);
 
-       /* Reject abnormal edges; we special-case setjmp/longjmp.  */
        if (cfg_sedge->get_flags () & EDGE_ABNORMAL)
-         return false;
+         {
+           const supernode *src_snode = cfg_sedge->m_src;
+           if (gimple *last_stmt = src_snode->get_last_stmt ())
+             if (last_stmt->code == GIMPLE_GOTO)
+               {
+                 /* For the program_point aspect here, consider all
+                    out-edges from goto stmts to be valid; we'll
+                    consider state separately.  */
+                 return true;
+               }
+
+           /* Reject other kinds of abnormal edges;
+              we special-case setjmp/longjmp.  */
+           return false;
+         }
       }
       break;
 
diff --git a/gcc/analyzer/region-model.cc b/gcc/analyzer/region-model.cc
index 999480e55ef7..a351e5cd214b 100644
--- a/gcc/analyzer/region-model.cc
+++ b/gcc/analyzer/region-model.cc
@@ -4997,7 +4997,7 @@ region_model::maybe_update_for_edge (const superedge 
&edge,
   if (last_stmt == NULL)
     return true;
 
-  /* Apply any constraints for conditionals/switch statements.  */
+  /* Apply any constraints for conditionals/switch/computed-goto statements.  
*/
 
   if (const gcond *cond_stmt = dyn_cast <const gcond *> (last_stmt))
     {
@@ -5013,6 +5013,12 @@ region_model::maybe_update_for_edge (const superedge 
&edge,
                                            ctxt, out);
     }
 
+  if (const ggoto *goto_stmt = dyn_cast <const ggoto *> (last_stmt))
+    {
+      const cfg_superedge *cfg_sedge = as_a <const cfg_superedge *> (&edge);
+      return apply_constraints_for_ggoto (*cfg_sedge, goto_stmt, ctxt);
+    }
+
   /* Apply any constraints due to an exception being thrown.  */
   if (const cfg_superedge *cfg_sedge = dyn_cast <const cfg_superedge *> 
(&edge))
     if (cfg_sedge->get_flags () & EDGE_EH)
@@ -5267,6 +5273,37 @@ region_model::apply_constraints_for_gswitch (const 
switch_cfg_superedge &edge,
   return sat;
 }
 
+/* Given an edge reached by GOTO_STMT, determine appropriate constraints
+   for the edge to be taken.
+
+   If they are feasible, add the constraints and return true.
+
+   Return false if the constraints contradict existing knowledge
+   (and so the edge should not be taken).  */
+
+bool
+region_model::apply_constraints_for_ggoto (const cfg_superedge &edge,
+                                          const ggoto *goto_stmt,
+                                          region_model_context *ctxt)
+{
+  tree dest = gimple_goto_dest (goto_stmt);
+  const svalue *dest_sval = get_rvalue (dest, ctxt);
+
+  /* If we know we were jumping to a specific label.  */
+  if (tree dst_label = edge.m_dest->get_label ())
+    {
+      const label_region *dst_label_reg
+       = m_mgr->get_region_for_label (dst_label);
+      const svalue *dst_label_ptr
+       = m_mgr->get_ptr_svalue (ptr_type_node, dst_label_reg);
+
+      if (!add_constraint (dest_sval, EQ_EXPR, dst_label_ptr, ctxt))
+       return false;
+    }
+
+  return true;
+}
+
 /* Apply any constraints due to an exception being thrown at LAST_STMT.
 
    If they are feasible, add the constraints and return true.
diff --git a/gcc/analyzer/region-model.h b/gcc/analyzer/region-model.h
index 1ac3a32b7a41..62d463419d67 100644
--- a/gcc/analyzer/region-model.h
+++ b/gcc/analyzer/region-model.h
@@ -589,6 +589,9 @@ private:
                                      const gswitch *switch_stmt,
                                      region_model_context *ctxt,
                                      rejected_constraint **out);
+  bool apply_constraints_for_ggoto (const cfg_superedge &edge,
+                                   const ggoto *goto_stmt,
+                                   region_model_context *ctxt);
   bool apply_constraints_for_exception (const gimple *last_stmt,
                                        region_model_context *ctxt,
                                        rejected_constraint **out);
diff --git a/gcc/analyzer/supergraph.cc b/gcc/analyzer/supergraph.cc
index a23ff15ece45..31707e743a58 100644
--- a/gcc/analyzer/supergraph.cc
+++ b/gcc/analyzer/supergraph.cc
@@ -829,6 +829,19 @@ supernode::get_stmt_index (const gimple *stmt) const
   gcc_unreachable ();
 }
 
+/* Get any label_decl for this supernode, or NULL_TREE if there isn't one.  */
+
+tree
+supernode::get_label () const
+{
+  if (m_stmts.length () == 0)
+    return NULL_TREE;
+  const glabel *label_stmt = dyn_cast<const glabel *> (m_stmts[0]);
+  if (!label_stmt)
+    return NULL_TREE;
+  return gimple_label_label (label_stmt);
+}
+
 /* Get a string for PK.  */
 
 static const char *
diff --git a/gcc/analyzer/supergraph.h b/gcc/analyzer/supergraph.h
index f8b36d789dc9..27ebd13feb2c 100644
--- a/gcc/analyzer/supergraph.h
+++ b/gcc/analyzer/supergraph.h
@@ -297,6 +297,8 @@ class supernode : public dnode<supergraph_traits>
 
   unsigned int get_stmt_index (const gimple *stmt) const;
 
+  tree get_label () const;
+
   function * const m_fun; // alternatively could be stored as runs of indices 
within the supergraph
   const basic_block m_bb;
   gcall * const m_returning_call; // for handling the result of a returned call
diff --git a/gcc/testsuite/c-c++-common/analyzer/computed-goto-1.c 
b/gcc/testsuite/c-c++-common/analyzer/computed-goto-1.c
new file mode 100644
index 000000000000..d6877d3959fe
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/analyzer/computed-goto-1.c
@@ -0,0 +1,60 @@
+#include "../../gcc.dg/analyzer/analyzer-decls.h"
+
+void test_1 (int pc)
+{
+  void *arr[2] = {&&x, &&y};
+  
+  goto *arr[pc];
+
+x:
+  __analyzer_dump_path (); /* { dg-message "path" } */
+  __analyzer_eval (pc == 0); /* { dg-warning "TRUE" "true" { xfail *-*-* } .-1 
} */
+  /* { dg-bogus "UNKNOWN" "unknown" { xfail *-*-* } .-1 } */
+  return;
+
+ y:
+  __analyzer_dump_path (); /* { dg-message "path" } */
+  __analyzer_eval (pc == 1); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+  /* { dg-bogus "UNKNOWN" "unknown" { xfail *-*-* } .-1 } */
+  return;
+}
+
+void test_duplicates (int pc)
+{
+  void *arr[3] = {&&x, &&y, &&x};
+  int var = 0;
+    
+  goto *arr[pc];
+
+ x:
+  __analyzer_dump_path (); /* { dg-message "path" } */
+  __analyzer_eval (pc == 0); /* { dg-warning "UNKNOWN" } */
+  return;
+
+ y:
+  __analyzer_dump_path (); /* { dg-message "path" } */
+  __analyzer_eval (pc == 1); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+  /* { dg-bogus "UNKNOWN" "unknown" { xfail *-*-* } .-1 } */
+  return;
+}
+
+void test_multiple (int pc)
+{
+  void *arr[2] = {&&x, &&y};
+  
+  goto *arr[pc];
+
+x:
+  __analyzer_dump_path (); /* { dg-message "path" } */
+  __analyzer_eval (pc == 0); /* { dg-warning "TRUE" "true" { xfail *-*-* } .-1 
} */
+  /* { dg-bogus "UNKNOWN" "unknown" { xfail *-*-* } .-1 } */
+
+  goto *arr[pc];
+
+ y:
+  __analyzer_dump_path (); /* { dg-message "path" } */
+  __analyzer_eval (pc == 1); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+  /* { dg-bogus "UNKNOWN" "unknown" { xfail *-*-* } .-1 } */
+
+  goto *arr[pc];
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/computed-goto-pr110529.c 
b/gcc/testsuite/gcc.dg/analyzer/computed-goto-pr110529.c
new file mode 100644
index 000000000000..988f94a0e819
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/computed-goto-pr110529.c
@@ -0,0 +1,27 @@
+/* C only: reuse of same array for int and label pointers.  */
+
+#include "../../gcc.dg/analyzer/analyzer-decls.h"
+
+void foo(int pc) {
+    int *arr[2] = {&&x, &&y};
+    int var = 0;
+    __analyzer_dump_path (); /* { dg-message "path" } */
+    
+    goto *arr[pc];
+
+x:
+    __analyzer_dump_path (); /* { dg-message "path" } */
+    __analyzer_eval (pc == 0); /* { dg-warning "TRUE" } */
+    /* { dg-bogus "UNKNOWN" "unknown" { xfail *-*-* } .-1 } */
+    arr[0] = (void *)0;
+    *arr[0] = 10086; /* { dg-warning "dereference of NULL" } */
+    return;
+y:
+    __analyzer_dump_path (); /* { dg-message "path" } */
+    __analyzer_eval (pc == 1); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+    /* { dg-bogus "FALSE" "" { target *-*-* } .-1 } */
+    /* { dg-bogus "UNKNOWN" "unknown" { xfail *-*-* } .-2 } */
+    return;
+}
+
+int main() { foo(0); }
-- 
2.26.3

Reply via email to