diff --git a/gcc/analyzer/program-point.cc b/gcc/analyzer/program-point.cc index f2d6490f0c0..d7db2f52239 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 (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 999480e55ef..a351e5cd214 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 (last_stmt)) { @@ -5013,6 +5013,12 @@ region_model::maybe_update_for_edge (const superedge &edge, ctxt, out); } + if (const ggoto *goto_stmt = dyn_cast (last_stmt)) + { + const cfg_superedge *cfg_sedge = as_a (&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 (&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 1ac3a32b7a4..62d463419d6 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 a23ff15ece4..31707e743a5 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 (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 f8b36d789dc..27ebd13feb2 100644 --- a/gcc/analyzer/supergraph.h +++ b/gcc/analyzer/supergraph.h @@ -297,6 +297,8 @@ class supernode : public dnode 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 00000000000..d6877d3959f --- /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 00000000000..988f94a0e81 --- /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); }