[pushed] analyzer: basic support for computed gotos (PR analyzer/110529)
Checks
Commit Message
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
@@ -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;
@@ -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.
@@ -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);
@@ -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 *
@@ -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
new file mode 100644
@@ -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];
+}
new file mode 100644
@@ -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); }