analyzer: basic support for computed gotos (PR analyzer/110529)
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. 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. Signed-off-by: David Malcolm <dmalcolm@redhat.com>
This commit is contained in:
parent
7ece864add
commit
1b761fede4
7 changed files with 158 additions and 3 deletions
|
@ -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
|
||||
|
|
60
gcc/testsuite/c-c++-common/analyzer/computed-goto-1.c
Normal file
60
gcc/testsuite/c-c++-common/analyzer/computed-goto-1.c
Normal file
|
@ -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];
|
||||
}
|
27
gcc/testsuite/gcc.dg/analyzer/computed-goto-pr110529.c
Normal file
27
gcc/testsuite/gcc.dg/analyzer/computed-goto-pr110529.c
Normal file
|
@ -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); }
|
Loading…
Add table
Reference in a new issue