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:
David Malcolm 2023-09-07 18:43:05 -04:00
parent 7ece864add
commit 1b761fede4
7 changed files with 158 additions and 3 deletions

View file

@ -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;

View file

@ -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.

View file

@ -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);

View file

@ -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 *

View file

@ -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

View 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];
}

View 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); }