analyzer: rewrite of switch handling
When investigating false positives on the Linux kernel from -Wanalyzer-use-of-uninitialized-value, I noticed that the existing implementation of switch statements in the analyzer is broken. Specifically, the existing implementation assumes a 1:1 association between CFG out-edges from the basic block and case labels in the gimple switch statement. This happened to be the case in the examples I had tested, but there is no such association in general. In particular, in the motivating example: arch/x86/kernel/cpu/mtrr/if.c: mtrr_ioctl the switch statement has 3 blocks, each covering multiple ranges of ioctl command IDs for which different local variables are initialized, which the existing implementation gets badly wrong. [1] This patch reimplements switch handling in the analyzer to eliminate this false assumption - instead, for each out-edge we gather the set of case labels for that out-edge, and use that to determine the set of value ranges for the edge. Avoiding false positives for the above example requires that we accurately track value ranges for symbolic values, so the patch extends constraint_manager with a new bounded_ranges_constraint, adding just enough information to capture the ranges for switch statements whilst retaining combatility with the existing constraint-handling (ultimately I'd prefer to simply throw all of this into a SAT solver and let it track things). Doing so fixes the false positives seen on the Linux kernel and an existing xfail in the test suite. The patch also fixes a long-standing bug in constraint_manager::add_unknown_constraint when updating constraints due to combining equivalence classes, spotted when debugging the same logic for the new kind of constraints. [1] a reduced version of this code is captured in this patch, in gcc.dg/analyzer/torture/switch-3.c gcc/analyzer/ChangeLog: * analyzer.h (struct rejected_constraint): Convert to... (class rejected_constraint): ...this. (class bounded_ranges): New forward decl. (class bounded_ranges_manager): New forward decl. * constraint-manager.cc: Include "analyzer/analyzer-logging.h" and "tree-pretty-print.h". (can_plus_one_p): New. (plus_one): New. (can_minus_one_p): New. (minus_one): New. (bounded_range::bounded_range): New. (dump_cst): New. (bounded_range::dump_to_pp): New. (bounded_range::dump): New. (bounded_range::to_json): New. (bounded_range::set_json_attr): New. (bounded_range::contains_p): New. (bounded_range::intersects_p): New. (bounded_range::operator==): New. (bounded_range::cmp): New. (bounded_ranges::bounded_ranges): New. (bounded_ranges::bounded_ranges): New. (bounded_ranges::bounded_ranges): New. (bounded_ranges::canonicalize): New. (bounded_ranges::validate): New. (bounded_ranges::operator==): New. (bounded_ranges::dump_to_pp): New. (bounded_ranges::dump): New. (bounded_ranges::to_json): New. (bounded_ranges::eval_condition): New. (bounded_ranges::contain_p): New. (bounded_ranges::cmp): New. (bounded_ranges_manager::~bounded_ranges_manager): New. (bounded_ranges_manager::get_or_create_empty): New. (bounded_ranges_manager::get_or_create_point): New. (bounded_ranges_manager::get_or_create_range): New. (bounded_ranges_manager::get_or_create_union): New. (bounded_ranges_manager::get_or_create_intersection): New. (bounded_ranges_manager::get_or_create_inverse): New. (bounded_ranges_manager::consolidate): New. (bounded_ranges_manager::get_or_create_ranges_for_switch): New. (bounded_ranges_manager::create_ranges_for_switch): New. (bounded_ranges_manager::make_case_label_ranges): New. (bounded_ranges_manager::log_stats): New. (bounded_ranges_constraint::print): New. (bounded_ranges_constraint::to_json): New. (bounded_ranges_constraint::operator==): New. (bounded_ranges_constraint::add_to_hash): New. (constraint_manager::constraint_manager): Update for new field m_bounded_ranges_constraints. (constraint_manager::operator=): Likewise. (constraint_manager::hash): Likewise. (constraint_manager::operator==): Likewise. (constraint_manager::print): Likewise. (constraint_manager::dump_to_pp): Likewise. (constraint_manager::to_json): Likewise. (constraint_manager::add_unknown_constraint): Update the lhs_ec_id if necessary in existing constraints when combining equivalence classes. Add similar code for handling m_bounded_ranges_constraints. (constraint_manager::add_constraint_internal): Add comment. (constraint_manager::add_bounded_ranges): New. (constraint_manager::eval_condition): Use new field m_bounded_ranges_constraints. (constraint_manager::purge): Update bounded_ranges_constraint instances. (constraint_manager::canonicalize): Update for new field. (merger_fact_visitor::on_ranges): New. (constraint_manager::for_each_fact): Use new field m_bounded_ranges_constraints. (constraint_manager::validate): Fix off-by-one error needed due to bug fixed above in add_unknown_constraint. Validate the EC IDs in m_bounded_ranges_constraints. (constraint_manager::get_range_manager): New. (selftest::assert_dump_bounded_range_eq): New. (ASSERT_DUMP_BOUNDED_RANGE_EQ): New. (selftest::test_bounded_range): New. (selftest::assert_dump_bounded_ranges_eq): New. (ASSERT_DUMP_BOUNDED_RANGES_EQ): New. (selftest::test_bounded_ranges): New. (selftest::run_constraint_manager_tests): Call the new selftests. * constraint-manager.h (struct bounded_range): New. (struct bounded_ranges): New. (template <> struct default_hash_traits<bounded_ranges::key_t>): New. (class bounded_ranges_manager): New. (fact_visitor::on_ranges): New pure virtual function. (class bounded_ranges_constraint): New. (constraint_manager::add_bounded_ranges): New decl. (constraint_manager::get_range_manager): New decl. (constraint_manager::m_bounded_ranges_constraints): New field. * diagnostic-manager.cc (epath_finder::process_worklist_item): Transfer ownership of rc to add_feasibility_problem. * engine.cc (feasibility_problem::dump_to_pp): Use get_model. * feasible-graph.cc (infeasible_node::dump_dot): Update for conversion of m_rc to a pointer. (feasible_graph::add_feasibility_problem): Pass RC by pointer and take ownership. * feasible-graph.h (infeasible_node::infeasible_node): Pass RC by pointer and take ownership. (infeasible_node::~infeasible_node): New. (infeasible_node::m_rc): Convert to a pointer. (feasible_graph::add_feasibility_problem): Pass RC by pointer and take ownership. * region-model-manager.cc: Include "analyzer/constraint-manager.h". (region_model_manager::region_model_manager): Initializer new field m_range_mgr. (region_model_manager::~region_model_manager): Delete it. (region_model_manager::log_stats): Call log_stats on it. * region-model.cc (region_model::add_constraint): Use new subclass rejected_op_constraint. (region_model::apply_constraints_for_gswitch): Reimplement using bounded_ranges_manager. (rejected_constraint::dump_to_pp): Convert to... (rejected_op_constraint::dump_to_pp): ...this. (rejected_ranges_constraint::dump_to_pp): New. * region-model.h (struct purge_stats): Add field m_num_bounded_ranges_constraints. (region_model_manager::get_range_manager): New. (region_model_manager::m_range_mgr): New. (region_model::get_range_manager): New. (struct rejected_constraint): Split into... (class rejected_constraint):...this new abstract base class, and... (class rejected_op_constraint): ...this new concrete subclass. (class rejected_ranges_constraint): New. * supergraph.cc: Include "tree-cfg.h". (supergraph::supergraph): Drop idx param from add_cfg_edge. (supergraph::add_cfg_edge): Drop idx param. (switch_cfg_superedge::switch_cfg_superedge): Move here from header. Populate m_case_labels with all cases which go to DST. (switch_cfg_superedge::dump_label_to_pp): Reimplement to use m_case_labels. (switch_cfg_superedge::get_case_label): Delete. * supergraph.h (supergraphadd_cfg_edge): Drop "idx" param. (switch_cfg_superedge::switch_cfg_superedge): Drop idx param and move implementation to supergraph.cc. (switch_cfg_superedge::get_case_label): Delete. (switch_cfg_superedge::get_case_labels): New. (switch_cfg_superedge::m_idx): Delete. (switch_cfg_superedge::m_case_labels): New field. gcc/testsuite/ChangeLog: * gcc.dg/analyzer/switch.c: Remove xfail. Add various tests. * gcc.dg/analyzer/torture/switch-2.c: New test. * gcc.dg/analyzer/torture/switch-3.c: New test. * gcc.dg/analyzer/torture/switch-4.c: New test. * gcc.dg/analyzer/torture/switch-5.c: New test.
This commit is contained in:
parent
192d4edd15
commit
8ca7fa84a3
17 changed files with 2114 additions and 109 deletions
|
@ -75,10 +75,12 @@ class region_model;
|
|||
class region_model_context;
|
||||
class impl_region_model_context;
|
||||
class call_details;
|
||||
struct rejected_constraint;
|
||||
class rejected_constraint;
|
||||
class constraint_manager;
|
||||
class equiv_class;
|
||||
class reachable_regions;
|
||||
class bounded_ranges;
|
||||
class bounded_ranges_manager;
|
||||
|
||||
class pending_diagnostic;
|
||||
class state_change_event;
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -64,6 +64,164 @@ struct range
|
|||
bound m_upper_bound;
|
||||
};
|
||||
|
||||
/* A closed range of values with constant integer bounds
|
||||
e.g. [3, 5] for the set {3, 4, 5}. */
|
||||
|
||||
struct bounded_range
|
||||
{
|
||||
bounded_range (const_tree lower, const_tree upper);
|
||||
|
||||
void dump_to_pp (pretty_printer *pp, bool show_types) const;
|
||||
void dump (bool show_types) const;
|
||||
|
||||
json::object *to_json () const;
|
||||
|
||||
bool contains_p (tree cst) const;
|
||||
|
||||
bool intersects_p (const bounded_range &other,
|
||||
bounded_range *out) const;
|
||||
|
||||
bool operator== (const bounded_range &other) const;
|
||||
bool operator!= (const bounded_range &other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
static int cmp (const bounded_range &a, const bounded_range &b);
|
||||
|
||||
tree m_lower;
|
||||
tree m_upper;
|
||||
|
||||
private:
|
||||
static void set_json_attr (json::object *obj, const char *name, tree value);
|
||||
};
|
||||
|
||||
/* A collection of bounded_range instances, suitable
|
||||
for representing the ranges on a case label within a switch
|
||||
statement. */
|
||||
|
||||
struct bounded_ranges
|
||||
{
|
||||
public:
|
||||
typedef bounded_ranges key_t;
|
||||
|
||||
bounded_ranges (const bounded_range &range);
|
||||
bounded_ranges (const vec<bounded_range> &ranges);
|
||||
bounded_ranges (enum tree_code op, tree rhs_const);
|
||||
|
||||
bool operator== (const bounded_ranges &other) const;
|
||||
|
||||
hashval_t get_hash () const { return m_hash; }
|
||||
|
||||
void dump_to_pp (pretty_printer *pp, bool show_types) const;
|
||||
void dump (bool show_types) const;
|
||||
|
||||
json::value *to_json () const;
|
||||
|
||||
tristate eval_condition (enum tree_code op,
|
||||
tree rhs_const,
|
||||
bounded_ranges_manager *mgr) const;
|
||||
|
||||
bool contain_p (tree cst) const;
|
||||
bool empty_p () const { return m_ranges.length () == 0; }
|
||||
|
||||
static int cmp (const bounded_ranges *a, const bounded_ranges *b);
|
||||
|
||||
private:
|
||||
void canonicalize ();
|
||||
void validate () const;
|
||||
|
||||
friend class bounded_ranges_manager;
|
||||
|
||||
auto_vec<bounded_range> m_ranges;
|
||||
hashval_t m_hash;
|
||||
};
|
||||
|
||||
} // namespace ana
|
||||
|
||||
template <> struct default_hash_traits<bounded_ranges::key_t>
|
||||
: public member_function_hash_traits<bounded_ranges::key_t>
|
||||
{
|
||||
static const bool empty_zero_p = true;
|
||||
};
|
||||
|
||||
namespace ana {
|
||||
|
||||
/* An object to own and consolidate bounded_ranges instances.
|
||||
This also caches the mapping from switch_cfg_superedge
|
||||
bounded_ranges instances, so that get_or_create_ranges_for_switch is
|
||||
memoized. */
|
||||
|
||||
class bounded_ranges_manager
|
||||
{
|
||||
public:
|
||||
~bounded_ranges_manager ();
|
||||
|
||||
const bounded_ranges *
|
||||
get_or_create_ranges_for_switch (const switch_cfg_superedge *edge,
|
||||
const gswitch *switch_stmt);
|
||||
|
||||
const bounded_ranges *get_or_create_empty ();
|
||||
const bounded_ranges *get_or_create_point (const_tree value);
|
||||
const bounded_ranges *get_or_create_range (const_tree lower_bound,
|
||||
const_tree upper_bound);
|
||||
const bounded_ranges *
|
||||
get_or_create_union (const vec <const bounded_ranges *> &others);
|
||||
const bounded_ranges *
|
||||
get_or_create_intersection (const bounded_ranges *a,
|
||||
const bounded_ranges *b);
|
||||
const bounded_ranges *
|
||||
get_or_create_inverse (const bounded_ranges *other, tree type);
|
||||
|
||||
void log_stats (logger *logger, bool show_objs) const;
|
||||
|
||||
private:
|
||||
const bounded_ranges *
|
||||
create_ranges_for_switch (const switch_cfg_superedge &edge,
|
||||
const gswitch *switch_stmt);
|
||||
|
||||
const bounded_ranges *
|
||||
make_case_label_ranges (const gswitch *switch_stmt,
|
||||
tree case_label);
|
||||
|
||||
const bounded_ranges *consolidate (bounded_ranges *);
|
||||
|
||||
struct hash_traits_t : public typed_noop_remove<bounded_ranges *>
|
||||
{
|
||||
typedef bounded_ranges *key_type;
|
||||
typedef bounded_ranges *value_type;
|
||||
|
||||
static inline bool
|
||||
equal (const key_type &k1, const key_type &k2)
|
||||
{
|
||||
return *k1 == *k2;
|
||||
}
|
||||
static inline hashval_t
|
||||
hash (const key_type &k)
|
||||
{
|
||||
return k->get_hash ();
|
||||
}
|
||||
static inline bool is_empty (key_type k) { return k == NULL; }
|
||||
static inline void mark_empty (key_type &k) { k = NULL; }
|
||||
static inline bool is_deleted (key_type k)
|
||||
{
|
||||
return k == reinterpret_cast<key_type> (1);
|
||||
}
|
||||
|
||||
static const bool empty_zero_p = true;
|
||||
};
|
||||
struct traits_t : public simple_hashmap_traits<hash_traits_t,
|
||||
bounded_ranges *>
|
||||
{
|
||||
};
|
||||
typedef hash_map<bounded_ranges *, bounded_ranges *, traits_t> map_t;
|
||||
map_t m_map;
|
||||
|
||||
typedef hash_map<const switch_cfg_superedge *,
|
||||
const bounded_ranges *> edge_cache_t;
|
||||
edge_cache_t m_edge_cache;
|
||||
};
|
||||
|
||||
/* An equivalence class within a constraint manager: a set of
|
||||
svalues that are known to all be equal to each other,
|
||||
together with an optional tree constant that they are equal to. */
|
||||
|
@ -190,6 +348,33 @@ class fact_visitor
|
|||
virtual void on_fact (const svalue *lhs,
|
||||
enum tree_code,
|
||||
const svalue *rhs) = 0;
|
||||
virtual void on_ranges (const svalue *lhs,
|
||||
const bounded_ranges *ranges) = 0;
|
||||
};
|
||||
|
||||
class bounded_ranges_constraint
|
||||
{
|
||||
public:
|
||||
bounded_ranges_constraint (equiv_class_id ec_id,
|
||||
const bounded_ranges *ranges)
|
||||
: m_ec_id (ec_id), m_ranges (ranges)
|
||||
{
|
||||
}
|
||||
|
||||
void print (pretty_printer *pp, const constraint_manager &cm) const;
|
||||
|
||||
json::object *to_json () const;
|
||||
|
||||
bool operator== (const bounded_ranges_constraint &other) const;
|
||||
bool operator!= (const bounded_ranges_constraint &other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
void add_to_hash (inchash::hash *hstate) const;
|
||||
|
||||
equiv_class_id m_ec_id;
|
||||
const bounded_ranges *m_ranges;
|
||||
};
|
||||
|
||||
/* A collection of equivalence classes and constraints on them.
|
||||
|
@ -248,6 +433,9 @@ public:
|
|||
enum tree_code op,
|
||||
equiv_class_id rhs_ec_id);
|
||||
|
||||
bool add_bounded_ranges (const svalue *sval,
|
||||
const bounded_ranges *ranges);
|
||||
|
||||
bool get_equiv_class_by_svalue (const svalue *sval,
|
||||
equiv_class_id *out) const;
|
||||
equiv_class_id get_or_add_equiv_class (const svalue *sval);
|
||||
|
@ -281,8 +469,11 @@ public:
|
|||
|
||||
void validate () const;
|
||||
|
||||
bounded_ranges_manager *get_range_manager () const;
|
||||
|
||||
auto_delete_vec<equiv_class> m_equiv_classes;
|
||||
auto_vec<constraint> m_constraints;
|
||||
auto_vec<bounded_ranges_constraint> m_bounded_ranges_constraints;
|
||||
|
||||
private:
|
||||
void add_constraint_internal (equiv_class_id lhs_id,
|
||||
|
|
|
@ -520,8 +520,7 @@ epath_finder::process_worklist_item (feasible_worklist *worklist,
|
|||
gcc_assert (rc);
|
||||
fg->add_feasibility_problem (fnode,
|
||||
succ_eedge,
|
||||
*rc);
|
||||
delete rc;
|
||||
rc);
|
||||
|
||||
/* Give up if there have been too many infeasible edges. */
|
||||
if (fg->get_num_infeasible ()
|
||||
|
|
|
@ -3842,7 +3842,7 @@ feasibility_problem::dump_to_pp (pretty_printer *pp) const
|
|||
pp_string (pp, "; rejected constraint: ");
|
||||
m_rc->dump_to_pp (pp);
|
||||
pp_string (pp, "; rmodel: ");
|
||||
m_rc->m_model.dump_to_pp (pp, true, false);
|
||||
m_rc->get_model ().dump_to_pp (pp, true, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -129,7 +129,7 @@ infeasible_node::dump_dot (graphviz_out *gv,
|
|||
|
||||
pp_string (pp, "rejected constraint:");
|
||||
pp_newline (pp);
|
||||
m_rc.dump_to_pp (pp);
|
||||
m_rc->dump_to_pp (pp);
|
||||
|
||||
pp_write_text_as_dot_label_to_stream (pp, /*for_record=*/true);
|
||||
|
||||
|
@ -178,12 +178,13 @@ feasible_graph::add_node (const exploded_node *enode,
|
|||
}
|
||||
|
||||
/* Add an infeasible_node to this graph and an infeasible_edge connecting
|
||||
to it from SRC_FNODE, capturing a failure of RC along EEDGE. */
|
||||
to it from SRC_FNODE, capturing a failure of RC along EEDGE.
|
||||
Takes ownership of RC. */
|
||||
|
||||
void
|
||||
feasible_graph::add_feasibility_problem (feasible_node *src_fnode,
|
||||
const exploded_edge *eedge,
|
||||
const rejected_constraint &rc)
|
||||
rejected_constraint *rc)
|
||||
{
|
||||
infeasible_node *dst_fnode
|
||||
= new infeasible_node (eedge->m_dest, m_nodes.length (), rc);
|
||||
|
|
|
@ -115,17 +115,18 @@ class infeasible_node : public base_feasible_node
|
|||
{
|
||||
public:
|
||||
infeasible_node (const exploded_node *inner_node, unsigned index,
|
||||
const rejected_constraint &rc)
|
||||
rejected_constraint *rc)
|
||||
: base_feasible_node (inner_node, index),
|
||||
m_rc (rc)
|
||||
{
|
||||
}
|
||||
~infeasible_node () { delete m_rc; }
|
||||
|
||||
void dump_dot (graphviz_out *gv,
|
||||
const dump_args_t &args) const FINAL OVERRIDE;
|
||||
|
||||
private:
|
||||
rejected_constraint m_rc;
|
||||
rejected_constraint *m_rc;
|
||||
};
|
||||
|
||||
/* Base class of edge within a feasible_graph. */
|
||||
|
@ -192,7 +193,7 @@ class feasible_graph : public digraph <fg_traits>
|
|||
|
||||
void add_feasibility_problem (feasible_node *src_fnode,
|
||||
const exploded_edge *eedge,
|
||||
const rejected_constraint &rc);
|
||||
rejected_constraint *rc);
|
||||
|
||||
exploded_path *make_epath (feasible_node *fnode) const;
|
||||
|
||||
|
|
|
@ -56,6 +56,7 @@ along with GCC; see the file COPYING3. If not see
|
|||
#include "analyzer/program-point.h"
|
||||
#include "analyzer/store.h"
|
||||
#include "analyzer/region-model.h"
|
||||
#include "analyzer/constraint-manager.h"
|
||||
|
||||
#if ENABLE_ANALYZER
|
||||
|
||||
|
@ -77,7 +78,8 @@ region_model_manager::region_model_manager ()
|
|||
m_fndecls_map (), m_labels_map (),
|
||||
m_globals_region (alloc_region_id (), &m_root_region),
|
||||
m_globals_map (),
|
||||
m_store_mgr (this)
|
||||
m_store_mgr (this),
|
||||
m_range_mgr (new bounded_ranges_manager ())
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -142,6 +144,8 @@ region_model_manager::~region_model_manager ()
|
|||
for (string_map_t::iterator iter = m_string_map.begin ();
|
||||
iter != m_string_map.end (); ++iter)
|
||||
delete (*iter).second;
|
||||
|
||||
delete m_range_mgr;
|
||||
}
|
||||
|
||||
/* Return true if C exceeds the complexity limit for svalues. */
|
||||
|
@ -1574,6 +1578,7 @@ region_model_manager::log_stats (logger *logger, bool show_objs) const
|
|||
logger->log (" # managed dynamic regions: %i",
|
||||
m_managed_dynamic_regions.length ());
|
||||
m_store_mgr.log_stats (logger, show_objs);
|
||||
m_range_mgr->log_stats (logger, show_objs);
|
||||
}
|
||||
|
||||
/* Dump the number of objects of each class that were managed by this
|
||||
|
|
|
@ -2773,7 +2773,7 @@ region_model::add_constraint (tree lhs, enum tree_code op, tree rhs,
|
|||
{
|
||||
bool sat = add_constraint (lhs, op, rhs, ctxt);
|
||||
if (!sat && out)
|
||||
*out = new rejected_constraint (*this, lhs, op, rhs);
|
||||
*out = new rejected_op_constraint (*this, lhs, op, rhs);
|
||||
return sat;
|
||||
}
|
||||
|
||||
|
@ -3329,56 +3329,15 @@ region_model::apply_constraints_for_gswitch (const switch_cfg_superedge &edge,
|
|||
region_model_context *ctxt,
|
||||
rejected_constraint **out)
|
||||
{
|
||||
bounded_ranges_manager *ranges_mgr = get_range_manager ();
|
||||
const bounded_ranges *all_cases_ranges
|
||||
= ranges_mgr->get_or_create_ranges_for_switch (&edge, switch_stmt);
|
||||
tree index = gimple_switch_index (switch_stmt);
|
||||
tree case_label = edge.get_case_label ();
|
||||
gcc_assert (TREE_CODE (case_label) == CASE_LABEL_EXPR);
|
||||
tree lower_bound = CASE_LOW (case_label);
|
||||
tree upper_bound = CASE_HIGH (case_label);
|
||||
if (lower_bound)
|
||||
{
|
||||
if (upper_bound)
|
||||
{
|
||||
/* Range. */
|
||||
if (!add_constraint (index, GE_EXPR, lower_bound, ctxt, out))
|
||||
return false;
|
||||
return add_constraint (index, LE_EXPR, upper_bound, ctxt, out);
|
||||
}
|
||||
else
|
||||
/* Single-value. */
|
||||
return add_constraint (index, EQ_EXPR, lower_bound, ctxt, out);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* The default case.
|
||||
Add exclusions based on the other cases. */
|
||||
for (unsigned other_idx = 1;
|
||||
other_idx < gimple_switch_num_labels (switch_stmt);
|
||||
other_idx++)
|
||||
{
|
||||
tree other_label = gimple_switch_label (switch_stmt,
|
||||
other_idx);
|
||||
tree other_lower_bound = CASE_LOW (other_label);
|
||||
tree other_upper_bound = CASE_HIGH (other_label);
|
||||
gcc_assert (other_lower_bound);
|
||||
if (other_upper_bound)
|
||||
{
|
||||
/* Exclude this range-valued case.
|
||||
For now, we just exclude the boundary values.
|
||||
TODO: exclude the values within the region. */
|
||||
if (!add_constraint (index, NE_EXPR, other_lower_bound,
|
||||
ctxt, out))
|
||||
return false;
|
||||
if (!add_constraint (index, NE_EXPR, other_upper_bound,
|
||||
ctxt, out))
|
||||
return false;
|
||||
}
|
||||
else
|
||||
/* Exclude this single-valued case. */
|
||||
if (!add_constraint (index, NE_EXPR, other_lower_bound, ctxt, out))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
const svalue *index_sval = get_rvalue (index, ctxt);
|
||||
bool sat = m_constraints->add_bounded_ranges (index_sval, all_cases_ranges);
|
||||
if (!sat && out)
|
||||
*out = new rejected_ranges_constraint (*this, index, all_cases_ranges);
|
||||
return sat;
|
||||
}
|
||||
|
||||
/* Apply any constraints due to an exception being thrown at LAST_STMT.
|
||||
|
@ -3860,10 +3819,10 @@ debug (const region_model &rmodel)
|
|||
rmodel.dump (false);
|
||||
}
|
||||
|
||||
/* struct rejected_constraint. */
|
||||
/* class rejected_op_constraint : public rejected_constraint. */
|
||||
|
||||
void
|
||||
rejected_constraint::dump_to_pp (pretty_printer *pp) const
|
||||
rejected_op_constraint::dump_to_pp (pretty_printer *pp) const
|
||||
{
|
||||
region_model m (m_model);
|
||||
const svalue *lhs_sval = m.get_rvalue (m_lhs, NULL);
|
||||
|
@ -3873,6 +3832,18 @@ rejected_constraint::dump_to_pp (pretty_printer *pp) const
|
|||
rhs_sval->dump_to_pp (pp, true);
|
||||
}
|
||||
|
||||
/* class rejected_ranges_constraint : public rejected_constraint. */
|
||||
|
||||
void
|
||||
rejected_ranges_constraint::dump_to_pp (pretty_printer *pp) const
|
||||
{
|
||||
region_model m (m_model);
|
||||
const svalue *sval = m.get_rvalue (m_expr, NULL);
|
||||
sval->dump_to_pp (pp, true);
|
||||
pp_string (pp, " in ");
|
||||
m_ranges->dump_to_pp (pp, true);
|
||||
}
|
||||
|
||||
/* class engine. */
|
||||
|
||||
/* Dump the managed objects by class to LOGGER, and the per-class totals. */
|
||||
|
|
|
@ -189,6 +189,7 @@ struct purge_stats
|
|||
m_num_regions (0),
|
||||
m_num_equiv_classes (0),
|
||||
m_num_constraints (0),
|
||||
m_num_bounded_ranges_constraints (0),
|
||||
m_num_client_items (0)
|
||||
{}
|
||||
|
||||
|
@ -196,6 +197,7 @@ struct purge_stats
|
|||
int m_num_regions;
|
||||
int m_num_equiv_classes;
|
||||
int m_num_constraints;
|
||||
int m_num_bounded_ranges_constraints;
|
||||
int m_num_client_items;
|
||||
};
|
||||
|
||||
|
@ -320,6 +322,7 @@ public:
|
|||
unsigned alloc_region_id () { return m_next_region_id++; }
|
||||
|
||||
store_manager *get_store_manager () { return &m_store_mgr; }
|
||||
bounded_ranges_manager *get_range_manager () const { return m_range_mgr; }
|
||||
|
||||
/* Dynamically-allocated region instances.
|
||||
The number of these within the analysis can grow arbitrarily.
|
||||
|
@ -456,6 +459,8 @@ private:
|
|||
|
||||
store_manager m_store_mgr;
|
||||
|
||||
bounded_ranges_manager *m_range_mgr;
|
||||
|
||||
/* "Dynamically-allocated" region instances.
|
||||
The number of these within the analysis can grow arbitrarily.
|
||||
They are still owned by the manager. */
|
||||
|
@ -698,6 +703,10 @@ class region_model
|
|||
void unset_dynamic_extents (const region *reg);
|
||||
|
||||
region_model_manager *get_manager () const { return m_mgr; }
|
||||
bounded_ranges_manager *get_range_manager () const
|
||||
{
|
||||
return m_mgr->get_range_manager ();
|
||||
}
|
||||
|
||||
void unbind_region_and_descendents (const region *reg,
|
||||
enum poison_kind pkind);
|
||||
|
@ -945,21 +954,54 @@ struct model_merger
|
|||
/* A record that can (optionally) be written out when
|
||||
region_model::add_constraint fails. */
|
||||
|
||||
struct rejected_constraint
|
||||
class rejected_constraint
|
||||
{
|
||||
rejected_constraint (const region_model &model,
|
||||
tree lhs, enum tree_code op, tree rhs)
|
||||
: m_model (model), m_lhs (lhs), m_op (op), m_rhs (rhs)
|
||||
public:
|
||||
virtual ~rejected_constraint () {}
|
||||
virtual void dump_to_pp (pretty_printer *pp) const = 0;
|
||||
|
||||
const region_model &get_model () const { return m_model; }
|
||||
|
||||
protected:
|
||||
rejected_constraint (const region_model &model)
|
||||
: m_model (model)
|
||||
{}
|
||||
|
||||
void dump_to_pp (pretty_printer *pp) const;
|
||||
|
||||
region_model m_model;
|
||||
};
|
||||
|
||||
class rejected_op_constraint : public rejected_constraint
|
||||
{
|
||||
public:
|
||||
rejected_op_constraint (const region_model &model,
|
||||
tree lhs, enum tree_code op, tree rhs)
|
||||
: rejected_constraint (model),
|
||||
m_lhs (lhs), m_op (op), m_rhs (rhs)
|
||||
{}
|
||||
|
||||
void dump_to_pp (pretty_printer *pp) const FINAL OVERRIDE;
|
||||
|
||||
tree m_lhs;
|
||||
enum tree_code m_op;
|
||||
tree m_rhs;
|
||||
};
|
||||
|
||||
class rejected_ranges_constraint : public rejected_constraint
|
||||
{
|
||||
public:
|
||||
rejected_ranges_constraint (const region_model &model,
|
||||
tree expr, const bounded_ranges *ranges)
|
||||
: rejected_constraint (model),
|
||||
m_expr (expr), m_ranges (ranges)
|
||||
{}
|
||||
|
||||
void dump_to_pp (pretty_printer *pp) const FINAL OVERRIDE;
|
||||
|
||||
private:
|
||||
tree m_expr;
|
||||
const bounded_ranges *m_ranges;
|
||||
};
|
||||
|
||||
/* A bundle of state. */
|
||||
|
||||
class engine
|
||||
|
|
|
@ -50,6 +50,7 @@ along with GCC; see the file COPYING3. If not see
|
|||
#include "cgraph.h"
|
||||
#include "cfg.h"
|
||||
#include "digraph.h"
|
||||
#include "tree-cfg.h"
|
||||
#include "analyzer/supergraph.h"
|
||||
#include "analyzer/analyzer-logging.h"
|
||||
|
||||
|
@ -246,7 +247,7 @@ supergraph::supergraph (logger *logger)
|
|||
supernode *dest_supernode
|
||||
= *m_bb_to_initial_node.get (dest_cfg_block);
|
||||
cfg_superedge *cfg_sedge
|
||||
= add_cfg_edge (src_supernode, dest_supernode, cfg_edge, idx);
|
||||
= add_cfg_edge (src_supernode, dest_supernode, cfg_edge);
|
||||
m_cfg_edge_to_cfg_superedge.put (cfg_edge, cfg_sedge);
|
||||
}
|
||||
}
|
||||
|
@ -505,17 +506,16 @@ supergraph::add_node (function *fun, basic_block bb, gcall *returning_call,
|
|||
adding it to this supergraph.
|
||||
|
||||
If the edge is for a switch statement, create a switch_cfg_superedge
|
||||
subclass using IDX (the index of E within the out-edges from SRC's
|
||||
underlying basic block). */
|
||||
subclass. */
|
||||
|
||||
cfg_superedge *
|
||||
supergraph::add_cfg_edge (supernode *src, supernode *dest, ::edge e, int idx)
|
||||
supergraph::add_cfg_edge (supernode *src, supernode *dest, ::edge e)
|
||||
{
|
||||
/* Special-case switch edges. */
|
||||
gimple *stmt = src->get_last_stmt ();
|
||||
cfg_superedge *new_edge;
|
||||
if (stmt && stmt->code == GIMPLE_SWITCH)
|
||||
new_edge = new switch_cfg_superedge (src, dest, e, idx);
|
||||
new_edge = new switch_cfg_superedge (src, dest, e);
|
||||
else
|
||||
new_edge = new cfg_superedge (src, dest, e);
|
||||
add_edge (new_edge);
|
||||
|
@ -1072,6 +1072,23 @@ cfg_superedge::get_phi_arg (const gphi *phi) const
|
|||
return gimple_phi_arg_def (phi, index);
|
||||
}
|
||||
|
||||
switch_cfg_superedge::switch_cfg_superedge (supernode *src,
|
||||
supernode *dst,
|
||||
::edge e)
|
||||
: cfg_superedge (src, dst, e)
|
||||
{
|
||||
/* Populate m_case_labels with all cases which go to DST. */
|
||||
const gswitch *gswitch = get_switch_stmt ();
|
||||
for (unsigned i = 0; i < gimple_switch_num_labels (gswitch); i++)
|
||||
{
|
||||
tree case_ = gimple_switch_label (gswitch, i);
|
||||
basic_block bb = label_to_block (src->get_function (),
|
||||
CASE_LABEL (case_));
|
||||
if (bb == dst->m_bb)
|
||||
m_case_labels.safe_push (case_);
|
||||
}
|
||||
}
|
||||
|
||||
/* Implementation of superedge::dump_label_to_pp for CFG superedges for
|
||||
"switch" statements.
|
||||
|
||||
|
@ -1081,31 +1098,63 @@ void
|
|||
switch_cfg_superedge::dump_label_to_pp (pretty_printer *pp,
|
||||
bool user_facing ATTRIBUTE_UNUSED) const
|
||||
{
|
||||
tree case_label = get_case_label ();
|
||||
gcc_assert (TREE_CODE (case_label) == CASE_LABEL_EXPR);
|
||||
tree lower_bound = CASE_LOW (case_label);
|
||||
tree upper_bound = CASE_HIGH (case_label);
|
||||
if (lower_bound)
|
||||
if (user_facing)
|
||||
{
|
||||
pp_printf (pp, "case ");
|
||||
dump_generic_node (pp, lower_bound, 0, (dump_flags_t)0, false);
|
||||
if (upper_bound)
|
||||
for (unsigned i = 0; i < m_case_labels.length (); ++i)
|
||||
{
|
||||
pp_printf (pp, " ... ");
|
||||
dump_generic_node (pp, upper_bound, 0, (dump_flags_t)0, false);
|
||||
if (i > 0)
|
||||
pp_string (pp, ", ");
|
||||
tree case_label = m_case_labels[i];
|
||||
gcc_assert (TREE_CODE (case_label) == CASE_LABEL_EXPR);
|
||||
tree lower_bound = CASE_LOW (case_label);
|
||||
tree upper_bound = CASE_HIGH (case_label);
|
||||
if (lower_bound)
|
||||
{
|
||||
pp_printf (pp, "case ");
|
||||
dump_generic_node (pp, lower_bound, 0, (dump_flags_t)0, false);
|
||||
if (upper_bound)
|
||||
{
|
||||
pp_printf (pp, " ... ");
|
||||
dump_generic_node (pp, upper_bound, 0, (dump_flags_t)0,
|
||||
false);
|
||||
}
|
||||
pp_printf (pp, ":");
|
||||
}
|
||||
else
|
||||
pp_printf (pp, "default:");
|
||||
}
|
||||
pp_printf (pp, ":");
|
||||
}
|
||||
else
|
||||
pp_printf (pp, "default:");
|
||||
}
|
||||
|
||||
/* Get the case label for this "switch" superedge. */
|
||||
|
||||
tree
|
||||
switch_cfg_superedge::get_case_label () const
|
||||
{
|
||||
return gimple_switch_label (get_switch_stmt (), m_idx);
|
||||
{
|
||||
pp_character (pp, '{');
|
||||
for (unsigned i = 0; i < m_case_labels.length (); ++i)
|
||||
{
|
||||
if (i > 0)
|
||||
pp_string (pp, ", ");
|
||||
tree case_label = m_case_labels[i];
|
||||
gcc_assert (TREE_CODE (case_label) == CASE_LABEL_EXPR);
|
||||
tree lower_bound = CASE_LOW (case_label);
|
||||
tree upper_bound = CASE_HIGH (case_label);
|
||||
if (lower_bound)
|
||||
{
|
||||
if (upper_bound)
|
||||
{
|
||||
pp_character (pp, '[');
|
||||
dump_generic_node (pp, lower_bound, 0, (dump_flags_t)0,
|
||||
false);
|
||||
pp_string (pp, ", ");
|
||||
dump_generic_node (pp, upper_bound, 0, (dump_flags_t)0,
|
||||
false);
|
||||
pp_character (pp, ']');
|
||||
}
|
||||
else
|
||||
dump_generic_node (pp, lower_bound, 0, (dump_flags_t)0, false);
|
||||
}
|
||||
else
|
||||
pp_printf (pp, "default");
|
||||
}
|
||||
pp_character (pp, '}');
|
||||
}
|
||||
}
|
||||
|
||||
/* Implementation of superedge::dump_label_to_pp for interprocedural
|
||||
|
|
|
@ -181,7 +181,7 @@ public:
|
|||
private:
|
||||
supernode *add_node (function *fun, basic_block bb, gcall *returning_call,
|
||||
gimple_seq phi_nodes);
|
||||
cfg_superedge *add_cfg_edge (supernode *src, supernode *dest, ::edge e, int idx);
|
||||
cfg_superedge *add_cfg_edge (supernode *src, supernode *dest, ::edge e);
|
||||
call_superedge *add_call_superedge (supernode *src, supernode *dest,
|
||||
cgraph_edge *cedge);
|
||||
return_superedge *add_return_superedge (supernode *src, supernode *dest,
|
||||
|
@ -539,15 +539,12 @@ is_a_helper <const cfg_superedge *>::test (const superedge *sedge)
|
|||
namespace ana {
|
||||
|
||||
/* A subclass for edges from switch statements, retaining enough
|
||||
information to identify the pertinent case, and for adding labels
|
||||
information to identify the pertinent cases, and for adding labels
|
||||
when rendering via graphviz. */
|
||||
|
||||
class switch_cfg_superedge : public cfg_superedge {
|
||||
public:
|
||||
switch_cfg_superedge (supernode *src, supernode *dst, ::edge e, int idx)
|
||||
: cfg_superedge (src, dst, e),
|
||||
m_idx (idx)
|
||||
{}
|
||||
switch_cfg_superedge (supernode *src, supernode *dst, ::edge e);
|
||||
|
||||
const switch_cfg_superedge *dyn_cast_switch_cfg_superedge () const
|
||||
FINAL OVERRIDE
|
||||
|
@ -563,10 +560,10 @@ class switch_cfg_superedge : public cfg_superedge {
|
|||
return as_a <gswitch *> (m_src->get_last_stmt ());
|
||||
}
|
||||
|
||||
tree get_case_label () const;
|
||||
const vec<tree> &get_case_labels () const { return m_case_labels; }
|
||||
|
||||
private:
|
||||
const int m_idx;
|
||||
private:
|
||||
auto_vec<tree> m_case_labels;
|
||||
};
|
||||
|
||||
} // namespace ana
|
||||
|
|
|
@ -8,23 +8,156 @@ void test (int i)
|
|||
{
|
||||
case 0:
|
||||
__analyzer_eval (i == 0); /* { dg-warning "TRUE" } */
|
||||
__analyzer_eval (i != -1); /* { dg-warning "TRUE" } */
|
||||
__analyzer_eval (i != 0); /* { dg-warning "FALSE" } */
|
||||
__analyzer_eval (i != 1); /* { dg-warning "TRUE" } */
|
||||
break;
|
||||
|
||||
case 3 ... 5:
|
||||
__analyzer_eval (i != 0); /* { dg-warning "TRUE" } */
|
||||
__analyzer_eval (i > 1); /* { dg-warning "TRUE" } */
|
||||
__analyzer_eval (i > 2); /* { dg-warning "TRUE" } */
|
||||
__analyzer_eval (i >= 2); /* { dg-warning "TRUE" } */
|
||||
__analyzer_eval (i >= 3); /* { dg-warning "TRUE" } */
|
||||
__analyzer_eval (i <= 5); /* { dg-warning "TRUE" } */
|
||||
__analyzer_eval (i < 6); /* { dg-warning "TRUE" } */
|
||||
__analyzer_eval (i <= 6); /* { dg-warning "TRUE" } */
|
||||
__analyzer_eval (i < 7); /* { dg-warning "TRUE" } */
|
||||
__analyzer_eval (i != 6); /* { dg-warning "TRUE" } */
|
||||
__analyzer_eval (i != 3); /* { dg-warning "UNKNOWN" } */
|
||||
__analyzer_eval (i != 4); /* { dg-warning "UNKNOWN" } */
|
||||
__analyzer_eval (i != 5); /* { dg-warning "UNKNOWN" } */
|
||||
__analyzer_eval (i >= 4); /* { dg-warning "UNKNOWN" } */
|
||||
__analyzer_eval (i >= 5); /* { dg-warning "UNKNOWN" } */
|
||||
__analyzer_eval (i <= 3); /* { dg-warning "UNKNOWN" } */
|
||||
__analyzer_eval (i <= 4); /* { dg-warning "UNKNOWN" } */
|
||||
break;
|
||||
|
||||
default:
|
||||
__analyzer_eval (i == -1); /* { dg-warning "UNKNOWN" } */
|
||||
__analyzer_eval (i == 0); /* { dg-warning "FALSE" } */
|
||||
__analyzer_eval (i == 2); /* { dg-warning "UNKNOWN" } */
|
||||
__analyzer_eval (i == 3); /* { dg-warning "FALSE" } */
|
||||
__analyzer_eval (i == 4); /* { dg-warning "FALSE" "desired" { xfail *-*-* } } */
|
||||
/* { dg-warning "UNKNOWN" "status quo" { target *-*-* } .-1 } */
|
||||
/* TODO(xfail^^^): we're only checking against endpoints of case
|
||||
ranges, not the insides. */
|
||||
__analyzer_eval (i == 4); /* { dg-warning "FALSE" } */
|
||||
__analyzer_eval (i == 5); /* { dg-warning "FALSE" } */
|
||||
__analyzer_eval (i == 6); /* { dg-warning "UNKNOWN" } */
|
||||
__analyzer_eval (i != 0); /* { dg-warning "TRUE" } */
|
||||
__analyzer_eval (i != 1); /* { dg-warning "UNKNOWN" } */
|
||||
__analyzer_eval (i != 3); /* { dg-warning "TRUE" } */
|
||||
__analyzer_eval (i != 4); /* { dg-warning "TRUE" } */
|
||||
__analyzer_eval (i != 5); /* { dg-warning "TRUE" } */
|
||||
__analyzer_eval (i != 6); /* { dg-warning "UNKNOWN" } */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Verify that the analyzer follows the correct paths on a
|
||||
switch statement guarded by an if, using noinline to defeat
|
||||
optimizations. */
|
||||
|
||||
static void __attribute__((noinline))
|
||||
__analyzer_called_by_test_2 (int y)
|
||||
{
|
||||
switch (y)
|
||||
{
|
||||
case 0:
|
||||
__analyzer_dump_path (); /* { dg-bogus "path" } */
|
||||
break;
|
||||
case 1:
|
||||
__analyzer_dump_path (); /* { dg-message "path" } */
|
||||
break;
|
||||
case 2:
|
||||
__analyzer_dump_path (); /* { dg-bogus "path" } */
|
||||
break;
|
||||
default:
|
||||
__analyzer_dump_path (); /* { dg-bogus "path" } */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void test_2 (int x)
|
||||
{
|
||||
if (x == 1)
|
||||
__analyzer_called_by_test_2 (x);
|
||||
}
|
||||
|
||||
void test_3 (int x, int y)
|
||||
{
|
||||
if (y == 3)
|
||||
switch (x)
|
||||
{
|
||||
case 0 ... 9:
|
||||
case 20 ... 29:
|
||||
if (x == y)
|
||||
__analyzer_dump_path (); /* { dg-message "path" } */
|
||||
else
|
||||
__analyzer_dump_path (); /* { dg-message "path" } */
|
||||
}
|
||||
}
|
||||
|
||||
struct s4
|
||||
{
|
||||
unsigned char level:3;
|
||||
unsigned char key_id_mode:2;
|
||||
unsigned char reserved:3;
|
||||
};
|
||||
|
||||
void test_4 (struct s4 *p)
|
||||
{
|
||||
switch (p->key_id_mode)
|
||||
{
|
||||
case 0:
|
||||
__analyzer_dump_path (); /* { dg-message "path" } */
|
||||
break;
|
||||
case 1:
|
||||
__analyzer_dump_path (); /* { dg-message "path" } */
|
||||
break;
|
||||
case 2:
|
||||
__analyzer_dump_path (); /* { dg-message "path" } */
|
||||
break;
|
||||
case 3:
|
||||
__analyzer_dump_path (); /* { dg-message "path" } */
|
||||
break;
|
||||
}
|
||||
__analyzer_dump_path (); /* { dg-message "path" } */
|
||||
}
|
||||
|
||||
int test_5 (unsigned v)
|
||||
{
|
||||
switch (v)
|
||||
{
|
||||
case 0:
|
||||
return 7;
|
||||
break;
|
||||
case 1:
|
||||
return 23;
|
||||
break;
|
||||
default:
|
||||
return v * 2;
|
||||
}
|
||||
}
|
||||
|
||||
int test_6 (unsigned v)
|
||||
{
|
||||
switch (v)
|
||||
{
|
||||
case 0:
|
||||
return 3;
|
||||
case -1:
|
||||
return 22;
|
||||
}
|
||||
return -3;
|
||||
}
|
||||
|
||||
int g7 = -1;
|
||||
int test_7 ()
|
||||
{
|
||||
switch (g7++) {
|
||||
case 0:
|
||||
return 32;
|
||||
|
||||
case 100:
|
||||
return 42;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
42
gcc/testsuite/gcc.dg/analyzer/torture/switch-2.c
Normal file
42
gcc/testsuite/gcc.dg/analyzer/torture/switch-2.c
Normal file
|
@ -0,0 +1,42 @@
|
|||
struct s
|
||||
{
|
||||
int f0;
|
||||
int f1;
|
||||
};
|
||||
|
||||
int test (int cmd)
|
||||
{
|
||||
int err = 0;
|
||||
struct s foo;
|
||||
struct s bar;
|
||||
|
||||
switch (cmd)
|
||||
{
|
||||
case 0:
|
||||
foo.f0 = 0;
|
||||
break;
|
||||
case 1:
|
||||
foo.f0 = 1;
|
||||
break;
|
||||
case 30 ... 50:
|
||||
case 70 ... 80:
|
||||
__builtin_memset (&bar, 0, sizeof (bar));
|
||||
break;
|
||||
}
|
||||
|
||||
switch (cmd)
|
||||
{
|
||||
default:
|
||||
return -1;
|
||||
case 0 ... 1:
|
||||
return foo.f0;
|
||||
break;
|
||||
case 42:
|
||||
return bar.f1;
|
||||
break;
|
||||
case 65:
|
||||
return bar.f1;
|
||||
break;
|
||||
}
|
||||
return err;
|
||||
}
|
158
gcc/testsuite/gcc.dg/analyzer/torture/switch-3.c
Normal file
158
gcc/testsuite/gcc.dg/analyzer/torture/switch-3.c
Normal file
|
@ -0,0 +1,158 @@
|
|||
typedef unsigned int __u32;
|
||||
__extension__ typedef unsigned long long __u64;
|
||||
|
||||
extern unsigned long
|
||||
copy_from_user(void *to, const void *from, unsigned long n);
|
||||
|
||||
extern unsigned long
|
||||
copy_to_user(void *to, const void *from, unsigned long n);
|
||||
|
||||
struct mtrr_sentry {
|
||||
__u64 base;
|
||||
__u32 size;
|
||||
__u32 type;
|
||||
};
|
||||
|
||||
struct mtrr_gentry {
|
||||
__u64 base;
|
||||
__u32 size;
|
||||
__u32 regnum;
|
||||
__u32 type;
|
||||
__u32 _pad;
|
||||
};
|
||||
|
||||
#define _IOC_NRBITS 8
|
||||
#define _IOC_TYPEBITS 8
|
||||
#define _IOC_SIZEBITS 14
|
||||
#define _IOC_DIRBITS 2
|
||||
|
||||
#define _IOC_NRSHIFT 0
|
||||
#define _IOC_TYPESHIFT (_IOC_NRSHIFT+_IOC_NRBITS)
|
||||
#define _IOC_SIZESHIFT (_IOC_TYPESHIFT+_IOC_TYPEBITS)
|
||||
#define _IOC_DIRSHIFT (_IOC_SIZESHIFT+_IOC_SIZEBITS)
|
||||
|
||||
#define _IOC_WRITE 1U
|
||||
#define _IOC_READ 2U
|
||||
|
||||
#define _IOC(dir,type,nr,size) \
|
||||
(((dir) << _IOC_DIRSHIFT) | \
|
||||
((type) << _IOC_TYPESHIFT) | \
|
||||
((nr) << _IOC_NRSHIFT) | \
|
||||
((size) << _IOC_SIZESHIFT))
|
||||
|
||||
#define _IOC_TYPECHECK(t) (sizeof(t))
|
||||
|
||||
#define _IOW(type,nr,size) _IOC(_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size)))
|
||||
#define _IOWR(type,nr,size) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size)))
|
||||
|
||||
#define MTRR_IOCTL_BASE 'M'
|
||||
|
||||
#define EFAULT 14
|
||||
#define EINVAL 22
|
||||
#define ENOTTY 25
|
||||
|
||||
#define MTRRIOC_ADD_ENTRY _IOW(MTRR_IOCTL_BASE, 0, struct mtrr_sentry)
|
||||
#define MTRRIOC_SET_ENTRY _IOW(MTRR_IOCTL_BASE, 1, struct mtrr_sentry)
|
||||
#define MTRRIOC_DEL_ENTRY _IOW(MTRR_IOCTL_BASE, 2, struct mtrr_sentry)
|
||||
#define MTRRIOC_GET_ENTRY _IOWR(MTRR_IOCTL_BASE, 3, struct mtrr_gentry)
|
||||
#define MTRRIOC_KILL_ENTRY _IOW(MTRR_IOCTL_BASE, 4, struct mtrr_sentry)
|
||||
#define MTRRIOC_ADD_PAGE_ENTRY _IOW(MTRR_IOCTL_BASE, 5, struct mtrr_sentry)
|
||||
#define MTRRIOC_SET_PAGE_ENTRY _IOW(MTRR_IOCTL_BASE, 6, struct mtrr_sentry)
|
||||
#define MTRRIOC_DEL_PAGE_ENTRY _IOW(MTRR_IOCTL_BASE, 7, struct mtrr_sentry)
|
||||
#define MTRRIOC_GET_PAGE_ENTRY _IOWR(MTRR_IOCTL_BASE, 8, struct mtrr_gentry)
|
||||
#define MTRRIOC_KILL_PAGE_ENTRY _IOW(MTRR_IOCTL_BASE, 9, struct mtrr_sentry)
|
||||
|
||||
extern void check_init_u64 (__u64 v);
|
||||
extern void check_init_u32 (__u32 v);
|
||||
|
||||
/* Adapted/reduced from arch/x86/kernel/cpu/mtrr/if.c: mtrr_ioctl,
|
||||
which is GPL-2.0 */
|
||||
|
||||
long mtrr_ioctl(unsigned int cmd, unsigned long __arg) {
|
||||
int err = 0;
|
||||
struct mtrr_sentry sentry;
|
||||
struct mtrr_gentry gentry;
|
||||
void *arg = (void *)__arg;
|
||||
|
||||
__builtin_memset(&gentry, 0, sizeof(gentry));
|
||||
|
||||
switch (cmd) {
|
||||
case MTRRIOC_ADD_ENTRY:
|
||||
case MTRRIOC_SET_ENTRY:
|
||||
case MTRRIOC_DEL_ENTRY:
|
||||
case MTRRIOC_KILL_ENTRY:
|
||||
case MTRRIOC_ADD_PAGE_ENTRY:
|
||||
case MTRRIOC_SET_PAGE_ENTRY:
|
||||
case MTRRIOC_DEL_PAGE_ENTRY:
|
||||
case MTRRIOC_KILL_PAGE_ENTRY:
|
||||
if (copy_from_user(&sentry, arg, sizeof(sentry)))
|
||||
return -EFAULT;
|
||||
break;
|
||||
case MTRRIOC_GET_ENTRY:
|
||||
case MTRRIOC_GET_PAGE_ENTRY:
|
||||
if (copy_from_user(&gentry, arg, sizeof(gentry)))
|
||||
return -EFAULT;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (cmd) {
|
||||
default:
|
||||
return -ENOTTY;
|
||||
case MTRRIOC_ADD_ENTRY:
|
||||
check_init_u64 (sentry.base);
|
||||
check_init_u32 (sentry.size);
|
||||
check_init_u32 (sentry.type);
|
||||
break;
|
||||
case MTRRIOC_SET_ENTRY:
|
||||
check_init_u64 (sentry.base);
|
||||
check_init_u32 (sentry.size);
|
||||
check_init_u32 (sentry.type);
|
||||
break;
|
||||
case MTRRIOC_DEL_ENTRY:
|
||||
check_init_u64 (sentry.base);
|
||||
check_init_u32 (sentry.size);
|
||||
check_init_u32 (sentry.type);
|
||||
break;
|
||||
case MTRRIOC_KILL_ENTRY:
|
||||
check_init_u64 (sentry.base);
|
||||
check_init_u32 (sentry.size);
|
||||
check_init_u32 (sentry.type);
|
||||
break;
|
||||
case MTRRIOC_GET_ENTRY:
|
||||
check_init_u64 (gentry.base);
|
||||
check_init_u32 (gentry.size);
|
||||
check_init_u32 (gentry.regnum);
|
||||
check_init_u32 (gentry.type);
|
||||
check_init_u32 (gentry._pad);
|
||||
break;
|
||||
case MTRRIOC_ADD_PAGE_ENTRY:
|
||||
check_init_u64 (sentry.base);
|
||||
check_init_u32 (sentry.size);
|
||||
check_init_u32 (sentry.type);
|
||||
break;
|
||||
case MTRRIOC_SET_PAGE_ENTRY:
|
||||
check_init_u64 (sentry.base);
|
||||
check_init_u32 (sentry.size);
|
||||
check_init_u32 (sentry.type);
|
||||
break;
|
||||
case MTRRIOC_DEL_PAGE_ENTRY:
|
||||
check_init_u64 (sentry.base);
|
||||
check_init_u32 (sentry.size);
|
||||
check_init_u32 (sentry.type);
|
||||
break;
|
||||
case MTRRIOC_KILL_PAGE_ENTRY:
|
||||
check_init_u64 (sentry.base);
|
||||
check_init_u32 (sentry.size);
|
||||
check_init_u32 (sentry.type);
|
||||
break;
|
||||
case MTRRIOC_GET_PAGE_ENTRY:
|
||||
check_init_u64 (gentry.base);
|
||||
check_init_u32 (gentry.size);
|
||||
check_init_u32 (gentry.regnum);
|
||||
check_init_u32 (gentry.type);
|
||||
check_init_u32 (gentry._pad);
|
||||
break;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
27
gcc/testsuite/gcc.dg/analyzer/torture/switch-4.c
Normal file
27
gcc/testsuite/gcc.dg/analyzer/torture/switch-4.c
Normal file
|
@ -0,0 +1,27 @@
|
|||
struct snd_ac97 {
|
||||
// snip
|
||||
unsigned int id;
|
||||
// snip
|
||||
};
|
||||
|
||||
int snd_ac97_valid_reg(struct snd_ac97 *ac97, unsigned short reg) {
|
||||
|
||||
switch (ac97->id) {
|
||||
case 0x53544d02:
|
||||
if (reg == 0x22 || reg == 0x7a)
|
||||
return 1;
|
||||
__attribute__((__fallthrough__));
|
||||
case 0x414b4d00:
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int snd_ac97_update_bits(struct snd_ac97 *ac97, unsigned short reg) {
|
||||
if (ac97->id == 0x414c4781)
|
||||
{
|
||||
if (!snd_ac97_valid_reg(ac97, reg))
|
||||
return -22;
|
||||
}
|
||||
return 0;
|
||||
}
|
68
gcc/testsuite/gcc.dg/analyzer/torture/switch-5.c
Normal file
68
gcc/testsuite/gcc.dg/analyzer/torture/switch-5.c
Normal file
|
@ -0,0 +1,68 @@
|
|||
/* { dg-additional-options "-fno-analyzer-call-summaries" } */
|
||||
|
||||
typedef unsigned char u8;
|
||||
typedef signed int s32;
|
||||
typedef unsigned int u32;
|
||||
|
||||
enum v4l2_mpeg_video_hevc_profile {
|
||||
V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN = 0,
|
||||
V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_STILL_PICTURE = 1,
|
||||
V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10 = 2
|
||||
};
|
||||
enum v4l2_buf_type {
|
||||
V4L2_BUF_TYPE_VIDEO_CAPTURE = 1,
|
||||
V4L2_BUF_TYPE_VIDEO_OUTPUT = 2
|
||||
};
|
||||
struct v4l2_fmtdesc {
|
||||
u32 index;
|
||||
u32 type;
|
||||
};
|
||||
struct v4l2_ctrl;
|
||||
s32 v4l2_ctrl_g_ctrl(struct v4l2_ctrl *ctrl);
|
||||
struct create_channel_param {
|
||||
u8 profile;
|
||||
};
|
||||
|
||||
u8
|
||||
hevc_profile_to_mcu_profile(enum v4l2_mpeg_video_hevc_profile profile) {
|
||||
switch (profile) {
|
||||
default:
|
||||
case V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN:
|
||||
return 1;
|
||||
case V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10:
|
||||
return 2;
|
||||
case V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_STILL_PICTURE:
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
|
||||
int fill_create_channel_param(struct v4l2_ctrl *ctrl,
|
||||
struct create_channel_param *param) {
|
||||
enum v4l2_mpeg_video_hevc_profile profile;
|
||||
profile = v4l2_ctrl_g_ctrl(ctrl);
|
||||
param->profile = hevc_profile_to_mcu_profile(profile);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int allegro_enum_fmt_vid(struct v4l2_fmtdesc *f) {
|
||||
switch (f->type) {
|
||||
case V4L2_BUF_TYPE_VIDEO_OUTPUT:
|
||||
if (f->index >= 1)
|
||||
return -22;
|
||||
break;
|
||||
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
|
||||
if (f->index >= 2)
|
||||
return -22;
|
||||
break;
|
||||
default:
|
||||
return -22;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int allegro_ioctl_streamon(struct v4l2_ctrl *ctrl,
|
||||
struct create_channel_param *param) {
|
||||
fill_create_channel_param(ctrl, param);
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Add table
Reference in a new issue