{
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;
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))
{
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)
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.
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);
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 *
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
--- /dev/null
+#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];
+}
--- /dev/null
+/* 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); }